Docs

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:

PhaseScopeState
0Spike — prove Cap’n Web over the webview transport✅ passed
1Real package — transport + trust + sessions + trivial ping✅ passed
2Pane-controls capability + guest self-registration (cutover)✅ passed
3Types 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 registrytypes/host-api.d.ts (guest-facing capability catalog for window.tranquilHost) + types/tranquil-rpc.d.ts (consumer/module API from require("tranquil-rpc")). package.json "types" points at them; both type-check clean against capnweb’s own declarations.
  • SecuritySecurity Considerations, a living tracker (threat model, mitigations, open issues), with each item tagged [done] / [partial] / [open].
  • Phase history / handofftranquil-rpc — Status & Handoff (temporary; the dev doc + ADR are the durable record).
  • READMEREADME.md, the quick orientation (trust model, build, API, layout).

In www-tranquil (these docs)

  • ArchitectureGuest↔Host RPC: transport, trust model, capability + types registry, host/guest API, and the two Cap’n Web lifetime/shape gotchas.
  • Decision recordADR-0003: Cap’n Web RPC: Cap’n Web vs more IPC vs a bespoke protocol, the trust model, and consequences.
  • First consumerPane 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):

CapabilityShapeSignature
pingfunctionping(): Promise<"pong">
notifyfunctionnotify(msg): Promise<true>
paneControlsnamespacepaneControls.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)

  1. Argument stubs are disposed when the call returns — retain any stub you keep past a call with .dup(), and dispose the dup on teardown.
  2. Capability shape = the factory’s return type — a bare function is host.name(…); an RpcTarget is a namespace host.name.method(…).