[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill-rivet-per-tenant-database":3,"mdc-co08ra-key":38,"related-org-rivet-per-tenant-database":2232,"related-repo-rivet-per-tenant-database":2426},{"slug":4,"name":4,"fn":5,"description":6,"org":7,"tags":12,"stars":27,"repoUrl":28,"updatedAt":29,"license":30,"forks":31,"topics":32,"repo":33,"sourceUrl":36,"mdContent":37},"per-tenant-database","implement multi-tenant data isolation with Rivet","Multi-tenant data isolation with one Rivet Actor per tenant: the actor key is the tenant id, so each tenant gets its own isolated dataset and migrations.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},"rivet","Rivet","https:\u002F\u002Fpexgzepcugksgbtrxkhf.supabase.co\u002Fstorage\u002Fv1\u002Fobject\u002Fpublic\u002Forg-logos\u002Frivet.png","rivet-dev",[13,15,18,21,24],{"name":9,"slug":8,"type":14},"tag",{"name":16,"slug":17,"type":14},"Architecture","architecture",{"name":19,"slug":20,"type":14},"Database","database",{"name":22,"slug":23,"type":14},"Migration","migration",{"name":25,"slug":26,"type":14},"Multi-Tenant","multi-tenant",17,"https:\u002F\u002Fgithub.com\u002Frivet-dev\u002Fskills","2026-07-21T05:37:46.414425",null,7,[],{"repoUrl":28,"stars":27,"forks":31,"topics":34,"description":35},[],"Generated skill files for Rivet AI integrations","https:\u002F\u002Fgithub.com\u002Frivet-dev\u002Fskills\u002Ftree\u002FHEAD\u002Fper-tenant-database","---\nname: \"per-tenant-database\"\ndescription: \"Multi-tenant data isolation with one Rivet Actor per tenant: the actor key is the tenant id, so each tenant gets its own isolated dataset and migrations.\"\n---\n\n# Database per Tenant\n\n**IMPORTANT: Before doing anything, you MUST read `BASE_SKILL.md` in this skill's directory. It contains essential guidance on debugging, error handling, state management, deployment, and project setup. Those rules and patterns apply to all RivetKit work. Everything below assumes you have already read and understood it.**\n\n## Working Examples\n\nIf you need a reference implementation, read the raw working example code in these templates:\n\n- [per-tenant-database](https:\u002F\u002Fgithub.com\u002Frivet-dev\u002Frivet\u002Ftree\u002Fmain\u002Fexamples\u002Fper-tenant-database)\n\n\nPatterns for database-per-tenant architectures with RivetKit. Instead of one shared database with a `tenant_id` column on every table, each tenant gets its own Rivet Actor, and that actor owns the tenant's entire dataset.\n\n## Starter Code\n\nStart with the working example on [GitHub](https:\u002F\u002Fgithub.com\u002Frivet-dev\u002Frivet\u002Ftree\u002Fmain\u002Fexamples\u002Fper-tenant-database) and adapt it. The example stores each tenant's dataset in JSON actor state and serves a React dashboard with live event updates.\n\n| Topic | Summary |\n| --- | --- |\n| Isolation | One `companyDatabase` actor per tenant, keyed by company name. Switching tenants swaps the entire dataset. |\n| State | JSON actor state holding `employees` and `projects` arrays plus timestamps. No SQLite, no queues, no scheduling. |\n| Realtime | Every write action mutates state, then broadcasts a typed event (`employeeAdded`, `projectAdded`) to all connected clients of that tenant. |\n| Auth | None. The sign-in screen is cosmetic. Production guidance is in the [security checklist](#security-checklist). |\n\n## The Isolation Model\n\nThe actor key is the tenant id. The client connects with `useActor({ name: \"companyDatabase\", key: [companyName] })` and the actor reads `c.key[0]` in `createState` to seed that tenant's dataset. This gives you:\n\n- **One actor per tenant**: `companyDatabase[tenantId]` addresses exactly one actor instance. Two tenants can never share an actor.\n- **One dataset per tenant**: All reads and writes go through that actor's [state](\u002Fdocs\u002Factors\u002Fstate), so there is no shared table with a `tenant_id` column to filter incorrectly. Cross-tenant leaks require constructing the wrong key, not forgetting a `WHERE` clause.\n- **No key injection**: Keys are arrays, not interpolated strings. `key: [tenantId]` cannot be escaped the way `\"tenant:\" + tenantId` string concatenation can. See [Keys](\u002Fdocs\u002Factors\u002Fkeys).\n\nThe example's test ([tests\u002Fper-tenant-database.test.ts](https:\u002F\u002Fgithub.com\u002Frivet-dev\u002Frivet\u002Ftree\u002Fmain\u002Fexamples\u002Fper-tenant-database\u002Ftests\u002Fper-tenant-database.test.ts)) proves the isolation: data written to `companyDatabase[\"Alpha Co\"]` never appears in `companyDatabase[\"Beta Co\"]`.\n\n## Choosing a State Backend\n\nThe example uses plain JSON actor state. The same key-equals-tenant model works with any actor state backend.\n\n| Backend | Use When | Docs | Working Code |\n| --- | --- | --- | --- |\n| JSON actor state | Small datasets, simple reads, whole dataset fits comfortably in memory. What the example uses. | [State](\u002Fdocs\u002Factors\u002Fstate) | [GitHub](https:\u002F\u002Fgithub.com\u002Frivet-dev\u002Frivet\u002Ftree\u002Fmain\u002Fexamples\u002Fper-tenant-database) |\n| Actor SQLite (`rivetkit\u002Fdb`) | Tables, indexes, SQL queries, larger-than-memory data, per-tenant relational schema. | [SQLite](\u002Fdocs\u002Factors\u002Fsqlite) | [GitHub](https:\u002F\u002Fgithub.com\u002Frivet-dev\u002Frivet\u002Ftree\u002Fmain\u002Fexamples\u002Fkitchen-sink\u002Fsrc\u002Factors\u002Fstate\u002Fsqlite-raw.ts) |\n| SQLite + Drizzle | Typed schema, query builder, and generated migration files on top of actor SQLite. | [SQLite + Drizzle](\u002Fdocs\u002Factors\u002Fsqlite-drizzle) | [GitHub](https:\u002F\u002Fgithub.com\u002Frivet-dev\u002Frivet\u002Ftree\u002Fmain\u002Fexamples\u002Fkitchen-sink\u002Fsrc\u002Factors\u002Fstate\u002Fsqlite-drizzle\u002F) |\n\nWith either SQLite option, every tenant gets its own embedded SQLite database, since the database is scoped to the actor and the actor is scoped to the tenant.\n\n## Migrations\n\nThe per-tenant example has no migrations because JSON state has no schema. When you adopt SQLite, migrations run per tenant database:\n\n- **Raw SQL**: `db({ onMigrate })` runs your migration SQL inside a SQLite savepoint before the actor serves traffic. If `onMigrate` throws, all migration SQL rolls back atomically and the actor does not start. See [SQLite](\u002Fdocs\u002Factors\u002Fsqlite).\n- **Drizzle**: `drizzle-kit` generates migration files from your typed schema, and `db({ schema, migrations })` applies them when the actor wakes. See [SQLite + Drizzle](\u002Fdocs\u002Factors\u002Fsqlite-drizzle).\n\nBecause each tenant has its own database, migrations roll out per actor as each tenant's actor wakes, rather than as one large migration against a shared database.\n\n## Tenant Id Must Come From Auth\n\nThe example's sign-in is cosmetic: the client picks any company string and that string becomes the actor key, so any visitor can read and write any tenant's data. Do not ship this. As a required production extension (not implemented by the example):\n\n- Derive the tenant id from a verified credential, such as a JWT claim, never from user input.\n- Validate the credential against `c.key` in `onBeforeConnect` (pass\u002Ffail) or `createConnState` (store the verified user on connection state). See [Authentication](\u002Fdocs\u002Factors\u002Fauthentication) and [Connections](\u002Fdocs\u002Factors\u002Fconnections).\n- Add per-action permission checks on top of connection-level auth. See [Access Control](\u002Fdocs\u002Factors\u002Faccess-control).\n\n## Actors\n\n- **Key**: `companyDatabase[companyName]` (single-element array key; `c.key[0]` is the company name)\n- **Responsibility**: One actor per tenant. Holds that company's employees and projects in persistent state, serves reads and writes via actions, and broadcasts mutations to connected clients.\n- **Actions**\n  - `addEmployee`\n  - `listEmployees`\n  - `addProject`\n  - `listProjects`\n  - `getStats`\n- **Queues**\n  - None\n- **Events**\n  - `employeeAdded`\n  - `projectAdded`\n- **State**\n  - JSON\n  - `company_name`\n  - `employees`\n  - `projects`\n  - `created_at`\n  - `updated_at`\n\nEvery write action follows the same mutate-then-broadcast shape: push the record into `c.state`, bump `updated_at`, broadcast the typed event, return the record. See [Actions](\u002Fdocs\u002Factors\u002Factions) and [Events](\u002Fdocs\u002Factors\u002Fevents).\n\n## Lifecycle\n\n```mermaid\nsequenceDiagram\n\tparticipant A as Tenant A client\n\tparticipant DA as companyDatabase A\n\tparticipant B as Tenant B client\n\tparticipant DB as companyDatabase B\n\n\tNote over A: authenticate and derive tenant id\n\tA->>DA: connect with key [tenantA]\n\tNote over DA: createState seeds company_name, employees, projects\n\tA->>DA: listEmployees() + listProjects() + getStats()\n\tA->>DA: addEmployee(name, role)\n\tDA-->>A: employeeAdded event\n\tB->>DB: connect with key [tenantB]\n\tNote over DB: separate actor, separate dataset\n\tB->>DB: listEmployees()\n\tDB-->>B: tenant B data only\n```\n\nIn the example, the \"authenticate\" step is a free-text company picker. The rest of the flow matches the diagram: `createState` seeds the dataset on first creation, the dashboard loads with `listEmployees`, `listProjects`, and `getStats`, and every connected client of the same tenant receives `employeeAdded` and `projectAdded` events.\n\n## Security Checklist\n\nThe example ships with none of these. Apply all of them before production.\n\n- **Tenant identity**: Derive the tenant id from a verified JWT claim, never from a client-supplied string.\n- **Connection validation**: In `onBeforeConnect` or `createConnState`, verify the credential's tenant claim matches `c.key` and reject mismatches.\n- **Per-action authorization**: Check the caller's role before mutating actions (`addEmployee`, `addProject`), not just at connect time. See [Access Control](\u002Fdocs\u002Factors\u002Faccess-control).\n- **Input validation**: Clamp name and role lengths and validate enums. The example only trims input and substitutes fallback defaults.\n- **Key construction**: Always pass the tenant id as an array element (`key: [tenantId]`). Never interpolate tenant ids into key strings, and never build keys from one tenant's input to address another tenant's actor.\n- **Growth limits**: As a recommended extension, cap or paginate the `employees` and `projects` arrays. The example lets them grow unboundedly in JSON state; move to [SQLite](\u002Fdocs\u002Factors\u002Fsqlite) when the dataset outgrows memory.\n\n## Reference Map\n\n### Actors\n\n- [Access Control](reference\u002Factors\u002Faccess-control.md)\n- [Actions](reference\u002Factors\u002Factions.md)\n- [Actor Keys](reference\u002Factors\u002Fkeys.md)\n- [Actor Runtime Socket](reference\u002Factors\u002Factor-runtime-socket.md)\n- [Actor Statuses](reference\u002Factors\u002Fstatuses.md)\n- [Authentication](reference\u002Factors\u002Fauthentication.md)\n- [Cloudflare Workers Quickstart](reference\u002Factors\u002Fquickstart\u002Fcloudflare.md)\n- [Communicating Between Actors](reference\u002Factors\u002Fcommunicating-between-actors.md)\n- [Connections](reference\u002Factors\u002Fconnections.md)\n- [Custom Inspector Tabs](reference\u002Factors\u002Finspector-tabs.md)\n- [Debugging](reference\u002Factors\u002Fdebugging.md)\n- [Design Patterns](reference\u002Factors\u002Fdesign-patterns.md)\n- [Destroying Actors](reference\u002Factors\u002Fdestroy.md)\n- [Effect.ts Quickstart (Beta)](reference\u002Factors\u002Fquickstart\u002Feffect.md)\n- [Errors](reference\u002Factors\u002Ferrors.md)\n- [Fetch and WebSocket Handler](reference\u002Factors\u002Ffetch-and-websocket-handler.md)\n- [Helper Types](reference\u002Factors\u002Fhelper-types.md)\n- [Icons & Names](reference\u002Factors\u002Fappearance.md)\n- [In-Memory State](reference\u002Factors\u002Fstate.md)\n- [Input Parameters](reference\u002Factors\u002Finput.md)\n- [Lifecycle](reference\u002Factors\u002Flifecycle.md)\n- [Limits](reference\u002Factors\u002Flimits.md)\n- [Low-Level HTTP Request Handler](reference\u002Factors\u002Frequest-handler.md)\n- [Low-Level KV Storage](reference\u002Factors\u002Fkv.md)\n- [Low-Level WebSocket Handler](reference\u002Factors\u002Fwebsocket-handler.md)\n- [Metadata](reference\u002Factors\u002Fmetadata.md)\n- [Next.js Quickstart](reference\u002Factors\u002Fquickstart\u002Fnext-js.md)\n- [Node.js & Bun Quickstart](reference\u002Factors\u002Fquickstart\u002Fbackend.md)\n- [Queues & Run Loops](reference\u002Factors\u002Fqueues.md)\n- [React Quickstart](reference\u002Factors\u002Fquickstart\u002Freact.md)\n- [Realtime](reference\u002Factors\u002Fevents.md)\n- [Rust Quickstart (Beta)](reference\u002Factors\u002Fquickstart\u002Frust.md)\n- [Scaling & Concurrency](reference\u002Factors\u002Fscaling.md)\n- [Schedule & Cron](reference\u002Factors\u002Fschedule.md)\n- [Sharing and Joining State](reference\u002Factors\u002Fsharing-and-joining-state.md)\n- [SQLite](reference\u002Factors\u002Fsqlite.md)\n- [SQLite + Drizzle](reference\u002Factors\u002Fsqlite-drizzle.md)\n- [Supabase Functions Quickstart](reference\u002Factors\u002Fquickstart\u002Fsupabase.md)\n- [Testing](reference\u002Factors\u002Ftesting.md)\n- [Troubleshooting](reference\u002Factors\u002Ftroubleshooting.md)\n- [Types](reference\u002Factors\u002Ftypes.md)\n- [Vanilla HTTP API](reference\u002Factors\u002Fhttp-api.md)\n- [Versions & Upgrades](reference\u002Factors\u002Fversions.md)\n- [Workflows](reference\u002Factors\u002Fworkflows.md)\n\n### Cli\n\n- [CLI](reference\u002Fcli.md)\n\n### Clients\n\n- [Node.js & Bun](reference\u002Fclients\u002Fjavascript.md)\n- [React](reference\u002Fclients\u002Freact.md)\n- [Rust (Beta)](reference\u002Fclients\u002Frust.md)\n- [Swift](reference\u002Fclients\u002Fswift.md)\n- [SwiftUI](reference\u002Fclients\u002Fswiftui.md)\n\n### Cookbook\n\n- [AI Agent](reference\u002Fcookbook\u002Fai-agent.md)\n- [Chat Room](reference\u002Fcookbook\u002Fchat-room.md)\n- [Collaborative Text Editor](reference\u002Fcookbook\u002Fcollaborative-text-editor.md)\n- [Cron Jobs and Scheduled Tasks](reference\u002Fcookbook\u002Fcron-jobs.md)\n- [Database per Tenant](reference\u002Fcookbook\u002Fper-tenant-database.md)\n- [Deploying Rivet in a VPC or Air-Gapped Network](reference\u002Fcookbook\u002Fvpc-air-gapped.md)\n- [Live Cursors and Presence](reference\u002Fcookbook\u002Flive-cursors.md)\n- [Multiplayer Game](reference\u002Fcookbook\u002Fmultiplayer-game.md)\n\n### Deploy\n\n- [Container Runner](reference\u002Fdeploy\u002Fcontainer-runner.md)\n- [Deploy To Amazon Web Services Lambda](reference\u002Fdeploy\u002Faws-lambda.md)\n- [Deploying to AWS ECS](reference\u002Fdeploy\u002Faws-ecs.md)\n- [Deploying to Cloudflare Workers](reference\u002Fdeploy\u002Fcloudflare.md)\n- [Deploying to Freestyle](reference\u002Fdeploy\u002Ffreestyle.md)\n- [Deploying to Google Cloud Run](reference\u002Fdeploy\u002Fgcp-cloud-run.md)\n- [Deploying to Hetzner](reference\u002Fdeploy\u002Fhetzner.md)\n- [Deploying to Kubernetes](reference\u002Fdeploy\u002Fkubernetes.md)\n- [Deploying to Railway](reference\u002Fdeploy\u002Frailway.md)\n- [Deploying to Rivet Compute](reference\u002Fdeploy\u002Frivet-compute.md)\n- [Deploying to Supabase Functions](reference\u002Fdeploy\u002Fsupabase.md)\n- [Deploying to Vercel](reference\u002Fdeploy\u002Fvercel.md)\n- [Deploying to VMs & Bare Metal](reference\u002Fdeploy\u002Fvm-and-bare-metal.md)\n\n### General\n\n- [Actor Configuration](reference\u002Fgeneral\u002Factor-configuration.md)\n- [Architecture](reference\u002Fgeneral\u002Farchitecture.md)\n- [Cross-Origin Resource Sharing](reference\u002Fgeneral\u002Fcors.md)\n- [Documentation for LLMs & AI](reference\u002Fgeneral\u002Fdocs-for-llms.md)\n- [Edge Networking](reference\u002Fgeneral\u002Fedge.md)\n- [Endpoints](reference\u002Fgeneral\u002Fendpoints.md)\n- [Environment Variables](reference\u002Fgeneral\u002Fenvironment-variables.md)\n- [HTTP Server](reference\u002Fgeneral\u002Fhttp-server.md)\n- [Logging](reference\u002Fgeneral\u002Flogging.md)\n- [Pool Configuration](reference\u002Fgeneral\u002Fpool-configuration.md)\n- [Production Checklist](reference\u002Fgeneral\u002Fproduction-checklist.md)\n- [Registry Configuration](reference\u002Fgeneral\u002Fregistry-configuration.md)\n- [Runtime Modes](reference\u002Fgeneral\u002Fruntime-modes.md)\n- [WASM vs Native SDK](reference\u002Fgeneral\u002Fwasm-vs-native-sdk.md)\n\n### Self Hosting\n\n- [Configuration](reference\u002Fself-hosting\u002Fconfiguration.md)\n- [Docker Compose](reference\u002Fself-hosting\u002Fdocker-compose.md)\n- [Docker Container](reference\u002Fself-hosting\u002Fdocker-container.md)\n- [File System](reference\u002Fself-hosting\u002Ffilesystem.md)\n- [FoundationDB (Enterprise)](reference\u002Fself-hosting\u002Ffoundationdb.md)\n- [Installing Rivet Engine](reference\u002Fself-hosting\u002Finstall.md)\n- [Kubernetes](reference\u002Fself-hosting\u002Fkubernetes.md)\n- [Multi-Region](reference\u002Fself-hosting\u002Fmulti-region.md)\n- [PostgreSQL](reference\u002Fself-hosting\u002Fpostgres.md)\n- [Production Checklist](reference\u002Fself-hosting\u002Fproduction-checklist.md)\n- [Railway Deployment](reference\u002Fself-hosting\u002Frailway.md)\n- [Render Deployment](reference\u002Fself-hosting\u002Frender.md)\n- [TLS & Certificates](reference\u002Fself-hosting\u002Ftls.md)\n\n",{"data":39,"body":40},{"name":4,"description":6},{"type":41,"children":42},"root",[43,52,71,78,83,99,112,118,131,259,265,294,381,410,416,421,550,555,561,566,629,634,640,645,707,713,907,939,945,1102,1147,1153,1158,1286,1292,1298,1689,1695,1707,1713,1761,1767,1841,1847,1967,1973,2101,2107,2226],{"type":44,"tag":45,"props":46,"children":48},"element","h1",{"id":47},"database-per-tenant",[49],{"type":50,"value":51},"text","Database per Tenant",{"type":44,"tag":53,"props":54,"children":55},"p",{},[56],{"type":44,"tag":57,"props":58,"children":59},"strong",{},[60,62,69],{"type":50,"value":61},"IMPORTANT: Before doing anything, you MUST read ",{"type":44,"tag":63,"props":64,"children":66},"code",{"className":65},[],[67],{"type":50,"value":68},"BASE_SKILL.md",{"type":50,"value":70}," in this skill's directory. It contains essential guidance on debugging, error handling, state management, deployment, and project setup. Those rules and patterns apply to all RivetKit work. Everything below assumes you have already read and understood it.",{"type":44,"tag":72,"props":73,"children":75},"h2",{"id":74},"working-examples",[76],{"type":50,"value":77},"Working Examples",{"type":44,"tag":53,"props":79,"children":80},{},[81],{"type":50,"value":82},"If you need a reference implementation, read the raw working example code in these templates:",{"type":44,"tag":84,"props":85,"children":86},"ul",{},[87],{"type":44,"tag":88,"props":89,"children":90},"li",{},[91],{"type":44,"tag":92,"props":93,"children":97},"a",{"href":94,"rel":95},"https:\u002F\u002Fgithub.com\u002Frivet-dev\u002Frivet\u002Ftree\u002Fmain\u002Fexamples\u002Fper-tenant-database",[96],"nofollow",[98],{"type":50,"value":4},{"type":44,"tag":53,"props":100,"children":101},{},[102,104,110],{"type":50,"value":103},"Patterns for database-per-tenant architectures with RivetKit. Instead of one shared database with a ",{"type":44,"tag":63,"props":105,"children":107},{"className":106},[],[108],{"type":50,"value":109},"tenant_id",{"type":50,"value":111}," column on every table, each tenant gets its own Rivet Actor, and that actor owns the tenant's entire dataset.",{"type":44,"tag":72,"props":113,"children":115},{"id":114},"starter-code",[116],{"type":50,"value":117},"Starter Code",{"type":44,"tag":53,"props":119,"children":120},{},[121,123,129],{"type":50,"value":122},"Start with the working example on ",{"type":44,"tag":92,"props":124,"children":126},{"href":94,"rel":125},[96],[127],{"type":50,"value":128},"GitHub",{"type":50,"value":130}," and adapt it. The example stores each tenant's dataset in JSON actor state and serves a React dashboard with live event updates.",{"type":44,"tag":132,"props":133,"children":134},"table",{},[135,154],{"type":44,"tag":136,"props":137,"children":138},"thead",{},[139],{"type":44,"tag":140,"props":141,"children":142},"tr",{},[143,149],{"type":44,"tag":144,"props":145,"children":146},"th",{},[147],{"type":50,"value":148},"Topic",{"type":44,"tag":144,"props":150,"children":151},{},[152],{"type":50,"value":153},"Summary",{"type":44,"tag":155,"props":156,"children":157},"tbody",{},[158,180,209,238],{"type":44,"tag":140,"props":159,"children":160},{},[161,167],{"type":44,"tag":162,"props":163,"children":164},"td",{},[165],{"type":50,"value":166},"Isolation",{"type":44,"tag":162,"props":168,"children":169},{},[170,172,178],{"type":50,"value":171},"One ",{"type":44,"tag":63,"props":173,"children":175},{"className":174},[],[176],{"type":50,"value":177},"companyDatabase",{"type":50,"value":179}," actor per tenant, keyed by company name. Switching tenants swaps the entire dataset.",{"type":44,"tag":140,"props":181,"children":182},{},[183,188],{"type":44,"tag":162,"props":184,"children":185},{},[186],{"type":50,"value":187},"State",{"type":44,"tag":162,"props":189,"children":190},{},[191,193,199,201,207],{"type":50,"value":192},"JSON actor state holding ",{"type":44,"tag":63,"props":194,"children":196},{"className":195},[],[197],{"type":50,"value":198},"employees",{"type":50,"value":200}," and ",{"type":44,"tag":63,"props":202,"children":204},{"className":203},[],[205],{"type":50,"value":206},"projects",{"type":50,"value":208}," arrays plus timestamps. No SQLite, no queues, no scheduling.",{"type":44,"tag":140,"props":210,"children":211},{},[212,217],{"type":44,"tag":162,"props":213,"children":214},{},[215],{"type":50,"value":216},"Realtime",{"type":44,"tag":162,"props":218,"children":219},{},[220,222,228,230,236],{"type":50,"value":221},"Every write action mutates state, then broadcasts a typed event (",{"type":44,"tag":63,"props":223,"children":225},{"className":224},[],[226],{"type":50,"value":227},"employeeAdded",{"type":50,"value":229},", ",{"type":44,"tag":63,"props":231,"children":233},{"className":232},[],[234],{"type":50,"value":235},"projectAdded",{"type":50,"value":237},") to all connected clients of that tenant.",{"type":44,"tag":140,"props":239,"children":240},{},[241,246],{"type":44,"tag":162,"props":242,"children":243},{},[244],{"type":50,"value":245},"Auth",{"type":44,"tag":162,"props":247,"children":248},{},[249,251,257],{"type":50,"value":250},"None. The sign-in screen is cosmetic. Production guidance is in the ",{"type":44,"tag":92,"props":252,"children":254},{"href":253},"#security-checklist",[255],{"type":50,"value":256},"security checklist",{"type":50,"value":258},".",{"type":44,"tag":72,"props":260,"children":262},{"id":261},"the-isolation-model",[263],{"type":50,"value":264},"The Isolation Model",{"type":44,"tag":53,"props":266,"children":267},{},[268,270,276,278,284,286,292],{"type":50,"value":269},"The actor key is the tenant id. The client connects with ",{"type":44,"tag":63,"props":271,"children":273},{"className":272},[],[274],{"type":50,"value":275},"useActor({ name: \"companyDatabase\", key: [companyName] })",{"type":50,"value":277}," and the actor reads ",{"type":44,"tag":63,"props":279,"children":281},{"className":280},[],[282],{"type":50,"value":283},"c.key[0]",{"type":50,"value":285}," in ",{"type":44,"tag":63,"props":287,"children":289},{"className":288},[],[290],{"type":50,"value":291},"createState",{"type":50,"value":293}," to seed that tenant's dataset. This gives you:",{"type":44,"tag":84,"props":295,"children":296},{},[297,315,348],{"type":44,"tag":88,"props":298,"children":299},{},[300,305,307,313],{"type":44,"tag":57,"props":301,"children":302},{},[303],{"type":50,"value":304},"One actor per tenant",{"type":50,"value":306},": ",{"type":44,"tag":63,"props":308,"children":310},{"className":309},[],[311],{"type":50,"value":312},"companyDatabase[tenantId]",{"type":50,"value":314}," addresses exactly one actor instance. Two tenants can never share an actor.",{"type":44,"tag":88,"props":316,"children":317},{},[318,323,325,331,333,338,340,346],{"type":44,"tag":57,"props":319,"children":320},{},[321],{"type":50,"value":322},"One dataset per tenant",{"type":50,"value":324},": All reads and writes go through that actor's ",{"type":44,"tag":92,"props":326,"children":328},{"href":327},"\u002Fdocs\u002Factors\u002Fstate",[329],{"type":50,"value":330},"state",{"type":50,"value":332},", so there is no shared table with a ",{"type":44,"tag":63,"props":334,"children":336},{"className":335},[],[337],{"type":50,"value":109},{"type":50,"value":339}," column to filter incorrectly. Cross-tenant leaks require constructing the wrong key, not forgetting a ",{"type":44,"tag":63,"props":341,"children":343},{"className":342},[],[344],{"type":50,"value":345},"WHERE",{"type":50,"value":347}," clause.",{"type":44,"tag":88,"props":349,"children":350},{},[351,356,358,364,366,372,374,380],{"type":44,"tag":57,"props":352,"children":353},{},[354],{"type":50,"value":355},"No key injection",{"type":50,"value":357},": Keys are arrays, not interpolated strings. ",{"type":44,"tag":63,"props":359,"children":361},{"className":360},[],[362],{"type":50,"value":363},"key: [tenantId]",{"type":50,"value":365}," cannot be escaped the way ",{"type":44,"tag":63,"props":367,"children":369},{"className":368},[],[370],{"type":50,"value":371},"\"tenant:\" + tenantId",{"type":50,"value":373}," string concatenation can. See ",{"type":44,"tag":92,"props":375,"children":377},{"href":376},"\u002Fdocs\u002Factors\u002Fkeys",[378],{"type":50,"value":379},"Keys",{"type":50,"value":258},{"type":44,"tag":53,"props":382,"children":383},{},[384,386,393,395,401,403,409],{"type":50,"value":385},"The example's test (",{"type":44,"tag":92,"props":387,"children":390},{"href":388,"rel":389},"https:\u002F\u002Fgithub.com\u002Frivet-dev\u002Frivet\u002Ftree\u002Fmain\u002Fexamples\u002Fper-tenant-database\u002Ftests\u002Fper-tenant-database.test.ts",[96],[391],{"type":50,"value":392},"tests\u002Fper-tenant-database.test.ts",{"type":50,"value":394},") proves the isolation: data written to ",{"type":44,"tag":63,"props":396,"children":398},{"className":397},[],[399],{"type":50,"value":400},"companyDatabase[\"Alpha Co\"]",{"type":50,"value":402}," never appears in ",{"type":44,"tag":63,"props":404,"children":406},{"className":405},[],[407],{"type":50,"value":408},"companyDatabase[\"Beta Co\"]",{"type":50,"value":258},{"type":44,"tag":72,"props":411,"children":413},{"id":412},"choosing-a-state-backend",[414],{"type":50,"value":415},"Choosing a State Backend",{"type":44,"tag":53,"props":417,"children":418},{},[419],{"type":50,"value":420},"The example uses plain JSON actor state. The same key-equals-tenant model works with any actor state backend.",{"type":44,"tag":132,"props":422,"children":423},{},[424,450],{"type":44,"tag":136,"props":425,"children":426},{},[427],{"type":44,"tag":140,"props":428,"children":429},{},[430,435,440,445],{"type":44,"tag":144,"props":431,"children":432},{},[433],{"type":50,"value":434},"Backend",{"type":44,"tag":144,"props":436,"children":437},{},[438],{"type":50,"value":439},"Use When",{"type":44,"tag":144,"props":441,"children":442},{},[443],{"type":50,"value":444},"Docs",{"type":44,"tag":144,"props":446,"children":447},{},[448],{"type":50,"value":449},"Working Code",{"type":44,"tag":155,"props":451,"children":452},{},[453,481,520],{"type":44,"tag":140,"props":454,"children":455},{},[456,461,466,473],{"type":44,"tag":162,"props":457,"children":458},{},[459],{"type":50,"value":460},"JSON actor state",{"type":44,"tag":162,"props":462,"children":463},{},[464],{"type":50,"value":465},"Small datasets, simple reads, whole dataset fits comfortably in memory. What the example uses.",{"type":44,"tag":162,"props":467,"children":468},{},[469],{"type":44,"tag":92,"props":470,"children":471},{"href":327},[472],{"type":50,"value":187},{"type":44,"tag":162,"props":474,"children":475},{},[476],{"type":44,"tag":92,"props":477,"children":479},{"href":94,"rel":478},[96],[480],{"type":50,"value":128},{"type":44,"tag":140,"props":482,"children":483},{},[484,497,502,511],{"type":44,"tag":162,"props":485,"children":486},{},[487,489,495],{"type":50,"value":488},"Actor SQLite (",{"type":44,"tag":63,"props":490,"children":492},{"className":491},[],[493],{"type":50,"value":494},"rivetkit\u002Fdb",{"type":50,"value":496},")",{"type":44,"tag":162,"props":498,"children":499},{},[500],{"type":50,"value":501},"Tables, indexes, SQL queries, larger-than-memory data, per-tenant relational schema.",{"type":44,"tag":162,"props":503,"children":504},{},[505],{"type":44,"tag":92,"props":506,"children":508},{"href":507},"\u002Fdocs\u002Factors\u002Fsqlite",[509],{"type":50,"value":510},"SQLite",{"type":44,"tag":162,"props":512,"children":513},{},[514],{"type":44,"tag":92,"props":515,"children":518},{"href":516,"rel":517},"https:\u002F\u002Fgithub.com\u002Frivet-dev\u002Frivet\u002Ftree\u002Fmain\u002Fexamples\u002Fkitchen-sink\u002Fsrc\u002Factors\u002Fstate\u002Fsqlite-raw.ts",[96],[519],{"type":50,"value":128},{"type":44,"tag":140,"props":521,"children":522},{},[523,528,533,541],{"type":44,"tag":162,"props":524,"children":525},{},[526],{"type":50,"value":527},"SQLite + Drizzle",{"type":44,"tag":162,"props":529,"children":530},{},[531],{"type":50,"value":532},"Typed schema, query builder, and generated migration files on top of actor SQLite.",{"type":44,"tag":162,"props":534,"children":535},{},[536],{"type":44,"tag":92,"props":537,"children":539},{"href":538},"\u002Fdocs\u002Factors\u002Fsqlite-drizzle",[540],{"type":50,"value":527},{"type":44,"tag":162,"props":542,"children":543},{},[544],{"type":44,"tag":92,"props":545,"children":548},{"href":546,"rel":547},"https:\u002F\u002Fgithub.com\u002Frivet-dev\u002Frivet\u002Ftree\u002Fmain\u002Fexamples\u002Fkitchen-sink\u002Fsrc\u002Factors\u002Fstate\u002Fsqlite-drizzle\u002F",[96],[549],{"type":50,"value":128},{"type":44,"tag":53,"props":551,"children":552},{},[553],{"type":50,"value":554},"With either SQLite option, every tenant gets its own embedded SQLite database, since the database is scoped to the actor and the actor is scoped to the tenant.",{"type":44,"tag":72,"props":556,"children":558},{"id":557},"migrations",[559],{"type":50,"value":560},"Migrations",{"type":44,"tag":53,"props":562,"children":563},{},[564],{"type":50,"value":565},"The per-tenant example has no migrations because JSON state has no schema. When you adopt SQLite, migrations run per tenant database:",{"type":44,"tag":84,"props":567,"children":568},{},[569,599],{"type":44,"tag":88,"props":570,"children":571},{},[572,577,578,584,586,592,594,598],{"type":44,"tag":57,"props":573,"children":574},{},[575],{"type":50,"value":576},"Raw SQL",{"type":50,"value":306},{"type":44,"tag":63,"props":579,"children":581},{"className":580},[],[582],{"type":50,"value":583},"db({ onMigrate })",{"type":50,"value":585}," runs your migration SQL inside a SQLite savepoint before the actor serves traffic. If ",{"type":44,"tag":63,"props":587,"children":589},{"className":588},[],[590],{"type":50,"value":591},"onMigrate",{"type":50,"value":593}," throws, all migration SQL rolls back atomically and the actor does not start. See ",{"type":44,"tag":92,"props":595,"children":596},{"href":507},[597],{"type":50,"value":510},{"type":50,"value":258},{"type":44,"tag":88,"props":600,"children":601},{},[602,607,608,614,616,622,624,628],{"type":44,"tag":57,"props":603,"children":604},{},[605],{"type":50,"value":606},"Drizzle",{"type":50,"value":306},{"type":44,"tag":63,"props":609,"children":611},{"className":610},[],[612],{"type":50,"value":613},"drizzle-kit",{"type":50,"value":615}," generates migration files from your typed schema, and ",{"type":44,"tag":63,"props":617,"children":619},{"className":618},[],[620],{"type":50,"value":621},"db({ schema, migrations })",{"type":50,"value":623}," applies them when the actor wakes. See ",{"type":44,"tag":92,"props":625,"children":626},{"href":538},[627],{"type":50,"value":527},{"type":50,"value":258},{"type":44,"tag":53,"props":630,"children":631},{},[632],{"type":50,"value":633},"Because each tenant has its own database, migrations roll out per actor as each tenant's actor wakes, rather than as one large migration against a shared database.",{"type":44,"tag":72,"props":635,"children":637},{"id":636},"tenant-id-must-come-from-auth",[638],{"type":50,"value":639},"Tenant Id Must Come From Auth",{"type":44,"tag":53,"props":641,"children":642},{},[643],{"type":50,"value":644},"The example's sign-in is cosmetic: the client picks any company string and that string becomes the actor key, so any visitor can read and write any tenant's data. Do not ship this. As a required production extension (not implemented by the example):",{"type":44,"tag":84,"props":646,"children":647},{},[648,653,695],{"type":44,"tag":88,"props":649,"children":650},{},[651],{"type":50,"value":652},"Derive the tenant id from a verified credential, such as a JWT claim, never from user input.",{"type":44,"tag":88,"props":654,"children":655},{},[656,658,664,665,671,673,679,681,687,688,694],{"type":50,"value":657},"Validate the credential against ",{"type":44,"tag":63,"props":659,"children":661},{"className":660},[],[662],{"type":50,"value":663},"c.key",{"type":50,"value":285},{"type":44,"tag":63,"props":666,"children":668},{"className":667},[],[669],{"type":50,"value":670},"onBeforeConnect",{"type":50,"value":672}," (pass\u002Ffail) or ",{"type":44,"tag":63,"props":674,"children":676},{"className":675},[],[677],{"type":50,"value":678},"createConnState",{"type":50,"value":680}," (store the verified user on connection state). See ",{"type":44,"tag":92,"props":682,"children":684},{"href":683},"\u002Fdocs\u002Factors\u002Fauthentication",[685],{"type":50,"value":686},"Authentication",{"type":50,"value":200},{"type":44,"tag":92,"props":689,"children":691},{"href":690},"\u002Fdocs\u002Factors\u002Fconnections",[692],{"type":50,"value":693},"Connections",{"type":50,"value":258},{"type":44,"tag":88,"props":696,"children":697},{},[698,700,706],{"type":50,"value":699},"Add per-action permission checks on top of connection-level auth. See ",{"type":44,"tag":92,"props":701,"children":703},{"href":702},"\u002Fdocs\u002Factors\u002Faccess-control",[704],{"type":50,"value":705},"Access Control",{"type":50,"value":258},{"type":44,"tag":72,"props":708,"children":710},{"id":709},"actors",[711],{"type":50,"value":712},"Actors",{"type":44,"tag":84,"props":714,"children":715},{},[716,740,750,806,822,849],{"type":44,"tag":88,"props":717,"children":718},{},[719,724,725,731,733,738],{"type":44,"tag":57,"props":720,"children":721},{},[722],{"type":50,"value":723},"Key",{"type":50,"value":306},{"type":44,"tag":63,"props":726,"children":728},{"className":727},[],[729],{"type":50,"value":730},"companyDatabase[companyName]",{"type":50,"value":732}," (single-element array key; ",{"type":44,"tag":63,"props":734,"children":736},{"className":735},[],[737],{"type":50,"value":283},{"type":50,"value":739}," is the company name)",{"type":44,"tag":88,"props":741,"children":742},{},[743,748],{"type":44,"tag":57,"props":744,"children":745},{},[746],{"type":50,"value":747},"Responsibility",{"type":50,"value":749},": One actor per tenant. Holds that company's employees and projects in persistent state, serves reads and writes via actions, and broadcasts mutations to connected clients.",{"type":44,"tag":88,"props":751,"children":752},{},[753,758],{"type":44,"tag":57,"props":754,"children":755},{},[756],{"type":50,"value":757},"Actions",{"type":44,"tag":84,"props":759,"children":760},{},[761,770,779,788,797],{"type":44,"tag":88,"props":762,"children":763},{},[764],{"type":44,"tag":63,"props":765,"children":767},{"className":766},[],[768],{"type":50,"value":769},"addEmployee",{"type":44,"tag":88,"props":771,"children":772},{},[773],{"type":44,"tag":63,"props":774,"children":776},{"className":775},[],[777],{"type":50,"value":778},"listEmployees",{"type":44,"tag":88,"props":780,"children":781},{},[782],{"type":44,"tag":63,"props":783,"children":785},{"className":784},[],[786],{"type":50,"value":787},"addProject",{"type":44,"tag":88,"props":789,"children":790},{},[791],{"type":44,"tag":63,"props":792,"children":794},{"className":793},[],[795],{"type":50,"value":796},"listProjects",{"type":44,"tag":88,"props":798,"children":799},{},[800],{"type":44,"tag":63,"props":801,"children":803},{"className":802},[],[804],{"type":50,"value":805},"getStats",{"type":44,"tag":88,"props":807,"children":808},{},[809,814],{"type":44,"tag":57,"props":810,"children":811},{},[812],{"type":50,"value":813},"Queues",{"type":44,"tag":84,"props":815,"children":816},{},[817],{"type":44,"tag":88,"props":818,"children":819},{},[820],{"type":50,"value":821},"None",{"type":44,"tag":88,"props":823,"children":824},{},[825,830],{"type":44,"tag":57,"props":826,"children":827},{},[828],{"type":50,"value":829},"Events",{"type":44,"tag":84,"props":831,"children":832},{},[833,841],{"type":44,"tag":88,"props":834,"children":835},{},[836],{"type":44,"tag":63,"props":837,"children":839},{"className":838},[],[840],{"type":50,"value":227},{"type":44,"tag":88,"props":842,"children":843},{},[844],{"type":44,"tag":63,"props":845,"children":847},{"className":846},[],[848],{"type":50,"value":235},{"type":44,"tag":88,"props":850,"children":851},{},[852,856],{"type":44,"tag":57,"props":853,"children":854},{},[855],{"type":50,"value":187},{"type":44,"tag":84,"props":857,"children":858},{},[859,864,873,881,889,898],{"type":44,"tag":88,"props":860,"children":861},{},[862],{"type":50,"value":863},"JSON",{"type":44,"tag":88,"props":865,"children":866},{},[867],{"type":44,"tag":63,"props":868,"children":870},{"className":869},[],[871],{"type":50,"value":872},"company_name",{"type":44,"tag":88,"props":874,"children":875},{},[876],{"type":44,"tag":63,"props":877,"children":879},{"className":878},[],[880],{"type":50,"value":198},{"type":44,"tag":88,"props":882,"children":883},{},[884],{"type":44,"tag":63,"props":885,"children":887},{"className":886},[],[888],{"type":50,"value":206},{"type":44,"tag":88,"props":890,"children":891},{},[892],{"type":44,"tag":63,"props":893,"children":895},{"className":894},[],[896],{"type":50,"value":897},"created_at",{"type":44,"tag":88,"props":899,"children":900},{},[901],{"type":44,"tag":63,"props":902,"children":904},{"className":903},[],[905],{"type":50,"value":906},"updated_at",{"type":44,"tag":53,"props":908,"children":909},{},[910,912,918,920,925,927,932,933,938],{"type":50,"value":911},"Every write action follows the same mutate-then-broadcast shape: push the record into ",{"type":44,"tag":63,"props":913,"children":915},{"className":914},[],[916],{"type":50,"value":917},"c.state",{"type":50,"value":919},", bump ",{"type":44,"tag":63,"props":921,"children":923},{"className":922},[],[924],{"type":50,"value":906},{"type":50,"value":926},", broadcast the typed event, return the record. See ",{"type":44,"tag":92,"props":928,"children":930},{"href":929},"\u002Fdocs\u002Factors\u002Factions",[931],{"type":50,"value":757},{"type":50,"value":200},{"type":44,"tag":92,"props":934,"children":936},{"href":935},"\u002Fdocs\u002Factors\u002Fevents",[937],{"type":50,"value":829},{"type":50,"value":258},{"type":44,"tag":72,"props":940,"children":942},{"id":941},"lifecycle",[943],{"type":50,"value":944},"Lifecycle",{"type":44,"tag":946,"props":947,"children":952},"pre",{"className":948,"code":949,"language":950,"meta":951,"style":951},"language-mermaid shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","sequenceDiagram\n    participant A as Tenant A client\n    participant DA as companyDatabase A\n    participant B as Tenant B client\n    participant DB as companyDatabase B\n\n    Note over A: authenticate and derive tenant id\n    A->>DA: connect with key [tenantA]\n    Note over DA: createState seeds company_name, employees, projects\n    A->>DA: listEmployees() + listProjects() + getStats()\n    A->>DA: addEmployee(name, role)\n    DA-->>A: employeeAdded event\n    B->>DB: connect with key [tenantB]\n    Note over DB: separate actor, separate dataset\n    B->>DB: listEmployees()\n    DB-->>B: tenant B data only\n","mermaid","",[953],{"type":44,"tag":63,"props":954,"children":955},{"__ignoreMap":951},[956,967,976,985,994,1003,1013,1021,1030,1039,1048,1057,1066,1075,1084,1093],{"type":44,"tag":957,"props":958,"children":961},"span",{"class":959,"line":960},"line",1,[962],{"type":44,"tag":957,"props":963,"children":964},{},[965],{"type":50,"value":966},"sequenceDiagram\n",{"type":44,"tag":957,"props":968,"children":970},{"class":959,"line":969},2,[971],{"type":44,"tag":957,"props":972,"children":973},{},[974],{"type":50,"value":975},"    participant A as Tenant A client\n",{"type":44,"tag":957,"props":977,"children":979},{"class":959,"line":978},3,[980],{"type":44,"tag":957,"props":981,"children":982},{},[983],{"type":50,"value":984},"    participant DA as companyDatabase A\n",{"type":44,"tag":957,"props":986,"children":988},{"class":959,"line":987},4,[989],{"type":44,"tag":957,"props":990,"children":991},{},[992],{"type":50,"value":993},"    participant B as Tenant B client\n",{"type":44,"tag":957,"props":995,"children":997},{"class":959,"line":996},5,[998],{"type":44,"tag":957,"props":999,"children":1000},{},[1001],{"type":50,"value":1002},"    participant DB as companyDatabase B\n",{"type":44,"tag":957,"props":1004,"children":1006},{"class":959,"line":1005},6,[1007],{"type":44,"tag":957,"props":1008,"children":1010},{"emptyLinePlaceholder":1009},true,[1011],{"type":50,"value":1012},"\n",{"type":44,"tag":957,"props":1014,"children":1015},{"class":959,"line":31},[1016],{"type":44,"tag":957,"props":1017,"children":1018},{},[1019],{"type":50,"value":1020},"    Note over A: authenticate and derive tenant id\n",{"type":44,"tag":957,"props":1022,"children":1024},{"class":959,"line":1023},8,[1025],{"type":44,"tag":957,"props":1026,"children":1027},{},[1028],{"type":50,"value":1029},"    A->>DA: connect with key [tenantA]\n",{"type":44,"tag":957,"props":1031,"children":1033},{"class":959,"line":1032},9,[1034],{"type":44,"tag":957,"props":1035,"children":1036},{},[1037],{"type":50,"value":1038},"    Note over DA: createState seeds company_name, employees, projects\n",{"type":44,"tag":957,"props":1040,"children":1042},{"class":959,"line":1041},10,[1043],{"type":44,"tag":957,"props":1044,"children":1045},{},[1046],{"type":50,"value":1047},"    A->>DA: listEmployees() + listProjects() + getStats()\n",{"type":44,"tag":957,"props":1049,"children":1051},{"class":959,"line":1050},11,[1052],{"type":44,"tag":957,"props":1053,"children":1054},{},[1055],{"type":50,"value":1056},"    A->>DA: addEmployee(name, role)\n",{"type":44,"tag":957,"props":1058,"children":1060},{"class":959,"line":1059},12,[1061],{"type":44,"tag":957,"props":1062,"children":1063},{},[1064],{"type":50,"value":1065},"    DA-->>A: employeeAdded event\n",{"type":44,"tag":957,"props":1067,"children":1069},{"class":959,"line":1068},13,[1070],{"type":44,"tag":957,"props":1071,"children":1072},{},[1073],{"type":50,"value":1074},"    B->>DB: connect with key [tenantB]\n",{"type":44,"tag":957,"props":1076,"children":1078},{"class":959,"line":1077},14,[1079],{"type":44,"tag":957,"props":1080,"children":1081},{},[1082],{"type":50,"value":1083},"    Note over DB: separate actor, separate dataset\n",{"type":44,"tag":957,"props":1085,"children":1087},{"class":959,"line":1086},15,[1088],{"type":44,"tag":957,"props":1089,"children":1090},{},[1091],{"type":50,"value":1092},"    B->>DB: listEmployees()\n",{"type":44,"tag":957,"props":1094,"children":1096},{"class":959,"line":1095},16,[1097],{"type":44,"tag":957,"props":1098,"children":1099},{},[1100],{"type":50,"value":1101},"    DB-->>B: tenant B data only\n",{"type":44,"tag":53,"props":1103,"children":1104},{},[1105,1107,1112,1114,1119,1120,1125,1127,1132,1134,1139,1140,1145],{"type":50,"value":1106},"In the example, the \"authenticate\" step is a free-text company picker. The rest of the flow matches the diagram: ",{"type":44,"tag":63,"props":1108,"children":1110},{"className":1109},[],[1111],{"type":50,"value":291},{"type":50,"value":1113}," seeds the dataset on first creation, the dashboard loads with ",{"type":44,"tag":63,"props":1115,"children":1117},{"className":1116},[],[1118],{"type":50,"value":778},{"type":50,"value":229},{"type":44,"tag":63,"props":1121,"children":1123},{"className":1122},[],[1124],{"type":50,"value":796},{"type":50,"value":1126},", and ",{"type":44,"tag":63,"props":1128,"children":1130},{"className":1129},[],[1131],{"type":50,"value":805},{"type":50,"value":1133},", and every connected client of the same tenant receives ",{"type":44,"tag":63,"props":1135,"children":1137},{"className":1136},[],[1138],{"type":50,"value":227},{"type":50,"value":200},{"type":44,"tag":63,"props":1141,"children":1143},{"className":1142},[],[1144],{"type":50,"value":235},{"type":50,"value":1146}," events.",{"type":44,"tag":72,"props":1148,"children":1150},{"id":1149},"security-checklist",[1151],{"type":50,"value":1152},"Security Checklist",{"type":44,"tag":53,"props":1154,"children":1155},{},[1156],{"type":50,"value":1157},"The example ships with none of these. Apply all of them before production.",{"type":44,"tag":84,"props":1159,"children":1160},{},[1161,1171,1202,1230,1240,1257],{"type":44,"tag":88,"props":1162,"children":1163},{},[1164,1169],{"type":44,"tag":57,"props":1165,"children":1166},{},[1167],{"type":50,"value":1168},"Tenant identity",{"type":50,"value":1170},": Derive the tenant id from a verified JWT claim, never from a client-supplied string.",{"type":44,"tag":88,"props":1172,"children":1173},{},[1174,1179,1181,1186,1188,1193,1195,1200],{"type":44,"tag":57,"props":1175,"children":1176},{},[1177],{"type":50,"value":1178},"Connection validation",{"type":50,"value":1180},": In ",{"type":44,"tag":63,"props":1182,"children":1184},{"className":1183},[],[1185],{"type":50,"value":670},{"type":50,"value":1187}," or ",{"type":44,"tag":63,"props":1189,"children":1191},{"className":1190},[],[1192],{"type":50,"value":678},{"type":50,"value":1194},", verify the credential's tenant claim matches ",{"type":44,"tag":63,"props":1196,"children":1198},{"className":1197},[],[1199],{"type":50,"value":663},{"type":50,"value":1201}," and reject mismatches.",{"type":44,"tag":88,"props":1203,"children":1204},{},[1205,1210,1212,1217,1218,1223,1225,1229],{"type":44,"tag":57,"props":1206,"children":1207},{},[1208],{"type":50,"value":1209},"Per-action authorization",{"type":50,"value":1211},": Check the caller's role before mutating actions (",{"type":44,"tag":63,"props":1213,"children":1215},{"className":1214},[],[1216],{"type":50,"value":769},{"type":50,"value":229},{"type":44,"tag":63,"props":1219,"children":1221},{"className":1220},[],[1222],{"type":50,"value":787},{"type":50,"value":1224},"), not just at connect time. See ",{"type":44,"tag":92,"props":1226,"children":1227},{"href":702},[1228],{"type":50,"value":705},{"type":50,"value":258},{"type":44,"tag":88,"props":1231,"children":1232},{},[1233,1238],{"type":44,"tag":57,"props":1234,"children":1235},{},[1236],{"type":50,"value":1237},"Input validation",{"type":50,"value":1239},": Clamp name and role lengths and validate enums. The example only trims input and substitutes fallback defaults.",{"type":44,"tag":88,"props":1241,"children":1242},{},[1243,1248,1250,1255],{"type":44,"tag":57,"props":1244,"children":1245},{},[1246],{"type":50,"value":1247},"Key construction",{"type":50,"value":1249},": Always pass the tenant id as an array element (",{"type":44,"tag":63,"props":1251,"children":1253},{"className":1252},[],[1254],{"type":50,"value":363},{"type":50,"value":1256},"). Never interpolate tenant ids into key strings, and never build keys from one tenant's input to address another tenant's actor.",{"type":44,"tag":88,"props":1258,"children":1259},{},[1260,1265,1267,1272,1273,1278,1280,1284],{"type":44,"tag":57,"props":1261,"children":1262},{},[1263],{"type":50,"value":1264},"Growth limits",{"type":50,"value":1266},": As a recommended extension, cap or paginate the ",{"type":44,"tag":63,"props":1268,"children":1270},{"className":1269},[],[1271],{"type":50,"value":198},{"type":50,"value":200},{"type":44,"tag":63,"props":1274,"children":1276},{"className":1275},[],[1277],{"type":50,"value":206},{"type":50,"value":1279}," arrays. The example lets them grow unboundedly in JSON state; move to ",{"type":44,"tag":92,"props":1281,"children":1282},{"href":507},[1283],{"type":50,"value":510},{"type":50,"value":1285}," when the dataset outgrows memory.",{"type":44,"tag":72,"props":1287,"children":1289},{"id":1288},"reference-map",[1290],{"type":50,"value":1291},"Reference Map",{"type":44,"tag":1293,"props":1294,"children":1296},"h3",{"id":1295},"actors-1",[1297],{"type":50,"value":712},{"type":44,"tag":84,"props":1299,"children":1300},{},[1301,1309,1317,1326,1335,1344,1352,1361,1370,1378,1387,1396,1405,1414,1423,1432,1441,1450,1459,1468,1477,1485,1494,1503,1512,1521,1530,1539,1548,1557,1566,1574,1583,1592,1601,1610,1618,1626,1635,1644,1653,1662,1671,1680],{"type":44,"tag":88,"props":1302,"children":1303},{},[1304],{"type":44,"tag":92,"props":1305,"children":1307},{"href":1306},"reference\u002Factors\u002Faccess-control.md",[1308],{"type":50,"value":705},{"type":44,"tag":88,"props":1310,"children":1311},{},[1312],{"type":44,"tag":92,"props":1313,"children":1315},{"href":1314},"reference\u002Factors\u002Factions.md",[1316],{"type":50,"value":757},{"type":44,"tag":88,"props":1318,"children":1319},{},[1320],{"type":44,"tag":92,"props":1321,"children":1323},{"href":1322},"reference\u002Factors\u002Fkeys.md",[1324],{"type":50,"value":1325},"Actor Keys",{"type":44,"tag":88,"props":1327,"children":1328},{},[1329],{"type":44,"tag":92,"props":1330,"children":1332},{"href":1331},"reference\u002Factors\u002Factor-runtime-socket.md",[1333],{"type":50,"value":1334},"Actor Runtime Socket",{"type":44,"tag":88,"props":1336,"children":1337},{},[1338],{"type":44,"tag":92,"props":1339,"children":1341},{"href":1340},"reference\u002Factors\u002Fstatuses.md",[1342],{"type":50,"value":1343},"Actor Statuses",{"type":44,"tag":88,"props":1345,"children":1346},{},[1347],{"type":44,"tag":92,"props":1348,"children":1350},{"href":1349},"reference\u002Factors\u002Fauthentication.md",[1351],{"type":50,"value":686},{"type":44,"tag":88,"props":1353,"children":1354},{},[1355],{"type":44,"tag":92,"props":1356,"children":1358},{"href":1357},"reference\u002Factors\u002Fquickstart\u002Fcloudflare.md",[1359],{"type":50,"value":1360},"Cloudflare Workers Quickstart",{"type":44,"tag":88,"props":1362,"children":1363},{},[1364],{"type":44,"tag":92,"props":1365,"children":1367},{"href":1366},"reference\u002Factors\u002Fcommunicating-between-actors.md",[1368],{"type":50,"value":1369},"Communicating Between Actors",{"type":44,"tag":88,"props":1371,"children":1372},{},[1373],{"type":44,"tag":92,"props":1374,"children":1376},{"href":1375},"reference\u002Factors\u002Fconnections.md",[1377],{"type":50,"value":693},{"type":44,"tag":88,"props":1379,"children":1380},{},[1381],{"type":44,"tag":92,"props":1382,"children":1384},{"href":1383},"reference\u002Factors\u002Finspector-tabs.md",[1385],{"type":50,"value":1386},"Custom Inspector Tabs",{"type":44,"tag":88,"props":1388,"children":1389},{},[1390],{"type":44,"tag":92,"props":1391,"children":1393},{"href":1392},"reference\u002Factors\u002Fdebugging.md",[1394],{"type":50,"value":1395},"Debugging",{"type":44,"tag":88,"props":1397,"children":1398},{},[1399],{"type":44,"tag":92,"props":1400,"children":1402},{"href":1401},"reference\u002Factors\u002Fdesign-patterns.md",[1403],{"type":50,"value":1404},"Design Patterns",{"type":44,"tag":88,"props":1406,"children":1407},{},[1408],{"type":44,"tag":92,"props":1409,"children":1411},{"href":1410},"reference\u002Factors\u002Fdestroy.md",[1412],{"type":50,"value":1413},"Destroying Actors",{"type":44,"tag":88,"props":1415,"children":1416},{},[1417],{"type":44,"tag":92,"props":1418,"children":1420},{"href":1419},"reference\u002Factors\u002Fquickstart\u002Feffect.md",[1421],{"type":50,"value":1422},"Effect.ts Quickstart (Beta)",{"type":44,"tag":88,"props":1424,"children":1425},{},[1426],{"type":44,"tag":92,"props":1427,"children":1429},{"href":1428},"reference\u002Factors\u002Ferrors.md",[1430],{"type":50,"value":1431},"Errors",{"type":44,"tag":88,"props":1433,"children":1434},{},[1435],{"type":44,"tag":92,"props":1436,"children":1438},{"href":1437},"reference\u002Factors\u002Ffetch-and-websocket-handler.md",[1439],{"type":50,"value":1440},"Fetch and WebSocket Handler",{"type":44,"tag":88,"props":1442,"children":1443},{},[1444],{"type":44,"tag":92,"props":1445,"children":1447},{"href":1446},"reference\u002Factors\u002Fhelper-types.md",[1448],{"type":50,"value":1449},"Helper Types",{"type":44,"tag":88,"props":1451,"children":1452},{},[1453],{"type":44,"tag":92,"props":1454,"children":1456},{"href":1455},"reference\u002Factors\u002Fappearance.md",[1457],{"type":50,"value":1458},"Icons & Names",{"type":44,"tag":88,"props":1460,"children":1461},{},[1462],{"type":44,"tag":92,"props":1463,"children":1465},{"href":1464},"reference\u002Factors\u002Fstate.md",[1466],{"type":50,"value":1467},"In-Memory State",{"type":44,"tag":88,"props":1469,"children":1470},{},[1471],{"type":44,"tag":92,"props":1472,"children":1474},{"href":1473},"reference\u002Factors\u002Finput.md",[1475],{"type":50,"value":1476},"Input Parameters",{"type":44,"tag":88,"props":1478,"children":1479},{},[1480],{"type":44,"tag":92,"props":1481,"children":1483},{"href":1482},"reference\u002Factors\u002Flifecycle.md",[1484],{"type":50,"value":944},{"type":44,"tag":88,"props":1486,"children":1487},{},[1488],{"type":44,"tag":92,"props":1489,"children":1491},{"href":1490},"reference\u002Factors\u002Flimits.md",[1492],{"type":50,"value":1493},"Limits",{"type":44,"tag":88,"props":1495,"children":1496},{},[1497],{"type":44,"tag":92,"props":1498,"children":1500},{"href":1499},"reference\u002Factors\u002Frequest-handler.md",[1501],{"type":50,"value":1502},"Low-Level HTTP Request Handler",{"type":44,"tag":88,"props":1504,"children":1505},{},[1506],{"type":44,"tag":92,"props":1507,"children":1509},{"href":1508},"reference\u002Factors\u002Fkv.md",[1510],{"type":50,"value":1511},"Low-Level KV Storage",{"type":44,"tag":88,"props":1513,"children":1514},{},[1515],{"type":44,"tag":92,"props":1516,"children":1518},{"href":1517},"reference\u002Factors\u002Fwebsocket-handler.md",[1519],{"type":50,"value":1520},"Low-Level WebSocket Handler",{"type":44,"tag":88,"props":1522,"children":1523},{},[1524],{"type":44,"tag":92,"props":1525,"children":1527},{"href":1526},"reference\u002Factors\u002Fmetadata.md",[1528],{"type":50,"value":1529},"Metadata",{"type":44,"tag":88,"props":1531,"children":1532},{},[1533],{"type":44,"tag":92,"props":1534,"children":1536},{"href":1535},"reference\u002Factors\u002Fquickstart\u002Fnext-js.md",[1537],{"type":50,"value":1538},"Next.js Quickstart",{"type":44,"tag":88,"props":1540,"children":1541},{},[1542],{"type":44,"tag":92,"props":1543,"children":1545},{"href":1544},"reference\u002Factors\u002Fquickstart\u002Fbackend.md",[1546],{"type":50,"value":1547},"Node.js & Bun Quickstart",{"type":44,"tag":88,"props":1549,"children":1550},{},[1551],{"type":44,"tag":92,"props":1552,"children":1554},{"href":1553},"reference\u002Factors\u002Fqueues.md",[1555],{"type":50,"value":1556},"Queues & Run Loops",{"type":44,"tag":88,"props":1558,"children":1559},{},[1560],{"type":44,"tag":92,"props":1561,"children":1563},{"href":1562},"reference\u002Factors\u002Fquickstart\u002Freact.md",[1564],{"type":50,"value":1565},"React Quickstart",{"type":44,"tag":88,"props":1567,"children":1568},{},[1569],{"type":44,"tag":92,"props":1570,"children":1572},{"href":1571},"reference\u002Factors\u002Fevents.md",[1573],{"type":50,"value":216},{"type":44,"tag":88,"props":1575,"children":1576},{},[1577],{"type":44,"tag":92,"props":1578,"children":1580},{"href":1579},"reference\u002Factors\u002Fquickstart\u002Frust.md",[1581],{"type":50,"value":1582},"Rust Quickstart (Beta)",{"type":44,"tag":88,"props":1584,"children":1585},{},[1586],{"type":44,"tag":92,"props":1587,"children":1589},{"href":1588},"reference\u002Factors\u002Fscaling.md",[1590],{"type":50,"value":1591},"Scaling & Concurrency",{"type":44,"tag":88,"props":1593,"children":1594},{},[1595],{"type":44,"tag":92,"props":1596,"children":1598},{"href":1597},"reference\u002Factors\u002Fschedule.md",[1599],{"type":50,"value":1600},"Schedule & Cron",{"type":44,"tag":88,"props":1602,"children":1603},{},[1604],{"type":44,"tag":92,"props":1605,"children":1607},{"href":1606},"reference\u002Factors\u002Fsharing-and-joining-state.md",[1608],{"type":50,"value":1609},"Sharing and Joining State",{"type":44,"tag":88,"props":1611,"children":1612},{},[1613],{"type":44,"tag":92,"props":1614,"children":1616},{"href":1615},"reference\u002Factors\u002Fsqlite.md",[1617],{"type":50,"value":510},{"type":44,"tag":88,"props":1619,"children":1620},{},[1621],{"type":44,"tag":92,"props":1622,"children":1624},{"href":1623},"reference\u002Factors\u002Fsqlite-drizzle.md",[1625],{"type":50,"value":527},{"type":44,"tag":88,"props":1627,"children":1628},{},[1629],{"type":44,"tag":92,"props":1630,"children":1632},{"href":1631},"reference\u002Factors\u002Fquickstart\u002Fsupabase.md",[1633],{"type":50,"value":1634},"Supabase Functions Quickstart",{"type":44,"tag":88,"props":1636,"children":1637},{},[1638],{"type":44,"tag":92,"props":1639,"children":1641},{"href":1640},"reference\u002Factors\u002Ftesting.md",[1642],{"type":50,"value":1643},"Testing",{"type":44,"tag":88,"props":1645,"children":1646},{},[1647],{"type":44,"tag":92,"props":1648,"children":1650},{"href":1649},"reference\u002Factors\u002Ftroubleshooting.md",[1651],{"type":50,"value":1652},"Troubleshooting",{"type":44,"tag":88,"props":1654,"children":1655},{},[1656],{"type":44,"tag":92,"props":1657,"children":1659},{"href":1658},"reference\u002Factors\u002Ftypes.md",[1660],{"type":50,"value":1661},"Types",{"type":44,"tag":88,"props":1663,"children":1664},{},[1665],{"type":44,"tag":92,"props":1666,"children":1668},{"href":1667},"reference\u002Factors\u002Fhttp-api.md",[1669],{"type":50,"value":1670},"Vanilla HTTP API",{"type":44,"tag":88,"props":1672,"children":1673},{},[1674],{"type":44,"tag":92,"props":1675,"children":1677},{"href":1676},"reference\u002Factors\u002Fversions.md",[1678],{"type":50,"value":1679},"Versions & Upgrades",{"type":44,"tag":88,"props":1681,"children":1682},{},[1683],{"type":44,"tag":92,"props":1684,"children":1686},{"href":1685},"reference\u002Factors\u002Fworkflows.md",[1687],{"type":50,"value":1688},"Workflows",{"type":44,"tag":1293,"props":1690,"children":1692},{"id":1691},"cli",[1693],{"type":50,"value":1694},"Cli",{"type":44,"tag":84,"props":1696,"children":1697},{},[1698],{"type":44,"tag":88,"props":1699,"children":1700},{},[1701],{"type":44,"tag":92,"props":1702,"children":1704},{"href":1703},"reference\u002Fcli.md",[1705],{"type":50,"value":1706},"CLI",{"type":44,"tag":1293,"props":1708,"children":1710},{"id":1709},"clients",[1711],{"type":50,"value":1712},"Clients",{"type":44,"tag":84,"props":1714,"children":1715},{},[1716,1725,1734,1743,1752],{"type":44,"tag":88,"props":1717,"children":1718},{},[1719],{"type":44,"tag":92,"props":1720,"children":1722},{"href":1721},"reference\u002Fclients\u002Fjavascript.md",[1723],{"type":50,"value":1724},"Node.js & Bun",{"type":44,"tag":88,"props":1726,"children":1727},{},[1728],{"type":44,"tag":92,"props":1729,"children":1731},{"href":1730},"reference\u002Fclients\u002Freact.md",[1732],{"type":50,"value":1733},"React",{"type":44,"tag":88,"props":1735,"children":1736},{},[1737],{"type":44,"tag":92,"props":1738,"children":1740},{"href":1739},"reference\u002Fclients\u002Frust.md",[1741],{"type":50,"value":1742},"Rust (Beta)",{"type":44,"tag":88,"props":1744,"children":1745},{},[1746],{"type":44,"tag":92,"props":1747,"children":1749},{"href":1748},"reference\u002Fclients\u002Fswift.md",[1750],{"type":50,"value":1751},"Swift",{"type":44,"tag":88,"props":1753,"children":1754},{},[1755],{"type":44,"tag":92,"props":1756,"children":1758},{"href":1757},"reference\u002Fclients\u002Fswiftui.md",[1759],{"type":50,"value":1760},"SwiftUI",{"type":44,"tag":1293,"props":1762,"children":1764},{"id":1763},"cookbook",[1765],{"type":50,"value":1766},"Cookbook",{"type":44,"tag":84,"props":1768,"children":1769},{},[1770,1779,1788,1797,1806,1814,1823,1832],{"type":44,"tag":88,"props":1771,"children":1772},{},[1773],{"type":44,"tag":92,"props":1774,"children":1776},{"href":1775},"reference\u002Fcookbook\u002Fai-agent.md",[1777],{"type":50,"value":1778},"AI Agent",{"type":44,"tag":88,"props":1780,"children":1781},{},[1782],{"type":44,"tag":92,"props":1783,"children":1785},{"href":1784},"reference\u002Fcookbook\u002Fchat-room.md",[1786],{"type":50,"value":1787},"Chat Room",{"type":44,"tag":88,"props":1789,"children":1790},{},[1791],{"type":44,"tag":92,"props":1792,"children":1794},{"href":1793},"reference\u002Fcookbook\u002Fcollaborative-text-editor.md",[1795],{"type":50,"value":1796},"Collaborative Text Editor",{"type":44,"tag":88,"props":1798,"children":1799},{},[1800],{"type":44,"tag":92,"props":1801,"children":1803},{"href":1802},"reference\u002Fcookbook\u002Fcron-jobs.md",[1804],{"type":50,"value":1805},"Cron Jobs and Scheduled Tasks",{"type":44,"tag":88,"props":1807,"children":1808},{},[1809],{"type":44,"tag":92,"props":1810,"children":1812},{"href":1811},"reference\u002Fcookbook\u002Fper-tenant-database.md",[1813],{"type":50,"value":51},{"type":44,"tag":88,"props":1815,"children":1816},{},[1817],{"type":44,"tag":92,"props":1818,"children":1820},{"href":1819},"reference\u002Fcookbook\u002Fvpc-air-gapped.md",[1821],{"type":50,"value":1822},"Deploying Rivet in a VPC or Air-Gapped Network",{"type":44,"tag":88,"props":1824,"children":1825},{},[1826],{"type":44,"tag":92,"props":1827,"children":1829},{"href":1828},"reference\u002Fcookbook\u002Flive-cursors.md",[1830],{"type":50,"value":1831},"Live Cursors and Presence",{"type":44,"tag":88,"props":1833,"children":1834},{},[1835],{"type":44,"tag":92,"props":1836,"children":1838},{"href":1837},"reference\u002Fcookbook\u002Fmultiplayer-game.md",[1839],{"type":50,"value":1840},"Multiplayer Game",{"type":44,"tag":1293,"props":1842,"children":1844},{"id":1843},"deploy",[1845],{"type":50,"value":1846},"Deploy",{"type":44,"tag":84,"props":1848,"children":1849},{},[1850,1859,1868,1877,1886,1895,1904,1913,1922,1931,1940,1949,1958],{"type":44,"tag":88,"props":1851,"children":1852},{},[1853],{"type":44,"tag":92,"props":1854,"children":1856},{"href":1855},"reference\u002Fdeploy\u002Fcontainer-runner.md",[1857],{"type":50,"value":1858},"Container Runner",{"type":44,"tag":88,"props":1860,"children":1861},{},[1862],{"type":44,"tag":92,"props":1863,"children":1865},{"href":1864},"reference\u002Fdeploy\u002Faws-lambda.md",[1866],{"type":50,"value":1867},"Deploy To Amazon Web Services Lambda",{"type":44,"tag":88,"props":1869,"children":1870},{},[1871],{"type":44,"tag":92,"props":1872,"children":1874},{"href":1873},"reference\u002Fdeploy\u002Faws-ecs.md",[1875],{"type":50,"value":1876},"Deploying to AWS ECS",{"type":44,"tag":88,"props":1878,"children":1879},{},[1880],{"type":44,"tag":92,"props":1881,"children":1883},{"href":1882},"reference\u002Fdeploy\u002Fcloudflare.md",[1884],{"type":50,"value":1885},"Deploying to Cloudflare Workers",{"type":44,"tag":88,"props":1887,"children":1888},{},[1889],{"type":44,"tag":92,"props":1890,"children":1892},{"href":1891},"reference\u002Fdeploy\u002Ffreestyle.md",[1893],{"type":50,"value":1894},"Deploying to Freestyle",{"type":44,"tag":88,"props":1896,"children":1897},{},[1898],{"type":44,"tag":92,"props":1899,"children":1901},{"href":1900},"reference\u002Fdeploy\u002Fgcp-cloud-run.md",[1902],{"type":50,"value":1903},"Deploying to Google Cloud Run",{"type":44,"tag":88,"props":1905,"children":1906},{},[1907],{"type":44,"tag":92,"props":1908,"children":1910},{"href":1909},"reference\u002Fdeploy\u002Fhetzner.md",[1911],{"type":50,"value":1912},"Deploying to Hetzner",{"type":44,"tag":88,"props":1914,"children":1915},{},[1916],{"type":44,"tag":92,"props":1917,"children":1919},{"href":1918},"reference\u002Fdeploy\u002Fkubernetes.md",[1920],{"type":50,"value":1921},"Deploying to Kubernetes",{"type":44,"tag":88,"props":1923,"children":1924},{},[1925],{"type":44,"tag":92,"props":1926,"children":1928},{"href":1927},"reference\u002Fdeploy\u002Frailway.md",[1929],{"type":50,"value":1930},"Deploying to Railway",{"type":44,"tag":88,"props":1932,"children":1933},{},[1934],{"type":44,"tag":92,"props":1935,"children":1937},{"href":1936},"reference\u002Fdeploy\u002Frivet-compute.md",[1938],{"type":50,"value":1939},"Deploying to Rivet Compute",{"type":44,"tag":88,"props":1941,"children":1942},{},[1943],{"type":44,"tag":92,"props":1944,"children":1946},{"href":1945},"reference\u002Fdeploy\u002Fsupabase.md",[1947],{"type":50,"value":1948},"Deploying to Supabase Functions",{"type":44,"tag":88,"props":1950,"children":1951},{},[1952],{"type":44,"tag":92,"props":1953,"children":1955},{"href":1954},"reference\u002Fdeploy\u002Fvercel.md",[1956],{"type":50,"value":1957},"Deploying to Vercel",{"type":44,"tag":88,"props":1959,"children":1960},{},[1961],{"type":44,"tag":92,"props":1962,"children":1964},{"href":1963},"reference\u002Fdeploy\u002Fvm-and-bare-metal.md",[1965],{"type":50,"value":1966},"Deploying to VMs & Bare Metal",{"type":44,"tag":1293,"props":1968,"children":1970},{"id":1969},"general",[1971],{"type":50,"value":1972},"General",{"type":44,"tag":84,"props":1974,"children":1975},{},[1976,1985,1993,2002,2011,2020,2029,2038,2047,2056,2065,2074,2083,2092],{"type":44,"tag":88,"props":1977,"children":1978},{},[1979],{"type":44,"tag":92,"props":1980,"children":1982},{"href":1981},"reference\u002Fgeneral\u002Factor-configuration.md",[1983],{"type":50,"value":1984},"Actor Configuration",{"type":44,"tag":88,"props":1986,"children":1987},{},[1988],{"type":44,"tag":92,"props":1989,"children":1991},{"href":1990},"reference\u002Fgeneral\u002Farchitecture.md",[1992],{"type":50,"value":16},{"type":44,"tag":88,"props":1994,"children":1995},{},[1996],{"type":44,"tag":92,"props":1997,"children":1999},{"href":1998},"reference\u002Fgeneral\u002Fcors.md",[2000],{"type":50,"value":2001},"Cross-Origin Resource Sharing",{"type":44,"tag":88,"props":2003,"children":2004},{},[2005],{"type":44,"tag":92,"props":2006,"children":2008},{"href":2007},"reference\u002Fgeneral\u002Fdocs-for-llms.md",[2009],{"type":50,"value":2010},"Documentation for LLMs & AI",{"type":44,"tag":88,"props":2012,"children":2013},{},[2014],{"type":44,"tag":92,"props":2015,"children":2017},{"href":2016},"reference\u002Fgeneral\u002Fedge.md",[2018],{"type":50,"value":2019},"Edge Networking",{"type":44,"tag":88,"props":2021,"children":2022},{},[2023],{"type":44,"tag":92,"props":2024,"children":2026},{"href":2025},"reference\u002Fgeneral\u002Fendpoints.md",[2027],{"type":50,"value":2028},"Endpoints",{"type":44,"tag":88,"props":2030,"children":2031},{},[2032],{"type":44,"tag":92,"props":2033,"children":2035},{"href":2034},"reference\u002Fgeneral\u002Fenvironment-variables.md",[2036],{"type":50,"value":2037},"Environment Variables",{"type":44,"tag":88,"props":2039,"children":2040},{},[2041],{"type":44,"tag":92,"props":2042,"children":2044},{"href":2043},"reference\u002Fgeneral\u002Fhttp-server.md",[2045],{"type":50,"value":2046},"HTTP Server",{"type":44,"tag":88,"props":2048,"children":2049},{},[2050],{"type":44,"tag":92,"props":2051,"children":2053},{"href":2052},"reference\u002Fgeneral\u002Flogging.md",[2054],{"type":50,"value":2055},"Logging",{"type":44,"tag":88,"props":2057,"children":2058},{},[2059],{"type":44,"tag":92,"props":2060,"children":2062},{"href":2061},"reference\u002Fgeneral\u002Fpool-configuration.md",[2063],{"type":50,"value":2064},"Pool Configuration",{"type":44,"tag":88,"props":2066,"children":2067},{},[2068],{"type":44,"tag":92,"props":2069,"children":2071},{"href":2070},"reference\u002Fgeneral\u002Fproduction-checklist.md",[2072],{"type":50,"value":2073},"Production Checklist",{"type":44,"tag":88,"props":2075,"children":2076},{},[2077],{"type":44,"tag":92,"props":2078,"children":2080},{"href":2079},"reference\u002Fgeneral\u002Fregistry-configuration.md",[2081],{"type":50,"value":2082},"Registry Configuration",{"type":44,"tag":88,"props":2084,"children":2085},{},[2086],{"type":44,"tag":92,"props":2087,"children":2089},{"href":2088},"reference\u002Fgeneral\u002Fruntime-modes.md",[2090],{"type":50,"value":2091},"Runtime Modes",{"type":44,"tag":88,"props":2093,"children":2094},{},[2095],{"type":44,"tag":92,"props":2096,"children":2098},{"href":2097},"reference\u002Fgeneral\u002Fwasm-vs-native-sdk.md",[2099],{"type":50,"value":2100},"WASM vs Native SDK",{"type":44,"tag":1293,"props":2102,"children":2104},{"id":2103},"self-hosting",[2105],{"type":50,"value":2106},"Self Hosting",{"type":44,"tag":84,"props":2108,"children":2109},{},[2110,2119,2128,2137,2146,2155,2164,2173,2182,2191,2199,2208,2217],{"type":44,"tag":88,"props":2111,"children":2112},{},[2113],{"type":44,"tag":92,"props":2114,"children":2116},{"href":2115},"reference\u002Fself-hosting\u002Fconfiguration.md",[2117],{"type":50,"value":2118},"Configuration",{"type":44,"tag":88,"props":2120,"children":2121},{},[2122],{"type":44,"tag":92,"props":2123,"children":2125},{"href":2124},"reference\u002Fself-hosting\u002Fdocker-compose.md",[2126],{"type":50,"value":2127},"Docker Compose",{"type":44,"tag":88,"props":2129,"children":2130},{},[2131],{"type":44,"tag":92,"props":2132,"children":2134},{"href":2133},"reference\u002Fself-hosting\u002Fdocker-container.md",[2135],{"type":50,"value":2136},"Docker Container",{"type":44,"tag":88,"props":2138,"children":2139},{},[2140],{"type":44,"tag":92,"props":2141,"children":2143},{"href":2142},"reference\u002Fself-hosting\u002Ffilesystem.md",[2144],{"type":50,"value":2145},"File System",{"type":44,"tag":88,"props":2147,"children":2148},{},[2149],{"type":44,"tag":92,"props":2150,"children":2152},{"href":2151},"reference\u002Fself-hosting\u002Ffoundationdb.md",[2153],{"type":50,"value":2154},"FoundationDB (Enterprise)",{"type":44,"tag":88,"props":2156,"children":2157},{},[2158],{"type":44,"tag":92,"props":2159,"children":2161},{"href":2160},"reference\u002Fself-hosting\u002Finstall.md",[2162],{"type":50,"value":2163},"Installing Rivet Engine",{"type":44,"tag":88,"props":2165,"children":2166},{},[2167],{"type":44,"tag":92,"props":2168,"children":2170},{"href":2169},"reference\u002Fself-hosting\u002Fkubernetes.md",[2171],{"type":50,"value":2172},"Kubernetes",{"type":44,"tag":88,"props":2174,"children":2175},{},[2176],{"type":44,"tag":92,"props":2177,"children":2179},{"href":2178},"reference\u002Fself-hosting\u002Fmulti-region.md",[2180],{"type":50,"value":2181},"Multi-Region",{"type":44,"tag":88,"props":2183,"children":2184},{},[2185],{"type":44,"tag":92,"props":2186,"children":2188},{"href":2187},"reference\u002Fself-hosting\u002Fpostgres.md",[2189],{"type":50,"value":2190},"PostgreSQL",{"type":44,"tag":88,"props":2192,"children":2193},{},[2194],{"type":44,"tag":92,"props":2195,"children":2197},{"href":2196},"reference\u002Fself-hosting\u002Fproduction-checklist.md",[2198],{"type":50,"value":2073},{"type":44,"tag":88,"props":2200,"children":2201},{},[2202],{"type":44,"tag":92,"props":2203,"children":2205},{"href":2204},"reference\u002Fself-hosting\u002Frailway.md",[2206],{"type":50,"value":2207},"Railway Deployment",{"type":44,"tag":88,"props":2209,"children":2210},{},[2211],{"type":44,"tag":92,"props":2212,"children":2214},{"href":2213},"reference\u002Fself-hosting\u002Frender.md",[2215],{"type":50,"value":2216},"Render Deployment",{"type":44,"tag":88,"props":2218,"children":2219},{},[2220],{"type":44,"tag":92,"props":2221,"children":2223},{"href":2222},"reference\u002Fself-hosting\u002Ftls.md",[2224],{"type":50,"value":2225},"TLS & Certificates",{"type":44,"tag":2227,"props":2228,"children":2229},"style",{},[2230],{"type":50,"value":2231},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"items":2233,"total":27},[2234,2256,2274,2293,2309,2325,2342,2358,2366,2381,2397,2412],{"slug":2235,"name":2235,"fn":2236,"description":2237,"org":2238,"tags":2239,"stars":27,"repoUrl":28,"updatedAt":2255},"ai-agent","build AI agent backends with Rivet","Build an AI agent backend with persistent memory: one Rivet Actor per conversation, queued message handling, and streaming LLM responses as realtime events.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2240,2243,2245,2248,2251,2254],{"name":2241,"slug":2242,"type":14},"Agents","agents",{"name":434,"slug":2244,"type":14},"backend",{"name":2246,"slug":2247,"type":14},"LLM","llm",{"name":2249,"slug":2250,"type":14},"Memory","memory",{"name":2252,"slug":2253,"type":14},"Real-time","real-time",{"name":9,"slug":8,"type":14},"2026-07-21T05:37:45.411803",{"slug":2257,"name":2257,"fn":2258,"description":2259,"org":2260,"tags":2261,"stars":27,"repoUrl":28,"updatedAt":2273},"ai-agent-workspace","create persistent workspaces for AI agents","Give every AI agent its own computer: a persistent workspace with a filesystem, processes, shells, networking, and agent sessions on a lightweight in-process OS.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2262,2263,2266,2269,2270],{"name":2241,"slug":2242,"type":14},{"name":2264,"slug":2265,"type":14},"File Storage","file-storage",{"name":2267,"slug":2268,"type":14},"Infrastructure","infrastructure",{"name":9,"slug":8,"type":14},{"name":2271,"slug":2272,"type":14},"Sandboxing","sandboxing","2026-06-14T08:06:57.274844",{"slug":2275,"name":2275,"fn":2276,"description":2277,"org":2278,"tags":2279,"stars":27,"repoUrl":28,"updatedAt":2292},"chat-room","build realtime chat rooms with Rivet","Build a realtime chat room backend with Rivet Actors: one actor per room, SQLite-backed message history, and WebSocket broadcast to every connected client.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2280,2281,2284,2285,2286,2289],{"name":434,"slug":2244,"type":14},{"name":2282,"slug":2283,"type":14},"Messaging","messaging",{"name":2252,"slug":2253,"type":14},{"name":9,"slug":8,"type":14},{"name":2287,"slug":2288,"type":14},"SQL","sql",{"name":2290,"slug":2291,"type":14},"WebSockets","websockets","2026-07-21T05:37:48.403494",{"slug":2294,"name":2294,"fn":2295,"description":2296,"org":2297,"tags":2298,"stars":27,"repoUrl":28,"updatedAt":2308},"collaborative-text-editor","build collaborative text editors with Rivet","Build a collaborative text editor backend with Yjs CRDTs and Rivet Actors: per-document actors relay sync and awareness updates and persist snapshots.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2299,2300,2303,2306,2307],{"name":434,"slug":2244,"type":14},{"name":2301,"slug":2302,"type":14},"Collaboration","collaboration",{"name":2304,"slug":2305,"type":14},"Documents","documents",{"name":2252,"slug":2253,"type":14},{"name":9,"slug":8,"type":14},"2026-07-21T05:37:49.412829",{"slug":2310,"name":2310,"fn":2311,"description":2312,"org":2313,"tags":2314,"stars":27,"repoUrl":28,"updatedAt":2324},"cron-jobs","schedule durable cron jobs with Rivet","Patterns for durable one-shot, calendar, and fixed-interval work on Rivet Actors.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2315,2318,2319,2320,2321],{"name":2316,"slug":2317,"type":14},"Automation","automation",{"name":434,"slug":2244,"type":14},{"name":2267,"slug":2268,"type":14},{"name":9,"slug":8,"type":14},{"name":2322,"slug":2323,"type":14},"Scheduling","scheduling","2026-07-21T05:37:43.395911",{"slug":2326,"name":2326,"fn":2327,"description":2328,"org":2329,"tags":2330,"stars":27,"repoUrl":28,"updatedAt":2341},"live-cursors","implement live cursors and multiplayer presence","Live cursors and multiplayer presence with Rivet Actors: per-connection cursor state, realtime updates over events or raw WebSockets, and throttling.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2331,2332,2335,2336,2337,2340],{"name":2301,"slug":2302,"type":14},{"name":2333,"slug":2334,"type":14},"Frontend","frontend",{"name":2252,"slug":2253,"type":14},{"name":9,"slug":8,"type":14},{"name":2338,"slug":2339,"type":14},"UX Design","ux-design",{"name":2290,"slug":2291,"type":14},"2026-07-21T05:37:47.391088",{"slug":2343,"name":2343,"fn":2344,"description":2345,"org":2346,"tags":2347,"stars":27,"repoUrl":28,"updatedAt":2357},"multiplayer-game","build multiplayer games with Rivet","Pragmatic patterns for building multiplayer games: matchmaking, tick loops, realtime state, interest management, and validation.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2348,2349,2352,2355,2356],{"name":16,"slug":17,"type":14},{"name":2350,"slug":2351,"type":14},"Engineering","engineering",{"name":2353,"slug":2354,"type":14},"Game Development","game-development",{"name":2252,"slug":2253,"type":14},{"name":9,"slug":8,"type":14},"2026-07-21T05:37:44.627991",{"slug":4,"name":4,"fn":5,"description":6,"org":2359,"tags":2360,"stars":27,"repoUrl":28,"updatedAt":29},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2361,2362,2363,2364,2365],{"name":16,"slug":17,"type":14},{"name":19,"slug":20,"type":14},{"name":22,"slug":23,"type":14},{"name":25,"slug":26,"type":14},{"name":9,"slug":8,"type":14},{"slug":2367,"name":2367,"fn":2368,"description":2369,"org":2370,"tags":2371,"stars":27,"repoUrl":28,"updatedAt":2380},"rivetkit","build and debug Rivet Actors","RivetKit backend and Rivet Actor runtime guidance. Use for building, modifying, debugging, or testing Rivet Actors, registries, serverless\u002Frunner modes, deployment, or actor-based workflows.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2372,2373,2374,2376,2379],{"name":2241,"slug":2242,"type":14},{"name":434,"slug":2244,"type":14},{"name":1395,"slug":2375,"type":14},"debugging",{"name":2377,"slug":2378,"type":14},"Deployment","deployment",{"name":9,"slug":8,"type":14},"2026-07-24T05:38:58.10133",{"slug":2382,"name":2382,"fn":2383,"description":2384,"org":2385,"tags":2386,"stars":27,"repoUrl":28,"updatedAt":2396},"rivetkit-client-javascript","use RivetKit JavaScript client","RivetKit JavaScript client guidance. Use for browser, Node.js, or Bun clients that connect to Rivet Actors with rivetkit\u002Fclient, create clients, call actions, or manage connections.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2387,2388,2389,2392,2393],{"name":434,"slug":2244,"type":14},{"name":2333,"slug":2334,"type":14},{"name":2390,"slug":2391,"type":14},"JavaScript","javascript",{"name":9,"slug":8,"type":14},{"name":2394,"slug":2395,"type":14},"SDK","sdk","2026-07-30T05:31:37.725576",{"slug":2398,"name":2398,"fn":2399,"description":2400,"org":2401,"tags":2402,"stars":27,"repoUrl":28,"updatedAt":2411},"rivetkit-client-react","use RivetKit React client","RivetKit React client guidance. Use for React apps that connect to Rivet Actors with @rivetkit\u002Freact, create hooks with createRivetKit, or manage realtime state with useActor.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2403,2404,2406,2407,2408],{"name":2333,"slug":2334,"type":14},{"name":1733,"slug":2405,"type":14},"react",{"name":9,"slug":8,"type":14},{"name":2394,"slug":2395,"type":14},{"name":2409,"slug":2410,"type":14},"State Management","state-management","2026-07-30T05:31:36.618873",{"slug":2413,"name":2413,"fn":2414,"description":2415,"org":2416,"tags":2417,"stars":27,"repoUrl":28,"updatedAt":2425},"rivetkit-client-rust","build RivetKit Rust clients","RivetKit Rust client guidance. Use for Rust clients and backends that connect to Rivet Actors with rivetkit::client, create typed actor handles, call actions, or manage connections.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2418,2421,2422],{"name":2419,"slug":2420,"type":14},"API Development","api-development",{"name":434,"slug":2244,"type":14},{"name":2423,"slug":2424,"type":14},"Rust","rust","2026-07-24T05:39:02.105888",{"items":2427,"total":1095},[2428,2437,2445,2454,2462,2470,2479],{"slug":2235,"name":2235,"fn":2236,"description":2237,"org":2429,"tags":2430,"stars":27,"repoUrl":28,"updatedAt":2255},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2431,2432,2433,2434,2435,2436],{"name":2241,"slug":2242,"type":14},{"name":434,"slug":2244,"type":14},{"name":2246,"slug":2247,"type":14},{"name":2249,"slug":2250,"type":14},{"name":2252,"slug":2253,"type":14},{"name":9,"slug":8,"type":14},{"slug":2257,"name":2257,"fn":2258,"description":2259,"org":2438,"tags":2439,"stars":27,"repoUrl":28,"updatedAt":2273},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2440,2441,2442,2443,2444],{"name":2241,"slug":2242,"type":14},{"name":2264,"slug":2265,"type":14},{"name":2267,"slug":2268,"type":14},{"name":9,"slug":8,"type":14},{"name":2271,"slug":2272,"type":14},{"slug":2275,"name":2275,"fn":2276,"description":2277,"org":2446,"tags":2447,"stars":27,"repoUrl":28,"updatedAt":2292},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2448,2449,2450,2451,2452,2453],{"name":434,"slug":2244,"type":14},{"name":2282,"slug":2283,"type":14},{"name":2252,"slug":2253,"type":14},{"name":9,"slug":8,"type":14},{"name":2287,"slug":2288,"type":14},{"name":2290,"slug":2291,"type":14},{"slug":2294,"name":2294,"fn":2295,"description":2296,"org":2455,"tags":2456,"stars":27,"repoUrl":28,"updatedAt":2308},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2457,2458,2459,2460,2461],{"name":434,"slug":2244,"type":14},{"name":2301,"slug":2302,"type":14},{"name":2304,"slug":2305,"type":14},{"name":2252,"slug":2253,"type":14},{"name":9,"slug":8,"type":14},{"slug":2310,"name":2310,"fn":2311,"description":2312,"org":2463,"tags":2464,"stars":27,"repoUrl":28,"updatedAt":2324},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2465,2466,2467,2468,2469],{"name":2316,"slug":2317,"type":14},{"name":434,"slug":2244,"type":14},{"name":2267,"slug":2268,"type":14},{"name":9,"slug":8,"type":14},{"name":2322,"slug":2323,"type":14},{"slug":2326,"name":2326,"fn":2327,"description":2328,"org":2471,"tags":2472,"stars":27,"repoUrl":28,"updatedAt":2341},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2473,2474,2475,2476,2477,2478],{"name":2301,"slug":2302,"type":14},{"name":2333,"slug":2334,"type":14},{"name":2252,"slug":2253,"type":14},{"name":9,"slug":8,"type":14},{"name":2338,"slug":2339,"type":14},{"name":2290,"slug":2291,"type":14},{"slug":2343,"name":2343,"fn":2344,"description":2345,"org":2480,"tags":2481,"stars":27,"repoUrl":28,"updatedAt":2357},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2482,2483,2484,2485,2486],{"name":16,"slug":17,"type":14},{"name":2350,"slug":2351,"type":14},{"name":2353,"slug":2354,"type":14},{"name":2252,"slug":2253,"type":14},{"name":9,"slug":8,"type":14}]