OpenRoom documentation

OpenRoom is live at openroom.app and remains pre-1.0. It has two product routes: live classroom interaction and tutor-led sessions. This page describes the current service and names the features that are not available.

1. Quickstart

Host a session

  1. Open the host console.
  2. Sign in with Google. The hosted service uses the account to save agendas, enforce the 20-session daily quota and recover recent sessions on another device.
  3. Create a session in deck editor or import a YAML agenda. You can save it in a folder in one of your spaces and add a new immutable version after each edit.
  4. Start session. The console returns an 8-character session code and opens the host controls for that session.
  5. Open the stage URL on the projector. It shows the code and a QR code pointing at the join URL.
  6. Open a question, watch the response count, then reveal or advance. Use the phone remote if you want to move away from the laptop.
  7. Export CSV or JSON, then end the session.

An assistant can validate the agenda, create the session and control an owned session through MCP. The browser console, phone remote, CLI and authenticated HTTP endpoint use the same session commands.

Join as a participant

  1. Open the join page (join.openroom.app) on a phone or laptop.
  2. Type the 8-character session code. It is case-insensitive and auto-uppercased. The alphabet is 23456789BCDFGHJKMNPQRSTVWXYZ: no vowels, no look-alike characters.
  3. Scanning the QR opens https://join.openroom.app/?code=XXXXXXXX with the code filled in. A previous participant on the same browser resumes the stored session session.
  4. Answer the current question. If answer change is enabled, you can change your answer until the host closes the question.
  5. In the default identity mode, OpenRoom shows you a generated session handle. Keep it if you may need to rejoin from a different browser.
  6. A tutoring session may instead be identified. It is then open only to people holding the access link the tutor issued for that context, and you appear under the name the tutor already wrote on it. You are still never asked to create an account or type a name.

2. Tutor-led sessions

OpenRoom provides the delivery surface for a human tutor. Codex, Claude Desktop or another external MCP client can read school material, work through what is needed and produce a typed outline. OpenRoom stores the resulting outline and presentation context. It does not receive the original files, retain the preparation conversation or replace the tutor.

Open the tutor workspace.

Preparation and delivery

  1. Open the school material in the external preparation agent.
  2. The agent drafts an Outline v1, validates it with outline_validate, then saves an immutable version through openroom_api.
  3. Review the outline in the dedicated deck editor. The context list, deck list, create forms, edit forms, record page and deck editor are separate routes.
  4. Launch the selected version. OpenRoom copies it into a session Durable Object and returns the host, stage and participant surfaces.
  5. Move through semantic steps and interactions from the host console, a tablet, CLI, direct API or session_command.
  6. After teaching, save compact outcomes, notes, homework and selected artifacts. This is a Notes, not a transcript.

Tutor workspace routes

RouteJob
/host/#/tutor/contextsList and open presentation contexts (person, group, class, event).
/host/#/tutor/contexts/newCreate one context.
/host/#/tutor/contexts/:id/editEdit one context.
/host/#/decksList and open decks.
/host/#/decks/newCreate deck metadata.
/host/#/decks/:id/editReview and save an immutable outline version in deck editor.
/host/#/decks/:id/recordSave the compact Notes.
/host/#/tutor/trashRestore records or request browser-confirmed permanent deletion.

Outline v1

The normative schema is packages/schema/schema/outline.schema.json. An outline contains metadata, semantic steps and the existing typed interaction definitions. Step kinds are title, statement, cards, steps, term, activity, timer, media, debrief, break and interaction. OpenRoom supplies the layout; outlines cannot contain CSS or screen coordinates. tutorNotes are stripped from participant and projector snapshots.

D1 holds contexts, deck metadata, immutable outline versions, compact records, trash state and deletion intents. Only a launched outline is copied into a session Durable Object. Original documents and model conversations remain outside OpenRoom.

Normal deletion moves a record to recoverable trash. An MCP or CLI client can request permanent deletion, but it receives only a short-lived URL. The signed-in tutor must open that URL and confirm the irreversible purge in a browser.

3. Session agenda format

A session agenda is a YAML or JSON document. The normative definition is the JSON Schema (draft 2020-12) at packages/schema/schema/session.schema.json. YAML and JSON forms of the same agenda are equivalent and covered by tests.

Top level

