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
- Open the host console.
- 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.
- 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.
- Start session. The console returns an 8-character session code and opens the host controls for that session.
- Open the stage URL on the projector. It shows the code and a QR code pointing at the join URL.
- Open a question, watch the response count, then reveal or advance. Use the phone remote if you want to move away from the laptop.
- 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
- Open the join page (join.openroom.app) on a phone or laptop.
- Type the 8-character session code. It is case-insensitive and auto-uppercased.
The alphabet is
23456789BCDFGHJKMNPQRSTVWXYZ: no vowels, no look-alike characters. - Scanning the QR opens
https://join.openroom.app/?code=XXXXXXXXwith the code filled in. A previous participant on the same browser resumes the stored session session. - Answer the current question. If answer change is enabled, you can change your answer until the host closes the question.
- In the default identity mode, OpenRoom shows you a generated session handle. Keep it if you may need to rejoin from a different browser.
- 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.
Preparation and delivery
- Open the school material in the external preparation agent.
- The agent drafts an
Outline v1, validates it withoutline_validate, then saves an immutable version throughopenroom_api. - 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.
- Launch the selected version. OpenRoom copies it into a session Durable Object and returns the host, stage and participant surfaces.
- Move through semantic steps and interactions from the host console, a
tablet, CLI, direct API or
session_command. - After teaching, save compact outcomes, notes, homework and selected artifacts. This is a Notes, not a transcript.
Tutor workspace routes
| Route | Job |
|---|---|
/host/#/tutor/contexts | List and open presentation contexts (person, group, class, event). |
/host/#/tutor/contexts/new | Create one context. |
/host/#/tutor/contexts/:id/edit | Edit one context. |
/host/#/decks | List and open decks. |
/host/#/decks/new | Create deck metadata. |
/host/#/decks/:id/edit | Review and save an immutable outline version in deck editor. |
/host/#/decks/:id/record | Save the compact Notes. |
/host/#/tutor/trash | Restore 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
| Field | Type | Required | Notes |
|---|---|---|---|
version | 1 | yes | Literal 1. |
meta.title | string | yes | Session title. |
meta.description | string | no | |
meta.locale | string | no | e.g. en. |
meta.source | string | no | Free-text provenance. |
defaults | object | no | See below. |
qna | object | no | Session-wide audience Q&A: enabled and maxLength. |
interactions | array | yes | At least one. Ids unique, kebab-case, 1–64 characters. |
Defaults and per-interaction overrides
| Field | Values | Default | Meaning |
|---|---|---|---|
identityMode | pseudonymous, anonymous, identified | pseudonymous | Agenda-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. |
resultVisibility | hidden-until-close, live | hidden-until-close | live shows the aggregate on the stage while the question is open. hidden-until-close withholds it until reveal. |
allowAnswerChange | boolean | true | Participants may replace their answer while the question is open. |
theme | default, chalkboard, paper, projector, sherbet | default | Agenda-level starting theme. The host can change it while the session is live. |
allowDontKnow | boolean | false | Per-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
| Field | Type | Notes |
|---|---|---|
id | string | Required. Unique, kebab-case, 1–64 characters. |
type | enum | Required. choice, scale, numeric, text, qna, ranking. |
prompt | string | Required. 1–500 characters. |
display | string | Per type; see the display table. Mismatches are a validation error. |
resultVisibility | enum | Overrides the agenda default. |
allowAnswerChange | boolean | Overrides the agenda default. |
allowDontKnow | boolean | Adds an explicit "I don't know yet" option. |
timerSec | integer | 1–7200 seconds. Opening the question starts the countdown; the session closes voting at expiry. |
peerInstruction | boolean | Single-select choice only. Enables a second vote after discussion. |
notes | string | Host-only. Never sent to participant or stage clients, at any point. |
pedagogy | object | objective, explanation, followUp, durationSec. Host-facing. |
Type-specific fields
| Type | Fields | Rules |
|---|---|---|
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.
| Type | Allowed display | Default |
|---|---|---|
choice | bars, columns, donut, pie, radial, emoji-pulse | bars |
scale | dots, gauge, bars | dots |
numeric | histogram | histogram |
text | list, word-cloud | list |
qna | list | list |
ranking | ordered-bars | ordered-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)
| Command | Description | Example |
|---|---|---|
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.
| Command | Description | Example |
|---|---|---|
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 status | Print 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 advance | Close the current interaction and open the next pending one. | openroom session advance |
openroom session outline-next | Move an active tutor session to its next semantic step. | openroom session outline-next |
openroom session outline-previous | Move 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 end | End the session. The session code stops working, and the retention clock starts (see Live session behavior). | openroom session end |
openroom session freeze | Panic freeze: block all submissions and hide every participant text entry on the stage. | openroom session freeze |
openroom session unfreeze | Lift 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 results | Print 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.
| Method | Path | Auth | Purpose |
|---|---|---|---|
| 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 bypurged-1,purged-2and so on. The JSON export still works; the per-ballot CSV export returns410 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
allowAnswerChangeis 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
timerSecreceives an absoluteclosesAtdeadline 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
expectedRevisionfor 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.