Guest↔Host RPC — Delivery Notes
Delivery summary and documentation map for the
tranquil-rpc(Cap’n Web) guest↔host RPC layer. For the layer itself, read Guest↔Host RPC; this note just says what exists and where.
Status
Feature-complete, pending a final manual gate. Built in phases, one session each, with a manual verification gate at every phase:
| Phase | Scope | State |
|---|---|---|
| 0 | Spike — prove Cap’n Web over the webview transport | ✅ passed |
| 1 | Real package — transport + trust + sessions + trivial ping | ✅ passed |
| 2 | Pane-controls capability + guest self-registration (cutover) | ✅ passed |
| 3 | Types registry + docs + ADR + security doc | ⏳ built, awaiting read-through gate |
On the Phase 3 gate passing, the plan’s final end-to-end regression is the last confirmation before it ships.
Where everything lives
In tranquil-rpc (the package)
- Types registry —
types/host-api.d.ts(guest-facing capability catalog forwindow.tranquilHost) +types/tranquil-rpc.d.ts(consumer/module API fromrequire("tranquil-rpc")).package.json"types"points at them; both type-check clean against capnweb’s own declarations. - Security — Security Considerations, a living tracker (threat model, mitigations, open issues), with
each item tagged
[done]/[partial]/[open]. - Phase history / handoff — tranquil-rpc — Status & Handoff (temporary; the dev doc + ADR are the durable record).
- README —
README.md, the quick orientation (trust model, build, API, layout).
In www-tranquil (these docs)
- Architecture — Guest↔Host RPC: transport, trust model, capability + types registry, host/guest API, and the two Cap’n Web lifetime/shape gotchas.
- Decision record — ADR-0003: Cap’n Web RPC: Cap’n Web vs more IPC vs a bespoke protocol, the trust model, and consequences.
- First consumer — Pane Controls: trusted mockups self-register their own tab-bar controls over RPC (the hardcoded host path was removed).
Capability surface (shipped)
The catalog a trusted page reaches through window.tranquilHost (all calls resolve async over RPC):
| Capability | Shape | Signature |
|---|---|---|
ping | function | ping(): Promise<"pong"> |
notify | function | notify(msg): Promise<true> |
paneControls | namespace | paneControls.register(items): Promise<true> |
PaneControlItem = { id, glyph, title, action }, where action is a Cap’n Web function stub the
host invokes on click — so it runs in the page. Keep types/host-api.d.ts in lockstep with this.
Trust model (one line)
Default-deny: only file:// pages under a registered trusted root get the injected runtime + full
capabilities; every http(s):// (and file:// outside a root) gets no RPC surface at all.
Two Cap’n Web gotchas (learned live)
- Argument stubs are disposed when the call returns — retain any stub you keep past a call with
.dup(), and dispose the dup on teardown. - Capability shape = the factory’s return type — a bare function is
host.name(…); anRpcTargetis a namespacehost.name.method(…).