FieldTypeRequiredNotes
version1yesLiteral 1.
meta.titlestringyesSession title.
meta.descriptionstringno
meta.localestringnoe.g. en.
meta.sourcestringnoFree-text provenance.
defaultsobjectnoSee below.
qnaobjectnoSession-wide audience Q&A: enabled and maxLength.
interactionsarrayyesAt least one. Ids unique, kebab-case, 1–64 characters.

Defaults and per-interaction overrides

FieldValuesDefaultMeaning
identityModepseudonymous, anonymous, identifiedpseudonymousAgenda-level. Pseudonymous sessions assign a unique session-local recovery handle; explicit anonymous mode disables handles. Identified sessions take the handle from the context's display name — written by the tutor, never typed by the participant — and are enterable only with a context access link for that context. A live session with no durable session row has no context, so no link can identify into it.
resultVisibilityhidden-until-close, livehidden-until-closelive shows the aggregate on the stage while the question is open. hidden-until-close withholds it until reveal.
allowAnswerChangebooleantrueParticipants may replace their answer while the question is open.
themedefault, chalkboard, paper, projector, sherbetdefaultAgenda-level starting theme. The host can change it while the session is live.
allowDontKnowbooleanfalsePer-interaction only. Adds an explicit "I don't know yet" answer, counted separately in the aggregate.

resultVisibility and allowAnswerChange can be set in defaults and overridden on any interaction.

Fields on every interaction

FieldTypeNotes
idstringRequired. Unique, kebab-case, 1–64 characters.
typeenumRequired. choice, scale, numeric, text, qna, ranking.
promptstringRequired. 1–500 characters.
displaystringPer type; see the display table. Mismatches are a validation error.
resultVisibilityenumOverrides the agenda default.
allowAnswerChangebooleanOverrides the agenda default.
allowDontKnowbooleanAdds an explicit "I don't know yet" option.
timerSecinteger1–7200 seconds. Opening the question starts the countdown; the session closes voting at expiry.
peerInstructionbooleanSingle-select choice only. Enables a second vote after discussion.
notesstringHost-only. Never sent to participant or stage clients, at any point.
pedagogyobjectobjective, explanation, followUp, durationSec. Host-facing.

Type-specific fields

TypeFieldsRules
choice options[] (id, label, correct?, misconception?), multiple? 2–10 options. multiple defaults to false. correct and misconception are withheld from participants and the stage until reveal.
scale min, max, minLabel?, maxLabel? min and max are integers; max - min must be between 2 and 10.
numeric unit?, correct?, tolerance? tolerance requires correct (E_TOLERANCE_WITHOUT_CORRECT). Both are withheld until reveal.
text maxLength?, correctAnswers? Text length defaults to 200 and is capped at 500. Accepted answers support case-insensitive short-answer quizzes.
qna None Participants submit questions and vote on each other's entries.
ranking options[], correctOrder? 2–6 options. Results use Borda count. correctOrder must contain every option id once.

Display styles

The validator rejects a display value that does not belong to the interaction type. The first value listed is the default when display is omitted.

TypeAllowed displayDefault
choicebars, columns, donut, pie, radial, emoji-pulsebars
scaledots, gauge, barsdots
numerichistogramhistogram
textlist, word-cloudlist
qnalistlist
rankingordered-barsordered-bars

Validation errors

Validation returns stable error codes with a JSON path, so tooling can react to them: E_SCHEMA, E_DUPLICATE_ID, E_DISPLAY_MISMATCH, E_TOLERANCE_WITHOUT_CORRECT, E_OPTION_COUNT.

4. Full agenda example

This is examples/exit-ticket.yaml, a five-minute close for any session. All example agendas in the repository are validated in CI.

version: 1
meta:
  title: End-of-session exit ticket
  description: >-
    A five-minute close for any session: what stuck, what is still muddy, and how
    confident the class feels.
  locale: en
defaults:
  identityMode: anonymous
  resultVisibility: hidden-until-close
  allowAnswerChange: true

