Witbitz docs HomeTrustAll docs

The SDK — a Witbitz app in an afternoon

Witbitz is a runtime: a shared room with humans, an AI agent, sealed persistent state, identity and authority. The SDK is the small, stable surface over it. The trust machinery — the room key that never reaches the server, content-blind reads, the op-dispatch, the poll loop — disappears, so a static page can stand up a private, agent-backed, link-shared room in a few lines.

Status: public beta. Usable today from any static page; the surface below is small on purpose and meant to stay stable. It wraps the exact reference client the production Spaces app runs on — nothing is forked or reimplemented.

The whole thing

html
<script type="module">
  import { Witbitz } from 'https://witbitz-spaces.pages.dev/witbitz-sdk.js'

  const wb = new Witbitz()

  // A private, agent-backed room. The link IS the auth — share it to let someone in.
  const space = await wb.createSpace({
    agent: { name: 'Helper', instructions: 'You help a small group plan and decide together.' }
  })

  space.on('message', (m) => console.log(m.from, m.text))   // yours, others', and the agent's — all arrive here
  await space.send('Plan us a weekend in Kyoto')            // the agent answers on its own turn → via on('message')

  console.log('Share this:', space.shareLink)
</script>

That is a complete, multiplayer, private AI application. No backend of your own, no key handling, no database.

The surface

Three calls. That is the whole API.

new Witbitz({ endpoint?, viewer?, app? }) A client bound to the Space endpoint. Make one, reuse it. Defaults to the public production endpoint.
await wb.createSpace({ agent, tools?, immediateTools?, model? }) Mint a Space and return a live Space. agent = { name, instructions, tools?, model? }.
await wb.openSpace(link) Join a Space from a share link (or the current page URL, if it is one). The link's fragment carries the key; it never touches the server.

A Space:

space.shareLink The link to hand someone — the link is the admission capability.
space.room The server-visible room id.
space.on('message', cb) cb({ from, text, self, id, ts, widgets? }) for every entry — yours, other members', and the agent's (self: true). On an opened Space, the existing history replays here first.
await space.send(text, { from? }) Post a message. The agent decides whether to answer and does so on its own turn; the reply arrives via on('message'), not as a return value.
await space.sealedProof() What the server actually stores for this room — the opaque, sealed ledger, plus its size, hash, and who can decrypt it (the operator never can). Lets your app prove the operator can't read the store, not just claim it. null before the first message.
await space.privateLane({ agent, actions? }) Attach a private assistant lane for the current member — see Private lanes and human-approved actions. → a Lane.
space.on('error', cb) Transient poll errors (non-fatal; the loop keeps going).
space.close() Stop streaming and release the handle.

What you get, and what's handled for you

Standing up a Space gives your app, for free:

You never touch: the room key, envelope sealing, the /space op-dispatch, the poll/etag loop, or endpoint resolution.

Tools and human authority

Give the agent platform tools by name; the ones in immediateTools it may call directly, the rest become proposals a member approves before they run.

javascript
const space = await wb.createSpace({
  agent: { name: 'Travel', instructions: 'Plan trips; put options on the shared map and list.' },
  tools: ['search_places', 'show_places', 'add_place', 'search_flights', 'show_flights', 'set_itinerary'],
  immediateTools: ['search_places', 'show_places', 'set_itinerary'] // read/show freely; anything effectful is a proposal
})

The full catalogue (places, flights, itinerary, chart, photo, read_file, read_url, write_pdf) is in Tools and widgets.

Private lanes and human-approved actions

Attach a private assistant lane to a shared Space with space.privateLane({ agent, actions }). The lane is the member's own room: its agent sees the shared room's messages and thinks/drafts with the member privately, but can post into the shared room only through a crossing the member approves. Give it actions, and it can only ever propose them — nothing runs until a human approves, and the action then executes on the member's device from your own handler. The agent never holds the credential.

javascript
const shared = await wb.createSpace({ agent: { name: 'Team' } })

const lane = await shared.privateLane({
  agent: { name: 'Ops assistant', instructions: 'Help me run the store. Propose refunds and emails; I approve.' },
  actions: [
    { name: 'issue_refund', description: 'Refund a customer',
      input: { type: 'object', properties: { customer: { type: 'string' }, amount: { type: 'number' } }, required: ['customer', 'amount'] },
      run: (a) => myBackend.refund(a.customer, a.amount) },   // ← runs ONLY after the member approves
  ],
})

lane.on('message', renderPrivateThread)
lane.on('proposal', (p) => showApproveDeny(p))    // { id, kind:'message'|'action', action, args, text, approve(), deny() }
lane.on('resolved', ({ approved, action }) => { /* … */ })
await lane.send('Refund Jane $40 for the late order, and email her an apology')

A Lane is a Space (its private thread streams on message), plus:

await lane.send(text) Talk to your assistant privately. Anything it wants the shared room to see, or any effectful action, it must propose.
lane.on('proposal', cb) cb({ id, kind, action, args, text, approve(), deny() }). kind:'message' is a drafted crossing to the shared room; kind:'action' is one of your actions. Nothing happens until approve() (posts the crossing / runs your run handler on the device) or deny().
lane.on('resolved', cb) cb({ id, approved, action, result? }) after a decision.

Effectful actions execute on the member's device from your run() handler — the runtime records the approval but never runs them itself, so the credential and the side-effect stay on your side. This is the delegated-authority model (Delegated authority), one method call away. It's the piece that's genuinely hard to build safely by hand: a propose→approve→execute state machine, an audit trail, and a hard guarantee the agent can't act unilaterally.

Configuration

Try it

A complete example app (create-or-join a shared assistant room, ~40 lines) is live at witbitz-spaces.pages.dev/sdk-example.html — open it, then open the share link in a second tab to see the same sealed room from another "member." Its source is sdk-example.js on the same origin.

A second example shows what the SDK uniquely makes easy — The Confidential Room: talk to an AI advisor about something you'd never paste into an ordinary chatbot, while a live panel fetches space.sealedProof() and shows the server holds only an opaque envelope, sealed under a key it never receives. Building this safely without Witbitz means end-to-end encryption, a key model where the server can't hold the key, and a way for a user to audit it — here it's the runtime, and the proof is one method call. A normal AI product asks you to trust it won't read your data; this one lets a user check.

And The Ops Desk shows human-in-the-loop authority: a private assistant that can draft real actions — refunds, emails — but can never run one on its own. Each is a proposal you approve, and only then does it execute, on your device, from your handler. The agent never holds the credential. It's a privateLane({ actions }) plus on('proposal') + approve().

Notes and limits

Read next: Vault Spaces to build with the reference Spaces, and Platform for the runtime this rides on.

Machine-readable source: sdk.md · Generated 2026-08-29T18:46:30Z · build f3ff88cc · every doc in one fetch: llms-full.txt (HTML) · ← api-reference · embed