Runtime topology
& co-hosting.
The Hono trunk is the top-level server: it hosts the SPA as static assets, the JSON-RPC capability surface, auth, MCP, and an embedded collaboration server — all on one owned port. This document fixes that decision and the four production-attach blockers it leaves open.
01 Decision
Adopt a single-process trunk. The HTTP server, the static SPA assets, the /auth and /mcp mounts, the event-rendered published HTML, and the embedded Hocuspocus collaboration server co-host on one http.Server instance. No second framework hosts the collaboration WebSocket, and no reverse proxy is required for the surfaces to share a principal.
onAuthenticate. A rolled-back SQL transaction must never leave a mutation resident in the live Y.Doc once clients attach.02 Co-hosting mechanism
The trunk owns the server; raw ws in noServer mode receives the upgrade and hands the connection to Hocuspocus. Static assets are served from the SPA build directory; everything else routes through the capability surface, so one origin and one cookie carry every principal — human or agent.
// one owned http.Server โ all surfaces co-host server.on("upgrade", async (req, socket, head) => { const principal = await resolver.fromCookie(req); if (!principal) return socket.destroy(); ws.handleUpgrade(req, socket, head, (conn) => hocuspocus.handleConnection(conn, req, { principal }) ); });
03 Principals on one origin
Both humans and AI agents authenticate against the same cookie session and arrive as typed principals. An agent carries its own rate limit, capability scope, and audit attribution — the upgrade path does not special-case it beyond resolving the principal kind.
- Human — session cookie, interactive scopes, presence shown as a square ultramarine marker.
- Agent — scoped API key bridged to a session, distinct rate limit, notched-cyan presence and audit attribution.
- Every mutation from either principal writes exactly one audit entry; the log alone reconstructs final state.
04 Open blockers
Four production-attach blockers remain before this topology is hardened for untrusted networks:
- Role-aware read / write at
onAuthenticate, not the tenant-only floor. - An
Origincheck on the WebSocket upgrade to reject cross-site sockets. - Revocation freshness on the upgrade-time principal snapshot.
- Enforce the
onAuthenticateinvariant by construction, not by convention.