interactions:
  - id: understanding
    type: scale
    prompt: How well do you understand today's main idea?
    display: dots
    min: 1
    max: 5
    minLabel: Lost
    maxLabel: I could teach it
    allowDontKnow: true
    pedagogy:
      objective: Get a fast distribution of self-reported understanding.
      durationSec: 45
    notes: >-
      Watch the spread, not the mean. A bimodal picture means you have two
      classes in the session and need to split the next task.

  - id: key-takeaway
    type: text
    prompt: In one sentence, what is the most important thing you learned today?
    display: word-cloud
    maxLength: 200
    pedagogy:
      objective: Ask participants to retrieve the idea in their own words.
      followUp: Compare the cloud against your stated learning objective out loud.
      durationSec: 120

  - id: muddiest-point
    type: text
    prompt: What is still muddy? Name the thing you would ask if nobody could see who asked.
    display: list
    maxLength: 240
    pedagogy:
      objective: Surface confusion anonymously (muddiest point technique).
      durationSec: 120
    notes: >-
      Read them silently first. Pick the two most common and open the next
      session with those.

  - id: pace
    type: choice
    prompt: How was the pace today?
    display: bars
    options:
      - id: too-slow
        label: Too slow
      - id: about-right
        label: About right
      - id: too-fast
        label: Too fast
    resultVisibility: live
    pedagogy:
      objective: Calibrate the next session.
      durationSec: 30

  - id: questions-for-next-time
    type: qna
    prompt: Anything you want covered next time?
    display: list

5. MCP and repository CLI

MCP endpoint

POST /api/mcp provides a stateless Streamable HTTP endpoint. ChatGPT uses the OAuth flow. Other clients can use a personal API token created under Settings in the host console.

  • outline_validate: validate an Outline and return its compiled interaction summary.
  • session_validate: validate a SimpleSession or Session document.
  • session_create: create a session and return the join URL, stage token and host token.
  • session_status: read lifecycle state and participant count without results.
  • session_results: read aggregate results without ballots or hidden answer keys.
  • openroom_api: manage user-scoped contexts, decks, versions, records, launches and trash.
  • session_command: apply host commands to an owned session, including outline navigation.

The browser, MCP, CLI and direct API call the same tutoring application services. Interactive sign-in and final confirmation of permanent deletion are the two browser-only operations.

Repository CLI

The openroom CLI lives in packages/cli. Every command accepts --json for a single machine-readable object on stdout. -h/--help prints usage; -v/--version prints the version. The package is private and has not been published for external installation. These commands require a source checkout.

Exit codes: 0 ok, 1 validation or command failed, 2 usage error.

Authoring (offline)

CommandDescriptionExample
openroom init [file]Write a starter session agenda. Defaults to session.yaml; --force allows overwriting.openroom init session.yaml
openroom validate <file>Validate an agenda. Prints one line per error with code and path.openroom validate session.yaml
openroom preview <file>Print an interaction-by-interaction host summary and the participant payload for each interaction.openroom preview session.yaml --json
openroom outline validate <file>Validate an Outline and print its typed step and interaction summary.openroom outline validate examples/sessions/french-b1.yaml
openroom api <method> <path>Call an allowlisted tutoring route with a personal token and optional JSON or YAML body.openroom api GET /api/decks --url "$BASE" --token "$PAT"

Running a session (hits the HTTP API)

session start writes .openroom.json in the working directory with the session id, code, host token and URLs. Later commands read it, so they need no flags. --url overrides the stored base URL.

CommandDescriptionExample
openroom session start <file> --url <base> --admin-key <key>Validate the agenda, create the session, print the session id, code, join URL and stage URL, and store session state.openroom session start session.yaml --url https://your-worker.example --admin-key "$ADMIN_KEY"
openroom session statusPrint the host snapshot: session status, per-interaction status and answered counts.openroom session status --json
openroom session open <interactionId>Open an interaction for answers.openroom session open understanding
openroom session close <interactionId>Close an interaction. No further answers accepted.openroom session close understanding
openroom session reveal <interactionId>Reveal results, including correct answers, to participants and the stage.openroom session reveal understanding
openroom session advanceClose the current interaction and open the next pending one.openroom session advance
openroom session outline-nextMove an active tutor session to its next semantic step.openroom session outline-next
openroom session outline-previousMove an active tutor session to its previous semantic step.openroom session outline-previous
openroom session outline-goto <stepId>Show one named session step.openroom session outline-goto past-tense-check
openroom session outline-insert <step-file>Insert a validated, tutor-approved step, optionally after another step and show it.openroom session outline-insert prompt.yaml --after practice --show
openroom session endEnd the session. The session code stops working, and the retention clock starts (see Live session behavior).openroom session end
openroom session freezePanic freeze: block all submissions and hide every participant text entry on the stage.openroom session freeze
openroom session unfreezeLift the freeze.openroom session unfreeze
openroom session hide <interactionId> <participantId>Hide one text or Q&A entry. openroom results prints the participant id of every entry.openroom session hide reflection p-4f1c
openroom session unhide <interactionId> <participantId>Un-hide an entry that was hidden by the blocklist or by hand.openroom session unhide reflection p-4f1c
openroom resultsPrint current results for the session. Text and Q&A entries are printed as [participantId] text, with (hidden) on moderated entries.openroom results --json
openroom export --format csv|json [--out <file>]Download the export. Writes to stdout unless --out is given. The CSV fails with 410 once ballots have been purged.openroom export --format csv --out results.csv

