Skip to content

Host a hub client

A hub client is a full host: it owns a user file, renders apps in the sandbox, brokers every capability, and gives the user a place to build. The reference Playground and the macOS desktop shell are both hub clients over the same packages — this guide is the map for building yours, whether you reuse those packages or implement the spec in another stack.

Each stage below is independently testable, and the stages are ordered so you always have something running.

Render an app iframe with sandbox="allow-scripts" and no network (blocked connect-src), then speak the handshake: send snug:host-ready with your capability flags, accept snug:app-announce. Validate every inbound frame at the boundary against the published schemas — malformed frames are refused, not repaired.

A host at this stage can already run any LLM-optional app.

Implement snug:app-messagesnug:app-response: route the app’s action to whatever agent you host, stream cumulative text if you advertise streaming, and honor snug:app-cancel. How you assemble the agent’s context is Part IV’s subject — start naive, then adopt runtime contracts before your turns get expensive.

Implement Part II: one SQLite file per user, the hub-namespace tables with their normative DDL, per-app native tables materialized into an isolated runtime database at load. Then prove the property that matters: export a file, wipe your host, import it, and everything still runs. If that round-trip holds, your users own their data in fact rather than in copy.

snug:db-request / snug:db-response against the app’s own tables. Failures are typed and honest; a write the user would care about is a write your UI can show.

Part III is the largest surface and the most security-sensitive: requirements, user approval, credential custody in the user’s file, and the host executor with its frozen ceiling. Read the concept page first, then implement against the spec text — and keep its two constants sacred: credentials never enter the iframe, and injection has no lenient mode.

Part VI states the normative requirements; Appendix A is the error-code registry and Appendix B the constants (frame size classes among them). Three habits from the reference implementation worth copying:

  • Test the refusals, not just the accepts — and pair every “X cannot reach Y” test with a positive twin proving the legitimate caller still can.
  • Validate at the envelope boundary, everywhere, so a hostile app or a hostile file meets the same wall.
  • The sandbox flags are load-bearing. allow-scripts disables entire DOM behaviors (form submission among them) — enumerate what your CSP and sandbox actually enforce and probe them in a real browser, because jsdom will lie to you.

All of this exists as MIT packages if your stack is TypeScript: @snugprotocol/runner (stage 1), @snugprotocol/protocol (validation), @snugprotocol/db (stages 3–4), @snugprotocol/auth (stage 5), with apps/playground as the worked assembly. A non-TypeScript host implements the same spec — the schemas and Part VI are the contract, not the packages.