[{"data":1,"prerenderedAt":849},["ShallowReactive",2],{"navigation":3,"\u002Fguides\u002Foperations\u002Fself-hosting":169,"\u002Fguides\u002Foperations\u002Fself-hosting-surround":844},[4,28,85,127,157],{"title":5,"path":6,"stem":7,"children":8,"icon":27},"Getting Started","\u002Fgetting-started","1.getting-started\u002F1.index",[9,12,17,22],{"title":10,"path":6,"stem":7,"icon":11},"Introduction","i-lucide-house",{"title":13,"path":14,"stem":15,"icon":16},"Installation","\u002Fgetting-started\u002Finstallation","1.getting-started\u002F2.installation","i-lucide-download",{"title":18,"path":19,"stem":20,"icon":21},"Quick start","\u002Fgetting-started\u002Fquick-start","1.getting-started\u002F3.quick-start","i-lucide-zap",{"title":23,"path":24,"stem":25,"icon":26},"Architecture","\u002Fgetting-started\u002Farchitecture","1.getting-started\u002F4.architecture","i-lucide-layers","i-lucide-rocket",{"title":29,"icon":30,"path":31,"stem":32,"children":33,"page":84},"Concepts","i-lucide-lightbulb","\u002Fconcepts","2.concepts",[34,39,44,49,54,59,64,69,74,79],{"title":35,"path":36,"stem":37,"icon":38},"Durable agents","\u002Fconcepts\u002Fdurable-agents","2.concepts\u002F1.durable-agents","i-lucide-infinity",{"title":40,"path":41,"stem":42,"icon":43},"Glossary","\u002Fconcepts\u002Fglossary","2.concepts\u002F10.glossary","i-lucide-book-a",{"title":45,"path":46,"stem":47,"icon":48},"Restate in five minutes","\u002Fconcepts\u002Frestate-primer","2.concepts\u002F2.restate-primer","i-lucide-cpu",{"title":50,"path":51,"stem":52,"icon":53},"Entities & addressing","\u002Fconcepts\u002Fentities-addressing","2.concepts\u002F3.entities-addressing","i-lucide-at-sign",{"title":55,"path":56,"stem":57,"icon":58},"Timelines & events","\u002Fconcepts\u002Ftimelines-events","2.concepts\u002F4.timelines-events","i-lucide-list-ordered",{"title":60,"path":61,"stem":62,"icon":63},"Projections & the catalog","\u002Fconcepts\u002Fprojections-catalog","2.concepts\u002F5.projections-catalog","i-lucide-database",{"title":65,"path":66,"stem":67,"icon":68},"Workspaces & execution","\u002Fconcepts\u002Fworkspaces","2.concepts\u002F6.workspaces","i-lucide-terminal",{"title":70,"path":71,"stem":72,"icon":73},"Harnesses","\u002Fconcepts\u002Fharnesses","2.concepts\u002F7.harnesses","i-lucide-plug",{"title":75,"path":76,"stem":77,"icon":78},"Multi-agent patterns","\u002Fconcepts\u002Fmulti-agent","2.concepts\u002F8.multi-agent","i-lucide-network",{"title":80,"path":81,"stem":82,"icon":83},"Lifecycle & control","\u002Fconcepts\u002Flifecycle","2.concepts\u002F9.lifecycle","i-lucide-sliders-horizontal",false,{"title":86,"icon":87,"path":88,"stem":89,"children":90,"page":84},"Guides","i-lucide-book-open","\u002Fguides","3.guides",[91,106],{"title":92,"icon":93,"path":94,"stem":95,"children":96,"page":84},"Agents","i-lucide-bot","\u002Fguides\u002Fagents","3.guides\u002F1.agents",[97,101],{"title":98,"path":99,"stem":100,"icon":93},"Building agents","\u002Fguides\u002Fagents\u002Fbuilding-agents","3.guides\u002F1.agents\u002F1.building-agents",{"title":102,"path":103,"stem":104,"icon":105},"Frontend integration","\u002Fguides\u002Fagents\u002Ffrontend-integration","3.guides\u002F1.agents\u002F2.frontend-integration","i-lucide-monitor",{"title":107,"icon":108,"path":109,"stem":110,"children":111,"page":84},"Operations","i-lucide-server-cog","\u002Fguides\u002Foperations","3.guides\u002F2.operations",[112,117,122],{"title":113,"path":114,"stem":115,"icon":116},"Self-hosting","\u002Fguides\u002Foperations\u002Fself-hosting","3.guides\u002F2.operations\u002F1.self-hosting","i-lucide-server",{"title":118,"path":119,"stem":120,"icon":121},"Auth & API keys","\u002Fguides\u002Foperations\u002Fauth-api-keys","3.guides\u002F2.operations\u002F2.auth-api-keys","i-lucide-key-round",{"title":123,"path":124,"stem":125,"icon":126},"Backup & restore","\u002Fguides\u002Foperations\u002Fbackup-restore","3.guides\u002F2.operations\u002F3.backup-restore","i-lucide-database-backup",{"title":128,"icon":129,"path":130,"stem":131,"children":132,"page":84},"Reference","i-lucide-book-marked","\u002Freference","4.reference",[133,138,143,148,153],{"title":134,"path":135,"stem":136,"icon":137},"Event schema","\u002Freference\u002Fevents","4.reference\u002F1.events","i-lucide-list-tree",{"title":139,"path":140,"stem":141,"icon":142},"Addressing","\u002Freference\u002Faddressing","4.reference\u002F2.addressing","i-lucide-map-pin",{"title":144,"path":145,"stem":146,"icon":147},"Gateway HTTP API","\u002Freference\u002Fgateway-api","4.reference\u002F3.gateway-api","i-lucide-webhook",{"title":149,"path":150,"stem":151,"icon":152},"CLI","\u002Freference\u002Fcli","4.reference\u002F4.cli","i-lucide-square-terminal",{"title":154,"path":155,"stem":156,"icon":83},"Configuration","\u002Freference\u002Fconfiguration","4.reference\u002F5.configuration",{"title":158,"path":159,"stem":160,"children":161,"icon":163},"Contributing","\u002Fcontributing","5.contributing\u002F1.index",[162,164],{"title":158,"path":159,"stem":160,"icon":163},"i-lucide-git-pull-request",{"title":165,"path":166,"stem":167,"icon":168},"The package map","\u002Fcontributing\u002Fpackage-map","5.contributing\u002F2.package-map","i-lucide-boxes",{"id":170,"title":113,"body":171,"description":837,"extension":838,"links":839,"meta":840,"navigation":841,"path":114,"seo":842,"stem":115,"__hash__":843},"docs\u002F3.guides\u002F2.operations\u002F1.self-hosting.md",{"type":172,"value":173,"toc":826},"minimark",[174,178,183,200,211,233,236,240,247,331,352,356,367,381,384,477,486,510,514,521,537,544,577,583,587,594,635,645,659,684,688,695,705,708,740,743,747,750,802,806,809,812,816,822],[175,176,177],"p",{},"teaspill self-hosts as a small Docker Compose stack of infrastructure services plus two services you deploy yourself. This guide gets that stack running, explains what each piece does, and covers the networking and security decisions you need to get right before production.",[179,180,182],"h2",{"id":181},"deployment-model","Deployment model",[175,184,185,186,190,191,194,195,199],{},"There are two halves to a teaspill deployment: the ",[187,188,189],"strong",{},"infrastructure stack"," you run from Compose, and the ",[187,192,193],{},"two planes you deploy"," — the process that runs your agents, and the executor that hosts their ",[196,197,198],"a",{"href":66},"workspaces",".",[201,202,207],"pre",{"className":203,"code":205,"language":206},[204],"language-text","   your app \u002F UI \u002F CLI\n          │\n          │  API key (single entrypoint)\n          ▼\n   ┌──────────────────────────────────────────┐\n   │  compose stack (infrastructure)          │\n   │                                          │\n   │   gateway ──► restate                    │\n   │      │     ├─► postgres  ◄── electric    │\n   │      │     └─► durable-streams           │\n   └──────┼───────────────────────────────────┘\n          │  Restate dials your registered URLs directly\n          ▼\n   ┌──────────────────────────────┐\n   │  agent-loop service(s)       │  ← you deploy these\n   │  executor + workspaces       │     (they register through the gateway)\n   └──────────────────────────────┘\n","text",[208,209,205],"code",{"__ignoreMap":210},"",[175,212,213,214,217,218,221,222,225,226,229,230,199],{},"The ",[196,215,216],{"href":14},"gateway"," is the single front door: your app, your UIs, and the CLI reach teaspill only through it, and the internal services are never exposed to external callers directly. Your ",[187,219,220],{},"agent-loop service"," — the process running your ",[208,223,224],{},"defineAgent"," definitions — and your ",[187,227,228],{},"executor"," register themselves with the coordinator through the gateway, and from then on the coordinator dials them directly on every ",[196,231,232],{"href":36},"wake",[175,234,235],{},"The two planes scale on different axes: agent-loop replicas scale with how many model conversations run at once; the executor fleet scales with workspace compute and disk demand.",[179,237,239],{"id":238},"the-five-services","The five services",[175,241,242,243,246],{},"The infrastructure stack is five containers, defined in ",[208,244,245],{},"docker-compose.yml",". Every image tag is pinned.",[248,249,250,263],"table",{},[251,252,253],"thead",{},[254,255,256,260],"tr",{},[257,258,259],"th",{},"Service",[257,261,262],{},"Role",[264,265,266,276,294,307,317],"tbody",{},[254,267,268,273],{},[269,270,271],"td",{},[208,272,216],{},[269,274,275],{},"The single entrypoint. Authenticates every request, then proxies to the internal services. Built from the teaspill gateway package.",[254,277,278,283],{},[269,279,280],{},[208,281,282],{},"restate",[269,284,285,286,289,290,293],{},"The coordination core — the durable-execution engine (",[196,287,288],{"href":46},"Restate",") that runs each agent's ",[196,291,292],{"href":36},"wakes"," and holds its live working state. Single-node.",[254,295,296,301],{},[269,297,298],{},[208,299,300],{},"postgres",[269,302,213,303,306],{},[196,304,305],{"href":61},"catalog"," store: the registry of every entity, plus the archive of record for archived agents. Also Electric's replication source; runs with logical replication enabled.",[254,308,309,314],{},[269,310,311],{},[208,312,313],{},"electric",[269,315,316],{},"Streams catalog rows to your UIs as live-updating subscriptions, fed by Postgres logical replication.",[254,318,319,324],{},[269,320,321],{},[208,322,323],{},"durable-streams",[269,325,326,327,330],{},"The history store — every ",[196,328,329],{"href":56},"timeline",", delta stream, and workspace-output stream, append-only and resumable. Runs in a crash-safe, fsync-on-append mode.",[175,332,333,334,337,338,337,341,337,344,347,348,351],{},"Persistent data lives in named Docker volumes (",[208,335,336],{},"postgres_data",", ",[208,339,340],{},"restate_data",[208,342,343],{},"durable_streams_data",[208,345,346],{},"electric_storage","). Stopping the stack keeps them; ",[208,349,350],{},"docker compose down -v"," destroys them.",[179,353,355],{"id":354},"configuring-the-stack","Configuring the stack",[175,357,358,359,362,363,366],{},"Every variable the Compose file reads has a working default baked in (",[208,360,361],{},"${VAR:-default}","), so an empty or missing ",[208,364,365],{},".env"," still boots the whole stack. Copy the example and edit only what you need:",[201,368,372],{"className":369,"code":370,"language":371,"meta":210,"style":210},"language-sh shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","cp .env.example .env\n","sh",[208,373,374],{"__ignoreMap":210},[375,376,379],"span",{"class":377,"line":378},"line",1,[375,380,370],{},[175,382,383],{},"The variables you are most likely to touch:",[248,385,386,399],{},[251,387,388],{},[254,389,390,393,396],{},[257,391,392],{},"Variable",[257,394,395],{},"Default",[257,397,398],{},"Meaning",[264,400,401,416,431,454],{},[254,402,403,408,413],{},[269,404,405],{},[208,406,407],{},"POSTGRES_PASSWORD",[269,409,410],{},[208,411,412],{},"teaspill",[269,414,415],{},"Catalog database password. Change it before exposing Postgres beyond localhost.",[254,417,418,423,428],{},[269,419,420],{},[208,421,422],{},"GATEWAY_PORT",[269,424,425],{},[208,426,427],{},"8787",[269,429,430],{},"Host port the gateway is published on. This is the one port your clients use.",[254,432,433,438,443],{},[269,434,435],{},[208,436,437],{},"ELECTRIC_INSECURE",[269,439,440],{},[208,441,442],{},"true",[269,444,445,446,449,450,453],{},"Dev-only. Skips shape-API auth. Set ",[208,447,448],{},"false"," (and provide ",[208,451,452],{},"ELECTRIC_SECRET",") before exposing Electric.",[254,455,456,468,474],{},[269,457,458,337,461,337,464,467],{},[208,459,460],{},"POSTGRES_PORT",[208,462,463],{},"RESTATE_INGRESS_PORT",[208,465,466],{},"DURABLE_STREAMS_PORT",", …",[269,469,470,471],{},"see ",[208,472,473],{},".env.example",[269,475,476],{},"Host-published debug ports for the internal services.",[175,478,479,480,482,483,199],{},"The gateway reads further environment directly for auth and CORS — those are covered in ",[196,481,118],{"href":119},". The complete per-service table is in the ",[196,484,485],{"href":155},"Configuration reference",[487,488,489],"note",{},[175,490,491,494,495,497,498,502,503,506,507,199],{},[208,492,493],{},"DATABASE_URL"," is not in ",[208,496,473],{},". Compose synthesizes it for the in-network services from the Postgres credentials. Anything you run ",[499,500,501],"em",{},"outside"," the Compose network — the CLI's ",[208,504,505],{},"keys"," command, local tests — must set it explicitly, e.g. ",[208,508,509],{},"postgresql:\u002F\u002Fteaspill:teaspill@localhost:5432\u002Fteaspill?sslmode=disable",[179,511,513],{"id":512},"running-the-stack","Running the stack",[175,515,516,517,520],{},"The primary way to run everything, including your own agent-loop and executor, is the ",[208,518,519],{},"teaspill dev"," command. It brings the stack up, waits for the gateway to be healthy, registers your local deployments with retry and backoff (avoiding the register-before-up race), then tails logs:",[201,522,524],{"className":369,"code":523,"language":371,"meta":210,"style":210},"export COMPOSE_FILE=docker-compose.yml:docker-compose.overlay.yml\nteaspill dev --deployment http:\u002F\u002Fagent-loop:9080 --deployment http:\u002F\u002Fexecutor:9081\n",[208,525,526,531],{"__ignoreMap":210},[375,527,528],{"class":377,"line":378},[375,529,530],{},"export COMPOSE_FILE=docker-compose.yml:docker-compose.overlay.yml\n",[375,532,534],{"class":377,"line":533},2,[375,535,536],{},"teaspill dev --deployment http:\u002F\u002Fagent-loop:9080 --deployment http:\u002F\u002Fexecutor:9081\n",[175,538,539,540,543],{},"The reference deployment ships as a Compose overlay — ",[208,541,542],{},"docker-compose.overlay.yml"," — that adds a working agent-loop and executor to the base stack. It is the canonical worked example: copy it to start your own deployment. To run it with plain Compose instead of the CLI:",[201,545,547],{"className":369,"code":546,"language":371,"meta":210,"style":210},"# Build the service bundles first, then bring up base + overlay.\npnpm -r build\npnpm --filter @teaspill\u002Fgateway bundle\npnpm --filter @teaspill\u002Freference-deployment bundle\ndocker compose -f docker-compose.yml -f docker-compose.overlay.yml up -d --build\n",[208,548,549,554,559,565,571],{"__ignoreMap":210},[375,550,551],{"class":377,"line":378},[375,552,553],{},"# Build the service bundles first, then bring up base + overlay.\n",[375,555,556],{"class":377,"line":533},[375,557,558],{},"pnpm -r build\n",[375,560,562],{"class":377,"line":561},3,[375,563,564],{},"pnpm --filter @teaspill\u002Fgateway bundle\n",[375,566,568],{"class":377,"line":567},4,[375,569,570],{},"pnpm --filter @teaspill\u002Freference-deployment bundle\n",[375,572,574],{"class":377,"line":573},5,[375,575,576],{},"docker compose -f docker-compose.yml -f docker-compose.overlay.yml up -d --build\n",[175,578,579,582],{},[208,580,581],{},"teaspill dev --watch"," re-registers your deployment whenever its built output changes, so you can iterate on an agent without restarting the stack.",[179,584,586],{"id":585},"networking-rules","Networking rules",[175,588,589,590,593],{},"The coordinator dials the URL you register ",[187,591,592],{},"directly, from inside its own container",", on every invocation — that traffic never passes back through the gateway. This makes the registration URL the single thing operators get wrong, so it has one rule:",[595,596,597,603,626],"warning",{},[175,598,599,600,199],{},"Register a service by the address the coordinator can reach it at, from inside its container — never ",[208,601,602],{},"localhost",[604,605,606,617],"ul",{},[607,608,609,610,613,614,199],"li",{},"A service ",[187,611,612],{},"inside the Compose network"," registers its service name: ",[208,615,616],{},"http:\u002F\u002Fagent-loop:9080",[607,618,609,619,622,623,199],{},[187,620,621],{},"running on your host"," (the common case during local dev) registers ",[208,624,625],{},"http:\u002F\u002Fhost.docker.internal:9080",[175,627,628,631,632,634],{},[208,629,630],{},"http:\u002F\u002Flocalhost:9080"," will register successfully and then fail on the first wake, because ",[208,633,602],{}," inside the coordinator's container is the container itself, not your host.",[175,636,637,638,641,642,644],{},"The Compose file already adds the ",[208,639,640],{},"host.docker.internal"," mapping to the coordinator so this works on plain Docker Engine (Linux) as well as Docker Desktop. ",[208,643,519],{}," and the reference deployment default their registration URLs correctly for their context.",[646,647,648],"accordion",{},[649,650,653],"accordion-item",{"icon":651,"label":652},"i-lucide-microscope","Why localhost registers fine and then fails silently",[175,654,655,656,658],{},"Registration and invocation are two different network paths. When a service registers, the coordinator only records the URL — it does not dial it, so any syntactically valid URL is accepted. The failure surfaces later: on the first wake, the coordinator opens a connection to that URL from inside its own container's network namespace. ",[208,657,602],{}," there resolves to the coordinator container, which is not running your agent, so the call fails. teaspill deliberately does no URL rewriting — a \"helpful\" rewrite of loopback addresses is a well-known source of confusing, environment-dependent bugs, so the rule is explicit instead.",[175,660,661,662,337,665,337,668,671,672,675,676,679,680,683],{},"The internal services also publish their ports to your host (",[208,663,664],{},"5432",[208,666,667],{},"8080",[208,669,670],{},"4437",", and so on) so you can ",[208,673,674],{},"psql"," or ",[208,677,678],{},"curl"," them while debugging. Treat those as a ",[187,681,682],{},"localhost-only debugging surface"," — the production access path is the gateway, and nothing you build should depend on the internal ports being reachable from outside the host.",[179,685,687],{"id":686},"the-executor-and-the-docker-socket","The executor and the Docker socket",[175,689,690,691,694],{},"The default executor adapter runs each workspace in its own Docker container, and it gets access to Docker by ",[187,692,693],{},"mounting the host's Docker socket"," into the executor.",[696,697,698],"caution",{},[175,699,700,701,704],{},"Holding the Docker socket is ",[187,702,703],{},"root-equivalent access to the host machine"," — anything that can talk to the socket can start a container that mounts the whole host filesystem. Keep the executor internal, behind the gateway, running only your own agents. Do not expose it to untrusted callers, and do not use the socket-mount adapter to run hostile code. For multi-tenant or untrusted workloads, move to a boundary that does not hand out host root: rootless Docker-in-Docker, or a remote-VM adapter.",[175,706,707],{},"The workspace containers themselves are hardened, independently of the socket:",[604,709,710,720,737],{},[607,711,712,715,716,719],{},[187,713,714],{},"The default workspace image is digest-pinned"," (",[208,717,718],{},"alpine:3.20@sha256:…",") so every executor host materializes a byte-identical base. Pin your own images by digest too.",[607,721,722,725,726,729,730,733,734,736],{},[187,723,724],{},"Network isolation is per-workspace."," Choose ",[208,727,728],{},"none"," (loopback only), ",[208,731,732],{},"bridge"," (the default — egress for tool calls), or a named network. Set ",[208,735,728],{}," when running code that must not reach the network.",[607,738,739],{},"Containers run with dropped capabilities, no new privileges, and memory, CPU, and process-count limits.",[175,741,742],{},"None of this hardens the socket holder — it hardens the workspaces. The executor process itself must be treated as trusted-as-root.",[179,744,746],{"id":745},"production-checklist","Production checklist",[175,748,749],{},"Before you put a deployment in front of anyone:",[604,751,752,760,771,779,790,796],{},[607,753,754,759],{},[187,755,756,757],{},"Change ",[208,758,407],{}," from the default.",[607,761,762,768,769,199],{},[187,763,764,765],{},"Set ",[208,766,767],{},"ELECTRIC_INSECURE=false"," and provide ",[208,770,452],{},[607,772,773,776,777,199],{},[187,774,775],{},"Mint real API keys"," and drop the dev bootstrap key — see ",[196,778,118],{"href":119},[607,780,781,786,787,199],{},[187,782,764,783],{},[208,784,785],{},"GATEWAY_JWT_SECRET"," if you want browsers to read streams directly with ",[196,788,789],{"href":119},"read tokens",[607,791,792,795],{},[187,793,794],{},"Terminate TLS in front of the gateway"," (a reverse proxy or load balancer). The gateway speaks plain HTTP; put encryption at the edge.",[607,797,798,801],{},[187,799,800],{},"Keep the internal service ports unpublished"," on any host reachable from outside.",[179,803,805],{"id":804},"scaling","Scaling",[175,807,808],{},"The two planes you deploy scale independently and horizontally: add agent-loop replicas for more concurrent model conversations, add executor hosts for more workspace capacity. The coordinator load-balances wakes across registered replicas of the same deployment.",[175,810,811],{},"The infrastructure services run single-node in this stack — that is the supported self-host shape, and it is enough for real workloads. In particular Restate runs as a single node, which is the deployment shape its own backup guidance is written for.",[179,813,815],{"id":814},"next-steps","Next steps",[175,817,818,819,821],{},"Once you are running, plan for recovery: ",[196,820,123],{"href":124}," covers what each store holds and which restore combinations come back cleanly.",[823,824,825],"style",{},"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);}",{"title":210,"searchDepth":378,"depth":533,"links":827},[828,829,830,831,832,833,834,835,836],{"id":181,"depth":533,"text":182},{"id":238,"depth":533,"text":239},{"id":354,"depth":533,"text":355},{"id":512,"depth":533,"text":513},{"id":585,"depth":533,"text":586},{"id":686,"depth":533,"text":687},{"id":745,"depth":533,"text":746},{"id":804,"depth":533,"text":805},{"id":814,"depth":533,"text":815},"Run the teaspill stack yourself — the five infrastructure services, your two deployed planes, and the one networking rule that bites everyone.","md",null,{},{"icon":116},{"title":113,"description":837},"kHA4sCAmXZJWbAGcUWnufehgepYFPspH9U_kNCw0qQM",[845,847],{"title":102,"path":103,"stem":104,"description":846,"icon":105,"children":-1},"Spawn agents, read and live-follow their timelines, subscribe to the catalog, and mint browser read tokens with the frontend SDK.",{"title":118,"path":119,"stem":120,"description":848,"icon":121,"children":-1},"The server-side API key that authorizes everything, the optional read token that lets browsers read streams directly, and why authorization is yours to own.",1784473426417]