Participant actions (answering, Q&A voting) are intentionally not CLI operations; the CLI holds a host capability, not a seat in the session.

6. HTTP API reference

The Worker serves JSON under /api/. The table covers the live-session and tutoring route groups. See openapi.json for the machine-readable catalog and individual authenticated routes.

MethodPathAuthPurpose
POST/api/sessions Host session cookie plus CSRF header, or operator x-openroom-admin Create a session from { agenda }. Returns { sessionCode, code, hostToken, stageToken, joinUrl }.
POST/api/join none Join with { code, recoveryHandle?, contextLink? }. Returns { sessionCode, participantToken, participantId, identityMode, handle? }. contextLink is for identified sessions only: 403 context-link-required without it, 409 identified-join-unavailable if the session is not identified, 403 context-link-invalid for a bad, revoked, expired or wrong-context link.
GET / POST/api/tutoring/contexts/:id/links Host session plus CSRF, or personal token; space member, editor or above List or mint context access links. POST body { expiresInDays? } (default 180, max 365) returns the raw orlnk_… token once; listing shows only prefixes. 429 link-limit beyond ten live links per context.
DELETE/api/tutoring/contexts/:id/links/:linkId Same as above Revoke. Sets revoked_at; the row is never hard-deleted.
GET/api/learner/me Authorization: Bearer orlnk_… only Returns { contextId, displayName }. No cookie or personal token is accepted here.
GET/api/learner/sessions Authorization: Bearer orlnk_… only That one context's non-draft sessions plus curated records { outcomes, homework, artifacts }. Tutor notes and past session codes are never included.
GET/api/sessions/:sessionCode/state?role=participant|stage|host Bearer token Role-scoped snapshot with a revision. ?afterRevision=N returns 304 when unchanged.
POST/api/sessions/:sessionCode/commands Bearer token Apply a command envelope. 200 { ok: true, revision }; 409 on E_REVISION_CONFLICT; 422 on other domain errors.
GET/api/sessions/:sessionCode/export?format=csv|json Bearer host token Results export. format=json is aggregates only; format=csv is one row per ballot and returns 410 { ok: false, error: "ballots-purged" } once ballots have been purged 30 minutes after the session ended. Both return 404 once the session is deleted, 24 hours after it ended.
GET/api/sessions/:sessionCode/ws?token=... token in query WebSocket upgrade (Hibernation API). Server sends {"v":1,"type":"session.changed","revision":N}, coalesced to at most one per 250 ms. Clients send only pings.
POST/api/mcp OAuth access token, personal API token or operator key as Bearer Run the seven MCP tools listed above. User-scoped tutoring records and owned-session control require a user token.
GET, POST/api/tutoring/contexts Host session plus CSRF for mutation, or user personal/OAuth token List presentation contexts or create one curated context record.
GET, PATCH, DELETE/api/tutoring/contexts/:id same user scope Read, edit or move one context to recoverable trash. Restore and permanent-deletion-intent actions are child routes.
GET, POST/api/decks same user scope List decks (reusable content) or create one with an optional first immutable content version. createSession: true opts into also creating a draft session.
GET, PATCH, DELETE/api/decks/:id same user scope Read, edit or trash a deck. Child routes provide immutable versions, restore and permanent-deletion intent.
GET, POST/api/sessions same user scope List sessions (one delivery of a deck) or schedule one from a deckId. live/completed statuses are server-owned.
GET, PATCH, DELETE/api/sessions/:id same user scope Read, edit or trash a session. Child routes provide the Notes, launch, restore and permanent-deletion intent.

Tokens

HMAC-SHA256 via WebCrypto, formatted base64url(payload).base64url(sig), payload { sessionCode, role, participantId?, exp }. Participant tokens expire after 6 hours; host and stage tokens after 12. The signing key is the TOKEN_SECRET secret. These capabilities grant access to one session and remain separate from the host's Google account and session cookie.

Command envelope

POST /api/sessions/ABCD2345/commands
Authorization: Bearer <token>
Content-Type: application/json

{
  "idempotencyKey": "6f1c...",
  "expectedRevision": 42,
  "command": { "command": "interaction.close", "interactionId": "understanding" }
}

The actor is derived from the token, never from the body. Host commands: session.start, session.end, session.freeze, session.unfreeze, session.advance, interaction.open, interaction.close, interaction.reveal, interaction.revote, interaction.undoRevote, session.theme, text.hide, text.unhide, qna.hide, qna.unhide, qna.stage, outline.next, outline.previous, outline.goto and outline.insert. Participant commands are answer.submit, interaction-block qna.vote, session-wide qna.ask and qna.upvote.

7. Live session behavior

  • Session codes. 8 characters from 23456789BCDFGHJKMNPQRSTVWXYZ. The code is the session id. A code stops resolving once the session ends. Codes are not reserved or reusable across sessions in any way you should rely on.
  • Idle auto-end. A session with no activity for 12 hours is ended by the Durable Object's alarm. The same alarm prunes stored idempotency keys.
  • Retention after the end. Two deadlines run off the moment a session reaches ended, whether the host ended it or the idle alarm did. After 30 minutes the ballots are purged: every per-person answer and the participant roster are deleted, while the aggregates stay: counts, means and the wording of text and Q&A entries survive, with each entry's participant id replaced by purged-1, purged-2 and so on. The JSON export still works; the per-ballot CSV export returns 410 ballots-purged. After 24 hours the session is deleted outright; storage is wiped and the session id 404s exactly like a code that was never issued.
  • Answer change. When allowAnswerChange is true, a participant's new answer replaces the old one, and the aggregate is recomputed. This is allowed until the interaction is closed, not until reveal.
  • Countdown. A question with timerSec receives an absolute closesAt deadline when the host opens it. The Durable Object closes voting at that deadline. The timer does not award points.
  • Participant recovery. Pseudonymous sessions assign a generated handle at join. A participant can submit the handle with the session code to recover the same participant id. Failed recovery attempts are limited per session.
  • Session-wide Q&A. Participants can ask and upvote while the session is in the lobby or live state. A host can hide questions, show the list on stage or spotlight one question from the main console or Q&A desk.
  • Tutor outline cursor. A launched Outline adds one semantic step cursor to session state. Moving to an interaction step closes the previous interaction and opens the referenced one; moving to a content step clears the active interaction. Stage and participant snapshots receive only the current safe step, while the host snapshot retains the full outline and tutor notes.
  • Idempotent retries. Every command carries an idempotencyKey. The Durable Object stores key → result, so a retried request returns the original result instead of applying twice. The browser SDK generates the key and retries once on a network error with the same key. Re-opening an already-open interaction is a no-op success and does not bump the revision.
  • Revisions. A monotonic session revision bumps on every applied mutation. Clients pass expectedRevision for optimistic concurrency and get 409 on conflict. On reconnect a client fetches the snapshot rather than replaying events.
  • Transport. WebSocket notification first; the SDK falls back to polling (about 1.5 s while a question is open, 6 s idle, 15 s when the tab is backgrounded) after two WebSocket failures, and returns to WebSocket when it can.
  • Moderation. A built-in English profanity blocklist sessions on every text and Q&A submission: substring and word-boundary matching over roughly 100 terms, after normalizing leetspeak (a→@ e→3 i→1 o→0 s→5) and stretched letters. A match marks the entry hidden; the participant is not told. The host can hide or unhide an individual entry. Panic freeze blocks all submissions and hides every participant text entry on the stage until unfrozen.
  • Pre-reveal safety. Correct options, numeric correct values and tolerances, misconceptions and host notes are stripped from participant and stage snapshots before reveal. With the default hidden-until-close, the aggregate itself is also withheld. This is enforced in the snapshot layer and covered by dedicated tests.

8. Privacy facts

What is stored, for one live session, inside one Durable Object:

  • The normalized session agenda.
  • A server-generated participant id, join timestamp and handle for each participant in a session that uses handles. In a pseudonymous session the handle is generated and session-local; in an identified session it is the display name the tutor wrote on the context.
  • One current ballot per participant per interaction, and the recomputed aggregate.
  • Q&A entries and vote counts; hidden flags for moderated entries.
  • Idempotency keys with their results, pruned on the hourly alarm.

The D1 control database stores host accounts and reusable work:

  • Google subject id, email, display name and host session records.
  • Saved agendas with immutable versions, spaces, folders, memberships and invitations.
  • Presentation contexts, deck metadata, immutable Outline versions, compact session records, trash state and short-lived permanent-deletion intents.
  • Context access links: for each link, the SHA-256 of the token, its visible prefix, its context, and its creation, expiry and revocation timestamps. The link itself is never stored and cannot be read back after minting.
  • A session directory used for the 20-session daily quota, ownership checks and cross-device recovery of recent host controls.

OpenRoom does not store the original school documents supplied to an external preparation agent or the agent's model conversation. Outline provenance is labels and notes only.

Participants do not create accounts, and are never asked for a name, email or password in any identity mode. In an identified session the name shown is one the tutor authored on the context; nothing is collected from the participant. The browser apps contain no ads, analytics, tracking scripts, third-party fonts, CDNs or beacons. Application logs exclude participant answer text.

Context access links. A tutor can issue a link that lets one student or family read that student's own sessions and curated records. It is a revocable capability, not an account:

  • It creates no user record, no sign-in session and no workspace membership, and it is rejected on every tutoring and workspace route. It works only on /api/learner/*.
  • It reaches exactly one context. Students who share a tutor's workspace cannot read each other, and the context is taken from the link itself rather than from anything the caller sends.
  • It is shown once at mint (only its hash is kept), expires after 180 days by default, is capped at ten live links per context, and the tutor can revoke it at any time.
  • It never carries the tutor's private notes on a session, nor any past session code — a records link cannot become a way into a session.
  • Only a space editor or owner can issue or revoke one. A presenter can run a session but cannot issue a credential.

Live-session retention:

  • Ballots and the participant roster are deleted 30 minutes after the session ends. What is left is aggregate: counts, means, and text and Q&A entries whose participant ids have been replaced with purged-1, purged-2, … so no entry can be tied back to a person or to that person's other answers.
  • The whole session (agenda, aggregates, idempotency keys, everything) is deleted 24 hours after the session ends.
  • The per-participant CSV export is available for 30 minutes after the end and returns 410 after purge. The aggregate JSON export remains available until the session deletion deadline.

Host notes and pedagogy fields are host-only and are never sent to participant or stage clients. Tokens are session-scoped capabilities and expire (6 h participant, 12 h host and stage).

This is a description of the code, not a claim of compliance with any particular regulation. The hosted-service privacy policy and terms of use are the public legal pages.

9. Current availability

The hosted service sessions at openroom.app. Google sign-in is enabled there. The service has no billing system, and each signed-in host can create 20 sessions in a rolling 24-hour period.

The host application includes separate classroom-session and tutor-led session routes. The tutor workspace is at /host/#/decks. It includes presentation contexts, deck metadata, immutable outline versions, Outline Designer, synchronized delivery and compact Notes. It does not include original-document upload, video calling or a built-in heavy generation model.

OpenRoom does not offer public self-hosting yet. The source repository has not been published, no license has been selected, and no supported release or upgrade path exists. The internal deployment uses one Cloudflare Worker, a D1 control database and one SQLite-backed Durable Object per live session.

PowerPoint and Google Slides integrations are not implemented. The console shows inactive custom-branding controls marked “coming soon,” but the hosted service has no paid agenda. The 500-participant session target has not passed a load test.

10. Architecture

The Worker serves the marketing site, host console, participant app and projector view. It also handles Google OAuth, classroom and tutoring control-plane routes, session creation, participant joins and the machine interfaces.

D1 stores host accounts, sessions, API tokens, saved agenda versions, spaces, folders, collaborators, contexts, decks, immutable outline versions, compact Notes and the session directory. Ballot submission does not read or write D1. One SQLite-backed Durable Object owns each live session's agenda or launched session, lifecycle, session cursor, participant records, ballots, aggregates, moderation state and idempotency keys.

Clients send commands over HTTP and treat the HTTP response as the acknowledgement. Hibernation WebSockets carry revision notifications. A client that receives a notification fetches a role-scoped snapshot; polling takes over after repeated WebSocket failures.

  Browser host   MCP / CLI / API   Participant   Projector
         \             |                |             /
          +---------- Worker and static assets -------+
                         /                    \
                D1 business records       Deck Durable Object
                accounts, contexts         launched outline + cursor
                sessions, versions          ballots + aggregates
                records, session index         Hibernation WebSockets

R2 media storage and the public self-host adapter described in the PRD are not implemented.

OpenRoom (working name), live and pre-1.0. Home