From 070c6c0a91ab83ae4208ffed668832032ecb4ac7 Mon Sep 17 00:00:00 2001 From: -Puter <22245429+puterhimself@users.noreply.github.com> Date: Wed, 22 Jul 2026 00:21:04 +0530 Subject: [PATCH] skills and photots --- .agents/skills/live-cursors/BASE_SKILL.md | 537 +++++++ .agents/skills/live-cursors/SKILL.md | 282 ++++ .agents/skills/live-cursors/index.json | 694 +++++++++ .agents/skills/live-cursors/openapi.json | 1311 +++++++++++++++++ .../reference/actors/access-control.md | 48 + .../live-cursors/reference/actors/actions.md | 98 ++ .../reference/actors/actor-runtime-socket.md | 62 + .../reference/actors/appearance.md | 50 + .../reference/actors/authentication.md | 105 ++ .../actors/communicating-between-actors.md | 42 + .../reference/actors/connections.md | 119 ++ .../reference/actors/debugging.md | 644 ++++++++ .../reference/actors/design-patterns.md | 163 ++ .../live-cursors/reference/actors/destroy.md | 71 + .../live-cursors/reference/actors/errors.md | 81 + .../live-cursors/reference/actors/events.md | 143 ++ .../actors/fetch-and-websocket-handler.md | 10 + .../reference/actors/helper-types.md | 10 + .../live-cursors/reference/actors/http-api.md | 10 + .../live-cursors/reference/actors/input.md | 51 + .../reference/actors/inspector-tabs.md | 251 ++++ .../live-cursors/reference/actors/keys.md | 51 + .../live-cursors/reference/actors/kv.md | 44 + .../reference/actors/lifecycle.md | 388 +++++ .../live-cursors/reference/actors/limits.md | 169 +++ .../live-cursors/reference/actors/metadata.md | 39 + .../live-cursors/reference/actors/queues.md | 120 ++ .../reference/actors/quickstart/backend.md | 64 + .../reference/actors/quickstart/cloudflare.md | 165 +++ .../reference/actors/quickstart/effect.md | 239 +++ .../reference/actors/quickstart/next-js.md | 117 ++ .../reference/actors/quickstart/react.md | 82 ++ .../reference/actors/quickstart/rust.md | 249 ++++ .../reference/actors/quickstart/supabase.md | 109 ++ .../reference/actors/request-handler.md | 79 + .../live-cursors/reference/actors/scaling.md | 10 + .../live-cursors/reference/actors/schedule.md | 190 +++ .../actors/sharing-and-joining-state.md | 10 + .../reference/actors/sqlite-drizzle.md | 248 ++++ .../live-cursors/reference/actors/sqlite.md | 127 ++ .../live-cursors/reference/actors/state.md | 270 ++++ .../live-cursors/reference/actors/statuses.md | 36 + .../live-cursors/reference/actors/testing.md | 51 + .../reference/actors/troubleshooting.md | 154 ++ .../live-cursors/reference/actors/types.md | 46 + .../live-cursors/reference/actors/versions.md | 241 +++ .../reference/actors/websocket-handler.md | 201 +++ .../reference/actors/workflows.md | 228 +++ .agents/skills/live-cursors/reference/cli.md | 103 ++ .../reference/clients/javascript.md | 120 ++ .../live-cursors/reference/clients/react.md | 88 ++ .../live-cursors/reference/clients/rust.md | 367 +++++ .../live-cursors/reference/clients/swift.md | 454 ++++++ .../live-cursors/reference/clients/swiftui.md | 359 +++++ .../reference/cookbook/ai-agent.md | 127 ++ .../reference/cookbook/chat-room.md | 114 ++ .../cookbook/collaborative-text-editor.md | 157 ++ .../reference/cookbook/cron-jobs.md | 69 + .../reference/cookbook/live-cursors.md | 152 ++ .../reference/cookbook/multiplayer-game.md | 713 +++++++++ .../reference/cookbook/per-tenant-database.md | 119 ++ .../reference/cookbook/vpc-air-gapped.md | 136 ++ .../live-cursors/reference/deploy/aws-ecs.md | 13 + .../reference/deploy/aws-lambda.md | 10 + .../reference/deploy/cloudflare.md | 53 + .../reference/deploy/container-runner.md | 95 ++ .../reference/deploy/freestyle.md | 136 ++ .../reference/deploy/gcp-cloud-run.md | 74 + .../live-cursors/reference/deploy/hetzner.md | 10 + .../reference/deploy/kubernetes.md | 121 ++ .../live-cursors/reference/deploy/railway.md | 45 + .../reference/deploy/rivet-compute.md | 144 ++ .../live-cursors/reference/deploy/supabase.md | 50 + .../live-cursors/reference/deploy/vercel.md | 138 ++ .../reference/deploy/vm-and-bare-metal.md | 96 ++ .../reference/general/actor-configuration.md | 19 + .../reference/general/architecture.md | 452 ++++++ .../live-cursors/reference/general/cors.md | 18 + .../reference/general/docs-for-llms.md | 51 + .../live-cursors/reference/general/edge.md | 24 + .../reference/general/endpoints.md | 98 ++ .../general/environment-variables.md | 87 ++ .../reference/general/http-server.md | 96 ++ .../live-cursors/reference/general/logging.md | 74 + .../reference/general/pool-configuration.md | 119 ++ .../reference/general/production-checklist.md | 98 ++ .../general/registry-configuration.md | 32 + .../reference/general/runtime-modes.md | 99 ++ .../reference/general/wasm-vs-native-sdk.md | 79 + .../reference/self-hosting/configuration.md | 74 + .../reference/self-hosting/docker-compose.md | 188 +++ .../self-hosting/docker-container.md | 150 ++ .../reference/self-hosting/filesystem.md | 47 + .../reference/self-hosting/foundationdb.md | 35 + .../reference/self-hosting/install.md | 32 + .../reference/self-hosting/kubernetes.md | 236 +++ .../reference/self-hosting/multi-region.md | 10 + .../reference/self-hosting/postgres.md | 192 +++ .../self-hosting/production-checklist.md | 53 + .../reference/self-hosting/railway.md | 99 ++ .../reference/self-hosting/render.md | 131 ++ .../reference/self-hosting/tls.md | 22 + .agents/skills/multiplayer-game/BASE_SKILL.md | 537 +++++++ .agents/skills/multiplayer-game/SKILL.md | 842 +++++++++++ .agents/skills/multiplayer-game/index.json | 694 +++++++++ .agents/skills/multiplayer-game/openapi.json | 1311 +++++++++++++++++ .../reference/actors/access-control.md | 48 + .../reference/actors/actions.md | 98 ++ .../reference/actors/actor-runtime-socket.md | 62 + .../reference/actors/appearance.md | 50 + .../reference/actors/authentication.md | 105 ++ .../actors/communicating-between-actors.md | 42 + .../reference/actors/connections.md | 119 ++ .../reference/actors/debugging.md | 644 ++++++++ .../reference/actors/design-patterns.md | 163 ++ .../reference/actors/destroy.md | 71 + .../reference/actors/errors.md | 81 + .../reference/actors/events.md | 143 ++ .../actors/fetch-and-websocket-handler.md | 10 + .../reference/actors/helper-types.md | 10 + .../reference/actors/http-api.md | 10 + .../reference/actors/input.md | 51 + .../reference/actors/inspector-tabs.md | 251 ++++ .../multiplayer-game/reference/actors/keys.md | 51 + .../multiplayer-game/reference/actors/kv.md | 44 + .../reference/actors/lifecycle.md | 388 +++++ .../reference/actors/limits.md | 169 +++ .../reference/actors/metadata.md | 39 + .../reference/actors/queues.md | 120 ++ .../reference/actors/quickstart/backend.md | 64 + .../reference/actors/quickstart/cloudflare.md | 165 +++ .../reference/actors/quickstart/effect.md | 239 +++ .../reference/actors/quickstart/next-js.md | 117 ++ .../reference/actors/quickstart/react.md | 82 ++ .../reference/actors/quickstart/rust.md | 249 ++++ .../reference/actors/quickstart/supabase.md | 109 ++ .../reference/actors/request-handler.md | 79 + .../reference/actors/scaling.md | 10 + .../reference/actors/schedule.md | 190 +++ .../actors/sharing-and-joining-state.md | 10 + .../reference/actors/sqlite-drizzle.md | 248 ++++ .../reference/actors/sqlite.md | 127 ++ .../reference/actors/state.md | 270 ++++ .../reference/actors/statuses.md | 36 + .../reference/actors/testing.md | 51 + .../reference/actors/troubleshooting.md | 154 ++ .../reference/actors/types.md | 46 + .../reference/actors/versions.md | 241 +++ .../reference/actors/websocket-handler.md | 201 +++ .../reference/actors/workflows.md | 228 +++ .../skills/multiplayer-game/reference/cli.md | 103 ++ .../reference/clients/javascript.md | 120 ++ .../reference/clients/react.md | 88 ++ .../reference/clients/rust.md | 367 +++++ .../reference/clients/swift.md | 454 ++++++ .../reference/clients/swiftui.md | 359 +++++ .../reference/cookbook/ai-agent.md | 127 ++ .../reference/cookbook/chat-room.md | 114 ++ .../cookbook/collaborative-text-editor.md | 157 ++ .../reference/cookbook/cron-jobs.md | 69 + .../reference/cookbook/live-cursors.md | 152 ++ .../reference/cookbook/multiplayer-game.md | 713 +++++++++ .../reference/cookbook/per-tenant-database.md | 119 ++ .../reference/cookbook/vpc-air-gapped.md | 136 ++ .../reference/deploy/aws-ecs.md | 13 + .../reference/deploy/aws-lambda.md | 10 + .../reference/deploy/cloudflare.md | 53 + .../reference/deploy/container-runner.md | 95 ++ .../reference/deploy/freestyle.md | 136 ++ .../reference/deploy/gcp-cloud-run.md | 74 + .../reference/deploy/hetzner.md | 10 + .../reference/deploy/kubernetes.md | 121 ++ .../reference/deploy/railway.md | 45 + .../reference/deploy/rivet-compute.md | 144 ++ .../reference/deploy/supabase.md | 50 + .../reference/deploy/vercel.md | 138 ++ .../reference/deploy/vm-and-bare-metal.md | 96 ++ .../reference/general/actor-configuration.md | 19 + .../reference/general/architecture.md | 452 ++++++ .../reference/general/cors.md | 18 + .../reference/general/docs-for-llms.md | 51 + .../reference/general/edge.md | 24 + .../reference/general/endpoints.md | 98 ++ .../general/environment-variables.md | 87 ++ .../reference/general/http-server.md | 96 ++ .../reference/general/logging.md | 74 + .../reference/general/pool-configuration.md | 119 ++ .../reference/general/production-checklist.md | 98 ++ .../general/registry-configuration.md | 32 + .../reference/general/runtime-modes.md | 99 ++ .../reference/general/wasm-vs-native-sdk.md | 79 + .../reference/self-hosting/configuration.md | 74 + .../reference/self-hosting/docker-compose.md | 188 +++ .../self-hosting/docker-container.md | 150 ++ .../reference/self-hosting/filesystem.md | 47 + .../reference/self-hosting/foundationdb.md | 35 + .../reference/self-hosting/install.md | 32 + .../reference/self-hosting/kubernetes.md | 236 +++ .../reference/self-hosting/multi-region.md | 10 + .../reference/self-hosting/postgres.md | 192 +++ .../self-hosting/production-checklist.md | 53 + .../reference/self-hosting/railway.md | 99 ++ .../reference/self-hosting/render.md | 131 ++ .../reference/self-hosting/tls.md | 22 + .../skills/per-tenant-database/BASE_SKILL.md | 537 +++++++ .agents/skills/per-tenant-database/SKILL.md | 248 ++++ .agents/skills/per-tenant-database/index.json | 694 +++++++++ .../skills/per-tenant-database/openapi.json | 1311 +++++++++++++++++ .../reference/actors/access-control.md | 48 + .../reference/actors/actions.md | 98 ++ .../reference/actors/actor-runtime-socket.md | 62 + .../reference/actors/appearance.md | 50 + .../reference/actors/authentication.md | 105 ++ .../actors/communicating-between-actors.md | 42 + .../reference/actors/connections.md | 119 ++ .../reference/actors/debugging.md | 644 ++++++++ .../reference/actors/design-patterns.md | 163 ++ .../reference/actors/destroy.md | 71 + .../reference/actors/errors.md | 81 + .../reference/actors/events.md | 143 ++ .../actors/fetch-and-websocket-handler.md | 10 + .../reference/actors/helper-types.md | 10 + .../reference/actors/http-api.md | 10 + .../reference/actors/input.md | 51 + .../reference/actors/inspector-tabs.md | 251 ++++ .../reference/actors/keys.md | 51 + .../reference/actors/kv.md | 44 + .../reference/actors/lifecycle.md | 388 +++++ .../reference/actors/limits.md | 169 +++ .../reference/actors/metadata.md | 39 + .../reference/actors/queues.md | 120 ++ .../reference/actors/quickstart/backend.md | 64 + .../reference/actors/quickstart/cloudflare.md | 165 +++ .../reference/actors/quickstart/effect.md | 239 +++ .../reference/actors/quickstart/next-js.md | 117 ++ .../reference/actors/quickstart/react.md | 82 ++ .../reference/actors/quickstart/rust.md | 249 ++++ .../reference/actors/quickstart/supabase.md | 109 ++ .../reference/actors/request-handler.md | 79 + .../reference/actors/scaling.md | 10 + .../reference/actors/schedule.md | 190 +++ .../actors/sharing-and-joining-state.md | 10 + .../reference/actors/sqlite-drizzle.md | 248 ++++ .../reference/actors/sqlite.md | 127 ++ .../reference/actors/state.md | 270 ++++ .../reference/actors/statuses.md | 36 + .../reference/actors/testing.md | 51 + .../reference/actors/troubleshooting.md | 154 ++ .../reference/actors/types.md | 46 + .../reference/actors/versions.md | 241 +++ .../reference/actors/websocket-handler.md | 201 +++ .../reference/actors/workflows.md | 228 +++ .../per-tenant-database/reference/cli.md | 103 ++ .../reference/clients/javascript.md | 120 ++ .../reference/clients/react.md | 88 ++ .../reference/clients/rust.md | 367 +++++ .../reference/clients/swift.md | 454 ++++++ .../reference/clients/swiftui.md | 359 +++++ .../reference/cookbook/ai-agent.md | 127 ++ .../reference/cookbook/chat-room.md | 114 ++ .../cookbook/collaborative-text-editor.md | 157 ++ .../reference/cookbook/cron-jobs.md | 69 + .../reference/cookbook/live-cursors.md | 152 ++ .../reference/cookbook/multiplayer-game.md | 713 +++++++++ .../reference/cookbook/per-tenant-database.md | 119 ++ .../reference/cookbook/vpc-air-gapped.md | 136 ++ .../reference/deploy/aws-ecs.md | 13 + .../reference/deploy/aws-lambda.md | 10 + .../reference/deploy/cloudflare.md | 53 + .../reference/deploy/container-runner.md | 95 ++ .../reference/deploy/freestyle.md | 136 ++ .../reference/deploy/gcp-cloud-run.md | 74 + .../reference/deploy/hetzner.md | 10 + .../reference/deploy/kubernetes.md | 121 ++ .../reference/deploy/railway.md | 45 + .../reference/deploy/rivet-compute.md | 144 ++ .../reference/deploy/supabase.md | 50 + .../reference/deploy/vercel.md | 138 ++ .../reference/deploy/vm-and-bare-metal.md | 96 ++ .../reference/general/actor-configuration.md | 19 + .../reference/general/architecture.md | 452 ++++++ .../reference/general/cors.md | 18 + .../reference/general/docs-for-llms.md | 51 + .../reference/general/edge.md | 24 + .../reference/general/endpoints.md | 98 ++ .../general/environment-variables.md | 87 ++ .../reference/general/http-server.md | 96 ++ .../reference/general/logging.md | 74 + .../reference/general/pool-configuration.md | 119 ++ .../reference/general/production-checklist.md | 98 ++ .../general/registry-configuration.md | 32 + .../reference/general/runtime-modes.md | 99 ++ .../reference/general/wasm-vs-native-sdk.md | 79 + .../reference/self-hosting/configuration.md | 74 + .../reference/self-hosting/docker-compose.md | 188 +++ .../self-hosting/docker-container.md | 150 ++ .../reference/self-hosting/filesystem.md | 47 + .../reference/self-hosting/foundationdb.md | 35 + .../reference/self-hosting/install.md | 32 + .../reference/self-hosting/kubernetes.md | 236 +++ .../reference/self-hosting/multi-region.md | 10 + .../reference/self-hosting/postgres.md | 192 +++ .../self-hosting/production-checklist.md | 53 + .../reference/self-hosting/railway.md | 99 ++ .../reference/self-hosting/render.md | 131 ++ .../reference/self-hosting/tls.md | 22 + apps/web/design/zopu-audit-expanded.png | Bin 0 -> 176350 bytes apps/web/design/zopu-audit-stack.png | Bin 0 -> 166100 bytes apps/web/design/zopu-chat-preview.png | Bin 0 -> 77909 bytes apps/web/design/zopu-chat.fig | Bin 0 -> 48954 bytes .../zopu-core-product-experience-final.png | Bin 0 -> 244151 bytes .../zopu-core-product-experience-polish-1.png | Bin 0 -> 127613 bytes .../zopu-core-product-experience-polish-2.png | Bin 0 -> 226936 bytes .../design/zopu-core-product-experience.png | Bin 0 -> 128629 bytes apps/web/design/zopu-design-desktop.png | Bin 0 -> 105394 bytes apps/web/design/zopu-design-foundations.png | Bin 0 -> 183063 bytes apps/web/design/zopu-design-mobile.png | Bin 0 -> 41429 bytes apps/web/design/zopu-design-primitives.png | Bin 0 -> 67088 bytes apps/web/design/zopu-design.fig | Bin 0 -> 441520 bytes .../web/design/zopu-mobile-character-pass.png | Bin 0 -> 149482 bytes .../design/zopu-mobile-expanded-work-unit.png | Bin 0 -> 176203 bytes .../zopu-mobile-home-expanded-in-place.png | Bin 0 -> 171762 bytes .../zopu-mobile-stacked-before-polish.png | Bin 0 -> 120615 bytes .../zopu-mobile-stacked-final-check.png | Bin 0 -> 139078 bytes apps/web/design/zopu-mobile-stacked-final.png | Bin 0 -> 139204 bytes ...obile-stacked-work-units-before-polish.png | Bin 0 -> 120615 bytes .../design/zopu-mobile-stacked-work-units.png | Bin 0 -> 122698 bytes .../zopu-mobile-vertical-work-stack.png | Bin 0 -> 162678 bytes .../design/zopu-mobile-work-units-home.png | Bin 0 -> 190362 bytes .../zopu-safari-oauth-storyboard-final.png | Bin 0 -> 902588 bytes .../design/zopu-safari-oauth-storyboard.png | Bin 0 -> 900966 bytes .../design/zopu-task-card-system-options.png | Bin 0 -> 567697 bytes apps/web/design/zopu-work-object-system.png | Bin 0 -> 777182 bytes .../design/zopu-work-unit-lifecycle-final.png | Bin 0 -> 220825 bytes .../zopu-work-unit-lifecycle-polish-1.png | Bin 0 -> 147981 bytes .../zopu-work-unit-lifecycle-polish-2.png | Bin 0 -> 200469 bytes apps/web/design/zopu-work-unit-lifecycle.png | Bin 0 -> 147982 bytes 337 files changed, 46087 insertions(+) create mode 100644 .agents/skills/live-cursors/BASE_SKILL.md create mode 100644 .agents/skills/live-cursors/SKILL.md create mode 100644 .agents/skills/live-cursors/index.json create mode 100644 .agents/skills/live-cursors/openapi.json create mode 100644 .agents/skills/live-cursors/reference/actors/access-control.md create mode 100644 .agents/skills/live-cursors/reference/actors/actions.md create mode 100644 .agents/skills/live-cursors/reference/actors/actor-runtime-socket.md create mode 100644 .agents/skills/live-cursors/reference/actors/appearance.md create mode 100644 .agents/skills/live-cursors/reference/actors/authentication.md create mode 100644 .agents/skills/live-cursors/reference/actors/communicating-between-actors.md create mode 100644 .agents/skills/live-cursors/reference/actors/connections.md create mode 100644 .agents/skills/live-cursors/reference/actors/debugging.md create mode 100644 .agents/skills/live-cursors/reference/actors/design-patterns.md create mode 100644 .agents/skills/live-cursors/reference/actors/destroy.md create mode 100644 .agents/skills/live-cursors/reference/actors/errors.md create mode 100644 .agents/skills/live-cursors/reference/actors/events.md create mode 100644 .agents/skills/live-cursors/reference/actors/fetch-and-websocket-handler.md create mode 100644 .agents/skills/live-cursors/reference/actors/helper-types.md create mode 100644 .agents/skills/live-cursors/reference/actors/http-api.md create mode 100644 .agents/skills/live-cursors/reference/actors/input.md create mode 100644 .agents/skills/live-cursors/reference/actors/inspector-tabs.md create mode 100644 .agents/skills/live-cursors/reference/actors/keys.md create mode 100644 .agents/skills/live-cursors/reference/actors/kv.md create mode 100644 .agents/skills/live-cursors/reference/actors/lifecycle.md create mode 100644 .agents/skills/live-cursors/reference/actors/limits.md create mode 100644 .agents/skills/live-cursors/reference/actors/metadata.md create mode 100644 .agents/skills/live-cursors/reference/actors/queues.md create mode 100644 .agents/skills/live-cursors/reference/actors/quickstart/backend.md create mode 100644 .agents/skills/live-cursors/reference/actors/quickstart/cloudflare.md create mode 100644 .agents/skills/live-cursors/reference/actors/quickstart/effect.md create mode 100644 .agents/skills/live-cursors/reference/actors/quickstart/next-js.md create mode 100644 .agents/skills/live-cursors/reference/actors/quickstart/react.md create mode 100644 .agents/skills/live-cursors/reference/actors/quickstart/rust.md create mode 100644 .agents/skills/live-cursors/reference/actors/quickstart/supabase.md create mode 100644 .agents/skills/live-cursors/reference/actors/request-handler.md create mode 100644 .agents/skills/live-cursors/reference/actors/scaling.md create mode 100644 .agents/skills/live-cursors/reference/actors/schedule.md create mode 100644 .agents/skills/live-cursors/reference/actors/sharing-and-joining-state.md create mode 100644 .agents/skills/live-cursors/reference/actors/sqlite-drizzle.md create mode 100644 .agents/skills/live-cursors/reference/actors/sqlite.md create mode 100644 .agents/skills/live-cursors/reference/actors/state.md create mode 100644 .agents/skills/live-cursors/reference/actors/statuses.md create mode 100644 .agents/skills/live-cursors/reference/actors/testing.md create mode 100644 .agents/skills/live-cursors/reference/actors/troubleshooting.md create mode 100644 .agents/skills/live-cursors/reference/actors/types.md create mode 100644 .agents/skills/live-cursors/reference/actors/versions.md create mode 100644 .agents/skills/live-cursors/reference/actors/websocket-handler.md create mode 100644 .agents/skills/live-cursors/reference/actors/workflows.md create mode 100644 .agents/skills/live-cursors/reference/cli.md create mode 100644 .agents/skills/live-cursors/reference/clients/javascript.md create mode 100644 .agents/skills/live-cursors/reference/clients/react.md create mode 100644 .agents/skills/live-cursors/reference/clients/rust.md create mode 100644 .agents/skills/live-cursors/reference/clients/swift.md create mode 100644 .agents/skills/live-cursors/reference/clients/swiftui.md create mode 100644 .agents/skills/live-cursors/reference/cookbook/ai-agent.md create mode 100644 .agents/skills/live-cursors/reference/cookbook/chat-room.md create mode 100644 .agents/skills/live-cursors/reference/cookbook/collaborative-text-editor.md create mode 100644 .agents/skills/live-cursors/reference/cookbook/cron-jobs.md create mode 100644 .agents/skills/live-cursors/reference/cookbook/live-cursors.md create mode 100644 .agents/skills/live-cursors/reference/cookbook/multiplayer-game.md create mode 100644 .agents/skills/live-cursors/reference/cookbook/per-tenant-database.md create mode 100644 .agents/skills/live-cursors/reference/cookbook/vpc-air-gapped.md create mode 100644 .agents/skills/live-cursors/reference/deploy/aws-ecs.md create mode 100644 .agents/skills/live-cursors/reference/deploy/aws-lambda.md create mode 100644 .agents/skills/live-cursors/reference/deploy/cloudflare.md create mode 100644 .agents/skills/live-cursors/reference/deploy/container-runner.md create mode 100644 .agents/skills/live-cursors/reference/deploy/freestyle.md create mode 100644 .agents/skills/live-cursors/reference/deploy/gcp-cloud-run.md create mode 100644 .agents/skills/live-cursors/reference/deploy/hetzner.md create mode 100644 .agents/skills/live-cursors/reference/deploy/kubernetes.md create mode 100644 .agents/skills/live-cursors/reference/deploy/railway.md create mode 100644 .agents/skills/live-cursors/reference/deploy/rivet-compute.md create mode 100644 .agents/skills/live-cursors/reference/deploy/supabase.md create mode 100644 .agents/skills/live-cursors/reference/deploy/vercel.md create mode 100644 .agents/skills/live-cursors/reference/deploy/vm-and-bare-metal.md create mode 100644 .agents/skills/live-cursors/reference/general/actor-configuration.md create mode 100644 .agents/skills/live-cursors/reference/general/architecture.md create mode 100644 .agents/skills/live-cursors/reference/general/cors.md create mode 100644 .agents/skills/live-cursors/reference/general/docs-for-llms.md create mode 100644 .agents/skills/live-cursors/reference/general/edge.md create mode 100644 .agents/skills/live-cursors/reference/general/endpoints.md create mode 100644 .agents/skills/live-cursors/reference/general/environment-variables.md create mode 100644 .agents/skills/live-cursors/reference/general/http-server.md create mode 100644 .agents/skills/live-cursors/reference/general/logging.md create mode 100644 .agents/skills/live-cursors/reference/general/pool-configuration.md create mode 100644 .agents/skills/live-cursors/reference/general/production-checklist.md create mode 100644 .agents/skills/live-cursors/reference/general/registry-configuration.md create mode 100644 .agents/skills/live-cursors/reference/general/runtime-modes.md create mode 100644 .agents/skills/live-cursors/reference/general/wasm-vs-native-sdk.md create mode 100644 .agents/skills/live-cursors/reference/self-hosting/configuration.md create mode 100644 .agents/skills/live-cursors/reference/self-hosting/docker-compose.md create mode 100644 .agents/skills/live-cursors/reference/self-hosting/docker-container.md create mode 100644 .agents/skills/live-cursors/reference/self-hosting/filesystem.md create mode 100644 .agents/skills/live-cursors/reference/self-hosting/foundationdb.md create mode 100644 .agents/skills/live-cursors/reference/self-hosting/install.md create mode 100644 .agents/skills/live-cursors/reference/self-hosting/kubernetes.md create mode 100644 .agents/skills/live-cursors/reference/self-hosting/multi-region.md create mode 100644 .agents/skills/live-cursors/reference/self-hosting/postgres.md create mode 100644 .agents/skills/live-cursors/reference/self-hosting/production-checklist.md create mode 100644 .agents/skills/live-cursors/reference/self-hosting/railway.md create mode 100644 .agents/skills/live-cursors/reference/self-hosting/render.md create mode 100644 .agents/skills/live-cursors/reference/self-hosting/tls.md create mode 100644 .agents/skills/multiplayer-game/BASE_SKILL.md create mode 100644 .agents/skills/multiplayer-game/SKILL.md create mode 100644 .agents/skills/multiplayer-game/index.json create mode 100644 .agents/skills/multiplayer-game/openapi.json create mode 100644 .agents/skills/multiplayer-game/reference/actors/access-control.md create mode 100644 .agents/skills/multiplayer-game/reference/actors/actions.md create mode 100644 .agents/skills/multiplayer-game/reference/actors/actor-runtime-socket.md create mode 100644 .agents/skills/multiplayer-game/reference/actors/appearance.md create mode 100644 .agents/skills/multiplayer-game/reference/actors/authentication.md create mode 100644 .agents/skills/multiplayer-game/reference/actors/communicating-between-actors.md create mode 100644 .agents/skills/multiplayer-game/reference/actors/connections.md create mode 100644 .agents/skills/multiplayer-game/reference/actors/debugging.md create mode 100644 .agents/skills/multiplayer-game/reference/actors/design-patterns.md create mode 100644 .agents/skills/multiplayer-game/reference/actors/destroy.md create mode 100644 .agents/skills/multiplayer-game/reference/actors/errors.md create mode 100644 .agents/skills/multiplayer-game/reference/actors/events.md create mode 100644 .agents/skills/multiplayer-game/reference/actors/fetch-and-websocket-handler.md create mode 100644 .agents/skills/multiplayer-game/reference/actors/helper-types.md create mode 100644 .agents/skills/multiplayer-game/reference/actors/http-api.md create mode 100644 .agents/skills/multiplayer-game/reference/actors/input.md create mode 100644 .agents/skills/multiplayer-game/reference/actors/inspector-tabs.md create mode 100644 .agents/skills/multiplayer-game/reference/actors/keys.md create mode 100644 .agents/skills/multiplayer-game/reference/actors/kv.md create mode 100644 .agents/skills/multiplayer-game/reference/actors/lifecycle.md create mode 100644 .agents/skills/multiplayer-game/reference/actors/limits.md create mode 100644 .agents/skills/multiplayer-game/reference/actors/metadata.md create mode 100644 .agents/skills/multiplayer-game/reference/actors/queues.md create mode 100644 .agents/skills/multiplayer-game/reference/actors/quickstart/backend.md create mode 100644 .agents/skills/multiplayer-game/reference/actors/quickstart/cloudflare.md create mode 100644 .agents/skills/multiplayer-game/reference/actors/quickstart/effect.md create mode 100644 .agents/skills/multiplayer-game/reference/actors/quickstart/next-js.md create mode 100644 .agents/skills/multiplayer-game/reference/actors/quickstart/react.md create mode 100644 .agents/skills/multiplayer-game/reference/actors/quickstart/rust.md create mode 100644 .agents/skills/multiplayer-game/reference/actors/quickstart/supabase.md create mode 100644 .agents/skills/multiplayer-game/reference/actors/request-handler.md create mode 100644 .agents/skills/multiplayer-game/reference/actors/scaling.md create mode 100644 .agents/skills/multiplayer-game/reference/actors/schedule.md create mode 100644 .agents/skills/multiplayer-game/reference/actors/sharing-and-joining-state.md create mode 100644 .agents/skills/multiplayer-game/reference/actors/sqlite-drizzle.md create mode 100644 .agents/skills/multiplayer-game/reference/actors/sqlite.md create mode 100644 .agents/skills/multiplayer-game/reference/actors/state.md create mode 100644 .agents/skills/multiplayer-game/reference/actors/statuses.md create mode 100644 .agents/skills/multiplayer-game/reference/actors/testing.md create mode 100644 .agents/skills/multiplayer-game/reference/actors/troubleshooting.md create mode 100644 .agents/skills/multiplayer-game/reference/actors/types.md create mode 100644 .agents/skills/multiplayer-game/reference/actors/versions.md create mode 100644 .agents/skills/multiplayer-game/reference/actors/websocket-handler.md create mode 100644 .agents/skills/multiplayer-game/reference/actors/workflows.md create mode 100644 .agents/skills/multiplayer-game/reference/cli.md create mode 100644 .agents/skills/multiplayer-game/reference/clients/javascript.md create mode 100644 .agents/skills/multiplayer-game/reference/clients/react.md create mode 100644 .agents/skills/multiplayer-game/reference/clients/rust.md create mode 100644 .agents/skills/multiplayer-game/reference/clients/swift.md create mode 100644 .agents/skills/multiplayer-game/reference/clients/swiftui.md create mode 100644 .agents/skills/multiplayer-game/reference/cookbook/ai-agent.md create mode 100644 .agents/skills/multiplayer-game/reference/cookbook/chat-room.md create mode 100644 .agents/skills/multiplayer-game/reference/cookbook/collaborative-text-editor.md create mode 100644 .agents/skills/multiplayer-game/reference/cookbook/cron-jobs.md create mode 100644 .agents/skills/multiplayer-game/reference/cookbook/live-cursors.md create mode 100644 .agents/skills/multiplayer-game/reference/cookbook/multiplayer-game.md create mode 100644 .agents/skills/multiplayer-game/reference/cookbook/per-tenant-database.md create mode 100644 .agents/skills/multiplayer-game/reference/cookbook/vpc-air-gapped.md create mode 100644 .agents/skills/multiplayer-game/reference/deploy/aws-ecs.md create mode 100644 .agents/skills/multiplayer-game/reference/deploy/aws-lambda.md create mode 100644 .agents/skills/multiplayer-game/reference/deploy/cloudflare.md create mode 100644 .agents/skills/multiplayer-game/reference/deploy/container-runner.md create mode 100644 .agents/skills/multiplayer-game/reference/deploy/freestyle.md create mode 100644 .agents/skills/multiplayer-game/reference/deploy/gcp-cloud-run.md create mode 100644 .agents/skills/multiplayer-game/reference/deploy/hetzner.md create mode 100644 .agents/skills/multiplayer-game/reference/deploy/kubernetes.md create mode 100644 .agents/skills/multiplayer-game/reference/deploy/railway.md create mode 100644 .agents/skills/multiplayer-game/reference/deploy/rivet-compute.md create mode 100644 .agents/skills/multiplayer-game/reference/deploy/supabase.md create mode 100644 .agents/skills/multiplayer-game/reference/deploy/vercel.md create mode 100644 .agents/skills/multiplayer-game/reference/deploy/vm-and-bare-metal.md create mode 100644 .agents/skills/multiplayer-game/reference/general/actor-configuration.md create mode 100644 .agents/skills/multiplayer-game/reference/general/architecture.md create mode 100644 .agents/skills/multiplayer-game/reference/general/cors.md create mode 100644 .agents/skills/multiplayer-game/reference/general/docs-for-llms.md create mode 100644 .agents/skills/multiplayer-game/reference/general/edge.md create mode 100644 .agents/skills/multiplayer-game/reference/general/endpoints.md create mode 100644 .agents/skills/multiplayer-game/reference/general/environment-variables.md create mode 100644 .agents/skills/multiplayer-game/reference/general/http-server.md create mode 100644 .agents/skills/multiplayer-game/reference/general/logging.md create mode 100644 .agents/skills/multiplayer-game/reference/general/pool-configuration.md create mode 100644 .agents/skills/multiplayer-game/reference/general/production-checklist.md create mode 100644 .agents/skills/multiplayer-game/reference/general/registry-configuration.md create mode 100644 .agents/skills/multiplayer-game/reference/general/runtime-modes.md create mode 100644 .agents/skills/multiplayer-game/reference/general/wasm-vs-native-sdk.md create mode 100644 .agents/skills/multiplayer-game/reference/self-hosting/configuration.md create mode 100644 .agents/skills/multiplayer-game/reference/self-hosting/docker-compose.md create mode 100644 .agents/skills/multiplayer-game/reference/self-hosting/docker-container.md create mode 100644 .agents/skills/multiplayer-game/reference/self-hosting/filesystem.md create mode 100644 .agents/skills/multiplayer-game/reference/self-hosting/foundationdb.md create mode 100644 .agents/skills/multiplayer-game/reference/self-hosting/install.md create mode 100644 .agents/skills/multiplayer-game/reference/self-hosting/kubernetes.md create mode 100644 .agents/skills/multiplayer-game/reference/self-hosting/multi-region.md create mode 100644 .agents/skills/multiplayer-game/reference/self-hosting/postgres.md create mode 100644 .agents/skills/multiplayer-game/reference/self-hosting/production-checklist.md create mode 100644 .agents/skills/multiplayer-game/reference/self-hosting/railway.md create mode 100644 .agents/skills/multiplayer-game/reference/self-hosting/render.md create mode 100644 .agents/skills/multiplayer-game/reference/self-hosting/tls.md create mode 100644 .agents/skills/per-tenant-database/BASE_SKILL.md create mode 100644 .agents/skills/per-tenant-database/SKILL.md create mode 100644 .agents/skills/per-tenant-database/index.json create mode 100644 .agents/skills/per-tenant-database/openapi.json create mode 100644 .agents/skills/per-tenant-database/reference/actors/access-control.md create mode 100644 .agents/skills/per-tenant-database/reference/actors/actions.md create mode 100644 .agents/skills/per-tenant-database/reference/actors/actor-runtime-socket.md create mode 100644 .agents/skills/per-tenant-database/reference/actors/appearance.md create mode 100644 .agents/skills/per-tenant-database/reference/actors/authentication.md create mode 100644 .agents/skills/per-tenant-database/reference/actors/communicating-between-actors.md create mode 100644 .agents/skills/per-tenant-database/reference/actors/connections.md create mode 100644 .agents/skills/per-tenant-database/reference/actors/debugging.md create mode 100644 .agents/skills/per-tenant-database/reference/actors/design-patterns.md create mode 100644 .agents/skills/per-tenant-database/reference/actors/destroy.md create mode 100644 .agents/skills/per-tenant-database/reference/actors/errors.md create mode 100644 .agents/skills/per-tenant-database/reference/actors/events.md create mode 100644 .agents/skills/per-tenant-database/reference/actors/fetch-and-websocket-handler.md create mode 100644 .agents/skills/per-tenant-database/reference/actors/helper-types.md create mode 100644 .agents/skills/per-tenant-database/reference/actors/http-api.md create mode 100644 .agents/skills/per-tenant-database/reference/actors/input.md create mode 100644 .agents/skills/per-tenant-database/reference/actors/inspector-tabs.md create mode 100644 .agents/skills/per-tenant-database/reference/actors/keys.md create mode 100644 .agents/skills/per-tenant-database/reference/actors/kv.md create mode 100644 .agents/skills/per-tenant-database/reference/actors/lifecycle.md create mode 100644 .agents/skills/per-tenant-database/reference/actors/limits.md create mode 100644 .agents/skills/per-tenant-database/reference/actors/metadata.md create mode 100644 .agents/skills/per-tenant-database/reference/actors/queues.md create mode 100644 .agents/skills/per-tenant-database/reference/actors/quickstart/backend.md create mode 100644 .agents/skills/per-tenant-database/reference/actors/quickstart/cloudflare.md create mode 100644 .agents/skills/per-tenant-database/reference/actors/quickstart/effect.md create mode 100644 .agents/skills/per-tenant-database/reference/actors/quickstart/next-js.md create mode 100644 .agents/skills/per-tenant-database/reference/actors/quickstart/react.md create mode 100644 .agents/skills/per-tenant-database/reference/actors/quickstart/rust.md create mode 100644 .agents/skills/per-tenant-database/reference/actors/quickstart/supabase.md create mode 100644 .agents/skills/per-tenant-database/reference/actors/request-handler.md create mode 100644 .agents/skills/per-tenant-database/reference/actors/scaling.md create mode 100644 .agents/skills/per-tenant-database/reference/actors/schedule.md create mode 100644 .agents/skills/per-tenant-database/reference/actors/sharing-and-joining-state.md create mode 100644 .agents/skills/per-tenant-database/reference/actors/sqlite-drizzle.md create mode 100644 .agents/skills/per-tenant-database/reference/actors/sqlite.md create mode 100644 .agents/skills/per-tenant-database/reference/actors/state.md create mode 100644 .agents/skills/per-tenant-database/reference/actors/statuses.md create mode 100644 .agents/skills/per-tenant-database/reference/actors/testing.md create mode 100644 .agents/skills/per-tenant-database/reference/actors/troubleshooting.md create mode 100644 .agents/skills/per-tenant-database/reference/actors/types.md create mode 100644 .agents/skills/per-tenant-database/reference/actors/versions.md create mode 100644 .agents/skills/per-tenant-database/reference/actors/websocket-handler.md create mode 100644 .agents/skills/per-tenant-database/reference/actors/workflows.md create mode 100644 .agents/skills/per-tenant-database/reference/cli.md create mode 100644 .agents/skills/per-tenant-database/reference/clients/javascript.md create mode 100644 .agents/skills/per-tenant-database/reference/clients/react.md create mode 100644 .agents/skills/per-tenant-database/reference/clients/rust.md create mode 100644 .agents/skills/per-tenant-database/reference/clients/swift.md create mode 100644 .agents/skills/per-tenant-database/reference/clients/swiftui.md create mode 100644 .agents/skills/per-tenant-database/reference/cookbook/ai-agent.md create mode 100644 .agents/skills/per-tenant-database/reference/cookbook/chat-room.md create mode 100644 .agents/skills/per-tenant-database/reference/cookbook/collaborative-text-editor.md create mode 100644 .agents/skills/per-tenant-database/reference/cookbook/cron-jobs.md create mode 100644 .agents/skills/per-tenant-database/reference/cookbook/live-cursors.md create mode 100644 .agents/skills/per-tenant-database/reference/cookbook/multiplayer-game.md create mode 100644 .agents/skills/per-tenant-database/reference/cookbook/per-tenant-database.md create mode 100644 .agents/skills/per-tenant-database/reference/cookbook/vpc-air-gapped.md create mode 100644 .agents/skills/per-tenant-database/reference/deploy/aws-ecs.md create mode 100644 .agents/skills/per-tenant-database/reference/deploy/aws-lambda.md create mode 100644 .agents/skills/per-tenant-database/reference/deploy/cloudflare.md create mode 100644 .agents/skills/per-tenant-database/reference/deploy/container-runner.md create mode 100644 .agents/skills/per-tenant-database/reference/deploy/freestyle.md create mode 100644 .agents/skills/per-tenant-database/reference/deploy/gcp-cloud-run.md create mode 100644 .agents/skills/per-tenant-database/reference/deploy/hetzner.md create mode 100644 .agents/skills/per-tenant-database/reference/deploy/kubernetes.md create mode 100644 .agents/skills/per-tenant-database/reference/deploy/railway.md create mode 100644 .agents/skills/per-tenant-database/reference/deploy/rivet-compute.md create mode 100644 .agents/skills/per-tenant-database/reference/deploy/supabase.md create mode 100644 .agents/skills/per-tenant-database/reference/deploy/vercel.md create mode 100644 .agents/skills/per-tenant-database/reference/deploy/vm-and-bare-metal.md create mode 100644 .agents/skills/per-tenant-database/reference/general/actor-configuration.md create mode 100644 .agents/skills/per-tenant-database/reference/general/architecture.md create mode 100644 .agents/skills/per-tenant-database/reference/general/cors.md create mode 100644 .agents/skills/per-tenant-database/reference/general/docs-for-llms.md create mode 100644 .agents/skills/per-tenant-database/reference/general/edge.md create mode 100644 .agents/skills/per-tenant-database/reference/general/endpoints.md create mode 100644 .agents/skills/per-tenant-database/reference/general/environment-variables.md create mode 100644 .agents/skills/per-tenant-database/reference/general/http-server.md create mode 100644 .agents/skills/per-tenant-database/reference/general/logging.md create mode 100644 .agents/skills/per-tenant-database/reference/general/pool-configuration.md create mode 100644 .agents/skills/per-tenant-database/reference/general/production-checklist.md create mode 100644 .agents/skills/per-tenant-database/reference/general/registry-configuration.md create mode 100644 .agents/skills/per-tenant-database/reference/general/runtime-modes.md create mode 100644 .agents/skills/per-tenant-database/reference/general/wasm-vs-native-sdk.md create mode 100644 .agents/skills/per-tenant-database/reference/self-hosting/configuration.md create mode 100644 .agents/skills/per-tenant-database/reference/self-hosting/docker-compose.md create mode 100644 .agents/skills/per-tenant-database/reference/self-hosting/docker-container.md create mode 100644 .agents/skills/per-tenant-database/reference/self-hosting/filesystem.md create mode 100644 .agents/skills/per-tenant-database/reference/self-hosting/foundationdb.md create mode 100644 .agents/skills/per-tenant-database/reference/self-hosting/install.md create mode 100644 .agents/skills/per-tenant-database/reference/self-hosting/kubernetes.md create mode 100644 .agents/skills/per-tenant-database/reference/self-hosting/multi-region.md create mode 100644 .agents/skills/per-tenant-database/reference/self-hosting/postgres.md create mode 100644 .agents/skills/per-tenant-database/reference/self-hosting/production-checklist.md create mode 100644 .agents/skills/per-tenant-database/reference/self-hosting/railway.md create mode 100644 .agents/skills/per-tenant-database/reference/self-hosting/render.md create mode 100644 .agents/skills/per-tenant-database/reference/self-hosting/tls.md create mode 100644 apps/web/design/zopu-audit-expanded.png create mode 100644 apps/web/design/zopu-audit-stack.png create mode 100644 apps/web/design/zopu-chat-preview.png create mode 100644 apps/web/design/zopu-chat.fig create mode 100644 apps/web/design/zopu-core-product-experience-final.png create mode 100644 apps/web/design/zopu-core-product-experience-polish-1.png create mode 100644 apps/web/design/zopu-core-product-experience-polish-2.png create mode 100644 apps/web/design/zopu-core-product-experience.png create mode 100644 apps/web/design/zopu-design-desktop.png create mode 100644 apps/web/design/zopu-design-foundations.png create mode 100644 apps/web/design/zopu-design-mobile.png create mode 100644 apps/web/design/zopu-design-primitives.png create mode 100644 apps/web/design/zopu-design.fig create mode 100644 apps/web/design/zopu-mobile-character-pass.png create mode 100644 apps/web/design/zopu-mobile-expanded-work-unit.png create mode 100644 apps/web/design/zopu-mobile-home-expanded-in-place.png create mode 100644 apps/web/design/zopu-mobile-stacked-before-polish.png create mode 100644 apps/web/design/zopu-mobile-stacked-final-check.png create mode 100644 apps/web/design/zopu-mobile-stacked-final.png create mode 100644 apps/web/design/zopu-mobile-stacked-work-units-before-polish.png create mode 100644 apps/web/design/zopu-mobile-stacked-work-units.png create mode 100644 apps/web/design/zopu-mobile-vertical-work-stack.png create mode 100644 apps/web/design/zopu-mobile-work-units-home.png create mode 100644 apps/web/design/zopu-safari-oauth-storyboard-final.png create mode 100644 apps/web/design/zopu-safari-oauth-storyboard.png create mode 100644 apps/web/design/zopu-task-card-system-options.png create mode 100644 apps/web/design/zopu-work-object-system.png create mode 100644 apps/web/design/zopu-work-unit-lifecycle-final.png create mode 100644 apps/web/design/zopu-work-unit-lifecycle-polish-1.png create mode 100644 apps/web/design/zopu-work-unit-lifecycle-polish-2.png create mode 100644 apps/web/design/zopu-work-unit-lifecycle.png diff --git a/.agents/skills/live-cursors/BASE_SKILL.md b/.agents/skills/live-cursors/BASE_SKILL.md new file mode 100644 index 0000000..f03c1af --- /dev/null +++ b/.agents/skills/live-cursors/BASE_SKILL.md @@ -0,0 +1,537 @@ +--- +name: "rivetkit" +description: "RivetKit backend and Rivet Actor runtime guidance. Use for building, modifying, debugging, or testing Rivet Actors, registries, serverless/runner modes, deployment, or actor-based workflows." +--- + +# Rivet Actors + +Build high-performance, long-lived, in-memory processes on Rivet's actor runtime. Use this skill whenever HTTP endpoints, databases, or queues no longer meet the requirements for AI agents, realtime collaboration, workflow orchestration, or geo-distributed data. + +## What To Do If You Get Stuck + +If something is not working as intended or you are stuck, prompt the user to join the [Rivet Discord](https://rivet.dev/discord) or file an issue on the [Rivet GitHub](https://github.com/rivet-dev/rivet) to report an issue and get help. + +Provide the user with a pre-generated report with: + +- Symptoms + - If this is happening in local dev, deployed, or both + - The error you're seeing + - Relevant source code related to this +- What you've tried to solve it +- Environment + - RivetKit version + - Runtime (Node, Bun, etc) including version + - If applicable, provider in use (e.g. Vercel, Railway, etc) + - If applicable, HTTP router in use (e.g. Hono, Express, Elysia) + +## Debugging Actors + +Use the inspector HTTP API to examine running actors. These endpoints are accessible through the gateway at `/gateway/{actor_id}/inspector/*`. Key endpoints: + +- `GET /inspector/summary` - full actor snapshot (state, connections, RPCs, queue) +- `GET /inspector/state` / `PATCH /inspector/state` - read/write actor state +- `GET /inspector/connections` - active connections +- `GET /inspector/rpcs` - available actions +- `POST /inspector/action/{name}` - execute an action with `{"args": [...]}` +- `POST /inspector/database/execute` - run SQL with `{"sql": "...", "args": [...]}` or `{"sql": "...", "properties": {...}}` for reads or mutations +- `GET /inspector/queue?limit=50` - queue status +- `GET /inspector/traces?startMs=0&endMs=...&limit=1000` - trace spans (OTLP JSON) +- `GET /inspector/workflow-history` - workflow history and status as JSON (`nameRegistry`, `entries`, `entryMetadata`) +- `POST /inspector/workflow/replay` - replay a workflow from a specific step or from the beginning; returns `409 actor/workflow_in_flight` if the workflow is still running +- `GET /inspector/database/schema` - SQLite tables and views exposed by `c.db` +- `GET /inspector/database/rows?table=...&limit=100&offset=0` - paged SQLite rows for a table or view + +In local dev, no auth token is needed. In production, pass `Authorization: Bearer `, where the inspector token is the actor-specific token auto-generated on first start and persisted in the actor's internal KV at key `0x03`. The Rivet dashboard retrieves this token automatically; for direct API access, fetch it through the management KV endpoint. See the [debugging docs](https://rivet.dev/docs/actors/debugging) for details. + +## Citing Sources + +When providing information from Rivet documentation, cite the canonical URL so users can learn more. Each reference file includes its canonical URL in the header metadata. + +**How to cite:** + +- Use inline links for key concepts: "Use [actor keys](https://rivet.dev/docs/actors/keys) to uniquely identify instances." +- Add a "Learn more" link after explanations for complex topics + +**Finding canonical URLs:** + +The Reference Map below links to reference files. Each file's header contains: + +``` +> Canonical URL: https://rivet.dev/docs/actors/actions +``` + +Use that canonical URL when citing, not the reference file path. + +**Examples:** + +- Actions → `https://rivet.dev/docs/actors/actions` +- React client → `https://rivet.dev/docs/clients/react` +- Self-hosting on Kubernetes → `https://rivet.dev/docs/self-hosting/kubernetes` + +## Version Check + +Before starting any work, check if the user's project is on the latest version of RivetKit (latest: 2.3.5-rc.1). Look at the `rivetkit` version in the user's `package.json` (check both `dependencies` and `devDependencies`). If the installed version is older than 2.3.5-rc.1, inform the user and suggest upgrading: + +```bash +npm install rivetkit@2.3.5-rc.1 +``` + +If the user also uses `@rivetkit/react`, `@rivetkit/next-js`, or other `@rivetkit/*` client packages, suggest upgrading those too. Outdated versions may have known bugs or missing features that cause issues. + +## First Steps + +1. Install RivetKit (latest: 2.3.5-rc.1) + ```bash + npm install rivetkit@2.3.5-rc.1 + ``` +2. Define a registry with `setup({ use: { /* actors */ } })`. +3. Call `registry.start()` to start the server. For custom HTTP server integration, use `registry.handler()` with a router like Hono. For serverless deployments, use `registry.serve()`. For runner-only mode, use `registry.startEnvoy()`. +4. Verify `/api/rivet/metadata` returns 200 before deploying. +5. Configure Rivet Cloud or self-hosted engine + - You must configure versioning for production builds. This is not needed for local development. See [Versions & Upgrades](https://rivet.dev/docs/actors/versions). +6. Integrate clients (see client guides below for JavaScript, React, or Swift) +7. Prompt the user if they want to deploy. If so, go to Deploying Rivet Backends. + +For more information, read the quickstart guide relevant to the user's project. + +## Project Setup + +### .gitignore + +Every RivetKit project should have a `.gitignore`. Include at minimum: + +``` +node_modules/ +dist/ +.env +``` + +### .dockerignore + +Every project with a Dockerfile should have a `.dockerignore` to keep the image small and avoid leaking secrets: + +``` +node_modules/ +dist/ +.env +.git/ +``` + +### Dockerfile + +Use this as a base Dockerfile for deploying a RivetKit project. The `RIVET_RUNNER_VERSION` build arg is only needed when self-hosting or using a custom runner (not needed for Rivet Compute). It lets Rivet track which version of the actor is running and drain old actors on deploy. See https://rivet.dev/docs/actors/versions for details. + +```dockerfile +FROM node:24-alpine + +ARG RIVET_RUNNER_VERSION +ENV RIVET_RUNNER_VERSION=$RIVET_RUNNER_VERSION + +WORKDIR /app + +COPY package.json package-lock.json ./ +RUN npm ci + +COPY . . +RUN npm run build --if-present + +CMD ["node", "dist/index.js"] +``` + +Build with: + +```bash +docker build --build-arg RIVET_RUNNER_VERSION=$(date +%s) . +``` + +Adjust the `CMD` to match the project's entry point. If the project uses a different output directory or start command, update accordingly. + +## Error Handling Policy + +- Prefer fail-fast behavior by default. +- Avoid `try/catch` unless it is required for a real recovery path, cleanup boundary, or to add actionable context. +- Never swallow errors. If you add a `catch`, you must handle the error explicitly, at minimum by logging it. +- When you cannot recover, log context and rethrow. + +## State vs Vars: Persistence Rules + +**`c.vars` is ephemeral.** Data in `c.vars` is lost on every restart, crash, upgrade, or sleep/wake cycle. Only use `c.vars` for non-serializable objects (e.g., physics engines, WebSocket references, event emitters, caches) or truly transient runtime data (e.g., current input direction that doesn't matter after disconnect). + +**Persistent storage options.** Any data that must survive restarts belongs in one of these, NOT in `c.vars`: + +- **`c.state`** — CBOR-serializable data for small, bounded datasets. Ideal for configuration, counters, small player lists, phase flags, etc. Keep under 128 KB. Do not store unbounded or growing data here (e.g., chat logs, event histories, spawned entity lists that grow without limit). State is read/written as a single blob on every persistence cycle. +- **`c.kv`** — Key-value store for unbounded data. This is what `c.state` uses under the hood. Supports binary values. Use for larger or variable-size data like user inventories, world chunks, file blobs, or any collection that may grow over time. Keys are scoped to the actor instance. +- **`c.db`** — SQLite database for structured or complex data. Use when you need queries, indexes, joins, aggregations, or relational modeling. Ideal for leaderboards, match histories, player pools, or any data that benefits from SQL. + +**Common mistake:** Storing meaningful game/application data in `c.vars` instead of persisting it. For example, if users can spawn objects in a physics simulation, the spawn definitions (position, size, type) must be persisted in `c.state` (or `c.kv` if unbounded), even though the physics engine handles (non-serializable) live in `c.vars`. On restart, `run()` should recreate the runtime objects from the persisted data. + +## Deploying Rivet Backends + +Assume the user is deploying to Rivet Cloud, unless otherwise specified. If user is self-hosting, read the self-hosting guides below. + +1. Verify that Rivet Actors are working in local dev +2. Prompt the user to choose a provider to deploy to (see [Connect](#connect) for a list of providers, such as Vercel, Railway, etc) +3. Follow the deploy guide for that given provider. You will need to instruct the user when you need manual intervention. + +## API Reference + +The RivetKit OpenAPI specification is available in the skill directory at `openapi.json`. This file documents all HTTP endpoints for managing actors. + +## Misc Notes + +- The Rivet domain is rivet.dev, not rivet.gg + +## TypeScript Caveat: Actor Client Inference + +- In multi-file TypeScript projects, bidirectional actor calls can create a circular type dependency when both actors use `c.client()`. +- Symptoms usually include `c.state` becoming `unknown`, actor methods becoming possibly `undefined`, or `TS2322` / `TS2722` errors after the first cross-actor call. +- If an action returns the result of another actor call, prefer an explicit return type annotation on that action instead of relying on inference through `c.client()`. +- If explicit return types are not enough, use a narrower client or registry type for only the actors that action needs. +- As a last resort, pass `unknown` for the registry type and be explicit that this gives up type safety at that call site. + +## Features + +- **Long-Lived, Stateful Compute**: Each unit of compute is like a tiny server that remembers things between requests – no need to re-fetch data from a database or worry about timeouts. Like AWS Lambda, but with memory and no timeouts. +- **Blazing-Fast Reads & Writes**: State is stored on the same machine as your compute, so reads and writes are ultra-fast. No database round trips, no latency spikes. State is persisted to Rivet for long term storage, so it survives server restarts. +- **Realtime**: Update state and broadcast changes in realtime with WebSockets. No external pub/sub systems, no polling – just built-in low-latency events. +- **Infinitely Scalable**: Automatically scale from zero to millions of concurrent actors. Pay only for what you use with instant scaling and no cold starts. +- **Fault Tolerant**: Built-in error handling and recovery. Actors automatically restart on failure while preserving state integrity and continuing operations. + +## When to Use Rivet Actors + +- **AI agents & sandboxes**: multi-step toolchains, conversation memory, sandbox orchestration. +- **Multiplayer or collaborative apps**: CRDT docs, shared cursors, realtime dashboards, chat. +- **Workflow automation**: background jobs, cron, rate limiters, durable queues, backpressure control. +- **Data-intensive backends**: geo-distributed or per-tenant databases, in-memory caches, sharded SQL. +- **Networking workloads**: WebSocket servers, custom protocols, local-first sync, edge fanout. + +## Minimal Project + +### Backend + +**index.ts** + +### Client Docs + +Use the client SDK that matches your app: + +- [JavaScript Client](/docs/clients/javascript) +- [React Client](/docs/clients/react) +- [Swift Client](/docs/clients/swift) + +## Actor Quick Reference + +### In-Memory State + +Persistent data that survives restarts, crashes, and deployments. State is persisted on Rivet Cloud or Rivet self-hosted, so it survives restarts if the current process crashes or exits. + +### Static Initial State + +### Dynamic Initial State + +[Documentation](/docs/actors/state) + +### Keys + +Keys uniquely identify actor instances. Use compound keys (arrays) for hierarchical addressing: + +Don't build keys with string interpolation like `"org:${userId}"` when `userId` contains user data. Use arrays instead to prevent key injection attacks. + +[Documentation](/docs/actors/keys) + +### Input + +Pass initialization data when creating actors. Input is only available in `createState` and `onCreate`, so store it in state if you need it later. + +[Documentation](/docs/actors/input) + +### Temporary Variables + +Temporary data that doesn't survive restarts. Use for non-serializable objects (event emitters, connections, etc). + +### Static Initial Vars + +### Dynamic Initial Vars + +[Documentation](/docs/actors/state) + +### Actions + +Actions are the primary way clients and other actors communicate with an actor. + +[Documentation](/docs/actors/actions) + +### Events & Broadcasts + +Events enable real-time communication from actors to connected clients. + +[Documentation](/docs/actors/events) + +### Connections + +Access the current connection via `c.conn` or all connected clients via `c.conns`. Use `c.conn.id` or `c.conn.state` to securely identify who is calling an action. `c.conn` is only available for actions invoked through a connected client; stateless actor-handle calls run without a connection, so guard against that. Connection state is initialized via `connState` or `createConnState`, which receives parameters passed by the client on connect. + +### Static Connection Initial State + +### Dynamic Connection Initial State + +[Documentation](/docs/actors/connections) + +### Queues + +Use queues to process durable messages in order inside a `run` loop. + +[Documentation](/docs/actors/queues) + +### Workflows + +Use workflows when your `run` logic needs durable, replayable multi-step execution. + +[Documentation](/docs/actors/workflows) + +### Actor-to-Actor Communication + +Actors can call other actors using `c.client()`. + +[Documentation](/docs/actors/communicating-between-actors) + +### Schedule & Cron + +Run one-shot actions after a delay or at a specific time. Use named cron jobs for calendar schedules and fixed intervals. Both persist across sleep, restarts, upgrades, and crashes. + +[Documentation](/docs/actors/schedule) + +### Destroying Actors + +Permanently delete an actor and its state using `c.destroy()`. + +[Documentation](/docs/actors/destroy) + +### Lifecycle Hooks + +Actors support hooks for initialization, background processing, connections, networking, and state changes. Use `run` for long-lived background loops, and use `c.aborted` or `c.abortSignal` for graceful shutdown. + +[Documentation](/docs/actors/lifecycle) + +### Context Types + +When writing helper functions outside the actor definition, use `*ContextOf` to extract the correct context type. Helpers like `ActionContextOf`, `CreateContextOf`, `ConnContextOf`, and `ConnInitContextOf` are exported from `"rivetkit"`. Do not manually define your own context interface. Always derive it from the actor definition. + +[Documentation](/docs/actors/types) + +### Errors + +Use `UserError` to throw errors that are safely returned to clients. Pass `metadata` to include structured data. Other errors are converted to generic "internal error" for security. + +### Actor + +### Client + +[Documentation](/docs/actors/errors) + +### Low-Level HTTP & WebSocket Handlers + +For custom protocols or integrating libraries that need direct access to HTTP `Request`/`Response` or WebSocket connections, use `onRequest` and `onWebSocket`. + +[HTTP Handler Documentation](/docs/actors/request-handler) · [WebSocket Handler Documentation](/docs/actors/websocket-handler) + +### Icons & Names + +Customize how actors appear in the UI with display names and icons. It's recommended to always provide a name and icon to actors in order to make them easier to distinguish in the dashboard. + +```typescript +import { actor } from "rivetkit"; + +const chatRoom = actor({ + options: { + name: "Chat Room", + icon: "💬", // or FontAwesome: "comments", "chart-line", etc. + }, + // ... +}); +``` + +[Documentation](/docs/actors/appearance) + +## Client Documentation + +Find the full client guides here: + +- [JavaScript Client](/docs/clients/javascript) +- [React Client](/docs/clients/react) +- [Swift Client](/docs/clients/swift) + +## Common Patterns + +Actors scale naturally through isolated state and message-passing. Structure your applications with these patterns: + +[Documentation](/docs/actors/design-patterns) + +### Actor Per Entity + +Create one actor per user, document, or room. Use compound keys to scope entities: + +### Coordinator & Data Actors + +**Data actors** handle core logic (chat rooms, game sessions, user data). **Coordinator actors** track and manage collections of data actors—think of them as an index. + +### Run Loop + +Use a `run` loop for continuous background work inside an actor. Process queue messages in order, run logic on intervals, stream AI responses, or coordinate long-running tasks. + +### Workflow Loop + +Use this pattern for long-lived, durable workflows that initialize resources, process commands in a loop, then clean up. + +[Documentation](/docs/actors/workflows) + +### Actions vs Queues + +- **Actions** are not durable. Use them for realtime reads, ephemeral data, and low-latency communication like player input. +- **Queues** are durable. Use them to serialize mutations through the run loop, avoiding race conditions with SQLite and other local state. Callers can still wait for a response from queued work. + +### Authentication, Security, & CORS + +- Validate credentials in `onBeforeConnect` or `createConnState` and throw an error to reject unauthorized connections. +- Use `c.conn.state` to securely identify users in actions rather than trusting action parameters. +- For cross-origin access, validate the request origin in `onBeforeConnect`. + +[Authentication Documentation](/docs/actors/authentication) · [CORS Documentation](/docs/general/cors) + +### Versions & Upgrades + +When deploying new code, set a version number so Rivet can route new actors to the latest runner and optionally drain old ones. Use a build timestamp, git commit count, or CI build number as the version. It is very important to [configure versioning](/docs/actors/versions) before deploying to production. Without versioning, actors can regress by running on older runner versions, and existing actors will never be forced to migrate to new runners. They will continue running indefinitely on the old runners until they exit. + +[Documentation](/docs/actors/versions) + +### Anti-Patterns + +#### Never build a "god" actor + +Do not put all your logic in a single actor. A god actor serializes every operation through one bottleneck, kills parallelism, and makes the entire system fail as a unit. Split into focused actors per entity. + +#### Never create an actor per request + +Actors are long-lived and maintain state across requests. Creating a new actor for every incoming request throws away the core benefit of the model and wastes resources on actor creation and teardown. Use actors for persistent entities and regular functions for stateless work. + +## Reference Map + +### Actors + +- [Access Control](reference/actors/access-control.md) +- [Actions](reference/actors/actions.md) +- [Actor Keys](reference/actors/keys.md) +- [Actor Runtime Socket](reference/actors/actor-runtime-socket.md) +- [Actor Statuses](reference/actors/statuses.md) +- [Authentication](reference/actors/authentication.md) +- [Cloudflare Workers Quickstart](reference/actors/quickstart/cloudflare.md) +- [Communicating Between Actors](reference/actors/communicating-between-actors.md) +- [Connections](reference/actors/connections.md) +- [Custom Inspector Tabs](reference/actors/inspector-tabs.md) +- [Debugging](reference/actors/debugging.md) +- [Design Patterns](reference/actors/design-patterns.md) +- [Destroying Actors](reference/actors/destroy.md) +- [Effect.ts Quickstart (Beta)](reference/actors/quickstart/effect.md) +- [Errors](reference/actors/errors.md) +- [Fetch and WebSocket Handler](reference/actors/fetch-and-websocket-handler.md) +- [Helper Types](reference/actors/helper-types.md) +- [Icons & Names](reference/actors/appearance.md) +- [In-Memory State](reference/actors/state.md) +- [Input Parameters](reference/actors/input.md) +- [Lifecycle](reference/actors/lifecycle.md) +- [Limits](reference/actors/limits.md) +- [Low-Level HTTP Request Handler](reference/actors/request-handler.md) +- [Low-Level KV Storage](reference/actors/kv.md) +- [Low-Level WebSocket Handler](reference/actors/websocket-handler.md) +- [Metadata](reference/actors/metadata.md) +- [Next.js Quickstart](reference/actors/quickstart/next-js.md) +- [Node.js & Bun Quickstart](reference/actors/quickstart/backend.md) +- [Queues & Run Loops](reference/actors/queues.md) +- [React Quickstart](reference/actors/quickstart/react.md) +- [Realtime](reference/actors/events.md) +- [Rust Quickstart (Beta)](reference/actors/quickstart/rust.md) +- [Scaling & Concurrency](reference/actors/scaling.md) +- [Schedule & Cron](reference/actors/schedule.md) +- [Sharing and Joining State](reference/actors/sharing-and-joining-state.md) +- [SQLite](reference/actors/sqlite.md) +- [SQLite + Drizzle](reference/actors/sqlite-drizzle.md) +- [Supabase Functions Quickstart](reference/actors/quickstart/supabase.md) +- [Testing](reference/actors/testing.md) +- [Troubleshooting](reference/actors/troubleshooting.md) +- [Types](reference/actors/types.md) +- [Vanilla HTTP API](reference/actors/http-api.md) +- [Versions & Upgrades](reference/actors/versions.md) +- [Workflows](reference/actors/workflows.md) + +### Cli + +- [CLI](reference/cli.md) + +### Clients + +- [Node.js & Bun](reference/clients/javascript.md) +- [React](reference/clients/react.md) +- [Rust (Beta)](reference/clients/rust.md) +- [Swift](reference/clients/swift.md) +- [SwiftUI](reference/clients/swiftui.md) + +### Cookbook + +- [AI Agent](reference/cookbook/ai-agent.md) +- [Chat Room](reference/cookbook/chat-room.md) +- [Collaborative Text Editor](reference/cookbook/collaborative-text-editor.md) +- [Cron Jobs and Scheduled Tasks](reference/cookbook/cron-jobs.md) +- [Database per Tenant](reference/cookbook/per-tenant-database.md) +- [Deploying Rivet in a VPC or Air-Gapped Network](reference/cookbook/vpc-air-gapped.md) +- [Live Cursors and Presence](reference/cookbook/live-cursors.md) +- [Multiplayer Game](reference/cookbook/multiplayer-game.md) + +### Deploy + +- [Container Runner](reference/deploy/container-runner.md) +- [Deploy To Amazon Web Services Lambda](reference/deploy/aws-lambda.md) +- [Deploying to AWS ECS](reference/deploy/aws-ecs.md) +- [Deploying to Cloudflare Workers](reference/deploy/cloudflare.md) +- [Deploying to Freestyle](reference/deploy/freestyle.md) +- [Deploying to Google Cloud Run](reference/deploy/gcp-cloud-run.md) +- [Deploying to Hetzner](reference/deploy/hetzner.md) +- [Deploying to Kubernetes](reference/deploy/kubernetes.md) +- [Deploying to Railway](reference/deploy/railway.md) +- [Deploying to Rivet Compute](reference/deploy/rivet-compute.md) +- [Deploying to Supabase Functions](reference/deploy/supabase.md) +- [Deploying to Vercel](reference/deploy/vercel.md) +- [Deploying to VMs & Bare Metal](reference/deploy/vm-and-bare-metal.md) + +### General + +- [Actor Configuration](reference/general/actor-configuration.md) +- [Architecture](reference/general/architecture.md) +- [Cross-Origin Resource Sharing](reference/general/cors.md) +- [Documentation for LLMs & AI](reference/general/docs-for-llms.md) +- [Edge Networking](reference/general/edge.md) +- [Endpoints](reference/general/endpoints.md) +- [Environment Variables](reference/general/environment-variables.md) +- [HTTP Server](reference/general/http-server.md) +- [Logging](reference/general/logging.md) +- [Pool Configuration](reference/general/pool-configuration.md) +- [Production Checklist](reference/general/production-checklist.md) +- [Registry Configuration](reference/general/registry-configuration.md) +- [Runtime Modes](reference/general/runtime-modes.md) +- [WASM vs Native SDK](reference/general/wasm-vs-native-sdk.md) + +### Self Hosting + +- [Configuration](reference/self-hosting/configuration.md) +- [Docker Compose](reference/self-hosting/docker-compose.md) +- [Docker Container](reference/self-hosting/docker-container.md) +- [File System](reference/self-hosting/filesystem.md) +- [FoundationDB (Enterprise)](reference/self-hosting/foundationdb.md) +- [Installing Rivet Engine](reference/self-hosting/install.md) +- [Kubernetes](reference/self-hosting/kubernetes.md) +- [Multi-Region](reference/self-hosting/multi-region.md) +- [PostgreSQL](reference/self-hosting/postgres.md) +- [Production Checklist](reference/self-hosting/production-checklist.md) +- [Railway Deployment](reference/self-hosting/railway.md) +- [Render Deployment](reference/self-hosting/render.md) +- [TLS & Certificates](reference/self-hosting/tls.md) + diff --git a/.agents/skills/live-cursors/SKILL.md b/.agents/skills/live-cursors/SKILL.md new file mode 100644 index 0000000..8f9a6f1 --- /dev/null +++ b/.agents/skills/live-cursors/SKILL.md @@ -0,0 +1,282 @@ +--- +name: "live-cursors" +description: "Live cursors and multiplayer presence with Rivet Actors: per-connection cursor state, realtime updates over events or raw WebSockets, and throttling." +--- + +# Live Cursors and Presence + +**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.** + +## Working Examples + +If you need a reference implementation, read the raw working example code in these templates: + +- [cursors](https://github.com/rivet-dev/rivet/tree/main/examples/cursors) +- [cursors-raw-websocket](https://github.com/rivet-dev/rivet/tree/main/examples/cursors-raw-websocket) + + +Patterns for building live cursors, multiplayer presence, and realtime cursor sharing with RivetKit. One room actor fans cursor positions out to every connected client, keyed per room with [actor keys](/docs/actors/keys). + +## Starter Code + +Start with one of the two working variants on GitHub. Both implement the same collaborative cursor canvas with persistent text labels; they differ only in transport. + +| Variant | Starter Code | Transport | Presence Storage | +| --- | --- | --- | --- | +| `cursors` | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/cursors) | Typed [actions](/docs/actors/actions) and [events](/docs/actors/events) over the RivetKit connection | `connState` per connection | +| `cursors-raw-websocket` | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/cursors-raw-websocket) | Raw [`onWebSocket` handler](/docs/actors/websocket-handler) with a custom JSON message protocol | Socket map in `createVars` | + +Use `cursors` by default: typed actions, typed events, and automatic connection tracking cover most apps with less code. Use `cursors-raw-websocket` when you need full control of the wire format, for example a custom JSON or binary protocol, or clients that do not use the RivetKit client library. + +## Connection State vs Persistent State + +Presence is ephemeral by definition. A cursor position is only meaningful while its connection is alive, so it belongs in per-connection storage, not in persistent actor state. Persistent state is reserved for data that must survive disconnects and actor restarts. + +| Data | Where It Lives | Why | +| --- | --- | --- | +| Cursor position | `connState` (`cursors`) or the `createVars` socket map (`cursors-raw-websocket`) | Scoped to one connection and discarded with it. Stale presence cannot accumulate in storage. | +| Text labels (`textLabels`) | Persistent actor `state` in both variants | Canvas content must survive disconnects and actor restarts. | + +In the `cursors` variant, `updateCursor` writes `c.conn.state.cursor` and `getRoomState` rebuilds the presence snapshot by iterating `c.conns.values()`, so the cursor map is always derived from live connections rather than stored. See [Connections](/docs/actors/connections) for `connState` and [State](/docs/actors/state) for persistence semantics. + +## Presence Lifecycle + +- **Join**: The `cursors-raw-websocket` variant pushes an `init` message with the current `{ cursors, textLabels }` snapshot as soon as a socket connects. The `cursors` variant has no explicit join broadcast; the client calls the `getRoomState` action once after connecting to seed its local maps, and peers first see a new user on that user's first `cursorMoved` broadcast. +- **Move**: Every `updateCursor` call writes the connection's presence entry, then broadcasts `cursorMoved` to all connections, including the sender. +- **Leave**: The `cursors` variant handles leave in `onDisconnect`, broadcasting `cursorRemoved` with the connection's last cursor. The raw variant does the same from the socket `close` listener, then deletes the session from the `vars.websockets` map. Clients delete that user from their local cursor map, so stale cursors disappear the moment a tab closes. + +See [Lifecycle](/docs/actors/lifecycle) for `onDisconnect` and `createVars`. + +## Update Throttling + +Neither example throttles. Both frontends send a cursor update on every raw `mousemove` event with no debounce or interval cap. That is fine for a demo, but a fast mouse on a high-refresh display can emit hundreds of events per second per user. The patterns below are recommended production hardening on top of the starter code, not something the examples implement. + +| Layer | Pattern | Guidance | +| --- | --- | --- | +| Client (smoothness) | Throttle to 20-30Hz | Sample the latest pointer position every 33-50ms and send only that. Drop intermediate moves, but always flush the final position so cursors settle at the true location. Interpolate between received positions on the rendering side. | +| Server (enforcement) | Per-connection rate limit | Track the last accepted update timestamp per connection and drop or coalesce updates arriving faster than your cap. Client throttles are cooperative; the actor is the enforcement boundary. | + +## Actors + +- **Key**: `cursorRoom[roomId]` (the frontend defaults `roomId` to `"general"`) +- **Responsibility**: Holds per-connection cursor presence in `connState`, persists shared text labels in actor state, and broadcasts cursor and text updates to all connections. +- **Actions** + - `updateCursor` + - `updateText` + - `removeText` + - `getRoomState` +- **Events** + - `cursorMoved` + - `cursorRemoved` + - `textUpdated` + - `textRemoved` +- **Queues** + - None +- **State** + - JSON + - `textLabels` (persistent) + - `connState.cursor` per connection (ephemeral) + +- **Key**: `cursorRoom[roomId]` (resolved via `client.cursorRoom.getOrCreate(roomId)`) +- **Responsibility**: Exposes a raw WebSocket endpoint, tracks live sockets and their cursors in a `createVars` map keyed by a `sessionId` query parameter, persists text labels, and manually fans JSON frames out to every socket. +- **Actions** + - `getOrCreate` (stub returning `{ status: "ok" }`; the frontend resolves the actor ID with the client handle's `getOrCreate(roomId).resolve()`, which creates the actor without dispatching this action) + - `getRoomState` +- **Queues** + - None +- **State** + - JSON + - `textLabels` (persistent) + - `vars.websockets` map of `sessionId` to socket and cursor (in-memory, lost on restart) + +The raw variant defines no RivetKit events. Its message names are `type` fields on raw JSON frames: + +| Direction | Message `type` | Payload | +| --- | --- | --- | +| Client to server | `updateCursor` | `{ userId, x, y }` | +| Client to server | `updateText` | `{ id, userId, text, x, y }` | +| Client to server | `removeText` | `{ id }` | +| Server to client | `init` | `{ cursors, textLabels }` snapshot on connect | +| Server to client | `cursorMoved`, `textUpdated`, `textRemoved`, `cursorRemoved` | The corresponding cursor, label, or ID payload | + +## Lifecycle + +### cursors (Actions + Events) + +```mermaid +sequenceDiagram + participant A as Client A + participant R as cursorRoom + participant B as Other Clients + + A->>R: connect via useActor (cursorRoom[roomId]) + A->>R: getRoomState() + R-->>A: {cursors, textLabels} + loop every mouse move + A->>R: updateCursor(userId, x, y) + Note over R: write c.conn.state.cursor + R-->>B: cursorMoved (broadcast) + end + A->>R: updateText(id, userId, text, x, y) + Note over R: upsert persistent state.textLabels + R-->>B: textUpdated (broadcast) + Note over A: tab closes + Note over R: onDisconnect reads conn.state.cursor + R-->>B: cursorRemoved (broadcast) +``` + +### cursors-raw-websocket + +```mermaid +sequenceDiagram + participant A as Client A + participant R as cursorRoom + participant B as Other Clients + + A->>R: getOrCreate(roomId).resolve() + R-->>A: actorId + A->>R: open WebSocket /gateway/{actorId}/websocket?sessionId=... + Note over R: close 1008 if sessionId is missing + Note over R: store socket in vars.websockets + R-->>A: init {cursors, textLabels} + loop every mouse move + A->>R: {type: "updateCursor"} frame + Note over R: update session cursor in vars + R-->>B: cursorMoved frame + end + Note over A: socket closes + R-->>B: cursorRemoved frame + Note over R: delete session from vars.websockets +``` + +## Security Checklist + +Both examples ship without authentication so the presence pattern stays readable. Everything below is recommended hardening for production, not behavior the examples implement. + +- **Identity**: Bind presence identity to the connection (`c.conn.id` in the actions variant, a server-generated session ID in the raw variant). Never trust a client-supplied `userId`; in the examples it is a random client-generated string, so any client can impersonate or remove any cursor. +- **Authorization**: Authorize label mutations by owner. In the examples, `updateText` accepts arbitrary `id` and `userId` arguments and `removeText` accepts an arbitrary `id`, so any client can edit or delete any label. +- **Input validation**: Clamp `x` and `y` to canvas bounds, cap text label length, and cap the total `textLabels` count so persistent state cannot grow unbounded. +- **Rate limiting**: Enforce a per-connection cap on `updateCursor` (for example 30Hz) and on label writes, as described in [Update Throttling](#update-throttling). +- **Protocol strictness (raw variant)**: Validate message shape before use and close the socket on malformed JSON instead of logging and continuing. Reject duplicate `sessionId` values rather than silently overwriting another session's socket entry. + +## Reference Map + +### Actors + +- [Access Control](reference/actors/access-control.md) +- [Actions](reference/actors/actions.md) +- [Actor Keys](reference/actors/keys.md) +- [Actor Runtime Socket](reference/actors/actor-runtime-socket.md) +- [Actor Statuses](reference/actors/statuses.md) +- [Authentication](reference/actors/authentication.md) +- [Cloudflare Workers Quickstart](reference/actors/quickstart/cloudflare.md) +- [Communicating Between Actors](reference/actors/communicating-between-actors.md) +- [Connections](reference/actors/connections.md) +- [Custom Inspector Tabs](reference/actors/inspector-tabs.md) +- [Debugging](reference/actors/debugging.md) +- [Design Patterns](reference/actors/design-patterns.md) +- [Destroying Actors](reference/actors/destroy.md) +- [Effect.ts Quickstart (Beta)](reference/actors/quickstart/effect.md) +- [Errors](reference/actors/errors.md) +- [Fetch and WebSocket Handler](reference/actors/fetch-and-websocket-handler.md) +- [Helper Types](reference/actors/helper-types.md) +- [Icons & Names](reference/actors/appearance.md) +- [In-Memory State](reference/actors/state.md) +- [Input Parameters](reference/actors/input.md) +- [Lifecycle](reference/actors/lifecycle.md) +- [Limits](reference/actors/limits.md) +- [Low-Level HTTP Request Handler](reference/actors/request-handler.md) +- [Low-Level KV Storage](reference/actors/kv.md) +- [Low-Level WebSocket Handler](reference/actors/websocket-handler.md) +- [Metadata](reference/actors/metadata.md) +- [Next.js Quickstart](reference/actors/quickstart/next-js.md) +- [Node.js & Bun Quickstart](reference/actors/quickstart/backend.md) +- [Queues & Run Loops](reference/actors/queues.md) +- [React Quickstart](reference/actors/quickstart/react.md) +- [Realtime](reference/actors/events.md) +- [Rust Quickstart (Beta)](reference/actors/quickstart/rust.md) +- [Scaling & Concurrency](reference/actors/scaling.md) +- [Schedule & Cron](reference/actors/schedule.md) +- [Sharing and Joining State](reference/actors/sharing-and-joining-state.md) +- [SQLite](reference/actors/sqlite.md) +- [SQLite + Drizzle](reference/actors/sqlite-drizzle.md) +- [Supabase Functions Quickstart](reference/actors/quickstart/supabase.md) +- [Testing](reference/actors/testing.md) +- [Troubleshooting](reference/actors/troubleshooting.md) +- [Types](reference/actors/types.md) +- [Vanilla HTTP API](reference/actors/http-api.md) +- [Versions & Upgrades](reference/actors/versions.md) +- [Workflows](reference/actors/workflows.md) + +### Cli + +- [CLI](reference/cli.md) + +### Clients + +- [Node.js & Bun](reference/clients/javascript.md) +- [React](reference/clients/react.md) +- [Rust (Beta)](reference/clients/rust.md) +- [Swift](reference/clients/swift.md) +- [SwiftUI](reference/clients/swiftui.md) + +### Cookbook + +- [AI Agent](reference/cookbook/ai-agent.md) +- [Chat Room](reference/cookbook/chat-room.md) +- [Collaborative Text Editor](reference/cookbook/collaborative-text-editor.md) +- [Cron Jobs and Scheduled Tasks](reference/cookbook/cron-jobs.md) +- [Database per Tenant](reference/cookbook/per-tenant-database.md) +- [Deploying Rivet in a VPC or Air-Gapped Network](reference/cookbook/vpc-air-gapped.md) +- [Live Cursors and Presence](reference/cookbook/live-cursors.md) +- [Multiplayer Game](reference/cookbook/multiplayer-game.md) + +### Deploy + +- [Container Runner](reference/deploy/container-runner.md) +- [Deploy To Amazon Web Services Lambda](reference/deploy/aws-lambda.md) +- [Deploying to AWS ECS](reference/deploy/aws-ecs.md) +- [Deploying to Cloudflare Workers](reference/deploy/cloudflare.md) +- [Deploying to Freestyle](reference/deploy/freestyle.md) +- [Deploying to Google Cloud Run](reference/deploy/gcp-cloud-run.md) +- [Deploying to Hetzner](reference/deploy/hetzner.md) +- [Deploying to Kubernetes](reference/deploy/kubernetes.md) +- [Deploying to Railway](reference/deploy/railway.md) +- [Deploying to Rivet Compute](reference/deploy/rivet-compute.md) +- [Deploying to Supabase Functions](reference/deploy/supabase.md) +- [Deploying to Vercel](reference/deploy/vercel.md) +- [Deploying to VMs & Bare Metal](reference/deploy/vm-and-bare-metal.md) + +### General + +- [Actor Configuration](reference/general/actor-configuration.md) +- [Architecture](reference/general/architecture.md) +- [Cross-Origin Resource Sharing](reference/general/cors.md) +- [Documentation for LLMs & AI](reference/general/docs-for-llms.md) +- [Edge Networking](reference/general/edge.md) +- [Endpoints](reference/general/endpoints.md) +- [Environment Variables](reference/general/environment-variables.md) +- [HTTP Server](reference/general/http-server.md) +- [Logging](reference/general/logging.md) +- [Pool Configuration](reference/general/pool-configuration.md) +- [Production Checklist](reference/general/production-checklist.md) +- [Registry Configuration](reference/general/registry-configuration.md) +- [Runtime Modes](reference/general/runtime-modes.md) +- [WASM vs Native SDK](reference/general/wasm-vs-native-sdk.md) + +### Self Hosting + +- [Configuration](reference/self-hosting/configuration.md) +- [Docker Compose](reference/self-hosting/docker-compose.md) +- [Docker Container](reference/self-hosting/docker-container.md) +- [File System](reference/self-hosting/filesystem.md) +- [FoundationDB (Enterprise)](reference/self-hosting/foundationdb.md) +- [Installing Rivet Engine](reference/self-hosting/install.md) +- [Kubernetes](reference/self-hosting/kubernetes.md) +- [Multi-Region](reference/self-hosting/multi-region.md) +- [PostgreSQL](reference/self-hosting/postgres.md) +- [Production Checklist](reference/self-hosting/production-checklist.md) +- [Railway Deployment](reference/self-hosting/railway.md) +- [Render Deployment](reference/self-hosting/render.md) +- [TLS & Certificates](reference/self-hosting/tls.md) + diff --git a/.agents/skills/live-cursors/index.json b/.agents/skills/live-cursors/index.json new file mode 100644 index 0000000..b4d14f0 --- /dev/null +++ b/.agents/skills/live-cursors/index.json @@ -0,0 +1,694 @@ +{ + "name": "live-cursors", + "description": "Live cursors and multiplayer presence with Rivet Actors: per-connection cursor state, realtime updates over events or raw WebSockets, and throttling.", + "skill_url": "/metadata/skills/live-cursors/SKILL.md", + "generated_at": "2026-07-20T21:43:04.687Z", + "references": [ + { + "name": "actors/access-control", + "title": "Access Control", + "description": "Authorize actions, queue publishes, and event subscriptions with explicit hooks.", + "canonical_url": "https://rivet.dev/docs/actors/access-control", + "reference_url": "/metadata/skills/live-cursors/reference/actors/access-control.md" + }, + { + "name": "actors/actions", + "title": "Actions", + "description": "Actions are how your backend, frontend, or other actors can communicate with actors.", + "canonical_url": "https://rivet.dev/docs/actors/actions", + "reference_url": "/metadata/skills/live-cursors/reference/actors/actions.md" + }, + { + "name": "general/actor-configuration", + "title": "Actor Configuration", + "description": "This page documents the configuration options available when defining a RivetKit actor. The actor configuration is passed to the `actor()` function.", + "canonical_url": "https://rivet.dev/docs/general/actor-configuration", + "reference_url": "/metadata/skills/live-cursors/reference/general/actor-configuration.md" + }, + { + "name": "actors/keys", + "title": "Actor Keys", + "description": "Actor keys uniquely identify actor instances within each actor type. Keys are used for addressing which specific actor to communicate with.", + "canonical_url": "https://rivet.dev/docs/actors/keys", + "reference_url": "/metadata/skills/live-cursors/reference/actors/keys.md" + }, + { + "name": "actors/actor-runtime-socket", + "title": "Actor Runtime Socket", + "description": "Access an actor's SQLite database from another local process.", + "canonical_url": "https://rivet.dev/docs/actors/actor-runtime-socket", + "reference_url": "/metadata/skills/live-cursors/reference/actors/actor-runtime-socket.md" + }, + { + "name": "actors/statuses", + "title": "Actor Statuses", + "description": "Understand the lifecycle statuses of Rivet Actors, what they mean, how they appear in the API, and how to troubleshoot common issues.", + "canonical_url": "https://rivet.dev/docs/actors/statuses", + "reference_url": "/metadata/skills/live-cursors/reference/actors/statuses.md" + }, + { + "name": "cookbook/ai-agent", + "title": "AI Agent", + "description": "Build an AI agent backend with persistent memory: one Rivet Actor per conversation, queued message handling, and streaming LLM responses as realtime events.", + "canonical_url": "https://rivet.dev/cookbook/ai-agent", + "reference_url": "/metadata/skills/live-cursors/reference/cookbook/ai-agent.md" + }, + { + "name": "general/architecture", + "title": "Architecture", + "description": "- rivetkit is the typescript library used for both local development & to connect your application to rivet - a rivetkit instance is called a \"runner.\" you can run multiple runners to scale rivetkit horizontally. read omre about runners below.", + "canonical_url": "https://rivet.dev/docs/general/architecture", + "reference_url": "/metadata/skills/live-cursors/reference/general/architecture.md" + }, + { + "name": "actors/authentication", + "title": "Authentication", + "description": "Secure your actors with authentication and authorization.", + "canonical_url": "https://rivet.dev/docs/actors/authentication", + "reference_url": "/metadata/skills/live-cursors/reference/actors/authentication.md" + }, + { + "name": "cookbook/chat-room", + "title": "Chat Room", + "description": "Build a realtime chat room backend with Rivet Actors: one actor per room, SQLite-backed message history, and WebSocket broadcast to every connected client.", + "canonical_url": "https://rivet.dev/cookbook/chat-room", + "reference_url": "/metadata/skills/live-cursors/reference/cookbook/chat-room.md" + }, + { + "name": "cli", + "title": "CLI", + "description": "Reference for the optional rivet CLI: deploy to Rivet Compute and run local dev for serverless platforms.", + "canonical_url": "https://rivet.dev/docs/cli", + "reference_url": "/metadata/skills/live-cursors/reference/cli.md" + }, + { + "name": "actors/quickstart/cloudflare", + "title": "Cloudflare Workers Quickstart", + "description": "Set up a Rivet project locally targeting Cloudflare Workers.", + "canonical_url": "https://rivet.dev/docs/actors/quickstart/cloudflare", + "reference_url": "/metadata/skills/live-cursors/reference/actors/quickstart/cloudflare.md" + }, + { + "name": "cookbook/collaborative-text-editor", + "title": "Collaborative Text Editor", + "description": "Build a collaborative text editor backend with Yjs CRDTs and Rivet Actors: per-document actors relay sync and awareness updates and persist snapshots.", + "canonical_url": "https://rivet.dev/cookbook/collaborative-text-editor", + "reference_url": "/metadata/skills/live-cursors/reference/cookbook/collaborative-text-editor.md" + }, + { + "name": "actors/communicating-between-actors", + "title": "Communicating Between Actors", + "description": "Learn how actors can call other actors and share data", + "canonical_url": "https://rivet.dev/docs/actors/communicating-between-actors", + "reference_url": "/metadata/skills/live-cursors/reference/actors/communicating-between-actors.md" + }, + { + "name": "self-hosting/configuration", + "title": "Configuration", + "description": "Rivet Engine can be configured through environment variables or configuration files.", + "canonical_url": "https://rivet.dev/docs/self-hosting/configuration", + "reference_url": "/metadata/skills/live-cursors/reference/self-hosting/configuration.md" + }, + { + "name": "actors/connections", + "title": "Connections", + "description": "Connections represent client connections to your actor. They provide a way to handle client authentication, manage connection-specific data, and control the connection lifecycle.", + "canonical_url": "https://rivet.dev/docs/actors/connections", + "reference_url": "/metadata/skills/live-cursors/reference/actors/connections.md" + }, + { + "name": "deploy/container-runner", + "title": "Container Runner", + "description": "Run any containerized server as a Rivet Actor.", + "canonical_url": "https://rivet.dev/docs/deploy/container-runner", + "reference_url": "/metadata/skills/live-cursors/reference/deploy/container-runner.md" + }, + { + "name": "cookbook/cron-jobs", + "title": "Cron Jobs and Scheduled Tasks", + "description": "Patterns for durable one-shot, calendar, and fixed-interval work on Rivet Actors.", + "canonical_url": "https://rivet.dev/cookbook/cron-jobs", + "reference_url": "/metadata/skills/live-cursors/reference/cookbook/cron-jobs.md" + }, + { + "name": "general/cors", + "title": "Cross-Origin Resource Sharing", + "description": "Cross-Origin Resource Sharing (CORS) controls which origins (domains) can access your actors. When actors are exposed to the public internet, proper origin validation is critical to prevent security breaches and denial of service attacks.", + "canonical_url": "https://rivet.dev/docs/general/cors", + "reference_url": "/metadata/skills/live-cursors/reference/general/cors.md" + }, + { + "name": "actors/inspector-tabs", + "title": "Custom Inspector Tabs", + "description": "Ship your own UI tabs alongside a Rivet Actor — embedded directly in the dashboard inspector.", + "canonical_url": "https://rivet.dev/docs/actors/inspector-tabs", + "reference_url": "/metadata/skills/live-cursors/reference/actors/inspector-tabs.md" + }, + { + "name": "cookbook/per-tenant-database", + "title": "Database per Tenant", + "description": "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.", + "canonical_url": "https://rivet.dev/cookbook/per-tenant-database", + "reference_url": "/metadata/skills/live-cursors/reference/cookbook/per-tenant-database.md" + }, + { + "name": "actors/debugging", + "title": "Debugging", + "description": "Inspect and debug running Rivet Actors, runners, and provider configs using management, runner, and actor inspector HTTP APIs.", + "canonical_url": "https://rivet.dev/docs/actors/debugging", + "reference_url": "/metadata/skills/live-cursors/reference/actors/debugging.md" + }, + { + "name": "deploy/aws-lambda", + "title": "Deploy To Amazon Web Services Lambda", + "description": "_AWS Lambda is coming soon_", + "canonical_url": "https://rivet.dev/docs/deploy/aws-lambda", + "reference_url": "/metadata/skills/live-cursors/reference/deploy/aws-lambda.md" + }, + { + "name": "cookbook/vpc-air-gapped", + "title": "Deploying Rivet in a VPC or Air-Gapped Network", + "description": "Run Rivet entirely inside your own perimeter: single-binary or Docker Compose install, file system storage with no database infrastructure, and no outbound telemetry by default.", + "canonical_url": "https://rivet.dev/cookbook/vpc-air-gapped", + "reference_url": "/metadata/skills/live-cursors/reference/cookbook/vpc-air-gapped.md" + }, + { + "name": "deploy/aws-ecs", + "title": "Deploying to AWS ECS", + "description": "Run your backend on Amazon ECS with Fargate.", + "canonical_url": "https://rivet.dev/docs/deploy/aws-ecs", + "reference_url": "/metadata/skills/live-cursors/reference/deploy/aws-ecs.md" + }, + { + "name": "deploy/cloudflare", + "title": "Deploying to Cloudflare Workers", + "description": "Deploy an existing Rivet project to Cloudflare Workers.", + "canonical_url": "https://rivet.dev/docs/deploy/cloudflare", + "reference_url": "/metadata/skills/live-cursors/reference/deploy/cloudflare.md" + }, + { + "name": "deploy/freestyle", + "title": "Deploying to Freestyle", + "description": "Deploy RivetKit app to Freestyle.sh, a cloud platform for running AI-generated code with built-in security and scalability.", + "canonical_url": "https://rivet.dev/docs/deploy/freestyle", + "reference_url": "/metadata/skills/live-cursors/reference/deploy/freestyle.md" + }, + { + "name": "deploy/gcp-cloud-run", + "title": "Deploying to Google Cloud Run", + "description": "Deploy your RivetKit app to Google Cloud Run.", + "canonical_url": "https://rivet.dev/docs/deploy/gcp-cloud-run", + "reference_url": "/metadata/skills/live-cursors/reference/deploy/gcp-cloud-run.md" + }, + { + "name": "deploy/hetzner", + "title": "Deploying to Hetzner", + "description": "Please see the VM & Bare Metal guide.", + "canonical_url": "https://rivet.dev/docs/deploy/hetzner", + "reference_url": "/metadata/skills/live-cursors/reference/deploy/hetzner.md" + }, + { + "name": "deploy/kubernetes", + "title": "Deploying to Kubernetes", + "description": "Deploy your RivetKit app to any Kubernetes cluster.", + "canonical_url": "https://rivet.dev/docs/deploy/kubernetes", + "reference_url": "/metadata/skills/live-cursors/reference/deploy/kubernetes.md" + }, + { + "name": "deploy/railway", + "title": "Deploying to Railway", + "description": "Deploy your RivetKit app to Railway.", + "canonical_url": "https://rivet.dev/docs/deploy/railway", + "reference_url": "/metadata/skills/live-cursors/reference/deploy/railway.md" + }, + { + "name": "deploy/rivet-compute", + "title": "Deploying to Rivet Compute", + "description": "Run your backend on Rivet Compute.", + "canonical_url": "https://rivet.dev/docs/deploy/rivet-compute", + "reference_url": "/metadata/skills/live-cursors/reference/deploy/rivet-compute.md" + }, + { + "name": "deploy/supabase", + "title": "Deploying to Supabase Functions", + "description": "Deploy an existing Rivet project to Supabase Edge Functions.", + "canonical_url": "https://rivet.dev/docs/deploy/supabase", + "reference_url": "/metadata/skills/live-cursors/reference/deploy/supabase.md" + }, + { + "name": "deploy/vercel", + "title": "Deploying to Vercel", + "description": "Deploy your Next.js Rivet app to Vercel.", + "canonical_url": "https://rivet.dev/docs/deploy/vercel", + "reference_url": "/metadata/skills/live-cursors/reference/deploy/vercel.md" + }, + { + "name": "deploy/vm-and-bare-metal", + "title": "Deploying to VMs & Bare Metal", + "description": "Deploy your RivetKit app to any Linux VM or bare metal host.", + "canonical_url": "https://rivet.dev/docs/deploy/vm-and-bare-metal", + "reference_url": "/metadata/skills/live-cursors/reference/deploy/vm-and-bare-metal.md" + }, + { + "name": "actors/design-patterns", + "title": "Design Patterns", + "description": "Common patterns and anti-patterns for building scalable actor systems.", + "canonical_url": "https://rivet.dev/docs/actors/design-patterns", + "reference_url": "/metadata/skills/live-cursors/reference/actors/design-patterns.md" + }, + { + "name": "actors/destroy", + "title": "Destroying Actors", + "description": "Actors can be permanently destroyed. Common use cases include:", + "canonical_url": "https://rivet.dev/docs/actors/destroy", + "reference_url": "/metadata/skills/live-cursors/reference/actors/destroy.md" + }, + { + "name": "self-hosting/docker-compose", + "title": "Docker Compose", + "description": "Deploy Rivet Engine with docker-compose for multi-container setups.", + "canonical_url": "https://rivet.dev/docs/self-hosting/docker-compose", + "reference_url": "/metadata/skills/live-cursors/reference/self-hosting/docker-compose.md" + }, + { + "name": "self-hosting/docker-container", + "title": "Docker Container", + "description": "Run Rivet Engine in a single Docker container.", + "canonical_url": "https://rivet.dev/docs/self-hosting/docker-container", + "reference_url": "/metadata/skills/live-cursors/reference/self-hosting/docker-container.md" + }, + { + "name": "general/docs-for-llms", + "title": "Documentation for LLMs & AI", + "description": "Rivet provides optimized documentation formats specifically designed for Large Language Models (LLMs) and AI integration tools.", + "canonical_url": "https://rivet.dev/docs/general/docs-for-llms", + "reference_url": "/metadata/skills/live-cursors/reference/general/docs-for-llms.md" + }, + { + "name": "general/edge", + "title": "Edge Networking", + "description": "Actors automatically run near your users on your provider's global network.", + "canonical_url": "https://rivet.dev/docs/general/edge", + "reference_url": "/metadata/skills/live-cursors/reference/general/edge.md" + }, + { + "name": "actors/quickstart/effect", + "title": "Effect.ts Quickstart (Beta)", + "description": "Build a Rivet Actor with the Effect SDK", + "canonical_url": "https://rivet.dev/docs/actors/quickstart/effect", + "reference_url": "/metadata/skills/live-cursors/reference/actors/quickstart/effect.md" + }, + { + "name": "general/endpoints", + "title": "Endpoints", + "description": "Configure how your backend connects to Rivet and how clients reach your actors.", + "canonical_url": "https://rivet.dev/docs/general/endpoints", + "reference_url": "/metadata/skills/live-cursors/reference/general/endpoints.md" + }, + { + "name": "general/environment-variables", + "title": "Environment Variables", + "description": "This page documents all environment variables that configure RivetKit behavior.", + "canonical_url": "https://rivet.dev/docs/general/environment-variables", + "reference_url": "/metadata/skills/live-cursors/reference/general/environment-variables.md" + }, + { + "name": "actors/errors", + "title": "Errors", + "description": "Rivet provides robust error handling with security built in by default. Errors are handled differently based on whether they should be exposed to clients or kept private.", + "canonical_url": "https://rivet.dev/docs/actors/errors", + "reference_url": "/metadata/skills/live-cursors/reference/actors/errors.md" + }, + { + "name": "actors/fetch-and-websocket-handler", + "title": "Fetch and WebSocket Handler", + "description": "These docs have moved to [Low-Level WebSocket Handler](/docs/actors/websocket-handler) and [Low-Level Request Handler](/docs/actors/request-handler).", + "canonical_url": "https://rivet.dev/docs/actors/fetch-and-websocket-handler", + "reference_url": "/metadata/skills/live-cursors/reference/actors/fetch-and-websocket-handler.md" + }, + { + "name": "self-hosting/filesystem", + "title": "File System", + "description": "The file system backend stores all data on the local disk. This is suitable for single-node deployments, development, and testing.", + "canonical_url": "https://rivet.dev/docs/self-hosting/filesystem", + "reference_url": "/metadata/skills/live-cursors/reference/self-hosting/filesystem.md" + }, + { + "name": "self-hosting/foundationdb", + "title": "FoundationDB (Enterprise)", + "description": "FoundationDB is the recommended storage backend for scalable production Rivet deployments.", + "canonical_url": "https://rivet.dev/docs/self-hosting/foundationdb", + "reference_url": "/metadata/skills/live-cursors/reference/self-hosting/foundationdb.md" + }, + { + "name": "actors/helper-types", + "title": "Helper Types", + "description": "This page has moved to [Types](/docs/actors/types).", + "canonical_url": "https://rivet.dev/docs/actors/helper-types", + "reference_url": "/metadata/skills/live-cursors/reference/actors/helper-types.md" + }, + { + "name": "general/http-server", + "title": "HTTP Server", + "description": "Different ways to run your RivetKit HTTP server.", + "canonical_url": "https://rivet.dev/docs/general/http-server", + "reference_url": "/metadata/skills/live-cursors/reference/general/http-server.md" + }, + { + "name": "actors/appearance", + "title": "Icons & Names", + "description": "Customize actors with display names and icons for the Rivet inspector and dashboard.", + "canonical_url": "https://rivet.dev/docs/actors/appearance", + "reference_url": "/metadata/skills/live-cursors/reference/actors/appearance.md" + }, + { + "name": "actors/state", + "title": "In-Memory State", + "description": "Actors store state in memory for instant reads and writes. State can be persisted automatically or kept ephemeral.", + "canonical_url": "https://rivet.dev/docs/actors/state", + "reference_url": "/metadata/skills/live-cursors/reference/actors/state.md" + }, + { + "name": "actors/input", + "title": "Input Parameters", + "description": "Pass initialization data to actors when creating instances", + "canonical_url": "https://rivet.dev/docs/actors/input", + "reference_url": "/metadata/skills/live-cursors/reference/actors/input.md" + }, + { + "name": "self-hosting/install", + "title": "Installing Rivet Engine", + "description": "Install Rivet Engine using Docker, binaries, or a source build.", + "canonical_url": "https://rivet.dev/docs/self-hosting/install", + "reference_url": "/metadata/skills/live-cursors/reference/self-hosting/install.md" + }, + { + "name": "self-hosting/kubernetes", + "title": "Kubernetes", + "description": "Deploy production-ready Rivet Engine to Kubernetes with PostgreSQL storage.", + "canonical_url": "https://rivet.dev/docs/self-hosting/kubernetes", + "reference_url": "/metadata/skills/live-cursors/reference/self-hosting/kubernetes.md" + }, + { + "name": "actors/lifecycle", + "title": "Lifecycle", + "description": "Learn about actor lifecycle hooks for initialization, state management, and cleanup.", + "canonical_url": "https://rivet.dev/docs/actors/lifecycle", + "reference_url": "/metadata/skills/live-cursors/reference/actors/lifecycle.md" + }, + { + "name": "actors/limits", + "title": "Limits", + "description": "Limits and constraints for Rivet Actors.", + "canonical_url": "https://rivet.dev/docs/actors/limits", + "reference_url": "/metadata/skills/live-cursors/reference/actors/limits.md" + }, + { + "name": "cookbook/live-cursors", + "title": "Live Cursors and Presence", + "description": "Live cursors and multiplayer presence with Rivet Actors: per-connection cursor state, realtime updates over events or raw WebSockets, and throttling.", + "canonical_url": "https://rivet.dev/cookbook/live-cursors", + "reference_url": "/metadata/skills/live-cursors/reference/cookbook/live-cursors.md" + }, + { + "name": "general/logging", + "title": "Logging", + "description": "Actors provide a built-in way to log complex data to the console.", + "canonical_url": "https://rivet.dev/docs/general/logging", + "reference_url": "/metadata/skills/live-cursors/reference/general/logging.md" + }, + { + "name": "actors/request-handler", + "title": "Low-Level HTTP Request Handler", + "description": "Actors can handle HTTP requests through the `onRequest` handler.", + "canonical_url": "https://rivet.dev/docs/actors/request-handler", + "reference_url": "/metadata/skills/live-cursors/reference/actors/request-handler.md" + }, + { + "name": "actors/kv", + "title": "Low-Level KV Storage", + "description": "Use the built-in key-value store on ActorContext for durable string and binary data alongside actor state.", + "canonical_url": "https://rivet.dev/docs/actors/kv", + "reference_url": "/metadata/skills/live-cursors/reference/actors/kv.md" + }, + { + "name": "actors/websocket-handler", + "title": "Low-Level WebSocket Handler", + "description": "Actors can handle WebSocket connections through the `onWebSocket` handler.", + "canonical_url": "https://rivet.dev/docs/actors/websocket-handler", + "reference_url": "/metadata/skills/live-cursors/reference/actors/websocket-handler.md" + }, + { + "name": "actors/metadata", + "title": "Metadata", + "description": "Metadata provides information about the currently running actor.", + "canonical_url": "https://rivet.dev/docs/actors/metadata", + "reference_url": "/metadata/skills/live-cursors/reference/actors/metadata.md" + }, + { + "name": "self-hosting/multi-region", + "title": "Multi-Region", + "description": "Rivet Engine supports scaling transparently across multiple regions.", + "canonical_url": "https://rivet.dev/docs/self-hosting/multi-region", + "reference_url": "/metadata/skills/live-cursors/reference/self-hosting/multi-region.md" + }, + { + "name": "cookbook/multiplayer-game", + "title": "Multiplayer Game", + "description": "Pragmatic patterns for building multiplayer games: matchmaking, tick loops, realtime state, interest management, and validation.", + "canonical_url": "https://rivet.dev/cookbook/multiplayer-game", + "reference_url": "/metadata/skills/live-cursors/reference/cookbook/multiplayer-game.md" + }, + { + "name": "actors/quickstart/next-js", + "title": "Next.js Quickstart", + "description": "Get started with Rivet Actors in Next.js", + "canonical_url": "https://rivet.dev/docs/actors/quickstart/next-js", + "reference_url": "/metadata/skills/live-cursors/reference/actors/quickstart/next-js.md" + }, + { + "name": "clients/javascript", + "title": "Node.js & Bun", + "description": "Connect JavaScript apps to Rivet Actors.", + "canonical_url": "https://rivet.dev/docs/clients/javascript", + "reference_url": "/metadata/skills/live-cursors/reference/clients/javascript.md" + }, + { + "name": "actors/quickstart/backend", + "title": "Node.js & Bun Quickstart", + "description": "Get started with Rivet Actors in Node.js and Bun", + "canonical_url": "https://rivet.dev/docs/actors/quickstart/backend", + "reference_url": "/metadata/skills/live-cursors/reference/actors/quickstart/backend.md" + }, + { + "name": "general/pool-configuration", + "title": "Pool Configuration", + "description": "Reference for runner pool configuration, including drain behavior, actor eviction rate limiting, and serverless-specific options.", + "canonical_url": "https://rivet.dev/docs/general/pool-configuration", + "reference_url": "/metadata/skills/live-cursors/reference/general/pool-configuration.md" + }, + { + "name": "self-hosting/postgres", + "title": "PostgreSQL", + "description": "Configure PostgreSQL for self-hosted Rivet deployments.", + "canonical_url": "https://rivet.dev/docs/self-hosting/postgres", + "reference_url": "/metadata/skills/live-cursors/reference/self-hosting/postgres.md" + }, + { + "name": "general/production-checklist", + "title": "Production Checklist", + "description": "Checklist for deploying Rivet Actors to production.", + "canonical_url": "https://rivet.dev/docs/general/production-checklist", + "reference_url": "/metadata/skills/live-cursors/reference/general/production-checklist.md" + }, + { + "name": "self-hosting/production-checklist", + "title": "Production Checklist", + "description": "Checklist for deploying a self-hosted Rivet Engine to production.", + "canonical_url": "https://rivet.dev/docs/self-hosting/production-checklist", + "reference_url": "/metadata/skills/live-cursors/reference/self-hosting/production-checklist.md" + }, + { + "name": "actors/queues", + "title": "Queues & Run Loops", + "description": "Use actor-local durable queues for serial run loops and request/response workflows.", + "canonical_url": "https://rivet.dev/docs/actors/queues", + "reference_url": "/metadata/skills/live-cursors/reference/actors/queues.md" + }, + { + "name": "self-hosting/railway", + "title": "Railway Deployment", + "description": "Railway provides a simple platform for deploying Rivet Engine with automatic scaling and managed infrastructure.", + "canonical_url": "https://rivet.dev/docs/self-hosting/railway", + "reference_url": "/metadata/skills/live-cursors/reference/self-hosting/railway.md" + }, + { + "name": "clients/react", + "title": "React", + "description": "Connect React apps to Rivet Actors.", + "canonical_url": "https://rivet.dev/docs/clients/react", + "reference_url": "/metadata/skills/live-cursors/reference/clients/react.md" + }, + { + "name": "actors/quickstart/react", + "title": "React Quickstart", + "description": "Build realtime React applications with Rivet Actors", + "canonical_url": "https://rivet.dev/docs/actors/quickstart/react", + "reference_url": "/metadata/skills/live-cursors/reference/actors/quickstart/react.md" + }, + { + "name": "actors/events", + "title": "Realtime", + "description": "Events enable realtime communication from actors to clients. While clients use actions to send data to actors, events allow actors to push updates to connected clients instantly.", + "canonical_url": "https://rivet.dev/docs/actors/events", + "reference_url": "/metadata/skills/live-cursors/reference/actors/events.md" + }, + { + "name": "general/registry-configuration", + "title": "Registry Configuration", + "description": "This page documents the configuration options available when setting up a RivetKit registry. The registry configuration is passed to the `setup()` function.", + "canonical_url": "https://rivet.dev/docs/general/registry-configuration", + "reference_url": "/metadata/skills/live-cursors/reference/general/registry-configuration.md" + }, + { + "name": "self-hosting/render", + "title": "Render Deployment", + "description": "Deploy Rivet Engine to Render with managed PostgreSQL and automatic HTTPS, using the experimental PostgreSQL backend.", + "canonical_url": "https://rivet.dev/docs/self-hosting/render", + "reference_url": "/metadata/skills/live-cursors/reference/self-hosting/render.md" + }, + { + "name": "general/runtime-modes", + "title": "Runtime Modes", + "description": "RivetKit supports two runtime modes for running your actors:", + "canonical_url": "https://rivet.dev/docs/general/runtime-modes", + "reference_url": "/metadata/skills/live-cursors/reference/general/runtime-modes.md" + }, + { + "name": "clients/rust", + "title": "Rust (Beta)", + "description": "Connect Rust apps to Rivet Actors.", + "canonical_url": "https://rivet.dev/docs/clients/rust", + "reference_url": "/metadata/skills/live-cursors/reference/clients/rust.md" + }, + { + "name": "actors/quickstart/rust", + "title": "Rust Quickstart (Beta)", + "description": "Build a Rivet Actor in Rust", + "canonical_url": "https://rivet.dev/docs/actors/quickstart/rust", + "reference_url": "/metadata/skills/live-cursors/reference/actors/quickstart/rust.md" + }, + { + "name": "actors/scaling", + "title": "Scaling & Concurrency", + "description": "This page has moved to [design patterns](/docs/actors/design-patterns).", + "canonical_url": "https://rivet.dev/docs/actors/scaling", + "reference_url": "/metadata/skills/live-cursors/reference/actors/scaling.md" + }, + { + "name": "actors/schedule", + "title": "Schedule & Cron", + "description": "Run durable one-shot and recurring actor actions on a schedule.", + "canonical_url": "https://rivet.dev/docs/actors/schedule", + "reference_url": "/metadata/skills/live-cursors/reference/actors/schedule.md" + }, + { + "name": "actors/sharing-and-joining-state", + "title": "Sharing and Joining State", + "description": "This page has moved to [design patterns](/docs/actors/design-patterns).", + "canonical_url": "https://rivet.dev/docs/actors/sharing-and-joining-state", + "reference_url": "/metadata/skills/live-cursors/reference/actors/sharing-and-joining-state.md" + }, + { + "name": "actors/sqlite", + "title": "SQLite", + "description": "Use embedded SQLite in Rivet Actors with raw SQL queries.", + "canonical_url": "https://rivet.dev/docs/actors/sqlite", + "reference_url": "/metadata/skills/live-cursors/reference/actors/sqlite.md" + }, + { + "name": "actors/sqlite-drizzle", + "title": "SQLite + Drizzle", + "description": "Use Drizzle ORM with embedded SQLite in Rivet Actors.", + "canonical_url": "https://rivet.dev/docs/actors/sqlite-drizzle", + "reference_url": "/metadata/skills/live-cursors/reference/actors/sqlite-drizzle.md" + }, + { + "name": "actors/quickstart/supabase", + "title": "Supabase Functions Quickstart", + "description": "Set up a Rivet project locally targeting Supabase Edge Functions.", + "canonical_url": "https://rivet.dev/docs/actors/quickstart/supabase", + "reference_url": "/metadata/skills/live-cursors/reference/actors/quickstart/supabase.md" + }, + { + "name": "clients/swift", + "title": "Swift", + "description": "Connect Swift apps to Rivet Actors.", + "canonical_url": "https://rivet.dev/docs/clients/swift", + "reference_url": "/metadata/skills/live-cursors/reference/clients/swift.md" + }, + { + "name": "clients/swiftui", + "title": "SwiftUI", + "description": "Build SwiftUI apps with Rivet Actors.", + "canonical_url": "https://rivet.dev/docs/clients/swiftui", + "reference_url": "/metadata/skills/live-cursors/reference/clients/swiftui.md" + }, + { + "name": "actors/testing", + "title": "Testing", + "description": "Rivet provides a straightforward testing framework to build reliable and maintainable applications. This guide covers how to write effective tests for your actor-based services.", + "canonical_url": "https://rivet.dev/docs/actors/testing", + "reference_url": "/metadata/skills/live-cursors/reference/actors/testing.md" + }, + { + "name": "self-hosting/tls", + "title": "TLS & Certificates", + "description": "How Rivet validates TLS root certificates.", + "canonical_url": "https://rivet.dev/docs/self-hosting/tls", + "reference_url": "/metadata/skills/live-cursors/reference/self-hosting/tls.md" + }, + { + "name": "actors/troubleshooting", + "title": "Troubleshooting", + "description": "Common issues with Rivet Actors and how to resolve them.", + "canonical_url": "https://rivet.dev/docs/actors/troubleshooting", + "reference_url": "/metadata/skills/live-cursors/reference/actors/troubleshooting.md" + }, + { + "name": "actors/types", + "title": "Types", + "description": "TypeScript types for working with Rivet Actors. This page covers context types used in lifecycle hooks and actions, as well as helper types for extracting types from actor definitions.", + "canonical_url": "https://rivet.dev/docs/actors/types", + "reference_url": "/metadata/skills/live-cursors/reference/actors/types.md" + }, + { + "name": "actors/http-api", + "title": "Vanilla HTTP API", + "description": "Use the low-level HTTP handler to send and receive requests from actors.", + "canonical_url": "https://rivet.dev/docs/actors/http-api", + "reference_url": "/metadata/skills/live-cursors/reference/actors/http-api.md" + }, + { + "name": "actors/versions", + "title": "Versions & Upgrades", + "description": "When you deploy new code, Rivet ensures actors are upgraded seamlessly without downtime.", + "canonical_url": "https://rivet.dev/docs/actors/versions", + "reference_url": "/metadata/skills/live-cursors/reference/actors/versions.md" + }, + { + "name": "general/wasm-vs-native-sdk", + "title": "WASM vs Native SDK", + "description": "RivetKit runs your actors on a native or a WebAssembly runtime depending on your platform.", + "canonical_url": "https://rivet.dev/docs/general/wasm-vs-native-sdk", + "reference_url": "/metadata/skills/live-cursors/reference/general/wasm-vs-native-sdk.md" + }, + { + "name": "actors/workflows", + "title": "Workflows", + "description": "Build durable, replayable run loops in Rivet Actors with steps, queue waits, timers, and rollback.", + "canonical_url": "https://rivet.dev/docs/actors/workflows", + "reference_url": "/metadata/skills/live-cursors/reference/actors/workflows.md" + } + ] +} \ No newline at end of file diff --git a/.agents/skills/live-cursors/openapi.json b/.agents/skills/live-cursors/openapi.json new file mode 100644 index 0000000..ca482f7 --- /dev/null +++ b/.agents/skills/live-cursors/openapi.json @@ -0,0 +1,1311 @@ +{ + "openapi": "3.0.0", + "info": { + "version": "2.2.0", + "title": "RivetKit API" + }, + "components": { + "schemas": {}, + "parameters": {} + }, + "paths": { + "/actors": { + "get": { + "parameters": [ + { + "schema": { + "type": "string" + }, + "required": false, + "name": "name", + "in": "query" + }, + { + "schema": { + "type": "string" + }, + "required": false, + "name": "actor_ids", + "in": "query" + }, + { + "schema": { + "type": "string" + }, + "required": false, + "name": "key", + "in": "query" + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "actors": { + "type": "array", + "items": { + "type": "object", + "properties": { + "actor_id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "key": { + "type": "string" + }, + "namespace_id": { + "type": "string" + }, + "runner_name_selector": { + "type": "string" + }, + "create_ts": { + "type": "number" + }, + "connectable_ts": { + "type": "number", + "nullable": true + }, + "destroy_ts": { + "type": "number", + "nullable": true + }, + "sleep_ts": { + "type": "number", + "nullable": true + }, + "start_ts": { + "type": "number", + "nullable": true + }, + "error": { + "nullable": true + } + }, + "required": [ + "actor_id", + "name", + "key", + "namespace_id", + "runner_name_selector", + "create_ts" + ] + } + } + }, + "required": [ + "actors" + ] + } + } + } + }, + "400": { + "description": "User error" + }, + "500": { + "description": "Internal error" + } + } + }, + "put": { + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "datacenter": { + "type": "string" + }, + "name": { + "type": "string" + }, + "key": { + "type": "string" + }, + "runner_name_selector": { + "type": "string" + }, + "crash_policy": { + "type": "string" + }, + "input": { + "type": "string", + "nullable": true + } + }, + "required": [ + "name", + "key", + "runner_name_selector", + "crash_policy" + ] + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "actor": { + "type": "object", + "properties": { + "actor_id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "key": { + "type": "string" + }, + "namespace_id": { + "type": "string" + }, + "runner_name_selector": { + "type": "string" + }, + "create_ts": { + "type": "number" + }, + "connectable_ts": { + "type": "number", + "nullable": true + }, + "destroy_ts": { + "type": "number", + "nullable": true + }, + "sleep_ts": { + "type": "number", + "nullable": true + }, + "start_ts": { + "type": "number", + "nullable": true + }, + "error": { + "nullable": true + } + }, + "required": [ + "actor_id", + "name", + "key", + "namespace_id", + "runner_name_selector", + "create_ts" + ] + }, + "created": { + "type": "boolean" + } + }, + "required": [ + "actor", + "created" + ] + } + } + } + }, + "400": { + "description": "User error" + }, + "500": { + "description": "Internal error" + } + } + }, + "post": { + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "datacenter": { + "type": "string" + }, + "name": { + "type": "string" + }, + "runner_name_selector": { + "type": "string" + }, + "crash_policy": { + "type": "string" + }, + "key": { + "type": "string", + "nullable": true + }, + "input": { + "type": "string", + "nullable": true + } + }, + "required": [ + "name", + "runner_name_selector", + "crash_policy" + ] + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "actor": { + "type": "object", + "properties": { + "actor_id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "key": { + "type": "string" + }, + "namespace_id": { + "type": "string" + }, + "runner_name_selector": { + "type": "string" + }, + "create_ts": { + "type": "number" + }, + "connectable_ts": { + "type": "number", + "nullable": true + }, + "destroy_ts": { + "type": "number", + "nullable": true + }, + "sleep_ts": { + "type": "number", + "nullable": true + }, + "start_ts": { + "type": "number", + "nullable": true + }, + "error": { + "nullable": true + } + }, + "required": [ + "actor_id", + "name", + "key", + "namespace_id", + "runner_name_selector", + "create_ts" + ] + } + }, + "required": [ + "actor" + ] + } + } + } + }, + "400": { + "description": "User error" + }, + "500": { + "description": "Internal error" + } + } + } + }, + "/actors/names": { + "get": { + "parameters": [ + { + "schema": { + "type": "string" + }, + "required": true, + "name": "namespace", + "in": "query" + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "names": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "metadata": { + "type": "object", + "additionalProperties": { + "nullable": true + } + } + }, + "required": [ + "metadata" + ] + } + } + }, + "required": [ + "names" + ] + } + } + } + }, + "400": { + "description": "User error" + }, + "500": { + "description": "Internal error" + } + } + } + }, + "/actors/{actor_id}/kv/keys/{key}": { + "get": { + "parameters": [ + { + "schema": { + "type": "string" + }, + "required": true, + "name": "actor_id", + "in": "path" + }, + { + "schema": { + "type": "string" + }, + "required": true, + "name": "key", + "in": "path" + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "value": { + "type": "string", + "nullable": true + } + }, + "required": [ + "value" + ] + } + } + } + }, + "400": { + "description": "User error" + }, + "500": { + "description": "Internal error" + } + } + } + }, + "/gateway/{actorId}/health": { + "get": { + "parameters": [ + { + "name": "actorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the actor to target" + } + ], + "responses": { + "200": { + "description": "Health check", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/gateway/{actorId}/action/{action}": { + "post": { + "parameters": [ + { + "name": "actorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the actor to target" + }, + { + "name": "action", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The name of the action to execute" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "args": {} + }, + "required": [ + "args" + ], + "additionalProperties": false + } + } + } + }, + "responses": { + "200": { + "description": "Action executed successfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "output": {} + }, + "required": [ + "output" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "Invalid action" + }, + "500": { + "description": "Internal error" + } + } + } + }, + "/gateway/{actorId}/request/{path}": { + "get": { + "parameters": [ + { + "name": "actorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the actor to target" + }, + { + "name": "path", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The HTTP path to forward to the actor" + } + ], + "responses": { + "200": { + "description": "Response from actor's raw HTTP handler" + } + } + }, + "post": { + "parameters": [ + { + "name": "actorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the actor to target" + }, + { + "name": "path", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The HTTP path to forward to the actor" + } + ], + "responses": { + "200": { + "description": "Response from actor's raw HTTP handler" + } + } + }, + "put": { + "parameters": [ + { + "name": "actorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the actor to target" + }, + { + "name": "path", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The HTTP path to forward to the actor" + } + ], + "responses": { + "200": { + "description": "Response from actor's raw HTTP handler" + } + } + }, + "delete": { + "parameters": [ + { + "name": "actorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the actor to target" + }, + { + "name": "path", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The HTTP path to forward to the actor" + } + ], + "responses": { + "200": { + "description": "Response from actor's raw HTTP handler" + } + } + }, + "patch": { + "parameters": [ + { + "name": "actorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the actor to target" + }, + { + "name": "path", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The HTTP path to forward to the actor" + } + ], + "responses": { + "200": { + "description": "Response from actor's raw HTTP handler" + } + } + }, + "head": { + "parameters": [ + { + "name": "actorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the actor to target" + }, + { + "name": "path", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The HTTP path to forward to the actor" + } + ], + "responses": { + "200": { + "description": "Response from actor's raw HTTP handler" + } + } + }, + "options": { + "parameters": [ + { + "name": "actorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the actor to target" + }, + { + "name": "path", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The HTTP path to forward to the actor" + } + ], + "responses": { + "200": { + "description": "Response from actor's raw HTTP handler" + } + } + } + }, + "/gateway/{actorId}/inspector/state": { + "get": { + "parameters": [ + { + "name": "actorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the actor to target" + }, + { + "name": "Authorization", + "in": "header", + "required": false, + "schema": { + "type": "string" + }, + "description": "Bearer token for inspector authentication. Required in production, optional in development." + } + ], + "responses": { + "200": { + "description": "Current actor state", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "state": {} + } + } + } + } + }, + "401": { + "description": "Unauthorized" + } + } + }, + "patch": { + "parameters": [ + { + "name": "actorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the actor to target" + }, + { + "name": "Authorization", + "in": "header", + "required": false, + "schema": { + "type": "string" + }, + "description": "Bearer token for inspector authentication. Required in production, optional in development." + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "state": {} + }, + "required": [ + "state" + ] + } + } + } + }, + "responses": { + "200": { + "description": "State updated", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "ok": { + "type": "boolean" + } + } + } + } + } + }, + "401": { + "description": "Unauthorized" + } + } + } + }, + "/gateway/{actorId}/inspector/connections": { + "get": { + "parameters": [ + { + "name": "actorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the actor to target" + }, + { + "name": "Authorization", + "in": "header", + "required": false, + "schema": { + "type": "string" + }, + "description": "Bearer token for inspector authentication. Required in production, optional in development." + } + ], + "responses": { + "200": { + "description": "Current actor connections", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "connections": { + "type": "array", + "items": { + "type": "object" + } + } + } + } + } + } + }, + "401": { + "description": "Unauthorized" + } + } + } + }, + "/gateway/{actorId}/inspector/rpcs": { + "get": { + "parameters": [ + { + "name": "actorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the actor to target" + }, + { + "name": "Authorization", + "in": "header", + "required": false, + "schema": { + "type": "string" + }, + "description": "Bearer token for inspector authentication. Required in production, optional in development." + } + ], + "responses": { + "200": { + "description": "Available actor actions/RPCs", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "rpcs": { + "type": "object" + } + } + } + } + } + }, + "401": { + "description": "Unauthorized" + } + } + } + }, + "/gateway/{actorId}/inspector/action/{name}": { + "post": { + "parameters": [ + { + "name": "actorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the actor to target" + }, + { + "name": "name", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The name of the action to execute" + }, + { + "name": "Authorization", + "in": "header", + "required": false, + "schema": { + "type": "string" + }, + "description": "Bearer token for inspector authentication. Required in production, optional in development." + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "args": { + "type": "array", + "items": {} + } + } + } + } + } + }, + "responses": { + "200": { + "description": "Action executed successfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "output": {} + } + } + } + } + }, + "401": { + "description": "Unauthorized" + } + } + } + }, + "/gateway/{actorId}/inspector/queue": { + "get": { + "parameters": [ + { + "name": "actorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the actor to target" + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 50 + }, + "description": "Maximum number of queue messages to return" + }, + { + "name": "Authorization", + "in": "header", + "required": false, + "schema": { + "type": "string" + }, + "description": "Bearer token for inspector authentication. Required in production, optional in development." + } + ], + "responses": { + "200": { + "description": "Queue status", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "size": { + "type": "integer" + }, + "maxSize": { + "type": "integer" + }, + "truncated": { + "type": "boolean" + }, + "messages": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "createdAtMs": { + "type": "integer" + } + } + } + } + } + } + } + } + }, + "401": { + "description": "Unauthorized" + } + } + } + }, + "/gateway/{actorId}/inspector/traces": { + "get": { + "parameters": [ + { + "name": "actorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the actor to target" + }, + { + "name": "startMs", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 0 + }, + "description": "Start of time range in epoch milliseconds" + }, + { + "name": "endMs", + "in": "query", + "required": false, + "schema": { + "type": "integer" + }, + "description": "End of time range in epoch milliseconds. Defaults to now." + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 1000 + }, + "description": "Maximum number of spans to return" + }, + { + "name": "Authorization", + "in": "header", + "required": false, + "schema": { + "type": "string" + }, + "description": "Bearer token for inspector authentication. Required in production, optional in development." + } + ], + "responses": { + "200": { + "description": "Trace spans in OTLP JSON format", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "otlp": { + "type": "object" + }, + "clamped": { + "type": "boolean" + } + } + } + } + } + }, + "401": { + "description": "Unauthorized" + } + } + } + }, + "/gateway/{actorId}/inspector/workflow-history": { + "get": { + "parameters": [ + { + "name": "actorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the actor to target" + }, + { + "name": "Authorization", + "in": "header", + "required": false, + "schema": { + "type": "string" + }, + "description": "Bearer token for inspector authentication. Required in production, optional in development." + } + ], + "responses": { + "200": { + "description": "Workflow history and status", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "history": {}, + "isWorkflowEnabled": { + "type": "boolean" + } + } + } + } + } + }, + "401": { + "description": "Unauthorized" + } + } + } + }, + "/gateway/{actorId}/inspector/workflow/replay": { + "post": { + "parameters": [ + { + "name": "actorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the actor to target" + }, + { + "name": "Authorization", + "in": "header", + "required": false, + "schema": { + "type": "string" + }, + "description": "Bearer token for inspector authentication. Required in production, optional in development." + } + ], + "requestBody": { + "required": false, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "entryId": { + "type": "string" + } + } + } + } + } + }, + "responses": { + "200": { + "description": "Workflow history after scheduling a replay", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "history": {}, + "isWorkflowEnabled": { + "type": "boolean" + } + } + } + } + } + }, + "401": { + "description": "Unauthorized" + } + } + } + }, + "/gateway/{actorId}/inspector/summary": { + "get": { + "parameters": [ + { + "name": "actorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the actor to target" + }, + { + "name": "Authorization", + "in": "header", + "required": false, + "schema": { + "type": "string" + }, + "description": "Bearer token for inspector authentication. Required in production, optional in development." + } + ], + "responses": { + "200": { + "description": "Full actor inspector summary", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "state": {}, + "connections": { + "type": "array", + "items": { + "type": "object" + } + }, + "rpcs": { + "type": "object" + }, + "queueSize": { + "type": "integer" + }, + "isStateEnabled": { + "type": "boolean" + }, + "isDatabaseEnabled": { + "type": "boolean" + }, + "isWorkflowEnabled": { + "type": "boolean" + }, + "workflowHistory": {} + } + } + } + } + }, + "401": { + "description": "Unauthorized" + } + } + } + } + } +} \ No newline at end of file diff --git a/.agents/skills/live-cursors/reference/actors/access-control.md b/.agents/skills/live-cursors/reference/actors/access-control.md new file mode 100644 index 0000000..02dc4e8 --- /dev/null +++ b/.agents/skills/live-cursors/reference/actors/access-control.md @@ -0,0 +1,48 @@ +# Access Control + +> Source: `src/content/docs/actors/access-control.mdx` +> Canonical URL: https://rivet.dev/docs/actors/access-control +> Description: Authorize actions, queue publishes, and event subscriptions with explicit hooks. + +--- +Use access control to decide what authenticated clients are allowed to do. + +This is authorization, not authentication: + +- Use [authentication](/docs/actors/authentication) to identify who is calling. +- Use access-control rules to decide what they can do after connecting. + +## Permission Surfaces + +RivetKit authorization is explicit per surface: + +- `onBeforeConnect` rejects unauthenticated or malformed connections. +- Action handlers (`actions.*`) enforce action permissions. +- `queues..canPublish` allows or denies inbound queue publishes. +- `events..canSubscribe` allows or denies event subscriptions. + +## Fail By Default + +Use deny-by-default rules everywhere: + +1. Keep `onBeforeConnect` strict and reject invalid credentials. +2. In each action, explicitly allow expected roles and throw `forbidden` otherwise. +3. In `canPublish` and `canSubscribe`, return `true` only for allowed roles and end with `return false`. + +## Return Value Contract + +`canPublish` and `canSubscribe` must return a boolean: + +- `true`: allow +- `false`: deny with `forbidden` + +Returning `undefined`, `null`, or any non-boolean throws an internal error. + +## Notes + +- `canPublish` only applies to queue names defined in `queues`. +- Incoming queue messages for undefined queues are ignored and the publish succeeds as completed. +- `canSubscribe` only applies to event names defined in `events`. +- Broadcasting an event not defined in `events` still publishes to subscribers. + +_Source doc path: /docs/actors/access-control_ diff --git a/.agents/skills/live-cursors/reference/actors/actions.md b/.agents/skills/live-cursors/reference/actors/actions.md new file mode 100644 index 0000000..d00324e --- /dev/null +++ b/.agents/skills/live-cursors/reference/actors/actions.md @@ -0,0 +1,98 @@ +# Actions + +> Source: `src/content/docs/actors/actions.mdx` +> Canonical URL: https://rivet.dev/docs/actors/actions +> Description: Actions are how your backend, frontend, or other actors can communicate with actors. + +--- +Actions are very lightweight. They can be called thousands of times per second safely. Actions are executed via HTTP requests or via WebSockets if [using `.connect()`](/docs/actors/connections). + +For advanced use cases that require direct access to HTTP requests or WebSocket connections, see [raw HTTP and WebSocket handling](/docs/actors/fetch-and-websocket-handler). + +By default, actions run in parallel. If you need advanced control over concurrency, use [queues](/docs/actors/queues). + +## Writing Actions + +Actions are defined in the `actions` object when creating an actor: + +Each action receives a context object (commonly named `c`) as its first parameter, which provides access to state, connections, and other utilities. Additional parameters follow after that. + +## Calling Actions + +Actions can be called in different ways depending on your use case: + +### Frontend (createClient) + +Learn more about [communicating with actors from the frontend](/docs/actors/communicating-between-actors). + +### Backend (registry.handler) + +Learn more about [communicating with actors from the backend](/docs/actors/communicating-between-actors). + +### Actor-to-Actor (c.client()) + +Learn more about [communicating between actors](/docs/actors/communicating-between-actors). + +Calling actions from the client are async and require an `await`, even if the action itself is not async. + +### Type Safety + +The actor client includes type safety out of the box. When you use `createClient()`, TypeScript automatically infers action parameter and return types: + +## Error Handling + +Actors provide robust error handling out of the box for actions. + +### User Errors + +`UserError` can be used to return rich error data to the client. You can provide: + +- A human-readable message +- A machine-readable code that's useful for matching errors in a try-catch (optional) +- A metadata object for providing richer error context (optional) + +For example: + +### Internal Errors + +All other errors will return an error with the code `internal_error` to the client. This helps keep your application secure, as errors can sometimes expose sensitive information. + +## Schema Validation + +If passing data to an actor from the frontend, use a library like [Zod](https://zod.dev/) to validate input data. + +For example, to validate action parameters: + +## Streaming Data + +Actions have a single return value. To stream realtime data in response to an action, use [events](/docs/actors/events). + +## Canceling Long-Running Actions + +For operations that should be cancelable on-demand, create your own `AbortController`. Chain it with `c.abortSignal` so actor shutdown also cancels the operation. + +See [Actor Shutdown Abort Signal](/docs/actors/lifecycle#actor-shutdown-abort-signal) for automatically canceling operations when the actor stops. + +## Using `ActionContext` Externally + +When writing complex logic for actions, you may want to extract parts of your implementation into separate helper functions. When doing this, you'll need a way to properly type the context parameter. + +Rivet provides the `ActionContextOf` utility type for exactly this purpose: + +See [types](/docs/actors/types) for more details on using `ActionContextOf` and other utility types. + +## Debugging + +- `GET /inspector/rpcs` lists all available actions on an actor. +- `POST /inspector/action/:name` executes an action with JSON args and returns output. +- In non-dev mode, inspector endpoints require authorization. + +## API Reference + +- [`Actions`](/typedoc/interfaces/rivetkit.mod.Actions.html) - Interface for defining actions +- [`ActionContext`](/typedoc/interfaces/rivetkit.mod.ActionContext.html) - Context available in action handlers +- [`ActorDefinition`](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html) - Interface for defining actors with actions +- [`ActorHandle`](/typedoc/types/rivetkit.client_mod.ActorHandle.html) - Handle for calling actions from client +- [`ActorActionFunction`](/typedoc/types/rivetkit.client_mod.ActorActionFunction.html) - Type for action functions + +_Source doc path: /docs/actors/actions_ diff --git a/.agents/skills/live-cursors/reference/actors/actor-runtime-socket.md b/.agents/skills/live-cursors/reference/actors/actor-runtime-socket.md new file mode 100644 index 0000000..457472c --- /dev/null +++ b/.agents/skills/live-cursors/reference/actors/actor-runtime-socket.md @@ -0,0 +1,62 @@ +# Actor Runtime Socket + +> Source: `src/content/docs/actors/actor-runtime-socket.mdx` +> Canonical URL: https://rivet.dev/docs/actors/actor-runtime-socket +> Description: Access an actor's SQLite database from another local process. + +--- +The Actor Runtime Socket is an advanced Beta API for integrating Rivet Actors with external software without sacrificing performance. It currently exposes SQLite over Unix domain sockets in the native runtime. + +For example, an agentOS agent can use the socket to work with its actor's SQLite database. Software orchestrated by an actor, such as another process or container, can use it to access the actor's runtime resources. Like the Chrome DevTools Protocol exposes a browser to external tools, the Actor Runtime Socket exposes selected actor capabilities to colocated software. + +## Quickstart + +Enable the socket, call `c.actorRuntimeSocket()` to get its path, and pass that path to your process: + +```ts actor.ts +import { spawn } from "node:child_process"; +import { actor } from "rivetkit"; +import { db } from "rivetkit/db"; + +export const example = actor({ + options: { enableActorRuntimeSocket: true }, + db: db({ + onMigrate: (db) => + db.execute("CREATE TABLE IF NOT EXISTS items (value TEXT)"), + }), + actions: { + startWorker: async (c) => { + // Get the Unix socket path external processes use + // to communicate with the actor. + // (Example: /tmp/rivet-actor-runtime.abc/def.sock) + const { path } = await c.actorRuntimeSocket(); + + // Spawn a process that can access the actor's + // SQLite database over the socket. + spawn(process.execPath, ["worker.js"], { + env: { ...process.env, ACTOR_RUNTIME_SOCKET_PATH: path }, + }); + }, + }, +}); +``` + +```ts worker.ts +import { connectRuntimeSocket } from "./generated/runtime-socket-client"; + +const db = await connectRuntimeSocket(process.env.ACTOR_RUNTIME_SOCKET_PATH!); +const rows = await db.query("SELECT value FROM items", []); +console.log(rows); +``` + +The path is valid only for the current actor generation, so get it again after the actor restarts or wakes. Only share it with trusted local processes. + +## Protocol + +The [protocol schema](https://github.com/rivet-dev/rivet/blob/main/engine/sdks/rust/actor-runtime-socket-protocol/schemas/v1.bare) defines the handshake, SQLite requests, values, responses, and errors. Generate codecs using vbare's [TypeScript](https://github.com/rivet-dev/vbare/tree/main/typescript) or [Rust](https://github.com/rivet-dev/vbare/tree/main/rust) tooling, then add a small Unix socket wrapper for its length-prefixed frames. `connectRuntimeSocket` above represents that application-specific wrapper. + +## SQLite transaction interleaving + +Separate socket requests can interleave with SQLite work from the main actor. For multi-request atomic work, begin a transaction with a client-generated `leaseKey`, include that key with each query, and commit or roll back with the same key. Other actor and socket SQL waits until that transaction finishes. + +_Source doc path: /docs/actors/actor-runtime-socket_ diff --git a/.agents/skills/live-cursors/reference/actors/appearance.md b/.agents/skills/live-cursors/reference/actors/appearance.md new file mode 100644 index 0000000..f60b1a1 --- /dev/null +++ b/.agents/skills/live-cursors/reference/actors/appearance.md @@ -0,0 +1,50 @@ +# Icons & Names + +> Source: `src/content/docs/actors/appearance.mdx` +> Canonical URL: https://rivet.dev/docs/actors/appearance +> Description: Customize actors with display names and icons for the Rivet inspector and dashboard. + +--- +# Icons & Names + +Actors can be customized with a display name and icon that appear in the Rivet inspector & dashboard. This helps identify actors at a glance when managing your application. + +## Configuration + +Set the `name` and `icon` properties in your actor's `options`: + +## Icon Formats + +The `icon` property accepts two formats: + +### Emoji + +Use any emoji character directly: + +### FontAwesome Icons + +Use [FontAwesome](https://fontawesome.com/search) icon names without the "fa" prefix: + +## Default Behavior + +If no `icon` is specified, actors display the default actor icon. If no `name` is specified, the actor's registry key (e.g., `chatRoom`, `gameServer`) is displayed instead. + +## Examples + +Here are some common patterns: + +## Advanced: Run Handler Metadata + +For library developers creating reusable run handlers, you can bundle icon and name metadata directly with the `run` property. This allows libraries to provide sensible defaults without requiring users to configure them manually. + +Instead of returning a function from your run handler factory, return an object with `name`, `icon`, and `run`: + +Users can then use this directly: + +This run-handler metadata is currently applied through the registry and serverless metadata paths. The native runtime and inspector config read the actor's `options.name` and `options.icon` directly, so set those explicitly if you need the name or icon to appear everywhere. + +Actor-level `options.name` and `options.icon` always take precedence, allowing users to override library defaults: + +The built-in `workflow()` helper uses this pattern to automatically display the workflow icon for workflow-based actors. + +_Source doc path: /docs/actors/appearance_ diff --git a/.agents/skills/live-cursors/reference/actors/authentication.md b/.agents/skills/live-cursors/reference/actors/authentication.md new file mode 100644 index 0000000..c456dde --- /dev/null +++ b/.agents/skills/live-cursors/reference/actors/authentication.md @@ -0,0 +1,105 @@ +# Authentication + +> Source: `src/content/docs/actors/authentication.mdx` +> Canonical URL: https://rivet.dev/docs/actors/authentication +> Description: Secure your actors with authentication and authorization. + +--- +## Do You Need Authentication? + +### Rivet Cloud + + Actors are private by default on Rivet Cloud. Only requests with the publishable token can interact with actors. + + - **Backend-only actors**: If your publishable token is only included in your backend, then authentication is not necessary. + - **Frontend-accessible actors**: If your publishable token is included in your frontend, then implementing authentication is recommended. + +### Self-Hosted + + Actors are public by default on self-hosted Rivet. Anyone can access them without a token. + + - **Only accessible within private network**: If Rivet is only accessible within your private network, then authentication is not necessary. + - **Rivet exposed to the public internet**: If Rivet is configured to accept traffic from the public internet, then implementing authentication is recommended. + +## Authentication Connections + +Authentication is configured through either: + +- `onBeforeConnect` for simple pass/fail validation +- `createConnState` when you need to access user data in your actions via `c.conn.state` + +## Access Control + +After a connection is authenticated, use [Access Control](/docs/actors/access-control) to enforce authorization: + +- Check permissions in action handlers. +- Use `queues..canPublish` to gate inbound queue publishes. +- Use `events..canSubscribe` to gate event subscriptions. + +### `onBeforeConnect` + +The `onBeforeConnect` hook validates credentials before allowing a connection. Throw an error to reject the connection. + +### `createConnState` + +Use `createConnState` to extract user data from credentials and store it in connection state. This data is accessible in actions via `c.conn.state`. Like `onBeforeConnect`, throwing an error will reject the connection. See [connections](/docs/actors/connections) for more details. + +## Available Auth Data + +Authentication hooks have access to several properties: + +| Property | Description | +|----------|-------------| +| `params` | Custom data passed by the client when connecting (see [connection params](/docs/actors/connections#extracting-data-from-connection-params)) | +| `c.request` | The underlying HTTP request object | +| `c.request.headers` | Request headers for tokens, API keys (does not work for `.connect()`) | +| `c.state` | Actor state for authorization decisions (see [state](/docs/actors/state)) | +| `c.key` | The actor's key (see [keys](/docs/actors/keys)) | + +It's recommended to use `params` instead of `c.request.headers` whenever possible since it works for both HTTP & WebSocket connections. + +## Client Usage + +### Passing Credentials + +Pass authentication data when connecting. Use `getParams` when you need a fresh JWT for every connection or reconnect: + +### Handling Errors + +Authentication errors use the same system as regular errors. See [errors](/docs/actors/errors) for more details. + +## Examples + +### JWT + +Validate JSON Web Tokens and extract user claims: + +### External Auth Provider + +Validate credentials against an external authentication service: + +### Using `c.state` In Authorization + +Access actor state via `c.state` and the actor's key via `c.key` to make authorization decisions: + +### Role-Based Access Control + +Create helper functions for common authorization patterns: + +### Rate Limiting + +Use `c.vars` to track connection attempts and rate limit by user: + +The limits in this example are [ephemeral](/docs/actors/state#ephemeral-variables). If you wish to persist rate limits, you can optionally replace `vars` with `state`. + +### Caching Tokens + +Cache validated tokens in `c.vars` to avoid redundant validation on repeated connections. See [ephemeral variables](/docs/actors/state#ephemeral-variables) for more details. + +## API Reference + +- [`AuthIntent`](/typedoc/types/rivetkit.mod.AuthIntent.html) - Authentication intent type +- [`OnBeforeConnectContext`](/typedoc/interfaces/rivetkit.mod.OnBeforeConnectContext.html) - Context for auth checks +- [`OnConnectContext`](/typedoc/interfaces/rivetkit.mod.OnConnectContext.html) - Context after connection + +_Source doc path: /docs/actors/authentication_ diff --git a/.agents/skills/live-cursors/reference/actors/communicating-between-actors.md b/.agents/skills/live-cursors/reference/actors/communicating-between-actors.md new file mode 100644 index 0000000..8aeaf39 --- /dev/null +++ b/.agents/skills/live-cursors/reference/actors/communicating-between-actors.md @@ -0,0 +1,42 @@ +# Communicating Between Actors + +> Source: `src/content/docs/actors/communicating-between-actors.mdx` +> Canonical URL: https://rivet.dev/docs/actors/communicating-between-actors +> Description: Learn how actors can call other actors and share data + +--- +Actors can communicate with each other using the server-side actor client, enabling complex workflows and data sharing between different actor instances. + +We recommend reading the [clients documentation](/docs/clients) first. This guide focuses specifically on communication between actors. + +## Using the Server-Side Actor Client + +The server-side actor client allows actors to call other actors within the same registry. Access it via `c.client()` in your actor context: + +If two actors call each other and their return types are inferred from the other actor's response, you may hit circular type errors (`TS2322`, `TS2722`, or `c.state` becoming `unknown`). Fix this by writing explicit return types on those actions. + +## Use Cases and Patterns + +### Actor Orchestration + +Use a coordinator actor to manage complex workflows: + +### Data Aggregation + +Collect data from multiple actors: + +### Event-Driven Architecture + +Use connections to listen for events from other actors: + +### Batch Operations + +Process multiple items in parallel: + +## API Reference + +- [`ActorHandle`](/typedoc/types/rivetkit.client_mod.ActorHandle.html) - Handle for calling other actors +- [`Client`](/typedoc/types/rivetkit.mod.Client.html) - Client type for actor communication +- [`ActorAccessor`](/typedoc/interfaces/rivetkit.client_mod.ActorAccessor.html) - Accessor for getting actor handles + +_Source doc path: /docs/actors/communicating-between-actors_ diff --git a/.agents/skills/live-cursors/reference/actors/connections.md b/.agents/skills/live-cursors/reference/actors/connections.md new file mode 100644 index 0000000..e5f0384 --- /dev/null +++ b/.agents/skills/live-cursors/reference/actors/connections.md @@ -0,0 +1,119 @@ +# Connections + +> Source: `src/content/docs/actors/connections.mdx` +> Canonical URL: https://rivet.dev/docs/actors/connections +> Description: Connections represent client connections to your actor. They provide a way to handle client authentication, manage connection-specific data, and control the connection lifecycle. + +--- +For documentation on connecting to actors from clients, see the [Clients documentation](/docs/clients). + +## Parameters + +When clients connect to an actor, they can pass connection parameters that are handled during the connection process. Use `params` for static values, or `getParams` when you need fresh connection data each time a connection opens. + +For example: + +## Connection State + +There are two ways to define an actor's connection state: + + +### connState + + Define connection state as a constant value: + + + + This value will be cloned for every new connection using `structuredClone`. + + + +### createConnState + + Create connection state dynamically with a function called for each connection: + + + + +## Connection Lifecycle + +Each client connection goes through a series of lifecycle hooks that allow you to validate, initialize, and clean up connection-specific resources. + +**On Connect** (per client) + +- `onBeforeConnect` +- `createConnState` +- `onConnect` + +Pending connections are not visible in `c.conns` while `onBeforeConnect` or `createConnState` is running. RivetKit adds the connection to `c.conns` after those hooks succeed and before `onConnect` runs. + +**On Disconnect** (per client) + +- `onDisconnect` + +### `createConnState` and `connState` + +[API Reference](/typedoc/interfaces/rivetkit.mod.CreateConnStateContext.html) + +There are two ways to define the initial state for connections: +1. `connState`: Define a constant object that will be used as the initial state for all connections +2. `createConnState`: A function that dynamically creates initial connection state based on connection parameters. Can be async. + +Connections are not visible in `c.conns` until `createConnState` completes successfully. + +### `onBeforeConnect` + +[API Reference](/typedoc/interfaces/rivetkit.mod.OnBeforeConnectContext.html) + +The `onBeforeConnect` hook is called whenever a new client connects to the actor. Can be async. Clients can pass parameters when connecting, accessible via `params`. This hook is used for connection validation and can throw errors to reject connections. + +The `onBeforeConnect` hook does NOT return connection state - it's used solely for validation. + +Connections are not visible in `c.conns` while `onBeforeConnect` is running. + +Connections cannot interact with the actor until this method completes successfully. Throwing an error will abort the connection. This can be used for authentication, see [Authentication](/docs/actors/authentication) for details. + +### `onConnect` + +[API Reference](/typedoc/interfaces/rivetkit.mod.OnConnectContext.html) + +Executed after the client has successfully connected. Can be async. Receives the connection object as a second parameter. + +By the time `onConnect` runs, the connection is visible in `c.conns`. + +Messages will not be processed for this actor until this hook succeeds. Errors thrown from this hook will cause the client to disconnect. + +### `onDisconnect` + +[API Reference](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html) + +Called when a client disconnects from the actor. Can be async. Receives the connection object as a second parameter. Use this to clean up any connection-specific resources. + +## Connection List + +All active connections can be accessed through the context object's `conns` property. This is a `Map` of all current connections, keyed by connection ID. + +This is frequently used with `conn.send(name, event)` to send messages directly to clients. To send an event to all connections at once, use `c.broadcast()` instead. See [Events](/docs/actors/events) for more details on broadcasting. + +For example: + +`conn.send()` has no effect on [low-level WebSocket connections](/docs/actors/websocket-handler). For low-level WebSockets, use the WebSocket API directly (e.g., `websocket.send()`). + +## Disconnecting clients + +Connections can be disconnected from within an action: + +If you need to wait for the disconnection to complete, you can use `await`: + +This ensures the underlying network connections close cleanly before continuing. + +## API Reference + +- [`Conn`](/typedoc/interfaces/rivetkit.mod.Conn.html) - Connection interface +- [`ConnInitContext`](/typedoc/interfaces/rivetkit.mod.ConnInitContext.html) - Connection initialization context +- [`CreateConnStateContext`](/typedoc/interfaces/rivetkit.mod.CreateConnStateContext.html) - Context for creating connection state +- [`OnBeforeConnectContext`](/typedoc/interfaces/rivetkit.mod.OnBeforeConnectContext.html) - Pre-connection lifecycle hook context +- [`OnConnectContext`](/typedoc/interfaces/rivetkit.mod.OnConnectContext.html) - Post-connection lifecycle hook context +- [`ActorConn`](/typedoc/types/rivetkit.client_mod.ActorConn.html) - Typed connection from client side + +_Source doc path: /docs/actors/connections_ diff --git a/.agents/skills/live-cursors/reference/actors/debugging.md b/.agents/skills/live-cursors/reference/actors/debugging.md new file mode 100644 index 0000000..409b381 --- /dev/null +++ b/.agents/skills/live-cursors/reference/actors/debugging.md @@ -0,0 +1,644 @@ +# Debugging + +> Source: `src/content/docs/actors/debugging.mdx` +> Canonical URL: https://rivet.dev/docs/actors/debugging +> Description: Inspect and debug running Rivet Actors, runners, and provider configs using management, runner, and actor inspector HTTP APIs. + +--- +## Connecting to Rivet + +All debugging endpoints in this guide are available both locally and in production. In local development, the base URL is `http://localhost:6420` with no authentication. In production (Rivet Cloud or self-hosted), you connect to your Rivet Engine endpoint with a token. + +### Setup + +All examples in this guide use these shell variables. Extract them from your `RIVET_ENDPOINT` (`https://:@`): + +```bash +# From RIVET_ENDPOINT=https://my-namespace:sk_abc123@api.rivet.dev +export RIVET_API="https://api.rivet.dev" +export RIVET_NAMESPACE="my-namespace" +export RIVET_TOKEN="sk_abc123" + +# For local development: +# export RIVET_API="http://localhost:6420" +``` + +Rivet Cloud issues two token types: `sk_` (secret key, server-side only) and `pk_` (public key, client-safe). For debugging, always use `sk_`. See [Endpoints](/docs/general/endpoints) for more details. + +## Management API + +The management API runs on the manager base path (default root path) and is used to list, create, and look up actors. + +### Authentication + +| Environment | Authentication | +|---|---| +| **Local development** | No authentication required. All endpoints are accessible without tokens. | +| **Self-hosted engine** | Set `RIVET_TOKEN` to enable authenticated access to restricted endpoints like KV. | +| **Rivet Cloud** | Authentication is enforced by your deployment entrypoint. For manager KV access, use the bearer token header below when enabled. | + +Restricted endpoints (like KV reads) require the `Authorization: Bearer` header when `RIVET_TOKEN` is configured: + +```bash +curl "$RIVET_API/actors/{actor_id}/kv/keys/{base64_key}" \ + -H "Authorization: Bearer $RIVET_TOKEN" +``` + +### List Actors + +```bash +# List all actors with a given name +curl "$RIVET_API/actors?name=my-actor&namespace=$RIVET_NAMESPACE" \ + -H "Authorization: Bearer $RIVET_TOKEN" + +# Look up one actor by key (name is required when key is provided) +curl "$RIVET_API/actors?name=my-actor&key=%5B%22my-key%22%5D&namespace=$RIVET_NAMESPACE" \ + -H "Authorization: Bearer $RIVET_TOKEN" + +# List actors by IDs (comma-separated) +curl "$RIVET_API/actors?actor_ids=id1,id2&namespace=$RIVET_NAMESPACE" \ + -H "Authorization: Bearer $RIVET_TOKEN" +``` + +Rules: + +- `key` requires `name`. +- `actor_ids` cannot be combined with `name` or `key`. + +Returns: + +```json +{ + "actors": [ + { + "actor_id": "abc123", + "name": "my-actor", + "key": "[\"default\"]", + "namespace_id": "default", + "create_ts": 1706000000000 + } + ] +} +``` + +### Create Actor + +`POST /actors` creates a new actor. + +```bash +curl -X POST "$RIVET_API/actors?namespace=$RIVET_NAMESPACE" \ + -H "Authorization: Bearer $RIVET_TOKEN" \ + -H 'Content-Type: application/json' \ + -d '{ + "name": "my-actor", + "runner_name_selector": "default", + "crash_policy": "restart" + }' +``` + +### Create or Get Actor + +`PUT /actors` creates an actor if it does not exist, otherwise returns the existing one. + +```bash +curl -X PUT "$RIVET_API/actors?namespace=$RIVET_NAMESPACE" \ + -H "Authorization: Bearer $RIVET_TOKEN" \ + -H 'Content-Type: application/json' \ + -d '{ + "name": "my-actor", + "key": "[\"default\"]", + "runner_name_selector": "default", + "crash_policy": "restart" + }' +``` + +Returns the actor object with its `actor_id`. + +### List Actor Names + +```bash +curl "$RIVET_API/actors/names?namespace=$RIVET_NAMESPACE" \ + -H "Authorization: Bearer $RIVET_TOKEN" +``` + +Returns all registered actor names and their metadata. + +### Read Actor KV + +Requires authentication (see above). + +```bash +curl "$RIVET_API/actors/{actor_id}/kv/keys/{base64_key}" \ + -H "Authorization: Bearer $RIVET_TOKEN" +``` + +Returns the value stored at the given key in the actor KV compatibility snapshot. After an actor has migrated to SQLite-backed runtime storage, this endpoint is stale for user KV and internal runtime records; it continues to expose the frozen pre-migration KV data. The inspector token key is the exception and remains mirrored for dashboard compatibility. + +See the [OpenAPI spec](https://github.com/rivet-dev/rivet/tree/main/rivetkit-openapi) for the full schema of all management endpoints. + +## Runner API + +Use the runner endpoints to debug scheduler capacity and provider configuration (for example serverless URL, headers, and limits) through the Rivet API. + +### List Runner Names + +```bash +curl "$RIVET_API/runners/names?namespace=$RIVET_NAMESPACE" \ + -H "Authorization: Bearer $RIVET_TOKEN" +``` + +Returns the runner pools available in the namespace: + +```json +{ + "names": ["default", "gpu-workers"], + "pagination": { "cursor": null } +} +``` + +### List Runners in a Pool + +```bash +curl "$RIVET_API/runners?namespace=$RIVET_NAMESPACE&name=default&include_stopped=true&limit=100" \ + -H "Authorization: Bearer $RIVET_TOKEN" +``` + +Useful fields when debugging: + +- `remaining_slots` / `total_slots` for capacity. +- `drain_ts` and `stop_ts` for shutdown behavior. +- `last_ping_ts` and `last_connected_ts` for connectivity. + +### Inspect Provider Config (Runner Config) + +```bash +curl "$RIVET_API/runner-configs?namespace=$RIVET_NAMESPACE&runner_name=default" \ + -H "Authorization: Bearer $RIVET_TOKEN" +``` + +Returns the configured provider settings per datacenter and the latest pool error (if any): + +```json +{ + "runner_configs": { + "default": { + "datacenters": { + "dc-1": { + "serverless": { + "url": "https://your-deployment.example.com/rivet", + "headers": { "Authorization": "Bearer token" }, + "request_lifespan": 55, + "slots_per_runner": 1, + "max_runners": 10 + }, + "runner_pool_error": null + } + } + } + }, + "pagination": { "cursor": null } +} +``` + +`runner_pool_error` mirrors actor scheduling errors such as `serverless_http_error`, `serverless_connection_error`, and `serverless_stream_ended_early`. + +### Check Serverless Provider Health + +Use this to test whether Rivet can reach your serverless provider URL and read runner metadata: + +```bash +curl -X POST "$RIVET_API/runner-configs/serverless-health-check?namespace=$RIVET_NAMESPACE" \ + -H "Authorization: Bearer $RIVET_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "url": "https://your-deployment.example.com/rivet", + "headers": { + "Authorization": "Bearer token" + } + }' +``` + +Possible responses: + +```json +{ "success": { "version": "1.2.3" } } +``` + +```json +{ + "failure": { + "error": { + "message": "non-success status from metadata endpoint", + "details": "received status 503" + } + } +} +``` + +### Refresh Provider Metadata + +If you deploy new actor code or routes and metadata has not updated yet, force a refresh: + +```bash +curl -X POST "$RIVET_API/runner-configs/default/refresh-metadata?namespace=$RIVET_NAMESPACE" \ + -H "Authorization: Bearer $RIVET_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{}' +``` + +## Actor API + +All actor-level endpoints are accessed through the gateway. The gateway routes requests to the correct actor instance using the actor ID in the URL path: + +``` +{RIVET_API}/gateway/{actor_id}/{path} +``` + +The gateway only accepts actor IDs, not names. Use `GET /actors?name=...` from the management API to look up actor IDs first. + +### Authentication + +Standard actor endpoints (health, actions, requests) and inspector endpoints have separate authentication requirements. + +#### Standard Endpoints + +| Environment | Authentication | +|---|---| +| **Local development** | No authentication required. | +| **Self-hosted engine** | The Rivet Engine handles authentication at the gateway level. | +| **Rivet Cloud** | Authentication is handled by the Rivet Cloud platform at the gateway level. | + +#### Inspector Endpoints + +Each actor generates a unique inspector token on first start and persists it in internal SQLite storage. It is also mirrored to legacy KV key `0x03` (base64 `Aw==`) for dashboard compatibility. Pass it as a bearer token in the `Authorization` header. + +Inspector endpoints always require the actor's inspector token, including in local development. There is no local-development bypass. + +| Environment | Authentication | +|---|---| +| **Local development** | Bearer the actor's inspector token in the `Authorization` header. Fetch it through the management KV endpoint (see below). | +| **Self-hosted engine** | Bearer the actor's inspector token in the `Authorization` header. The Rivet dashboard fetches it automatically; for direct API access, fetch it through the management KV endpoint (see below). | +| **Rivet Cloud** | Bearer the actor's inspector token in the `Authorization` header. The Rivet dashboard fetches it automatically; for direct API access, fetch it through the management KV endpoint (see below). | + +```bash +curl "$RIVET_API/gateway/{actor_id}/inspector/summary" \ + -H 'Authorization: Bearer YOUR_INSPECTOR_TOKEN' +``` + +#### Retrieving the Inspector Token + +Each actor generates a unique inspector token on first start and persists it in internal SQLite storage. The token is also mirrored to the legacy KV key below so the Rivet dashboard can continue to retrieve it through the management KV endpoint. This applies in every environment, including local development. + +The inspector token is stored at internal KV key `0x03` (base64: `Aw==`). The response value is also base64-encoded. + +```bash +# Fetch the inspector token for a specific actor +ACTOR_ID="your-actor-id" + +RESPONSE=$(curl -s "$RIVET_API/actors/$ACTOR_ID/kv/keys/Aw==" \ + -H "Authorization: Bearer $RIVET_TOKEN") + +# Extract and decode the base64 value +INSPECTOR_TOKEN=$(echo "$RESPONSE" | jq -r '.value' | base64 -d) + +# Use it to call inspector endpoints +curl "$RIVET_API/gateway/$ACTOR_ID/inspector/summary" \ + -H "Authorization: Bearer $INSPECTOR_TOKEN" +``` + +### Standard Actor Endpoints + +These are the built-in actor endpoints available through the gateway: + +```bash +# Health check +curl $RIVET_API/gateway/{actor_id}/health + +# Metadata +curl $RIVET_API/gateway/{actor_id}/metadata + +# Call an action +curl -X POST $RIVET_API/gateway/{actor_id}/action/myAction \ + -H 'Content-Type: application/json' \ + -d '{"args": [1, 2, 3]}' + +# Send queue message (queue name in path) +curl -X POST $RIVET_API/gateway/{actor_id}/queue/jobs \ + -H 'Content-Type: application/json' \ + -d '{"body":{"id":"job-1"}}' + +# Send queue message and wait for completion (optional timeout in ms) +curl -X POST $RIVET_API/gateway/{actor_id}/queue/jobs \ + -H 'Content-Type: application/json' \ + -d '{"body":{"id":"job-1"},"wait":true,"timeout":5000}' + +# Forward an HTTP request to the actor's onRequest handler +curl $RIVET_API/gateway/{actor_id}/request/my/custom/path +``` + +Queue send responses always include a `status` field: + +```json +{ "status": "completed" } +``` + +The `response` field is only present when the queue handler returns a value: + +```json +{ "status": "completed", "response": { "result": "ok" } } +``` + +If `wait: true` and the timeout is reached, `status` is `"timedOut"`. + +### Inspector Endpoints + +The inspector HTTP API exposes JSON endpoints for querying and modifying actor internals at runtime. These are designed for agent-based debugging and tooling. + +Every inspector endpoint requires the actor's inspector token as a bearer token, including in local development. The examples below omit the `Authorization` header for brevity, but you must add `-H "Authorization: Bearer $INSPECTOR_TOKEN"` to each request. See [Retrieving the Inspector Token](#retrieving-the-inspector-token) above. + +#### Get State + +```bash +curl $RIVET_API/gateway/{actor_id}/inspector/state +``` + +Returns the actor's current persisted state: + +```json +{ + "state": { "count": 42, "users": [] }, + "isStateEnabled": true +} +``` + +#### Set State + +```bash +curl -X PATCH $RIVET_API/gateway/{actor_id}/inspector/state \ + -H 'Content-Type: application/json' \ + -d '{"state": {"count": 0, "users": []}}' +``` + +Returns: + +```json +{ "ok": true } +``` + +#### Get Connections + +```bash +curl $RIVET_API/gateway/{actor_id}/inspector/connections +``` + +Returns all active connections with their params, state, and metadata: + +```json +{ + "connections": [ + { + "type": "websocket", + "id": "conn-id", + "details": { + "type": "websocket", + "params": {}, + "stateEnabled": true, + "state": {}, + "subscriptions": 2, + "isHibernatable": true + } + } + ] +} +``` + +#### Get RPCs + +```bash +curl $RIVET_API/gateway/{actor_id}/inspector/rpcs +``` + +Returns a list of available actions: + +```json +{ "rpcs": ["increment", "getCount"] } +``` + +#### Execute Action + +```bash +curl -X POST $RIVET_API/gateway/{actor_id}/inspector/action/increment \ + -H 'Content-Type: application/json' \ + -d '{"args": [5]}' +``` + +Returns: + +```json +{ "output": 47 } +``` + +#### Get Queue Status + +```bash +curl $RIVET_API/gateway/{actor_id}/inspector/queue?limit=10 +``` + +Returns queue status with messages: + +```json +{ + "size": 3, + "maxSize": 1000, + "truncated": false, + "messages": [ + { "id": 1, "name": "process", "createdAtMs": 1706000000000 } + ] +} +``` + +#### Get Workflow History + +```bash +curl $RIVET_API/gateway/{actor_id}/inspector/workflow-history +``` + +Returns: + +```json +{ + "history": null, + "isWorkflowEnabled": false +} +``` + +#### Get Database Schema + +```bash +curl $RIVET_API/gateway/{actor_id}/inspector/database/schema +``` + +Returns discovered SQLite tables and views when the actor has `c.db` enabled: + +```json +{ + "schema": { + "tables": [ + { + "table": { "schema": "main", "name": "test_data", "type": "table" }, + "columns": [ + { "cid": 0, "name": "id", "type": "", "notnull": 0, "dflt_value": null, "pk": 0 } + ], + "foreignKeys": [], + "records": 2 + } + ] + } +} +``` + +#### Get Database Rows + +```bash +curl "$RIVET_API/gateway/{actor_id}/inspector/database/rows?table=test_data&limit=100&offset=0" +``` + +Returns paged rows for a specific SQLite table or view: + +```json +{ + "rows": [ + { + "id": 1, + "value": "Alice", + "payload": "", + "created_at": 1706000000000 + } + ] +} +``` + +#### Execute SQLite + +Run manual SQL against an actor's SQLite database. This supports both read-only queries and mutations. + +```bash +curl -X POST http://localhost:6420/gateway/{actor_id}/inspector/database/execute \ + -H 'Content-Type: application/json' \ + -d '{ + "sql": "SELECT id, value FROM test_data WHERE value = ? ORDER BY id DESC", + "args": ["alpha"] + }' +``` + +Returns: + +```json +{ + "rows": [ + { "id": 2, "value": "alpha" } + ] +} +``` + +You can also use named SQLite bindings through a `properties` object: + +```bash +curl -X POST http://localhost:6420/gateway/{actor_id}/inspector/database/execute \ + -H 'Content-Type: application/json' \ + -d '{ + "sql": "SELECT id, value FROM test_data WHERE value = :value ORDER BY id DESC", + "properties": { + "value": "alpha" + } + }' +``` + +For mutations, use `RETURNING` if you want rows back. Otherwise the statement still runs and `rows` is empty: + +```bash +curl -X POST http://localhost:6420/gateway/{actor_id}/inspector/database/execute \ + -H 'Content-Type: application/json' \ + -d '{ + "sql": "INSERT INTO test_data (value, created_at) VALUES (?, ?) RETURNING id, value", + "args": ["beta", 1706000000000] + }' +``` + +For workflow-enabled actors, `history` is a JSON object with `nameRegistry`, `entries`, and `entryMetadata`. Step outputs, loop state, and message payloads are decoded from CBOR into normal JSON values. + +#### Replay Workflow From Step + +Reset a workflow to a specific step and restart execution immediately. Omitting `entryId` replays the workflow from the beginning. + +If the workflow is still running when you call replay, the endpoint rejects the request with `409 Conflict` and an `actor/workflow_in_flight` error instead of cancelling the live run for you. + +```bash +curl -X POST http://localhost:6420/gateway/{actor_id}/inspector/workflow/replay \ + -H 'Content-Type: application/json' \ + -d '{"entryId":"workflow-step-id"}' +``` + +Returns the same JSON shape as `/inspector/workflow-history`: + +```json +{ + "history": { + "nameRegistry": ["step-one", "step-two"], + "entries": [], + "entryMetadata": {} + }, + "isWorkflowEnabled": true +} +``` + +While a workflow is in flight, the response shape is: + +```json +{ + "group": "actor", + "code": "workflow_in_flight", + "message": "Workflow replay is unavailable while the workflow is currently in flight.", + "metadata": null +} +``` + +#### Summary + +Get a full snapshot of the actor in a single request: + +```bash +curl $RIVET_API/gateway/{actor_id}/inspector/summary +``` + +Returns: + +```json +{ + "state": { "count": 42 }, + "connections": [], + "rpcs": ["increment", "getCount"], + "queueSize": 0, + "isStateEnabled": true, + "isDatabaseEnabled": false, + "isWorkflowEnabled": false, + "workflowHistory": null +} +``` + +When workflow history is present in `/inspector/summary`, `workflowHistory` is returned as the same decoded JSON shape as `/inspector/workflow-history`. + +### Polling + +Inspector endpoints are safe to poll. For live monitoring, poll at 1-5 second intervals. The `/inspector/summary` endpoint is useful for periodic snapshots since it returns all data in a single request. + +## OpenAPI Spec + +An OpenAPI specification covering many of the management and actor endpoints is available: + +- In the repository at [`rivetkit-openapi/openapi.json`](https://github.com/rivet-dev/rivet/tree/main/rivetkit-openapi) +- Served at `/doc` on the manager when running locally + +The checked-in spec does not yet list every endpoint documented on this page (for example the actor metadata and queue routes and the inspector database routes), so treat this page as the authoritative reference where they differ. + +_Source doc path: /docs/actors/debugging_ diff --git a/.agents/skills/live-cursors/reference/actors/design-patterns.md b/.agents/skills/live-cursors/reference/actors/design-patterns.md new file mode 100644 index 0000000..77ecb40 --- /dev/null +++ b/.agents/skills/live-cursors/reference/actors/design-patterns.md @@ -0,0 +1,163 @@ +# Design Patterns + +> Source: `src/content/docs/actors/design-patterns.mdx` +> Canonical URL: https://rivet.dev/docs/actors/design-patterns +> Description: Common patterns and anti-patterns for building scalable actor systems. + +--- +## How Actors Scale + +Actors are inherently scalable because of how they're designed: + +- **Isolated state:** Each actor manages its own private data. No shared state means no conflicts and no locks, so actors run concurrently without coordination. +- **Actor-to-actor communication:** Actors interact through [actions](/docs/actors/actions) and [events](/docs/actors/events), so they don't need to coordinate access to shared data. This makes it easy to distribute them across machines. +- **Small, focused units:** Each actor handles a limited scope (a single user, document, or chat room), so load naturally spreads across many actors rather than concentrating in one place. +- **Horizontal scaling:** Adding more machines automatically distributes actors across them. + +These properties form the foundation for the patterns described below. + +## Actor Per Entity + +The core pattern is creating one actor per entity in your system. Each actor represents a single user, document, chat room, or other distinct object. This keeps actors small, independent, and easy to scale. + +**Good examples** + +- `User`: Manages user profile, preferences, and authentication +- `Document`: Handles document content, metadata, and versioning +- `ChatRoom`: Manages participants and message history + +**Bad examples** + +- `Application`: Too broad, handles everything +- `DocumentWordCount`: Too granular, should be part of Document actor + +## Coordinator & Data Actors + +Actors scale by splitting state into isolated entities. However, it's common to need to track and coordinate actors in a central place. This is where coordinator actors come in. + +**Data actors** handle the main logic in your application. Examples: chat rooms, user sessions, game lobbies. + +**Coordinator actors** track other actors. Think of them as an index of data actors. Examples: a list of chat rooms, a list of active users, a list of game lobbies. + +**Example: Chat Room Coordinator** + +### Actor + +### Client + +## Sharding + +Sharding splits a single actor's workload across multiple actors based on a key. Use this when one actor can't handle all the load or data for an entity. + +**How it works:** +- Partition data using a shard key (user ID, region, time bucket, or random) +- Requests are routed to shards based on the key +- Shards operate independently without coordination + +**Example: Sharding by Time** + +### Actor + +### Client + +**Example: Random Sharding** + +### Actor + +### Client + +Choose shard keys that distribute load evenly. Note that cross-shard queries require coordination. + +## Fan-In & Fan-Out + +Fan-in and fan-out are patterns for distributing work and aggregating results. + +**Fan-Out**: One actor spawns work across multiple actors. Use for parallel processing or broadcasting updates. + +**Fan-In**: Multiple actors send results to one aggregator. Use for collecting results or reducing data. + +**Example: Map-Reduce** + +### Actor + +### Client + +## Integrating With External Databases & APIs + +Actors can integrate with external resources like databases or external APIs. + +### Loading State + +Load external data during actor initialization using `createVars`. This keeps your actor's persisted state clean while caching expensive lookups. + +Use this when: + +- Fetching user profiles, configs, or permissions from a database +- Loading data that changes externally and shouldn't be persisted +- Caching expensive API calls or computations + +**Example: Loading User Profile** + +### Actor + +### Client + +### Syncing State Changes + +Use `onStateChange` to automatically sync actor state changes to external resources. This hook runs after state changes are flushed, which is coalesced to once per event loop tick rather than once per individual field mutation. + +Use this when: + +- You need to mirror actor state in an external database +- Triggering external side effects when state changes +- Keeping external systems in sync with actor state + +**Example: Syncing to Database** + +### Actor + +### Client + +`onStateChange` is called once per flush with the final coalesced state, ensuring external resources stay in sync. In the `updateEmail` example above, the two synchronous assignments produce a single `onStateChange` call. + +Do not mutate `c.state` inside `onStateChange`; re-entrant state mutation is rejected. + +## Anti-Patterns + +### "God" Actor + +Avoid creating a single actor that handles everything. This defeats the purpose of the actor model and creates a bottleneck. + +**Problem:** +```ts +import { actor } from "rivetkit"; + +// Bad: one actor doing everything +const app = actor({ + state: { users: {}, orders: {}, inventory: {}, analytics: {} }, + actions: { + createUser: (c, user) => { /* ... */ }, + processOrder: (c, order) => { /* ... */ }, + updateInventory: (c, item) => { /* ... */ }, + trackEvent: (c, event) => { /* ... */ }, + }, +}); +``` + +**Solution:** Split into focused actors per entity (User, Order, Inventory, Analytics). + +### Actor-Per-Request + +Actors are designed to maintain state across multiple requests. Creating a new actor for each request wastes resources and loses the benefits of persistent state. + +**Problem:** + +**Solution:** Use actors for entities that persist (users, sessions, documents), not for one-off operations. For stateless request handling, use regular functions. + +## API Reference + +- [`ActorDefinition`](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html) - Interface for pattern examples +- [`ActorContext`](/typedoc/interfaces/rivetkit.mod.ActorContext.html) - Context usage patterns +- [`ActionContext`](/typedoc/interfaces/rivetkit.mod.ActionContext.html) - Action patterns + +_Source doc path: /docs/actors/design-patterns_ diff --git a/.agents/skills/live-cursors/reference/actors/destroy.md b/.agents/skills/live-cursors/reference/actors/destroy.md new file mode 100644 index 0000000..87e1b76 --- /dev/null +++ b/.agents/skills/live-cursors/reference/actors/destroy.md @@ -0,0 +1,71 @@ +# Destroying Actors + +> Source: `src/content/docs/actors/destroy.mdx` +> Canonical URL: https://rivet.dev/docs/actors/destroy +> Description: Actors can be permanently destroyed. Common use cases include: + +--- +- User account deletion +- Ending a user session +- Closing a room or game +- Cleaning up temporary resources +- GDPR/compliance data removal + +Actors sleep when idle, so destruction is only needed to permanently remove data — not to save compute. + +## Destroying An Actor + +### Destroy via Action + +To destroy an actor, use `c.destroy()` like this: + +### Destroy via HTTP + +Send a DELETE request to destroy an actor. This requires a token for authentication: + +- **Rivet Cloud**: Use the service key (`sk_*`) from your `RIVET_ENDPOINT` URL. Find this in the dashboard under **Settings > Advanced > Backend Configuration** (e.g. the `sk_...` portion of `https://default:sk_abc123@api.rivet.dev`). +- **Self-hosted**: Use an admin token. + +```typescript +const actorId = "your-actor-id"; +const namespace = "default"; +const token = "your-token"; + +await fetch(`https://api.rivet.dev/actors/${actorId}?namespace=${namespace}`, { + method: "DELETE", + headers: { + Authorization: `Bearer ${token}`, + }, +}); +``` + +```bash +curl -X DELETE "https://api.rivet.dev/actors/{actorId}?namespace={namespace}" \ + -H "Authorization: Bearer {token}" +``` + +To find the actor ID, you can list actors first: + +```bash +curl "https://api.rivet.dev/actors?namespace={namespace}" \ + -H "Authorization: Bearer {token}" +``` + +### Destroy via Dashboard + +To destroy an actor via the dashboard, navigate to the actor and press the red "X" in the top right. + +## Lifecycle Hook + +Once destroyed, the `onDestroy` hook will be called. This can be used to clean up resources related to the actor. For example: + +## Accessing Actor After Destroy + +Once an actor is destroyed, any subsequent requests to it will fail with an `actor.not_found` error (`{ group: "actor", code: "not_found" }`). The actor's state is permanently deleted. + +## API Reference + +- [`ActorHandle`](/typedoc/types/rivetkit.client_mod.ActorHandle.html) - Has destroy methods +- [`ActorContext`](/typedoc/interfaces/rivetkit.mod.ActorContext.html) - Context during destruction + +_Source doc path: /docs/actors/destroy_ diff --git a/.agents/skills/live-cursors/reference/actors/errors.md b/.agents/skills/live-cursors/reference/actors/errors.md new file mode 100644 index 0000000..af24c4c --- /dev/null +++ b/.agents/skills/live-cursors/reference/actors/errors.md @@ -0,0 +1,81 @@ +# Errors + +> Source: `src/content/docs/actors/errors.mdx` +> Canonical URL: https://rivet.dev/docs/actors/errors +> Description: Rivet provides robust error handling with security built in by default. Errors are handled differently based on whether they should be exposed to clients or kept private. + +--- +There are two types of errors: + +- **UserError**: Thrown from actors and safely returned to clients with full details +- **Internal errors**: All other errors that are converted to a generic error message for security + +## Throwing and Catching Errors + +`UserError` lets you throw custom errors that will be safely returned to the client. + +Throw a `UserError` with just a message: + +### Actor + +### Client (Connection) + +### Client (Stateless) + +## Error Codes + +Use error codes for explicit error matching in try-catch blocks: + +### Actor + +### Client (Connection) + +### Client (Stateless) + +## Errors With Metadata + +Include metadata to provide additional context for rich error handling: + +### Actor + +### Client (Connection) + +### Client (Stateless) + +## Internal Errors + +All errors that are not UserError instances are automatically converted to a generic "internal error" response. This prevents accidentally leaking sensitive information like stack traces, database details, or internal system information. + +### Actor + +### Client (Connection) + +### Client (Stateless) + +### Server-Side Logging + +**All internal errors are logged server-side with full details.** When an internal error occurs, the complete error message, stack trace, and context are written to your server logs. This is where you should look first when debugging internal errors in production. + +The client receives only a generic "Internal error" message for security, but you can find the full error details in your server logs including: + +- Complete error message +- Stack trace +- Request context (actor ID, action name, connection ID, etc.) +- Timestamp + +**Always check your server logs to see the actual error details when debugging internal errors.** + +### Exposing Errors to Clients (Development Only) + +**Warning:** Only enable error exposure in development environments. In production, this will leak sensitive internal details to clients. + +For faster debugging during development, you can expose internal error details to clients by setting `RIVET_EXPOSE_ERRORS=1`. + +With error exposure enabled, clients will see the full error message instead of the generic "Internal error" response: + +## API Reference + +- [`UserError`](/typedoc/classes/rivetkit.actor_errors.UserError.html) - User-facing error class +- [`ActorError`](/typedoc/classes/rivetkit.client_mod.ActorError.html) - Errors received by the client + +_Source doc path: /docs/actors/errors_ diff --git a/.agents/skills/live-cursors/reference/actors/events.md b/.agents/skills/live-cursors/reference/actors/events.md new file mode 100644 index 0000000..36e2c69 --- /dev/null +++ b/.agents/skills/live-cursors/reference/actors/events.md @@ -0,0 +1,143 @@ +# Realtime + +> Source: `src/content/docs/actors/events.mdx` +> Canonical URL: https://rivet.dev/docs/actors/events +> Description: Events enable realtime communication from actors to clients. While clients use actions to send data to actors, events allow actors to push updates to connected clients instantly. + +--- +Events can be sent to clients connected using `.connect()`. They have no effect on [low-level WebSocket connections](/docs/actors/websocket-handler). + +## Publishing Events from Actors + +### Broadcasting to All Clients + +Define event names and payload types with `events` and `event()`, then use `c.broadcast(eventName, ...args)` to send events to all connected clients: + +### Sending to Specific Connections + +Send events to individual connections using `conn.send(eventName, ...args)`: + +Send events to all connections except the sender: + +## Subscribing to Events from Clients + +Clients must establish a connection to receive events from actors. Use `.connect()` to create a persistent connection, then listen for events. + +### Basic Event Subscription + +Use `connection.on(eventName, callback)` to listen for events: + +```tsx React @nocheck +import { useState } from "react"; +import { useActor } from "./rivetkit"; + +function ChatRoom() { + const [messages, setMessages] = useState>([]); + + const chatRoom = useActor({ + name: "chatRoom", + key: ["general"] + }); + + // Listen for events + chatRoom.useEvent("messageReceived", (message) => { + setMessages(prev => [...prev, message]); + }); + + // ...rest of component... +} +``` + +### One-time Event Listeners + +Use `connection.once(eventName, callback)` for events that should only trigger once: + +```tsx React @nocheck +import { useState, useEffect } from "react"; +import { useActor } from "./rivetkit"; + +function GameLobby() { + const [gameStarted, setGameStarted] = useState(false); + + const gameRoom = useActor({ + name: "gameRoom", + key: ["room-456"], + params: { + playerId: "player-789", + role: "player" + } + }); + + // Listen for game start (only once) + useEffect(() => { + if (!gameRoom.connection) return; + + const handleGameStart = () => { + console.log('Game has started!'); + setGameStarted(true); + }; + + gameRoom.connection.once('gameStarted', handleGameStart); + }, [gameRoom.connection]); + + // ...rest of component... +} +``` + +### Removing Event Listeners + +Use the callback returned from `.on()` to remove event listeners: + +```tsx React @nocheck +import { useState, useEffect } from "react"; +import { useActor } from "./rivetkit"; + +function ConditionalListener() { + const [isListening, setIsListening] = useState(false); + const [messages, setMessages] = useState([]); + + const chatRoom = useActor({ + name: "chatRoom", + key: ["general"] + }); + + useEffect(() => { + if (!chatRoom.connection || !isListening) return; + + // Add listener + const unsubscribe = chatRoom.connection.on('messageReceived', (message) => { + setMessages(prev => [...prev, message.text]); + }); + + // Cleanup - remove listener when component unmounts or listening stops + return () => { + unsubscribe(); + }; + }, [chatRoom.connection, isListening]); + + // ...rest of component... +} +``` + +## Debugging + +- `GET /inspector/connections` shows active connections and connection metadata. +- Use this to confirm clients are connected before expecting broadcasts. +- `GET /inspector/summary` provides connections, RPCs, and queue size in one response. +- In non-dev mode, inspector endpoints require authorization. + +## More About Connections + +For more details on actor connections, including connection lifecycle, authentication, and advanced connection patterns, see the [Connections documentation](/docs/actors/connections). + +## API Reference + +- [`RivetEvent`](/typedoc/interfaces/rivetkit.mod.RivetEvent.html) - Base event interface +- [`RivetMessageEvent`](/typedoc/interfaces/rivetkit.mod.RivetMessageEvent.html) - Message event type +- [`RivetCloseEvent`](/typedoc/interfaces/rivetkit.mod.RivetCloseEvent.html) - Close event type +- [`UniversalEvent`](/typedoc/interfaces/rivetkit.mod.UniversalEvent.html) - Universal event type +- [`UniversalMessageEvent`](/typedoc/interfaces/rivetkit.mod.UniversalMessageEvent.html) - Universal message event +- [`UniversalErrorEvent`](/typedoc/interfaces/rivetkit.mod.UniversalErrorEvent.html) - Universal error event +- [`EventUnsubscribe`](/typedoc/types/rivetkit.client_mod.EventUnsubscribe.html) - Unsubscribe function type + +_Source doc path: /docs/actors/events_ diff --git a/.agents/skills/live-cursors/reference/actors/fetch-and-websocket-handler.md b/.agents/skills/live-cursors/reference/actors/fetch-and-websocket-handler.md new file mode 100644 index 0000000..b8b0509 --- /dev/null +++ b/.agents/skills/live-cursors/reference/actors/fetch-and-websocket-handler.md @@ -0,0 +1,10 @@ +# Fetch and WebSocket Handler + +> Source: `src/content/docs/actors/fetch-and-websocket-handler.mdx` +> Canonical URL: https://rivet.dev/docs/actors/fetch-and-websocket-handler +> Description: These docs have moved to [Low-Level WebSocket Handler](/docs/actors/websocket-handler) and [Low-Level Request Handler](/docs/actors/request-handler). + +--- + + +_Source doc path: /docs/actors/fetch-and-websocket-handler_ diff --git a/.agents/skills/live-cursors/reference/actors/helper-types.md b/.agents/skills/live-cursors/reference/actors/helper-types.md new file mode 100644 index 0000000..55555c7 --- /dev/null +++ b/.agents/skills/live-cursors/reference/actors/helper-types.md @@ -0,0 +1,10 @@ +# Helper Types + +> Source: `src/content/docs/actors/helper-types.mdx` +> Canonical URL: https://rivet.dev/docs/actors/helper-types +> Description: This page has moved to [Types](/docs/actors/types). + +--- + + +_Source doc path: /docs/actors/helper-types_ diff --git a/.agents/skills/live-cursors/reference/actors/http-api.md b/.agents/skills/live-cursors/reference/actors/http-api.md new file mode 100644 index 0000000..1d8d5be --- /dev/null +++ b/.agents/skills/live-cursors/reference/actors/http-api.md @@ -0,0 +1,10 @@ +# Vanilla HTTP API + +> Source: `src/content/docs/actors/http-api.mdx` +> Canonical URL: https://rivet.dev/docs/actors/http-api +> Description: Use the low-level HTTP handler to send and receive requests from actors. + +--- +TODO + +_Source doc path: /docs/actors/http-api_ diff --git a/.agents/skills/live-cursors/reference/actors/input.md b/.agents/skills/live-cursors/reference/actors/input.md new file mode 100644 index 0000000..c5c5dde --- /dev/null +++ b/.agents/skills/live-cursors/reference/actors/input.md @@ -0,0 +1,51 @@ +# Input Parameters + +> Source: `src/content/docs/actors/input.mdx` +> Canonical URL: https://rivet.dev/docs/actors/input +> Description: Pass initialization data to actors when creating instances + +--- +Actors can receive input parameters when created, allowing for flexible initialization and configuration. Input is passed during actor creation and is available in lifecycle hooks. + +## Passing Input to Actors + +Input is provided when creating actor instances using the `input` property: + +## Accessing Input in Lifecycle Hooks + +Input is available as the second argument to the `createState` and `onCreate` lifecycle hooks: + +## Input Validation + +You can validate input parameters in the `createState` or `onCreate` hooks: + +## Input vs Connection Parameters + +Input parameters are different from connection parameters: + +- **Input**: + - Passed when creating the actor instance + - Use for actor-wide configuration + - Available in lifecycle hooks +- **Connection parameters**: + - Passed when connecting to an existing actor + - Used for connection-specific configuration + - Available in connection hooks + +## Input Best Practices + +### Use Type Safety + +Define input types to ensure type safety: + +### Store Input in State + +Input is only available in `createState` and `onCreate` lifecycle hooks. If you need to access input data later (in actions, timers, or other hooks), store it in the actor's state during creation. This is the recommended pattern because input shapes can evolve over time, and persisting input in state ensures you always have access to the values the actor was created with: + +## API Reference + +- [`CreateOptions`](/typedoc/interfaces/rivetkit.client_mod.CreateOptions.html) - Options for creating actors +- [`CreateRequest`](/typedoc/types/rivetkit.client_mod.CreateRequest.html) - Request type for creation +- [`ActorDefinition`](/typedoc/classes/rivetkit.mod.ActorDefinition.html) - Actor definition returned by `actor()` + +_Source doc path: /docs/actors/input_ diff --git a/.agents/skills/live-cursors/reference/actors/inspector-tabs.md b/.agents/skills/live-cursors/reference/actors/inspector-tabs.md new file mode 100644 index 0000000..34de206 --- /dev/null +++ b/.agents/skills/live-cursors/reference/actors/inspector-tabs.md @@ -0,0 +1,251 @@ +# Custom Inspector Tabs + +> Source: `src/content/docs/actors/inspector-tabs.mdx` +> Canonical URL: https://rivet.dev/docs/actors/inspector-tabs +> Description: Ship your own UI tabs alongside a Rivet Actor — embedded directly in the dashboard inspector. + +--- +Custom inspector tabs let you embed your own UI directly in the Rivet +dashboard, next to the built-in tabs. Declare a tab on your actor, point +it at a folder of static files, and the dashboard picks it up +automatically. + +Common uses: + +- Domain-specific debugging panels (queue depth, connection maps, log + filters). +- Operational tools (admin buttons, drain controls, snapshot uploaders). +- Any author-defined view that ships with your actor. + +A runnable example lives at +[`examples/inspector-tabs`](https://github.com/rivet-dev/rivet/tree/main/examples/inspector-tabs). + +## Quickstart + +Declare a tab on your actor: + +Drop an `index.html` in the `source` directory: + +```html + + + + + + + +

Counter:

+ + + +``` + +Open the dashboard and the "Counter" tab appears alongside the built-ins. + +## Configuration + +Each entry in `inspector.tabs[]` is either a **custom tab** or a +**hide modifier** for a built-in. + +### Custom tab + +```ts @nocheck +{ + id: string, // URL-safe id: /^[A-Za-z0-9_-]+$/ + label: string, // Shown in the tab strip + source: string, // Directory of static assets + icon?: string, // Optional icon id +} +``` + +- **`id`** — used as the URL segment and tab-strip key. Cannot collide + with a built-in id (`workflow`, `database`, `state`, `queue`, + `connections`, `console`). +- **`source`** — directory of static files. The bytes you put there are + the bytes the browser sees. Point it at a Vite/webpack `dist/` and + any framework works (React, Vue, Svelte, vanilla — all fine). +- **`icon`** — one of `workflow`, `database`, `state`, `queue`, `plug`, + `terminal`, `tag`, `logs`. Anything else falls back to a neutral icon. + +### Hide a built-in tab + +```ts @nocheck +{ + id: "workflow" | "database" | "state" | "queue" | "connections" | "console", + hidden: true, +} +``` + +Use this to clean up the strip when the actor doesn't use a given +subsystem — e.g. a counter actor with no queues: + +```ts +inspector: { tabs: [{ id: "queue", hidden: true }] } +``` + +Misconfigurations (missing directory, duplicate id, invalid characters, +empty label) throw at registry construction, so problems show up +immediately. + +## Talking to the dashboard + +The tab loads in an iframe and communicates with the dashboard via +`postMessage`. The contract is small. + +### From the dashboard + +The dashboard sends an `init` message on load and again whenever the +inspector token rotates. Always overwrite the cached token when it +arrives. + +```ts @nocheck +{ + type: "init", + v: 1, + actorId: string, + authToken: string, // Per-actor inspector bearer token + theme?: "light" | "dark", + activeTab?: string, // For multi-view tabs +} +``` + +Multi-view tabs can read the optional `activeTab` field on `init` to +seed their initial sub-view. The dashboard does not send a separate +message when the user switches custom tabs — it navigates the iframe +`src` instead, so the tab reloads and receives a fresh `init`. + +### From the tab + +Send `ready` once your message listener is registered: + +```ts @nocheck +{ type: "ready", v: 1 } +``` + +If a fetch returns 401, the token has rotated. Ask the dashboard for a +fresh one and wait for the next `init` — don't retry with the stale +token: + +```ts @nocheck +{ type: "token-refresh-needed", v: 1 } +``` + +### Security check + +Always reject messages whose `event.origin` doesn't match the +`?shellOrigin=` URL parameter. Without this check, any page that frames +your tab could forge an `init` and feed you a fake token. + +### TypeScript types + +If you build the tab with TypeScript, the message and response types +are exported as types-only: + +## Reading state and calling actions + +The tab can hit any inspector endpoint with the supplied bearer token. +Use relative paths so the tab doesn't need to know the engine origin or +actor id: + +```js +fetch("../../state", { headers: { Authorization: `Bearer ${token}` } }); +fetch("../../action/increment", { method: "POST", headers: { ... }, body: ... }); +fetch("../../rpcs", { headers: { Authorization: `Bearer ${token}` } }); +fetch("../../connections", { headers: { Authorization: `Bearer ${token}` } }); +fetch("../../queue", { headers: { Authorization: `Bearer ${token}` } }); +``` + +The action body shape is `{ args: [...] }` — the array is passed as +positional arguments to the action. + +Full endpoint reference: +[Debugging → Inspector Endpoints](/docs/actors/debugging#inspector-endpoints). + +For high-frequency UIs, prefer the inspector WebSocket +(`/inspector/connect`) over polling. + +## Styling + +A shared stylesheet matching the dashboard's design tokens is served at +`../../tab.css`: + +```html + +``` + +It defines `--rivet-*` tokens for colors, spacing, radius, and +typography, plus sensible defaults so a bare tab looks at home without +custom CSS: + +```css +.my-card { + background: var(--rivet-card); + color: var(--rivet-foreground); + border: 1px solid var(--rivet-border); + border-radius: var(--rivet-radius-md); + padding: var(--rivet-space-4); +} +``` + +Toggle dark mode by adding the `dark` class to `` — the dashboard +sends the active theme in the `init` message. + +Color tokens come in both pre-wrapped (`--rivet-card`) and raw HSL +(`--rivet-card-raw`) forms, so you can compose with alpha: + +```css +.overlay { background: hsl(var(--rivet-background-raw) / 0.6); } +``` + +You're free to skip the stylesheet entirely and bring your own. + +## Security + +The tab runs in an iframe at the engine origin and can call any +inspector endpoint with the supplied token. Treat the bundle like any +author code that ships with your actor: + +- **Don't inline secrets.** The bundle is fetchable by anyone who can + reach the actor. +- **Always validate `event.origin`.** Reject inbound messages from + anywhere other than the dashboard origin. +- **Never retry silently on 401.** Post `token-refresh-needed` and wait + for a fresh `init`. + +## See also + +- [Debugging](/docs/actors/debugging) — full inspector HTTP API +- [Actions](/docs/actors/actions) — actions your tab can invoke +- [State](/docs/actors/state) — state your tab can read +- [`examples/inspector-tabs`](https://github.com/rivet-dev/rivet/tree/main/examples/inspector-tabs) — runnable example + +_Source doc path: /docs/actors/inspector-tabs_ diff --git a/.agents/skills/live-cursors/reference/actors/keys.md b/.agents/skills/live-cursors/reference/actors/keys.md new file mode 100644 index 0000000..b8f1614 --- /dev/null +++ b/.agents/skills/live-cursors/reference/actors/keys.md @@ -0,0 +1,51 @@ +# Actor Keys + +> Source: `src/content/docs/actors/keys.mdx` +> Canonical URL: https://rivet.dev/docs/actors/keys +> Description: Actor keys uniquely identify actor instances within each actor type. Keys are used for addressing which specific actor to communicate with. + +--- +## Key Format + +Actor keys can be either a string or an array of strings: + +### Compound Keys & User Data + +Array keys are useful when you need compound keys with user-provided data. Using arrays makes adding user data safe by preventing key injection attacks: + +This allows you to create hierarchical addressing schemes and organize actors by multiple dimensions. + +Don't build keys using string interpolation like `"foo:${userId}:bar"` when `userId` contains user data. If a user provides a value containing the delimiter (`:` in this example), it can break your key structure and cause key injection attacks. + +### Omitting Keys + +You can create actors without specifying a key in situations where there is a singleton actor (i.e. only one actor of a given type). For example: + +This pattern should be avoided, since a singleton actor usually means you have a single actor serving all traffic & your application will not scale. See [scaling documentation](/docs/actors/scaling) for more information. + +### Key Uniqueness + +Keys are unique within each actor name. Different actor types can use the same key: + +## Accessing Keys in Metadata + +Access the actor's key within the actor using the [metadata](/docs/actors/metadata) API: + +## Configuration Examples + +### Simple Configuration with Keys + +Use keys to provide basic actor configuration: + +### Complex Configuration with Input + +For more complex configuration, use [input parameters](/docs/actors/input): + +## API Reference + +- [`ActorKey`](/typedoc/types/rivetkit.mod.ActorKey.html) - Key type for actors +- [`ActorQuery`](/typedoc/types/rivetkit.mod.ActorQuery.html) - Query type using keys +- [`GetOptions`](/typedoc/interfaces/rivetkit.client_mod.GetOptions.html) - Options for getting by key +- [`QueryOptions`](/typedoc/interfaces/rivetkit.client_mod.QueryOptions.html) - Options for querying + +_Source doc path: /docs/actors/keys_ diff --git a/.agents/skills/live-cursors/reference/actors/kv.md b/.agents/skills/live-cursors/reference/actors/kv.md new file mode 100644 index 0000000..384769b --- /dev/null +++ b/.agents/skills/live-cursors/reference/actors/kv.md @@ -0,0 +1,44 @@ +# Low-Level KV Storage + +> Source: `src/content/docs/actors/kv.mdx` +> Canonical URL: https://rivet.dev/docs/actors/kv +> Description: Use the built-in key-value store on ActorContext for durable string and binary data alongside actor state. + +--- +KV is deprecated. It is a low-level escape hatch kept for backward compatibility. For new actors, prefer [in-memory state](/docs/actors/state) for small serializable values or [SQLite](/docs/actors/sqlite) for larger or queryable data. + +Every Rivet Actor includes a lightweight key-value store on `c.kv`. It is useful for dynamic keys, blobs, or data that does not fit well in structured state. + +If your data has a known schema, prefer [state](/docs/actors/state). KV is best for flexible or user-defined keys. + +## Basic Usage + +Keys and values default to `text`, so you can use strings without extra options. + +## Value Types + +You can store binary values by passing `Uint8Array` or `ArrayBuffer`. Use `type` on both reads and writes to get the right value type: `binary` for `Uint8Array` and `arrayBuffer` for `ArrayBuffer`. + +TypeScript returns a concrete type based on the option you pass in: + +## Key Types + +Keys accept either `string` or `Uint8Array`. String keys are encoded as UTF-8 by default. + +When listing by prefix, you can control how keys are decoded with `keyType`. Returned keys have the prefix removed. + +If you use binary keys, set `keyType: "binary"` so the returned keys stay as `Uint8Array`. + +## Range Operations + +Use `listRange(start, end)` to read an arbitrary half-open range `[start, end)`. Use `deleteRange(start, end)` to clear that same range efficiently. + +## Batch Operations + +KV supports batch operations for efficiency. `batchPut` and `batchGet` work on raw `Uint8Array` keys and values, so encode strings before passing them in. + +## API Reference + +- [`ActorContext`](/typedoc/interfaces/rivetkit.mod.ActorContext.html) - `c.kv` is available on the context + +_Source doc path: /docs/actors/kv_ diff --git a/.agents/skills/live-cursors/reference/actors/lifecycle.md b/.agents/skills/live-cursors/reference/actors/lifecycle.md new file mode 100644 index 0000000..89955bb --- /dev/null +++ b/.agents/skills/live-cursors/reference/actors/lifecycle.md @@ -0,0 +1,388 @@ +# Lifecycle + +> Source: `src/content/docs/actors/lifecycle.mdx` +> Canonical URL: https://rivet.dev/docs/actors/lifecycle +> Description: Learn about actor lifecycle hooks for initialization, state management, and cleanup. + +--- +# Lifecycle + +Actors follow a well-defined lifecycle with hooks at each stage. Understanding these hooks is essential for proper initialization, state management, and cleanup. + +## Lifecycle + +Actors transition through several states during their lifetime. Each transition triggers specific hooks that let you initialize resources, manage connections, and clean up state. + +``` +Loading ──Start──▶ Ready ──spawn driver──▶ Started + │ + ┌────────────────────────────────────────┤ + │ │ + │ idle timer + can_sleep │ Destroy command + ▼ ▼ + SleepGrace ─── grace window closes ──▶ Destroying + │ │ + ▼ │ + SleepFinalize ──── stop sequence ───────────┤ + ▼ + Terminated +``` + +**On Create** (runs once per actor) + +1. `onMigrate` +2. `createState` +3. `onCreate` +4. `createVars` +5. `onWake` +6. `run` (background, does not block) + +**On Destroy** + +1. `onDestroy` + +**On Wake** (after sleep, restart, or crash) + +1. `onMigrate` +2. `createVars` +3. `onWake` +4. `run` (background, does not block) + +**On Sleep** (after idle period) + +1. Wait for `run` to complete (with timeout) +2. `onSleep` + +**On Connect** (per client) + +1. `onBeforeConnect` +2. `createConnState` +3. `onConnect` + +**On Disconnect** (per client) + +1. `onDisconnect` + +**On Inbound Action Invoke** (per action call) + +1. Action handler executes + +**On Inbound Queue Publish** (per `handle.send(...)`) + +1. `queues..canPublish` (if defined) +2. Queue message is enqueued + +**On Event Subscription Request** (per subscribe request) + +1. `events..canSubscribe` (if defined) +2. Subscription is applied + +## Lifecycle Hooks + +Actor lifecycle hooks are defined as functions in the actor configuration. + +### `state` + +The `state` constant defines the initial state of the actor. See [state documentation](/docs/actors/state) for more information. + +### `onMigrate` + +[API Reference](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html) + +The `onMigrate` hook runs on every actor start, before `createState`, `onCreate`, `createVars`, and `onWake`. Can be async. It runs early so that database migrations are applied before any other lifecycle hook accesses the database. The second parameter is `true` when the actor is being created for the first time. + +### `createState` + +[API Reference](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html) + +The `createState` function dynamically initializes state based on input. Called only once when the actor is first created. Can be async. See [state documentation](/docs/actors/state) for more information. + +### `vars` + +The `vars` constant defines ephemeral variables for the actor. These variables are not persisted and are useful for storing runtime-only data. The value for `vars` must be clonable via `structuredClone`. See [ephemeral variables documentation](/docs/actors/state#ephemeral-variables) for more information. + +### `createVars` + +[API Reference](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html) + +The `createVars` function dynamically initializes ephemeral variables. Can be async. Use this when you need to initialize values at runtime. See [ephemeral variables documentation](/docs/actors/state#ephemeral-variables) for more information. + +### `onCreate` + +[API Reference](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html) + +The `onCreate` hook is called when the actor is first created. Can be async. Use this hook for initialization logic that doesn't affect the initial state. + +### `onDestroy` + +[API Reference](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html) + +The `onDestroy` hook is called when the actor is being permanently destroyed. Can be async. Use this for final cleanup operations like closing external connections, releasing resources, or performing any last-minute state persistence. + +The actor is still fully functional when `onDestroy` runs. You can access the database, broadcast events, call `waitUntil`, send queue messages, and use `schedule.after`. State mutations made during `onDestroy` are persisted before the actor is torn down. + +### `onWake` + +[API Reference](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html) + +This hook is called any time the actor is started (e.g. after restarting, upgrading code, or crashing). Can be async. + +This is called after the actor has been initialized but before any connections are accepted. + +Use this hook to set up any resources or start any background tasks, such as `setInterval`. + +### `onSleep` + +[API Reference](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html) + +This hook is called when the actor is going to sleep. Can be async. Use this to clean up resources, close connections, or perform any shutdown operations. + +The actor is still fully functional when `onSleep` runs. You can access the database, broadcast events, call `waitUntil`, send queue messages, and use `schedule.after`. State mutations made during `onSleep` are persisted before the actor finishes sleeping. + +This hook may not always be called in situations like crashes or forced terminations. Don't rely on it for critical cleanup operations. + +Not supported on Cloudflare Workers. + +### `run` + +[API Reference](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html) + +The `run` hook is called after the actor starts and runs in the background without blocking actor startup. This is ideal for long-running background tasks like: + +- Reading from message queues in a loop +- Tick loops for periodic work +- Custom workflow logic +- Background processing + +The handler exposes `c.aborted` for loop checks and `c.abortSignal` for canceling operations when the actor is stopping. You should always check or listen for shutdown to exit gracefully. + +**Important behavior:** +- The actor may go to sleep at any time during the `run` handler. Wrap work that must keep the actor awake with `c.keepAwake(promise)` to block idle sleep until the promise settles. +- If the `run` handler exits (returns), the actor follows its normal idle sleep timeout once it becomes idle +- If the `run` handler throws an error, the actor logs the error and then follows its normal idle sleep timeout once it becomes idle +- On shutdown, `c.abortSignal` fires so the `run` handler can exit within the graceful shutdown window. + +Finite `run` handlers leave the actor alive after they finish. If you want a one shot task to clean itself up when it is done, call `c.destroy()` before returning. + +### `onStateChange` + +[API Reference](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html) + +Called whenever the actor's state changes. Cannot be async. This is often used to broadcast state updates. + +Do not mutate `c.state` inside `onStateChange`; re-entrant state mutation is rejected. + +### `createConnState` and `connState` + +[API Reference](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html) + +There are two ways to define the initial state for connections: +1. `connState`: Define a constant object that will be used as the initial state for all connections +2. `createConnState`: A function that dynamically creates initial connection state based on connection parameters. Can be async. + +### `onBeforeConnect` + +[API Reference](/typedoc/interfaces/rivetkit.mod.BeforeConnectContext.html) + +The `onBeforeConnect` hook is called whenever a new client connects to the actor. Can be async. Clients can pass parameters when connecting, accessible via `params`. This hook is used for connection validation and can throw errors to reject connections. + +The `onBeforeConnect` hook does NOT return connection state - it's used solely for validation. + +Connections cannot interact with the actor until this method completes successfully. Throwing an error will abort the connection. This can be used for authentication - see [Authentication](/docs/actors/authentication) for details. + +### `onConnect` + +[API Reference](/typedoc/interfaces/rivetkit.mod.ConnectContext.html) + +Executed after the client has successfully connected. Can be async. Receives the connection object as a second parameter. + +Messages will not be processed for this actor until this hook succeeds. Errors thrown from this hook will cause the client to disconnect. + +### `canPublish` and `canSubscribe` + +[API Reference](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html) + +Use schema-level hooks to authorize queue publishes and event subscriptions. Both hooks can be async and must return booleans: + +- `queues..canPublish` runs before inbound queue publishes. +- `events..canSubscribe` runs before inbound event subscription requests. + +For actions, enforce authorization directly inside each action handler. + +Use deny-by-default rules for each hook and return `false` unless explicitly allowed. See [Access Control](/docs/actors/access-control) for full guidance. + +### `onDisconnect` + +[API Reference](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html) + +Called when a client disconnects from the actor. Can be async. Receives the connection object as a second parameter. Use this to clean up any connection-specific resources. + +### `onRequest` + +[API Reference](/typedoc/interfaces/rivetkit.mod.RequestContext.html) + +The `onRequest` hook handles HTTP requests sent to your actor at `/actors/{actorName}/http/*` endpoints. Can be async. It receives the request context and a standard `Request` object, and should return a `Response` object. + +See [Request Handler](/docs/actors/request-handler) for more details. + +### `onWebSocket` + +[API Reference](/typedoc/interfaces/rivetkit.mod.WebSocketContext.html) + +The `onWebSocket` hook handles WebSocket connections to your actor. Can be async. It receives the actor context and a `WebSocket` object. Use this to set up WebSocket event listeners and handle real-time communication. + +See [WebSocket Handler](/docs/actors/websocket-handler) for more details. + +### `onBeforeActionResponse` + +[API Reference](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html) + +The `onBeforeActionResponse` hook is called before sending an action response to the client. Can be async. Use this hook to modify or transform the output of an action before it's sent to the client. This is useful for formatting responses, adding metadata, or applying transformations to the output. + +## Sleeping + +Actors automatically sleep after a period of inactivity to free up resources. When a request arrives for a sleeping actor, it wakes up, restores its state, and handles the request. + +### When Actors Sleep + +#### Idle Timeout + +An actor is considered idle and eligible to sleep when **all** of the following are true: + +- No active HTTP requests +- No active connections (unless they are hibernatable WebSockets) +- No active `run` handler (unless it is waiting on a queue) +- No outstanding `c.keepAwake(promise)` promises +- No pending disconnect callbacks +- No async `onWebSocket` event handlers (eg `open`, `message`, `close`) still running + +Once the actor becomes idle, the sleep timer starts. After `sleepTimeout` (default 30 seconds) of continuous inactivity, the actor begins the sleep process. Any activity resets the timer. + +Outbound requests (e.g. `fetch` calls) do not count as activity and will not keep the actor awake. Wrap them with `c.waitUntil()` if they must complete before the actor sleeps. + +#### Upgrades & Eviction + +The platform may force an actor to migrate to a new machine during version upgrades or when a serverless request is about to timeout. The same [shutdown sequence](#shutdown-sequence) runs, then the actor is rescheduled on a new machine and wakes up with its persisted state. + +Use `onSleep`, `waitUntil`, or `keepAwake` to control the length of the grace period before the actor moves to another machine. + +### Manual Lifecycle Controls + +You can also trigger lifecycle transitions from the Rivet Cloud API. These endpoints are useful for operational workflows, debugging, and forcing an actor to move through the same sleep or reschedule path that the platform would normally trigger. + +```bash +curl -X POST \ + "https://cloud-api.rivet.dev/actors/$ACTOR_ID/sleep?namespace=$NAMESPACE" \ + -H "Authorization: Bearer $RIVET_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{}' +``` + +```bash +curl -X POST \ + "https://cloud-api.rivet.dev/actors/$ACTOR_ID/reschedule?namespace=$NAMESPACE" \ + -H "Authorization: Bearer $RIVET_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{}' +``` + +`/sleep` asks the actor to enter the normal sleep shutdown sequence. `/reschedule` asks the platform to allocate the actor again, which is useful after crashes or when you need to force a fresh placement. Both endpoints require the actor ID and namespace. + +### Skip Ready Wait + +The gateway normally holds requests until the actor is ready. The actor is not ready during startup (before `onWake` finishes) or during the sleep grace period (while `onSleep` and `waitUntil` are running). Probes and readiness checks can opt out with `skipReadyWait` to reach the actor's `onRequest` or `onWebSocket` handler in either window. + +See [Skip Ready Wait](/docs/clients/javascript#skip-ready-wait) on the JavaScript client page for usage. + +### Keeping the Actor Awake + +RivetKit gives you two primitives for holding the actor awake across background work. Both take a `Promise` and differ in how they interact with idle sleep and the grace period. + +| Method | Accepts | Blocks idle sleep | Blocks grace finalize | Use case | +| --- | --- | --- | --- | --- | +| `c.keepAwake(promise)` | `Promise` (returns same promise) | Yes | Yes | Critical work that must keep the actor running end to end (for example a turn in a game, an ongoing tool call). | +| `c.waitUntil(promise)` | `Promise` (returns void) | No | Yes | Best-effort finalization work that may complete during the grace window (for example analytics flushes, cleanup writes). | + +`c.keepAwake(promise)` is the preferred primitive for long-running work the actor should not sleep through. It holds a keep-awake counter until the promise settles, which blocks both idle sleep and the grace finalize step. The promise is returned unchanged, so you can `await` it if you need the value. + +`setPreventSleep(enabled)` is deprecated and now a no-op. Wrap the work you want to keep alive with `c.keepAwake(promise)` instead. + +### On Sleep Hook + +The [`onSleep`](#onsleep) hook runs during shutdown for cleanup like clearing intervals or closing connections. It is best-effort and will not run if the actor crashes. + +### Wait Before Sleep + +`c.waitUntil(promise)` registers a background promise that must resolve before the actor finishes sleeping. Use this to flush data or finish in-flight work during shutdown without blocking the main execution flow. + +The actor waits up to `sleepGracePeriod` for graceful sleep work during the [shutdown sequence](#shutdown-sequence). That single budget covers `onSleep`, `waitUntil`, `keepAwake`, async raw WebSocket handlers such as `message` and `close`. By default, this graceful sleep window is 15 seconds total. If the timeout is exceeded, the actor proceeds with sleep anyway. + +### Sleep Timeouts + +| Option | Default | Description | +|--------|---------|-------------| +| `sleepTimeout` | 30 seconds | Time of inactivity before the actor begins sleeping. | +| `sleepGracePeriod` | 15 seconds | Total graceful shutdown window for hooks, `waitUntil`, `keepAwake`, async raw WebSocket handlers, and disconnects. | + +Rivet enforces a hard limit of **30 minutes** for the entire stop process. These can be configured in the [options](#options). + +### WebSocket Hibernation + +WebSocket connections are preserved across sleep cycles by default and transparently migrated to the new actor instance. Client stays connected and sees no interruption. Actor migration is very fast, realtime workloads are not interrupted. + +### Shutdown Sequence + +When an actor sleeps or is destroyed, it enters the graceful shutdown window: + +1. `c.abortSignal` fires and `c.aborted` becomes `true`. New connections and dispatch are rejected. Alarm timeouts are cancelled. On sleep, scheduled events are persisted and will be re-armed when the actor wakes. +2. `onSleep` or `onDestroy` and `onDisconnect` for each closing connection run during the same window. User `waitUntil` promises and async raw WebSocket handlers are drained. Hibernatable WebSocket connections are preserved on sleep and closed on destroy. +3. Once graceful work has completed, state is saved and final cleanup runs. + +The entire window is bounded by `sleepGracePeriod` on both sleep and destroy. Defaults to 15 seconds. If the window is exceeded, the actor proceeds to state save anyway. + +#### Graceful shutdown window + +During steps 1 through 6, the actor is still fully functional. Database access, `broadcast`, `waitUntil`, `queue.send`, and `schedule.after` all work. State mutations are persisted at step 7. Actions invoked by pre-existing connections or lifecycle hooks continue to execute normally. + +New connections and raw WebSocket upgrades are rejected as soon as the shutdown sequence begins. New requests that arrive during shutdown are held until the actor wakes up again. The caller does not need to retry. + +If `schedule.after` is called during shutdown, the event is persisted so it survives the sleep/wake cycle, but no local timeout is scheduled. The event will fire after the actor wakes. + +In-flight actions are **not** waited on during shutdown. If an action must complete before the actor stops, wrap the critical work with `c.waitUntil()`. + +## Options + +The `options` object allows you to configure various timeouts and behaviors for your actor. + +| Option | Default | Description | +|--------|---------|-------------| +| `createVarsTimeout` | 5000ms | Timeout for `createVars` function | +| `createConnStateTimeout` | 5000ms | Timeout for `createConnState` function | +| `onConnectTimeout` | 5000ms | Timeout for `onConnect` hook | +| `sleepGracePeriod` | 15000ms | Total graceful shutdown window for both sleep and destroy | +| `stateSaveInterval` | 1000ms | Interval for persisting state | +| `actionTimeout` | 60000ms | Timeout for action execution | +| `connectionLivenessTimeout` | 2500ms | Timeout for connection liveness check | +| `connectionLivenessInterval` | 5000ms | Interval for connection liveness check | +| `sleepTimeout` | 30000ms | Time before actor sleeps due to inactivity | +| `canHibernateWebSocket` | false | Whether WebSockets can hibernate (experimental) | + +## Advanced + +### Actor Shutdown Abort Signal + +The `c.abortSignal` provides an `AbortSignal` that fires when the actor is stopping, and `c.aborted` is the shorthand boolean for loop checks. Use these to cancel ongoing operations when the actor sleeps or is destroyed. + +The abort signal fires at the very start of the [shutdown sequence](#shutdown-sequence), before `onSleep` or `onDestroy` runs. This means `c.aborted` is already `true` inside those lifecycle hooks. The signal fires early so that the `run` handler can exit promptly, but the actor remains fully functional throughout the graceful shutdown window. + +See [Canceling Long-Running Actions](/docs/actors/actions#canceling-long-running-actions) for manually canceling operations on-demand. + +### Using `ActorContext` Type Externally + +When extracting logic from lifecycle hooks or actions into external functions, you'll often need to define the type of the context parameter. Rivet provides helper types that make it easy to extract and pass these context types to external functions. + +See [Types](/docs/actors/types) for more details on using `ActorContextOf`. + +## Full Example + +_Source doc path: /docs/actors/lifecycle_ diff --git a/.agents/skills/live-cursors/reference/actors/limits.md b/.agents/skills/live-cursors/reference/actors/limits.md new file mode 100644 index 0000000..d69c889 --- /dev/null +++ b/.agents/skills/live-cursors/reference/actors/limits.md @@ -0,0 +1,169 @@ +# Limits + +> Source: `src/content/docs/actors/limits.mdx` +> Canonical URL: https://rivet.dev/docs/actors/limits +> Description: Limits and constraints for Rivet Actors. + +--- +This page documents the limits for Rivet Actors. + +There are two types of limits: + +- **Soft Limit**: Application-level limit, configurable in RivetKit. These cannot exceed the hard limit. +- **Hard Limit**: Infrastructure-level limit that cannot be configured. + +Soft limits are configured in two places. Registry-level WebSocket message-size limits are passed to `setup`: + +Per-actor limits such as queue sizes and lifecycle timeouts are passed to `actor(...)` via `options`: + +## Limits + +### WebSocket + +These limits affect actions that use `.connect()` and [low-level WebSockets](/docs/actors/websocket-handler). + +| Name | Soft Limit | Hard Limit | Description | +|------|------------|------------|-------------| +| Max incoming message size | 64 KiB | 32 MiB | Maximum size of incoming WebSocket messages. Soft limit configurable via `maxIncomingMessageSize`; hard limit configurable in self-hosted engine config via `guard.websocket_max_message_size`. | +| Max outgoing message size | 1 MiB | 32 MiB | Maximum size of outgoing WebSocket messages. Soft limit configurable via `maxOutgoingMessageSize`; hard limit configurable in self-hosted engine config via `guard.websocket_max_message_size`. | +| Max WebSocket frame size | — | 32 MiB | Maximum size of a single WebSocket frame accepted by Rivet's transport edge. Configurable in self-hosted engine config via `guard.websocket_max_frame_size`. This is not configurable by browser clients and is not negotiated during the WebSocket handshake. | +| WebSocket open timeout | — | 15 seconds | Time allowed for WebSocket connection to be established, including `onBeforeConnect` and `createConnState` hooks. Connection is closed if exceeded. | +| Message ack timeout | — | 30 seconds | Time allowed for message acknowledgment before connection is closed. Only relevant in the case of a network issue and does not affect your application. | + +### Hibernating WebSocket + +Hibernating WebSockets allow actors to sleep while keeping client connections alive. All WebSocket limits above also apply to hibernating WebSockets. See [WebSocket Hibernation](/docs/actors/websocket-handler#web-socket-hibernation) for details. + +| Name | Soft Limit | Hard Limit | Description | +|------|------------|------------|-------------| +| Max pending buffer size | — | 128 MiB | Total size of all buffered messages per connection while actor is sleeping. | +| Max pending message count | — | 65,535 | Maximum number of buffered messages per connection while actor is sleeping. | +| Hibernation timeout | — | 90 seconds | Maximum time an actor has to wake up before the client is disconnected. Only relevant if something is wrong with starting actors. | + +### HTTP + +These limits affect actions that do not use `.connect()` and [low-level HTTP requests](/docs/actors/request-handler). + +| Name | Soft Limit | Hard Limit | Description | +|------|------------|------------|-------------| +| Max request body size | — | 20 MiB | Maximum size of HTTP request bodies. | +| Max response body size | — | 20 MiB | Maximum size of HTTP response bodies. | +| Request timeout | 60 seconds | — | Maximum time for an `onRequest` handler to complete. Defaults to `actionTimeout`; configure with `actionTimeout`. | + +### Networking + +| Name | Soft Limit | Hard Limit | Description | +|------|------------|------------|-------------| +| Connection ping timeout | — | 30 seconds | Connection is closed if a ping is not acknowledged within this time. Applies to both HTTP and WebSocket. Only relevant in the case of a network issue and does not affect your application. | + +### Queue + +| Name | Soft Limit | Hard Limit | Description | +|------|------------|------------|-------------| +| Max queue size | 1,000 messages | — | Maximum number of messages in the queue before new messages are rejected. Configurable via `maxQueueSize`. | +| Max queue message size | 64 KiB | 128 KiB (effective) | Maximum size of each individual queue message. Configurable via `maxQueueMessageSize`. Actual payload is slightly lower after queue serialization overhead. | + +### Actor KV Storage + +These limits apply to the low-level KV storage interface powering Rivet Actors. They likely do not affect your application, but are documented for completeness. + +| Name | Soft Limit | Hard Limit | Description | +|------|------------|------------|-------------| +| Max key size | — | 2 KiB | Maximum size of a single key. | +| Max value size | — | 128 KiB | Maximum size of a single value. | +| Max keys per operation | — | 128 | Maximum number of keys in a single batch get/put/delete operation. Does not apply to range operations (`listRange`, `deleteRange`). | +| Max batch put payload size | — | 976 KiB | Maximum total size of all key-value pairs in a single batch put operation. | +| Max storage size per actor | — | 10 GiB | Maximum total KV storage size for a single actor. | +| List default limit | — | 16,384 | Default maximum number of keys returned by a list operation. | + +### Actor SQLite Storage + +These limits apply to the [SQLite database](/docs/actors/state#sqlite-database) available via `this.sql`. SQLite data is stored through the KV layer, so the storage limit is shared with KV storage. + +| Name | Soft Limit | Hard Limit | Description | +|------|------------|------------|-------------| +| Max storage size per actor | — | 10 GiB | Maximum total storage size for a single actor. This limit is shared with KV storage. | +| Max dirty data per commit | — | 1,310,720 bytes | Maximum raw SQLite page data that can be written in a single commit. This is 320 dirty pages at 4 KiB per page. | +| Transaction deadline | 60 seconds | — | Safety backstop for `db.transaction()`. Configure per transaction with `{ timeout: milliseconds }`; there is no product-level maximum. | +| Transaction coordinator queue | — | 128 operations | Maximum admitted SQLite operations waiting behind an active coordinated transaction. Additional operations fail with `sqlite.transaction_queue_full`. | + +SQLite commit deltas are compressed and split into internal chunks before storage, but all chunks for one commit are published atomically. The commit limit is therefore based on raw dirty page bytes before compression, not the compressed delta size. + +Avoid `VACUUM` inside actor databases. It rewrites the database in one transaction and can exceed the dirty-page commit limit even when ordinary incremental writes fit. + +### Actor Runtime Socket + +The Beta [Actor Runtime Socket](/docs/actors/actor-runtime-socket) negotiates its active frame limit during the handshake. + +| Name | Soft Limit | Hard Limit | Description | +|------|------------|------------|-------------| +| Frame payload size | 32 MiB | — | Default maximum request or response payload. Self-hosted native runtimes can configure `RIVET_ACTOR_RUNTIME_SOCKET_MAX_FRAME_BYTES`; oversized responses return `ResponseTooLarge`. | + +### KV Preloading + +When an actor starts, the engine can pre-fetch KV data declared in the actor name metadata and deliver it alongside the start command. This removes round-trips to storage during actor startup. RivetKit emits the preload manifest from its own key layout and exposes per-actor overrides via `options`. Operators can still enforce a global cap in the [engine config](/docs/self-hosting/configuration) with `pegboard.preload_max_total_bytes`. In serverless mode, this data is serialized into the `/api/rivet/start` request body along with actor config and protocol metadata, so the accepted body size must be larger than the preload budget. RivetKit defaults `serverless.maxStartPayloadBytes` to 16 MiB to leave margin for the default 1 MiB preload budget and larger SQLite startup page preloads. + +| Name | Soft Limit | Hard Limit | Description | +|------|------------|------------|-------------| +| Max total preload size | 1 MiB | — | Maximum total size of all preloaded KV data sent with the start command. Configurable via `pegboard.preload_max_total_bytes`. Setting to 0 disables all preloading. | +| Max workflow preload size | 128 KiB | — | Default maximum size of preloaded workflow data for RivetKit actors. Configurable per actor via `options.preloadMaxWorkflowBytes`. Setting to 0 disables workflow preloading for that actor. | +| Max connections preload size | 64 KiB | — | Default maximum size of preloaded connection data for RivetKit actors. Configurable per actor via `options.preloadMaxConnectionsBytes`. Setting to 0 disables connections preloading for that actor. | + +### Actor Input + +See [Actor Input](/docs/actors/input) for details. + +| Name | Soft Limit | Hard Limit | Description | +|------|------------|------------|-------------| +| Max actor input size | — | 4 MiB | Maximum size of the input passed when creating an actor. | +| Max connection params size | — | 4 KiB | Maximum size of connection parameters passed when connecting to an actor. | +| Max actor key component size | — | 128 bytes | Maximum size of each component in an actor key array. | +| Max actor key total size | — | 1,024 bytes | Maximum total size of the serialized actor key string. | +| Max actor name length | — | 64 characters | Maximum length for actor and project identifiers. | + +### Rate Limiting + +| Name | Soft Limit | Hard Limit | Description | +|------|------------|------------|-------------| +| Rate limit | — | 1200 requests/minute | Default rate limit per actor per IP address with a 1 minute time bucket. | +| Max in-flight requests | — | 32 | Default maximum concurrent requests to an actor per IP address. | + +### Timeouts + +| Name | Soft Limit | Hard Limit | Description | +|------|------------|------------|-------------| +| Action timeout | 60 seconds | — | Timeout for RPC actions. Configurable via `actionTimeout`. | +| On request timeout | 60 seconds | — | Timeout for raw `onRequest` handlers. Defaults to `actionTimeout`; configure with `actionTimeout`. | +| On before connect timeout | 5 seconds | — | Timeout for the `onBeforeConnect` hook. Configurable via `onBeforeConnectTimeout`. | +| Create vars timeout | 5 seconds | — | Timeout for `createVars` hook. Configurable via `createVarsTimeout`. | +| Create conn state timeout | 5 seconds | — | Timeout for `createConnState` hook. Configurable via `createConnStateTimeout`. | +| On connect timeout | 5 seconds | — | Timeout for `onConnect` hook. Configurable via `onConnectTimeout`. | +| On migrate timeout | 30 seconds | — | Timeout for `onMigrate` hook. Configurable via `onMigrateTimeout`. | +| Sleep grace period | 15 seconds | — | Total graceful shutdown budget for both sleep and destroy. Covers `onSleep`/`onDestroy`, run handler shutdown, `waitUntil`, `keepAwake`, async raw WebSocket handlers, and connection cleanup. Configurable via `sleepGracePeriod`. | +| Sleep timeout | 30 seconds | — | Time of inactivity before actor hibernates. Configurable via `sleepTimeout`. | +| State save interval | 1 second | — | Interval between automatic state saves. Configurable via `stateSaveInterval`. | +| Connection liveness timeout | 2.5 seconds | — | Time a stateful connection can miss liveness checks before RivetKit treats it as unhealthy. Configurable via `connectionLivenessTimeout`. | +| Connection liveness interval | 5 seconds | — | Interval between stateful connection liveness checks. Configurable via `connectionLivenessInterval`. | + +### Serverless Shutdown + +These timeouts control how actors are shut down when a serverless request reaches its lifespan limit. See [Shutdown Sequence](/docs/general/runtime-modes#shutdown-sequence) for details. + +| Name | Soft Limit | Hard Limit | Description | +|------|------------|------------|-------------| +| Request lifespan | 3600 seconds (60 min) | — | Total lifespan of a serverless request before drain begins. Configurable via `requestLifespan` in [`configurePool`](/docs/general/registry-configuration). | +| Runner config drain grace period | — | 30 minutes | Time a serverless runner reserves for actors to stop gracefully. Configurable via `drainGracePeriod` in [`configurePool`](/docs/general/registry-configuration). | +| Engine serverless drain fallback | — | 10 seconds | Engine-level fallback used when no per-runner config applies. Configurable via [engine config](/docs/self-hosting/configuration) (`pegboard.serverless_drain_grace_period`). | + +### Actor Lifecycle + +| Name | Soft Limit | Hard Limit | Description | +|------|------------|------------|-------------| +| Actor start threshold | — | 30 seconds | Maximum time for an actor to start before it is considered lost and rescheduled. | +| Actor stop threshold | — | 30 minutes | Maximum time for an actor to stop before it is considered lost. | + +## Increasing Limits + +These limits are sane defaults designed to protect your application from exploits and accidental runaway bugs. If you have a use case that requires different limits, [contact us](https://rivet.dev/contact) to discuss your requirements. + +_Source doc path: /docs/actors/limits_ diff --git a/.agents/skills/live-cursors/reference/actors/metadata.md b/.agents/skills/live-cursors/reference/actors/metadata.md new file mode 100644 index 0000000..d3dcc24 --- /dev/null +++ b/.agents/skills/live-cursors/reference/actors/metadata.md @@ -0,0 +1,39 @@ +# Metadata + +> Source: `src/content/docs/actors/metadata.mdx` +> Canonical URL: https://rivet.dev/docs/actors/metadata +> Description: Metadata provides information about the currently running actor. + +--- +## Actor ID + +Get the unique instance ID of the actor: + +## Actor Name + +Get the actor type name: + +This is useful when you need to know which actor type is running, especially if you have generic utility functions that are shared between different actor implementations. + +## Actor Key + +Get the actor key used to identify this actor instance: + +The key is used to route requests to the correct actor instance and can include parameters passed when creating the actor. + +Learn more about using keys for actor addressing and configuration in the [keys documentation](/docs/actors/keys). + +## Region + +Region can be accessed from the context object via `c.region`. + +`c.region` is only supported on Rivet at the moment. + +## Example Usage + +## API Reference + +- [`ActorDefinition`](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html) - Interface for defining metadata +- [`CreateOptions`](/typedoc/interfaces/rivetkit.client_mod.CreateOptions.html) - Options for creating an actor, including `region` and `input` + +_Source doc path: /docs/actors/metadata_ diff --git a/.agents/skills/live-cursors/reference/actors/queues.md b/.agents/skills/live-cursors/reference/actors/queues.md new file mode 100644 index 0000000..cf75e0b --- /dev/null +++ b/.agents/skills/live-cursors/reference/actors/queues.md @@ -0,0 +1,120 @@ +# Queues & Run Loops + +> Source: `src/content/docs/actors/queues.mdx` +> Canonical URL: https://rivet.dev/docs/actors/queues +> Description: Use actor-local durable queues for serial run loops and request/response workflows. + +--- +## What are queues? + +- **Realtime**: messages are delivered to a live actor as soon as possible. +- **Durable**: messages are persisted and survive actor sleep/restart. +- **Request/response**: clients can wait for a queue completion response. +- **Scalable**: queues absorb large bursts and handle heavy backpressure safely. +- **Local per actor**: each actor instance has its own queue storage (scoped by actor key/id). + +Queues are commonly referred to as "mailboxes" in other actor frameworks. + +## What are queues good for? + +- Great for any task that changes actor state. +- Helps avoid race conditions by handling work in order. +- Makes complex behavior easier to organize. + +## Basic queue + +This is the default pattern. Define queue names in `queues`, process them in `run`, and publish from the client with `handle.send(...)`. + +## Completable messages + +Use this when you want explicit completion/ack semantics but do not need to return data. + +- `message.complete()` resolves a sender waiting on `wait: true` (or `enqueueAndWait`). It does not change durability: messages are removed from queue storage when they are received, not when they are completed. +- If processing fails before `message.complete()`, the message is not redelivered, and any waiting sender times out instead of receiving a completion. +- `status: "timedOut"` means sender timeout elapsed before `message.complete(...)`. + +## Request/reply pattern + +Use this when the sender needs data back from queued work. + +## Queue messages from within an actor + +Queueing is useful from inside actor logic too, not just from clients. + +- Use actions as entrypoints, then enqueue into the run loop to keep mutations serialized. +- You can also call `c.queue.send(...)` from other parts of `run` when needed. +- `c.queue.send(...)` confirms durable enqueue. It does not wait for processing to finish. + +## Defining queue schemas + +You can define queue types with `queue()` or with schema objects. Schema objects support [Standard Schema](https://standardschema.dev/) validators, including [Zod](https://zod.dev/). + +## Pull messages with `next` and `nextBatch` + +Use `next` when you want to wait for one queue message. +Use `nextBatch` when you want to wait for multiple queue messages. + +- Waits until messages are available unless timeout is hit. +- Omit `timeout` to wait indefinitely. + +## Poll messages + +Use `tryNext` when you need one non-blocking read. +Use `tryNextBatch` for non-blocking batch reads. + +- Returns immediately and never waits. + +## Abort signals + +Use `signal` when your receive loop needs external cancellation semantics in addition to actor shutdown behavior. + +## Multiple queues + +Multiple queues let you separate message flows by purpose. By default, receive calls race across all queues when `names` is not specified. In this pattern, prompt messages run through a streaming loop while stop messages act as control signals on a separate receive path. + +Use `iter({ names: ["prompt"] })` as the main stream and `next({ names: ["stop"] })` as a stop signal. + +## Sleeping behavior + +If an actor has a `run` handler, it does not sleep while that handler is actively doing work. It only can sleep when the run loop is blocked waiting for queue entries (for example inside `iter(...)` or `next(...)`). + +This means you can run normal code in `run` without worrying about sleep interrupting it mid-call. + +## Debugging + +- `GET /inspector/queue?limit=50` returns queue size and pending message metadata. +- `GET /inspector/summary` includes `queueSize` for quick queue health checks. +- `POST /queue/:name` with `wait: true` is useful to verify completable/request-response behavior. +- In non-dev mode, inspector endpoints require authorization. + +## Recommendations + +- Actions are for getting data, queue entries are for mutating data. +- Implement connection auth in `onBeforeConnect`. See [Authentication](/docs/actors/authentication). +- Route most state changes through one queue loop so ordering stays predictable. +- If you need more complex multi-step run loops, consider using workflows. +- Use `c.aborted` and `c.abortSignal` for actor shutdown. Use your own `AbortController` for earlier loop cancellation. +- Add `timeout` when callers need bounded wait behavior. +- Use `wait: true` only when the caller actually needs a response. + +## Pitfalls + +### Avoid `wait: true` between actors + +`wait: true` blocks the sender's run loop until the receiver finishes. Between actors, this adds unnecessary overhead and risks deadlocks, especially if the target actor needs to communicate back. If an actor sends a `wait: true` message to *itself*, it is a guaranteed deadlock because the run loop is already busy processing the current message. + +Reserve `wait: true` for external callers (HTTP handlers, CLI tools, client apps). For actor-to-actor communication, send a queue message to the other actor without `wait: true`, then have that actor send a queue message back when the work is done. + +## Tips + +### Message TTL + +Every queue message includes a `createdAt` timestamp. Use this to skip or discard stale messages in your run loop: + +### Delayed delivery + +Use [`c.schedule`](/docs/actors/schedule) to enqueue messages at a future time instead of processing them immediately: + +See [Schedule](/docs/actors/schedule) for the full scheduling API. + +_Source doc path: /docs/actors/queues_ diff --git a/.agents/skills/live-cursors/reference/actors/quickstart/backend.md b/.agents/skills/live-cursors/reference/actors/quickstart/backend.md new file mode 100644 index 0000000..86cbdf2 --- /dev/null +++ b/.agents/skills/live-cursors/reference/actors/quickstart/backend.md @@ -0,0 +1,64 @@ +# Node.js & Bun Quickstart + +> Source: `src/content/docs/actors/quickstart/backend.mdx` +> Canonical URL: https://rivet.dev/docs/actors/quickstart/backend +> Description: Get started with Rivet Actors in Node.js and Bun + +--- +Prefer to start from a complete project? See the runnable [`hello-world`](https://github.com/rivet-dev/rivet/tree/main/examples/hello-world) example. + +## Steps + +### Add Rivet Skill to Coding Agent (Optional) + +If you're using an AI coding assistant (like Claude Code, Cursor, Windsurf, etc.), add Rivet skills for enhanced development assistance: + +```sh +npx skills add rivet-dev/skills +``` + +### Install Rivet + +```sh +npm install rivetkit +``` + +### Create Actors and Start Server + +Create a file with your actors, set up the registry, and start the server: + +### Run Server + +```sh Node.js +npx tsx --watch index.ts +``` + +```sh Bun +bun --watch index.ts +``` + +```sh Deno +deno run --allow-net --allow-read --allow-env --watch index.ts +``` + +Your server is now running on `http://localhost:6420`. Clients connect directly to the Rivet Engine on this port. + +Visit [http://localhost:6420](http://localhost:6420) in your browser (or point your AI agent at it) to open the Rivet developer tools and inspect your actors live. + +### Connect To The Rivet Actor + +This code can run either in your frontend or within your backend: + +### TypeScript + +See the [JavaScript client documentation](/docs/clients/javascript) for more information. + +### React + +See the [React documentation](/docs/clients/react) for more information. + +### Deploy + +## Configuration Options + +_Source doc path: /docs/actors/quickstart/backend_ diff --git a/.agents/skills/live-cursors/reference/actors/quickstart/cloudflare.md b/.agents/skills/live-cursors/reference/actors/quickstart/cloudflare.md new file mode 100644 index 0000000..e39cbe4 --- /dev/null +++ b/.agents/skills/live-cursors/reference/actors/quickstart/cloudflare.md @@ -0,0 +1,165 @@ +# Cloudflare Workers Quickstart + +> Source: `src/content/docs/actors/quickstart/cloudflare.mdx` +> Canonical URL: https://rivet.dev/docs/actors/quickstart/cloudflare +> Description: Set up a Rivet project locally targeting Cloudflare Workers. + +--- +Set up a Rivet project locally that runs on Cloudflare Workers. The `@rivetkit/cloudflare-workers` package wires the WebAssembly runtime for you. + +Prefer to start from a complete project? See the runnable [`hello-world-cloudflare-workers`](https://github.com/rivet-dev/rivet/tree/main/examples/hello-world-cloudflare-workers) example (with [Hono](https://github.com/rivet-dev/rivet/tree/main/examples/hello-world-cloudflare-workers-hono) and [raw router](https://github.com/rivet-dev/rivet/tree/main/examples/hello-world-cloudflare-workers-raw) variants). + +## Steps + +### Install Packages + +```sh +npm install rivetkit @rivetkit/cloudflare-workers +npm install --save-dev wrangler +``` + +### Configure Wrangler + +```toml wrangler.toml +name = "rivetkit-cloudflare" +main = "src/index.ts" +compatibility_date = "2025-04-01" +compatibility_flags = ["nodejs_compat"] +``` + +The `nodejs_compat` flag is required so the runtime can read its connection config from `process.env`. + +### Create the Worker + +`createHandler` serves the Rivet manager API on `/api/rivet`. Pick how you want to handle your own routes: + +- **Default**: Rivet handles everything. +- **Hono**: Mount a [Hono](https://hono.dev) app for your routes (`npm install hono`). +- **Raw**: Provide a `fetch` and route requests yourself. + +```ts Default @nocheck +import { actor } from "rivetkit"; +import { createHandler } from "@rivetkit/cloudflare-workers"; + +const counter = actor({ + state: { count: 0 }, + actions: { + increment: (c, amount = 1) => { + c.state.count += amount; + return c.state.count; + }, + }, +}); + +export default createHandler({ use: { counter } }); +``` + +```ts Hono @nocheck +import { actor } from "rivetkit"; +import { createClient } from "rivetkit/client"; +import { createHandler, setup } from "@rivetkit/cloudflare-workers"; +import { Hono } from "hono"; + +const counter = actor({ + state: { count: 0 }, + actions: { + increment: (c, amount = 1) => { + c.state.count += amount; + return c.state.count; + }, + }, +}); + +// `setup` returns a typed registry, so the client below is fully typed. +const registry = setup({ use: { counter } }); + +const app = new Hono(); + +app.get("/", (c) => c.text("Hello from Hono + Rivet Actors!")); + +app.post("/increment/:name", async (c) => { + // `createClient` reads RIVET_ENDPOINT from the environment (passed by `rivet + // dev`, or set as a Worker secret in production). + const client = createClient(); + const count = await client.counter.getOrCreate(c.req.param("name")).increment(1); + return c.json({ count }); +}); + +// Rivet keeps `/api/rivet`; Hono handles everything else. +export default createHandler(registry, { fetch: app.fetch }); +``` + +```ts Raw @nocheck +import { actor } from "rivetkit"; +import { createClient } from "rivetkit/client"; +import { createHandler, setup } from "@rivetkit/cloudflare-workers"; + +const counter = actor({ + state: { count: 0 }, + actions: { + increment: (c, amount = 1) => { + c.state.count += amount; + return c.state.count; + }, + }, +}); + +// `setup` returns a typed registry, so the client below is fully typed. +const registry = setup({ use: { counter } }); + +// Rivet keeps `/api/rivet`; your `fetch` handles everything else. +export default createHandler(registry, { + fetch: async (request: Request) => { + const url = new URL(request.url); + + if (url.pathname === "/") { + return new Response("Hello from a raw Rivet Worker router!"); + } + + const increment = url.pathname.match(/^\/increment\/(.+)$/); + if (request.method === "POST" && increment) { + // `createClient` reads RIVET_ENDPOINT from the environment (passed by + // `rivet dev`, or set as a Worker secret in production). + const client = createClient(); + const count = await client.counter.getOrCreate(increment[1]).increment(1); + return Response.json({ count }); + } + + return new Response("Not found", { status: 404 }); + }, +}); +``` + +### Run Locally + +Start Rivet. The CLI runs the local engine and spawns `wrangler dev` for you: + +```sh +npx @rivetkit/cli dev --provider cloudflare +``` + +Visit [http://localhost:6420](http://localhost:6420) in your browser (or point your AI agent at it) to open the Rivet developer tools and inspect your actors live. + +### Connect To The Rivet Actor + +This code can run either in your frontend or within your backend. It connects directly to the local engine on `http://localhost:6420`: + +### TypeScript + +See the [JavaScript client documentation](/docs/clients/javascript) for more information. + +### React + +See the [React documentation](/docs/clients/react) for more information. + +### Deploy + +Ready to ship? See [Deploying to Cloudflare Workers](/docs/deploy/cloudflare). + +## Related + +- [Quickstart](/docs/actors/quickstart) +- [Deploying to Cloudflare Workers](/docs/deploy/cloudflare) +- [SQLite](/docs/actors/sqlite) + +_Source doc path: /docs/actors/quickstart/cloudflare_ diff --git a/.agents/skills/live-cursors/reference/actors/quickstart/effect.md b/.agents/skills/live-cursors/reference/actors/quickstart/effect.md new file mode 100644 index 0000000..fc252e9 --- /dev/null +++ b/.agents/skills/live-cursors/reference/actors/quickstart/effect.md @@ -0,0 +1,239 @@ +# Effect.ts Quickstart (Beta) + +> Source: `src/content/docs/actors/quickstart/effect.mdx` +> Canonical URL: https://rivet.dev/docs/actors/quickstart/effect +> Description: Build a Rivet Actor with the Effect SDK + +--- +Effect support is in beta. The `@rivetkit/effect` API may change between releases. See the [`hello-world-effect`](https://github.com/rivet-dev/rivet/tree/main/examples/hello-world-effect) and [`chat-room-effect`](https://github.com/rivet-dev/rivet/tree/main/examples/chat-room-effect) examples for complete runnable projects. + +## Steps + +### Install Rivet + +Add `rivetkit`, the Effect SDK, and its Effect peers: + +```sh +npm install rivetkit @rivetkit/effect effect @effect/platform-node +``` + +### Define Your Actor + +Split each actor into a public contract and a server-only implementation so the contract can be imported from client code without leaking server details. + +The contract declares the actor and its actions. Actions are standalone values with explicit [`effect/Schema`](https://effect.website/docs/schema/introduction/) payloads and successes, validated end to end: + +```ts src/actors/counter/api.ts @nocheck +import { Action, Actor } from "@rivetkit/effect"; +import { Schema } from "effect"; + +export const Increment = Action.make("Increment", { + payload: { amount: Schema.Number }, + success: Schema.Number, +}); + +export const GetCount = Action.make("GetCount", { + success: Schema.Number, +}); + +export const Counter = Actor.make("Counter", { + actions: [Increment, GetCount], +}); +``` + +The implementation registers the actor with `.toLayer`. The wake function runs once when the actor awakes and returns the action handlers. Persisted state is accessed through a `SubscriptionRef`-like `State` API: + +```ts src/actors/counter/live.ts @nocheck +import { Actor, State } from "@rivetkit/effect"; +import { Effect, Schema } from "effect"; +import { Counter } from "./api.ts"; + +export const CounterLive = Counter.toLayer( + Effect.fnUntraced(function* ({ rawRivetkitContext, state }) { + return Counter.of({ + Increment: Effect.fnUntraced(function* ({ payload }) { + const next = yield* State.updateAndGet(state, (current) => ({ + count: current.count + payload.amount, + })).pipe(Effect.orDie); + + // Broadcast the new value to every connected client. + rawRivetkitContext.broadcast("newCount", next.count); + + return next.count; + }), + GetCount: () => + State.get(state).pipe( + Effect.map((current) => current.count), + Effect.orDie, + ), + }); + }), + { + state: { + schema: Schema.Struct({ count: Schema.Number }), + initialValue: () => ({ count: 0 }), + }, + name: "Counter", + icon: "calculator", + }, +); +``` + +### Serve The Registry + +Compose the actor layers and serve them with `Registry.serve`. `Registry.layer()` reads engine config from the environment, and the actor layer is provided a `Client` so actors can call other actors: + +```ts src/main.ts @nocheck +import { NodeRuntime } from "@effect/platform-node"; +import { Client, Registry } from "@rivetkit/effect"; +import { Layer } from "effect"; +import { CounterLive } from "./actors/counter/live.ts"; + +const endpoint = process.env.RIVET_ENDPOINT ?? "http://127.0.0.1:6420"; + +const ActorsLayer = CounterLive.pipe(Layer.provide(Client.layer({ endpoint }))); + +const MainLayer = Registry.serve(ActorsLayer).pipe(Layer.provide(Registry.layer())); + +// Keeps the layer alive. Tears down on SIGINT/SIGTERM. +Layer.launch(MainLayer).pipe(NodeRuntime.runMain); +``` + +### Run The Server + +Set `RIVET_RUN_ENGINE=1` to spawn a local Rivet Engine alongside the server. The engine binary is downloaded and cached the first time you run, so there is nothing else to install: + +```sh +RIVET_RUN_ENGINE=1 npx tsx --watch src/main.ts +``` + +Your server now connects to the Rivet Engine on `http://localhost:6420`. Clients connect directly to the engine on this port. + +Visit [http://localhost:6420](http://localhost:6420) in your browser (or point your AI agent at it) to open the Rivet developer tools and inspect your actors live. + +To point at a remote engine instead, set `RIVET_ENDPOINT=https://...` and omit `RIVET_RUN_ENGINE`. + +### Connect To The Rivet Actor + +This code can run either in your frontend or within your backend: + +### Effect + +The Effect client imports the same actor contract from your registry. `Counter.client` yields a typed accessor backed by the client layer: + +```ts src/client.ts @nocheck +import { NodeRuntime } from "@effect/platform-node"; +import { Client } from "@rivetkit/effect"; +import { Effect } from "effect"; +import { Counter } from "./actors/counter/api.ts"; + +const program = Effect.gen(function* () { + const counter = (yield* Counter.client).getOrCreate("my-counter"); + + const count = yield* counter.Increment({ amount: 3 }); + yield* Effect.log(`New count: ${count}`); + + const total = yield* counter.GetCount(); + yield* Effect.log(`Total: ${total}`); +}); + +const ClientLayer = Client.layer({ endpoint: "http://localhost:6420" }); + +program.pipe(Effect.provide(ClientLayer), NodeRuntime.runMain); +``` + +With the server still running, start the client in another terminal: + +```sh +npx tsx src/client.ts +``` + +See the [`chat-room-effect`](https://github.com/rivet-dev/rivet/tree/main/examples/chat-room-effect) example for a larger project with typed errors and actor-to-actor calls. + +### TypeScript + +A plain RivetKit client can call your Effect actor by name through the same engine. Actor and action names are resolved at runtime, so the client is untyped here: + +```ts client.ts @nocheck +import { createClient } from "rivetkit/client"; + +const client = createClient("http://localhost:6420"); + +const counter = client.Counter.getOrCreate(["my-counter"]); + +const count = await counter.Increment({ amount: 3 }); +console.log("New count:", count); +``` + +See the [JavaScript client documentation](/docs/clients/javascript) for more information. + +### Deploy + +## Feature Support + +The Effect SDK wraps the most common actor features with typed, schema-validated APIs. Everything else is still fully usable through the raw RivetKit context (see [Raw Escape Hatch](#raw-escape-hatch) below), so no feature is off limits, it just isn't typed yet. + +| Feature | Effect-native API | Access | +| --- | --- | --- | +| Actor contract & actions | `Actor.make`, `Action.make` | Typed | +| Persisted state | `State.get` / `set` / `update` / `updateAndGet` / `changes` | Typed | +| Typed client | `Actor.client`, `Client.layer` | Typed | +| Typed errors | `RivetError` | Typed | +| Logging | `RivetLogger` | Typed | +| Sleep request | `Actor.Sleep` | Typed | +| Actor address (`actorId` / `name` / `key`) | `Actor.CurrentAddress` | Typed | +| Registry serve / test / web handler | `Registry` | Typed | +| [Events & broadcast](/docs/actors/events) | Not yet wrapped | `rawRivetkitContext.broadcast(...)` | +| [Schedule](/docs/actors/schedule) | Not yet wrapped | `rawRivetkitContext.schedule.*` | +| [Embedded SQLite](/docs/actors/sqlite) | Not yet wrapped | `rawRivetkitContext.db.execute(...)` | +| [Destroy](/docs/actors/lifecycle) | Not yet wrapped | `rawRivetkitContext.destroy()` | +| Queues, connections, vars, alarms | Not yet wrapped | `rawRivetkitContext.*` | +| Lifecycle hooks (`onSleep` / `onDestroy`) | Not yet wrapped | `rawRivetkitContext.*` | +| Raw HTTP / WebSocket handlers | Not yet wrapped | `rawRivetkitContext.*` | + +### Raw Escape Hatch + +Every wake function receives `rawRivetkitContext`, the underlying RivetKit [actor context](/docs/actors). Reach for it to use any feature that does not have a typed wrapper yet. The typed `state` argument and the raw context point at the same actor, so you can mix both: + +```ts src/actors/counter/live.ts @nocheck +export const CounterLive = Counter.toLayer( + Effect.fnUntraced(function* ({ rawRivetkitContext, state }) { + return Counter.of({ + Increment: Effect.fnUntraced(function* ({ payload }) { + // Typed state wrapper + const next = yield* State.updateAndGet(state, (current) => ({ + count: current.count + payload.amount, + })).pipe(Effect.orDie); + + // Untyped features run through the raw context + rawRivetkitContext.broadcast("newCount", next.count); + rawRivetkitContext.schedule.after(1_000, "tick", {}); + + return next.count; + }), + }); + }), + { + state: { + schema: Schema.Struct({ count: Schema.Number }), + initialValue: () => ({ count: 0 }), + }, + name: "Counter", + }, +); +``` + +Calls through `rawRivetkitContext` are not validated by `effect/Schema` and their payloads are typed as they are in the base RivetKit API. + +## Next Steps + + +- [Actions](/docs/actors/actions) — Define the RPC surface clients call on your actor. + + +- [State](/docs/actors/state) — Persist and load actor state across sleeps and restarts. + + +- [Events](/docs/actors/events) — Broadcast realtime updates to connected clients. + +_Source doc path: /docs/actors/quickstart/effect_ diff --git a/.agents/skills/live-cursors/reference/actors/quickstart/next-js.md b/.agents/skills/live-cursors/reference/actors/quickstart/next-js.md new file mode 100644 index 0000000..f621606 --- /dev/null +++ b/.agents/skills/live-cursors/reference/actors/quickstart/next-js.md @@ -0,0 +1,117 @@ +# Next.js Quickstart + +> Source: `src/content/docs/actors/quickstart/next-js.mdx` +> Canonical URL: https://rivet.dev/docs/actors/quickstart/next-js +> Description: Get started with Rivet Actors in Next.js + +--- +### Add Rivet Skill to Coding Agent (Optional) + +If you're using an AI coding assistant (like Claude Code, Cursor, Windsurf, etc.), add Rivet skills for enhanced development assistance: + +```sh +npx skills add rivet-dev/skills +``` + +### Create a Next.js App + +```sh +npx create-next-app@latest my-app +cd my-app +``` + +### Install RivetKit + +### Create an Actor + +Create a file at `src/rivet/registry.ts` with a simple counter actor: + +### Setup Rivet API route + +Create a file at `src/app/api/rivet/[...all]/route.ts` to setup the API routes: + +```ts src/app/api/rivet/[...all]/route.ts @nocheck +import { toNextHandler } from "@rivetkit/next-js"; +import { registry } from "@/rivet/registry"; + +export const maxDuration = 300; + +export const { GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS } = toNextHandler(registry); +``` + +### Use the Actor in a component + +Create a Counter component and add it to your page: + +```tsx src/components/Counter.tsx @nocheck +"use client"; + +import { createRivetKit } from "@rivetkit/next-js/client"; +import type { registry } from "@/rivet/registry"; +import { useState } from "react"; + +export const { useActor } = createRivetKit({ + endpoint: + process.env.NEXT_PUBLIC_RIVET_ENDPOINT ?? "http://localhost:3000/api/rivet", + namespace: process.env.NEXT_PUBLIC_RIVET_NAMESPACE, + token: process.env.NEXT_PUBLIC_RIVET_TOKEN, +}); + +export function Counter() { + const [count, setCount] = useState(0); + + // Get or create a counter actor for the key "my-counter" + const counter = useActor({ + name: "counter", + key: ["my-counter"] + }); + + // Listen to realtime events + counter.useEvent("newCount", (x: number) => setCount(x)); + + const increment = async () => { + // Call actions + await counter.connection?.increment(1); + }; + + return ( +
+

Count: {count}

+ +
+ ); +} +``` + +```tsx src/app/page.tsx @nocheck +import { Counter } from "@/components/Counter"; + +export default function Home() { + return ( +
+

My App

+ +
+ ); +} +``` + +For information about the Next.js client API, see the [React Client API Reference](/docs/clients/react). + +### Run Locally + +Start the Next.js dev server. Rivet auto-starts a local engine alongside it: + +```sh +npm run dev +``` + +Open `http://localhost:3000` in your browser to use the app. + +Visit [http://localhost:6420](http://localhost:6420) in your browser (or point your AI agent at it) to open the Rivet developer tools and inspect your actors live. + +### Deploy to Vercel + +See the [Vercel deployment guide](/docs/deploy/vercel) for detailed instructions on deploying your Rivet app to Vercel. + +_Source doc path: /docs/actors/quickstart/next-js_ diff --git a/.agents/skills/live-cursors/reference/actors/quickstart/react.md b/.agents/skills/live-cursors/reference/actors/quickstart/react.md new file mode 100644 index 0000000..8722fed --- /dev/null +++ b/.agents/skills/live-cursors/reference/actors/quickstart/react.md @@ -0,0 +1,82 @@ +# React Quickstart + +> Source: `src/content/docs/actors/quickstart/react.mdx` +> Canonical URL: https://rivet.dev/docs/actors/quickstart/react +> Description: Build realtime React applications with Rivet Actors + +--- +## Steps + +### Add Rivet Skill to Coding Agent (Optional) + +If you're using an AI coding assistant (like Claude Code, Cursor, Windsurf, etc.), add Rivet skills for enhanced development assistance: + +```sh +npx skills add rivet-dev/skills +``` + +### Install Dependencies + +```sh +npm install rivetkit @rivetkit/react +``` + +### Create Backend Actor and Start Server + +Create your actor registry on the backend and start the server: + +### Create React Frontend + +Set up your React application: + +For detailed information about the React client API, see the [React Client API Reference](/docs/clients/react). + +### Setup Vite Configuration + +Configure Vite for development: + +```ts vite.config.ts @nocheck +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' + +export default defineConfig({ + plugins: [react()], + server: { + port: 5173, + }, +}) +``` + +### Run Your Application + +Start both the backend and frontend: + +**Terminal 1**: Start the backend + +```sh Node.js +npx tsx --watch backend/index.ts +``` + +```sh Bun +bun --watch backend/index.ts +``` + +```sh Deno +deno run --allow-net --allow-read --allow-env --watch backend/index.ts +``` + +**Terminal 2**: Start the frontend + +```sh Frontend +npx vite +``` + +Open `http://localhost:5173` in your browser. Try opening multiple tabs to see realtime sync in action. + +Visit [http://localhost:6420](http://localhost:6420) in your browser (or point your AI agent at it) to open the Rivet developer tools and inspect your actors live. + +### Deploy + +## Configuration Options + +_Source doc path: /docs/actors/quickstart/react_ diff --git a/.agents/skills/live-cursors/reference/actors/quickstart/rust.md b/.agents/skills/live-cursors/reference/actors/quickstart/rust.md new file mode 100644 index 0000000..1e55471 --- /dev/null +++ b/.agents/skills/live-cursors/reference/actors/quickstart/rust.md @@ -0,0 +1,249 @@ +# Rust Quickstart (Beta) + +> Source: `src/content/docs/actors/quickstart/rust.mdx` +> Canonical URL: https://rivet.dev/docs/actors/quickstart/rust +> Description: Build a Rivet Actor in Rust + +--- +Rust support is in beta. The supported public Rust API is `rivetkit` and `rivetkit-client`; lower-level crates are internal implementation details and do not carry a stability guarantee. See the full API reference on [docs.rs/rivetkit](https://docs.rs/rivetkit), or the runnable [`hello-world-rust`](https://github.com/rivet-dev/rivet/tree/main/examples/hello-world-rust) example. + +## Steps + +### Add Rivet + +Add the `rivetkit` crate and its companions: + +```sh +cargo add rivetkit anyhow async-trait +cargo add serde --features derive +cargo add tokio --features full +``` + +### Define Your Actor + +Put the actor in `src/lib.rs` so both your server and your client can share the same types. An actor is a type that implements `Actor`, plus one `Handles` implementation for each action. Persisted state lives in `type State`; ephemeral runtime state is just fields on your actor struct. + +```rust src/lib.rs +use std::{future::Future, pin::Pin, sync::Arc}; + +use async_trait::async_trait; +use rivetkit::prelude::*; +use serde::{Deserialize, Serialize}; + +type BoxFuture = Pin> + Send>>; + +pub struct Counter; + +#[derive(Default, Serialize, Deserialize)] +pub struct CounterState { + pub count: i64, +} + +#[derive(Serialize, Deserialize)] +pub struct Increment { + pub amount: i64, +} + +impl Action for Increment { + type Output = i64; + + const NAME: &'static str = "increment"; +} + +#[derive(Serialize, Deserialize)] +pub struct NewCount { + pub count: i64, +} + +impl Event for NewCount { + const NAME: &'static str = "newCount"; +} + +#[async_trait] +impl Actor for Counter { + type State = CounterState; + type Input = (); + type Actions = (Increment,); + type Events = (NewCount,); + type Queue = (); + type ConnParams = (); + type ConnState = (); + type Action = action::Raw; + + async fn create_state(_ctx: &Ctx, _input: Self::Input) -> Result { + Ok(CounterState::default()) + } + + async fn create(_ctx: &Ctx) -> Result { + Ok(Self) + } +} + +impl Handles for Counter { + type Future = BoxFuture; + + fn handle(self: Arc, ctx: Ctx, action: Increment) -> Self::Future { + Box::pin(async move { + let count = { + let mut state = ctx.state_mut(); + state.count += action.amount; + state.count + }; + ctx.emit(NewCount { count })?; + Ok(count) + }) + } +} + +pub fn registry() -> Registry { + let mut registry = Registry::new(); + registry.register_actor::("counter"); + registry +} +``` + +### Serve The Registry + +Your `src/main.rs` just starts the registry from the library: + +```rust src/main.rs +#[tokio::main] +async fn main() -> anyhow::Result<()> { + counter::registry().start().await +} +``` + +Replace `counter` with your crate name (the package `name` in `Cargo.toml`, with dashes as underscores). + +### Run The Server + +The Rust runtime connects to the Rivet Engine. Setting `RIVETKIT_ENGINE_AUTO_DOWNLOAD=1` lets the runtime download and cache a matching engine binary the first time you run, so there is nothing else to install: + +```sh +RIVETKIT_ENGINE_AUTO_DOWNLOAD=1 cargo run +``` + +Your server now connects to the Rivet Engine on `http://localhost:6420`. Clients connect directly to the engine on this port. + +Visit [http://localhost:6420](http://localhost:6420) in your browser (or point your AI agent at it) to open the Rivet developer tools and inspect your actors live. + +Already have an engine binary? Set `RIVET_ENGINE_BINARY_PATH=/path/to/rivet-engine` to point at it instead. If you are working inside the [Rivet monorepo](https://github.com/rivet-dev/rivet), a local `cargo build -p rivet-engine` is discovered automatically from `target/debug`. + +### Connect To The Rivet Actor + +This code can run either in your frontend or within your backend: + +### Rust + +Add a `src/bin/client.rs` that imports the same actor types from your library. There is no need to redefine the actor on the client. + +```rust src/bin/client.rs +use counter::{Counter, Increment, NewCount}; +use rivetkit::{ + client::{Client, ClientConfig}, + prelude::*, + TypedClientExt, +}; + +#[tokio::main] +async fn main() -> Result<()> { + let client = Client::new(ClientConfig::new("http://localhost:6420").namespace("default")); + + let counter = client.get_or_create_typed_default::("counter", ["my-counter"])?; + let count = counter.send(Increment { amount: 3 }).await?; + println!("New count: {count}"); + + let connection = counter.connect(); + connection + .on::(|event| println!("Count changed: {}", event.count)) + .await; + connection.send(Increment { amount: 1 }).await?; + + Ok(()) +} +``` + +With the server still running, start the client in another terminal: + +```sh +cargo run --bin client +``` + +See the [`hello-world-rust`](https://github.com/rivet-dev/rivet/tree/main/examples/hello-world-rust) example for a complete runnable counter. + +### TypeScript + +A TypeScript client can call your Rust actor by name through the same engine. Actor and action names are resolved at runtime, so the client is untyped here: + +```ts client.ts @nocheck +import { createClient } from "rivetkit/client"; + +const client = createClient("http://localhost:6420"); + +const counter = client.counter.getOrCreate(["my-counter"]); + +const counterConnection = counter.connect(); +counterConnection.on("newCount", (event) => { + console.log("Event count:", event.count); +}); + +const count = await counterConnection.increment(3); +console.log("New count:", count); + +await counterConnection.increment(1); +``` + +See the [JavaScript client documentation](/docs/clients/javascript) for more information. + +### React + +```tsx Counter.tsx @nocheck +import { createRivetKit } from "@rivetkit/react"; +import { useState } from "react"; + +const { useActor } = createRivetKit("http://localhost:6420"); + +function Counter() { + const [count, setCount] = useState(0); + + const counter = useActor({ + name: "counter", + key: ["my-counter"], + }); + + const increment = async () => { + await counter.connection?.increment(1); + }; + + counter.useEvent("newCount", (event) => { + setCount(event.count); + }); + + return ( +
+

Count: {count}

+ +
+ ); +} +``` + +See the [React documentation](/docs/clients/react) for more information. + +### Deploy + +## Next Steps + + +- [API Reference](https://docs.rs/rivetkit) — Full `rivetkit` crate documentation on docs.rs. + + +- [Actions](/docs/actors/actions) — Define the RPC surface clients call on your actor. + + +- [State](/docs/actors/state) — Persist and load actor state across sleeps and restarts. + + +- [Events](/docs/actors/events) — Broadcast realtime updates to connected clients. + +_Source doc path: /docs/actors/quickstart/rust_ diff --git a/.agents/skills/live-cursors/reference/actors/quickstart/supabase.md b/.agents/skills/live-cursors/reference/actors/quickstart/supabase.md new file mode 100644 index 0000000..20b59d0 --- /dev/null +++ b/.agents/skills/live-cursors/reference/actors/quickstart/supabase.md @@ -0,0 +1,109 @@ +# Supabase Functions Quickstart + +> Source: `src/content/docs/actors/quickstart/supabase.mdx` +> Canonical URL: https://rivet.dev/docs/actors/quickstart/supabase +> Description: Set up a Rivet project locally targeting Supabase Edge Functions. + +--- +Set up a Rivet project locally that runs on Supabase Edge Functions. The `@rivetkit/supabase` package wires the WebAssembly runtime for you. + +Prefer to start from a complete project? See the runnable [`hello-world-supabase-functions`](https://github.com/rivet-dev/rivet/tree/main/examples/hello-world-supabase-functions) example. + +## Steps + +### Prerequisites + +- [Node.js](https://nodejs.org/) +- [Supabase CLI](https://supabase.com/docs/guides/cli) +- Docker, for Supabase's local Edge Runtime + +The CLI runs the local Rivet engine as a bundled native binary, so Docker is only needed for Supabase itself. A Supabase project is only needed to deploy. + +### Create the Function + +```sh +npx supabase functions new rivet +``` + +Add the packages used by the function: + +```sh +npm install rivetkit @rivetkit/supabase +``` + +### Configure the Function + +Call `serve` from `@rivetkit/supabase`. It loads the WebAssembly runtime and serves the Rivet handler. + +```ts supabase/functions/rivet/index.ts @nocheck +import { actor } from "rivetkit"; +import { serve, setup } from "@rivetkit/supabase"; + +const counter = actor({ + state: { count: 0 }, + actions: { + increment: (c, amount = 1) => { + c.state.count += amount; + return c.state.count; + }, + }, +}); + +// `setup` returns a typed registry, so a client can type itself with +// `typeof registry`. +export const registry = setup({ use: { counter } }); + +await serve(registry); +``` + +Add a `deno.json` next to the function so the deploy bundles only the WebAssembly runtime. It points `rivetkit` at the pre-bundled `@rivetkit/supabase`, keeping the deploy small. Without it, the deploy pulls Rivet's native engine and 413s. + +```json supabase/functions/rivet/deno.json +{ + "imports": { + "rivetkit": "npm:@rivetkit/supabase", + "@rivetkit/supabase": "npm:@rivetkit/supabase" + } +} +``` + +Your function code keeps importing from `rivetkit` as usual. The import map only changes how Deno resolves it at bundle time. + +### Run Locally + +Start Rivet. The CLI runs the local engine, spawns `supabase functions serve` for you, and populates the connection values: + +```sh +npx @rivetkit/cli dev --provider supabase +``` + +Visit [http://localhost:6420](http://localhost:6420) in your browser (or point your AI agent at it) to open the Rivet developer tools and inspect your actors live. + +### Call the Actor + +Connect to your actor from a client. This connects directly to the local engine on `http://localhost:6420`: + +```ts client.ts @nocheck +import { createClient } from "rivetkit/client"; +import type { registry } from "./supabase/functions/rivet/index"; + +const client = createClient("http://localhost:6420"); + +const counter = client.counter.getOrCreate(["my-counter"]); +const count = await counter.increment(3); +console.log("New count:", count); +``` + +See the [JavaScript client documentation](/docs/clients/javascript) for more information. + +### Deploy + +Ready to ship? See [Deploying to Supabase Functions](/docs/deploy/supabase). + +## Related + +- [Quickstart](/docs/actors/quickstart) +- [Deploying to Supabase Functions](/docs/deploy/supabase) +- [SQLite](/docs/actors/sqlite) + +_Source doc path: /docs/actors/quickstart/supabase_ diff --git a/.agents/skills/live-cursors/reference/actors/request-handler.md b/.agents/skills/live-cursors/reference/actors/request-handler.md new file mode 100644 index 0000000..0536b9c --- /dev/null +++ b/.agents/skills/live-cursors/reference/actors/request-handler.md @@ -0,0 +1,79 @@ +# Low-Level HTTP Request Handler + +> Source: `src/content/docs/actors/request-handler.mdx` +> Canonical URL: https://rivet.dev/docs/actors/request-handler +> Description: Actors can handle HTTP requests through the `onRequest` handler. + +--- +For most use cases, [actions](/docs/actors/actions) provide high-level API powered by HTTP that's easier to work with than low-level HTTP. However, low-level handlers are required when implementing custom use cases or integrating external libraries that need direct access to the underlying HTTP `Request`/`Response` objects or WebSocket connections. + +## Handling HTTP Requests + +The `onRequest` handler processes HTTP requests sent to your actor. It receives the actor context and a standard `Request` object and returns a `Response` object. + +### Raw HTTP + +### Hono + +See also the [raw fetch handler example](https://github.com/rivet-dev/rivet/tree/main/examples/raw-fetch-handler). + +## Sending Requests To Actors + +### Via RivetKit Client + +Use the `.fetch()` method on an actor handle to send HTTP requests to the actor's `onRequest` handler. This can be executed from either your frontend or backend. + +### Via getGatewayUrl + +Use `.getGatewayUrl()` to get the raw gateway URL for the actor. This is useful when you need to use the URL with external tools or custom HTTP clients. + +### Via HTTP API + +This handler can be accessed with raw HTTP using `https://api.rivet.dev/gateway/{actorId}/request/{...path}`. + +For example, to call `POST /increment` on the counter actor above: + +```bash +curl -X POST "https://api.rivet.dev/gateway/{actorId}/request/increment" \ + -H "x-rivet-token: {token}" +``` + +The request is routed to the actor's `onRequest` handler where: + +- `request.method` is `"POST"` +- `request.url` ends with `/increment` (the path after `/request/`) +- Headers, body, and other request properties are passed through unchanged + +See the [HTTP API reference](/docs/actors/http-api) for more information on HTTP routing and authentication. + +### Via Proxying Requests + +You can proxy HTTP requests from your own server to actor handlers using the RivetKit client. This is useful when you need to add custom authentication, rate limiting, or request transformation before forwarding to actors. + +## Connection & Lifecycle Hooks + +`onRequest` will trigger the `onBeforeConnect`, `onConnect`, and `onDisconnect` hooks. Read more about [lifecycle hooks](/docs/actors/lifecycle). + +Requests in flight will be listed in `c.conns`. Read more about [connections](/docs/actors/connections). + +## WinterTC Compliance + +The `onRequest` handler is WinterTC compliant and will work with existing libraries using the standard `Request` and `Response` types. + +## Limitations + +- Does not support streaming responses & server-sent events at the moment. See the [tracking issue](https://github.com/rivet-dev/rivet/issues/3529). +- `OPTIONS` requests currently are handled by Rivet and are not passed to `onRequest` + +## Advanced + +### Skip Ready Wait + +Requests are normally held at the gateway until the actor is ready. Pass `skipReadyWait: true` on `handle.fetch()` to deliver immediately, including while the actor is still starting or in the [sleep grace period](/docs/actors/lifecycle#shutdown-sequence). See [Skip Ready Wait](/docs/clients/javascript#skip-ready-wait) for details. + +## API Reference + +- [`RequestContext`](/typedoc/interfaces/rivetkit.mod.RequestContext.html) - Context for HTTP request handlers +- [`ActorDefinition`](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html) - Interface for defining request handlers + +_Source doc path: /docs/actors/request-handler_ diff --git a/.agents/skills/live-cursors/reference/actors/scaling.md b/.agents/skills/live-cursors/reference/actors/scaling.md new file mode 100644 index 0000000..b597115 --- /dev/null +++ b/.agents/skills/live-cursors/reference/actors/scaling.md @@ -0,0 +1,10 @@ +# Scaling & Concurrency + +> Source: `src/content/docs/actors/scaling.mdx` +> Canonical URL: https://rivet.dev/docs/actors/scaling +> Description: This page has moved to [design patterns](/docs/actors/design-patterns). + +--- + + +_Source doc path: /docs/actors/scaling_ diff --git a/.agents/skills/live-cursors/reference/actors/schedule.md b/.agents/skills/live-cursors/reference/actors/schedule.md new file mode 100644 index 0000000..fc9c631 --- /dev/null +++ b/.agents/skills/live-cursors/reference/actors/schedule.md @@ -0,0 +1,190 @@ +# Schedule & Cron + +> Source: `src/content/docs/actors/schedule.mdx` +> Canonical URL: https://rivet.dev/docs/actors/schedule +> Description: Run durable one-shot and recurring actor actions on a schedule. + +--- +Rivet Actor schedules invoke actions on the same actor and survive sleep, restarts, upgrades, and crashes. Use one-shot schedules for delayed work, cron jobs for calendar-based work, and fixed intervals for frequent jobs. + +## Quick start + +```ts After +const reminders = actor({ + onCreate: async (c) => { + await c.schedule.after(30_000, "sendReminder", "reminder-123"); + }, + actions: { + sendReminder: (_c, reminderId: string) => { + console.log("Sending reminder", reminderId); + }, + }, +}); +``` + +```ts Cron +const reports = actor({ + onCreate: async (c) => { + await c.cron.set({ + name: "daily-report", + expression: "0 9 * * *", + action: "runReport", + args: ["sales"], // Optional. + timezone: "America/Los_Angeles", // Optional; defaults to UTC. + maxHistory: 100, // Optional; defaults to 100. Set to 0 to disable. + }); + }, + actions: { + runReport: (_c, report: string) => { + console.log("Running report", report); + }, + }, +}); +``` + +```ts Every +const cache = actor({ + onCreate: async (c) => { + await c.cron.every({ + name: "refresh-cache", + interval: 60_000, // Minimum 5 seconds. + action: "refreshCache", + args: ["products"], // Optional. + maxHistory: 100, // Optional; defaults to 100. Set to 0 to disable. + }); + }, + actions: { + refreshCache: (_c, cache: string) => { + console.log("Refreshing cache", cache); + }, + }, +}); +``` + +## Sleep Between Runs + +[Actors do not need to stay awake](/docs/actors/lifecycle#sleeping) while waiting for a scheduled action. Once an actor becomes idle, it can sleep normally. Sleeping does not pause or remove its schedules. Rivet wakes the actor when the next action is due, whether that is seconds or arbitrarily far in the future. + +Recurring Cron and fixed-interval schedules continue until they are updated or deleted. One-shot schedules remain pending until their target time. + +## One-shot schedules + +### Run after a delay + +Runs once after the given delay in milliseconds and returns the generated schedule ID. + +```ts +const id = await c.schedule.after(5_000, "sendReminder", reminderId); +``` + +### Run at a specific time + +Runs once at a Unix timestamp in milliseconds. + +```ts +const id = await c.schedule.at(Date.parse("2026-08-01T09:00:00Z"), "openSale", saleId); +``` + +### Inspect and cancel schedules + +```ts +const schedule = await c.schedule.get(id); +const pending = await c.schedule.list(); +const cancelled = await c.schedule.cancel(id); +``` + +## Recurring jobs + +### Run on a cron schedule + +Cron jobs use standard five-field expressions. Names are unique per actor, and reusing a name updates the existing job. The timezone defaults to UTC. + +```ts +await c.cron.set({ + name: "daily-report", + expression: "0 9 * * *", + action: "runReport", + args: ["sales"], // Optional. + timezone: "America/Los_Angeles", // Optional; defaults to UTC. + maxHistory: 100, // Optional; defaults to 100. Set to 0 to disable. +}); +``` + +### Run at a fixed interval + +Fixed-interval jobs keep their cadence anchored to scheduled deadlines, so action runtime does not introduce drift. Names are unique per actor, and reusing a name updates the existing job. The minimum interval is 5 seconds. + +```ts +await c.cron.every({ + name: "presence-sweep", + interval: 15_000, // Minimum 5 seconds. + action: "sweepPresence", + args: [], // Optional. + maxHistory: 25, // Optional; defaults to 100. Set to 0 to disable. +}); +``` + +### Update and delete jobs + +Submitting a recurring job with an existing name updates it. Changing its cadence calculates a new next run, while changing only its action, arguments, or history settings preserves the existing cadence. + +```ts +const job = await c.cron.get("daily-report"); +const jobs = await c.cron.list(); +const deleted = await c.cron.delete("daily-report"); +``` + +### View run history + +```ts +const recent = await c.cron.history("daily-report", { + limit: 20, // Optional. +}); +``` + +Each entry's `result` is `running`, `ok`, `error`, or `skipped`. Jobs retain 100 entries by default. Set maximum history to zero to disable and clear history, or choose up to 1,000 entries. An actor retains at most 10,000 history entries across all jobs. + +## Run information + +When a schedule invokes an action, it appends a ScheduledFireInfo object after the configured action arguments. + +```ts +import type { ScheduledFireInfo } from "rivetkit"; + +const reports = actor({ + onCreate: async (c) => { + await c.cron.set({ + name: "daily-report", + expression: "0 9 * * *", + action: "runReport", + args: ["sales"], // Optional. + }); + }, + actions: { + runReport: ( + _c, + report: string, + fire: ScheduledFireInfo, + ) => { + console.log( + report, + fire.kind, + fire.id, + fire.name, + fire.scheduledAt, + fire.firedAt, + ); + }, + }, +}); +``` + +## Execution behavior + +- **Durability**: Schedules survive actor sleep, restarts, upgrades, and crashes. See [Sleep Between Runs](#sleep-between-runs). +- **Failures**: Failed runs are not retried immediately. A failed recurring run continues at the next normal cadence; a failed one-shot is complete after its attempted invocation. Cron failures are available in [run history](#view-run-history), so you can build a custom retry mechanism. +- **Idempotency**: Scheduled actions should be idempotent so manually retrying a failed or interrupted run is safe. +- **Overlaps**: Overlapping recurring runs are skipped. +- **Missing actions**: Recurring jobs are deleted when their action no longer exists. + +_Source doc path: /docs/actors/schedule_ diff --git a/.agents/skills/live-cursors/reference/actors/sharing-and-joining-state.md b/.agents/skills/live-cursors/reference/actors/sharing-and-joining-state.md new file mode 100644 index 0000000..1fe0683 --- /dev/null +++ b/.agents/skills/live-cursors/reference/actors/sharing-and-joining-state.md @@ -0,0 +1,10 @@ +# Sharing and Joining State + +> Source: `src/content/docs/actors/sharing-and-joining-state.mdx` +> Canonical URL: https://rivet.dev/docs/actors/sharing-and-joining-state +> Description: This page has moved to [design patterns](/docs/actors/design-patterns). + +--- + + +_Source doc path: /docs/actors/sharing-and-joining-state_ diff --git a/.agents/skills/live-cursors/reference/actors/sqlite-drizzle.md b/.agents/skills/live-cursors/reference/actors/sqlite-drizzle.md new file mode 100644 index 0000000..edc157a --- /dev/null +++ b/.agents/skills/live-cursors/reference/actors/sqlite-drizzle.md @@ -0,0 +1,248 @@ +# SQLite + Drizzle + +> Source: `src/content/docs/actors/sqlite-drizzle.mdx` +> Canonical URL: https://rivet.dev/docs/actors/sqlite-drizzle +> Description: Use Drizzle ORM with embedded SQLite in Rivet Actors. + +--- +Use Drizzle when you want typed schema, typed queries, and generated migrations on top of actor-local SQLite. + +For a high-level overview of where to store actor data, see [State & Storage](/docs/actors/state). + +## What is Drizzle good for? + +- **Typed schema**: define tables in TypeScript and get typed query results. +- **Typed query builder**: write SQL-like queries with autocompletion. +- **Migration workflow**: generate SQL migration files from schema changes. +- **Raw SQL escape hatch**: use `c.db.execute(...)` for direct SQLite when needed. + +## Project structure + +Use one folder per actor database: + +```txt +src/ + actors/ + todo-list/ + index.ts + schema.ts + drizzle.config.ts + drizzle/ + 0000_init.sql + migrations.js + migrations.d.ts + meta/ + _journal.json +``` + +- `index.ts` is the actor implementation. +- `drizzle/` holds the SQL migrations (`*.sql`) and `meta/_journal.json` generated by `drizzle-kit`. +- `migrations.js` is a small RivetKit glue file you maintain by hand. It imports the journal and each `*.sql` file and exports a `{ journal, migrations }` object keyed by migration (for example `m0000`). Add a new entry here whenever `db:generate` produces a new migration. +- Commit the generated migration files and `migrations.js` to source control. + +## Basic setup + +```json package.json +{ + "scripts": { + "db:generate": "find src/actors -name drizzle.config.ts -exec drizzle-kit generate --config {} \\;" + }, + "dependencies": { + "rivetkit": "*", + "drizzle-orm": "^0.44.2" + }, + "devDependencies": { + "drizzle-kit": "^0.31.2" + } +} +``` + +```ts vite.config.ts @nocheck +import { defineConfig, type Plugin } from "vite"; +import { readFileSync } from "node:fs"; + +function sqlRawPlugin(): Plugin { + return { + name: "sql-raw", + transform(_code, id) { + if (id.endsWith(".sql")) { + const content = readFileSync(id, "utf-8"); + return { code: `export default ${JSON.stringify(content)};` }; + } + }, + }; +} + +export default defineConfig({ + plugins: [sqlRawPlugin()], +}); +``` + +```ts drizzle.config.ts @nocheck +import { defineConfig } from "rivetkit/db/drizzle"; + +export default defineConfig({ + schema: "./src/actors/todo-list/schema.ts", + out: "./src/actors/todo-list/drizzle", +}); +``` + +```sql 0000_init.sql +CREATE TABLE `todos` ( + `id` integer PRIMARY KEY AUTOINCREMENT NOT NULL, + `title` text NOT NULL, + `created_at` integer NOT NULL +); +``` + +```json _journal.json +{ + "version": "7", + "dialect": "sqlite", + "entries": [ + { + "idx": 0, + "version": "7", + "when": 1735689600000, + "tag": "0000_init", + "breakpoints": true + } + ] +} +``` + +```ts migrations.js @nocheck +import journal from "./meta/_journal.json"; +import m0000 from "./0000_init.sql"; + +export default { + journal, + migrations: { + m0000, + }, +}; +``` + +```ts index.ts @nocheck +import { actor } from "rivetkit"; +import { db } from "rivetkit/db/drizzle"; +import migrations from "./drizzle/migrations.js"; +import { schema, todos } from "./schema.ts"; + +export const todoList = actor({ + db: db({ schema, migrations }), + actions: { + addTodo: async (c, title: string) => { + const rows = await c.db + .insert(todos) + .values({ title, createdAt: Date.now() }) + .returning(); + return rows[0]; + }, + getTodos: async (c) => { + return await c.db.select().from(todos).orderBy(todos.id); + }, + getTodoCount: async (c) => { + const rows = (await c.db.execute( + "SELECT COUNT(*) AS count FROM todos", + )) as { count: number }[]; + return rows[0]?.count ?? 0; + }, + }, +}); +``` + +```ts index.ts @nocheck +import { setup } from "rivetkit"; +import { todoList } from "./todo-list/index.ts"; + +export const registry = setup({ use: { todoList } }); +registry.start(); +``` + +```ts client.ts @nocheck +import { createClient } from "rivetkit/client"; +import type { registry } from "./index"; + +const client = createClient("http://localhost:6420"); +const todoList = client.todoList.getOrCreate(["main"]); + +await todoList.addTodo("Write Drizzle docs"); + +const todos = await todoList.getTodos(); +const count = await todoList.getTodoCount(); + +console.log(todos, count); +``` + +## Queries + +### Query builder + +Use Drizzle's typed query APIs for most reads and writes. + +```ts @nocheck +import { eq } from "drizzle-orm"; + +await c.db.insert(todos).values({ title, createdAt: Date.now() }); + +const rows = await c.db + .select() + .from(todos) + .where(eq(todos.title, title)); +``` + +### Raw SQL + +`rivetkit/db/drizzle` also exposes raw SQLite access through `c.db.execute(...)`. + +```ts @nocheck +await c.db.execute( + "CREATE INDEX IF NOT EXISTS idx_todos_created_at ON todos(created_at)", +); +``` + +## Queues + +Use queues for ordered mutations and keep actions read-only. Import `queue` alongside `actor` from `rivetkit`. + +```ts @nocheck +import { actor, queue } from "rivetkit"; + +// ... + +queues: { + addTodo: queue<{ title: string }>(), +}, +run: async (c) => { + for await (const message of c.queue.iter()) { + if (message.name === "addTodo") { + await c.db.insert(todos).values({ + title: message.body.title, + createdAt: Date.now(), + }); + } + } +}, +actions: { + getTodos: async (c) => await c.db.select().from(todos), +}, +``` + +## Recommendations + +- Prefer Drizzle query APIs for app code and use raw SQL for advanced SQLite features. +- Keep one `drizzle.config.ts` per actor folder. +- Re-run `db:generate` after schema changes and commit generated migration files. +- Use queues for writes and actions for reads. +- Keep related writes in one action or queue message to reduce interleaved query risk. + +## Read more + +- [Drizzle SQLite quickstart](https://orm.drizzle.team/docs/get-started-sqlite) +- [Drizzle `drizzle-kit generate`](https://orm.drizzle.team/docs/drizzle-kit-generate) +- [Drizzle + Cloudflare D1](https://orm.drizzle.team/docs/deploy-cloudflare-d1) +- [Drizzle + Cloudflare Durable Objects](https://orm.drizzle.team/docs/deploy-cloudflare-do) +- [Cloudflare Durable Objects SQLite storage](https://developers.cloudflare.com/durable-objects/api/sqlite-storage-api/) + +_Source doc path: /docs/actors/sqlite-drizzle_ diff --git a/.agents/skills/live-cursors/reference/actors/sqlite.md b/.agents/skills/live-cursors/reference/actors/sqlite.md new file mode 100644 index 0000000..cc42bf3 --- /dev/null +++ b/.agents/skills/live-cursors/reference/actors/sqlite.md @@ -0,0 +1,127 @@ +# SQLite + +> Source: `src/content/docs/actors/sqlite.mdx` +> Canonical URL: https://rivet.dev/docs/actors/sqlite +> Description: Use embedded SQLite in Rivet Actors with raw SQL queries. + +--- +For a high-level overview of where to store actor data, including when to use `c.state` versus SQLite, see [State & Storage](/docs/actors/state). + +## What is SQLite? + +- **Database per actor**: each actor instance has its own SQLite database, scoped to that actor. +- **High performance**: Rivet Actors keep compute and storage together, so queries avoid network round trips to an external database. +- **Larger-than-memory storage**: SQLite stores data on disk, so you can work with datasets that do not fit in actor memory. +- **Embedded relational database**: use tables, indexes, and SQL queries directly inside actor logic. + +### SQLite features + +- **Indexes**: speed up lookups on frequently queried fields. +- **Search and filtering**: use `WHERE`, `LIKE`, and `ORDER BY` instead of manual in-memory loops. +- **Relationships**: use multiple tables and `JOIN` queries for connected data. +- **Constraints**: use primary keys, unique constraints, and foreign keys for data integrity. +- **Transactions**: apply multiple writes atomically when changes must stay consistent. + +## Raw SQL vs ORM (Drizzle) + +Rivet supports both raw SQL and [Drizzle](https://orm.drizzle.team/) for actor-local SQLite. + +Use **raw SQL** when you want direct query control and minimal abstraction. + +```ts @nocheck +await c.db.execute("INSERT INTO todos (title) VALUES (?)", title); +const rows = await c.db.execute("SELECT id, title FROM todos ORDER BY id DESC"); +``` + +Use **Drizzle** when you want typed schema and typed query APIs. + +```ts @nocheck +await c.vars.drizzle.insert(todos).values({ title }); +const rows = await c.vars.drizzle.select().from(todos).orderBy(desc(todos.id)); +``` + +You can mix both in the same actor. + +For Drizzle setup, see [SQLite + Drizzle](/docs/actors/sqlite-drizzle). + +## Basic setup + +Define `db: db({ onMigrate })` on your actor, create your schema in `onMigrate`, and execute SQL with `c.db.execute(...)`. + +RivetKit wraps `onMigrate` in a SQLite savepoint, so migration steps are atomic. If `onMigrate` throws, all SQL run by that hook is rolled back before the actor starts. + +## Queries + +`c.db.execute(...)` returns an array of row objects for `SELECT` queries. + +```ts @nocheck +const rows = await c.db.execute( + "SELECT id, title FROM todos WHERE title LIKE ?", + `%${query}%`, +); +``` + +### Parameterized queries + +Use `?` placeholders for dynamic values and pass parameters in order after the SQL string. + +```ts @nocheck +await c.db.execute("INSERT INTO todos (title) VALUES (?)", title); +``` + +You can also use named SQLite bindings by passing a single properties object. + +```ts @nocheck +const rows = await c.db.execute( + "SELECT id, title FROM todos WHERE title = :title", + { title: "Write SQLite docs" }, +); +``` + +### Transactions + +Use transactions when multiple writes must succeed or fail together. + +```ts @nocheck +await c.db.transaction(async (tx) => { + await tx.execute("INSERT INTO todos (title) VALUES (?)", title); + await tx.execute( + "INSERT INTO comments (todo_id, body) VALUES (last_insert_rowid(), ?)", + body, + ); +}); +``` + +RivetKit commits when the callback resolves and rolls back when it throws. Other transactions and ordinary actor SQL queue in FIFO order until the callback finishes. Transactions have a 60-second safety timeout by default; increase it for legitimately long work with `{ timeout: 120_000 }`. + +Always use the callback's `tx` value inside the transaction. Starting another transaction or using the outer `c.db` from the callback waits behind the active transaction and eventually reaches the safety timeout; the resulting error points to this possible deadlock. + +Manual `BEGIN`/`COMMIT` calls remain supported for compatibility, but cannot protect against interleaving callers. RivetKit logs a warning recommending `db.transaction()`. Set `warnOnManualTransactions: false` in `db(...)` to disable the warning; the warning itself mentions this flag. + +## Queues + +It's recommended to use queues for mutations and actions for read-only queries. This is the same code structure as the basic setup, but mutation writes are routed through queues. + +## Debugging + +- `GET /inspector/summary` includes `isDatabaseEnabled` so you can confirm SQLite is configured. +- `GET /inspector/database/schema` returns the tables and views discovered in the actor's SQLite database. +- `GET /inspector/database/rows?table=...&limit=100&offset=0` returns paged rows for a specific table or view. +- `POST /inspector/database/execute` lets you run ad-hoc SQL for debugging and data fixes with positional `args` or named `properties`. +- Keep a small read-only action for quick query verification while debugging. +- In non-dev mode, inspector endpoints require authorization. + +## Recommendations + +- Keep schema creation and migration steps in `onMigrate`; RivetKit runs them atomically inside a SQLite savepoint. +- Use `?` placeholders for dynamic values. +- Prefer queue-driven writes for ordered or background work. +- Use transactions for related multi-step mutations when atomicity matters. + +## Read more + +- [SQLite + Drizzle in Rivet Actors](/docs/actors/sqlite-drizzle) +- [SQLite docs](https://sqlite.org/docs.html) +- [SQLite SQL language reference](https://sqlite.org/lang.html) + +_Source doc path: /docs/actors/sqlite_ diff --git a/.agents/skills/live-cursors/reference/actors/state.md b/.agents/skills/live-cursors/reference/actors/state.md new file mode 100644 index 0000000..559b49c --- /dev/null +++ b/.agents/skills/live-cursors/reference/actors/state.md @@ -0,0 +1,270 @@ +# In-Memory State + +> Source: `src/content/docs/actors/state.mdx` +> Canonical URL: https://rivet.dev/docs/actors/state +> Description: Actors store state in memory for instant reads and writes. State can be persisted automatically or kept ephemeral. + +--- +## Types of State + +There are three ways to store data in an actor, depending on what it looks like and whether it needs to survive restarts. + +### Durable + +Simple, serializable data on `c.state` that is automatically persisted and restored across restarts. The default starting point. + +### Ephemeral + +Live objects on `c.vars` like database connections, API clients, and event emitters, or data loaded from an external source. Never persisted. + +```typescript @nocheck External database +import { actor } from "rivetkit"; +import { Pool } from "pg"; + +// One shared pool for the whole process, created once and reused by every actor +const pool = new Pool({ connectionString: process.env.DATABASE_URL }); + +const userActor = actor({ + state: { profile: null as Record | null }, + + // Load this actor's row from the shared pool on each start + createVars: async (c) => { + const { rows } = await pool.query("SELECT * FROM users WHERE id = $1", [c.key[0]]); + return { profile: rows[0] }; + }, + + actions: { + updateEmail: async (c, email: string) => { + await pool.query("UPDATE users SET email = $1 WHERE id = $2", [email, c.key[0]]); + } + } +}); +``` + +### SQLite + +Rivet also provides an embedded SQLite database (`c.db`) for when your data needs to be queried, requires safe schema migrations, or grows too large to hold in memory. See [SQLite](/docs/actors/sqlite). + +```typescript @nocheck Basic +import { actor } from "rivetkit"; +import { db } from "rivetkit/db"; + +const todoList = actor({ + db: db({ + onMigrate: async (db) => { + await db.execute(` + CREATE TABLE IF NOT EXISTS todos ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + title TEXT NOT NULL + ); + `); + }, + }), + + actions: { + add: async (c, title: string) => { + await c.db.execute("INSERT INTO todos (title) VALUES (?)", title); + }, + + list: async (c) => { + return (await c.db.execute( + "SELECT id, title FROM todos ORDER BY id DESC", + )) as { id: number; title: string }[]; + }, + }, +}); +``` + +```typescript @nocheck Load into memory +import { actor } from "rivetkit"; +import { db } from "rivetkit/db"; + +const counter = actor({ + db: db({ + onMigrate: async (db) => { + await db.execute(` + CREATE TABLE IF NOT EXISTS counter ( + id INTEGER PRIMARY KEY CHECK (id = 1), + count INTEGER NOT NULL + ); + `); + await db.execute("INSERT OR IGNORE INTO counter (id, count) VALUES (1, 0)"); + }, + }), + + // Load the count from SQLite into memory on every start + createVars: async (c) => { + const rows = (await c.db.execute( + "SELECT count FROM counter WHERE id = 1", + )) as { count: number }[]; + return { count: rows[0].count }; + }, + + actions: { + get: (c) => c.vars.count, + + increment: async (c) => { + // Update the in-memory value and write it back to SQLite + c.vars.count += 1; + await c.db.execute("UPDATE counter SET count = ? WHERE id = 1", c.vars.count); + return c.vars.count; + }, + }, +}); +``` + +## State Isolation + +Each actor's state is fully isolated. Other actors and clients can't touch it directly; all reads and writes go through the actor's own [Actions](/docs/actors/actions). To share state across actors, see [sharing and joining state](/docs/actors/sharing-and-joining-state). + +## Durable State + +`c.state` lives in memory and is persisted automatically, so reads and writes have no added latency while the data still survives sleeps, restarts, upgrades, and crashes. Use it for small, simple values like counters, flags, and small maps. + +`createState` runs once when the actor is first created. On later starts, state is loaded from storage instead of recreated. See [Lifecycle](/docs/actors/lifecycle). + +### When state saves + +Mutating `c.state` schedules a save automatically. Rapid mutations are batched into a single write on a throttle (`stateSaveInterval`, default 1 second). Reads never trigger a save, saves aren't tied to action or handler boundaries, and state is also flushed when the actor sleeps or shuts down. + +To force a save mid-action, call `c.saveState()`: + +- `c.saveState({ immediate: true })` writes immediately and resolves once the write completes. +- `c.saveState()` schedules a throttled save and returns right away, without waiting for the write. + +Force an immediate save before a risky side effect so a crash can't lose progress: + +### Supported types + +State must be serializable. + +- `null`, `undefined`, `boolean`, `string`, `number`, `BigInt` +- `Date`, `RegExp`, `Error` +- `ArrayBuffer` and typed arrays (`Uint8Array`, `Int8Array`, `Float32Array`, etc.) +- `Map`, `Set`, `Array` +- Plain objects + +When data grows large or needs querying, store it in [Embedded SQLite](#embedded-sqlite) instead. + +## Ephemeral State + +`c.vars` holds data that exists only while the actor runs and is never saved. Use it for live objects that can't be serialized (connections, clients, emitters) or for data loaded from an external source. Most actors use both: `state` for durable data, `vars` for live objects. + +`createVars` runs on every actor start, unlike `createState` which runs once. That makes it the place to open connections and load data each time the actor wakes. + +### Runtime objects + +Build non-serializable objects in `createVars` and use them from actions: + +### Loading from external sources + +Create the connection pool once at module scope and share it across all actors, then use `createVars` (which can be `async`) to load this actor's data from it on each start: + +```typescript @nocheck +import { actor } from "rivetkit"; +import { Pool } from "pg"; + +// One shared pool for the whole process, not one per actor +const pool = new Pool({ connectionString: process.env.DATABASE_URL }); + +const profile = actor({ + state: { cachedName: "" }, + + createVars: async (c) => { + const { rows } = await pool.query("SELECT * FROM users WHERE id = $1", [c.key[0]]); + return { user: rows[0] }; + }, + + actions: { + updateEmail: async (c, email: string) => { + await pool.query("UPDATE users SET email = $1 WHERE id = $2", [email, c.key[0]]); + } + } +}); +``` + +When the actor owns its data, prefer [durable state](#durable-state) or [SQLite](#embedded-sqlite), which need no external infrastructure. + +### Cleanup + +`vars` is dropped when the actor stops, but per-actor resources like timers, subscriptions, and dedicated connections aren't cleaned up for you. Release them in `onSleep` and `onDestroy`. A shared pool stays open for the whole process, so don't close it per actor. + +```typescript @nocheck +const poller = actor({ + state: { ticks: 0 }, + + // Per-actor timer started on each wake + createVars: (c) => ({ timer: setInterval(() => c.state.ticks++, 5000) }), + + // Clear it before the actor sleeps or is destroyed + onSleep: (c) => clearInterval(c.vars.timer), + onDestroy: (c) => clearInterval(c.vars.timer), + + actions: { /* ... */ } +}); +``` + +## Embedded SQLite + +`c.db` is a SQLite database scoped to each actor and stored on disk. Use it for queryable, relational, or larger-than-memory data. Because compute and storage live together, queries run locally with no network round trips. + +A common pattern is to treat SQLite as the source of truth and keep a working copy in `c.vars`: load rows in `createVars`, serve reads from memory, and write changes back to `c.db`. + +```typescript @nocheck +import { actor } from "rivetkit"; +import { db } from "rivetkit/db"; + +const leaderboard = actor({ + db: db({ + onMigrate: async (db) => { + await db.execute(` + CREATE TABLE IF NOT EXISTS scores ( + player TEXT PRIMARY KEY, + score INTEGER NOT NULL + ); + `); + }, + }), + + // Load the table into memory once per start + createVars: async (c) => { + const rows = (await c.db.execute("SELECT player, score FROM scores")) as { + player: string; + score: number; + }[]; + return { scores: new Map(rows.map((r) => [r.player, r.score])) }; + }, + + actions: { + top: (c) => [...c.vars.scores].sort((a, b) => b[1] - a[1]).slice(0, 10), + + record: async (c, player: string, score: number) => { + c.vars.scores.set(player, score); + // Write through to SQLite + await c.db.execute( + "INSERT INTO scores (player, score) VALUES (?, ?) ON CONFLICT(player) DO UPDATE SET score = ?", + player, score, score, + ); + }, + }, +}); +``` + +For the full query API, schema migrations, transactions, and the Drizzle ORM, see: + +- [SQLite](/docs/actors/sqlite): raw SQL against the embedded per-actor database. +- [SQLite + Drizzle](/docs/actors/sqlite-drizzle): typed schema and query APIs. + +## Debugging + +- `GET /inspector/state` returns the actor's current state and `isStateEnabled`. +- `PATCH /inspector/state` lets you set state directly while debugging. +- In non-dev mode, inspector endpoints require authorization. + +## API Reference + +- [`CreateContext`](/typedoc/types/rivetkit.mod.CreateContext.html) - Context available during actor state creation +- [`ActorContext`](/typedoc/interfaces/rivetkit.mod.ActorContext.html) - Context available throughout actor lifecycle +- [`ActorDefinition`](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html) - Interface for defining actors with state + +_Source doc path: /docs/actors/state_ diff --git a/.agents/skills/live-cursors/reference/actors/statuses.md b/.agents/skills/live-cursors/reference/actors/statuses.md new file mode 100644 index 0000000..cccf52a --- /dev/null +++ b/.agents/skills/live-cursors/reference/actors/statuses.md @@ -0,0 +1,36 @@ +# Actor Statuses + +> Source: `src/content/docs/actors/statuses.mdx` +> Canonical URL: https://rivet.dev/docs/actors/statuses +> Description: Understand the lifecycle statuses of Rivet Actors, what they mean, how they appear in the API, and how to troubleshoot common issues. + +--- +## Statuses + +These are the statuses you can see in the dashboard for each actor. + +| Status | Description | +|---|---| +| **Starting** | The actor has been created but has not yet become connectable. | +| **Running** | The actor is live and accepting connections. | +| **Destroyed** | The actor has been gracefully destroyed. | +| **Crashed** | The actor failed to start or encountered a fatal error. See [Troubleshooting](/docs/actors/troubleshooting#actor-status-is-crashed) for common failure reasons. | +| **Sleeping** | The actor has been put to sleep from inactivity. It will be woken up automatically when a new request arrives. | +| **Pending** | The actor is waiting to be allocated to a runner. This happens when no runner is available to handle the actor. See [Troubleshooting](/docs/actors/troubleshooting#actor-status-is-pending) for common causes. | +| **Crash Loop Backoff** | The actor failed to allocate and is waiting to retry with a backoff. This typically means repeated allocation failures. The backoff prevents overloading your infrastructure in the case of a widespread misconfiguration in your backend. See [Troubleshooting](/docs/actors/troubleshooting#actor-status-is-crashed) for common failure reasons. | + +## API Representation + +The actor object returned by the full engine API (used by the dashboard) includes the following timestamp fields used to derive status: + +| Field | Description | +|---|---| +| `createTs` | When the actor was first created. Always present. | +| `connectableTs` | When the actor became connectable. Null if not yet running. | +| `destroyTs` | When the actor was destroyed. | +| `sleepTs` | When the actor entered a sleeping state. | +| `pendingAllocationTs` | When the actor started waiting for an allocation. | +| `rescheduleTs` | When the actor will retry allocation after a failure. | +| `error` | Error details if the actor failed. | + +_Source doc path: /docs/actors/statuses_ diff --git a/.agents/skills/live-cursors/reference/actors/testing.md b/.agents/skills/live-cursors/reference/actors/testing.md new file mode 100644 index 0000000..1a30fff --- /dev/null +++ b/.agents/skills/live-cursors/reference/actors/testing.md @@ -0,0 +1,51 @@ +# Testing + +> Source: `src/content/docs/actors/testing.mdx` +> Canonical URL: https://rivet.dev/docs/actors/testing +> Description: Rivet provides a straightforward testing framework to build reliable and maintainable applications. This guide covers how to write effective tests for your actor-based services. + +--- +## Setup + +To set up testing with Rivet: + +```bash +# Install Vitest +npm install -D vitest + +# Run tests +npm test +``` + +## Basic Testing Setup + +Rivet includes a test helper called `setupTest` that starts your registry in test mode and returns a client connected to it. This allows for fast, isolated tests without external dependencies. + +## Testing Actor State + +State persists within each test, allowing you to verify that your actor correctly maintains state between operations. + +## Testing Events + +For actors that emit events, you can verify events are correctly triggered by subscribing to them: + +## Testing Schedules + +Rivet's schedule functionality can be tested by scheduling work and waiting for it to run: + +Use a short schedule and `expect.poll` the action's observable result. Vitest's fake date and fake JavaScript timers do not advance RivetKit's scheduler, which runs outside the test's JavaScript timer queue. + +## Best Practices + +1. **Isolate tests**: Each test should run independently, avoiding shared state. +2. **Test edge cases**: Verify how your actor handles invalid inputs, concurrent operations, and error conditions. +3. **Test scheduled operations**: Poll observable state or output with a bounded timeout instead of sleeping for an exact duration. +4. **Use realistic data**: Test with data that resembles production scenarios. + +`setupTest` starts the registry and disposes the returned client when the test finishes, so you can focus on writing effective tests for your business logic. + +## API Reference + +- [`setupTest`](/typedoc/functions/rivetkit.test_mod.setupTest.html) - Test setup helper function + +_Source doc path: /docs/actors/testing_ diff --git a/.agents/skills/live-cursors/reference/actors/troubleshooting.md b/.agents/skills/live-cursors/reference/actors/troubleshooting.md new file mode 100644 index 0000000..65b234d --- /dev/null +++ b/.agents/skills/live-cursors/reference/actors/troubleshooting.md @@ -0,0 +1,154 @@ +# Troubleshooting + +> Source: `src/content/docs/actors/troubleshooting.mdx` +> Canonical URL: https://rivet.dev/docs/actors/troubleshooting +> Description: Common issues with Rivet Actors and how to resolve them. + +--- +## Common Steps + +Before diving into specific errors, try these general troubleshooting steps: + +- Check your server logs for `level=ERROR` or `level=WARN` messages. +- Check if any of your backend processes have crashed or restarted unexpectedly. +- If you need more diagnostics, set `RIVET_LOG_LEVEL=DEBUG` for verbose logging. See [Logging](/docs/general/logging) for more options. + +## Reporting Issues + +If you're stuck, reach out on [Discord](https://rivet.dev/discord) or file an issue on [GitHub](https://github.com/rivet-dev/rivet/issues). + +When reporting, please include: + +- **Symptoms** + - Whether this is happening in local dev, deployed, or both + - The error you're seeing (screenshot or error message) + - Relevant source code related to the issue +- **What you've tried to solve it** +- **Environment** + - RivetKit version + - Runtime (Node, Bun, etc.) including version + - If applicable, provider in use (e.g. Vercel, Railway, Cloudflare) + - If applicable, HTTP router in use (e.g. Hono, Express, Elysia) + +## Actor status is crashed + +See [Actor Statuses](/docs/actors/statuses) for more about this status. + +The dashboard will show the specific failure reason. Common errors include: + +### `crashed` + +The actor's `run` handler threw an unhandled exception or exited unexpectedly. Check your actor logs for the error message and stack trace. + +### `no_capacity` + +No server was available to run your actor. The cause depends on your [runtime mode](/docs/general/runtime-modes): + +**Serverless**: + +- Your provider configuration does not have the region enabled that the actor is trying to run in. This is uncommon and usually only happens if an actor was created and then the provider config was updated to remove the region. +- There is an issue connecting to your backend. If the engine is hitting `/api/rivet/start` and failing, check your backend logs for errors. + +**Runners**: + +- You don't have enough runners online. Check your runner list in the dashboard to verify runners are visible and connected. +- Your runners are full. Each runner has a limited number of actor slots (configured via `RIVET_TOTAL_SLOTS`, default: 100,000). Check the dashboard to see if your runners have available capacity and scale up if needed. + +### `runner_no_response` + +The server running your actor did not respond in time. This can happen if your server is overloaded or experienced a network issue. Try restarting your server or checking its health. + +### `runner_connection_lost` + +The server running your actor lost its connection to Rivet. This is usually caused by a network interruption or your server restarting. + +### `runner_draining_timeout` + +Your server is shutting down and the actor did not finish in time. Consider handling graceful shutdown in your actor or increasing your shutdown timeout. + +### `concurrent_actor_limit_reached` + +The actor could not be allocated because the concurrent actor limit was reached. Reduce the number of concurrently running actors or increase your limit. + +### `no_envoys` + +No server was available to run your actor. This is equivalent to `no_capacity` on the current allocation path. See the `no_capacity` section above for the causes and fixes for your [runtime mode](/docs/general/runtime-modes). + +### `envoy_no_response` + +The server running your actor did not respond in time. This can happen if your server is overloaded or experienced a network issue. Try restarting your server or checking its health. + +### `envoy_connection_lost` + +The server running your actor lost its connection to Rivet. This is usually caused by a network interruption or your server restarting. + +### `serverless_http_error` + +Your serverless endpoint returned an HTTP error. Common causes: + +- Your backend is returning an error before the actor can start. Check your server logs. +- Your endpoint is behind authentication or a firewall that is blocking Rivet's requests. +- Your serverless function crashed during startup. Check your platform's function logs (e.g. Vercel, Cloudflare). + +### `serverless_connection_error` + +Rivet was unable to connect to your serverless endpoint. Check that: + +- Your backend is deployed and the endpoint URL is correct. +- Your server is publicly reachable from the internet. +- There are no DNS or firewall issues blocking the connection. + +### `serverless_stream_ended_early` + +The connection to your serverless endpoint was terminated before the actor finished. This usually means your serverless function hit its execution time limit. Ensure that your Rivet provider's request lifespan is configured to match the max duration of your serverless platform. + +### `serverless_invalid_sse_payload` + +Rivet received an unexpected response from your serverless endpoint. This typically means something is intercepting or modifying the request before it reaches your RivetKit handler. Check that: + +- Your server routes requests to `registry.start()`, `registry.serve()`, or `registry.handler()` correctly. +- No middleware is modifying the request or response body. + +### `internal_error` + +An unexpected error occurred within Rivet. If this persists, please [contact support](https://rivet.dev/docs). + +## Actors crashing immediately on startup + +If your actors are being created and then immediately destroyed or crashing, there is likely an error being thrown in your `createState` or `onCreate` lifecycle hooks. These hooks run during actor initialization before the actor is marked as ready. + +Check your server logs for the error message and stack trace. Common causes include: + +- An exception thrown in `createState` (e.g. invalid input, failed validation, or a runtime error when computing initial state) +- An exception thrown in `onCreate` (e.g. a failing external API call, missing configuration, or invalid setup logic) + +Fix the error in the relevant lifecycle hook and redeploy. + +## Actors not upgrading to new code + +If your actors are still running old code after deploying a new version, your [versioning](/docs/actors/versions) is likely not configured correctly. + +Without versioning, Rivet has no way to distinguish old deployments from new ones. The behavior depends on your [runtime mode](/docs/general/runtime-modes): + +- **Serverless**: Old requests may still be open from the previous deployment, so actors continue running on the old version's connection until those requests close. +- **Runners**: The old runner container is still running and will continue accepting new actors. New actors may be scheduled on the old runner instead of the new one. + +To fix this, configure a version number in your [registry configuration](/docs/general/registry-configuration). When a new version is deployed, Rivet will allocate new actors to the latest version and optionally drain old actors to migrate them. + +## Actor status is pending + +See [Actor Statuses](/docs/actors/statuses) for more about this status. + +An actor stays in "pending" status when Rivet is waiting for a server to accept it. The cause depends on your [runtime mode](/docs/general/runtime-modes): + +**Serverless**: + +- A region may have been removed from your provider configuration while an existing actor still lives in that region. The actor has no available server to start on because no provider serves that region anymore. Re-add the region to your provider config or destroy the affected actors. +- Check your backend logs for errors on the `/api/rivet/start` endpoint. + +**Runners**: + +- You don't have enough runners online. Check the dashboard to verify your runners are connected. +- Your runners may be at capacity. Check the dashboard to see if runners have available actor slots and scale up if needed. + +_Source doc path: /docs/actors/troubleshooting_ diff --git a/.agents/skills/live-cursors/reference/actors/types.md b/.agents/skills/live-cursors/reference/actors/types.md new file mode 100644 index 0000000..3e8f9b0 --- /dev/null +++ b/.agents/skills/live-cursors/reference/actors/types.md @@ -0,0 +1,46 @@ +# Types + +> Source: `src/content/docs/actors/types.mdx` +> Canonical URL: https://rivet.dev/docs/actors/types +> Description: TypeScript types for working with Rivet Actors. This page covers context types used in lifecycle hooks and actions, as well as helper types for extracting types from actor definitions. + +--- +## Context Types + +Context types define what properties and methods are available in different parts of the actor lifecycle. + +### Extracting Context Types + +When writing helper functions that work with actor contexts, use context extractor types like `CreateContextOf` or `ActionContextOf` to extract the appropriate context type from your actor definition. + +### All Context Types + +Each lifecycle hook and handler has a corresponding `*ContextOf` type, exported from `"rivetkit"`. Pass `typeof myActor` as the type parameter. + +| Hook / Handler | Context Type | +|---|---| +| `createState` | `CreateContextOf` | +| `onCreate` | `CreateContextOf` | +| `createVars` | `CreateVarsContextOf` | +| `createConnState` | `CreateConnStateContextOf` | +| `onBeforeConnect` | `BeforeConnectContextOf` | +| `onConnect` | `ConnectContextOf` | +| `onDisconnect` | `DisconnectContextOf` | +| `onDestroy` | `DestroyContextOf` | +| `onMigrate` | `MigrateContextOf` | +| `onWake` | `WakeContextOf` | +| `onSleep` | `SleepContextOf` | +| `onStateChange` | `StateChangeContextOf` | +| `onBeforeActionResponse` | `BeforeActionResponseContextOf` | +| `actions.*` | `ActionContextOf` | +| `run` | `RunContextOf` | +| `workflow` orchestration helpers (root, `loop`, `try`, `race`, `join` branches) | `WorkflowContextOf` | +| `workflow` `step` / `tryStep` run + rollback helpers | `WorkflowStepContextOf` | +| `onRequest` | `RequestContextOf` | +| `onWebSocket` | `WebSocketContextOf` | + +`ActorContextOf`, `ConnContextOf`, and `ConnInitContextOf` are general-purpose base context types useful for helper functions that don't correspond to a specific hook. + +Workflow context extractors are exported from both `"rivetkit"` and `"rivetkit/workflow"`. + +_Source doc path: /docs/actors/types_ diff --git a/.agents/skills/live-cursors/reference/actors/versions.md b/.agents/skills/live-cursors/reference/actors/versions.md new file mode 100644 index 0000000..1b45129 --- /dev/null +++ b/.agents/skills/live-cursors/reference/actors/versions.md @@ -0,0 +1,241 @@ +# Versions & Upgrades + +> Source: `src/content/docs/actors/versions.mdx` +> Canonical URL: https://rivet.dev/docs/actors/versions +> Description: When you deploy new code, Rivet ensures actors are upgraded seamlessly without downtime. + +--- +## How Versions Work + +Each runner has a **version number**. When you deploy new code with a new version, Rivet handles the transition automatically: + +- **New actors go to the newest version**: When allocating actors, Rivet always prefers runners with the highest version number +- **Multiple versions can coexist**: Old actors continue running on old versions while new actors are created on the new version +- **Drain old actors**: When enabled, a runner connecting with a newer version number will gracefully stop old actors to be rescheduled to the new version + +Versions are not configured by default. See [Registry Configuration](/docs/general/registry-configuration) to learn how to configure the runner version. + +`RIVET_ENVOY_VERSION` is only needed when self-hosting or using a custom runner. Rivet Compute handles versioning automatically. + +### Example Scenario + +### Drain Enabled + +When a new version is deployed, existing actors are gracefully stopped on the old runner and rescheduled onto the new version. + +```mermaid +sequenceDiagram + participant R1 as Runner v1 + participant R2 as Runner v2 + + Note over R1: Currently running + Note over R2: Deployed + R2->>R1: Drain old actors + R1->>R2: Reschedule actors + Note over R1: Shut down when all actors stopped +``` + +### Drain Disabled + +When a new version is deployed, both versions coexist. New actors are created on the new version while existing actors continue running on the old version until. + +```mermaid +sequenceDiagram + participant R1 as Runner v1 + participant R2 as Runner v2 + + Note over R1: Currently running + Note over R2: Deployed + Note over R1: Actor 1 sleeps from inactivity + Note over R2: Actor 1 wakes up when prompted +``` + +## Configuration + +### Setting the Version + +Configure the runner version using an environment variable or programmatically: + +```bash {{"title": "Environment Variable"}} +RIVET_ENVOY_VERSION=2 +``` + +The version **must** be set at build time, not at runtime. Do not use `Date.now()` or similar runtime values in your registry setup code. This would assign a different version every time the server starts, causing actors to be drained and rescheduled on every restart instead of only on new deployments. + +### Example Configurations + +We recommend injecting a build-time value that increments with every deployment. Here are concrete examples for common setups: + +### Dockerfile + +Generate the version at build time and bake it into the image as an environment variable: + +```bash @nocheck +docker build --build-arg RIVET_ENVOY_VERSION=$(date +%s) . +``` + +```dockerfile @nocheck +FROM node:20-slim +ARG RIVET_ENVOY_VERSION +ENV RIVET_ENVOY_VERSION=$RIVET_ENVOY_VERSION +WORKDIR /app +COPY . . +RUN npm install && npm run build +CMD ["node", "dist/server.js"] +``` + +All containers from this image will share the same version. + +### Next.js + +Set the version in `next.config.ts`. Next.js evaluates this file once at build time and inlines the value into the bundle: + +```typescript @nocheck +import type { NextConfig } from "next"; + +const nextConfig: NextConfig = { + env: { + RIVET_ENVOY_VERSION: String(Math.floor(Date.now() / 1000)), + }, +}; + +export default nextConfig; +``` + +### Vite + +Use `define` in your Vite config. This is evaluated once at build time and inlined into the bundle: + +```typescript @nocheck +import { defineConfig } from "vite"; + +export default defineConfig({ + define: { + "process.env.RIVET_ENVOY_VERSION": JSON.stringify( + String(Math.floor(Date.now() / 1000)) + ), + }, +}); +``` + +### CI/CD + +Set the version from your CI pipeline: + +```yaml @nocheck +# GitHub Actions +env: + RIVET_ENVOY_VERSION: ${{ github.run_number }} +``` + +```bash @nocheck +# Railway / Render / generic CI +export RIVET_ENVOY_VERSION=$(date +%s) +``` + +```bash @nocheck +# Git commit count +export RIVET_ENVOY_VERSION=$(git rev-list --count HEAD) +``` + +### Build Script + +Generate a version file during your build step and import it: + +```json @nocheck +{ + "scripts": { + "build": "echo \"export const BUILD_VERSION = $(date +%s);\" > src/build-version.ts && tsc" + } +} +``` + +```typescript @nocheck +import { actor, setup } from "rivetkit"; +import { BUILD_VERSION } from "./build-version"; + +const myActor = actor({ state: {}, actions: {} }); + +const registry = setup({ + use: { myActor }, + envoy: { + version: BUILD_VERSION, + }, +}); +``` + +### Drain on Version Upgrade + +The `drainOnVersionUpgrade` option controls whether old actors are stopped when a new version is deployed. This is configured in the Rivet dashboard under your runner configuration. See [Pool Configuration](/docs/general/pool-configuration) for the full set of pool options, including how to rate-limit actor eviction during the drain. + +| Value | Behavior | +|-------|----------| +| `false` | Old actors continue running. New actors go to new version. Versions coexist. | +| `true` (default) | Old actors receive stop signal and have 30m to finish gracefully. | + +## Upgrading Actor State + +When you deploy a new version, existing actors may need to handle schema changes in their persisted data. + +### SQLite (recommended for complex schemas) + +**Drizzle (recommended)** + +Use [Drizzle](/docs/actors/sqlite-drizzle) for typed schemas with generated migrations. Drizzle generates versioned `.sql` migration files from your TypeScript schema and applies them in order automatically. This is the recommended approach when your schema evolves frequently. + +**Raw SQL** + +For actors using [raw SQLite](/docs/actors/sqlite), migrations run automatically via the `onMigrate` hook on every actor start. RivetKit wraps the hook in a SQLite savepoint, so the migration is fully atomic. Use SQLite's `user_version` pragma to track which migrations have run: + +### In-memory state (`c.state`) + +If you use `c.state` for persistence, you are responsible for handling schema changes yourself. If you add, remove, or rename fields between versions, your code must handle the old shape gracefully. + +**Manual defaults in `onWake`** + +Apply defaults for missing fields: + +**Zod schema coercion** + +Use [Zod](https://zod.dev/) to parse persisted state on wake. Zod's `.default()` fills in missing fields automatically, so old actor state is coerced to the current schema: + +For anything beyond simple defaults, consider moving to [SQLite](/docs/actors/sqlite) where you get proper migration tooling. + +## Advanced + +### How Version Upgrade Detection Works + +When `drainOnVersionUpgrade` is enabled, Rivet uses two mechanisms to detect version changes: + +- **New runner connection**: When a runner connects with a newer version number, the engine immediately drains all older runners with the same name. This is the primary mechanism for [runner mode](/docs/general/runtime-modes) deployments. +- **Metadata polling** (serverless only): In [serverless mode](/docs/general/runtime-modes), runners periodically poll the engine to check for newer versions and self-drain if one is found. This ensures old runners drain even if no new requests trigger a runner connection. + +### SIGTERM Handling + +When a runner process receives SIGTERM, it gracefully stops all actors before exiting: + +- Each actor's `onSleep` hook is called, giving it time to save state +- Actors are rescheduled to other available runners +- The runner waits up to **30 minutes** for all actors to finish stopping +- If the process is force-killed before actors finish (e.g. SIGKILL), actors are rescheduled with a crash backoff penalty instead of a clean handoff + +Actors have a maximum of 30 minutes to clean up during shutdown. Ensure your platform's drain grace period is at most 30 minutes. + +### Shutdown Timeouts + +Several timeouts control how long each part of the shutdown process can take: + +| Timeout | Default | Description | Configuration | +|---------|---------|-------------|---------------| +| `actor_stop_threshold` | 30m | Engine-side limit on how long each actor has to stop before being marked lost | [Engine config](/docs/self-hosting/configuration) (`pegboard.actor_stop_threshold`) | +| `sleepGracePeriod` | 15s | Total graceful sleep budget for `onSleep`, `waitUntil`, `keepAwake`, and async raw WebSocket handlers | [Actor options](/docs/actors/lifecycle#options) | +| `runner_lost_threshold` | 15s | Fallback detection if the runner dies without graceful shutdown | [Engine config](/docs/self-hosting/configuration) (`pegboard.runner_lost_threshold`) | + +Rivet has a max shutdown grace period of 30 minutes that cannot be configured. + +## Related + +- [Runtime Modes](/docs/general/runtime-modes): Serverless vs runner deployment modes +- [Lifecycle](/docs/actors/lifecycle): Actor lifecycle hooks including `onSleep` + +_Source doc path: /docs/actors/versions_ diff --git a/.agents/skills/live-cursors/reference/actors/websocket-handler.md b/.agents/skills/live-cursors/reference/actors/websocket-handler.md new file mode 100644 index 0000000..f94499d --- /dev/null +++ b/.agents/skills/live-cursors/reference/actors/websocket-handler.md @@ -0,0 +1,201 @@ +# Low-Level WebSocket Handler + +> Source: `src/content/docs/actors/websocket-handler.mdx` +> Canonical URL: https://rivet.dev/docs/actors/websocket-handler +> Description: Actors can handle WebSocket connections through the `onWebSocket` handler. + +--- +For most use cases, [actions](/docs/actors/actions) and [events](/docs/actors/events) provide high-level connection handling powered by WebSockets that's easier to work with than low-level WebSockets. However, low-level handlers are required when implementing custom use cases. + +## Handling WebSocket Connections + +The `onWebSocket` handler manages low-level WebSocket connections. It receives the actor context and a `WebSocket` object. + +See also the [raw WebSocket handler example](https://github.com/rivet-dev/rivet/tree/main/examples/raw-websocket-handler). + +## Connecting To Actors + +### Via RivetKit Client + +Use the `.webSocket()` method on an actor handle to open a WebSocket connection to the actor's `onWebSocket` handler. This can be executed from either your frontend or backend. + +```typescript index.ts @hide @nocheck +import { actor, setup } from "rivetkit"; + +export const chat = actor({ + state: { messages: [] as string[] }, + onWebSocket: (c, websocket) => { + websocket.addEventListener("message", (event) => { + c.state.messages.push(event.data as string); + }); + }, + actions: {} +}); + +export const registry = setup({ use: { chat } }); +registry.start(); +``` + +```typescript client.ts @nocheck +import { createClient } from "rivetkit/client"; +import type { registry } from "./index"; + +const client = createClient("http://localhost:6420"); + +const actor = client.chat.getOrCreate(["my-chat"]); + +// Open WebSocket connection +const ws = await actor.webSocket("/"); + +// Listen for messages +ws.addEventListener("message", (event) => { + const message = JSON.parse(event.data as string); + console.log("Received:", message); +}); + +// Send messages +ws.send(JSON.stringify({ type: "chat", text: "Hello!" })); +``` + +The `.webSocket()` method returns a standard WebSocket. + +### Via getGatewayUrl + +Use `.getGatewayUrl()` to get the raw gateway URL for the actor. This is useful when you need to use the URL with external tools or custom WebSocket clients. + +```typescript index.ts @hide @nocheck +import { actor, setup } from "rivetkit"; + +export const chat = actor({ + state: { messages: [] as string[] }, + onWebSocket: (c, websocket) => { + websocket.addEventListener("message", (event) => { + c.state.messages.push(event.data as string); + }); + }, + actions: {} +}); + +export const registry = setup({ use: { chat } }); +registry.start(); +``` + +```typescript client.ts @nocheck +import { createClient } from "rivetkit/client"; +import type { registry } from "./index"; + +const client = createClient("http://localhost:6420"); + +const actor = client.chat.getOrCreate(["my-chat"]); + +// Get the raw gateway URL +const gatewayUrl = await actor.getGatewayUrl(); +// gatewayUrl = "https://...rivet.dev/..." + +// Convert to WebSocket URL and connect +const wsUrl = gatewayUrl.replace("http://", "ws://").replace("https://", "wss://"); +const ws = new WebSocket(`${wsUrl}/websocket/`); + +ws.addEventListener("message", (event) => { + const message = JSON.parse(event.data as string); + console.log("Received:", message); +}); + +ws.addEventListener("open", () => { + ws.send(JSON.stringify({ type: "chat", text: "Hello!" })); +}); +``` + +### Via HTTP API + +This handler can be accessed with raw WebSockets using `wss://api.rivet.dev/gateway/{actorId}@{token}/websocket/{...path}`. + +For example, to connect to the chat actor above: + +```typescript +// Replace with your actor ID and token +const actorId = "your-actor-id"; +const token = "your-token"; + +const ws = new WebSocket( + `wss://api.rivet.dev/gateway/${actorId}@${token}/websocket/` +); + +ws.addEventListener("message", (event) => { + const message = JSON.parse(event.data as string); + console.log("Received:", message); +}); + +ws.addEventListener("open", () => { + ws.send(JSON.stringify({ type: "chat", text: "Hello!" })); +}); +``` + +```bash +wscat -c "wss://api.rivet.dev/gateway/{actorId}@{token}/websocket/" +``` + +The path after `/websocket/` is passed to your `onWebSocket` handler and can be used to route to different functionality within your actor. For example, to connect with a custom path `/admin`: + +```typescript +// Replace with your actor ID and token +const actorId = "your-actor-id"; +const token = "your-token"; + +const ws = new WebSocket( + `wss://api.rivet.dev/gateway/${actorId}@${token}/websocket/admin` +); +``` + +```bash +wscat -c "wss://api.rivet.dev/gateway/{actorId}@{token}/websocket/admin" +``` + +See the [HTTP API reference](/docs/actors/http-api) for more information on WebSocket routing and authentication. + +### Via Proxying Connections + +You can proxy WebSocket connections from your own server to actor handlers using the RivetKit client. This is useful when you need to add custom authentication or connection management before forwarding to actors. + +See also the [raw WebSocket handler with proxy example](https://github.com/rivet-dev/rivet/tree/main/examples/raw-websocket-handler-proxy). + +## Connection & Lifecycle Hooks + +`onWebSocket` will trigger the `onBeforeConnect`, `onConnect`, and `onDisconnect` hooks. Read more about [lifecycle hooks](/docs/actors/lifecycle). + +Open WebSockets will be listed in `c.conns`. `conn.send` and `c.broadcast` have no effect on low-level WebSocket connections. Read more about [connections](/docs/actors/connections). + +## WinterTC Compliance + +The `onWebSocket` handler uses standard WebSocket APIs and will work with existing libraries expecting WinterTC-compliant WebSocket objects. + +## Advanced + +## WebSocket Hibernation + +WebSocket hibernation allows actors to go to sleep while keeping WebSocket connections alive. Actors automatically wake up when a message is received or the connection closes. + +Enable hibernation by setting `canHibernateWebSocket: true`. You can also pass a function `(request) => boolean` for conditional control. + +Since `open` only fires once when the client first connects, use `c.conn.state` to store per-connection data that persists across sleep cycles. See [connections](/docs/actors/connections) for more details. + +### Accessing the Request + +The underlying HTTP request is available via `c.request`. This is useful for accessing the path or query parameters. + +### Skip Ready Wait + +Connections are normally held at the gateway until the actor is ready. Pass `skipReadyWait: true` on `handle.webSocket()` to connect immediately, including while the actor is still starting or in the [sleep grace period](/docs/actors/lifecycle#shutdown-sequence). See [Skip Ready Wait](/docs/clients/javascript#skip-ready-wait) for details. + +### Async Handlers + +The `onWebSocket` handler can be async, allowing you to perform async code before setting up event listeners: + +## API Reference + +- [`WebSocketContext`](/typedoc/interfaces/rivetkit.mod.WebSocketContext.html) - Context for WebSocket handlers +- [`UniversalWebSocket`](/typedoc/interfaces/rivetkit.mod.UniversalWebSocket.html) - Universal WebSocket interface +- [`handleRawWebSocketHandler`](/typedoc/functions/rivetkit.mod.handleRawWebSocketHandler.html) - Function to handle raw WebSocket +- [`UpgradeWebSocketArgs`](/typedoc/interfaces/rivetkit.mod.UpgradeWebSocketArgs.html) - Arguments for WebSocket upgrade + +_Source doc path: /docs/actors/websocket-handler_ diff --git a/.agents/skills/live-cursors/reference/actors/workflows.md b/.agents/skills/live-cursors/reference/actors/workflows.md new file mode 100644 index 0000000..9c59975 --- /dev/null +++ b/.agents/skills/live-cursors/reference/actors/workflows.md @@ -0,0 +1,228 @@ +# Workflows + +> Source: `src/content/docs/actors/workflows.mdx` +> Canonical URL: https://rivet.dev/docs/actors/workflows +> Description: Build durable, replayable run loops in Rivet Actors with steps, queue waits, timers, and rollback. + +--- +Use workflows for durable, multi-step execution with replay safety. + +## What are workflows? + +A workflow is a durable, replayable run handler for a Rivet Actor. + +- Survives restarts: workflow progress is saved automatically. +- Re-runs safely: replay follows the same recorded steps. +- Event-driven: workflows can pause for queue messages, then continue. + +## Getting started + +### Simple workflow + +Use this when you need a short multi-step sequence. + +### Loops + +This is the recommended workflow shape for most actor workloads. + +- Use a queue wait inside the loop to receive the next unit of work. +- Keep actor state changes in a single workflow loop. +- This gives you one durable workflow that manages all actor progress. + +### Setup & teardown + +Use this when the workflow should initialize resources, process queued commands, then clean up. + +## Features + +### Queue + +Use this for fire-and-forget commands where the client does not need a reply. + +Use the `Loops` example above as the baseline pattern. + +### Request/response (using queue) + +Use this when the caller needs a response from queued processing. + +### Timers + +Use queue messages as the trigger source, then sleep durably inside the workflow. + +### Join + +Use `join` when several independent tasks can run in parallel. + +### Race + +Use `race` when you need first-winner behavior. + +### Timeouts + +Use step timeouts and retries for slow or flaky dependencies. + +Step timeouts are critical by default and fail immediately. Set `retryOnTimeout: true` if a timeout should retry like any other error using `maxRetries`. + +Workflows use roll-forward semantics everywhere. When a step throws, any `state` or `vars` mutations it made before failing are never rolled back, whether the step retries or the failure is caught by `tryStep` or `try`. The next attempt observes whatever the failed attempt already wrote, so write steps idempotently: check before you increment, or move the mutation after the fallible work. + +### Handling terminal failures as data + +Use `tryStep` when a step failure should produce data instead of failing the whole workflow. + +Use `try` when you want to recover from terminal `step`, `join`, or `race` failures inside a named block. + +```ts +async function runPaymentFlow(ctx: any) { + return await ctx.try("payment-flow", async (blockCtx: any) => { + const auth = await blockCtx.step("authorize", async (blockCtx) => + authorizeOrder("order-123"), + ); + const capture = await blockCtx.step("capture", async (blockCtx) => + captureOrder("order-123"), + ); + return { auth, capture }; + }); +} + +async function authorizeOrder(orderId: string): Promise { + return `auth-${orderId}`; +} + +async function captureOrder(orderId: string): Promise { + return `capture-${orderId}`; +} +``` + +- `tryStep` and `try` only catch terminal failures. Retry backoff, sleeps, queue waits, eviction, and history divergence still rethrow. +- Catching a failure does not undo it. `state` and `vars` mutations made before the failure remain visible after `tryStep` or `try` returns, so use explicit compensating steps when a caught failure needs cleanup. +- `RollbackError` is not caught by default. Pass `catch: ["rollback"]` when you want rollback failures returned as data. + +### Error hooks + +Use `onError` when you want a best-effort notification for workflow failures. + +- Step failures include the attempt number, retry counts, whether the step will retry, and the next retry delay. +- Workflow failures also include terminal errors outside steps, such as rollback failures or code/history mismatches. +- The hook is observational. It is not part of workflow replay, so use it for logging, metrics, or updating non-critical actor state. +- This is also a good place to forward workflow failures to Sentry or another error reporting pipeline. + +### Rollback + +Use rollback checkpoints before steps that have compensating actions. + +## Patterns + +### Store workflow progress in state + broadcast + +Store progress in `state` so replay and recovery always restore it. Broadcast state changes so clients can render progress in realtime. + +### Cron (queue-driven) + +Rivet scheduling triggers actions. For cron-like workflows, use a small scheduled action as a bridge that enqueues work, then process that work in the workflow loop. + +These are common workflow shapes used in production systems. + +### Queue-driven worker + +Use this when external systems enqueue work and the actor should process each item durably. + +### Setup & teardown + +Use this when you need one-time initialization before a long-lived loop, plus cleanup when the actor stops sleeping or is destroyed. + +### Human approval gate + +Use this when an operation must pause for a user or system decision before continuing. + +### Fan-out / fan-in (join) + +Use this when independent work items can run in parallel and you need a single merged result. + +### Batch drainer + +Use this when throughput matters and handling one message at a time is too expensive. + +### Coordinator -> worker RPC + +Use this when one actor orchestrates work by calling actions on other actors. + +### Request/response over queue (async RPC) + +Use this when you want decoupled actor-to-actor communication with durable waits and explicit completion. + +### Scatter-gather across actors + +Use this when multiple actors can process independent parts of a request in parallel, then return a merged response. + +### Timeout + fallback actor + +Use this when a primary actor call might be slow or unavailable and you need a deterministic fallback path. + +### Cross-actor saga (compensating actions) + +Use this when a workflow spans multiple actors and each side effect may need compensation. + +### Signal-driven control loop + +Use this when workflow progress should be triggered by commands/events instead of fixed polling intervals. + +### Poll + backoff loop + +Use this when an external dependency has variable availability and retries should slow down after failures. + +### Child worker orchestration + +Use this when one workflow coordinates many child workers (actors or worker workflows) and manages their lifecycle. + +### Bounded drain + concurrency cap + +Use this when inbound work can spike and you need predictable per-iteration limits. + +### Versioned workflow evolution + +Use this when workflow structure changes across deployments and old histories must still replay. + +### Version gates with `getVersion` + +Use `ctx.getVersion(name, latest)` to branch behavior when you change a workflow's logic while old instances are still in flight. It returns the version this instance is pinned to at that point: + +- A fresh instance resolves to `latest`. +- An instance that already executed past this point under older code resolves to `1` (the implicit floor). + +The resolved version is recorded in history, so replays are deterministic and each instance stays on the branch it started on. Inside a loop, every iteration resolves independently, so in-flight iterations finish on the old branch while new iterations pick up `latest`. + +`latest` must be an integer `>= 1`, and the gate name must be unique within its scope like any other entry. Once every old instance has drained, retire the gate by replacing the call with `ctx.removed(name, "version_check")`. + +### Checkpoint-friendly loop design + +Use this when you need reliable replay and resume semantics across crashes and restarts. + +## Migrations + +- Keep workflow entry names stable once deployed. +- If an old entry was removed or renamed, call `ctx.removed(name, originalType)`. +- To change behavior at a point while old instances are still running, gate it with `ctx.getVersion(name, latest)` (see [Version gates](#version-gates-with-getversion)). +- This keeps replay compatible across deployments. + +## Step-only access to actor APIs + +`state`, `vars`, `db`, `client()`, and connection/event APIs are only valid inside `ctx.step(...)` callbacks. + +Use non-step workflow code for orchestration only: queue waits, sleeps, loops, joins, races, and rollback boundaries. Keep actor-local side effects in steps. + +## Debugging + +- `GET /inspector/workflow-history` returns workflow history status for an actor. +- Response includes `isWorkflowEnabled` and `history`. +- In non-dev mode, inspector endpoints require authorization. + +## Recommendations + +- Prefer queue-driven loops for long-lived workflows. +- Structure long-lived workflows with setup and teardown around the main loop. +- Keep actor state changes and side effects inside steps. +- Store workflow progress in `state` and broadcast updates as progress changes. +- Use timeouts and rollback for external side effects. +- Write step bodies idempotently. `state` and `vars` mutations from a failed attempt are never rolled back, whether the step retries or `tryStep`/`try` catches the failure. + +_Source doc path: /docs/actors/workflows_ diff --git a/.agents/skills/live-cursors/reference/cli.md b/.agents/skills/live-cursors/reference/cli.md new file mode 100644 index 0000000..34880ea --- /dev/null +++ b/.agents/skills/live-cursors/reference/cli.md @@ -0,0 +1,103 @@ +# CLI + +> Source: `src/content/docs/cli.mdx` +> Canonical URL: https://rivet.dev/docs/cli +> Description: Reference for the optional rivet CLI: deploy to Rivet Compute and run local dev for serverless platforms. + +--- +The `rivet` CLI (`@rivetkit/cli`) is optional. You only need it for: + +| Use case | Command | +| --- | --- | +| Deploy to Rivet Compute | `rivet deploy` | +| Local dev for serverless platforms (Cloudflare, Supabase) | `rivet dev --provider ` | + +Run it with your package runner: + +```bash +npx @rivetkit/cli +``` + +| Command | Description | +| --- | --- | +| `rivet dev` | Run a local engine and your handler's dev server. | +| `rivet deploy` | Build and deploy the project to Rivet Cloud. | +| `rivet engine` | Run the bundled `rivet-engine` binary directly. | +| `rivet setup-ci` | Install the GitHub Actions deploy workflow. | + +## `rivet dev` + +Starts a local engine, spawns your dev server, and registers the serverless runner pointing at it. The engine keeps running across restarts; Ctrl-C stops only the dev server. + +```bash +rivet dev [--provider ] [--port N] [--fn-name NAME] [--url URL] [-- ...] +``` + +`--provider` selects how the dev server is launched. Anything after `--` is appended to the preset command (or is the command to run when no provider is set). + +| `--provider` | Spawns | Port | +| --- | --- | --- | +| _(omitted)_ | your `-- ` (needs `--port`) | from `--port` | +| `serverless` | your `-- ` (gets `PORT`) | auto | +| `cloudflare` | `wrangler dev` | `8787` | +| `supabase` | `supabase functions serve` | `54321` | +| `none` | nothing (engine only) | — | + +For `cloudflare`, the CLI also passes the engine endpoint as `--var RIVET_ENDPOINT:...`, so the Worker connects back with no `wrangler.toml` config. + +| Flag | Description | +| --- | --- | +| `--port` | Handler port. Required without a provider unless `--url` is set. | +| `--fn-name` | Supabase function name (default `rivet`). | +| `--url` | Explicit handler URL, overriding port and path. | +| `--engine-binary` | Path to a `rivet-engine` binary. | + +## `rivet deploy` + +Builds and pushes your project's Docker image and upserts the managed pool, printing the dashboard URL. See [Deploying to Rivet Compute](/docs/deploy/rivet-compute). + +```bash +rivet deploy --token cloud_api_xxxxx +``` + +The token is saved to `~/.rivet/credentials` (also read from `RIVET_CLOUD_TOKEN`), so later deploys can omit it. + +| Flag | Default | Description | +| --- | --- | --- | +| `--token` | env / credentials | Rivet Cloud API token. | +| `--namespace` | `production` | Cloud namespace. | +| `--project` / `--org` | from token | Override project/org. | +| `--dockerfile` | `Dockerfile` | Dockerfile to build. | +| `--build-context` | `.` | Docker build context. | +| `--env KEY=VAL` | — | Environment override, repeatable. | +| `--image` | project slug | Image repository name. | +| `--tag` | git short SHA | Image tag. | + +## `rivet engine` + +Runs the bundled `rivet-engine` binary directly, against the same local database and ports as `rivet dev`. Arguments are forwarded verbatim. + +```bash +rivet engine nuke # wipe local engine state +rivet engine wf list # inspect workflows +``` + +## `rivet setup-ci` + +Installs `.github/workflows/rivet-deploy.yml`, which deploys to Rivet Cloud on push and pull request. Add `--force` to overwrite. Then set the token secret: + +```bash +gh secret set RIVET_CLOUD_TOKEN +``` + +## Engine binary resolution + +`rivet dev` and `rivet engine` resolve the `rivet-engine` binary from, in order: `--engine-binary`, `RIVET_ENGINE_BINARY_PATH`, a binary bundled next to the CLI, a local `target/{debug,release}` build, then an auto-downloaded release. Set `RIVETKIT_ENGINE_AUTO_DOWNLOAD=0` to require a local binary. + +## Related + +- [Deploying to Rivet Compute](/docs/deploy/rivet-compute) +- [Cloudflare Workers Quickstart](/docs/actors/quickstart/cloudflare) +- [Supabase Functions Quickstart](/docs/actors/quickstart/supabase) + +_Source doc path: /docs/cli_ diff --git a/.agents/skills/live-cursors/reference/clients/javascript.md b/.agents/skills/live-cursors/reference/clients/javascript.md new file mode 100644 index 0000000..689d79b --- /dev/null +++ b/.agents/skills/live-cursors/reference/clients/javascript.md @@ -0,0 +1,120 @@ +# Node.js & Bun + +> Source: `src/content/docs/clients/javascript.mdx` +> Canonical URL: https://rivet.dev/docs/clients/javascript +> Description: Connect JavaScript apps to Rivet Actors. + +--- +## Getting Started + +See the [backend quickstart guide](/docs/actors/quickstart/backend) for getting started. + +## Minimal Client + +## Stateless vs Stateful + +## Getting Actors + +## Connection Parameters + +Use `params` for static connection parameters. Use `getParams` when the value can change between connection attempts, such as refreshing a JWT before each `.connect()` or reconnect. + +## Subscribing to Events + +## Connection Lifecycle + +## Low-Level HTTP & WebSocket + +For actors that implement `onRequest` or `onWebSocket`, call them directly: + +```ts @nocheck +import { createClient } from "rivetkit/client"; + +const client = createClient(); +const handle = client.chatRoom.getOrCreate(["general"]); + +const response = await handle.fetch("history"); +const history = await response.json(); + +const ws = await handle.webSocket("stream"); +ws.addEventListener("message", (event) => { + console.log("message:", event.data); +}); +ws.send("hello"); +``` + +## Calling from Backend + +## Error Handling + +## Concepts + +### Keys + +Keys uniquely identify actor instances. Use compound keys (arrays) for hierarchical addressing: + +Don't build keys with string interpolation like `"org:${userId}"` when `userId` contains user data. Use arrays instead to prevent key injection attacks. + +### Environment Variables + +`createClient()` automatically reads: + +- `RIVET_ENDPOINT` (endpoint) +- `RIVET_NAMESPACE` +- `RIVET_TOKEN` +- `RIVET_RUNNER` + +Defaults to `http://localhost:6420` when unset. RivetKit runs on port 6420 by default. + +### Endpoint Format + +Endpoints support URL auth syntax: + +``` +https://namespace:token@api.rivet.dev +``` + +You can also pass the endpoint without auth and provide `RIVET_NAMESPACE` and `RIVET_TOKEN` separately. For serverless deployments, use your app's `/api/rivet` URL. See [Endpoints](/docs/general/endpoints#url-auth-syntax) for details. + +## Advanced + +### Skip Ready Wait + +Requests are normally held at the gateway until the actor is ready to accept traffic. An actor is not ready while it's still starting (before `onWake` finishes) or while it's in the [sleep grace period](/docs/actors/lifecycle#shutdown-sequence) (running `onSleep`, `waitUntil`, and pending disconnects). + +Pass `skipReadyWait: true` on the [low-level HTTP and WebSocket APIs](#low-level-http--websocket) to deliver immediately and reach the actor's `onRequest` / `onWebSocket` handler in either window: + +```ts @nocheck +import { createClient } from "rivetkit/client"; + +const client = createClient(); +const handle = client.chatRoom.getOrCreate(["general"]); + +const response = await handle.fetch("/healthz", { + skipReadyWait: true, +}); + +const ws = await handle.webSocket("probe", undefined, { + skipReadyWait: true, +}); +``` + +Requests can still return transient lifecycle or gateway errors. Retry once the actor is available again. + +- `actor.stopping`: the actor has fully stopped, i.e. the sleep grace period has ended but it has not yet restarted. +- `guard.actor_stopped_while_waiting`: the request reached the actor tunnel, but the actor stopped before the gateway received a response. +- `guard.tunnel_request_aborted`: the actor tunnel aborted the request before a response started. +- `guard.tunnel_message_timeout`: the gateway dropped the in-flight tunnel request after its tunnel message timeout. +- `guard.tunnel_response_closed`: the actor tunnel closed before sending a response. +- `guard.gateway_response_start_timeout`: the gateway timed out waiting for the actor response to start. + +## API Reference + +**Package:** [rivetkit](https://www.npmjs.com/package/rivetkit) + +See the [RivetKit client overview](/docs/clients). + +- [`createClient`](/typedoc/functions/rivetkit.client_mod.createClient.html) - Create a client +- [`Client`](/typedoc/types/rivetkit.mod.Client.html) - Client type + +_Source doc path: /docs/clients/javascript_ diff --git a/.agents/skills/live-cursors/reference/clients/react.md b/.agents/skills/live-cursors/reference/clients/react.md new file mode 100644 index 0000000..9b976e2 --- /dev/null +++ b/.agents/skills/live-cursors/reference/clients/react.md @@ -0,0 +1,88 @@ +# React + +> Source: `src/content/docs/clients/react.mdx` +> Canonical URL: https://rivet.dev/docs/clients/react +> Description: Connect React apps to Rivet Actors. + +--- +## Getting Started + +See the [React quickstart guide](/docs/actors/quickstart/react) for getting started. + +## Install + +## Minimal Client + +## Stateless vs Stateful + +## Getting Actors + +## Connection Parameters + +## Subscribing to Events + +## Connection Lifecycle + +## Low-Level HTTP & WebSocket + +Use the JavaScript client for raw HTTP and WebSocket access: + +```tsx @nocheck +import { createClient } from "rivetkit/client"; + +const client = createClient(); +const handle = client.chatRoom.getOrCreate(["general"]); + +const response = await handle.fetch("history"); +const history = await response.json(); + +const ws = await handle.webSocket("stream"); +ws.addEventListener("message", (event) => { + console.log("message:", event.data); +}); +ws.send("hello"); +``` + +## Calling from Backend + +Use the JavaScript client on your backend (Node.js/Bun). See the [JavaScript client docs](/docs/clients/javascript). + +## Error Handling + +## Concepts + +### Keys + +Keys uniquely identify actor instances. Use compound keys (arrays) for hierarchical addressing: + +Don't build keys with string interpolation like `"org:${userId}"` when `userId` contains user data. Use arrays instead to prevent key injection attacks. + +### Environment Variables + +`createRivetKit()` (and the underlying `createClient()` instance) automatically read: + +- `RIVET_ENDPOINT` +- `RIVET_NAMESPACE` +- `RIVET_TOKEN` +- `RIVET_RUNNER` + +Defaults to `http://localhost:6420` when unset. RivetKit runs on port 6420 by default. + +### Endpoint Format + +Endpoints support URL auth syntax: + +``` +https://namespace:token@api.rivet.dev +``` + +You can also pass the endpoint without auth and provide `RIVET_NAMESPACE` and `RIVET_TOKEN` separately. For serverless deployments, use your app's `/api/rivet` URL. See [Endpoints](/docs/general/endpoints#url-auth-syntax) for details. + +## API Reference + +**Package:** [@rivetkit/react](https://www.npmjs.com/package/@rivetkit/react) + +- [`createRivetKit`](/docs/clients/react) - Create hooks for React +- [`useActor`](/docs/clients/react) - Hook for actor instances + +_Source doc path: /docs/clients/react_ diff --git a/.agents/skills/live-cursors/reference/clients/rust.md b/.agents/skills/live-cursors/reference/clients/rust.md new file mode 100644 index 0000000..32bebfc --- /dev/null +++ b/.agents/skills/live-cursors/reference/clients/rust.md @@ -0,0 +1,367 @@ +# Rust (Beta) + +> Source: `src/content/docs/clients/rust.mdx` +> Canonical URL: https://rivet.dev/docs/clients/rust +> Description: Connect Rust apps to Rivet Actors. + +--- +Rust support is in beta. The supported public Rust API is `rivetkit` and `rivetkit::client`; lower-level crates are internal implementation details and do not carry a stability guarantee. See the full API reference on [docs.rs/rivetkit](https://docs.rs/rivetkit), or the runnable [`hello-world-rust`](https://github.com/rivet-dev/rivet/tree/main/examples/hello-world-rust) example. + +## Getting Started + +See the [Rust quickstart guide](/docs/actors/quickstart/rust) for getting started. + +## Install + +Add the `rivetkit` crate and its companions: + +```sh +cargo add rivetkit anyhow async-trait +cargo add serde --features derive +cargo add tokio --features full +``` + +The Rust client is strongly typed. It shares the same action and event types as your actor, so define your actor in `src/lib.rs` and import those types from both your server and your client. There is no need to redefine the actor on the client. See [Define Your Actor](/docs/actors/quickstart/rust#define-your-actor) in the quickstart for the actor definition this page builds on. + +## Minimal Client + +```rust @nocheck +use counter::{Counter, Increment}; +use rivetkit::{ + client::{Client, ClientConfig}, + prelude::*, + TypedClientExt, +}; + +#[tokio::main] +async fn main() -> Result<()> { + let client = Client::new(ClientConfig::new("http://localhost:6420").namespace("default")); + + let counter = client.get_or_create_typed_default::("counter", ["my-counter"])?; + let count = counter.send(Increment { amount: 1 }).await?; + println!("New count: {count}"); + + Ok(()) +} +``` + +`counter` here is your crate name (the package `name` in `Cargo.toml`, with dashes as underscores). `Counter` and `Increment` are the types you defined alongside your actor. + +## Stateless vs Stateful + +```rust @nocheck +use counter::{Counter, Increment, NewCount}; +use rivetkit::{ + client::{Client, ClientConfig}, + prelude::*, + TypedClientExt, +}; + +#[tokio::main] +async fn main() -> Result<()> { + let client = Client::new(ClientConfig::new("http://localhost:6420").namespace("default")); + let counter = client.get_or_create_typed_default::("counter", ["my-counter"])?; + + // Stateless: each call is independent + counter.send(Increment { amount: 1 }).await?; + + // Stateful: keep a connection open for realtime events + let connection = counter.connect(); + connection + .on::(|event| println!("count: {}", event.count)) + .await; + connection.send(Increment { amount: 1 }).await?; + + connection.disconnect().await; + Ok(()) +} +``` + +A stateless call on the handle opens a short-lived request per action. A connection keeps a WebSocket open so you can receive events and reuse it across calls. + +## Getting Actors + +```rust @nocheck +use counter::Counter; +use rivetkit::{ + client::{Client, ClientConfig, GetOrCreateOptions}, + prelude::*, + TypedClientExt, +}; +use serde_json::json; + +#[tokio::main] +async fn main() -> Result<()> { + let client = Client::new(ClientConfig::new("http://localhost:6420").namespace("default")); + + // Get or create an actor + let room = client.get_or_create_typed_default::("counter", ["room-42"])?; + + // Get an existing actor handle (fails when used if the actor does not exist) + let existing = client.get_typed_default::("counter", ["room-42"])?; + + // Create a new actor with input + let created = client.get_or_create_typed::( + "counter", + ["game-1"], + GetOrCreateOptions { + create_with_input: Some(json!({ "mode": "ranked" })), + ..Default::default() + }, + )?; + + // Get an actor handle by ID + let by_id = client.get_for_id("counter", "actor-id", Default::default())?; + + // Resolve the actor ID + let resolved_id = room.inner().resolve().await?; + println!("Resolved ID: {resolved_id}"); + + Ok(()) +} +``` + +`get_typed_default` / `get_or_create_typed_default` use default options. The non-default variants (`get_typed` / `get_or_create_typed`) take `GetOptions` / `GetOrCreateOptions` for connection parameters, input, and region. + +## Connection Parameters + +Pass connection parameters through the handle options. They are delivered to the actor's `create_conn_state` callback: + +```rust @nocheck +use counter::Counter; +use rivetkit::{ + client::{Client, ClientConfig, GetOrCreateOptions}, + prelude::*, + TypedClientExt, +}; +use serde_json::json; + +#[tokio::main] +async fn main() -> Result<()> { + let client = Client::new(ClientConfig::new("http://localhost:6420").namespace("default")); + + let chat = client.get_or_create_typed::( + "counter", + ["general"], + GetOrCreateOptions { + params: Some(json!({ "authToken": "jwt-token-here" })), + ..Default::default() + }, + )?; + + let connection = chat.connect(); + connection.disconnect().await; + Ok(()) +} +``` + +## Subscribing to Events + +`on` registers a typed callback for an event and returns once the subscription is registered: + +```rust @nocheck +use counter::{Counter, NewCount}; +use rivetkit::{ + client::{Client, ClientConfig}, + prelude::*, + TypedClientExt, +}; + +#[tokio::main] +async fn main() -> Result<()> { + let client = Client::new(ClientConfig::new("http://localhost:6420").namespace("default")); + let connection = client + .get_or_create_typed_default::("counter", ["general"])? + .connect(); + + connection + .on::(|event| println!("count changed: {}", event.count)) + .await; + + Ok(()) +} +``` + +Event callbacks are synchronous and run for every matching event. The actor's emitted event type (here `NewCount`) is decoded into the typed value for you. + +## Connection Lifecycle + +The lower-level connection exposes lifecycle callbacks and the current status. Reach it with `connection.inner()`: + +```rust @nocheck +use counter::Counter; +use rivetkit::{ + client::{Client, ClientConfig}, + prelude::*, + TypedClientExt, +}; + +#[tokio::main] +async fn main() -> Result<()> { + let client = Client::new(ClientConfig::new("http://localhost:6420").namespace("default")); + let connection = client + .get_or_create_typed_default::("counter", ["general"])? + .connect(); + let inner = connection.inner().clone(); + + inner.on_open(|| println!("connected")).await; + inner.on_close(|| println!("disconnected")).await; + inner.on_error(|message| eprintln!("error: {message}")).await; + inner + .on_status_change(|status| println!("status: {status:?}")) + .await; + + println!("current status: {:?}", inner.conn_status()); + + connection.disconnect().await; + Ok(()) +} +``` + +`ConnectionStatus` is one of `Idle`, `Connecting`, `Connected`, or `Disconnected`. Connections reconnect automatically with backoff until you call `disconnect`. + +## Low-Level HTTP & WebSocket + +For actors that implement `on_request` or `on_websocket`, call them directly on the untyped handle (`handle.inner()`). `fetch` returns a `reqwest::Response`, and `web_socket` returns a `tokio_tungstenite` stream. This example also needs a few extra crates: + +```sh +cargo add futures-util tokio-tungstenite +cargo add reqwest --features json +``` + +```rust @nocheck +use counter::Counter; +use futures_util::{SinkExt, StreamExt}; +use reqwest::{header::HeaderMap, Method}; +use rivetkit::{ + client::{Client, ClientConfig}, + prelude::*, + TypedClientExt, +}; +use tokio_tungstenite::tungstenite::Message; + +#[tokio::main] +async fn main() -> Result<()> { + let client = Client::new(ClientConfig::new("http://localhost:6420").namespace("default")); + let handle = client.get_or_create_typed_default::("counter", ["general"])?; + + // Raw HTTP request + let response = handle + .inner() + .fetch("history", Method::GET, HeaderMap::new(), None) + .await?; + let history: Vec = response.json().await?; + println!("history: {history:?}"); + + // Raw WebSocket connection + let mut ws = handle.inner().web_socket("stream", None).await?; + ws.send(Message::text("hello")).await?; + if let Some(message) = ws.next().await { + println!("received: {:?}", message?); + } + + Ok(()) +} +``` + +## Calling from Backend + +The client is a normal Tokio type, so you can hold it in your backend (Axum, Actix, etc.) and call actors from request handlers. The client is `Clone` and cheap to share: + +```rust @nocheck +use counter::{Counter, Increment}; +use rivetkit::{ + client::{Client, ClientConfig}, + prelude::*, + TypedClientExt, +}; + +async fn increment(client: Client) -> Result { + let counter = client.get_or_create_typed_default::("counter", ["server-counter"])?; + let count = counter.send(Increment { amount: 1 }).await?; + Ok(count) +} +``` + +## Error Handling + +Action and connection calls return `anyhow::Result`. Actor-side errors surface as an `anyhow::Error` carrying the error group, code, message, and metadata: + +```rust @nocheck +use counter::{Counter, Increment}; +use rivetkit::{ + client::{Client, ClientConfig}, + prelude::*, + TypedClientExt, +}; + +#[tokio::main] +async fn main() -> Result<()> { + let client = Client::new(ClientConfig::new("http://localhost:6420").namespace("default")); + let counter = client.get_or_create_typed_default::("counter", ["my-counter"])?; + + match counter.send(Increment { amount: 1 }).await { + Ok(count) => println!("count: {count}"), + Err(error) => eprintln!("action failed: {error:#}"), + } + + Ok(()) +} +``` + +## Concepts + +### Keys + +Keys uniquely identify actor instances. Use compound keys (arrays) for hierarchical addressing: + +```rust @nocheck +use counter::Counter; +use rivetkit::{ + client::{Client, ClientConfig}, + prelude::*, + TypedClientExt, +}; + +#[tokio::main] +async fn main() -> Result<()> { + let client = Client::new(ClientConfig::new("http://localhost:6420").namespace("default")); + + // Compound key: [org, room] + let room = client.get_or_create_typed_default::("counter", ["org-acme", "general"])?; + let actor_id = room.inner().resolve().await?; + println!("Actor ID: {actor_id}"); + + Ok(()) +} +``` + +Keys accept arrays of `&str` or `String` (`["org-acme", "general"]`). Don't build keys with string interpolation like `format!("org:{user_id}")` when `user_id` contains user data. Use arrays instead to prevent key injection attacks. + +### Configuration + +`ClientConfig::new(endpoint)` is a builder. The endpoint is always required; there is no default. Common options: + +```rust @nocheck +use rivetkit::client::ClientConfig; + +let config = ClientConfig::new("http://localhost:6420") + .namespace("default") + .token("pk_...") + .pool_name("my-pool") + .header("x-custom", "value"); +``` + +- `namespace` - target namespace (defaults to the engine's configured namespace). +- `token` - authentication token for the engine. +- `pool_name` - runner pool to target. +- `header` / `headers` - extra HTTP headers sent with each request. +- `max_input_size` - cap on encoded action input size. + +For serverless deployments, set the endpoint to your app's `/api/rivet` URL. See [Endpoints](/docs/general/endpoints) for details. + +## API Reference + +See the full client API documentation on [docs.rs/rivetkit-client](https://docs.rs/rivetkit-client/latest/rivetkit_client/). + +_Source doc path: /docs/clients/rust_ diff --git a/.agents/skills/live-cursors/reference/clients/swift.md b/.agents/skills/live-cursors/reference/clients/swift.md new file mode 100644 index 0000000..c760645 --- /dev/null +++ b/.agents/skills/live-cursors/reference/clients/swift.md @@ -0,0 +1,454 @@ +# Swift + +> Source: `src/content/docs/clients/swift.mdx` +> Canonical URL: https://rivet.dev/docs/clients/swift +> Description: Connect Swift apps to Rivet Actors. + +--- +## Install + +Add the Swift package dependency and import `RivetKitClient`: + +```swift +// Package.swift +dependencies: [ + .package(url: "https://github.com/rivet-dev/rivetkit-swift", from: "2.0.0") +] + +targets: [ + .target( + name: "MyApp", + dependencies: [ + .product(name: "RivetKitClient", package: "rivetkit-swift") + ] + ) +] +``` + +## Minimal Client + +### Endpoint URL + +```swift +import RivetKitClient + +let config = try ClientConfig( + endpoint: "https://my-namespace:pk_...@api.rivet.dev" +) +let client = RivetKitClient(config: config) + +let handle = client.getOrCreate("counter", ["my-counter"]) +let count: Int = try await handle.action("increment", 1, as: Int.self) +``` + +### Explicit Fields + +```swift +import RivetKitClient + +let config = try ClientConfig( + endpoint: "https://api.rivet.dev", + namespace: "my-namespace", + token: "pk_..." +) +let client = RivetKitClient(config: config) + +let handle = client.getOrCreate("counter", ["my-counter"]) +let count: Int = try await handle.action("increment", 1, as: Int.self) +``` + +## Stateless vs Stateful + +```swift +import RivetKitClient + +let config = try ClientConfig(endpoint: "http://localhost:6420") +let client = RivetKitClient(config: config) + +let handle = client.getOrCreate("counter", ["my-counter"]) + +// Stateless: each call is independent +let current: Int = try await handle.action("getCount", as: Int.self) +print("Current count: \(current)") + +// Stateful: keep a connection open for realtime events +let conn = handle.connect() + +// Subscribe to events using AsyncStream +let eventTask = Task { + for await count in await conn.events("count", as: Int.self) { + print("Event: \(count)") + } +} + +_ = try await conn.action("increment", 1, as: Int.self) + +eventTask.cancel() +await conn.dispose() +await client.dispose() +``` + +## Getting Actors + +```swift +import RivetKitClient + +struct GameInput: Encodable { + let mode: String +} + +let config = try ClientConfig(endpoint: "http://localhost:6420") +let client = RivetKitClient(config: config) + +// Get or create an actor +let room = client.getOrCreate("chatRoom", ["room-42"]) + +// Get an existing actor (fails if not found) +let existing = client.get("chatRoom", ["room-42"]) + +// Create a new actor with input +let created = try await client.create( + "game", + ["game-1"], + options: CreateOptions(input: GameInput(mode: "ranked")) +) + +// Get actor by ID +let byId = client.getForId("chatRoom", "actor-id") + +// Resolve actor ID +let resolvedId = try await room.resolve() +print("Resolved ID: \(resolvedId)") + +await client.dispose() +``` + +Actions support positional overloads for 0–5 args: + +```swift +import RivetKitClient + +let config = try ClientConfig(endpoint: "http://localhost:6420") +let client = RivetKitClient(config: config) +let handle = client.getOrCreate("counter", ["my-counter"]) + +let count: Int = try await handle.action("getCount") +let updated: String = try await handle.action("rename", "new-name") +let ok: Bool = try await handle.action("setScore", "user-1", 42) + +print("Count: \(count), Updated: \(updated), OK: \(ok)") +await client.dispose() +``` + +If you need more than 5 arguments, use the raw JSON fallback: + +```swift +import RivetKitClient + +let config = try ClientConfig(endpoint: "http://localhost:6420") +let client = RivetKitClient(config: config) +let handle = client.getOrCreate("counter", ["my-counter"]) + +let args: [JSONValue] = [ + .string("user-1"), + .number(.int(42)), + .string("extra"), + .string("more"), + .string("args"), + .string("here") +] +let ok: Bool = try await handle.action("setScore", args: args, as: Bool.self) +print("OK: \(ok)") + +await client.dispose() +``` + +## Connection Parameters + +```swift +import RivetKitClient + +struct ConnParams: Encodable { + let authToken: String +} + +let config = try ClientConfig(endpoint: "http://localhost:6420") +let client = RivetKitClient(config: config) + +let chat = client.getOrCreate( + "chatRoom", + ["general"], + options: GetOrCreateOptions(params: ConnParams(authToken: "jwt-token-here")) +) + +let conn = chat.connect() + +// Use the connection... +for await status in await conn.statusChanges() { + print("Status: \(status.rawValue)") + if status == .connected { + break + } +} + +await conn.dispose() +await client.dispose() +``` + +## Subscribing to Events + +```swift +import RivetKitClient + +let config = try ClientConfig(endpoint: "http://localhost:6420") +let client = RivetKitClient(config: config) +let conn = client.getOrCreate("chatRoom", ["general"]).connect() + +// Subscribe to events using AsyncStream +let messageTask = Task { + for await (from, body) in await conn.events("message", as: (String, String).self) { + print("\(from): \(body)") + } +} + +// For one-time events, break after receiving +let gameOverTask = Task { + for await _ in await conn.events("gameOver", as: Void.self) { + print("done") + break + } +} + +// Let it run for a bit +try await Task.sleep(for: .seconds(5)) + +// Cancel when done +messageTask.cancel() +gameOverTask.cancel() +await conn.dispose() +await client.dispose() +``` + +Event streams support 0–5 typed arguments. If you need raw values or more than 5 arguments, use `JSONValue`: + +```swift +import RivetKitClient + +let config = try ClientConfig(endpoint: "http://localhost:6420") +let client = RivetKitClient(config: config) +let conn = client.getOrCreate("chatRoom", ["general"]).connect() + +let rawTask = Task { + for await args in await conn.events("message") { + print(args) + } +} + +try await Task.sleep(for: .seconds(5)) +rawTask.cancel() +await conn.dispose() +await client.dispose() +``` + +## Connection Lifecycle + +```swift +import RivetKitClient + +let config = try ClientConfig(endpoint: "http://localhost:6420") +let client = RivetKitClient(config: config) +let conn = client.getOrCreate("chatRoom", ["general"]).connect() + +// Monitor status changes (immediately yields current status) +let statusTask = Task { + for await status in await conn.statusChanges() { + print("status: \(status.rawValue)") + } +} + +// Monitor errors +let errorTask = Task { + for await error in await conn.errors() { + print("error: \(error.group).\(error.code)") + } +} + +// Monitor open/close events +let openTask = Task { + for await _ in await conn.opens() { + print("connected") + } +} + +let closeTask = Task { + for await _ in await conn.closes() { + print("disconnected") + } +} + +// Check current status +let current = await conn.currentStatus +print("Current status: \(current.rawValue)") + +// Let it run for a bit +try await Task.sleep(for: .seconds(5)) + +// Cleanup +statusTask.cancel() +errorTask.cancel() +openTask.cancel() +closeTask.cancel() +await conn.dispose() +await client.dispose() +``` + +## Low-Level HTTP & WebSocket + +For actors that implement `onRequest` or `onWebSocket`, you can call them directly: + +```swift +import RivetKitClient + +let config = try ClientConfig(endpoint: "http://localhost:6420") +let client = RivetKitClient(config: config) +let handle = client.getOrCreate("chatRoom", ["general"]) + +// Raw HTTP request +let response = try await handle.fetch("history") +let history: [String] = try response.json([String].self) +print("History: \(history)") + +// Raw WebSocket connection +let websocket = try await handle.websocket(path: "stream") +try await websocket.send(text: "hello") +let message = try await websocket.receive() +print("Received: \(message)") + +await client.dispose() +``` + +## Calling from Backend + +Use the same client in server-side Swift (Vapor, Hummingbird, etc.): + +```swift +import RivetKitClient + +let config = try ClientConfig(endpoint: "http://localhost:6420") +let client = RivetKitClient(config: config) + +let handle = client.getOrCreate("counter", ["server-counter"]) +let count: Int = try await handle.action("increment", 1, as: Int.self) +print("Count: \(count)") + +await client.dispose() +``` + +## Error Handling + +```swift +import RivetKitClient + +let config = try ClientConfig(endpoint: "http://localhost:6420") +let client = RivetKitClient(config: config) + +do { + _ = try await client.getOrCreate("user", ["user-123"]) + .action("updateUsername", "ab", as: String.self) +} catch let error as ActorError { + print("Error code: \(error.code)") + print("Metadata: \(String(describing: error.metadata))") +} + +await client.dispose() +``` + +If you need an untyped response, you can decode to `JSONValue`: + +```swift +import RivetKitClient + +let config = try ClientConfig(endpoint: "http://localhost:6420") +let client = RivetKitClient(config: config) +let handle = client.getOrCreate("data", ["raw"]) + +let value: JSONValue = try await handle.action("getRawPayload") +print("Raw value: \(value)") + +await client.dispose() +``` + +## Concepts + +### Keys + +Keys uniquely identify actor instances. Use compound keys (arrays) for hierarchical addressing: + +```swift +import RivetKitClient + +let config = try ClientConfig(endpoint: "http://localhost:6420") +let client = RivetKitClient(config: config) + +// Use compound keys for hierarchical addressing +let room = client.getOrCreate("chatRoom", ["org-acme", "general"]) +let actorId = try await room.resolve() +print("Actor ID: \(actorId)") + +await client.dispose() +``` + +Don't build keys with string interpolation like `"org:\(userId)"` when `userId` contains user data. Use arrays instead to prevent key injection attacks. + +### Environment Variables + +`ClientConfig` reads optional values from environment variables: + +- `RIVET_NAMESPACE` - Namespace (can also be in endpoint URL) +- `RIVET_TOKEN` - Authentication token (can also be in endpoint URL) +- `RIVET_RUNNER` - Runner name (defaults to `"default"`) + +The `endpoint` parameter is always required. There is no default endpoint. + +### Endpoint Format + +Endpoints support URL auth syntax: + +``` +https://namespace:token@api.rivet.dev +``` + +You can also pass the endpoint without auth and provide `RIVET_NAMESPACE` and `RIVET_TOKEN` separately. For serverless deployments, set the endpoint to your app's `/api/rivet` URL. See [Endpoints](/docs/general/endpoints#url-auth-syntax) for details. + +## API Reference + +### Client +- `RivetKitClient(config:)` - Create a client with a config +- `ClientConfig` - Configure endpoint, namespace, and token +- `client.get()` / `getOrCreate()` / `getForId()` / `create()` - Get actor handles +- `client.dispose()` - Dispose the client and all connections + +### ActorHandle +- `handle.action(name, args..., as:)` - Stateless action call +- `handle.connect()` - Create a stateful connection +- `handle.resolve()` - Get the actor ID +- `handle.getGatewayUrl()` - Get the raw gateway URL +- `handle.fetch(path, request:)` - Raw HTTP request +- `handle.websocket(path:)` - Raw WebSocket connection + +### ActorConnection +- `conn.action(name, args..., as:)` - Action call over WebSocket +- `conn.events(name, as:)` - AsyncStream of typed events +- `conn.statusChanges()` - AsyncStream of status changes +- `conn.errors()` - AsyncStream of connection errors +- `conn.opens()` - AsyncStream that yields on connection open +- `conn.closes()` - AsyncStream that yields on connection close +- `conn.currentStatus` - Current connection status +- `conn.dispose()` - Close the connection + +### Types +- `ActorConnStatus` - Connection status enum (`.idle`, `.connecting`, `.connected`, `.disconnected`, `.disposed`) +- `ActorError` - Typed actor errors with `group`, `code`, `message`, `metadata` +- `JSONValue` - Raw JSON value for untyped responses + +_Source doc path: /docs/clients/swift_ diff --git a/.agents/skills/live-cursors/reference/clients/swiftui.md b/.agents/skills/live-cursors/reference/clients/swiftui.md new file mode 100644 index 0000000..4ae65d8 --- /dev/null +++ b/.agents/skills/live-cursors/reference/clients/swiftui.md @@ -0,0 +1,359 @@ +# SwiftUI + +> Source: `src/content/docs/clients/swiftui.mdx` +> Canonical URL: https://rivet.dev/docs/clients/swiftui +> Description: Build SwiftUI apps with Rivet Actors. + +--- +## Install + +Add the Swift package dependency and import `RivetKitSwiftUI`: + +```swift +// Package.swift +dependencies: [ + .package(url: "https://github.com/rivet-dev/rivetkit-swift", from: "2.0.0") +] + +targets: [ + .target( + name: "MyApp", + dependencies: [ + .product(name: "RivetKitSwiftUI", package: "rivetkit-swift") + ] + ) +] +``` + +`RivetKitSwiftUI` re-exports `RivetKitClient` and `SwiftUI`, so a single import covers both. + +## Minimal Client + +```swift HelloWorldApp.swift +import RivetKitSwiftUI +import SwiftUI + +@main +struct HelloWorldApp: App { + var body: some Scene { + WindowGroup { + ContentView() + .rivetKit(endpoint: "https://my-namespace:pk_...@api.rivet.dev") + } + } +} +``` + +```swift ContentView.swift +import RivetKitSwiftUI +import SwiftUI + +struct ContentView: View { + @Actor("counter", key: ["my-counter"]) private var counter + @State private var count = 0 + + var body: some View { + VStack(spacing: 16) { + Text("\(count)") + .font(.system(size: 64, weight: .bold, design: .rounded)) + + Button("Increment") { + counter.send("increment", 1) + } + .disabled(!counter.isConnected) + } + .task { + count = (try? await counter.action("getCount")) ?? 0 + } + .onActorEvent(counter, "newCount") { (newCount: Int) in + count = newCount + } + } +} +``` + +## Actor Options + +The `@Actor` property wrapper always uses get-or-create semantics and accepts: + +- `name` (required) +- `key` as `String` or `[String]` (required) +- `params` (optional connection parameters) +- `createWithInput` (optional creation input) +- `createInRegion` (optional creation hint) +- `enabled` (toggle connection lifecycle) + +```swift +import RivetKitSwiftUI +import SwiftUI + +struct ConnParams: Encodable { + let authToken: String +} + +struct ChatView: View { + @Actor( + "chatRoom", + key: ["general"], + params: ConnParams(authToken: "jwt-token"), + enabled: true + ) private var chat + + var body: some View { + Text("Chat: \(chat.connStatus.rawValue)") + } +} +``` + +## Actions + +```swift +import RivetKitSwiftUI +import SwiftUI + +struct CounterView: View { + @Actor("counter", key: ["my-counter"]) private var counter + @State private var count = 0 + @State private var name = "" + + var body: some View { + VStack { + Text("Count: \(count)") + Text("Name: \(name)") + + Button("Fetch") { + Task { + count = try await counter.action("getCount") + name = try await counter.action("rename", "new-name") + } + } + + Button("Increment") { + counter.send("increment", 1) + } + } + } +} +``` + +## Subscribing to Events + +```swift +import RivetKitSwiftUI +import SwiftUI + +struct GameView: View { + @Actor("game", key: ["game-1"]) private var game + @State private var count = 0 + @State private var isGameOver = false + + var body: some View { + VStack { + Text("Count: \(count)") + if isGameOver { + Text("Game Over!") + } + } + .onActorEvent(game, "newCount") { (newCount: Int) in + count = newCount + } + .onActorEvent(game, "gameOver") { + isGameOver = true + } + } +} +``` + +## Async Event Streams + +```swift +import RivetKitSwiftUI +import SwiftUI + +struct ChatView: View { + @Actor("chatRoom", key: ["general"]) private var chat + @State private var messages: [String] = [] + + var body: some View { + List(messages, id: \.self) { message in + Text(message) + } + .task { + for await message in chat.events("message", as: String.self) { + messages.append(message) + } + } + } +} +``` + +## Connection Status + +```swift +import RivetKitSwiftUI +import SwiftUI + +struct StatusView: View { + @Actor("counter", key: ["my-counter"]) private var counter + @State private var count = 0 + + var body: some View { + VStack { + Text("Status: \(counter.connStatus.rawValue)") + + if counter.connStatus == .connected { + Text("Connected!") + .foregroundStyle(.green) + } + + Button("Fetch via Handle") { + Task { + if let handle = counter.handle { + count = try await handle.action("getCount", as: Int.self) + } + } + } + .disabled(!counter.isConnected) + } + } +} +``` + +## Error Handling + +```swift +import RivetKitSwiftUI +import SwiftUI + +struct UserView: View { + @Actor("user", key: ["user-123"]) private var user + @State private var errorMessage: String? + @State private var username = "" + + var body: some View { + VStack { + TextField("Username", text: $username) + + Button("Update Username") { + Task { + do { + let _: String = try await user.action("updateUsername", username) + } catch let error as ActorError { + errorMessage = "\(error.code): \(String(describing: error.metadata))" + } + } + } + + if let errorMessage { + Text(errorMessage) + .foregroundStyle(.red) + } + } + .onActorError(user) { error in + errorMessage = "\(error.group).\(error.code): \(error.message)" + } + } +} +``` + +## Concepts + +### Keys + +Keys uniquely identify actor instances. Use compound keys (arrays) for hierarchical addressing: + +```swift +import RivetKitSwiftUI +import SwiftUI + +struct OrgChatView: View { + @Actor("chatRoom", key: ["org-acme", "general"]) private var room + + var body: some View { + Text("Room: \(room.connStatus.rawValue)") + } +} +``` + +Don't build keys with string interpolation like `"org:\(userId)"` when `userId` contains user data. Use arrays instead to prevent key injection attacks. + +### Environment Configuration + +Call `.rivetKit(endpoint:)` or `.rivetKit(client:)` once at the root of your view tree: + +```swift +// With endpoint string (recommended for most apps) +@main +struct MyApp: App { + var body: some Scene { + WindowGroup { + ContentView() + .rivetKit(endpoint: "https://my-namespace:pk_...@api.rivet.dev") + } + } +} + +// With custom client (for advanced configuration) +@main +struct MyApp: App { + private let client = RivetKitClient( + config: try! ClientConfig(endpoint: "https://api.rivet.dev", token: "pk_...") + ) + + var body: some Scene { + WindowGroup { + ContentView() + .rivetKit(client: client) + } + } +} +``` + +When using `.rivetKit(endpoint:)`, the client is created once and cached per endpoint. When using `.rivetKit(client:)`, store the client as a property on `App` (not inside `body`) since SwiftUI can call `body` multiple times. + +### Environment Variables + +`ClientConfig` reads optional values from environment variables: + +- `RIVET_NAMESPACE` - Namespace (can also be in endpoint URL) +- `RIVET_TOKEN` - Authentication token (can also be in endpoint URL) +- `RIVET_RUNNER` - Runner name (defaults to `"default"`) + +The endpoint is always required. There is no default endpoint. + +### Endpoint Format + +Endpoints support URL auth syntax: + +``` +https://namespace:token@api.rivet.dev +``` + +You can also pass the endpoint without auth and provide `RIVET_NAMESPACE` and `RIVET_TOKEN` separately. For serverless deployments, set the endpoint to your app's `/api/rivet` URL. See [Endpoints](/docs/general/endpoints#url-auth-syntax) for details. + +## API Reference + +### Property Wrapper +- `@Actor(name, key:, params:, createWithInput:, createInRegion:, enabled:)` - SwiftUI property wrapper for actor connections + +### View Modifiers +- `.rivetKit(endpoint:)` - Configure client with an endpoint URL (creates cached client) +- `.rivetKit(client:)` - Configure client with a custom instance +- `.onActorEvent(actor, event) { ... }` - Subscribe to actor events (supports 0–5 typed args) +- `.onActorError(actor) { error in ... }` - Handle actor errors + +### ActorObservable +- `actor.action(name, args..., as:)` - Async action call +- `actor.send(name, args...)` - Fire-and-forget action +- `actor.events(name, as:)` - AsyncStream of typed events +- `actor.connStatus` - Current connection status +- `actor.isConnected` - Whether connected +- `actor.handle` - Underlying `ActorHandle` (optional) +- `actor.connection` - Underlying `ActorConnection` (optional) +- `actor.error` - Most recent error (optional) + +### Types +- `ActorConnStatus` - Connection status enum (`.idle`, `.connecting`, `.connected`, `.disconnected`, `.disposed`) +- `ActorError` - Typed actor errors with `group`, `code`, `message`, `metadata` + +_Source doc path: /docs/clients/swiftui_ diff --git a/.agents/skills/live-cursors/reference/cookbook/ai-agent.md b/.agents/skills/live-cursors/reference/cookbook/ai-agent.md new file mode 100644 index 0000000..1adb285 --- /dev/null +++ b/.agents/skills/live-cursors/reference/cookbook/ai-agent.md @@ -0,0 +1,127 @@ +# AI Agent + +> Source: `src/content/cookbook/ai-agent.mdx` +> Canonical URL: https://rivet.dev/cookbook/ai-agent +> Description: Build an AI agent backend with persistent memory: one Rivet Actor per conversation, queued message handling, and streaming LLM responses as realtime events. + +--- +Patterns for building AI agent backends with RivetKit, where each conversation is one Rivet Actor that owns its memory, its message queue, and its streaming output. + +## Starter Code + +Start with one of the working examples on [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/ai-agent) and adapt it. The sections below describe the flagship `ai-agent` example unless a variant is called out explicitly. + +| Variant | Starter Code | Use When | +| --- | --- | --- | +| Queue-driven AI SDK agent | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/ai-agent) | You want a streaming chat agent where each conversation keeps its own persistent memory and processes one message at a time. | +| Sandbox coding agent | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/sandbox-coding-agent) | The agent should run a coding agent (Codex by default) inside an isolated [sandbox](/docs/actors/sandbox) via Docker, Daytona, or E2B. | +| Durable streams agent (experimental) | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/experimental-durable-streams-ai-agent) | You want replayable, restart-safe prompt and response delivery through durable streams instead of actor state and events. | + +## Conversation Memory + +Use one actor per conversation, keyed by a conversation or agent id (see [Actor Keys](/docs/actors/keys)). The agent actor's persistent [state](/docs/actors/state) is the conversation memory: in the `ai-agent` example, `messages` and `status` live in JSON actor state and survive sleep and restarts with no external database. Every model call rebuilds the prompt from `c.state.messages` plus a system prompt, so memory and inference input are the same data. + +| Variant | Where Memory Lives | Persisted State Fields | +| --- | --- | --- | +| `ai-agent` | JSON actor state | `messages`, `status` | +| `sandbox-coding-agent` | JSON actor state plus the sandbox ACP session | `messages`, `status`, `sessionId` | +| `experimental-durable-streams-ai-agent` | Durable streams; the actor stores only its conversation id and a read cursor | `conversationId`, `promptStreamOffset` | + +## Message Handling + +In the `ai-agent` example, the client pushes user input onto the agent's `message` [queue](/docs/actors/queues) with `agent.connection.send("message", { text, sender })`. This is a queue push, not an action call. The actor's `run` hook (see [Lifecycle](/docs/actors/lifecycle)) consumes the queue serially with `for await (const queued of c.queue.iter())`. + +Serial queue consumption is the per-conversation concurrency guarantee: at most one in-flight model call per actor, with no extra locking. The `status` field (`thinking` while a model call is in flight) is UI signal only; the run loop is the actual lock. The loop also checks `c.aborted` inside the token stream so shutdown exits gracefully. + +| Variant | Message Ingress | Serialization Guarantee | +| --- | --- | --- | +| `ai-agent` | `message` queue pushed via `connection.send` | `run` hook pops one queued message at a time with `c.queue.iter()`. | +| `sandbox-coding-agent` | `sendMessage` [action](/docs/actors/actions), no queue | Each call awaits the sandbox round trip before broadcasting the result. | +| `experimental-durable-streams-ai-agent` | Durable prompt stream long-polled from `onWake` | `promptStreamOffset` is persisted per chunk, so restarts resume without reprocessing prompts. | + +## Streaming Responses + +The `ai-agent` actor broadcasts a `response` [event](/docs/actors/events) for every model text delta. The payload carries `messageId`, the per-token `delta`, the cumulative `content`, and a `done` flag (plus `error` on failure), so clients can either append deltas or idempotently replace the message by `messageId` using `content`. The example frontend replaces by `messageId`, which tolerates dropped events. The terminal broadcast has an empty `delta`, the full `content`, and `done: true`. + +Because the assistant message object lives in `c.state.messages` and is mutated in place during streaming, partial content persists if the actor restarts mid-stream. The example broadcasts once per AI SDK delta with no throttling; batching or throttling deltas is a recommended extension for high-traffic deployments, not something the example implements. + +Variant differences: `sandbox-coding-agent` sends a single `response` broadcast with `done: true` after the sandbox finishes (no incremental streaming), and `experimental-durable-streams-ai-agent` appends per-token chunks to a durable response stream, then broadcasts `responseComplete` or `responseError`. + +## Architecture + +| Topic | Summary | +| --- | --- | +| Topology | `agentManager["primary"]` singleton directory plus one `agent[agentId]` actor per conversation. | +| Ingress | Client pushes `AgentQueueMessage` payloads onto the agent's `message` queue with `connection.send`. | +| Streaming | One `response` broadcast per model delta, terminal broadcast with `done: true`. | +| Memory | Full transcript and status in JSON actor state; no external database. | + +The manager creates `AgentInfo` records and warms each agent through [actor-to-actor communication](/docs/actors/communicating-between-actors): `createAgent` calls `c.client()`, then `client.agent.getOrCreate([info.id])` and awaits `getStatus()` so the conversation actor exists before the client connects. The sandbox variant extends this topology with a `codingSandbox` actor that shares the agent's key (`codingSandbox.getOrCreate([c.key[0]])`), so the agent-to-sandbox mapping is implicit in the key space. + +**Actors** + +- **Key**: `agentManager["primary"]` +- **Responsibility**: Directory actor. Creates `AgentInfo` records, lists agents, and warms each agent actor via `c.client()`. +- **Actions** + - `createAgent` + - `listAgents` +- **Queues** + - None +- **State** + - JSON + - `agents` + +- **Key**: `agent[agentId]` +- **Responsibility**: One actor per conversation. Holds the full message history and status, consumes queued user messages in its `run` loop, calls the model via the AI SDK, and broadcasts streaming deltas. +- **Actions** + - `getHistory` + - `getStatus` +- **Queues** + - `message` +- **Events** + - `messageAdded` + - `status` + - `response` +- **State** + - JSON + - `messages` + - `status` + +**Lifecycle** + +```mermaid +sequenceDiagram + participant C as Client + participant AM as agentManager + participant A as agent + participant LLM as Model API + + C->>AM: createAgent(name) + AM->>A: getOrCreate([info.id]) + getStatus() + AM-->>C: AgentInfo + C->>A: connection.send("message", {text, sender}) + Note over A: run loop pops queue via c.queue.iter() + A-->>C: messageAdded (user message) + A-->>C: messageAdded (assistant placeholder) + A-->>C: status (thinking) + A->>LLM: streamText(system prompt + history) + loop each text delta + LLM-->>A: delta + A-->>C: response {messageId, delta, content, done: false} + end + A-->>C: response {delta: "", content, done: true} + A-->>C: status (idle) +``` + +## Security Checklist + +The examples ship without auth so they stay minimal. Apply this baseline before exposing an agent backend. + +- **API keys stay server-side**: `OPENAI_API_KEY` (or `ANTHROPIC_API_KEY`) is read by the AI SDK inside the actor process. The key never reaches the browser; clients only talk to the actor over RivetKit. The sandbox variant forwards keys into the sandbox env, never to the client. +- **Add authentication**: The examples have no auth, so anyone who reaches the server can create agents, list them, and message any agent whose key they can guess. Add `onBeforeConnect` or `createConnState` checks with scoped tokens as a recommended extension. See [Authentication](/docs/actors/authentication). +- **Validate and rate-limit queue payloads**: The example only skips bodies without a string `text`. Enforce payload size limits, schema validation, and per-connection rate limits as a recommended extension. +- **Derive sender identity server-side**: The example trusts the client-supplied `sender` field verbatim. Bind sender identity to the authenticated connection instead. +- **Cap or trim message history**: The example sends the full transcript on every model call with no cap. Trim or summarize old messages as a recommended extension so prompts and state stay bounded. +- **Set cost ceilings per conversation**: Add per-agent token budgets and quotas as a recommended extension. The sandbox variant runs real compute, so also enforce per-user sandbox quotas and restrict sandbox network egress. + +_Source doc path: /cookbook/ai-agent_ diff --git a/.agents/skills/live-cursors/reference/cookbook/chat-room.md b/.agents/skills/live-cursors/reference/cookbook/chat-room.md new file mode 100644 index 0000000..cb8dbe4 --- /dev/null +++ b/.agents/skills/live-cursors/reference/cookbook/chat-room.md @@ -0,0 +1,114 @@ +# Chat Room + +> Source: `src/content/cookbook/chat-room.mdx` +> Canonical URL: https://rivet.dev/cookbook/chat-room +> Description: Build a realtime chat room backend with Rivet Actors: one actor per room, SQLite-backed message history, and WebSocket broadcast to every connected client. + +--- +Patterns for building a chat room backend with RivetKit: room-scoped actors, persistent message history, and realtime delivery over WebSocket connections. + +## Starter Code + +Start with the working example on [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/chat-room) and adapt it. The backend is a single `chatRoom` actor; the frontend is a React app using `@rivetkit/react` (see the [React quickstart](/docs/actors/quickstart/react)). + +| Topic | Summary | +| --- | --- | +| Room model | One `chatRoom` actor per room key. The frontend defaults the key to `general`; typing a different room name connects to a different actor. | +| History | SQLite `messages` table created in `db({ onMigrate })`, read back with `ORDER BY id ASC`. | +| Delivery | `sendMessage` inserts the row, then broadcasts a typed `newMessage` event to every connected client. | +| Identity | None in the example. `sender` is a plain action argument; production should bind identity to the connection. | + +## Room-Per-Actor Model + +Each room is one Rivet Actor instance, addressed by [key](/docs/actors/keys). The client calls `useActor({ name: "chatRoom", key: [roomId] })`, which gets-or-creates the actor for that room. This gives you: + +- **Isolation**: each room's history and connections are fully scoped to its key. Switching the room input re-keys the hook and connects to a different actor with separate history. +- **A single serialized writer**: all `sendMessage` calls for one room run through one actor, so message ordering is consistent without locks. The SQLite `AUTOINCREMENT` id is the canonical order, which is why `getHistory` sorts by `id` rather than by timestamp. +- **Natural scaling**: rooms spread across the cluster independently. A hot room does not slow down other rooms. + +## Message History Storage + +This example stores history in the actor's SQLite database, not in JSON state. Pick based on history size and query needs: + +| Approach | Use When | Implementation Guidance | +| --- | --- | --- | +| [SQLite](/docs/actors/sqlite) (what this example uses) | Large or long-lived history that needs ordering, caps, pagination, or search | Create the `messages` table in `db({ onMigrate })`, insert with parameterized queries (`c.db.execute("INSERT ... VALUES (?, ?, ?)", ...)`), and read with `ORDER BY id ASC`. History survives actor sleep and scales past what you want in memory. | +| [JSON state](/docs/actors/state) | Small recent history, for example the last 50 to 100 messages | Push onto a `messages` array in actor state and trim to a cap on every send. Simplest option, but the whole history lives in memory and there is no query layer, so it only fits bounded recent-history use cases. | + +## Broadcast Delivery + +New messages reach connected clients through a typed [event](/docs/actors/events): + +- The actor declares `events: { newMessage: event() }`, where `Message` is `{ sender, text, timestamp }`. +- The `sendMessage` [action](/docs/actors/actions) builds the message with a server-side `Date.now()` timestamp, inserts it into the `messages` table, then calls `c.broadcast("newMessage", message)` and returns the message to the caller. +- Each client subscribes with `useEvent("newMessage", ...)` and appends to its local list. The sender renders its own message through the same broadcast path as everyone else, so all clients stay on one code path. +- History load is connection-gated: once the connection is ready, the client calls `getHistory()` once to render the backlog, then relies on events for everything after. + +Use `c.broadcast(...)` for room-wide messages. For private or per-recipient payloads (such as DMs inside a room), send on the individual connection instead, which is a recommended extension beyond this example. + +## Typing Indicators And Presence (Extension) + +The example does not implement typing indicators, presence, or join/leave handling of any kind. There is no `createConnState`, `onConnect`, or `onDisconnect` in the code. If you need them, add them as ephemeral [connection](/docs/actors/connections) behavior: + +- **Keep it ephemeral**: store the username and typing flag in per-connection state, never in SQLite or persisted actor state. Presence is derived from live connections and should disappear with them. +- **Broadcast on change only**: emit a typing event when a user starts or stops typing, and a presence event from `onConnect` / `onDisconnect`, rather than polling or ticking. +- **Expire on the client**: clear a typing indicator after a short client-side timeout so a dropped connection never leaves a stuck "is typing" row. + +## Per-User Inbox (Extension) + +For offline delivery, DMs, unread counts, or notification fanout, add a `userInbox[userId]` actor per user. This is an extension beyond the example: + +- The room actor forwards each message to the inbox actor of every member via [actor-to-actor calls](/docs/actors/communicating-between-actors), so users who are not connected to the room still accumulate messages. +- The inbox actor owns per-user unread state and serves it when the user comes online, independent of which rooms they are in. +- DMs become a degenerate room: either a `chatRoom` keyed by the sorted pair of user ids, or direct inbox-to-inbox delivery if you do not need shared history semantics. + +## Actors + +- **Key**: `chatRoom[roomId]` +- **Responsibility**: Owns one chat room. Persists the room's message history in its SQLite database and broadcasts each new message to every connected client. +- **Actions** + - `sendMessage` + - `getHistory` +- **Queues** + - None +- **Events** + - `newMessage` +- **State** + - SQLite + - `messages` table: `id` (autoincrement primary key), `sender`, `text`, `timestamp` + +## Lifecycle + +```mermaid +sequenceDiagram + participant A as Client A + participant B as Client B + participant R as chatRoom + + A->>R: connect with key [roomId] + Note over R: every start runs onMigrate (CREATE TABLE IF NOT EXISTS messages) + A->>R: getHistory() + R-->>A: Message[] ordered by id + B->>R: connect with key [roomId] + B->>R: getHistory() + R-->>B: Message[] ordered by id + A->>R: sendMessage(sender, text) + Note over R: INSERT row with server timestamp + R-->>A: newMessage (broadcast) + R-->>B: newMessage (broadcast) + A->>R: disconnect + Note over R: history stays in SQLite for the next connection +``` + +## Security Checklist + +The example is intentionally minimal and skips all of the following. Add them before production: + +- **Auth before join**: any client can join any room by knowing its name, and `sender` is arbitrary client input on every call. Validate a token during [connection auth](/docs/actors/authentication), bind identity to [connection state](/docs/actors/connections), and check room membership before serving history. Never trust a sender name passed as an action argument. +- **Message length clamps**: the example accepts empty messages and has no length limit. Trim server-side, reject empty text, and clamp to a maximum length. +- **Per-connection rate limiting**: rate limit `sendMessage` per connection to stop spam and broadcast amplification. +- **Server-side timestamps and ids**: the example already does this correctly. `timestamp` comes from `Date.now()` inside the action and `id` from SQLite `AUTOINCREMENT`. Keep it that way; never accept client-supplied timestamps or ids. +- **History caps**: `getHistory` returns every row with no limit. Add a `LIMIT` plus pagination, and prune or archive old rows so a long-lived room cannot grow unbounded. +- **Parameterized queries**: the example already inserts with `?` placeholders. Keep all user-supplied text out of SQL string interpolation. + +_Source doc path: /cookbook/chat-room_ diff --git a/.agents/skills/live-cursors/reference/cookbook/collaborative-text-editor.md b/.agents/skills/live-cursors/reference/cookbook/collaborative-text-editor.md new file mode 100644 index 0000000..cacbc1f --- /dev/null +++ b/.agents/skills/live-cursors/reference/cookbook/collaborative-text-editor.md @@ -0,0 +1,157 @@ +# Collaborative Text Editor + +> Source: `src/content/cookbook/collaborative-text-editor.mdx` +> Canonical URL: https://rivet.dev/cookbook/collaborative-text-editor +> Description: Build a collaborative text editor backend with Yjs CRDTs and Rivet Actors: per-document actors relay sync and awareness updates and persist snapshots. + +--- +Patterns for building a Yjs server on RivetKit: CRDT document sync, presence and cursors, and snapshot persistence, with one Rivet Actor per document acting as a relay. + +## Starter Code + +Start with the working example on [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/collaborative-document) and adapt it to your editor. It ships a React frontend with a plain textarea, remote cursor overlays, and a workspace document index. + +| Use Case | Starter Code | Common Examples | +| --- | --- | --- | +| Shared document editing | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/collaborative-document) | Notion-style docs, shared notes, pair-writing tools, form co-editing | + +## CRDT vs OT + +Two families of algorithms solve concurrent text editing. The choice decides what your server has to do. + +| Dimension | CRDT (Yjs) | Operational Transformation | +| --- | --- | --- | +| Conflict resolution model | Commutative merges. Updates apply in any order on any peer and converge to the same result. | Server transforms each operation against every concurrent operation. Correctness depends on a central sequencer. | +| Offline support | Strong. Clients keep editing locally and merge buffered updates on reconnect. | Weak. Long-lived divergence makes transformation chains complex and fragile. | +| Server role | Relay plus persistence. The server applies opaque updates and rebroadcasts them. It never needs to understand document semantics. | Authoritative transformer. The server must implement transformation logic for every operation type. | +| Library maturity | Yjs is mature and widely deployed, with bindings for ProseMirror, CodeMirror, Monaco, and others. | Production-grade implementations are mostly proprietary (Google Docs) or aging (ShareDB). | + +The example uses Yjs because CRDTs let the server stay a relay-style Rivet Actor. The actor applies each incoming update to a server-side `Y.Doc` so it can persist the merged state and serve late joiners, but it never transforms operations or arbitrates conflicts. Ordering does not matter because Yjs merges are commutative. + +## Document Actor Model + +| Topic | Summary | +| --- | --- | +| Topology | One `document[workspaceId, documentId]` actor per document plus one `documentList[workspaceId]` coordinator per workspace. | +| Sync model | Each client holds a local `Y.Doc`. The document actor relays incremental Yjs updates as broadcast [events](/docs/actors/events) and keeps a server-side merged copy in vars. | +| Persistence | Full merged Yjs snapshot overwritten in one binary [actor KV](/docs/actors/kv) key (`yjs:doc`) on every sync update. Document metadata lives in JSON [state](/docs/actors/state). | +| Queues | None. The example is purely [actions](/docs/actors/actions) plus broadcast events. | +| Presence | Yjs Awareness relayed through the same `applyUpdate` action. Per-connection `connState` tracks asserted awareness clientIds for disconnect cleanup. | + +The two-actor split follows the coordinator pattern from [Design Patterns](/docs/actors/design-patterns): the coordinator owns discovery and creation, and each document actor owns one document's realtime state. Multi-part [keys](/docs/actors/keys) scope both actors to a workspace. + +**Actors** + +- **Key**: `document[workspaceId, documentId]` +- **Responsibility**: Applies incoming sync and awareness updates to a server-side `Y.Doc` and `Awareness`, persists the merged Yjs snapshot to actor KV, and broadcasts updates to all connected collaborators. +- **Actions** + - `getContent` + - `applyUpdate` + - `getAwareness` +- **Queues** + - None +- **State** + - JSON metadata only: `title`, `createdAt`, `updatedAt` + - Binary KV key `yjs:doc` holding the full merged Yjs snapshot + - Ephemeral vars: the live `Y.Doc` and `Awareness`, created in `createVars` and rehydrated from KV on actor start + - Per-connection `connState`: `clientIds` of awareness clients asserted by that connection + +- **Key**: `documentList[workspaceId]` +- **Responsibility**: Coordinator for one workspace. Creates document actors through the actor-to-actor client and maintains the index of document summaries. +- **Actions** + - `createDocument` + - `listDocuments` + - `deleteDocument` +- **Queues** + - None +- **State** + - JSON + - `documents` array of `DocumentSummary` entries (`id`, `title`, `createdAt`, `updatedAt`) + +The coordinator's `createDocument` generates a UUID, then explicitly creates the document actor with `c.client()` and passes `{ title, createdAt }` as creation [input](/docs/actors/input), which the document actor's `createState` consumes. See [Communicating Between Actors](/docs/actors/communicating-between-actors) for the actor-to-actor client. + +## Update Relay + +A single `applyUpdate(update, kind, clientId?)` action handles both update kinds. Updates cross the action boundary as `number[]` byte arrays and are converted back to `Uint8Array` on each side. + +| Kind | Server Applies To | Persists | Broadcasts | +| --- | --- | --- | --- | +| `"sync"` | `c.vars.doc` via `Y.applyUpdate` with origin `"client"` | Full merged snapshot to KV key `yjs:doc`, then bumps `updatedAt` | `sync` event carrying the incremental update | +| `"awareness"` | `c.vars.awareness` via `applyAwarenessUpdate` with origin `"client"` | Nothing. Presence is ephemeral. | `awareness` event carrying the update | + +Note the asymmetry on the sync branch: the broadcast carries only the small incremental update, while the KV write stores the full merged document re-encoded with `Y.encodeStateAsUpdate`. + +Yjs origin tags are the echo guards that keep the relay loop-free: + +| Origin Tag | Set Where | Effect | +| --- | --- | --- | +| `"local"` | Client edits inside `doc.transact(..., "local")` | The client's update listener fires and sends `applyUpdate` to the actor. | +| `"client"` | Server applying an incoming update to its `Y.Doc` or `Awareness` | Marks the change as client-originated on the server copy. | +| `"remote"` | Client applying broadcast events or initial sync data | Update listeners early-return on `"remote"`, so a client never re-sends its own echo. | + +On connect or reconnect, the client calls `getContent` and `getAwareness`, then applies both results to its local `Y.Doc` and `Awareness` with origin `"remote"`. After that, every change flows through `applyUpdate` and the broadcast events. + +## Awareness And Presence + +Presence (user names, colors, cursor positions) rides on the Yjs Awareness protocol instead of actor state: + +- Clients set presence with `awareness.setLocalStateField` for the `user` and `cursor` fields. The awareness update listener encodes the change and sends `applyUpdate(update, "awareness", awareness.clientID)`. +- The actor records each asserted `clientId` in that connection's `connState.clientIds`, applies the update to the server-side `Awareness`, and broadcasts the `awareness` event to all peers. See [Connections](/docs/actors/connections) for per-connection state. +- `onDisconnect` reads the connection's `clientIds`, calls `removeAwarenessStates` on the server-side `Awareness`, and broadcasts the encoded removal so every remaining client drops the departed user's cursor. See [Lifecycle](/docs/actors/lifecycle) for the hook. + +Because the actor tracks which awareness clientIds belong to which connection, presence cleanup is automatic on disconnect with no client cooperation required. + +## Persistence And Compaction + +The example persists with a full-snapshot overwrite: on every `"sync"` update, the actor re-encodes the entire merged document with `Y.encodeStateAsUpdate` and overwrites the single binary KV key `yjs:doc`. There is no append-only update log and no separate compaction job. Compaction is implicit because `Y.encodeStateAsUpdate` emits one compact merged representation of the document, so Yjs merge semantics keep the stored blob compact on their own. + +| Property | Full-Snapshot Overwrite (the example) | +| --- | --- | +| Write cost | One full-document KV write per sync update, so every keystroke rewrites the whole blob. | +| Read cost | One binary KV read in `createVars` rehydrates the document on actor start. | +| Crash safety | The last completed `applyUpdate` is durable. No log replay needed. | +| Sweet spot | Small to medium documents where simplicity beats write amplification. | + +**Recommended extension (not in the example)**: for large documents or very high edit rates, switch to appending incremental updates to a KV update log and writing a merged snapshot only periodically (for example every N updates). Boot becomes snapshot plus log replay, and the snapshot write becomes the explicit compaction step that truncates the log. Adopt this only when full-snapshot writes become the measured bottleneck or the blob approaches KV value size limits. + +## Lifecycle + +```mermaid +sequenceDiagram + participant A as Client A + participant B as Client B + participant DL as documentList + participant D as document + + A->>DL: listDocuments() + A->>DL: createDocument(title) + DL->>D: create([workspaceId, documentId], input) + DL-->>A: DocumentSummary + A->>D: connect + B->>D: connect + Note over D: createVars rehydrates Y.Doc from KV "yjs:doc" + A->>D: getContent() + getAwareness() + D-->>A: encoded doc + awareness state + Note over A: local edit with origin "local" + A->>D: applyUpdate(update, "sync") + Note over D: apply with origin "client", overwrite KV snapshot + D-->>B: sync event (incremental update) + Note over B: apply with origin "remote", no echo + A->>D: applyUpdate(update, "awareness", clientId) + D-->>B: awareness event + B-->>D: disconnect + Note over D: onDisconnect removes B's awareness clientIds + D-->>A: awareness event (removal) +``` + +## Security Checklist + +The example ships with no authentication or authorization. Harden it with this baseline before production. None of these are implemented in the example. + +- **Authenticate before connect**: Anyone who knows or guesses a workspace ID can connect, and because `useActor` implicitly getOrCreates, connecting with a nonexistent workspace ID silently creates a blank `documentList` coordinator. Add connection auth so unauthenticated clients never reach an actor. See [Authentication](/docs/actors/authentication). +- **Per-document access control**: Validate that the authenticated user is allowed to access the specific `[workspaceId, documentId]` key, not just any document. +- **Cap and rate limit `applyUpdate`**: Update payloads are unvalidated `number[]` arrays with no size limit, and the example client sends one action per keystroke and per cursor move with zero throttling. Enforce payload size caps and per-connection rate limits on the server, and debounce on the client. +- **Do not trust client-asserted awareness clientIds**: The `clientId` argument to `applyUpdate` is client-supplied and trusted as-is. Derive or verify presence identity from connection-scoped server state instead. +- **Destroy actors and KV on delete**: `deleteDocument` only filters the entry out of the coordinator's index. The document actor and its KV snapshot are orphaned. On delete, also destroy the document actor and its storage, with a permission check on who may delete. + +_Source doc path: /cookbook/collaborative-text-editor_ diff --git a/.agents/skills/live-cursors/reference/cookbook/cron-jobs.md b/.agents/skills/live-cursors/reference/cookbook/cron-jobs.md new file mode 100644 index 0000000..21c7657 --- /dev/null +++ b/.agents/skills/live-cursors/reference/cookbook/cron-jobs.md @@ -0,0 +1,69 @@ +# Cron Jobs and Scheduled Tasks + +> Source: `src/content/cookbook/cron-jobs.mdx` +> Canonical URL: https://rivet.dev/cookbook/cron-jobs +> Description: Patterns for durable one-shot, calendar, and fixed-interval work on Rivet Actors. + +--- +Rivet Actor schedules are durable actor-local timers. They survive actor sleep, restarts, upgrades, deploys, and crashes without a separate cron service. + +## Choose a schedule type + +| API | Use it for | +| --- | --- | +| `c.schedule.after(delayMs, action, ...args)` | One-time work after a relative delay. | +| `c.schedule.at(timestamp, action, ...args)` | One-time work at an exact Unix timestamp in milliseconds. | +| `c.cron.set({ ... })` | Named calendar recurrence in an IANA timezone. | +| `c.cron.every({ ... })` | Named fixed intervals of at least 5 seconds. | + +All callbacks are ordinary actions on the same actor. Keep the action name fixed in your code rather than accepting an arbitrary action name from a client. + +See [Schedule & Cron](/docs/actors/schedule) for the full API, history, cancellation, failure behavior, and limits. + +## Calendar job + +Use `cron.set` instead of manually re-arming a one-shot action: + +Install fixed background jobs in `onCreate` so setup runs once per actor. The job name remains an upsert key, so a later `cron.set` call updates the existing job rather than creating a duplicate. `cron.set` also handles timezone and daylight-saving transitions. + +## Fixed-interval job + +Use `cron.every` for frequent work such as presence sweeps or cache refreshes: + +```ts +await c.cron.every({ + name: "presence-sweep", + interval: 15_000, // Minimum 5 seconds. + action: "sweepPresence", + maxHistory: 25, +}); +``` + +Intervals remain anchored to scheduled deadlines rather than drifting by the action's runtime. If a previous run is still active, the overlapping occurrence is skipped. + +## Cancellation and updates + +Keep the ID returned by a one-shot schedule when it may need cancellation: + +```ts +const id = await c.schedule.after(60_000, "expireSession", sessionId); +await c.schedule.cancel(id); +``` + +Recurring jobs are managed by name: + +```ts +await c.cron.delete("presence-sweep"); +``` + +Calling `cron.set` or `cron.every` again with the same name replaces its configuration. + +## Failure and idempotency + +Keep scheduled actions idempotent when duplicate work would be harmful. See [Execution behavior](/docs/actors/schedule#execution-behavior) for retry behavior and workflow guidance. + +## Topology + +Use a singleton actor key for one global job, such as `jobs["daily-report"]`. Use an actor per user or resource for isolated reminders, trials, billing periods, or other per-entity schedules. + +_Source doc path: /cookbook/cron-jobs_ diff --git a/.agents/skills/live-cursors/reference/cookbook/live-cursors.md b/.agents/skills/live-cursors/reference/cookbook/live-cursors.md new file mode 100644 index 0000000..71c94ab --- /dev/null +++ b/.agents/skills/live-cursors/reference/cookbook/live-cursors.md @@ -0,0 +1,152 @@ +# Live Cursors and Presence + +> Source: `src/content/cookbook/live-cursors.mdx` +> Canonical URL: https://rivet.dev/cookbook/live-cursors +> Description: Live cursors and multiplayer presence with Rivet Actors: per-connection cursor state, realtime updates over events or raw WebSockets, and throttling. + +--- +Patterns for building live cursors, multiplayer presence, and realtime cursor sharing with RivetKit. One room actor fans cursor positions out to every connected client, keyed per room with [actor keys](/docs/actors/keys). + +## Starter Code + +Start with one of the two working variants on GitHub. Both implement the same collaborative cursor canvas with persistent text labels; they differ only in transport. + +| Variant | Starter Code | Transport | Presence Storage | +| --- | --- | --- | --- | +| `cursors` | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/cursors) | Typed [actions](/docs/actors/actions) and [events](/docs/actors/events) over the RivetKit connection | `connState` per connection | +| `cursors-raw-websocket` | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/cursors-raw-websocket) | Raw [`onWebSocket` handler](/docs/actors/websocket-handler) with a custom JSON message protocol | Socket map in `createVars` | + +Use `cursors` by default: typed actions, typed events, and automatic connection tracking cover most apps with less code. Use `cursors-raw-websocket` when you need full control of the wire format, for example a custom JSON or binary protocol, or clients that do not use the RivetKit client library. + +## Connection State vs Persistent State + +Presence is ephemeral by definition. A cursor position is only meaningful while its connection is alive, so it belongs in per-connection storage, not in persistent actor state. Persistent state is reserved for data that must survive disconnects and actor restarts. + +| Data | Where It Lives | Why | +| --- | --- | --- | +| Cursor position | `connState` (`cursors`) or the `createVars` socket map (`cursors-raw-websocket`) | Scoped to one connection and discarded with it. Stale presence cannot accumulate in storage. | +| Text labels (`textLabels`) | Persistent actor `state` in both variants | Canvas content must survive disconnects and actor restarts. | + +In the `cursors` variant, `updateCursor` writes `c.conn.state.cursor` and `getRoomState` rebuilds the presence snapshot by iterating `c.conns.values()`, so the cursor map is always derived from live connections rather than stored. See [Connections](/docs/actors/connections) for `connState` and [State](/docs/actors/state) for persistence semantics. + +## Presence Lifecycle + +- **Join**: The `cursors-raw-websocket` variant pushes an `init` message with the current `{ cursors, textLabels }` snapshot as soon as a socket connects. The `cursors` variant has no explicit join broadcast; the client calls the `getRoomState` action once after connecting to seed its local maps, and peers first see a new user on that user's first `cursorMoved` broadcast. +- **Move**: Every `updateCursor` call writes the connection's presence entry, then broadcasts `cursorMoved` to all connections, including the sender. +- **Leave**: The `cursors` variant handles leave in `onDisconnect`, broadcasting `cursorRemoved` with the connection's last cursor. The raw variant does the same from the socket `close` listener, then deletes the session from the `vars.websockets` map. Clients delete that user from their local cursor map, so stale cursors disappear the moment a tab closes. + +See [Lifecycle](/docs/actors/lifecycle) for `onDisconnect` and `createVars`. + +## Update Throttling + +Neither example throttles. Both frontends send a cursor update on every raw `mousemove` event with no debounce or interval cap. That is fine for a demo, but a fast mouse on a high-refresh display can emit hundreds of events per second per user. The patterns below are recommended production hardening on top of the starter code, not something the examples implement. + +| Layer | Pattern | Guidance | +| --- | --- | --- | +| Client (smoothness) | Throttle to 20-30Hz | Sample the latest pointer position every 33-50ms and send only that. Drop intermediate moves, but always flush the final position so cursors settle at the true location. Interpolate between received positions on the rendering side. | +| Server (enforcement) | Per-connection rate limit | Track the last accepted update timestamp per connection and drop or coalesce updates arriving faster than your cap. Client throttles are cooperative; the actor is the enforcement boundary. | + +## Actors + +- **Key**: `cursorRoom[roomId]` (the frontend defaults `roomId` to `"general"`) +- **Responsibility**: Holds per-connection cursor presence in `connState`, persists shared text labels in actor state, and broadcasts cursor and text updates to all connections. +- **Actions** + - `updateCursor` + - `updateText` + - `removeText` + - `getRoomState` +- **Events** + - `cursorMoved` + - `cursorRemoved` + - `textUpdated` + - `textRemoved` +- **Queues** + - None +- **State** + - JSON + - `textLabels` (persistent) + - `connState.cursor` per connection (ephemeral) + +- **Key**: `cursorRoom[roomId]` (resolved via `client.cursorRoom.getOrCreate(roomId)`) +- **Responsibility**: Exposes a raw WebSocket endpoint, tracks live sockets and their cursors in a `createVars` map keyed by a `sessionId` query parameter, persists text labels, and manually fans JSON frames out to every socket. +- **Actions** + - `getOrCreate` (stub returning `{ status: "ok" }`; the frontend resolves the actor ID with the client handle's `getOrCreate(roomId).resolve()`, which creates the actor without dispatching this action) + - `getRoomState` +- **Queues** + - None +- **State** + - JSON + - `textLabels` (persistent) + - `vars.websockets` map of `sessionId` to socket and cursor (in-memory, lost on restart) + +The raw variant defines no RivetKit events. Its message names are `type` fields on raw JSON frames: + +| Direction | Message `type` | Payload | +| --- | --- | --- | +| Client to server | `updateCursor` | `{ userId, x, y }` | +| Client to server | `updateText` | `{ id, userId, text, x, y }` | +| Client to server | `removeText` | `{ id }` | +| Server to client | `init` | `{ cursors, textLabels }` snapshot on connect | +| Server to client | `cursorMoved`, `textUpdated`, `textRemoved`, `cursorRemoved` | The corresponding cursor, label, or ID payload | + +## Lifecycle + +### cursors (Actions + Events) + +```mermaid +sequenceDiagram + participant A as Client A + participant R as cursorRoom + participant B as Other Clients + + A->>R: connect via useActor (cursorRoom[roomId]) + A->>R: getRoomState() + R-->>A: {cursors, textLabels} + loop every mouse move + A->>R: updateCursor(userId, x, y) + Note over R: write c.conn.state.cursor + R-->>B: cursorMoved (broadcast) + end + A->>R: updateText(id, userId, text, x, y) + Note over R: upsert persistent state.textLabels + R-->>B: textUpdated (broadcast) + Note over A: tab closes + Note over R: onDisconnect reads conn.state.cursor + R-->>B: cursorRemoved (broadcast) +``` + +### cursors-raw-websocket + +```mermaid +sequenceDiagram + participant A as Client A + participant R as cursorRoom + participant B as Other Clients + + A->>R: getOrCreate(roomId).resolve() + R-->>A: actorId + A->>R: open WebSocket /gateway/{actorId}/websocket?sessionId=... + Note over R: close 1008 if sessionId is missing + Note over R: store socket in vars.websockets + R-->>A: init {cursors, textLabels} + loop every mouse move + A->>R: {type: "updateCursor"} frame + Note over R: update session cursor in vars + R-->>B: cursorMoved frame + end + Note over A: socket closes + R-->>B: cursorRemoved frame + Note over R: delete session from vars.websockets +``` + +## Security Checklist + +Both examples ship without authentication so the presence pattern stays readable. Everything below is recommended hardening for production, not behavior the examples implement. + +- **Identity**: Bind presence identity to the connection (`c.conn.id` in the actions variant, a server-generated session ID in the raw variant). Never trust a client-supplied `userId`; in the examples it is a random client-generated string, so any client can impersonate or remove any cursor. +- **Authorization**: Authorize label mutations by owner. In the examples, `updateText` accepts arbitrary `id` and `userId` arguments and `removeText` accepts an arbitrary `id`, so any client can edit or delete any label. +- **Input validation**: Clamp `x` and `y` to canvas bounds, cap text label length, and cap the total `textLabels` count so persistent state cannot grow unbounded. +- **Rate limiting**: Enforce a per-connection cap on `updateCursor` (for example 30Hz) and on label writes, as described in [Update Throttling](#update-throttling). +- **Protocol strictness (raw variant)**: Validate message shape before use and close the socket on malformed JSON instead of logging and continuing. Reject duplicate `sessionId` values rather than silently overwriting another session's socket entry. + +_Source doc path: /cookbook/live-cursors_ diff --git a/.agents/skills/live-cursors/reference/cookbook/multiplayer-game.md b/.agents/skills/live-cursors/reference/cookbook/multiplayer-game.md new file mode 100644 index 0000000..425b113 --- /dev/null +++ b/.agents/skills/live-cursors/reference/cookbook/multiplayer-game.md @@ -0,0 +1,713 @@ +# Multiplayer Game + +> Source: `src/content/cookbook/multiplayer-game.mdx` +> Canonical URL: https://rivet.dev/cookbook/multiplayer-game +> Description: Pragmatic patterns for building multiplayer games: matchmaking, tick loops, realtime state, interest management, and validation. + +--- +Patterns for building multiplayer games with RivetKit, intended as a practical checklist you can adapt per genre. + +## Starter Code + +Start with one of the working examples on [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/multiplayer-game-patterns/src/actors/) and adapt it to your game. Do not start from scratch for matchmaking and lifecycle flows. + +| Game Classification | Starter Code | Common Examples | +| --- | --- | --- | +| Battle Royale | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/multiplayer-game-patterns/src/actors/battle-royale/) | Fortnite, Apex Legends, PUBG, Warzone | +| Arena | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/multiplayer-game-patterns/src/actors/arena/) | Call of Duty TDM/FFA, Halo Slayer, Counter-Strike casual, VALORANT unrated, Overwatch Quick Play, Rocket League | +| IO Style | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/multiplayer-game-patterns/src/actors/io-style/) | Agar.io, Slither.io, surviv.io | +| Open World | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/multiplayer-game-patterns/src/actors/open-world/) | Minecraft survival servers, Rust-like worlds, MMO zone/chunk worlds | +| Party | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/multiplayer-game-patterns/src/actors/party/) | Fall Guys private lobbies, custom game rooms, social party sessions | +| Physics 2D | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/multiplayer-game-patterns/src/actors/physics-2d/) | Top-down physics brawlers, 2D arena games, platform fighters | +| Physics 3D | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/multiplayer-game-patterns/src/actors/physics-3d/) | Physics sandbox sessions, 3D arena games, movement playgrounds | +| Ranked | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/multiplayer-game-patterns/src/actors/ranked/) | Chess ladders, competitive card games, duel arena ranked queues | +| Turn-Based | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/multiplayer-game-patterns/src/actors/turn-based/) | Chess correspondence, Words With Friends, async board games | +| Idle | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/multiplayer-game-patterns/src/actors/idle/) | Cookie Clicker, Idle Miner Tycoon, Adventure Capitalist | + +## Server Simulation + +### Game Loop And Tick Rates + +| Pattern | Use When | Implementation Guidance | +| --- | --- | --- | +| Fixed realtime loop | Battle Royale, Arena, IO Style, Open World, Ranked | Run in `run` with `sleep(tickMs)` and exit on `c.aborted`. | +| Action-driven updates | Party, Turn-Based | Mutate and broadcast only on actions/events rather than scheduled ticks. | +| Coarse offline progression | Any mode with idle progression | Use `c.schedule.after(...)` with coarse windows (for example 5 to 15 minutes) and apply catch-up from elapsed wall clock time. | + +### Physics + +Start with custom kinematic logic for simple games. Switch to a full physics engine when you need joints, stacked bodies, high collision density, or complex shapes (rotated polygons, capsules, convex hulls, triangle meshes). + +Pick one engine per simulation. Keep frontend-only libs out of backend simulation paths and treat server state as authoritative. + +| Dimension | Primary Engine | Fallback Engines | Example Code | +| --- | --- | --- | --- | +| 2D | `@dimforge/rapier2d` | `planck-js`, `matter-js` | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/multiplayer-game-patterns/src/actors/physics-2d/) | +| 3D | `@dimforge/rapier3d` | `cannon-es`, `ammo.js` | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/multiplayer-game-patterns/src/actors/physics-3d/) | + +### Spatial Indexing + +For non-physics spatial queries, use a dedicated index instead of naive `O(n^2)` checks: + +| Index Type | Recommendation | +| --- | --- | +| AABB index | For AOI, visibility, and non-collider entities, use `rbush` for dynamic sets or `flatbush` for static-ish sets. | +| Point index | For nearest-neighbor or within-radius queries, use `d3-quadtree`. | + +## Networking & State Sync + +### Netcode + +| Model | When To Use | Implementation | +| --- | --- | --- | +| Hybrid (client movement, server combat) | Shooters, action sports, ranked duels | Client owns movement and sends capped-rate position updates. Server validates for anti-cheat. Combat (projectiles, hits, damage) is fully server-authoritative. | +| Server-authoritative with interpolation | IO Style, persistent worlds | Client sends input commands. Server simulates on fixed ticks and publishes authoritative snapshots. Client interpolates between snapshots. | +| Server-authoritative (basic logic) | Turn-based, event-driven | Server validates and applies discrete actions (turns, phase transitions, votes). Client displays confirmed state. | + +### Realtime Data Model + +- **Snapshots and diffs**: Publish state as events. Send a full snapshot on join/resync, then per-tick diffs for regular updates. +- **Batch per tick**: Keep events small and typed. Batch high-frequency updates per tick. +- **Avoid UI framework state for game updates**: Use `requestAnimationFrame` or a Canvas/Three.js loop for simulation, not React state. Reserve UI framework state for menus, HUD, and forms. +- **Broadcast vs per-connection**: Use `c.broadcast(...)` for shared updates and `conn.send(...)` for private/per-player data. + +### Shared Simulation Logic + +Shared simulation logic runs on both the client and the server. For example, an `applyInput(state, input, dt)` function that integrates velocity and clamps to world bounds can run on the client for prediction and on the server for validation. + +- **Hybrid modes**: Client runs shared movement as primary authority, server runs it for anti-cheat validation. +- **Server-authoritative modes**: Client uses shared logic for interpolation and prediction only. +- **Keep it pure**: Movement integration, input transforms, collision helpers, and constants only. +- **Put shared code in `src/shared/`**: Keep deterministic helpers in `src/shared/sim/*` with no side effects. + +### Interest Management + +Control what each client receives to reduce bandwidth and prevent information leaks. + +#### Per-Player Replication Filters + +- **Filter by relevance**: Send each client only state relevant to that player (proximity, line-of-sight, team, or game phase). +- **Shooters and action games**: Limit replication by proximity and optional field-of-view checks. +- **Server-side only**: Clients should never receive data they should not see. + +#### Sharded Worlds + +- **Partition large worlds**: Use chunk actors keyed by `worldId:chunkX:chunkY`. +- **Subscribe to nearby chunks**: Clients connect only to nearby partitions (for example a 3x3 chunk window). +- **Use sparingly**: Only when the world is large and state-heavy (sandbox builders, MMOs), not as a default for small matches. + +## Backend Infrastructure + +### Persistence + +- **In-memory state**: Best for realtime game state that changes every tick (player positions, inputs, match phase, scores). +- **SQLite (`rivetkit/db`)**: Better for large or table-like state that needs queries, indexes, or long-term persistence (tiles, inventory, matchmaking pools). Serialize DB work through a queue since multiple actions can hit the same actor concurrently. + +### Matchmaking Patterns + +Common building blocks used across the architecture patterns below. + +#### Actor Topology + +| Primitive | Use When | Typical Ownership | +| --- | --- | --- | +| `matchmaker["main"]` + `match[matchId]` | Session-based multiplayer (battle royale, arena, ranked, party, turn-based) | Matchmaker owns discovery/assignment. Match owns lifecycle and gameplay state. | +| `chunk[worldId,chunkX,chunkY]` | Large continuous worlds that need sharding | Each chunk owns local players, chunk state, and local simulation. | +| `world[playerId]` | Per-player progression loops (idle/solo world state) | Per-player resources, buildings, timers, and progression. | +| `player[username]` | Canonical profile/rating reused across matches | Durable player stats (for example rating and win/loss). | +| `leaderboard["main"]` | Shared rankings across many matches/players | Global ordered score rows and top lists. | + +#### Queueing Strategy + +- Multiple players can hit the matchmaker at the same time, so actions like find/create, queue/unqueue, and close need to be serialized through actor queues to avoid races. +- Match-local actions (gameplay, scoring) do not need queueing unless they write back to the matchmaker. + +## Security And Anti-Cheat + +Start with this baseline, then harden further for competitive or high-risk environments. + +### Baseline Checklist + +- **Identity**: Use `c.conn.id` as the authoritative transport identity. Treat `playerId`/`username` in params as untrusted input and bind through server-issued assignment/join tickets. +- **Authorization**: Validate the caller is allowed to mutate the target entity (room membership, turn ownership, host-only actions). +- **Input validation**: Clamp sizes/lengths, validate enums, and validate usernames (length, allowed chars, avoid unbounded Unicode). +- **Rate limiting**: Per-connection rate limits for spammy actions (chat, join/leave, fire, movement updates). +- **State integrity**: Server recomputes derived state (scores, win conditions, placements). Never allow client-authoritative changes to inventory/currency/leaderboard totals. + +### Movement Validation + +For any mode with client-authoritative movement (hybrid flows), clients may send position/rotation updates for smoothness, but the server must: + +- Enforce max delta per update (speed cap) based on elapsed time. +- Reject or clamp teleports. +- Enforce world bounds (and basic collision if applicable). +- Rate limit update frequency (for example 20Hz max). + +## Architecture Patterns + +Each game type below starts with a quick summary table, then details actors and lifecycle. + +### Battle Royale + +| Topic | Summary | +| --- | --- | +| Matchmaking | Immediate routing to the fullest non-started lobby (oldest tie-break); players wait in lobby until capacity, then the match starts. | +| Netcode | Hybrid. Client owns movement, camera, and local prediction. Server owns zone state, projectiles, hit resolution, eliminations, loot, and final placement. | +| Tick Rate | 10 ticks/sec (`100ms`) with a fixed loop for zone progression and lifecycle checks. | +| Physics | Client owns movement with server anti-cheat validation; projectiles, hits, and damage are server-authoritative. Use `@dimforge/rapier3d` for 3D or `@dimforge/rapier2d` for top-down 2D. | + +**Actors** + +- **Key**: `matchmaker["main"]` +- **Responsibility**: Finds or creates lobbies, tracks pending reservations, and maintains occupancy. +- **Actions** + - `findMatch` + - `pendingPlayerConnected` + - `updateMatch` + - `closeMatch` +- **Queues** + - `findMatch` + - `pendingPlayerConnected` + - `updateMatch` + - `closeMatch` +- **State** + - SQLite + - `matches` + - `pending_players` + - `player_count` includes connected and pending players + +- **Key**: `match[matchId]` +- **Responsibility**: Runs lobby/live/finished phases, owns player state, zone progression, and eliminations. +- **Actions** + - `connect` + - Movement and combat actions +- **Queues** + - None +- **State** + - JSON + - `phase` + - `players` + - `zone` + - `eliminations` + - `snapshot data` + +**Lifecycle** + +```mermaid +sequenceDiagram + participant C as Client + participant MM as matchmaker + participant M as match + + C->>MM: findMatch() + alt no open lobby + MM->>M: create(matchId) + end + MM-->>C: {matchId, playerId} + C->>M: connect(playerId) + M->>MM: pendingPlayerConnected(matchId, playerId) + MM-->>M: accepted + Note over M: lobby countdown -> live + M-->>C: snapshot + shoot events + M->>MM: closeMatch(matchId) +``` + +### Arena + +| Topic | Summary | +| --- | --- | +| Matchmaking | Mode-based fixed-capacity queues (`duo`, `squad`, `ffa`) that build only full matches and pre-assign teams (except FFA). | +| Netcode | Hybrid. Client owns movement plus prediction and smoothing. Server owns team or FFA assignment, projectiles, hit resolution, phase transitions, and scoring. | +| Tick Rate | 20 ticks/sec (`50ms`) with a tighter loop for live team and FFA snapshots. | +| Physics | Medium to high intensity; client movement with server validation and server-authoritative combat/entities. | + +**Actors** + +- **Key**: `matchmaker["main"]` +- **Responsibility**: Runs mode queues, builds full matches, assigns teams, and publishes assignments. +- **Actions** + - `queueForMatch` + - `unqueueForMatch` + - `matchCompleted` +- **Queues** + - `queueForMatch` + - `unqueueForMatch` + - `matchCompleted` +- **State** + - SQLite + - `player_pool` + - `matches` + - `assignments` keyed by connection and player + +- **Key**: `match[matchId]` +- **Responsibility**: Runs match phases and in-match player/team state for score and win conditions. +- **Actions** + - `connect` + - Gameplay actions +- **Queues** + - None +- **State** + - JSON + - `phase` + - `players` + - `team assignments` + - `score and win state` + +**Lifecycle** + +```mermaid +sequenceDiagram + participant C as Client + participant MM as matchmaker + participant M as match + + C->>MM: queueForMatch(mode) + Note over MM: enqueue in player_pool + Note over MM: fill when capacity reached + MM->>M: create(matchId, assignments) + Note over MM: persist assignments + MM-->>C: assignmentReady + C->>M: connect(playerId) + Note over M: waiting -> live when all players connect + M->>MM: matchCompleted(matchId) +``` + +### IO Style + +| Topic | Summary | +| --- | --- | +| Matchmaking | Open-lobby routing to the fullest room below capacity; room counts are heartbeated and new lobbies are auto-created when needed. | +| Netcode | Server-authoritative with interpolation. Client sends input intents and interpolates. Server owns movement, bounds, room membership, and canonical snapshots. | +| Tick Rate | 10 ticks/sec (`100ms`) with lightweight periodic room snapshots. | +| Physics | Low to medium intensity; server-authoritative kinematic movement, escalating to a physics engine only when collisions get complex. | + +**Actors** + +- **Key**: `matchmaker["main"]` +- **Responsibility**: Routes players into the fullest open lobby and tracks reservations and occupancy. +- **Actions** + - `findLobby` + - `pendingPlayerConnected` + - `updateMatch` + - `closeMatch` +- **Queues** + - `findLobby` + - `pendingPlayerConnected` + - `updateMatch` + - `closeMatch` +- **State** + - SQLite + - `matches` + - `pending_players` + - Occupancy includes pending reservations + +- **Key**: `match[matchId]` +- **Responsibility**: Runs per-match movement simulation and broadcasts snapshots. +- **Actions** + - `connect` + - `setInput` +- **Queues** + - None +- **State** + - JSON + - `players` + - `inputs` + - `movement state` + - `snapshot cache` + +**Lifecycle** + +```mermaid +sequenceDiagram + participant C as Client + participant MM as matchmaker + participant M as match + + C->>MM: findLobby() + alt no open lobby + MM->>M: create(matchId) + end + MM-->>C: {matchId, playerId} + C->>M: connect(playerId) + M->>MM: pendingPlayerConnected(matchId, playerId) + MM-->>M: accepted + Note over M: fixed tick simulation + M-->>C: snapshot events + M->>MM: closeMatch(matchId) +``` + +### Open World + +| Topic | Summary | +| --- | --- | +| Matchmaking | Client-driven chunk routing from world coordinates, with nearby chunk windows preloaded via adjacent chunk connections. | +| Netcode | Hybrid for sandbox (client movement with validation) or server-authoritative for MMO-like flows. Server owns chunk routing, persistence, and canonical world state. | +| Tick Rate | 10 ticks/sec per chunk actor (`100ms`), so load scales with active chunks. | +| Physics | Medium to high at scale; chunk-local simulation can be server-authoritative (MMO-like) or client movement with server validation (sandbox-like). | + +**Actors** + +- **Key**: `chunk[worldId,chunkX,chunkY]` +- **Responsibility**: Owns chunk-local players, blocks, movement tick, and chunk membership. +- **Actions** + - `connect` + - `enterChunk` + - `addPlayer` + - `setInput` + - `leaveChunk` + - `removePlayer` +- **Queues** + - None +- **State** + - JSON + - `connections` + - `players` + - `blocks` scoped to one chunk key + +**Lifecycle** + +```mermaid +sequenceDiagram + participant C as Client + participant CH as chunk + + Note over C: resolve chunk keys from world position + loop each visible chunk + C->>CH: connect(worldId, chunkX, chunkY, playerId) + Note over CH: store connection metadata + end + C->>CH: enterChunk/addPlayer + loop movement updates + C->>CH: setInput(...) + CH-->>C: snapshot + end + C->>CH: leaveChunk/removePlayer or disconnect + Note over CH: remove membership and metadata +``` + +### Party + +| Topic | Summary | +| --- | --- | +| Matchmaking | Host-created private party flow using party codes and explicit joins. | +| Netcode | Server-authoritative (basic logic). Server owns membership, host permissions, and phase transitions. | +| Tick Rate | No continuous tick; updates are event-driven (`join`, `start`, `finish`). | +| Physics | Low intensity for lobby-first flows; usually no dedicated physics or indexing unless you add realtime mini-games. | + +**Actors** + +- **Key**: `matchmaker["main"]` +- **Responsibility**: Handles party create/join flow, validates join tickets, and tracks party size. +- **Actions** + - `createParty` + - `joinParty` + - `verifyJoin` + - `updatePartySize` + - `closeParty` +- **Queues** + - `createParty` + - `joinParty` + - `verifyJoin` + - `updatePartySize` + - `closeParty` +- **State** + - SQLite + - `parties` + - `join_tickets` for party lookup and join validation + +- **Key**: `match[matchId]` +- **Responsibility**: Owns party members, host role, ready flags, and phase transitions. +- **Actions** + - `connect` + - `startGame` + - `finishGame` +- **Queues** + - None +- **State** + - JSON + - `members` + - `host` + - `ready state` + - `phase` + - `party events` + +**Lifecycle** + +### Host Flow + +```mermaid +sequenceDiagram + participant H as Host Client + participant MM as matchmaker + participant M as match + + H->>MM: createParty() + MM-->>H: {matchId, partyCode, playerId, joinToken} + H->>M: connect(playerId, joinToken) + M->>MM: verifyJoin(...) + MM-->>M: allowed + M->>MM: updatePartySize(playerCount) + H->>M: startGame() / finishGame() + M->>MM: closeParty(matchId) +``` + +### Joiner Flow + +```mermaid +sequenceDiagram + participant J as Joiner Client + participant MM as matchmaker + participant M as match + + J->>MM: joinParty(partyCode) + MM-->>J: {matchId, playerId, joinToken} + J->>M: connect(playerId, joinToken) + M->>MM: verifyJoin(...) + MM-->>M: allowed / denied + M->>MM: updatePartySize(playerCount) +``` + +### Ranked + +| Topic | Summary | +| --- | --- | +| Matchmaking | ELO-based queue pairing with a widening search window as wait time increases. | +| Netcode | Hybrid. Client owns movement with local prediction and interpolation. Server owns projectiles, hit resolution, match results, and rating updates. | +| Tick Rate | 20 ticks/sec (`50ms`) with fixed live ticks for deterministic pacing and broadcast cadence. | +| Physics | Medium to high intensity; client movement with server validation and server-authoritative combat/hit resolution. | + +**Actors** + +- **Key**: `matchmaker["main"]` +- **Responsibility**: Runs rating-based queueing, pairing, assignment persistence, and completion fanout. +- **Actions** + - `queueForMatch` + - `unqueueForMatch` + - `matchCompleted` +- **Queues** + - `queueForMatch` + - `unqueueForMatch` + - `matchCompleted` +- **State** + - SQLite + - `player_pool` + - `matches` + - `assignments` with rating window and connection scoping + +- **Key**: `match[matchId]` +- **Responsibility**: Runs ranked match phase, score, and winner reporting. +- **Actions** + - `connect` + - Gameplay actions +- **Queues** + - None +- **State** + - JSON + - `phase` + - `players` + - `score` + - `winner` + - `completion payload` + +- **Key**: `player[username]` +- **Responsibility**: Stores canonical player MMR and win/loss profile. +- **Actions** + - `initialize` + - `getRating` + - `applyMatchResult` +- **Queues** + - None +- **State** + - JSON + - `rating` + - `wins` + - `losses` + - `match counters` + +- **Key**: `leaderboard["main"]` +- **Responsibility**: Stores and serves top-ranked players. +- **Actions** + - `updatePlayer` +- **Queues** + - None +- **State** + - SQLite + - Leaderboard score rows + - Top-list ordering + +**Lifecycle** + +```mermaid +sequenceDiagram + participant C as Client + participant MM as matchmaker + participant P as player + participant M as match + participant LB as leaderboard + + C->>MM: queueForMatch(username) + MM->>P: initialize/getRating + P-->>MM: rating + Note over MM: store queue row + retry pairing + MM->>M: create(matchId, assigned players) + MM-->>C: assignmentReady + C->>M: connect(username) + M->>MM: matchCompleted(...) + MM->>P: applyMatchResult(...) + MM->>LB: updatePlayer(...) + Note over MM: remove matches + assignments rows +``` + +### Turn-Based + +| Topic | Summary | +| --- | --- | +| Matchmaking | Async private-invite and public-queue pairing in the same pattern. | +| Netcode | Server-authoritative (basic logic). Client can draft moves before submit. Server owns turn ownership, committed move log, turn order, and completion state. | +| Tick Rate | No continuous tick; move submission and turn transitions drive updates. | +| Physics | Very low intensity; no realtime physics loop, just discrete rules validation. Indexing is optional and mostly for board or query convenience at scale. | + +**Actors** + +- **Key**: `matchmaker["main"]` +- **Responsibility**: Handles private invite and public queue pairing for async matches. +- **Actions** + - `createGame` + - `joinByCode` + - `queueForMatch` + - `unqueueForMatch` + - `closeMatch` +- **Queues** + - `createGame` + - `joinByCode` + - `queueForMatch` + - `unqueueForMatch` + - `closeMatch` +- **State** + - SQLite + - `matches` + - `player_pool` + - `assignments` for invite and queue mapping + +- **Key**: `match[matchId]` +- **Responsibility**: Owns board state, turn order, move validation, and final result. +- **Actions** + - `connect` + - `makeMove` +- **Queues** + - None +- **State** + - JSON + - `board` + - `turns` + - `players` + - `connection presence` + - `result` + +**Lifecycle** + +### Public Queue + +```mermaid +sequenceDiagram + participant A as Client A + participant B as Client B + participant MM as matchmaker + participant M as match + + A->>MM: queueForMatch() + B->>MM: queueForMatch() + Note over MM: pair first two queued players + MM->>M: create(matchId) + seed X/O players + MM-->>A: assignment/match info + MM-->>B: assignment/match info + A->>M: connect(playerId) + B->>M: connect(playerId) + A->>M: makeMove() + B->>M: makeMove() + opt all players disconnected for timeout + Note over M: destroy after idle timeout + end + M->>MM: closeMatch(matchId) +``` + +### Private Invite + +```mermaid +sequenceDiagram + participant A as Client A + participant B as Client B + participant MM as matchmaker + participant M as match + + A->>MM: createGame() + MM-->>A: {matchId, playerId, inviteCode} + B->>MM: joinByCode(inviteCode) + MM->>M: create(matchId) + seed X/O players + MM-->>A: assignment/match info + MM-->>B: assignment/match info + A->>M: connect(playerId) + B->>M: connect(playerId) + A->>M: makeMove() + B->>M: makeMove() + M->>MM: closeMatch(matchId) +``` + +### Idle + +| Topic | Summary | +| --- | --- | +| Matchmaking | No matchmaker; each player uses a direct per-player actor and a shared leaderboard actor. | +| Netcode | Server-authoritative (basic logic). Client owns UI and build intent. Server owns resources, production rates, building validation, and leaderboard totals. | +| Tick Rate | No continuous tick; use `c.schedule.after(...)` for coarse intervals and compute offline catch-up from elapsed wall time. | +| Physics | None for standard idle loops; transitions are discrete (`build`, `collect`, `upgrade`) and do not need spatial indexing. | + +**Actors** + +- **Key**: `world[playerId]` +- **Responsibility**: Owns one player's progression, buildings, production scheduling, and state updates. +- **Actions** + - `initialize` + - `build` + - `collectProduction` +- **Queues** + - None +- **State** + - JSON + - Per-player buildings + - `resources` + - `timers` + - `progression state` + +- **Key**: `leaderboard["main"]` +- **Responsibility**: Stores global scores and serves leaderboard updates. +- **Actions** + - `updateScore` +- **Queues** + - `updateScore` +- **State** + - SQLite + - `scores` table keyed by player + - Current leaderboard totals + +**Lifecycle** + +```mermaid +sequenceDiagram + participant C as Client + participant W as world + participant LB as leaderboard + + C->>W: getOrCreate(playerId) + initialize() + Note over W: seed state + schedule collection + W-->>C: stateUpdate + loop gameplay loop + C->>W: build() / collectProduction() + W->>LB: updateScore(...) + Note over LB: upsert scores + LB-->>C: leaderboardUpdate + W-->>C: stateUpdate + end +``` + +_Source doc path: /cookbook/multiplayer-game_ diff --git a/.agents/skills/live-cursors/reference/cookbook/per-tenant-database.md b/.agents/skills/live-cursors/reference/cookbook/per-tenant-database.md new file mode 100644 index 0000000..e3c5616 --- /dev/null +++ b/.agents/skills/live-cursors/reference/cookbook/per-tenant-database.md @@ -0,0 +1,119 @@ +# Database per Tenant + +> Source: `src/content/cookbook/per-tenant-database.mdx` +> Canonical URL: https://rivet.dev/cookbook/per-tenant-database +> Description: 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. + +--- +Patterns 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. + +## Starter Code + +Start with the working example on [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/per-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. + +| Topic | Summary | +| --- | --- | +| Isolation | One `companyDatabase` actor per tenant, keyed by company name. Switching tenants swaps the entire dataset. | +| State | JSON actor state holding `employees` and `projects` arrays plus timestamps. No SQLite, no queues, no scheduling. | +| Realtime | Every write action mutates state, then broadcasts a typed event (`employeeAdded`, `projectAdded`) to all connected clients of that tenant. | +| Auth | None. The sign-in screen is cosmetic. Production guidance is in the [security checklist](#security-checklist). | + +## The Isolation Model + +The 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: + +- **One actor per tenant**: `companyDatabase[tenantId]` addresses exactly one actor instance. Two tenants can never share an actor. +- **One dataset per tenant**: All reads and writes go through that actor's [state](/docs/actors/state), 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. +- **No key injection**: Keys are arrays, not interpolated strings. `key: [tenantId]` cannot be escaped the way `"tenant:" + tenantId` string concatenation can. See [Keys](/docs/actors/keys). + +The example's test ([tests/per-tenant-database.test.ts](https://github.com/rivet-dev/rivet/tree/main/examples/per-tenant-database/tests/per-tenant-database.test.ts)) proves the isolation: data written to `companyDatabase["Alpha Co"]` never appears in `companyDatabase["Beta Co"]`. + +## Choosing a State Backend + +The example uses plain JSON actor state. The same key-equals-tenant model works with any actor state backend. + +| Backend | Use When | Docs | Working Code | +| --- | --- | --- | --- | +| JSON actor state | Small datasets, simple reads, whole dataset fits comfortably in memory. What the example uses. | [State](/docs/actors/state) | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/per-tenant-database) | +| Actor SQLite (`rivetkit/db`) | Tables, indexes, SQL queries, larger-than-memory data, per-tenant relational schema. | [SQLite](/docs/actors/sqlite) | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/kitchen-sink/src/actors/state/sqlite-raw.ts) | +| SQLite + Drizzle | Typed schema, query builder, and generated migration files on top of actor SQLite. | [SQLite + Drizzle](/docs/actors/sqlite-drizzle) | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/kitchen-sink/src/actors/state/sqlite-drizzle/) | + +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. + +## Migrations + +The per-tenant example has no migrations because JSON state has no schema. When you adopt SQLite, migrations run per tenant database: + +- **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](/docs/actors/sqlite). +- **Drizzle**: `drizzle-kit` generates migration files from your typed schema, and `db({ schema, migrations })` applies them when the actor wakes. See [SQLite + Drizzle](/docs/actors/sqlite-drizzle). + +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. + +## Tenant Id Must Come From Auth + +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): + +- Derive the tenant id from a verified credential, such as a JWT claim, never from user input. +- Validate the credential against `c.key` in `onBeforeConnect` (pass/fail) or `createConnState` (store the verified user on connection state). See [Authentication](/docs/actors/authentication) and [Connections](/docs/actors/connections). +- Add per-action permission checks on top of connection-level auth. See [Access Control](/docs/actors/access-control). + +## Actors + +- **Key**: `companyDatabase[companyName]` (single-element array key; `c.key[0]` is the company name) +- **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. +- **Actions** + - `addEmployee` + - `listEmployees` + - `addProject` + - `listProjects` + - `getStats` +- **Queues** + - None +- **Events** + - `employeeAdded` + - `projectAdded` +- **State** + - JSON + - `company_name` + - `employees` + - `projects` + - `created_at` + - `updated_at` + +Every 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](/docs/actors/actions) and [Events](/docs/actors/events). + +## Lifecycle + +```mermaid +sequenceDiagram + participant A as Tenant A client + participant DA as companyDatabase A + participant B as Tenant B client + participant DB as companyDatabase B + + Note over A: authenticate and derive tenant id + A->>DA: connect with key [tenantA] + Note over DA: createState seeds company_name, employees, projects + A->>DA: listEmployees() + listProjects() + getStats() + A->>DA: addEmployee(name, role) + DA-->>A: employeeAdded event + B->>DB: connect with key [tenantB] + Note over DB: separate actor, separate dataset + B->>DB: listEmployees() + DB-->>B: tenant B data only +``` + +In 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. + +## Security Checklist + +The example ships with none of these. Apply all of them before production. + +- **Tenant identity**: Derive the tenant id from a verified JWT claim, never from a client-supplied string. +- **Connection validation**: In `onBeforeConnect` or `createConnState`, verify the credential's tenant claim matches `c.key` and reject mismatches. +- **Per-action authorization**: Check the caller's role before mutating actions (`addEmployee`, `addProject`), not just at connect time. See [Access Control](/docs/actors/access-control). +- **Input validation**: Clamp name and role lengths and validate enums. The example only trims input and substitutes fallback defaults. +- **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. +- **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](/docs/actors/sqlite) when the dataset outgrows memory. + +_Source doc path: /cookbook/per-tenant-database_ diff --git a/.agents/skills/live-cursors/reference/cookbook/vpc-air-gapped.md b/.agents/skills/live-cursors/reference/cookbook/vpc-air-gapped.md new file mode 100644 index 0000000..f7d87a3 --- /dev/null +++ b/.agents/skills/live-cursors/reference/cookbook/vpc-air-gapped.md @@ -0,0 +1,136 @@ +# Deploying Rivet in a VPC or Air-Gapped Network + +> Source: `src/content/cookbook/vpc-air-gapped.mdx` +> Canonical URL: https://rivet.dev/cookbook/vpc-air-gapped +> Description: Run Rivet entirely inside your own perimeter: single-binary or Docker Compose install, file system storage with no database infrastructure, and no outbound telemetry by default. + +--- +Patterns for running self-hosted Rivet inside a private network: a VPC without internet egress, an on-premises rack, or a fully air-gapped environment. The engine is one service, the recommended single-node storage backend is the local file system, and the engine makes no outbound connections by default. Self-hosting is the only Rivet deployment model that supports air-gapped networks; see the [Self-Hosting Overview](/docs/self-hosting) for the full comparison with BYOC. + +## What Runs Inside the Perimeter + +A self-hosted deployment has three components, all of which live inside your network: + +| Component | Role | Inside the perimeter | +| --- | --- | --- | +| Your backend | Your application server, including the runner that executes actor code | Yes | +| Rivet Engine | Orchestration service that manages actor lifecycle, routes messages, and serves the dashboard and APIs | Yes | +| Storage | Persistence for actor state. Local file system for single-node, PostgreSQL or FoundationDB for multi-node | Yes | + +There is no license server, no Rivet Cloud account, and no callback to `rivet.dev`. Clients inside the perimeter reach actors through the engine's gateway over your private network. See [Architecture](/docs/self-hosting#architecture). + +## Single-Binary Install + +The engine compiles to a single `rivet-engine` binary. Build it from source outside the perimeter, then copy the binary across the boundary: + +```bash +git clone https://github.com/rivet-dev/rivet.git +cd rivet +cargo build --release -p rivet-engine +# Copy target/release/rivet-engine into the perimeter. +``` + +Prebuilt binaries are coming soon; see [Installing Rivet Engine](/docs/self-hosting/install) for current options. + +Run it with the file system backend, which stores everything on local disk and is the production-ready choice for single-node deployments. The [File System](/docs/self-hosting/filesystem) docs list air-gapped environments as a primary use case because it needs no database infrastructure: + +```bash +RIVET__database__file_system__path="/var/lib/rivet/data" ./rivet-engine +``` + +Configuration can also come from files. The engine discovers config at `/etc/rivet/config.json` on Linux (JSON, JSON5, JSONC, YAML, and YML are all supported), and `--config` overrides the path. Environment variables use the `RIVET__` prefix with `__` as the separator. See [Configuration](/docs/self-hosting/configuration). + +The engine serves its own dashboard on port `6420`, so inspection and namespace management work with nothing but a browser inside the perimeter. + +## Docker Compose Deployment + +For Docker hosts without registry access, move the engine image across the boundary the standard way: + +```bash +# Outside the perimeter. +docker pull rivetdev/engine:latest +docker save rivetdev/engine:latest -o rivet-engine.tar +# Inside the perimeter. +docker load -i rivet-engine.tar +``` + +Then run the engine and your app together in one Compose file: + +```yaml +services: + rivet-engine: + image: rivetdev/engine:latest + ports: + - "6420:6420" + volumes: + - rivet-data:/data + environment: + RIVET__FILE_SYSTEM__PATH: "/data" + restart: unless-stopped + + my-app: + build: . + environment: + RIVET_ENDPOINT: "http://default:admin@rivet-engine:6420" + depends_on: + - rivet-engine + restart: unless-stopped + +volumes: + rivet-data: +``` + +`RIVET_ENDPOINT` uses the format `http://namespace:token@host:port` and tells your app to connect to the engine as a runner instead of running standalone. After both services start, register your runner with the engine through the dashboard or its API. The full walkthrough, including PostgreSQL setup for multi-node deployments, is in [Docker Compose](/docs/self-hosting/docker-compose). + +## No Outbound Telemetry + +The engine exports traces and metrics only when you opt in with OpenTelemetry. Export is disabled unless `RIVET_OTEL_ENABLED=1` is set, and the export target defaults to a local collector at `http://localhost:4317`. With no configuration, nothing crosses the perimeter. + +When you want observability, keep it inside the network: + +- Set `RIVET_OTEL_ENABLED=1` and point `RIVET_OTEL_GRPC_ENDPOINT` at a collector you run inside the perimeter. +- Adjust `RIVET_OTEL_SAMPLER_RATIO` to control trace sampling. +- Use the engine's health endpoint for liveness and readiness probes. + +See the [Production Checklist](/docs/self-hosting/production-checklist) for monitoring guidance. + +## Embedding Rivet in a Customer's Environment + +If you ship software that runs inside your customers' VPCs, the same setup turns Rivet into an internal component of your product rather than a service your customers must reach over the internet: + +- **Ship the engine next to your app.** Add `rivetdev/engine` to the Compose file or chart you already deliver. Your app finds it over the private network via `RIVET_ENDPOINT`, so one artifact deploys the whole stack. +- **One namespace per install.** The endpoint URL carries the namespace and token (`http://namespace:token@host:port`), so a single image works across customer deployments. See [Endpoints](/docs/general/endpoints). +- **Generate a strong admin token per install.** Replace the default token and keep it server-side. Never include the admin token in `RIVET_PUBLIC_ENDPOINT` or anywhere clients can read it. +- **Public endpoint only when needed.** `RIVET_PUBLIC_ENDPOINT` with a public (`pk_`) token is only required when browser clients connect to actors in [serverless runtime mode](/docs/general/runtime-modes). Backend-only deployments can skip it entirely. +- **TLS at the customer's edge.** Terminate TLS with the customer's reverse proxy or load balancer in front of the engine. + +## Scaling Past One Node + +| Backend | Use when | Status | +| --- | --- | --- | +| [File System](/docs/self-hosting/filesystem) (RocksDB-based) | Single-node deployments, including air-gapped installs | Production-ready, single node only | +| [PostgreSQL](/docs/self-hosting/postgres) | Multi-node deployments | Recommended for multi-node today, but experimental | +| FoundationDB | Largest production deployments | [Enterprise](/sales) | + +For multi-node deployments, run two or more engine nodes behind a load balancer and add NATS for pub/sub, which replaces the default PostgreSQL `LISTEN`/`NOTIFY` path at high throughput. Neither is needed for a single-node file system install. See the [Production Checklist](/docs/self-hosting/production-checklist). + +## Perimeter Checklist + +- **Admin token**: Generate a strong, random token for engine authentication and verify it is not exposed to clients. +- **TLS termination**: Encrypt connections to the engine via a reverse proxy or load balancer. +- **No public exposure**: Keep port `6420` reachable only from inside the perimeter unless clients outside it genuinely need access. +- **Health checks**: Configure liveness and readiness probes against the engine health endpoint. +- **Telemetry**: Leave OpenTelemetry export off, or point it at a collector inside the network. +- **Backups**: With the file system backend, back up the data directory. With PostgreSQL, configure automated backups and failover. + +## Full Configuration + +- [Self-Hosting Overview](/docs/self-hosting) for architecture and the self-host vs BYOC comparison +- [Installing Rivet Engine](/docs/self-hosting/install) for Docker, binary, and source installs +- [Docker Container](/docs/self-hosting/docker-container) and [Docker Compose](/docs/self-hosting/docker-compose) for container deployments +- [Kubernetes](/docs/self-hosting/kubernetes) for cluster deployments +- [Configuration](/docs/self-hosting/configuration) for every option and the full JSON schema +- [Endpoints](/docs/general/endpoints) for connecting your backend and clients +- [Production Checklist](/docs/self-hosting/production-checklist) before going live + +_Source doc path: /cookbook/vpc-air-gapped_ diff --git a/.agents/skills/live-cursors/reference/deploy/aws-ecs.md b/.agents/skills/live-cursors/reference/deploy/aws-ecs.md new file mode 100644 index 0000000..ab381ce --- /dev/null +++ b/.agents/skills/live-cursors/reference/deploy/aws-ecs.md @@ -0,0 +1,13 @@ +# Deploying to AWS ECS + +> Source: `src/content/docs/deploy/aws-ecs.mdx` +> Canonical URL: https://rivet.dev/docs/deploy/aws-ecs +> Description: Run your backend on Amazon ECS with Fargate. + +--- +See the [AWS ECS template](https://github.com/rivet-dev/template-aws-ecs) for deployment instructions: + +- [Deploy with Terraform](https://github.com/rivet-dev/template-aws-ecs#option-a-deploy-with-terraform) +- [Deploy with AWS CLI](https://github.com/rivet-dev/template-aws-ecs#option-b-deploy-with-aws-cli) + +_Source doc path: /docs/deploy/aws-ecs_ diff --git a/.agents/skills/live-cursors/reference/deploy/aws-lambda.md b/.agents/skills/live-cursors/reference/deploy/aws-lambda.md new file mode 100644 index 0000000..f1327cc --- /dev/null +++ b/.agents/skills/live-cursors/reference/deploy/aws-lambda.md @@ -0,0 +1,10 @@ +# Deploy To Amazon Web Services Lambda + +> Source: `src/content/docs/deploy/aws-lambda.mdx` +> Canonical URL: https://rivet.dev/docs/deploy/aws-lambda +> Description: _AWS Lambda is coming soon_ + +--- + + +_Source doc path: /docs/deploy/aws-lambda_ diff --git a/.agents/skills/live-cursors/reference/deploy/cloudflare.md b/.agents/skills/live-cursors/reference/deploy/cloudflare.md new file mode 100644 index 0000000..970078e --- /dev/null +++ b/.agents/skills/live-cursors/reference/deploy/cloudflare.md @@ -0,0 +1,53 @@ +# Deploying to Cloudflare Workers + +> Source: `src/content/docs/deploy/cloudflare.mdx` +> Canonical URL: https://rivet.dev/docs/deploy/cloudflare +> Description: Deploy an existing Rivet project to Cloudflare Workers. + +--- +This guide covers deploying an existing Rivet project to Cloudflare Workers. + +## Prerequisites + +- [Cloudflare account](https://dash.cloudflare.com/) +- [`wrangler`](https://developers.cloudflare.com/workers/wrangler/) configured for your account +- A Rivet namespace from the [Rivet Dashboard](https://dashboard.rivet.dev/) or a self-hosted Rivet Engine + +## Steps + +### Set up your project + +Follow the [Cloudflare Workers Quickstart](/docs/actors/quickstart/cloudflare) to set up your project locally. + +### Configure Wrangler + +Set your Rivet connection values as Worker variables. Find `RIVET_ENDPOINT` and `RIVET_PUBLIC_ENDPOINT` in the [Rivet Dashboard](https://dashboard.rivet.dev/) under **Settings → Namespace → Advanced → Backend Configuration** and copy them in. + +```toml wrangler.toml +name = "rivetkit-cloudflare" +main = "src/index.ts" +compatibility_date = "2025-04-01" +compatibility_flags = ["nodejs_compat"] + +[vars] +RIVET_ENDPOINT = "https://your-namespace:sk_...@api.rivet.dev" +RIVET_PUBLIC_ENDPOINT = "https://your-namespace@api.rivet.dev" +``` + +### Deploy + +```sh +npx wrangler deploy +``` + +### Register the Serverless Runner URL + +After deploy, set the Worker URL with the `/api/rivet` path as the serverless runner URL in Rivet. + +## Related + +- [Cloudflare Workers Quickstart](/docs/actors/quickstart/cloudflare) +- [Deploying to Supabase Functions](/docs/deploy/supabase) +- [SQLite](/docs/actors/sqlite) + +_Source doc path: /docs/deploy/cloudflare_ diff --git a/.agents/skills/live-cursors/reference/deploy/container-runner.md b/.agents/skills/live-cursors/reference/deploy/container-runner.md new file mode 100644 index 0000000..7bb0c5a --- /dev/null +++ b/.agents/skills/live-cursors/reference/deploy/container-runner.md @@ -0,0 +1,95 @@ +# Container Runner + +> Source: `src/content/docs/deploy/container-runner.mdx` +> Canonical URL: https://rivet.dev/docs/deploy/container-runner +> Description: Run any containerized server as a Rivet Actor. + +--- +The container runner (`rivet-container-runner`) is an adapter for running arbitrary containers as Rivet Actors. Use it for non-RivetKit workloads such as Unity or Godot dedicated game servers and batch jobs like FFmpeg transcoding. + +## Steps + +### Prerequisites + +- A containerized server (a Unity or Godot dedicated server, a plain Node process, or any HTTP/WebSocket server) +- Access to the [Rivet Cloud](https://dashboard.rivet.dev/) or a [self-hosted Rivet Engine](/docs/general/self-hosting) +- Docker running locally + +### Install in Your Container + +Download the static binary from Rivet's release artifacts in your Dockerfile and set it as the entrypoint, passing your server's launch command after `--`: + +```dockerfile @nocheck +FROM debian:bookworm-slim +RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates curl && rm -rf /var/lib/apt/lists/* + +# Install the Rivet container runner. +RUN curl -fsSL https://releases.rivet.dev/rivet/latest/container-runner/rivet-container-runner-x86_64-unknown-linux-musl \ + -o /usr/local/bin/rivet-container-runner \ + && chmod +x /usr/local/bin/rivet-container-runner + +# Your server binary and assets. +COPY build/ /game/ +WORKDIR /game + +ENTRYPOINT ["rivet-container-runner", "--", "./GameServer", "-batchmode", "-nographics", "-logFile", "-"] +``` + +Artifacts are published for `x86_64-unknown-linux-musl` and `aarch64-unknown-linux-musl`. The binaries are fully static, so they run in any Linux base image, including `scratch`. Pin a version by replacing `latest` with a release version, for example `https://releases.rivet.dev/rivet/2.3.3/container-runner/rivet-container-runner-x86_64-unknown-linux-musl`. + +### Deploy + +Deploy the image to [Rivet Compute](/docs/deploy/rivet-compute) with the CLI. For game servers, configure the pool with one actor per instance: + +```bash +npx @rivetkit/cli deploy \ + --token "$RIVET_CLOUD_TOKEN" \ + --instance-request-concurrency 1 \ + --dockerfile Dockerfile +``` + +### Create Actors and Connect Clients + +Create actors against the pool's runner (`default`) and connect clients through the gateway URL shown in the dashboard. WebSocket clients connect at the bare gateway URL with the `rivet` WebSocket subprotocol. + +## How It Works + +1. The engine cold-starts your container and calls `POST /api/rivet/start` on the port it injects as `RIVET_PORT`. +2. The runner spawns your server as a child process with `PORT` set to the child port, waits for the port to open, and reports the actor as running. +3. Gateway traffic for the actor arrives over Rivet's tunnel and is proxied to `127.0.0.1:`. WebSocket clients connect at the bare gateway URL with the `rivet` WebSocket subprotocol. Raw HTTP reaches the child under the `/request/*` prefix on the actor surface (the prefix is stripped before proxying); other paths are reserved for the runtime's own endpoints. +4. Child stdout and stderr are re-emitted with an `[actorId=... key=...]` prefix so actor logs are attributed in the dashboard. +5. When an actor stops, the runner sends its child `SIGTERM`, escalates to `SIGKILL` after a grace period, and exits the process once no actors remain. + +## Configuration + +All flags can also be set through environment variables: + +| Flag | Environment variable | Default | Description | +| --- | --- | --- | --- | +| `--port` | `RIVET_PORT` / `PORT` | `8080` | Serverless front-door HTTP port. Rivet Compute injects `RIVET_PORT` automatically. | +| `--child-port` | `CHILD_PORT` | `7770` | First local child port; each actor's child gets the next free port at or above this, exported to the child as `PORT`. | +| `--actor-name` | `RIVET_ACTOR_NAME` | `game` | Actor name this runner serves. | +| `--runner-version` | `RIVET_RUNNER_VERSION` | `1` | Version reported to the engine, used to drain old runners on deploy. | +| `--base-path` | `RIVET_SERVERLESS_BASE_PATH` | `/api/rivet` | Base path the engine calls for serverless start. | +| `--stop-grace-secs` | `RIVET_STOP_GRACE_SECS` | `25` | `SIGTERM` to `SIGKILL` grace period when stopping the child. Capped to a few seconds when the platform itself is reclaiming the instance, so shutdown fits inside the platform's own kill window. | +| `--readiness-timeout-secs` | `RIVET_READINESS_TIMEOUT_SECS` | `30` | How long to wait for the child's port to open before failing the start. | + +### Per-Actor Input + +The actor's `input` payload can override the launch spec per actor. All fields are optional and fall back to the entrypoint command. RivetKit clients pass this object directly; when creating actors through the raw engine API, encode it as CBOR before base64-encoding the `input` field: + +```json +{ + "command": ["./GameServer", "-batchmode"], + "args": ["-extra-flag"], + "env": { "MATCH_MODE": "ranked" } +} +``` + +`command` replaces the entrypoint command template, `args` are appended to it, and `env` adds environment variables for the child. + +## Source and Examples + +The runner and a full end-to-end example, including a Unity FishNet demo project and a local test harness, live in the Rivet repository under [`container-runner/`](https://github.com/rivet-dev/rivet/tree/main/container-runner). + +_Source doc path: /docs/deploy/container-runner_ diff --git a/.agents/skills/live-cursors/reference/deploy/freestyle.md b/.agents/skills/live-cursors/reference/deploy/freestyle.md new file mode 100644 index 0000000..eb6ed57 --- /dev/null +++ b/.agents/skills/live-cursors/reference/deploy/freestyle.md @@ -0,0 +1,136 @@ +# Deploying to Freestyle + +> Source: `src/content/docs/deploy/freestyle.mdx` +> Canonical URL: https://rivet.dev/docs/deploy/freestyle +> Description: Deploy RivetKit app to Freestyle.sh, a cloud platform for running AI-generated code with built-in security and scalability. + +--- +Freestyle provides built-in security for running untrusted AI-generated code, making it ideal for AI agent applications. Using Rivet, it is easy to deploy your vibe-coded or user-provided RivetKit backends straight to Freestyle. + +- [Freestyle + Rivet](https://github.com/rivet-dev/rivet/tree/main/examples/freestyle) — Complete example of deploying RivetKit app to Freestyle.sh. + +## Setup + +### Install packages + +Install RivetKit and Hono and create your registry: + +```bash +npm install rivetkit hono +``` + +### Configure serverless driver + +Update your server code to run the registry serverless with Deno. + +```typescript index.ts @hide +import { actor, setup } from "rivetkit"; + +export const counter = actor({ + state: { count: 0 }, + actions: { + increment: (c, x: number) => { + c.state.count += x; + return c.state.count; + }, + }, +}); + +export const registry = setup({ + use: { counter }, +}); +registry.start(); +``` + +```typescript server.ts @nocheck +import { registry } from "./index"; + +// Freestyle uses Deno under the hood for web deployments +// @ts-ignore Deno is a Freestyle runtime global +Deno.serve((request: Request) => registry.handler(request)); +``` + +The `Deno.serve` API is provided by Freestyle's runtime environment. The `@ts-ignore` comment suppresses TypeScript errors for this platform-specific API. + +### Deploy to Freestyle + +Deploy your application to Freestyle with the correct configuration. Create a deployment script or add this to your existing deployment process: + +```typescript @nocheck +const FREESTYLE_DOMAIN = "my-domain.style.dev"; // Change to your desired Freestyle domain + +declare const freestyle: any; +declare const buildDir: string; + +const res = await freestyle.deployWeb(buildDir, { + envVars: { + FREESTYLE_ENDPOINT: `https://${FREESTYLE_DOMAIN}`, + RIVET_RUNNER_KIND: "serverless", + // For self-hosted instances: + // RIVET_ENDPOINT: "http://127.0.0.1:6420", + RIVET_ENDPOINT: "api.rivet.dev", + }, + timeout: 60 * 5, // Increases max request lifetime on the runner + entrypoint: "server.ts", // File which starts serverless runner + domains: [FREESTYLE_DOMAIN], + build: false, +}); +``` + +Details on `buildDir` and other settings are available on [Freestyle docs](https://docs.freestyle.sh/web/web). + +Run this deployment script to push your application to Freestyle. + +**Deployment Configuration:** + +- `timeout: 60 * 5` - Set timeout to 5 minutes for actor operations - it's important to keep this high +- `entrypoint: "server.ts"` - Entry point file with your serverless setup +- `domains` - Your Freestyle domain(s) +- `build: false` - Disable build if you're pre-building your assets + +### Configure runner + +Update the runner configuration on the Rivet side to connect with your Freestyle deployment. Create a configuration script and run it after your Freestyle deployment is live: + +```typescript @nocheck +import { RivetClient } from "@rivetkit/engine-api-full"; + +const rivet = new RivetClient({ + environment: "https://api.rivet.dev", + token: process.env.RIVET_API_TOKEN, +}); + +const FREESTYLE_DOMAIN = "my-domain.style.dev"; // Change to your desired Freestyle domain +const RIVET_NAMESPACE = "my-rivet-namespace"; // Change to your Rivet namespace + +await rivet.runnerConfigsUpsert("freestyle-runner", { + namespace: RIVET_NAMESPACE, + datacenters: { + default: { + serverless: { + url: `https://${FREESTYLE_DOMAIN}/start`, + runnersMargin: 1, + minRunners: 1, + maxRunners: 1, + slotsPerRunner: 1, + // Must be shorter than Freestyle request `timeout` config + requestLifespan: 60 * 5 - 5, + }, + }, + }, +}); +``` + +Execute this configuration script to register your Freestyle deployment with Rivet. + +**Runner Configuration:** + +- `url` - Freestyle deployment URL with `/start` endpoint +- `runnersMargin` - Buffer of runners to maintain +- `minRunners/maxRunners` - Scaling limits +- `slotsPerRunner` - Concurrent actors per runner +- `requestLifespan` - Request timeout (slightly less than Freestyle timeout) + +Once executed, Rivet will be connected to your Freestyle serverless instance. + +_Source doc path: /docs/deploy/freestyle_ diff --git a/.agents/skills/live-cursors/reference/deploy/gcp-cloud-run.md b/.agents/skills/live-cursors/reference/deploy/gcp-cloud-run.md new file mode 100644 index 0000000..f140950 --- /dev/null +++ b/.agents/skills/live-cursors/reference/deploy/gcp-cloud-run.md @@ -0,0 +1,74 @@ +# Deploying to Google Cloud Run + +> Source: `src/content/docs/deploy/gcp-cloud-run.mdx` +> Canonical URL: https://rivet.dev/docs/deploy/gcp-cloud-run +> Description: Deploy your RivetKit app to Google Cloud Run. + +--- +## Steps + +### Prerequisites + +- Google Cloud project with Cloud Run and Artifact Registry enabled +- `gcloud` CLI authenticated (`gcloud auth login`) and project set (`gcloud config set project YOUR_PROJECT`) +- Artifact Registry repository or Container Registry enabled +- Your RivetKit app + - If you don't have one, see the [Quickstart](/docs/actors/quickstart) page or our [Examples](https://github.com/rivet-dev/rivet/tree/main/examples) +- Access to the [Rivet Cloud](https://dashboard.rivet.dev/) or a [self-hosted Rivet Engine](/docs/general/self-hosting) + +### Package Your App + +Create a `Dockerfile` in your project root: + +```dockerfile +FROM node:20-alpine +WORKDIR /app +COPY package*.json ./ +RUN npm ci --omit=dev +COPY . . +ENV PORT=8080 +CMD ["node", "server.js"] +``` + +### Build and Push the Image + +Use Cloud Build to build and push the image. Replace the region and repository with your own. + +```bash +gcloud builds submit --tag us-central1-docker.pkg.dev/YOUR_PROJECT/rivetkit-app/rivetkit-app:latest +``` + +### Set Environment Variables + +After creating your project on the Rivet dashboard, select Google Cloud Run as your provider. You'll be provided `RIVET_ENDPOINT` and `RIVET_PUBLIC_ENDPOINT` environment variables to use when deploying. + +### Deploy to Cloud Run + +Deploy the service to Cloud Run, passing the Rivet environment variables. Adjust the region and image as needed. + +```bash +gcloud run deploy rivetkit-app \ + --image us-central1-docker.pkg.dev/YOUR_PROJECT/rivetkit-app/rivetkit-app:latest \ + --region us-central1 \ + --allow-unauthenticated \ + --min-instances 1 \ + --set-env-vars RIVET_ENDPOINT=,RIVET_PUBLIC_ENDPOINT= +``` + +### Connect to Rivet + +1. After deployment, note the service URL (e.g. `https://rivetkit-app-xxxxx-uc.a.run.app`) +2. On the Rivet dashboard, paste your URL with the `/api/rivet` path into the connect form (e.g. `https://rivetkit-app-xxxxx-uc.a.run.app/api/rivet`) +3. Click "Done" + +### Verify + +Confirm the service is running: + +```bash +gcloud run services describe rivetkit-app --region us-central1 --format 'value(status.conditions[?type="Ready"].status)' +``` + +Your app should appear as connected on the Rivet dashboard once the service reports ready. + +_Source doc path: /docs/deploy/gcp-cloud-run_ diff --git a/.agents/skills/live-cursors/reference/deploy/hetzner.md b/.agents/skills/live-cursors/reference/deploy/hetzner.md new file mode 100644 index 0000000..5b61024 --- /dev/null +++ b/.agents/skills/live-cursors/reference/deploy/hetzner.md @@ -0,0 +1,10 @@ +# Deploying to Hetzner + +> Source: `src/content/docs/deploy/hetzner.mdx` +> Canonical URL: https://rivet.dev/docs/deploy/hetzner +> Description: Please see the VM & Bare Metal guide. + +--- + + +_Source doc path: /docs/deploy/hetzner_ diff --git a/.agents/skills/live-cursors/reference/deploy/kubernetes.md b/.agents/skills/live-cursors/reference/deploy/kubernetes.md new file mode 100644 index 0000000..43496a9 --- /dev/null +++ b/.agents/skills/live-cursors/reference/deploy/kubernetes.md @@ -0,0 +1,121 @@ +# Deploying to Kubernetes + +> Source: `src/content/docs/deploy/kubernetes.mdx` +> Canonical URL: https://rivet.dev/docs/deploy/kubernetes +> Description: Deploy your RivetKit app to any Kubernetes cluster. + +--- +## Steps + +### Prerequisites + +- A Kubernetes cluster with `kubectl` access (EKS, GKE, k3s, etc.) +- Container registry credentials (Docker Hub, GHCR, GCR, etc.) +- Your RivetKit app + - If you don't have one, see the [Quickstart](/docs/actors/quickstart) page or our [Examples](https://github.com/rivet-dev/rivet/tree/main/examples) +- Access to the [Rivet Cloud](https://dashboard.rivet.dev/) or a [self-hosted Rivet Engine](/docs/general/self-hosting) + +### Package Your App + +Create a `Dockerfile` in your project root: + +```dockerfile +FROM node:20-alpine +WORKDIR /app +COPY package*.json ./ +RUN npm ci --omit=dev +COPY . . +ENV PORT=8080 +CMD ["node", "server.js"] +``` + +### Build and Push the Image + +```bash +docker build -t registry.example.com/your-team/rivetkit-app:latest . +docker push registry.example.com/your-team/rivetkit-app:latest +``` + +Replace `registry.example.com/your-team` with your registry path. Auth with `docker login` first if needed. + +### Set Environment Variables + +After creating your project on the Rivet dashboard, select Kubernetes as your provider. You'll be provided `RIVET_ENDPOINT` and `RIVET_PUBLIC_ENDPOINT` environment variables. + +Create a `rivetkit-secrets.yaml` for your environment variables: + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: rivetkit-secrets +type: Opaque +stringData: + RIVET_ENDPOINT: + RIVET_PUBLIC_ENDPOINT: +``` + +### Deploy to Kubernetes + +Create a `deployment.yaml`: + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: rivetkit-app +spec: + replicas: 1 + selector: + matchLabels: + app: rivetkit-app + template: + metadata: + labels: + app: rivetkit-app + spec: + # Allow enough time for actors to gracefully stop on SIGTERM. + # The runner waits up to 30m for actors to finish. + # Add buffer for runner shutdown overhead after actors stop. + # See: /docs/actors/versions#graceful-shutdown-sigterm + terminationGracePeriodSeconds: 2100 + containers: + - name: rivetkit-app + image: registry.example.com/your-team/rivetkit-app:latest + envFrom: + - secretRef: + name: rivetkit-secrets +``` + +Apply both manifests: + +```bash +kubectl apply -f rivetkit-secrets.yaml +kubectl apply -f deployment.yaml +``` + +### Connect to Rivet + +1. Add a `Service` and Ingress to expose your app externally (e.g. `my-app.example.com`) +2. On the Rivet dashboard, paste your domain with the `/api/rivet` path into the connect form (e.g. `https://my-app.example.com/api/rivet`) +3. Click "Done" + +Rivet envoys connect to your app over long-lived WebSockets, and your app's clients (browsers, SDKs) do the same. Default Ingress and cloud load balancer idle timeouts (typically 30 to 60 seconds) drop these connections and cause reconnect storms. + +Raise the idle / read / send timeout on every Ingress and load balancer in front of your app to at least 1 hour (`3600` seconds). Examples: + +- **NGINX Ingress**: `nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"` and `nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"` +- **AWS Load Balancer Controller (ALB)**: `alb.ingress.kubernetes.io/load-balancer-attributes: idle_timeout.timeout_seconds=3600` +- **GCE Ingress (GKE)**: set `timeoutSec: 3600` on the `BackendConfig` referenced by the Service. + +### Verify + +Check that the pod is running: + +```bash +kubectl get pods -l app=rivetkit-app +``` + +Your app should appear as connected on the Rivet dashboard once the pod is ready. + +_Source doc path: /docs/deploy/kubernetes_ diff --git a/.agents/skills/live-cursors/reference/deploy/railway.md b/.agents/skills/live-cursors/reference/deploy/railway.md new file mode 100644 index 0000000..640ac37 --- /dev/null +++ b/.agents/skills/live-cursors/reference/deploy/railway.md @@ -0,0 +1,45 @@ +# Deploying to Railway + +> Source: `src/content/docs/deploy/railway.mdx` +> Canonical URL: https://rivet.dev/docs/deploy/railway +> Description: Deploy your RivetKit app to Railway. + +--- +## Steps + +### Prerequisites + +- [Railway account](https://railway.app) +- Your RivetKit app + - If you don't have one, see the [Quickstart](/docs/actors/quickstart) page or our [Examples](https://github.com/rivet-dev/rivet/tree/main/examples) +- Access to the [Rivet Cloud](https://dashboard.rivet.dev/) or a [self-hosted Rivet Engine](/docs/general/self-hosting) + +### Deploy to Railway + +1. Connect your GitHub account to Railway +2. Select your repository containing your RivetKit app +3. Railway will automatically detect and deploy your app + +See [Railway's deployment docs](https://docs.railway.com/quick-start) for more details. + +### Set Environment Variables + +After creating your project on the Rivet dashboard, select Railway as your provider. You'll be provided `RIVET_ENDPOINT` and `RIVET_PUBLIC_ENDPOINT` environment variables to add to your Railway service. + +See [Railway's environment variables docs](https://docs.railway.com/guides/variables#service-variables) for more details. + +### Connect to Rivet + +1. In your Railway project, go to **Settings > Networking** +2. Click **Create Custom Domain** then **Create Domain** to generate a Railway domain (e.g. `my-app.railway.app`) +3. On the Rivet dashboard, paste your domain with the `/api/rivet` path into the connect form (e.g. `https://my-app.railway.app/api/rivet`) +4. Click "Done" + +Rivet envoys connect to your app over long-lived WebSockets, and your app's clients (browsers, SDKs) do the same. Railway's HTTP proxy supports WebSockets, but if you front your app with your own reverse proxy (NGINX, Caddy, etc.) inside the Railway service, raise its idle / read timeout to at least 1 hour (`3600` seconds). Default proxy timeouts (typically 30 to 60 seconds) drop these connections and cause reconnect storms. + +### Configure Sleeping & Multi-Region (Optional) + +- [Enable App Sleeping](https://docs.railway.com/reference/app-sleeping) to reduce costs when idle +- [Configure Multi-Region](https://docs.railway.com/reference/deployment-regions) to deploy closer to your users + +_Source doc path: /docs/deploy/railway_ diff --git a/.agents/skills/live-cursors/reference/deploy/rivet-compute.md b/.agents/skills/live-cursors/reference/deploy/rivet-compute.md new file mode 100644 index 0000000..69507aa --- /dev/null +++ b/.agents/skills/live-cursors/reference/deploy/rivet-compute.md @@ -0,0 +1,144 @@ +# Deploying to Rivet Compute + +> Source: `src/content/docs/deploy/rivet-compute.mdx` +> Canonical URL: https://rivet.dev/docs/deploy/rivet-compute +> Description: Run your backend on Rivet Compute. + +--- +Using an AI coding agent? Open **Connect** on the [Rivet dashboard](https://dashboard.rivet.dev), select **Rivet Cloud**, and paste the one-shot prompt into your agent and have it connect with Rivet Compute for you. + +## Steps + +### Prerequisites + +- Your RivetKit app + - If you don't have one, see the [Quickstart](/docs/actors/quickstart) page or our [Examples](https://github.com/rivet-dev/rivet/tree/main/examples) +- A [Rivet Cloud](https://dashboard.rivet.dev) account and project +- Docker running locally + +### Create a Dockerfile + +Add a `Dockerfile` to your project root that builds and runs your RivetKit server: + +```dockerfile @nocheck +FROM node:24-alpine +WORKDIR /app +COPY package*.json ./ +RUN npm ci --omit=dev +COPY . . +CMD ["node", "src/server.js"] +``` + +### Get Your Cloud Token + +1. Open the [Rivet dashboard](https://dashboard.rivet.dev) and navigate to your project +2. Click **Connect** and select **Rivet Cloud** +3. Copy the **`RIVET_CLOUD_TOKEN`** value shown + +### Deploy + +Run the deploy command from your project root. The token is saved to `~/.rivet/credentials`, so later deploys can omit it. + +```bash +npx @rivetkit/cli deploy --token cloud_api_xxxxx +``` + +The CLI resolves your project from the token, builds and pushes your Docker image to Rivet's built-in registry, upserts the managed pool, and prints the deployment URL on stdout when the pool is ready. + +### Optionally Add CI + +After local deploys work, install the GitHub Actions workflow that deploys on every push and pull request: + +```bash +npx @rivetkit/cli setup-ci +``` + +This writes `.github/workflows/rivet-deploy.yml`. Add your token as a repository secret to enable it: + +```bash +gh secret set RIVET_CLOUD_TOKEN +``` + +The workflow creates production and pull-request namespaces, posts preview links, and cleans up PR namespaces when pull requests close. See the [CLI reference](/docs/cli) for all commands. + +### Monitor Deployment + +The dashboard shows live status as Rivet Compute provisions your backend: + +| Status | Description | +| --- | --- | +| Initializing | Starting the runtime environment | +| Deploying | Pulling and launching your container | +| Binding | Connecting the runner to the network | +| Ready | Deployment complete | + +Once the status reaches **Ready**, your backend is live and actors are available for connections. + +If you are an agent monitoring the deployment via API rather than the dashboard, poll the managed-pool endpoint on the Cloud API. + +The `RIVET_CLOUD_TOKEN` secret is a `cloud_api_*` management token scoped to the Cloud API at `cloud-api.rivet.dev`. Use it for `Authorization: Bearer ...` against the Cloud API. Do not confuse it with a `pk_*` publishable key, which is scoped to the Rivet Engine API at `api.rivet.dev` and will 401 against this endpoint. + +Substitute `$CLOUD_API_URL` (typically `https://cloud-api.rivet.dev`), `$PROJECT`, `$ORG`, `$CLOUD_NAMESPACE`, and `$CLOUD_TOKEN`. + +Poll every 5 seconds until `status` is `ready`. Stop and investigate if `status` is `error`. + +```bash +curl -s "$CLOUD_API_URL/projects/$PROJECT/namespaces/$CLOUD_NAMESPACE/managed-pools/default?org=$ORG" -H "Authorization: Bearer $CLOUD_TOKEN" +``` + +## Checking Logs + +Use the CLI to read your deployment's logs. By default `rivet logs` prints the last 100 lines from the `production` namespace, oldest to newest, then exits. + +```bash +npx @rivetkit/cli logs +``` + +The CLI resolves your token the same way `deploy` does (the `--token` flag, then the `RIVET_CLOUD_TOKEN` environment variable, then `~/.rivet/credentials`). + +### Follow logs live + +Pass `--follow` (`-f`) to stream new logs as they arrive instead of fetching history: + +```bash +npx @rivetkit/cli logs --follow +``` + +Each formatted line is printed as ` [] `: + +``` +2026-06-16T18:26:51.160Z [INFO] eu-central-1 server listening on port 3000 +2026-06-17T11:24:20.425Z [ERROR] us-east-1 failed to connect to upstream +``` + +A few examples: + +```bash +# Last 200 lines from a specific namespace +npx @rivetkit/cli logs --namespace production -n 200 + +# Live tail, only lines containing "error" +npx @rivetkit/cli logs --follow --contains error + +# JSON output piped to jq +npx @rivetkit/cli logs -n 50 --json | jq . +``` + +## Troubleshooting + +If the status stays in **Initializing** for more than a few minutes, verify that: + +- The `RIVET_CLOUD_TOKEN` secret is correctly set in your GitHub repository +- The GitHub Actions workflow completed without errors — check the run logs + +If the status shows **Error**, check that your container starts successfully and does not exit immediately (you can check this with container logs). Common causes: + +- The server file is not calling `registry.start()` +- A runtime crash on startup — test the image locally with `docker run` +- The server is not listening on the `RIVET_PORT` environment variable (RivetKit reads `RIVET_PORT`, defaulting to `3000`) + +## Pricing + +You are billed for the compute resources your deployment uses while it is running. + +_Source doc path: /docs/deploy/rivet-compute_ diff --git a/.agents/skills/live-cursors/reference/deploy/supabase.md b/.agents/skills/live-cursors/reference/deploy/supabase.md new file mode 100644 index 0000000..fa98436 --- /dev/null +++ b/.agents/skills/live-cursors/reference/deploy/supabase.md @@ -0,0 +1,50 @@ +# Deploying to Supabase Functions + +> Source: `src/content/docs/deploy/supabase.mdx` +> Canonical URL: https://rivet.dev/docs/deploy/supabase +> Description: Deploy an existing Rivet project to Supabase Edge Functions. + +--- +This guide covers deploying an existing Rivet project to Supabase Edge Functions. + +## Prerequisites + +- [Supabase project](https://supabase.com/) +- [Supabase CLI](https://supabase.com/docs/guides/cli) configured for your project +- A Rivet namespace from the [Rivet Dashboard](https://dashboard.rivet.dev/) or a self-hosted Rivet Engine + +## Steps + +### Set up your project + +Follow the [Supabase Functions Quickstart](/docs/actors/quickstart/supabase) to set up your project locally. + +### Set Secrets + +Set your Rivet connection values as Supabase secrets. Find `RIVET_ENDPOINT` and `RIVET_PUBLIC_ENDPOINT` in the [Rivet Dashboard](https://dashboard.rivet.dev/) under **Settings → Namespace → Advanced → Backend Configuration** and copy them in. + +```sh +npx supabase secrets set \ + RIVET_ENDPOINT=https://your-namespace:sk_...@api.rivet.dev \ + RIVET_PUBLIC_ENDPOINT=https://your-namespace@api.rivet.dev +``` + +### Deploy + +Make sure the function has the `deno.json` import map from the [quickstart](/docs/actors/quickstart/supabase) that points `rivetkit` at `@rivetkit/supabase`. It keeps the deploy to the WebAssembly runtime; without it the deploy pulls Rivet's native engine and fails with a `413` (request too large). + +```sh +npx supabase functions deploy rivet +``` + +### Register the Serverless Runner URL + +After deploy, set the function URL with the `/api/rivet` path as the serverless runner URL in Rivet. For a function named `rivet`, this is usually `https://your-project.functions.supabase.co/functions/v1/rivet/api/rivet`. + +## Related + +- [Supabase Functions Quickstart](/docs/actors/quickstart/supabase) +- [Deploying to Cloudflare Workers](/docs/deploy/cloudflare) +- [SQLite](/docs/actors/sqlite) + +_Source doc path: /docs/deploy/supabase_ diff --git a/.agents/skills/live-cursors/reference/deploy/vercel.md b/.agents/skills/live-cursors/reference/deploy/vercel.md new file mode 100644 index 0000000..e8b5ba3 --- /dev/null +++ b/.agents/skills/live-cursors/reference/deploy/vercel.md @@ -0,0 +1,138 @@ +# Deploying to Vercel + +> Source: `src/content/docs/deploy/vercel.mdx` +> Canonical URL: https://rivet.dev/docs/deploy/vercel +> Description: Deploy your Next.js Rivet app to Vercel. + +--- +This guide assumes a Next.js app. + +## Prerequisites + +- [Vercel account](https://vercel.com/) +- A Next.js Rivet app +- Access to the [Rivet Cloud](https://dashboard.rivet.dev/) or a [self-hosted Rivet Engine](/docs/general/self-hosting) + +## Steps + +### Set up your project + +Follow the [Next.js Quickstart](/docs/actors/quickstart/next-js) to set up your project. + +### Verify Your Project Structure + +Your Next.js project should have the following structure: + +- `src/app/api/rivet/[...all]/route.ts`: RivetKit route handler +- `src/rivet/registry.ts`: Actor definitions and registry + +The route handler sets `maxDuration` to extend the serverless function timeout so long-lived actor requests are not cut short: + +```ts src/app/api/rivet/[...all]/route.ts @nocheck +import { toNextHandler } from "@rivetkit/next-js"; +import { registry } from "@/rivet/registry"; + +export const maxDuration = 300; + +export const { GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS } = toNextHandler(registry); +``` + +### Set Environment Variables + +Set `RIVET_ENDPOINT` and `RIVET_PUBLIC_ENDPOINT` in your Vercel project settings using the URL auth format: + +``` +RIVET_ENDPOINT=https://my-namespace:sk_****@api.rivet.dev +RIVET_PUBLIC_ENDPOINT=https://my-namespace:pk_****@api.rivet.dev +``` + +`RIVET_ENDPOINT` uses the secret token for server-side access. `RIVET_PUBLIC_ENDPOINT` uses the publishable token and tells the metadata endpoint what connection info to provide to clients. + +### Deploy to Vercel + +1. Connect your GitHub repository to Vercel +2. Vercel will deploy your app + +### Configure Preview Deployments (Recommended) + +Add a GitHub action to automatically create isolated Rivet namespaces for each PR: + +1. Add these secrets to your GitHub repository: + - `RIVET_CLOUD_TOKEN`: Get from [Rivet Dashboard](https://dashboard.rivet.dev) → Settings → Advanced → Cloud API Tokens + - `VERCEL_TOKEN`: Get from [Vercel Account Settings](https://vercel.com/account/tokens) + +2. Create `.github/workflows/rivet-preview.yml`: + +```yaml +name: Rivet Preview + +on: + pull_request: + types: [opened, synchronize, reopened] + push: + branches: [main] + +concurrency: + group: rivet-preview-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + rivet-preview: + runs-on: ubuntu-latest + permissions: + pull-requests: write + steps: + - uses: rivet-dev/preview-namespace-action@v1 + with: + platform: vercel + rivet-token: ${{ secrets.RIVET_CLOUD_TOKEN }} + vercel-token: ${{ secrets.VERCEL_TOKEN }} +``` + +## Troubleshooting + +``` +Error: ENOENT: no such file or directory, mkdir '.../rivetkit/.../state' +``` + +**Cause:** The `RIVET_ENDPOINT` environment variable is not configured. RivetKit falls back to the file system driver, which fails in Vercel's read-only serverless environment. + +**Solution:** Ensure `RIVET_ENDPOINT` is set with your Rivet endpoint using the URL auth format: + +``` +RIVET_ENDPOINT=https://my-namespace:sk_****@api.rivet.dev +``` + +If using the [preview-namespace-action](https://github.com/rivet-dev/preview-namespace-action), this is configured automatically. + +The `/api/rivet/metadata` endpoint returns data but `clientEndpoint`, `clientNamespace`, and `clientToken` are missing. + +**Cause:** The `RIVET_PUBLIC_ENDPOINT` environment variable is not configured. This tells the metadata endpoint what connection info to provide to clients. + +**Solution:** Set `RIVET_PUBLIC_ENDPOINT` with the publishable token (for client access): + +``` +RIVET_PUBLIC_ENDPOINT=https://my-namespace:pk_****@api.rivet.dev +``` + +If using the [preview-namespace-action](https://github.com/rivet-dev/preview-namespace-action), this is configured automatically. + +Rivet fails to connect to your Vercel deployment with a 401 error mentioning "Authentication Required". + +**Cause:** [Vercel Deployment Protection](https://vercel.com/docs/security/deployment-protection) is blocking requests from Rivet. + +**Solution:** + +1. Create a bypass secret in your Vercel project settings +2. In Rivet, go to **Settings > Providers** +3. Click the three dots on your provider and select **Edit** +4. Click **Add Header** and add `x-vercel-protection-bypass` with your bypass secret + +If using the [preview-namespace-action](https://github.com/rivet-dev/preview-namespace-action), this is configured automatically. + +## Related + +- [Next.js Quickstart](/docs/actors/quickstart/next-js) +- [Self-Hosting](/docs/general/self-hosting) + +_Source doc path: /docs/deploy/vercel_ diff --git a/.agents/skills/live-cursors/reference/deploy/vm-and-bare-metal.md b/.agents/skills/live-cursors/reference/deploy/vm-and-bare-metal.md new file mode 100644 index 0000000..6a123f9 --- /dev/null +++ b/.agents/skills/live-cursors/reference/deploy/vm-and-bare-metal.md @@ -0,0 +1,96 @@ +# Deploying to VMs & Bare Metal + +> Source: `src/content/docs/deploy/vm-and-bare-metal.mdx` +> Canonical URL: https://rivet.dev/docs/deploy/vm-and-bare-metal +> Description: Deploy your RivetKit app to any Linux VM or bare metal host. + +--- +## Steps + +### Prerequisites + +- A Linux VM or bare metal server with SSH access +- Your RivetKit app + - If you don't have one, see the [Quickstart](/docs/actors/quickstart) page or our [Examples](https://github.com/rivet-dev/rivet/tree/main/examples) +- Access to the [Rivet Cloud](https://dashboard.rivet.dev/) or a [self-hosted Rivet Engine](/docs/general/self-hosting) + +### Upload Your App + +- Build your RivetKit app locally +- Copy the build output to your server (example): + +```bash +scp -r ./dist user@server:/opt/rivetkit-app +``` + +Place the files somewhere readable by the service user, such as `/opt/rivetkit-app`. + +### Set Environment Variables + +After creating your project on the Rivet dashboard, select VM & Bare Metal as your provider. You'll be provided `RIVET_ENDPOINT` and `RIVET_PUBLIC_ENDPOINT` environment variables to use in the next step. + +### Create the systemd Service + +Create `/etc/systemd/system/rivetkit-app.service`: + +```ini +[Unit] +Description=RivetKit App +After=network.target + +[Service] +Type=simple +WorkingDirectory=/opt/rivetkit-app +ExecStart=/usr/bin/node server.js +Restart=on-failure +Environment=RIVET_ENDPOINT= +Environment=RIVET_PUBLIC_ENDPOINT= + +[Install] +WantedBy=multi-user.target +``` + +Replace the environment values with those from the Rivet dashboard and adjust paths to match your deployment. + +### Start the Service + +Reload systemd units and start the service: + +```bash +sudo systemctl daemon-reload +sudo systemctl enable --now rivetkit-app.service +``` + +### Connect to Rivet + +1. Ensure your server is accessible via a public URL (e.g. `https://my-app.example.com`) +2. On the Rivet dashboard, paste your URL with the `/api/rivet` path into the connect form (e.g. `https://my-app.example.com/api/rivet`) +3. Click "Done" + +If you front your app with a reverse proxy or load balancer (NGINX, HAProxy, Caddy, AWS ALB/NLB, etc.), raise its idle / read timeout to at least 1 hour (`3600` seconds). Rivet envoys connect to your app over long-lived WebSockets, and clients (browsers, SDKs) do the same. Default proxy timeouts (typically 30 to 60 seconds) drop these connections and cause reconnect storms. + +Examples: + +- **NGINX**: `proxy_read_timeout 3600s;` and `proxy_send_timeout 3600s;` in the relevant `location` block. +- **HAProxy**: `timeout client 1h` and `timeout server 1h` in the matching frontend / backend. +- **AWS ALB / NLB**: set the load balancer attribute `idle_timeout.timeout_seconds = 3600`. + +## Operating + +### Restart + +Restart the service after deploying new builds or environment changes: + +```bash +sudo systemctl restart rivetkit-app.service +``` + +### Logs + +Follow realtime logs when debugging: + +```bash +sudo journalctl -u rivetkit-app.service -f +``` + +_Source doc path: /docs/deploy/vm-and-bare-metal_ diff --git a/.agents/skills/live-cursors/reference/general/actor-configuration.md b/.agents/skills/live-cursors/reference/general/actor-configuration.md new file mode 100644 index 0000000..72bc6fd --- /dev/null +++ b/.agents/skills/live-cursors/reference/general/actor-configuration.md @@ -0,0 +1,19 @@ +# Actor Configuration + +> Source: `src/content/docs/general/actor-configuration.mdx` +> Canonical URL: https://rivet.dev/docs/general/actor-configuration +> Description: This page documents the configuration options available when defining a RivetKit actor. The actor configuration is passed to the `actor()` function. + +--- +## Basic Example + +## Configuration Reference + +## Related + +- [Registry Configuration](/docs/general/registry-configuration): Configure the RivetKit registry +- [State](/docs/actors/state): Managing actor state +- [Actions](/docs/actors/actions): Defining actor actions +- [Lifecycle](/docs/actors/lifecycle): Actor lifecycle hooks + +_Source doc path: /docs/general/actor-configuration_ diff --git a/.agents/skills/live-cursors/reference/general/architecture.md b/.agents/skills/live-cursors/reference/general/architecture.md new file mode 100644 index 0000000..3da2912 --- /dev/null +++ b/.agents/skills/live-cursors/reference/general/architecture.md @@ -0,0 +1,452 @@ +# Architecture + +> Source: `src/content/docs/general/architecture.mdx` +> Canonical URL: https://rivet.dev/docs/general/architecture +> Description: - rivetkit is the typescript library used for both local development & to connect your application to rivet - a rivetkit instance is called a "runner." you can run multiple runners to scale rivetkit horizontally. read omre about runners below. + +--- +## 3 ways of running + +### rivetkit + +- rivetkit is the typescript library used for both local development & to connect your application to rivet +- a rivetkit instance is called a "runner." you can run multiple runners to scale rivetkit horizontally. read more about runners below. + +#### local development + +- in local development, rivetkit provides a full actor environment for single-node deployments + +#### drivers + +- rivetkit supports multiple drivers. currently supports: file system (default in local dev), memory, rivet engine (used for rivet cloud & self-hosting), cloudflare durable objects (does not rely on rivet engine) +- drivers are very flexible to enable you to write your actors once and plug in to any system that fits your architecture adequately +- see the driver interface + - actordriver https://github.com/rivet-dev/rivet/blob/eeb01fc4d9ca0e06f2e740d267bd53280ca7330e/rivetkit-typescript/packages/rivetkit/src/actor/driver.ts + - managerdriver https://github.com/rivet-dev/rivet/blob/eeb01fc4d9ca0e06f2e740d267bd53280ca7330e/rivetkit-typescript/packages/rivetkit/src/manager/driver.ts + +### rivet cloud + +- provides multi-region and highest performance out of the box +- accessible at dashboard.rivet.dev and the api is avialble at api.rivet.dev + +### rivet self-hosted + +- available as a standalone rust binary or a docker contianer +- can be configured to persist to postgres or rocksdb +- can scale horiziontally across multipe nodes and can scale across multiple regions +- see [self-hosting docs](/docs/self-hosting/) + +## actors + +- Actors for long-lived processes with durable state, realtime, and hibernate when not in use. read more about actors at a high level at (link to actors/index) + +### actor-per-entity + +- actors are designed to have an actor-per-entity +- you can think about actors a bit like objects in object-oriented programming where each is responsible for their own state and expose methods (ie actions in our case) +- examples include + - actor per user + - actor per user session + - actor per document + - actor per game room + - actor per tenant + - actor per rate limit topic + +### architecting for scale + +- actors scale by: + - having isolated state to each actor that combines compute and storage for in-memory reads and writes + - communication is standardized based on actions & events + - scale horizontally +- read more about scaling at [Design Patterns](/docs/actors/design-patterns) + +### horizontal scaling + +- actors can run across multiple rivetkit runners. this is orchestrated by rivetkit itself. + +### lifecycle + +actors have create, destroy, wake, and sleep lifecycle hooks that you can implement to modify behavior. see the lifecycle docs for reference on actor lifecycel hook sequences + +### actor sleeping + +- actors sleep when not in use +- an actor is considered not in use when there are no active network connections to the actor (or the network connections are hibernatable websockets, see below) and there are no actions in flight +- actors have a sleep timeout (configured in `options.sleepTimeout`) that decides how long to keep the actor in memory with no recent activity +- sleep can be held off for the lifetime of a promise with `c.keepAwake(promise)` +- see the [sleeping docs](/docs/actors/lifecycle#sleeping) for full details + +### wake events + +- actors can wake to any of the follwoing events: + - network requests + - websocket messages + - alarms (see scheduling docs) + +### live actor migration + +- live actor migrations lets your application ugprade, crash, or hot reload cahnges without interruption to your user or application (including websockets) +- this is powered by hibernating websockets for live websocket migraiton & our fault tolerance mechanism (read more below) + +### coldstart performance + +- actors have negligible coldstart performance. the code to run the actor is already started (ie the runner), so creating/starting an actor is incredibly cheap. +- creating new actors with a key requires some overhead to communicate with other regions in order to reserve the actor's key (see below). actors can be created without keys with near-0 latency. + +### multi-region, globally unique actor keys + +- actors can optionally have a globally unique "key" +- when creating an actor with a key +- this system is highly optimized to reduce wan round trips using per-key Paxos with a custom database called Epoxy (https://github.com/rivet-dev/rivet/tree/main/engine/packages/epoxy) +- limitation: when creating an actor with a given key, that key will always be pinned to that region even if the actor is destroyed. creating a new actor with the same key will always live in the same region. +- see the actors keys document + +### input + +- actors have input data that can be passed to them when constructed +- this is similar to apssing data to a constructor in an object + +### generic parameters + +actor definitions include the following generic parameters that you'll see frequently in the code: + +- state +- conn state +- conn params +- ephemeral variables +- input data +- (experimental) database connector + +### persistence + +- state automatically flushes to storage intelligently +- to force a state flush and wait for it to finish, call (TODO: look this up in state document) +- read more about state persistence in the state document (link to document) +- state is stored in the same place as where the actor lives. loading an actor in to memory has comparable performance to network attached storage, and once in memory, has performance of any standard in-memory read/write like a variable. + +### scheduling & alarms + +- actors have a scheduling api to be able to wake up at any time in the indefinite future +- think of this like setTimeout but without a max timeout +- rivet is responsible for waking the actor when this timeout wakes + +### ephemeral variables + +- actors have the ability to create ephemrla variables for things that you do not want to be persisted with the actor's state +- this is useful for non-serializable data like a utility class like a pubsubs erver or something (TODO extra info) +- link to ephemeral variables docs + +### actions + +- for stateless clients, actions are sent as http requests via `POST /gateway/{actor id}/actions/{action name}` +- for stateful clients, actions are sent as websocket messages + +### events & subscriptions + +- events are sent as websocket messages + +### error handling + +- this is different than fault tolerance: + - error handling is a user error + - fault tolerance is something goes wrong that your applciation was not built to handle (ie hard crash, oom, network error) +- rivet provdies a special UserError class to throw custom errors that will be returned to the client +- all other errors are returned as a generic "internal error" +- this is becuase leaking error deatils is a common security hole, so we default to expose-nothing errors + +### logging + +- rivet uses pino for logging +- we expose a scoped child logger for each actor at `c.log` that automatically logs the actor id + key +- this allows you to search lgos easily by actor id without having to log the actor id frequently +- logs can be configured via the `RIVET_LOG_LEVEL` env var + +### fault tolerance + +- actors are fault tolerant, meaning that the host machine can crash and the actors will proceed to operate as if nothing happened +- runners maintain a socket with rivet engine. when this socket closes or takes to long to ping, actors will reschedule +- hibernating websockets (enabled by default) will live-migrate to the new actor as if nothing happened + +### crash policy + +- there are 3 crash policies: sleep, restart, and destroyed + - sleep (default, usually the option you want): + - when to use: actors that need high-performance in-memory logic. + - when not to use: you need this actor running at all times no matter what, even if idle + - examples: (list commone xamples) + - destroy: + - when to use: actors that need to run once until completion. on crash, do not try to reschedule. + - when not to use: if you want your actor to have fault tolerance and be able to run transaprenlty to the underlying runner + - examples: batch jobs, image conversions, ephemeral jobs, (TODO come up with better eaxmples) + - restart: + - when to use: actors that should be running at all times + - when not to use: if you don't absolutely need something running at all times, since this consumes needless compute resources. considure using the scheduling api instead. + - examples: maintain outbound sockets, daemons, always-running jobs, (TODO come up with better examples) + +the behavior for each is described below: + +| Event | Restart | Sleep | Destroy | +|------------------------------|--------------|--------------|--------------| +| Graceful exit (StopCode::Ok) | Destroy | Destroy | Destroy | +| Crash (non-Ok exit) | Reschedule | Sleep | Destroy | +| Lost (runner disappeared) | Reschedule | Sleep | Destroy | +| Lost + force_reschedule | Reschedule | Reschedule | Reschedule | +| GoingAway (runner draining) | Reschedule | Sleep | Destroy | +| No capacity (allocation) | Queue (wait) | Sleep | Queue (wait) | +| No capacity + serverless | Queue (wait) | Queue (wait) | Queue (wait) | +| Wake signal (while sleeping) | Reschedule | Reschedule | Reschedule | + +### inspector + +- actors provide an inspector api to implement the: + - repl + - state read/write + - network inspector + - event log +- this is impelmented over a websocket over bare + +### http api + +- see the http api document on actors + +### multi-region + +- actors can be scheduled across multiple regions +- each actor has an actor id which embeds which region it lives in +- networking is automatically routed to the region that an actor lives in +- limitation: actors curretnly cannot migrate across regions + +### backpressure + +#### no runner capacity + +- this is how actors with different crash policies behave when when there's backpressure: + - sleep = sleeps (sheds load by not rescheduling) + - restart = queues + - destroy = queues +- see the above matrix for more details on actor crash policy on how it handles no capacity. + +- the actor queue is built to withstand high amounts of backpressure on rivet, so queueing actors is fine here +- a large queue means it'll take more time for your application to process the queue to catch up with demand when it comes online. + +#### per-actor cpu & networking exhaustion + +- actors are isolated, so they each have their own individual bottleneck. you can think of this like a process thread where each thread can only do so much. +- there is no durable message queue/"mailbox" for actors. if the actor cannot respond in time, then the request is dropped. +- if an actor exhauses its cpu or networking, then the runner +- returns service unavailble (503) if the actor fails to respond to a request in time +- there is no hard cap on the networking or cpu usage for each actor at the moment +- if your actor is resource intensive, it's common to use a separate mailbox actor to act as a queue + +## runners + +### regular vs serverless runners + +there are 2 types of runners: + +- regular: these are standard nodejs processes connected to rivet that rivet can orchestrate actors to and send network requests to at any time +- serverless: rivet works with serverless platforms. when an actor is created, it has a request-per-actor model where it opens a long-running request on the serverless platofrm to run a given actor. + +### runner pool + +- runners are pooled together by sharing a common name (ie "default") +- when an actor is created, it chooses the pool by selecting the runner name to run on +- rivet will automatically load balance actors across these runners + +### runner key + +- not relevnat for serverless runners +- each runner has a unique key that it provides when connecting. keys are unique to the instace the runner is running on and should be the same if the runner is restarted. +- this can be the: machine's ip, k8s pod name, etc +- if there is an existing runner connected with a given key, the runner will disconnect the old runner and replace it +- rivet is designed to handle network partitions by waiting for runners to miss a ping, indicating it's no longer alive. however, often times runners restart immediately after a hard crash and reconnect. in this case, the runner will reconnect on restart and terminate the old runner in order to prevent further actors from scheduling to the crashed runner. + +### capacity + +- not relevnat for serverless runners +- each runner can be assigned a capacity of how many actors it can run +- rivet will schedule with spread (not binpacking) in order to spread load over actors + +#### usefulness of capacity = 1 + +- setting a capacity of 1 is helpful for situations where you have cpu-intensive apps that should not run with any other actors +- examples include game servers, ffmpeg jobs, etc + +### versions & upgrading code + +- each runner has a version index +- actors are always scheduled to the highest verison index (see runner priority below) +- this means that when a new runner is deployed: + 1. runners with higher index come online + 2. actors schedule to the highest index, stop scheduling to the older index + 3. old index runners start draining and migrating actors to new index + 4. all old runners are now shut down +- websocekts are live migrated to the new version when upgrading using hibernating websockets to users see no hiccup in their applications +- this is important because actors should never downgrade their runner. they should always move to a newer version of code in order to prevent corruption. + +### runner scheduling prioroty + +- actors are scheduled to runners sorted by priority of (version DESC, remaining capacity ASC) + +### multi-region + +TODO + +### shutdown sequence + +- runner shutdown is important to ensure that actors do not get unexpectedly terminated when either: + - upgrading your applciation and taking down old pods + - scaling down your runners horizontally (ie from an hpa) + - pressing ctrl-c when in development +- on shutdown: + 1. tell rivet the runner is stopping + 2. rivet tells all the actors on this runner to migrate + 3. runner waits for all actors to finish migrating + 4. runner exits process + +### reconnection + +- runners can handle temporary network partitions +- they'll automatically reconnect and replay missed commands/events between rivet and the runner +- this happens transparenlty to the user +- if disconnected for too long (indicating a network partition), the runner will shut itself down and exit + +### autoscaling + +- not relevant to serverless +- runners currently autoscale on cpu. more intelligent scaling is coming soon. +- tune your runner total slots capacity accordingly +- it's up to you to configure your hpa/etc to work like this. see the Connect guides (link to index page) for reference on hwo to configure this. + +### serverless timeouts + +- serverless runners take in to account the maximum run duration of the serverless platform +- the runners will mgirate actors to a new request before the request times out +- this is completely transparent to you and the user because of the fault tolerance and websocket migraiton characteristics +- it's common for actors to go sleep before hitting the serverless timeout + +## networking + +### web standards + +- everything in rivet is built on webstandards by default +- nothing in rivet requires you to use our sdk, our sdks are meant to be a convenience. it's built to be as easy to use raw http/websocket endpoints from scratch. +- actions, events, etc are all built on simple, well-documented http/websocket under the hood (link to openapi & asyncapi docs). +- you can use low-level request handlers (lnk to dock) and low-level weboscket handlers (link to doc) to handle low-level primtivies yourself + +### encoding + +- rivetkit's action/events api supports communicating via [VBARE](link to github repo, see the blog post for the link), CBOR, or JSON + - VBARE: high-perofrmance & compact, optimal use case + - CBOR: descent encoding/decoding perf + portable libraries, good for implemnting high-ish performance on other platforms + - JSON: good for fast implementations & debugging (easy to read) + +### tunneling + +- when a runner connects it opens a tunnel to rivet to allow incoming traffic + - this is simila to systems like tailscale, ngrok, or other vpns + - we do this for security & configuraiton simplicity since it means that you don't have to manage exposing your rivetkit applications' networkig to rivet. instead, anything that can open a socket to rivet can accept inbound traffic to actors. + +### gateway + +- incoming traffic to actors come to the Rivet gateway and are routed to the appropriate runner +- the rivet gateway automatically handles: + - multi-region routing to route traffic to the correct reigon for an actor + - automatically waking the actor if needed + - sending traffic over the runner + +### hibernating websockets + +- hibernating web sockets are a core component of live actor migration & fault tolerance. it allows us to maintain an open websocket while the actor crashes, upgrades, or moves to another runner. +TODO: copy the rest of this from low-level webosckets document and rephrase + +### actor health endpoin + +- actors provide a simple, utility health endpoint at `/health` that lets you check if your actor is reachable (e.g. `curl https://api.rivet.dev/gateway/{actor id}/health`) + +## multi-region + +### networking + +- actors may live in different regions than inbound requests +- Rivet uses the Epoxy (link again) system to handle global routing to route traffic to the correct region with high performance +- this is completely transparent to you. your app sends traffic to https://api.rivet.dev/gateway/* and it automatically routes to the correct actor in the appropriate region + +### globally unique actor keys + +- actor keys are globally unique to be able to benefit from multi-region capabilities without any extra work +- see more about globally uniuqe actor keys above +- see the actor keys document + +### regional endpoints + +- each reigon has a regional endpoint +- this endpoint is used specifically for connecting runners (for example https://api-us-east-1.rivet.dev), opt to use api.rivet.dev for all other traffic +- runners are required to connect to the regional endpoints + - this is because runners are sensitive to latency to the rivet regional datacenter + - we add datacenters regularly so each runner needs to be pinned to a single datacenter in order to ensure your availble datacneter list doesn't change sporadically without your consent + +### persistence + +- data is always persisted in the same region that is written +- this is important for minimal coldstarts & data locality laws + +## namespaces + +- rivet provides namespaces to run multiple actor systems in isolation +- this makes it really easy to have prod/staging environments or completely different applications running on the same rivet instance +- when you connect to rivet, you can specify which namespace you're connecting to +- self-hotsed rivet defaults to namespace "default" +- rivet cloud provdies isolated tokens for each namespace + +## manager api + +- rivet provides a standard rest api for managing actors + - useful endoints include: + - get /actors + - delete /actors/{} + - get /actors/names -> get all actor types available + +## comparison to prior art for actors + +### runtime + +- there are very few serious actor implementation targeted at the javascirpt eocsystem. rivet is arguably the most serious open-source actor implementation for typescript out there. + +### library vs orchestrator + +- some actor systems opt to be purely a library while rivet opts to have an orchestrator (i.e. the single rust binary) +- this lets us to a lot of things other actor systems can't: + - separating orchestration, persistence, and proxy lets us isoalte the core to be incredibly reliable while the fast-changing applications that ocnnect to rivet can be more error-prone safely. with a library, the blast radius of your application also affects the entire actor system. + - support for serverless platforms to benefit from cost, multi-region, blitz scaling, and relibaiblity benefits + - optimize fault tolerance since we can make more assumtions about application state when the rivet core does not crash and your app does + +### scheduling + +actors is a loose term, but there are generally 2 types of schedulign in practice: + +- ephemeral actors + - examples: erlang/otp, akka, swift + - provides no persistence or sleeping mechanism by defualt + - relies on supervisors for managing persistence +- [virtual actors](https://www.microsoft.com/en-us/research/project/orleans-virtual-actors/) + - an extension of the actor pattern that provides actors that can hibernate ("sleep") when not in use + - examples: orleans, dapr, durable objects + +rivet has similarities with both to provide more flexibility: + +- crash policies provdie for 3 types of actors: + - sleep -> most similar to virutal actors + - restart -> most similar to ephemeral actors but with a supervisor to auto-restart, however still has a durable queue ot handle backpressure + - crash -> most similar to traditional actors but with no supervisor to restart, however still has a durable queue to handle backpressure + +### communication + +- many actor frameworks use inbox patterns (think: queue-per-actor) to handle sending messages between actors +- there is no callback mechanims, instead you need to send messages back to the actual actor +- rivet opts to behave like web standards instead of using the message pattern + - actors can impelment the inbox pattern optionally + - but we provide lower-level networking to be able to be compatible with more techniologies + - rivet assumes the same serial concurerntly that other actors do (by the nature of javascript being single-threaded) but we allow you to run promises in parallel or handl eyour own concurrency control (which some other actor frameworks might require a spawning new actor to do) + +_Source doc path: /docs/general/architecture_ diff --git a/.agents/skills/live-cursors/reference/general/cors.md b/.agents/skills/live-cursors/reference/general/cors.md new file mode 100644 index 0000000..330fe38 --- /dev/null +++ b/.agents/skills/live-cursors/reference/general/cors.md @@ -0,0 +1,18 @@ +# Cross-Origin Resource Sharing + +> Source: `src/content/docs/general/cors.mdx` +> Canonical URL: https://rivet.dev/docs/general/cors +> Description: Cross-Origin Resource Sharing (CORS) controls which origins (domains) can access your actors. When actors are exposed to the public internet, proper origin validation is critical to prevent security breaches and denial of service attacks. + +--- +Unlike stateless HTTP APIs that use CORS headers, Rivet Actors are stateful and support persistent WebSocket connections. Since WebSockets don't natively support CORS, we validate origins manually in the `onBeforeConnect` hook before connections may open. + +## Implementing Origin Restrictions + +To implement origin restrictions on Rivet Actors, use the `onBeforeConnect` hook to verify the request. + +To catch the error on the client, use the following code: + + See tracking issue for [configuring CORS per-actor on the gateway](https://github.com/rivet-dev/rivet/issues/3539) that will remove the need to implement origin restrictions in `onBforeRequest`. + +_Source doc path: /docs/general/cors_ diff --git a/.agents/skills/live-cursors/reference/general/docs-for-llms.md b/.agents/skills/live-cursors/reference/general/docs-for-llms.md new file mode 100644 index 0000000..db0c216 --- /dev/null +++ b/.agents/skills/live-cursors/reference/general/docs-for-llms.md @@ -0,0 +1,51 @@ +# Documentation for LLMs & AI + +> Source: `src/content/docs/general/docs-for-llms.mdx` +> Canonical URL: https://rivet.dev/docs/general/docs-for-llms +> Description: Rivet provides optimized documentation formats specifically designed for Large Language Models (LLMs) and AI integration tools. + +--- +## Skills (Recommended) + +For AI coding assistants like Claude Code, Cursor, or Windsurf, install Rivet skills for the best development experience: + +```sh +npx skills add rivet-dev/skills +``` + +Skills provide your AI assistant with Rivet-specific knowledge, best practices, and code patterns directly in your project context. + +## Available Formats + +### `llms.txt` (Condensed) +A condensed version of the documentation perfect for quick reference and context-aware AI assistance. + +**Access:** /llms.txt + +This format includes: +- Key concepts and features +- Essential getting started information +- Summaries of main functionality +- Optimized for token efficiency + +### `llms-full.txt` (Complete) +The complete documentation in a single file, ideal for comprehensive AI assistance and in-depth analysis. + +**Access:** /llms-full.txt + +This format includes: +- Complete documentation content +- All examples and detailed explanations +- Full API references and guides +- Suitable for complex queries and comprehensive understanding + +## Access Pages As Markdown + +Each documentation page is also available as clean markdown by appending `.md` to any documentation URL path. + +For example: + +- Original URL: `https://rivet.dev/docs/actors` +- Markdown URL: `https://rivet.dev/docs/actors.md` + +_Source doc path: /docs/general/docs-for-llms_ diff --git a/.agents/skills/live-cursors/reference/general/edge.md b/.agents/skills/live-cursors/reference/general/edge.md new file mode 100644 index 0000000..814dde2 --- /dev/null +++ b/.agents/skills/live-cursors/reference/general/edge.md @@ -0,0 +1,24 @@ +# Edge Networking + +> Source: `src/content/docs/general/edge.mdx` +> Canonical URL: https://rivet.dev/docs/general/edge +> Description: Actors automatically run near your users on your provider's global network. + +--- +At the moment, edge networking is only supported on Rivet Cloud & Cloudflare Workers. More self-hosted platforms are on the roadmap. + +## Region selection + +### Automatic region selection + +By default, actors will choose the nearest region based on the client's location. + +Under the hood, Rivet and Cloudflare use [Anycast routing](https://en.wikipedia.org/wiki/Anycast) to automatically find the best location for the client to connect to without relying on a slow manual pinging process. + +### Manual region selection + +The region an actor is created in can be overridden using region options: + +See [Create Manage Actors](/docs/actors/communicating-between-actors) for more information. + +_Source doc path: /docs/general/edge_ diff --git a/.agents/skills/live-cursors/reference/general/endpoints.md b/.agents/skills/live-cursors/reference/general/endpoints.md new file mode 100644 index 0000000..5c376e1 --- /dev/null +++ b/.agents/skills/live-cursors/reference/general/endpoints.md @@ -0,0 +1,98 @@ +# Endpoints + +> Source: `src/content/docs/general/endpoints.mdx` +> Canonical URL: https://rivet.dev/docs/general/endpoints +> Description: Configure how your backend connects to Rivet and how clients reach your actors. + +--- +## Local Development + +No configuration is needed for local development. RivetKit runs entirely on your local machine without any extra configuration to run Rivet Actors. + +## Production Deployment + +When deploying to production, you need to configure endpoints so your backend can communicate with Rivet Engine and clients can reach your actors. + +Diagram showing Client connecting to Rivet, which connects to Your Backend + +### Private Endpoint + +The private endpoint tells your backend where to find the Rivet Engine. + +### Environment Variable + +```bash +RIVET_ENDPOINT=https://my-namespace:sk_xxxxx@api.rivet.dev +``` + +### Config + +### Public Endpoint + +The public endpoint tells clients where to connect to reach your actors. + +This endpoint and token will be exposed to the internet. Use a public token (`pk_`), not your secret token (`sk_`). + +The public endpoint is only required if using the [serverless runtime mode](/docs/general/runtime-modes#runners) and if you have a frontend using RivetKit. + +### Environment Variable + +```bash +RIVET_PUBLIC_ENDPOINT=https://my-namespace:pk_xxxxx@api.rivet.dev +``` + +### Config + +## Advanced + +### URL Auth Syntax + +Endpoint URLs support embedding namespace and token directly in the URL: + +``` +https://namespace:token@host/path +``` + +This is the recommended approach for simplicity. Alternatively, you can use separate environment variables: + +```bash +RIVET_ENDPOINT=https://api.rivet.dev +RIVET_NAMESPACE=my-namespace +RIVET_TOKEN=sk_xxxxx +``` + +### Security + +In serverless mode, the private endpoint is used to validate that requests to `GET /api/rivet/start` are coming from your trusted Rivet endpoint. If the private endpoint is not configured, anyone can run a self-hosted instance of Rivet and connect to your backend from any endpoint. + +### How Clients Connect + +This flow applies to [serverless runtime mode](/docs/general/runtime-modes#serverless). For [runner runtime mode](/docs/general/runtime-modes#runners) or [clients configured to connect directly to Rivet](/docs/clients/javascript), clients connect directly to Rivet and this metadata flow is not needed. + +When a client connects to your serverless application, it follows this flow: + +1. Client makes a request to `https://my-app.example.com/api/rivet/metadata` +2. Your app returns the public endpoint configuration: + ```json + { + "clientEndpoint": "https://api.rivet.dev", + "clientNamespace": "my-namespace", + "clientToken": "pk_xxxxx" + } + ``` +3. Client caches these values and uses them for subsequent requests +4. Client connects to `https://api.rivet.dev/gateway/{actor}`, which routes requests to your actors + +This indirection exists because Rivet acts as a gateway between clients and your actors. This is because Rivet handles routing, load balancing, and actor lifecycle management of actors. + +## Reference + +| Environment Variable | Config Option | Description | +|---------------------|---------------|-------------| +| `RIVET_ENDPOINT` | `endpoint` | Rivet Engine URL for your backend | +| `RIVET_NAMESPACE` | `namespace` | Namespace for actor isolation | +| `RIVET_TOKEN` | `token` | Authentication token for engine connection | +| `RIVET_PUBLIC_ENDPOINT` | `serverless.publicEndpoint` | Client-facing endpoint | +| `RIVET_PUBLIC_TOKEN` | `serverless.publicToken` | Client-facing token | + +_Source doc path: /docs/general/endpoints_ diff --git a/.agents/skills/live-cursors/reference/general/environment-variables.md b/.agents/skills/live-cursors/reference/general/environment-variables.md new file mode 100644 index 0000000..2a86e98 --- /dev/null +++ b/.agents/skills/live-cursors/reference/general/environment-variables.md @@ -0,0 +1,87 @@ +# Environment Variables + +> Source: `src/content/docs/general/environment-variables.mdx` +> Canonical URL: https://rivet.dev/docs/general/environment-variables +> Description: This page documents all environment variables that configure RivetKit behavior. + +--- +## Connection + +| Environment Variable | Description | +|---------------------|-------------| +| `RIVET_ENDPOINT` | Endpoint URL to connect to Rivet Engine. Supports [URL auth syntax](/docs/general/endpoints#url-auth-syntax). | +| `RIVET_TOKEN` | Authentication token for Rivet Engine | +| `RIVET_NAMESPACE` | Namespace to use (default: "default") | + +## Public Endpoint + +These variables configure how clients connect to your actors. + +| Environment Variable | Description | +|---------------------|-------------| +| `RIVET_PUBLIC_ENDPOINT` | Public endpoint for client connections. Supports [URL auth syntax](/docs/general/endpoints#url-auth-syntax). | +| `RIVET_PUBLIC_TOKEN` | Public token for client authentication | + +## Runner Configuration + +| Environment Variable | Description | +|---------------------|-------------| +| `RIVET_RUNNER` | Runner name (default: "default") | +| `RIVET_RUNNER_VERSION` | Version number for the runner. See [Versions & Upgrades](/docs/actors/versions). | +| `RIVET_RUNNER_KIND` | Type of runner | +| `RIVET_TOTAL_SLOTS` | Total actor slots available (default: 100000) | + +## Engine + +| Environment Variable | Description | +|---------------------|-------------| +| `RIVET_RUN_ENGINE` | Set to `1` to spawn the engine process | +| `RIVET_RUN_ENGINE_HOST` | Host to bind the spawned local engine process to. Defaults to `127.0.0.1`. | +| `RIVET_RUN_ENGINE_PORT` | Port to bind the spawned local engine process to. Defaults to `6420`. | +| `RIVET_RUN_ENGINE_VERSION` | Version of engine to download | + +## Inspector + +| Environment Variable | Description | +|---------------------|-------------| +| `RIVET_INSPECTOR_DISABLE` | Set to `1` to disable the inspector | + +## Metrics + +| Environment Variable | Description | +|---------------------|-------------| +| `_RIVET_METRICS_TOKEN` | Bearer token that gates the per-actor Prometheus `/metrics` endpoint. When unset, `/metrics` is disabled. | + +## Experimental + +| Environment Variable | Description | +|---------------------|-------------| +| `RIVET_EXPERIMENTAL_OTEL` | Set to `1` to enable experimental OTel tracing in Rivet Actors | + +## Storage + +| Environment Variable | Description | +|---------------------|-------------| +| `RIVETKIT_RUNTIME` | Runtime binding to use for RivetKit core: `auto`, `native`, or `wasm`. Defaults to `auto`. | +| `RIVETKIT_STORAGE_PATH` | Overrides the default file-system storage path used by RivetKit when using the default driver. | + +## Lifecycle + +| Environment Variable | Description | +|---------------------|-------------| +| `RIVETKIT_RUNTIME_MODE` | Controls how `registry.start()` runs. Accepted values are `envoy` and `serverless`; any other explicit value errors. Defaults to `envoy`: opens a long-lived WebSocket to the engine (Mode A). Set to `serverless` to bind an HTTP listener via `registry.listen()` (Mode B). | +| `RIVETKIT_PUBLIC_DIR` | Directory of static assets to serve alongside the framework routes when calling `registry.listen()`. Used as a fallback when `opts.publicDir` is not passed. On auto-listen via `registry.start()`, defaults to `/public` when this env var is unset. | +| `RIVET_PORT` | Port the listener binds when calling `registry.listen()` without an explicit `opts.port`. Must be an integer between 1 and 65535. Defaults to `3000`. | + +## Logging + +| Environment Variable | Description | +|---------------------|-------------| +| `RIVET_LOG_LEVEL` | Log level: `trace`, `debug`, `info`, `warn`, `error`, `fatal`, `silent` | +| `RIVET_LOG_TARGET` | Set to `1` to include log target | +| `RIVET_LOG_TIMESTAMP` | Set to `1` to include timestamps | +| `RIVET_LOG_MESSAGE` | Set to `1` to include message formatting | +| `RIVET_LOG_ERROR_STACK` | Set to `1` to include error stack traces | +| `RIVET_LOG_HEADERS` | Set to `1` to log request headers | + +_Source doc path: /docs/general/environment-variables_ diff --git a/.agents/skills/live-cursors/reference/general/http-server.md b/.agents/skills/live-cursors/reference/general/http-server.md new file mode 100644 index 0000000..c3a386c --- /dev/null +++ b/.agents/skills/live-cursors/reference/general/http-server.md @@ -0,0 +1,96 @@ +# HTTP Server + +> Source: `src/content/docs/general/http-server.mdx` +> Canonical URL: https://rivet.dev/docs/general/http-server +> Description: Different ways to run your RivetKit HTTP server. + +--- +## Methods of Running Your Server + +### registry.start() + +The simplest way to run your server. Starts a local RivetKit server, serves static files from a `public` directory, and starts the actor runner: + +Run with `npx tsx --watch index.ts` (Node.js), `bun --watch index.ts` (Bun), or `deno run --allow-net --allow-read --allow-env --watch index.ts` (Deno). Clients connect to the Rivet Engine on `http://localhost:6420`. + +### With Fetch Handlers + +A [fetch handler](https://wintercg.org/) is a function that takes a `Request` and returns a `Response`. This is the standard pattern used by Cloudflare Workers, Deno Deploy, Bun, and other modern runtimes. + +Use `registry.serve()` to get a fetch handler: + +To integrate with a router like [Hono](https://hono.dev/) or [Elysia](https://elysiajs.com/), use `registry.handler()`: + +### Hono + +### Elysia + +Then run your server: + +```bash Node.js +npx tsx --watch server.ts +``` + +```bash Bun +bun --watch server.ts +``` + +```bash Deno +deno run --allow-net --allow-read --allow-env --watch server.ts +``` + +### Explicit HTTP Server + +If you need to explicitly start the HTTP server instead of using the fetch handler pattern: + +### Node.js (Hono) + +Using Hono with `@hono/node-server`: + +### Node.js (Adapter) + +Using `@whatwg-node/server` to adapt the fetch handler to Node's HTTP server: + +```ts server.ts @nocheck +import { actor, setup } from "rivetkit"; +import { createServer } from "node:http"; +import { createServerAdapter } from "@whatwg-node/server"; + +const myActor = actor({ state: {}, actions: {} }); +const registry = setup({ use: { myActor } }); + +const handler = createServerAdapter(registry.serve().fetch); +const server = createServer(handler); +server.listen(3000); +``` + +### Bun + +Using Bun's native server: + +```ts server.ts @nocheck +import { actor, setup } from "rivetkit"; + +const myActor = actor({ state: {}, actions: {} }); +const registry = setup({ use: { myActor } }); + +Bun.serve({ + port: 3000, + fetch: (request: Request) => registry.handler(request), +}); +``` + +### Deno + +Using Deno's native server: + +```ts server.ts @nocheck +import { actor, setup } from "rivetkit"; + +const myActor = actor({ state: {}, actions: {} }); +const registry = setup({ use: { myActor } }); + +Deno.serve({ port: 3000 }, (request: Request) => registry.handler(request)); +``` + +_Source doc path: /docs/general/http-server_ diff --git a/.agents/skills/live-cursors/reference/general/logging.md b/.agents/skills/live-cursors/reference/general/logging.md new file mode 100644 index 0000000..f7ce9eb --- /dev/null +++ b/.agents/skills/live-cursors/reference/general/logging.md @@ -0,0 +1,74 @@ +# Logging + +> Source: `src/content/docs/general/logging.mdx` +> Canonical URL: https://rivet.dev/docs/general/logging +> Description: Actors provide a built-in way to log complex data to the console. + +--- +Using the context's log object (`c.log`) allows you to log complex data using structured logging. + +Using the actor logging API is completely optional. + +## Log levels + +There are 7 log levels: + +| Level | Call | Description | +| ------ | ------------------------------- | ---------------------------------------------------------------- | +| Fatal | `c.log.fatal(message, ...args);` | Critical errors that prevent core functionality | +| Error | `c.log.error(message, ...args);` | Errors that affect functionality but allow continued operation | +| Warn | `c.log.warn(message, ...args);` | Potentially harmful situations that should be addressed | +| Info | `c.log.info(message, ...args);` | General information about significant events & state changes | +| Debug | `c.log.debug(message, ...args);` | Detailed debugging information, usually used in development | +| Trace | `c.log.trace(message, ...args);` | Very detailed debugging information, usually for tracing flow | +| Silent | N/A | Disables all logging output | + +## Structured logging + +The built-in logging API (using `c.log`) provides structured logging to let you log key-value +pairs instead of raw strings. Structured logs are readable by both machines & +humans to make them easier to parse & search. + +When using `c.log`, the actor's name, key, and actor ID are automatically included in every log output. This makes it easy to filter and trace logs by specific actors in production environments. + +### Examples + +The logging system is built on [Pino](https://getpino.io/#/docs/api?id=logger), a high-performance structured logger for Node.js. + +## Configuration + +### Environment Variables + +You can configure logging behavior using environment variables: + +| Variable | Description | Values | Default | +| -------- | ----------- | ------ | ------- | +| `RIVET_LOG_LEVEL` | Sets the minimum log level to display | `trace`, `debug`, `info`, `warn`, `error`, `fatal`, `silent` | `warn` | +| `RIVET_LOG_TARGET` | Include the module name that logged the message | `1` to enable, `0` to disable | `0` | +| `RIVET_LOG_TIMESTAMP` | Include timestamp in log output | `1` to enable, `0` to disable | `0` | +| `RIVET_LOG_MESSAGE` | Enable detailed message logging for debugging | `1` to enable, `0` to disable | `0` | +| `RIVET_LOG_ERROR_STACK` | Include stack traces in error output | `1` to enable, `0` to disable | `0` | +| `RIVET_LOG_HEADERS` | Log HTTP headers in requests | `1` to enable, `0` to disable | `0` | + +Example: +```bash +RIVET_LOG_LEVEL=debug RIVET_LOG_TARGET=1 RIVET_LOG_TIMESTAMP=1 node server.js +``` + +### Log Level + +You can configure the log level programmatically when setting up your registry: + +### Custom Pino Logger + +You can also provide a custom Pino base logger for more advanced logging configurations: + +If using a custom base logger, you must manually configure your own log level in the Pino logger. + +For more advanced Pino configuration options, see the [Pino API documentation](https://getpino.io/#/docs/api?id=export). + +### Disable Welcome Message + +You can disable the default RivetKit welcome message with: + +_Source doc path: /docs/general/logging_ diff --git a/.agents/skills/live-cursors/reference/general/pool-configuration.md b/.agents/skills/live-cursors/reference/general/pool-configuration.md new file mode 100644 index 0000000..21b00f1 --- /dev/null +++ b/.agents/skills/live-cursors/reference/general/pool-configuration.md @@ -0,0 +1,119 @@ +# Pool Configuration + +> Source: `src/content/docs/general/pool-configuration.mdx` +> Canonical URL: https://rivet.dev/docs/general/pool-configuration +> Description: Reference for runner pool configuration, including drain behavior, actor eviction rate limiting, and serverless-specific options. + +--- +A **runner pool** is the set of runners Rivet manages for a given runner name within a namespace. The pool configuration controls how runners are scaled, drained on version upgrades, and how quickly actors are evicted from drained runners. + +There are two pool kinds: + +- **`normal`** — runners connect to the engine themselves (for example a long-running process started by you, Docker, or Kubernetes). The engine does not start or stop runners. +- **`serverless`** — the engine calls an HTTP endpoint to wake runners on demand. Used by serverless platforms (Vercel, Cloudflare Workers, Freestyle, etc.). See [Runtime Modes](/docs/general/runtime-modes). + +## Setting the Configuration + +Configure a pool from the [Rivet dashboard](https://dashboard.rivet.dev) under your namespace's runner settings. The dashboard is the recommended way to manage pool configuration. + +You can also set pool configuration directly through the API or the TypeScript SDK: + +```typescript SDK @nocheck +import { RivetClient } from "@rivetkit/engine-api-full"; + +const rivet = new RivetClient({ + environment: "https://api.rivet.dev", + token: process.env.RIVET_TOKEN!, +}); + +await rivet.runnerConfigsUpsert("default", { + namespace: "default", + datacenters: { + default: { + serverless: { + url: "https://my-app.example.com/api/rivet", + requestLifespan: 60 * 15, + actorEvictionDelay: 5, + actorEvictionPeriod: 60, + actorEvictionRate: 1, + }, + }, + }, +}); +``` + +```bash curl +curl -X PUT "https://api.rivet.dev/runner-configs/default?namespace=default" \ + -H "Authorization: Bearer $RIVET_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "datacenters": { + "default": { + "serverless": { + "url": "https://my-app.example.com/api/rivet", + "request_lifespan": 900, + "actor_eviction_delay": 5, + "actor_eviction_period": 60, + "actor_eviction_rate": 1 + } + } + } + }' +``` + +The HTTP API uses `snake_case`. The TypeScript SDK uses `camelCase`. The field names below use `camelCase`. + +## Common Options + +These options apply to both `normal` and `serverless` pools. + +| Option | Type | Default | Description | +|---|---|---|---| +| `drainOnVersionUpgrade` | `bool` | `true` | When a new runner version is deployed, stop old runners and migrate their actors. See [Versions & Upgrades](/docs/actors/versions#drain-on-version-upgrade). | +| `actorEvictionDelay` | `u32` (seconds) | `0` | Delay before actor eviction begins after a drain is triggered. Gives clients time to receive the drain signal before migrations start. | +| `actorEvictionPeriod` | `u32` (seconds) | `0` | Window over which evictions are batched. Used together with `actorEvictionRate` to smooth eviction load on the rest of the pool. | +| `actorEvictionRate` | `f32` (actors/sec) | `1.0` | Maximum number of actors evicted per second once eviction is underway. Set higher for fast cutovers, lower to spread reschedule load. | +| `metadata` | `object` | — | Arbitrary JSON metadata attached to the pool. Useful for tagging and dashboards. | + +### Tuning eviction rate limiting + +Eviction kicks in whenever a runner is drained — most commonly during a version upgrade with `drainOnVersionUpgrade: true`, but also when a runner disconnects ungracefully or is replaced. + +A typical tuning starts from: + +- `actorEvictionDelay: 5` — five-second head start so clients see the drain notification before migrations begin. +- `actorEvictionPeriod: 60` — batch over a one-minute window. +- `actorEvictionRate: 1` — evict one actor per second. + +Increase `actorEvictionRate` for small pools where a full cutover finishes in seconds. Decrease it for large pools to avoid bursts of reschedule traffic. + +## Serverless-Only Options + +These options only apply when `kind: "serverless"`. + +| Option | Type | Default | Description | +|---|---|---|---| +| `url` | `string` | — | HTTP endpoint the engine calls to wake a runner. | +| `headers` | `map` | `{}` | Additional headers sent with the wake request (for example an auth token). | +| `requestLifespan` | `u32` (seconds) | — | Total lifespan of a serverless request before drain begins. Must be shorter than your platform's request timeout. | +| `maxConcurrentActors` | `u64` | `1000` | Soft cap on concurrent actors hosted across the pool. | +| `drainGracePeriod` | `u32` (seconds) | `1800` (30 min) | Time a serverless runner reserves at the end of its lifespan for actors to stop gracefully. | +| `metadataPollInterval` | `u64` (ms) | engine default | How often each runner re-fetches pool metadata to detect new versions. | + +### Deprecated options + +The following options still parse for backwards compatibility but should not be used in new configurations: + +- `slotsPerRunner` +- `minRunners` +- `maxRunners` +- `runnersMargin` + +## Related + +- [Runtime Modes](/docs/general/runtime-modes) — runner vs serverless behavior. +- [Versions & Upgrades](/docs/actors/versions) — how `drainOnVersionUpgrade` interacts with version detection. +- [Limits](/docs/actors/limits) — request lifespan and drain grace period limits. +- [Debugging](/docs/actors/debugging) — inspect a pool configuration via the API. + +_Source doc path: /docs/general/pool-configuration_ diff --git a/.agents/skills/live-cursors/reference/general/production-checklist.md b/.agents/skills/live-cursors/reference/general/production-checklist.md new file mode 100644 index 0000000..1ebe257 --- /dev/null +++ b/.agents/skills/live-cursors/reference/general/production-checklist.md @@ -0,0 +1,98 @@ +# Production Checklist + +> Source: `src/content/docs/general/production-checklist.mdx` +> Canonical URL: https://rivet.dev/docs/general/production-checklist +> Description: Checklist for deploying Rivet Actors to production. + +--- +We recommend passing this page to your coding agent to verify your configuration before deploying. + +## Environment + +- **Set `NODE_ENV=production`.** Ensures optimized performance and disables development-only behavior. +- **Ensure log level is not set to debug.** Leave `RIVET_LOG_LEVEL` at its default or explicitly set it to `warn` to avoid excessive logging. See [Logging](/docs/general/logging). +- **Do not set `RIVET_EXPOSE_ERRORS=1` in production.** This exposes internal error details to clients. It is automatically enabled when `NODE_ENV=development`. See [Errors](/docs/actors/errors). + +## Runtime Mode + +- **Configure a runner version.** Required for graceful upgrades and draining of old actors. Applies to both serverless and runner modes. See [Versions & Upgrades](/docs/actors/versions). + +### Serverless + +- **Check platform timeouts.** Rivet handles migration between invocations automatically, but shorter timeouts increase migration frequency. See [Timeouts](/docs/general/runtime-modes#timeouts). +- **Verify `/api/rivet/start` body size limits.** Serverless actor starts carry actor config and preloaded KV or SQLite startup data in the request body. Keep `serverless.maxStartPayloadBytes` and your platform or proxy body limit at **16 MiB or higher**, or lower the preload budget if your platform cannot accept that size. See [Limits](/docs/actors/limits#kv-preloading). +- **Configure max runners.** Go to Settings > Providers > Edit Provider > Max Runners to set the limit. The default is 100,000 runners. This is effectively your max actor count. +- **Verify your platform rate limit accommodates your actor create and wake frequency.** Actor start requests are sent from Rivet's servers, so they all originate from the same IP. Per-IP rate limits will throttle the engine well before they would throttle real end-user traffic. Size your platform's rate limit to your peak actor create and wake rate, not your end-user request rate. +- **Set the per-instance max concurrent actor limit.** Each serverless instance hosts one actor per in-flight `/api/rivet/start` request, so your platform's per-instance concurrency (e.g. GCP Cloud Run `--concurrency`, AWS Lambda reserved concurrency, Vercel `maxDuration` + concurrency) directly caps actors per instance. Pick a value based on per-actor memory and CPU; the platform autoscales out additional instances once existing ones hit the cap. +- **Tune `requestLifespan` to your platform's hard request timeout.** `requestLifespan` (default `3600`, 60 minutes) is the total lifespan of each serverless request before actors migrate to a fresh instance. Set it just below your platform's hard timeout (e.g. `295` for Vercel Hobby, `3595` for Vercel Pro, `840` for Cloud Run's 15-min cap). Configure via [`configurePool`](/docs/general/registry-configuration). See [Timeouts](/docs/general/runtime-modes#timeouts). +- **Tune `drainGracePeriod` to cover graceful actor shutdown.** Time reserved at the end of `requestLifespan` for actors to stop gracefully before the request is forcibly closed. Default is 30 minutes from the engine; lower it for short-lived stateless actors, raise it if your actors do non-trivial cleanup or final SQLite writes. Configure via [`configurePool`](/docs/general/registry-configuration). See [Limits](/docs/actors/limits). + +### Runner + +- **Set a graceful shutdown period of at least 35 minutes.** Runners need up to 30 minutes to drain actors during upgrades, plus buffer for shutdown overhead. In Kubernetes, set `terminationGracePeriodSeconds: 2100` on the pod spec. + +## Actors + +### Design Patterns + +- **Do not use god actors.** Avoid putting all logic into a single actor type. See [Design Patterns](/docs/actors/design-patterns). +- **Do not use actor-per-request patterns.** Avoid creating a new actor for each request. See [Design Patterns](/docs/actors/design-patterns). + +### Lifecycle + +- **Do not rely on `onSleep` for critical cleanup.** `onSleep` is not called during crashes or forced terminations. See [Lifecycle](/docs/actors/lifecycle). + +### State + +- **Verify `c.state` does not grow unbounded.** Avoid using arrays or objects that grow over time in state. Use [SQLite](/docs/actors/sqlite) for unbounded or append-heavy data instead. +- **Verify actor data does not exceed 10 GB.** Contact [enterprise support](https://rivet.dev/sales) if you need more storage. +- **Use input parameters and `createState` for actor initialization.** See [Input Parameters](/docs/actors/input). + +### Events + +- **Use `conn.send()` instead of `c.broadcast()` for private events.** `c.broadcast()` sends to all connected clients. Use `conn.send()` to send events to a specific connection. See [Realtime](/docs/actors/events). + +### Actions + +- **Review action timeout configuration.** The default `actionTimeout` is 60 seconds. Increase it if you have long-running actions like API calls or file processing. See [Actor Configuration](/docs/general/actor-configuration). +- **Review message size limits.** The default `maxIncomingMessageSize` is 64 KiB and `maxOutgoingMessageSize` is 1 MiB. Increase if your actors send or receive large JSON payloads. See [Registry Configuration](/docs/general/registry-configuration). + +### Queues + +- **Review queue limits.** The default `maxQueueSize` is 1,000 messages and `maxQueueMessageSize` is 64 KiB. Increase if you expect burst traffic or large queue payloads. See [Actor Configuration](/docs/general/actor-configuration). +- **Ensure queue handlers are idempotent.** If processing fails before `message.complete()`, the message will be retried. See [Queues](/docs/actors/queues). + +### Workflows + +- **Verify workflows do not generate infinite steps.** Use `ctx.loop` to avoid creating unbounded step histories. See [Workflows](/docs/actors/workflows). + +## Security + +### Authentication + +- **Validate connections in `createConnState` or `onBeforeConnect`.** Do not trust client input without validation. See [Authentication](/docs/actors/authentication). + +### CORS + +- **Configure CORS for production.** Restrict allowed origins instead of allowing all. See [CORS](/docs/general/cors). + +### Tokens (Rivet Cloud) + +- **Use `pk_*` tokens for `RIVET_PUBLIC_ENDPOINT`.** Public tokens are safe to expose to clients. +- **Use `sk_*` tokens for `RIVET_ENDPOINT`.** Secret tokens should only be used server-side. +- **Do not leak your secret token.** Never expose `sk_*` tokens in client-side code, public repositories, or browser environments. See [Endpoints](/docs/general/endpoints). +- **Verify you're connecting to the correct region.** Use the nearest datacenter endpoint (e.g. `api-us-west-1.rivet.dev`) for lowest latency. + +### Access Control + +Access control is only needed if you want granular permissions for different clients. For most use cases, basic authentication in `onBeforeConnect` or `createConnState` is sufficient. + +- **Use deny-by-default rules.** Reject unknown roles in `onBeforeConnect`, action handlers, `canPublish`, and `canSubscribe`. See [Access Control](/docs/actors/access-control). +- **Authorize actions explicitly.** Check the caller's role in each action handler and throw `forbidden` for unauthorized access. +- **Gate event subscriptions and queue publishes.** Use `canSubscribe` and `canPublish` hooks to restrict which clients can subscribe to events or publish to queues. + +## Clients + +- **Dispose connections and/or client when not in use (JavaScript client).** Call `conn.dispose()` or `client.dispose()` when no longer needed to free resources. React and SwiftUI clients handle this automatically. See [Connection Lifecycle](/docs/clients/javascript#connection-lifecycle). + +_Source doc path: /docs/general/production-checklist_ diff --git a/.agents/skills/live-cursors/reference/general/registry-configuration.md b/.agents/skills/live-cursors/reference/general/registry-configuration.md new file mode 100644 index 0000000..3574c4a --- /dev/null +++ b/.agents/skills/live-cursors/reference/general/registry-configuration.md @@ -0,0 +1,32 @@ +# Registry Configuration + +> Source: `src/content/docs/general/registry-configuration.mdx` +> Canonical URL: https://rivet.dev/docs/general/registry-configuration +> Description: This page documents the configuration options available when setting up a RivetKit registry. The registry configuration is passed to the `setup()` function. + +--- +## Example Configurations + +### Basic Setup + +### Connecting to Rivet Engine + +## Starting Your App + +After configuring your registry, start it: + +See [Runtime Modes](/docs/general/runtime-modes) for details on when to use each mode. + +## Environment Variables + +Many configuration options can be set via environment variables. See [Environment Variables](/docs/general/environment-variables) for a complete reference. + +## Configuration Reference + +## Related + +- [Actor Configuration](/docs/general/actor-configuration): Configure individual actors +- [HTTP Server Setup](/docs/general/http-server): Set up HTTP routing and middleware +- [Architecture](/docs/general/architecture): Understand how RivetKit works + +_Source doc path: /docs/general/registry-configuration_ diff --git a/.agents/skills/live-cursors/reference/general/runtime-modes.md b/.agents/skills/live-cursors/reference/general/runtime-modes.md new file mode 100644 index 0000000..43ba986 --- /dev/null +++ b/.agents/skills/live-cursors/reference/general/runtime-modes.md @@ -0,0 +1,99 @@ +# Runtime Modes + +> Source: `src/content/docs/general/runtime-modes.mdx` +> Canonical URL: https://rivet.dev/docs/general/runtime-modes +> Description: RivetKit supports two runtime modes for running your actors: + +--- +- **Serverless**: Default mode. Responds to HTTP requests and scales automatically. +- **Runners**: Background processes without HTTP endpoints. Only needed for advanced scenarios. + +## Serverless + +Serverless is the default and recommended mode. Rivet sends HTTP requests to your backend to run actor logic, allowing your infrastructure to scale automatically. + +### Benefits + +- **Platform support**: Works with serverless platforms (Vercel, Cloudflare Workers, etc.) +- **Scale to zero**: No cost when idle +- **Edge deployments**: Easier to deploy to edge locations +- **Preview deployments**: Integrates with preview deployments on platforms like Vercel and Railway +- **Efficient autoscaling**: Request-based autoscaling can be faster and more efficient than CPU-based autoscaling depending on the platform + +### Example + +See [Server Setup](/docs/general/http-server/) for more configuration options. + +### Architecture + +When a client creates an actor, it sends a request to the Rivet Engine. The engine then calls `GET /api/rivet/start` on your serverless backend to run the actor. + +Serverless architecture diagram + +### Advanced + +#### Endpoints + +Rivet exposes the following endpoints: + +- `GET /api/rivet/metadata`: Validates configuration +- `GET /api/rivet/start`: Runs an actor + +You should never call these endpoints yourself, this is included purely for comprehension of how Rivet works under the hood. + +#### Timeouts + +Serverless platforms like Vercel have function timeouts. Rivet handles this automatically by migrating actors between function invocations, preserving state through `ctx.state`. Write your code as if it runs forever, Rivet handles the rest. + +Read more about [how we handle timeouts](/blog/2025-10-20-how-we-built-websocket-servers-for-vercel-functions/#timeouts-and-failover). + +#### Shutdown Sequence + +Each serverless request has a configurable lifespan (`requestLifespan`, default: 60 minutes). Set this to match your platform's function timeout (e.g. `requestLifespan: 3600` for Vercel Pro). + +When the request nears its lifespan, the engine reserves a grace period (`serverless_drain_grace_period`, default: 10 seconds) at the end to gracefully stop actors. For example, with a 3600-second lifespan, actors begin stopping at 3590 seconds. After the full lifespan elapses, the connection is forcibly closed and any remaining actors are rescheduled. + +See [Limits](/docs/actors/limits#serverless-shutdown) for configuration details. + +## Runners + +Runners run actors as long-running background processes without exposing an HTTP endpoint. + +### When to Use Runners + +- **No HTTP server**: Your app does not or cannot expose an HTTP server +- **No load balancer**: You don't have a load balancer to distribute HTTP requests across your servers +- **Custom scaling**: You have custom scaling requirements + +### Example + +The runner runs in the background, ready to run actors. + +### Architecture + +On startup, your backend calls `registry.startEnvoy()` which opens a persistent connection to the Rivet Engine. When a client creates an actor, the engine sends a command through this connection to start the actor on your backend. + +Runners architecture diagram + +### Configuration + +#### Runner Pool + +Use `RIVET_RUNNER` to assign runners to a pool. This lets you control which runners handle specific actors. + +```bash +RIVET_RUNNER=gpu-workers +``` + +See [Pool Configuration](/docs/general/pool-configuration) for how pools are scaled, drained on version upgrades, and rate-limited during actor eviction. + +## Comparison + +| Mode | Method | Use Case | +|------|--------|----------| +| Auto | `registry.start()` | Simplest setup. Starts server, serves static files, and runs actors. | +| Serverless | `registry.serve()` | Fetch handler for serverless platforms | +| Serverless | `registry.handler()` | Integrating with existing routers (Hono, Elysia, etc.) | +| Runner | `registry.startEnvoy()` | Long-running processes without HTTP endpoints | + +_Source doc path: /docs/general/runtime-modes_ diff --git a/.agents/skills/live-cursors/reference/general/wasm-vs-native-sdk.md b/.agents/skills/live-cursors/reference/general/wasm-vs-native-sdk.md new file mode 100644 index 0000000..18cdd13 --- /dev/null +++ b/.agents/skills/live-cursors/reference/general/wasm-vs-native-sdk.md @@ -0,0 +1,79 @@ +# WASM vs Native SDK + +> Source: `src/content/docs/general/wasm-vs-native-sdk.mdx` +> Canonical URL: https://rivet.dev/docs/general/wasm-vs-native-sdk +> Description: RivetKit runs your actors on a native or a WebAssembly runtime depending on your platform. + +--- +RivetKit ships two runtimes for executing actor logic. Most projects use the +native runtime automatically. Edge and serverless platforms that cannot run +native binaries use the WebAssembly runtime. + +- **Native**: Default. Runs on Node.js and Bun via native bindings. Best performance. +- **WebAssembly (wasm)**: Runs anywhere a `WebAssembly.Module` can be instantiated, including Cloudflare Workers, Supabase Edge Functions, and Deno Deploy. + +## Native runtime + +The native runtime is the default and requires no configuration. It loads +platform-specific native bindings for the best performance and full feature +support on Node.js and Bun. + +## WebAssembly runtime + +Edge platforms run on isolates (V8, Deno) that cannot load native binaries, so +RivetKit provides a WebAssembly build of its core runtime in the +`@rivetkit/rivetkit-wasm` package. You select it with `runtime: "wasm"` and pass +the wasm bindings and binary through the `wasm` option. + +### Recommended: use a platform package + +For supported platforms, use the dedicated package instead of wiring the wasm +runtime by hand. They install the wasm runtime, load the binary, and (on +Cloudflare) provide the outbound WebSocket the engine tunnel needs. + +- **Cloudflare Workers**: [`@rivetkit/cloudflare-workers`](/docs/actors/quickstart/cloudflare) +- **Supabase Edge Functions**: [`@rivetkit/supabase`](/docs/actors/quickstart/supabase) + +```typescript @nocheck +// Cloudflare Workers +import { actor } from "rivetkit"; +import { createHandler } from "@rivetkit/cloudflare-workers"; + +const counter = actor({ state: { count: 0 }, actions: {} }); + +export default createHandler({ use: { counter } }); +``` + +### Manual configuration + +For platforms without a dedicated package, configure the wasm runtime directly. +Pass the bindings from `@rivetkit/rivetkit-wasm` and the wasm binary as +`initInput`. `initInput` accepts a `Uint8Array`, `URL`, `Response`, or a +`WebAssembly.Module`. + +```typescript @nocheck +import { actor, setup } from "rivetkit"; +import * as wasmBindings from "@rivetkit/rivetkit-wasm"; +import wasmModule from "@rivetkit/rivetkit-wasm/rivetkit_wasm_bg.wasm"; + +const counter = actor({ state: { count: 0 }, actions: {} }); + +const registry = setup({ + runtime: "wasm", + wasm: { bindings: wasmBindings, initInput: wasmModule }, + use: { counter }, +}); +``` + +On platforms without an outbound `new WebSocket(url)` constructor (such as +Cloudflare Workers), you must also install a fetch-based `globalThis.WebSocket` +shim so the actor can open its tunnel to the engine. The platform packages handle +this for you, which is why they are recommended. + +## Related + +- [Runtime Modes](/docs/general/runtime-modes) +- [Cloudflare Workers Quickstart](/docs/actors/quickstart/cloudflare) +- [Supabase Functions Quickstart](/docs/actors/quickstart/supabase) + +_Source doc path: /docs/general/wasm-vs-native-sdk_ diff --git a/.agents/skills/live-cursors/reference/self-hosting/configuration.md b/.agents/skills/live-cursors/reference/self-hosting/configuration.md new file mode 100644 index 0000000..a71d16e --- /dev/null +++ b/.agents/skills/live-cursors/reference/self-hosting/configuration.md @@ -0,0 +1,74 @@ +# Configuration + +> Source: `src/content/docs/self-hosting/configuration.mdx` +> Canonical URL: https://rivet.dev/docs/self-hosting/configuration +> Description: Rivet Engine can be configured through environment variables or configuration files. + +--- +The full JSON Schema for the configuration is available at [/docs/engine-config-schema.json](/docs/engine-config-schema.json). + +## Configuration Sources + +Rivet supports JSON, JSON5, JSONC, YAML, YML, and environment variable configurations. + +**Environment Variables** + +Use the `RIVET__` prefix with `__` as separator to configure properties in the config. For example: set the `RIVET__database__postgres__url` environment variable for `database.postgres.url`. + +**Configuration Paths** + +Configuration files are automatically discovered in platform-specific directories: + +- Linux: `/etc/rivet/config.json` +- macOS: `/Library/Application Support/rivet/config.json` +- Windows: `C:\ProgramData\rivet\config.json` + +**Multiple Files** + +Multiple configuration files in the same directory are loaded and merged together. For example: `/etc/rivet/config.json` and `/etc/rivet/database.json` will be merged together. + +**Override Configuration Path** + +You can override the default configuration path using the `--config` flag: + +```bash +# Load from a specific file +rivet-engine --config /path/to/config.json + +# Load from a directory +rivet-engine --config /etc/rivet + +# Load multiple paths (merged in order) +rivet-engine --config /etc/rivet/base.json --config /etc/rivet/override.json +``` + +## Configuration Reference + +### Pegboard Envoy Load Balancing + +`pegboard.envoy_load_balancer` supports the `hash` strategy for hash-ring-based envoy selection: + +```json +{ + "pegboard": { + "envoy_load_balancer": { + "hash": { + "virtual_nodes": 8, + "samples": 2, + "max_scan": 16, + "use_snapshot_read": true + } + } + } +} +``` + +Use `samples: 1` for a uniform random pick that skips slot reads. Use `samples >= 2` for power-of-K choices over envoy slot counts. Treat `virtual_nodes` as an operational invariant once envoys have registered. + +## Related + +- RivetKit actor runtime persistence lives in SQLite. Existing actor KV data is imported into SQLite the first time an actor wakes on the migrated runtime, then the original KV data is left frozen for downgrade safety. +- [PostgreSQL](/docs/self-hosting/postgres): Configure the experimental PostgreSQL backend +- [File System](/docs/self-hosting/filesystem): Configure file system storage for development + +_Source doc path: /docs/self-hosting/configuration_ diff --git a/.agents/skills/live-cursors/reference/self-hosting/docker-compose.md b/.agents/skills/live-cursors/reference/self-hosting/docker-compose.md new file mode 100644 index 0000000..8f2cea1 --- /dev/null +++ b/.agents/skills/live-cursors/reference/self-hosting/docker-compose.md @@ -0,0 +1,188 @@ +# Docker Compose + +> Source: `src/content/docs/self-hosting/docker-compose.mdx` +> Canonical URL: https://rivet.dev/docs/self-hosting/docker-compose +> Description: Deploy Rivet Engine with docker-compose for multi-container setups. + +--- +## Quick Start + +### Create docker-compose.yaml + +Create a `docker-compose.yaml` in your project root: + +```yaml +services: + rivet-engine: + image: rivetdev/engine:latest + ports: + - "6420:6420" + volumes: + - rivet-data:/data + environment: + RIVET__FILE_SYSTEM__PATH: "/data" + restart: unless-stopped + +volumes: + rivet-data: +``` + +### Start the engine + +```bash +docker-compose up -d +``` + +## Connecting Your Project + +Once the engine is running, add your app as a service in the same Compose file. + +### Create your server + +Follow the [quickstart](/docs/actors/quickstart/backend) to create a working server with actors. + +### Create a Dockerfile + +Create a `Dockerfile` in your project root: + +```dockerfile @nocheck +FROM node:22-slim +WORKDIR /app +COPY package.json pnpm-lock.yaml ./ +RUN corepack enable && pnpm install --frozen-lockfile +COPY . . +RUN pnpm build +CMD ["node", "dist/index.js"] +``` + +### Add your app to docker-compose.yaml + +Update your `docker-compose.yaml` to include your app alongside the engine: + +```yaml +services: + rivet-engine: + image: rivetdev/engine:latest + ports: + - "6420:6420" + volumes: + - rivet-data:/data + environment: + RIVET__FILE_SYSTEM__PATH: "/data" + restart: unless-stopped + + my-app: + build: . + environment: + RIVET_ENDPOINT: "http://default:admin@rivet-engine:6420" + depends_on: + - rivet-engine + restart: unless-stopped + +volumes: + rivet-data: +``` + +`RIVET_ENDPOINT` tells your app to connect to the engine as a runner instead of running standalone. The URL uses the format `http://namespace:token@host:port`. Inside the Docker network, your app reaches the engine at `rivet-engine:6420`. See [Endpoints](/docs/general/endpoints) for all options. + +### Start the services + +```bash +docker-compose up -d +``` + +### Register your runner with the engine + +### Dashboard + +1. Open the Rivet Engine dashboard at `http://localhost:6420`. +2. Enter your admin token when prompted. +3. In the namespace sidebar, click **Settings**. +4. Click **Add Provider**, then choose **Custom**. +5. Click **Next** (these settings can be changed later). +6. Click **Next** (you can safely skip the env ar step for Docker Compose). +5. Go to **Confirm Connection**, enter your app endpoint (`http://my-app:6420/api/rivet`), then click **Add**. + +### CLI (curl) + +Register your runner programmatically via the engine API: + +```bash @nocheck +curl -X PUT "http://localhost:6420/runner-configs/default?namespace=default" \ + -H "Content-Type: application/json" \ + -d '{ + "datacenters": { + "default": { + "normal": {} + } + } + }' +``` + +## Configuration + +### Config File + +Mount a JSON configuration file in your `docker-compose.yaml`: + +```yaml +services: + rivet-engine: + image: rivetdev/engine:latest + ports: + - "6420:6420" + volumes: + - ./rivet-config.json:/etc/rivet/config.json:ro + - rivet-data:/data + restart: unless-stopped + +volumes: + rivet-data: +``` + +Create `rivet-config.json` in the same directory as your `docker-compose.yaml`. See the [Configuration](/docs/self-hosting/configuration) docs for all available options and the full [JSON Schema](/docs/engine-config-schema.json). + +```json +{ + "postgres": { + "url": "postgresql://rivet:password@postgres:5432/rivet" + } +} +``` + +### Postgres Setup + +PostgreSQL is the recommended backend for multi-node self-hosted deployments today, but it remains experimental. For a production-ready single-node Rivet deployment, use the file system backend (RocksDB-based). Enterprise teams can contact [enterprise support](https://rivet.dev/sales) about FoundationDB for the most scalable production-ready deployment. + +```yaml +services: + postgres: + image: postgres:15 + environment: + POSTGRES_DB: rivet + POSTGRES_USER: rivet + POSTGRES_PASSWORD: rivet_password + volumes: + - postgres-data:/var/lib/postgresql/data + restart: unless-stopped + + rivet-engine: + image: rivetdev/engine:latest + ports: + - "6420:6420" + environment: + RIVET__POSTGRES__URL: postgresql://rivet:rivet_password@postgres:5432/rivet + depends_on: + - postgres + restart: unless-stopped + +volumes: + postgres-data: +``` + +## Next Steps + +- Review the [Production Checklist](/docs/self-hosting/production-checklist) before going live +- See [Configuration](/docs/self-hosting/configuration) for all options + +_Source doc path: /docs/self-hosting/docker-compose_ diff --git a/.agents/skills/live-cursors/reference/self-hosting/docker-container.md b/.agents/skills/live-cursors/reference/self-hosting/docker-container.md new file mode 100644 index 0000000..e6983ae --- /dev/null +++ b/.agents/skills/live-cursors/reference/self-hosting/docker-container.md @@ -0,0 +1,150 @@ +# Docker Container + +> Source: `src/content/docs/self-hosting/docker-container.mdx` +> Canonical URL: https://rivet.dev/docs/self-hosting/docker-container +> Description: Run Rivet Engine in a single Docker container. + +--- +## Quick Start + +### Start the engine + +```bash +docker run -d \ + --name rivet-engine \ + -p 6420:6420 \ + -v rivet-data:/data \ + -e RIVET__FILE_SYSTEM__PATH="/data" \ + rivetdev/engine:latest +``` + +### Verify the engine is running + +```bash +curl http://localhost:6420/health +``` + +## Connecting Your Project + +Once the engine is running, connect your app to it. + +### Create your server + +Follow the [quickstart](/docs/actors/quickstart/backend) to create a working server with actors. + +### Start your app with RIVET_ENDPOINT + +Set `RIVET_ENDPOINT` to tell your app to connect to the engine as a runner instead of running standalone: + +```bash @nocheck +RIVET_ENDPOINT="http://default:admin@host.docker.internal:6420" npm start +``` + +If running your app outside of Docker, use `localhost` instead of `host.docker.internal`: + +```bash @nocheck +RIVET_ENDPOINT="http://default:admin@localhost:6420" npm start +``` + +See [Endpoints](/docs/general/endpoints) for all options. + +### Register your runner with the engine + +### Dashboard + +1. Open the Rivet Engine dashboard at `http://localhost:6420`. +2. Enter your admin token when prompted. +3. In the namespace sidebar, click **Settings**. +4. Click **Add Provider**, then choose **Custom**. +5. Click **Next**. +6. Go to **Confirm Connection**, enter your app endpoint, then click **Add**. + +### CLI (curl) + +Register your runner programmatically via the engine API: + +```bash @nocheck +curl -X PUT "http://localhost:6420/runner-configs/default?namespace=default" \ + -H "Content-Type: application/json" \ + -d '{ + "datacenters": { + "default": { + "normal": {} + } + } + }' +``` + +## Configuration + +### Environment Variables + +Configure Rivet using environment variables: + +```bash +docker run -d \ + --name rivet-engine \ + -p 6420:6420 \ + -v rivet-data:/data \ + -e RIVET__FILE_SYSTEM__PATH="/data" \ + -e RIVET__POSTGRES__URL="postgresql://postgres:password@localhost:5432/db" \ + rivetdev/engine:latest +``` + +### Config File + +Mount a JSON configuration file: + +```bash +docker run -d \ + --name rivet-engine \ + -p 6420:6420 \ + -v rivet-data:/data \ + -v $(pwd)/rivet-config.json:/etc/rivet/config.json:ro \ + rivetdev/engine:latest +``` + +Create `rivet-config.json` in your working directory. See the [Configuration](/docs/self-hosting/configuration) docs for all available options and the full [JSON Schema](/docs/engine-config-schema.json). + +```json +{ + "postgres": { + "url": "postgresql://postgres:password@localhost:5432/db" + } +} +``` + +### Postgres Setup + +PostgreSQL is the recommended backend for multi-node self-hosted deployments today, but it remains experimental. For a production-ready single-node Rivet deployment, use the file system backend (RocksDB-based). Enterprise teams can contact [enterprise support](https://rivet.dev/sales) about FoundationDB for the most scalable production-ready deployment. + +```bash +# Create network +docker network create rivet-net + +# Run PostgreSQL +docker run -d \ + --name postgres \ + --network rivet-net \ + -e POSTGRES_DB=rivet \ + -e POSTGRES_USER=rivet \ + -e POSTGRES_PASSWORD=rivet_password \ + -v postgres-data:/var/lib/postgresql/data \ + postgres:15 + +# Run Rivet Engine +docker run -d \ + --name rivet-engine \ + --network rivet-net \ + -p 6420:6420 \ + -e RIVET__POSTGRES__URL="postgresql://rivet:rivet_password@postgres:5432/rivet" \ + rivetdev/engine +``` + +## Next Steps + +- Review the [Production Checklist](/docs/self-hosting/production-checklist) before going live +- Use [Docker Compose](/docs/self-hosting/docker-compose) for multi-container setups +- See [Configuration](/docs/self-hosting/configuration) for all options + +_Source doc path: /docs/self-hosting/docker-container_ diff --git a/.agents/skills/live-cursors/reference/self-hosting/filesystem.md b/.agents/skills/live-cursors/reference/self-hosting/filesystem.md new file mode 100644 index 0000000..30f7bdb --- /dev/null +++ b/.agents/skills/live-cursors/reference/self-hosting/filesystem.md @@ -0,0 +1,47 @@ +# File System + +> Source: `src/content/docs/self-hosting/filesystem.mdx` +> Canonical URL: https://rivet.dev/docs/self-hosting/filesystem +> Description: The file system backend stores all data on the local disk. This is suitable for single-node deployments, development, and testing. + +--- +For a production-ready single-node Rivet deployment, use the file system backend (RocksDB-based); for multi-node deployments, PostgreSQL is the recommended backend today but remains experimental as we evaluate the best fit for scalability and performance, and Enterprise teams can contact [enterprise support](https://rivet.dev/sales) about FoundationDB. + +## Configuration + +```json Configuration-file +{ + "database": { + "file_system": { + "path": "/var/lib/rivet/data" + } + } +} +``` + +```bash Environment-variables +RIVET__database__file_system__path="/var/lib/rivet/data" +``` + +## Default Paths + +If no path is specified, Rivet uses platform-specific default locations: + +- Linux: `~/.local/share/rivet-engine/db` +- macOS: `~/Library/Application Support/rivet-engine/db` +- Windows: `%APPDATA%\rivet-engine\db` + +When running in a container or as a service, the path defaults to `./data/db` relative to the working directory. + +## When to Use File System + +The file system backend is ideal for: + +- Local development +- Single-node deployments +- Testing and prototyping +- Air-gapped environments without database infrastructure + +If you need a production-ready Rivet deployment today, use this backend for smaller single-node setups; for multi-node deployments, PostgreSQL is the recommended backend today though still experimental, and Enterprise teams can contact [enterprise support](https://rivet.dev/sales) about FoundationDB for the most scalable production-ready deployment. + +_Source doc path: /docs/self-hosting/filesystem_ diff --git a/.agents/skills/live-cursors/reference/self-hosting/foundationdb.md b/.agents/skills/live-cursors/reference/self-hosting/foundationdb.md new file mode 100644 index 0000000..b8331bb --- /dev/null +++ b/.agents/skills/live-cursors/reference/self-hosting/foundationdb.md @@ -0,0 +1,35 @@ +# FoundationDB (Enterprise) + +> Source: `src/content/docs/self-hosting/foundationdb.mdx` +> Canonical URL: https://rivet.dev/docs/self-hosting/foundationdb +> Description: FoundationDB is the recommended storage backend for scalable production Rivet deployments. + +--- +FoundationDB requires an enterprise license. Contact [enterprise support](https://rivet.dev/sales) for setup guidance and access. + +## Overview + +FoundationDB is the recommended storage backend for scalable, production-ready Rivet deployments. It is a distributed, ordered key-value store originally built by Apple. + +FoundationDB powers some of the largest infrastructure in the world: + +- **Apple**: iCloud and other Apple services +- **Snowflake**: Cloud data platform metadata layer +- **Datadog**: Observability and monitoring platform +- **Tigris Data**: Globally distributed object storage + +Its strict serializability guarantees, fault tolerance, and ability to scale linearly across nodes make it the ideal backend for Rivet's actor state and orchestration layer. + +## Why FoundationDB + +| | RocksDB (File System) | PostgreSQL | FoundationDB | +|---|---|---|---| +| **Scalability** | Single node | Primary/replica failover | Linear horizontal scaling | +| **Fault tolerance** | None | Primary/replica failover | Automatic recovery with no data loss | +| **Production readiness** | Development and small deployments | Experimental for multi-node | Battle-tested at global scale | + +## Getting Started + +FoundationDB configuration and cluster setup are handled as part of enterprise onboarding. Contact [enterprise support](https://rivet.dev/sales) to get started. + +_Source doc path: /docs/self-hosting/foundationdb_ diff --git a/.agents/skills/live-cursors/reference/self-hosting/install.md b/.agents/skills/live-cursors/reference/self-hosting/install.md new file mode 100644 index 0000000..344be8e --- /dev/null +++ b/.agents/skills/live-cursors/reference/self-hosting/install.md @@ -0,0 +1,32 @@ +# Installing Rivet Engine + +> Source: `src/content/docs/self-hosting/install.mdx` +> Canonical URL: https://rivet.dev/docs/self-hosting/install +> Description: Install Rivet Engine using Docker, binaries, or a source build. + +--- +For more options: + +- [Docker Container](/docs/self-hosting/docker-container) for persistent storage, configuration, and production setups +- [Docker Compose](/docs/self-hosting/docker-compose) for multi-container deployments with PostgreSQL + +## Docker + +```bash +docker run -p 6420:6420 rivetdev/engine +``` + +## Prebuilt Binaries + + Prebuilt binaries coming soon + +## Build From Source + +```bash +git clone https://github.com/rivet-dev/rivet.git +cd rivet +cargo build --release -p rivet-engine +./target/release/rivet-engine +``` + +_Source doc path: /docs/self-hosting/install_ diff --git a/.agents/skills/live-cursors/reference/self-hosting/kubernetes.md b/.agents/skills/live-cursors/reference/self-hosting/kubernetes.md new file mode 100644 index 0000000..a2c619c --- /dev/null +++ b/.agents/skills/live-cursors/reference/self-hosting/kubernetes.md @@ -0,0 +1,236 @@ +# Kubernetes + +> Source: `src/content/docs/self-hosting/kubernetes.mdx` +> Canonical URL: https://rivet.dev/docs/self-hosting/kubernetes +> Description: Deploy production-ready Rivet Engine to Kubernetes with PostgreSQL storage. + +--- +PostgreSQL is the recommended backend for multi-node self-hosted deployments today, but it remains experimental. For a production-ready single-node Rivet deployment, use the file system backend (RocksDB-based). Enterprise teams can contact [enterprise support](https://rivet.dev/sales) about FoundationDB for the most scalable production-ready deployment. + +## Prerequisites + +- Kubernetes cluster +- `kubectl` configured +- [Metrics server](https://github.com/kubernetes-sigs/metrics-server) (required for HPA) — included by default in most distributions (k3d, GKE, EKS, AKS) + +## Deploy Rivet Engine + + +### Download Manifests + + Download the `self-host/k8s/engine` directory from the Rivet repository: + + ```bash + npx giget@latest gh:rivet-dev/rivet/self-host/k8s/engine rivet-k8s + cd rivet-k8s + ``` + + + +### Configure Engine + + In `02-engine-configmap.yaml`, set `public_url` to your engine's external URL. + + + +### Configure PostgreSQL + + In `11-postgres-secret.yaml`, update the PostgreSQL password. See [Using a Managed PostgreSQL Service](#using-a-managed-postgresql-service) for external databases. + + + +### Configure Admin Token + + Generate a secure admin token and save it somewhere safe: + + ```bash + openssl rand -hex 32 + ``` + + Create the namespace and store the token as a Kubernetes secret: + + ```bash + kubectl create namespace rivet-engine + kubectl -n rivet-engine create secret generic rivet-secrets --from-literal=admin-token=YOUR_TOKEN_HERE + ``` + + + +### Deploy + + ```bash + # Apply all manifests + kubectl apply -f . + + # Wait for all pods to be ready + kubectl -n rivet-engine wait --for=condition=ready pod -l app=nats --timeout=300s + kubectl -n rivet-engine wait --for=condition=ready pod -l app=postgres --timeout=300s + kubectl -n rivet-engine wait --for=condition=ready pod -l app=rivet-engine --timeout=300s + + # Verify all pods are running + kubectl -n rivet-engine get pods + ``` + + + +### Access the Engine + + Visit `/ui` on your `public_url` to access the dashboard. + + +## Connecting Your Project + +### Create your server + +Follow the [quickstart](/docs/actors/quickstart/backend) to create a working server with actors. + +### Create Kubernetes manifests + +Create these manifest files for your app: + +```yaml deployment.yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: rivetkit-app + namespace: your-namespace +spec: + replicas: 1 + selector: + matchLabels: + app: rivetkit-app + template: + metadata: + labels: + app: rivetkit-app + spec: + # Allow enough time for actors to gracefully stop on SIGTERM. + # The runner waits up to 30m for actors to finish. + # Add buffer for runner shutdown overhead after actors stop. + # See: /docs/actors/versions#graceful-shutdown-sigterm + terminationGracePeriodSeconds: 2100 + containers: + - name: rivetkit-app + image: registry.example.com/your-team/rivetkit-app:latest + envFrom: + - secretRef: + name: rivetkit-secrets +``` + +```yaml service.yaml +apiVersion: v1 +kind: Service +metadata: + name: rivetkit-app + namespace: your-namespace +spec: + selector: + app: rivetkit-app + ports: + - name: http + port: 8080 + targetPort: 8080 +``` + +### Configure the endpoint + +Create `rivetkit-secrets.yaml` with `RIVET_ENDPOINT` pointing to the engine. This tells your app to connect as a runner instead of running standalone. See [Endpoints](/docs/general/endpoints) for all options. + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: rivetkit-secrets + namespace: your-namespace +type: Opaque +stringData: + RIVET_ENDPOINT: http://my-app:your-admin-token@your-engine.example.com +``` + +### Deploy your app + +```bash +kubectl apply -f rivetkit-secrets.yaml +kubectl apply -f deployment.yaml +kubectl apply -f service.yaml +``` + +### Register your runner with the engine + +### Dashboard + +1. Open the Rivet Engine dashboard in your browser. +2. Enter your admin token when prompted. +3. In the namespace sidebar, click **Settings**. +4. Click **Add Provider**, then choose **Custom**. +5. Click **Next**. +6. Go to **Confirm Connection**, enter your app endpoint (e.g. `http://rivetkit-app.your-namespace:8080/api/rivet`), then click **Add**. + +### CLI (curl) + +Register your runner programmatically via the engine API: + +```bash @nocheck +curl -X PUT "https://your-engine.example.com/runner-configs/default?namespace=default" \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer your-admin-token" \ + -d '{ + "datacenters": { + "default": { + "normal": {} + } + } + }' +``` + +## Advanced + +### Using a Managed PostgreSQL Service + +If you prefer to use a managed PostgreSQL service (e.g. Amazon RDS, Cloud SQL, Azure Database) instead of the bundled Postgres deployment: + +- Update the `postgres.url` connection string in `02-engine-configmap.yaml` to point to your managed instance +- Delete the bundled PostgreSQL manifests: + - `10-postgres-configmap.yaml` + - `11-postgres-secret.yaml` + - `12-postgres-statefulset.yaml` + - `13-postgres-service.yaml` + +### Applying Configuration Updates + +When making subsequent changes to `02-engine-configmap.yaml`, restart the engine pods to pick up the new configuration: + +```bash +kubectl apply -f 02-engine-configmap.yaml +kubectl -n rivet-engine rollout restart deployment/rivet-engine +``` + +### Ingress and Load Balancer Timeouts + +Rivet uses long-lived WebSocket connections for both client traffic (browsers, SDKs) and envoy traffic (actor hosts connecting back to the engine). Default Ingress and cloud load balancer idle timeouts (typically 30 to 60 seconds) drop these connections and cause reconnect storms. + +Raise the idle / read / send timeout on every Ingress and load balancer in front of the engine to at least 1 hour (`3600` seconds). Examples: + +- **NGINX Ingress** (annotations on the Ingress): + + ```yaml + nginx.ingress.kubernetes.io/proxy-read-timeout: "3600" + nginx.ingress.kubernetes.io/proxy-send-timeout: "3600" + ``` + +- **AWS Load Balancer Controller (ALB)**: + + ```yaml + alb.ingress.kubernetes.io/load-balancer-attributes: idle_timeout.timeout_seconds=3600 + ``` + +- **GCE Ingress (GKE)**: set `timeoutSec: 3600` on the `BackendConfig` referenced by the Service. + +The same guidance applies to the load balancer fronting your RivetKit app, since envoys connect to it over WebSocket. + +## Next Steps + +- Review the [Production Checklist](/docs/self-hosting/production-checklist) before going live +- See [Configuration](/docs/self-hosting/configuration) for all engine config options + +_Source doc path: /docs/self-hosting/kubernetes_ diff --git a/.agents/skills/live-cursors/reference/self-hosting/multi-region.md b/.agents/skills/live-cursors/reference/self-hosting/multi-region.md new file mode 100644 index 0000000..2ecca85 --- /dev/null +++ b/.agents/skills/live-cursors/reference/self-hosting/multi-region.md @@ -0,0 +1,10 @@ +# Multi-Region + +> Source: `src/content/docs/self-hosting/multi-region.mdx` +> Canonical URL: https://rivet.dev/docs/self-hosting/multi-region +> Description: Rivet Engine supports scaling transparently across multiple regions. + +--- +Documentation coming soon + +_Source doc path: /docs/self-hosting/multi-region_ diff --git a/.agents/skills/live-cursors/reference/self-hosting/postgres.md b/.agents/skills/live-cursors/reference/self-hosting/postgres.md new file mode 100644 index 0000000..e935246 --- /dev/null +++ b/.agents/skills/live-cursors/reference/self-hosting/postgres.md @@ -0,0 +1,192 @@ +# PostgreSQL + +> Source: `src/content/docs/self-hosting/postgres.mdx` +> Canonical URL: https://rivet.dev/docs/self-hosting/postgres +> Description: Configure PostgreSQL for self-hosted Rivet deployments. + +--- +PostgreSQL is the recommended backend for multi-node self-hosted deployments today, but it remains experimental. For a production-ready single-node Rivet deployment, use the file system backend (RocksDB-based). Enterprise teams can contact [enterprise support](https://rivet.dev/sales) about FoundationDB for the most scalable production-ready deployment. + +## Basic Configuration + +```json Configuration-file +{ + "database": { + "postgres": { + "url": "postgresql://user:password@host:5432/database" + } + } +} +``` + +```bash Environment-variables +RIVET__database__postgres__url="postgresql://user:password@host:5432/database" +``` + +## Managed Postgres Compatibility + +Some hosted PostgreSQL platforms require additional configuration due to platform-specific restrictions. + +### PlanetScale + +Use direct connection (not connection pooler). + +```json Configuration-file +{ + "database": { + "postgres": { + "url": "postgresql://pscale_api_.:@.pg.psdb.cloud:5432/postgres?sslmode=require", + "unstable_disable_lock_customization": true + } + } +} +``` + +```bash Environment-variables +RIVET__database__postgres__url="postgresql://pscale_api_.:@.pg.psdb.cloud:5432/postgres?sslmode=require" +RIVET__database__postgres__unstable_disable_lock_customization=true +``` + +### Supabase + +Use direct connection on port `5432` (not connection pooler). + +#### Without SSL + +```json Configuration-file +{ + "database": { + "postgres": { + "url": "postgresql://postgres:@db..supabase.co:5432/postgres?sslmode=disable", + "unstable_disable_lock_customization": true + } + } +} +``` + +```bash Environment-variables +RIVET__database__postgres__url="postgresql://postgres:@db..supabase.co:5432/postgres?sslmode=disable" +RIVET__database__postgres__unstable_disable_lock_customization=true +``` + +#### With SSL + +Download the root certificate from your Supabase dashboard and specify its path. See [Supabase SSL Enforcement](https://supabase.com/docs/guides/platform/ssl-enforcement) for details. + +```json Configuration-file +{ + "database": { + "postgres": { + "url": "postgresql://postgres:@db..supabase.co:5432/postgres?sslmode=require", + "unstable_disable_lock_customization": true, + "ssl": { + "root_cert_path": "/path/to/supabase-ca.crt" + } + } + } +} +``` + +```bash Environment-variables +RIVET__database__postgres__url="postgresql://postgres:@db..supabase.co:5432/postgres?sslmode=require" +RIVET__database__postgres__unstable_disable_lock_customization=true +RIVET__database__postgres__ssl__root_cert_path="/path/to/supabase-ca.crt" +``` + +## SSL/TLS Support + +To enable SSL for Postgres, add `sslmode=require` to your PostgreSQL connection URL: + +```json Configuration-file +{ + "database": { + "postgres": { + "url": "postgresql://user:password@host.example.com:5432/database?sslmode=require" + } + } +} +``` + +```bash Environment-variables +RIVET__database__postgres__url="postgresql://user:password@host.example.com:5432/database?sslmode=require" +``` + +The `sslmode` parameter controls TLS usage: + +- `disable`: Do not use TLS +- `prefer`: Use TLS if available, otherwise connect without TLS (default) +- `require`: Require TLS connection (fails if TLS is not available) + +To verify the server certificate against a CA or verify the hostname, use custom SSL certificates (see below). + +### Custom SSL Certificates + +For databases using custom certificate authorities (e.g., Supabase) or requiring client certificate authentication, you can specify certificate paths in the configuration: + +```json Configuration-file +{ + "database": { + "postgres": { + "url": "postgresql://user:password@host:5432/database?sslmode=require", + "ssl": { + "root_cert_path": "/path/to/root-ca.crt", + "client_cert_path": "/path/to/client.crt", + "client_key_path": "/path/to/client.key" + } + } + } +} +``` + +```bash Environment-variables +RIVET__database__postgres__url="postgresql://user:password@host:5432/database?sslmode=require" +RIVET__database__postgres__ssl__root_cert_path="/path/to/root-ca.crt" +RIVET__database__postgres__ssl__client_cert_path="/path/to/client.crt" +RIVET__database__postgres__ssl__client_key_path="/path/to/client.key" +``` + +| Parameter | Description | PostgreSQL Equivalent | +|-----------|-------------|----------------------| +| `root_cert_path` | Path to the root certificate file for verifying the server's certificate | `sslrootcert` | +| `client_cert_path` | Path to the client certificate file for client certificate authentication | `sslcert` | +| `client_key_path` | Path to the client private key file for client certificate authentication | `sslkey` | + +All SSL paths are optional. If not specified, Rivet uses the default system root certificates from Mozilla's root certificate store. + +## Do Not Use Connection Poolers + +Rivet requires direct PostgreSQL connections for session-level features and does not support connection poolers. + +Do not use: + +- PgBouncer +- Supavisor +- AWS RDS Proxy + +## Troubleshooting + +### Permission Denied Errors + +If you see errors like: + +``` +ERROR: permission denied to set parameter "deadlock_timeout" +ERROR: current transaction is aborted, commands ignored until end of transaction block +``` + +Add `unstable_disable_lock_customization: true` to your configuration: + +```json +{ + "database": { + "postgres": { + "url": "postgresql://...", + "unstable_disable_lock_customization": true + } + } +} +``` + +This disables Rivet's attempt to set `lock_timeout = 0` and `deadlock_timeout = 10ms`. Since `lock_timeout` defaults to `0` in PostgreSQL, skipping these settings is safe. Deadlock detection will use the default `1s` timeout instead of `10ms`. + +_Source doc path: /docs/self-hosting/postgres_ diff --git a/.agents/skills/live-cursors/reference/self-hosting/production-checklist.md b/.agents/skills/live-cursors/reference/self-hosting/production-checklist.md new file mode 100644 index 0000000..3cefa5a --- /dev/null +++ b/.agents/skills/live-cursors/reference/self-hosting/production-checklist.md @@ -0,0 +1,53 @@ +# Production Checklist + +> Source: `src/content/docs/self-hosting/production-checklist.mdx` +> Canonical URL: https://rivet.dev/docs/self-hosting/production-checklist +> Description: Checklist for deploying a self-hosted Rivet Engine to production. + +--- +We recommend passing this page to your coding agent to verify your configuration before deploying. + +PostgreSQL is the recommended backend for multi-node self-hosted deployments today, but it remains experimental. For a production-ready single-node Rivet deployment, use the file system backend (RocksDB-based). Enterprise teams can contact [enterprise support](https://rivet.dev/sales) about FoundationDB for the most scalable production-ready deployment. + +Also review the [general production checklist](/docs/general/production-checklist). + +## Security + +- **Validate that you have an admin token configured.** Generate a strong, random token for engine authentication. See [Configuration](/docs/self-hosting/configuration). +- **Verify your admin token is not exposed publicly.** Do not include the admin token in `RIVET_PUBLIC_ENDPOINT` or anywhere accessible to clients. See [Endpoints](/docs/general/endpoints#public-endpoint). +- **Configure TLS termination.** Ensure connections to the engine are encrypted via a reverse proxy or load balancer. + +## Resources + +- **Set container resource limits.** Recommended at least 1 CPU and 2 GB of RAM per Rivet Engine instance. +- **Configure health checks.** Set up liveness and readiness probes on port `6421` at `/health`. Recommended timeout of 5 seconds. + +## Scaling + +- **Configure autoscaling for the Rivet Engine.** Set target CPU utilization to 70% and memory to 80% to ensure headroom for traffic spikes. In Kubernetes, this is configured via a Horizontal Pod Autoscaler (HPA). +- **Use 2+ engine nodes for redundancy.** Running a single engine node is a single point of failure. Deploy at least two engine instances behind a load balancer. +- **RocksDB only supports a single node.** Do not run multiple RocksDB nodes. For a production-ready single-node Rivet deployment, use the file system backend (RocksDB-based). For multi-node deployments, PostgreSQL is the recommended backend today, though it remains experimental as we evaluate the best fit for scalability and performance. +- **Validate the rate limit on your serverless actor host.** Actor start requests are sent from your engine instances, so they all originate from a small set of IPs. Per-IP rate limits on the actor host will throttle the engine before they would throttle end-user traffic. Size the limit to your peak actor create and wake rate, and configure platform max concurrency (e.g. on GCP Cloud Run) to match your expected concurrent actor count. + +## PostgreSQL + +- **PostgreSQL is recommended for multi-node deployments, but remains experimental.** Validate the deployment carefully before rollout. +- **Configure automated backups.** Set up regular backups for your PostgreSQL database to prevent data loss. +- **Configure failover.** Set up a standby replica with automatic failover to ensure high availability. +- **Use FoundationDB for the most scalable production-ready deployments.** FoundationDB provides the best performance, scalability, and uptime for Rivet. Contact [enterprise support](https://rivet.dev/sales) for FoundationDB guidance. + +## NATS + +- **Use NATS for pub/sub (recommended).** By default, Rivet uses PostgreSQL `LISTEN`/`NOTIFY` for pub/sub which has limited throughput. NATS significantly improves performance for high-traffic deployments. This is not needed if using RocksDB. See [Configuration](/docs/self-hosting/configuration). +- **Deploy 2+ NATS replicas.** Run at least two NATS replicas for high availability. + +## Monitoring + +- **Configure OpenTelemetry.** The Rivet Engine supports exporting traces and metrics via OpenTelemetry. Set `RIVET_OTEL_ENABLED=1` and `RIVET_OTEL_GRPC_ENDPOINT` to your collector endpoint (defaults to `http://localhost:4317`). Adjust `RIVET_OTEL_SAMPLER_RATIO` to control trace sampling (defaults to `0.001`). See [Configuration](/docs/self-hosting/configuration). +- **Set up alerts for critical metrics.** Monitor engine CPU, memory, request latency, and error rates. Configure alerts to notify your team before issues become outages. + +## Enterprise + +- **Contact [enterprise support](https://rivet.dev/sales) for production-ready deployments.** We can help with architecture review, scaling guidance, and FoundationDB support. + +_Source doc path: /docs/self-hosting/production-checklist_ diff --git a/.agents/skills/live-cursors/reference/self-hosting/railway.md b/.agents/skills/live-cursors/reference/self-hosting/railway.md new file mode 100644 index 0000000..c4bb922 --- /dev/null +++ b/.agents/skills/live-cursors/reference/self-hosting/railway.md @@ -0,0 +1,99 @@ +# Railway Deployment + +> Source: `src/content/docs/self-hosting/railway.mdx` +> Canonical URL: https://rivet.dev/docs/self-hosting/railway +> Description: Railway provides a simple platform for deploying Rivet Engine with automatic scaling and managed infrastructure. + +--- +## Video Tutorial + +PostgreSQL is the recommended backend for multi-node self-hosted deployments today, but it remains experimental. For a production-ready single-node Rivet deployment, use the file system backend (RocksDB-based). Enterprise teams can contact [enterprise support](https://rivet.dev/sales) about FoundationDB for the most scalable production-ready deployment. + +## Quick Deploy + +Choose the template that best fits your needs: + +| **Rivet Template** | **Rivet Starter** | +|-------------------|-------------------| +| [![Deploy on Railway](https://railway.com/button.svg)](https://railway.com/deploy/rivet?referralCode=RC7bza&utm_medium=integration&utm_source=template&utm_campaign=generic) | [![Deploy Rivet Starter](https://railway.com/button.svg)](https://railway.com/deploy/rivet-starter) | +| **Blank template** to start fresh | **Complete example** with chat app | +| - Rivet Engine | - Pre-configured Rivet Engine | +| - PostgreSQL database | - Example chat application with Actors | +| - Basic configuration | - PostgreSQL database | +| - Manual setup required | - Rivet Inspector for debugging | +| | - Ready to run immediately | + +You can also use the [Rivet Railway template](https://github.com/rivet-dev/template-railway) as a starting point for your application. + +After deploying either template, you can find the `RIVET__AUTH__ADMIN_TOKEN` under the **Variables** tab in the Railway dashboard. This token is required to access the Rivet Inspector. + +## Manual Deployment + +### Prerequisites + +1. [Railway account](https://railway.app) +2. [Railway CLI](https://docs.railway.app/develop/cli) (optional) + +### Step 1: Create New Project + +```bash +# Using Railway CLI +railway init + +# Or create via dashboard +# https://railway.app/new +``` + +### Step 2: Add Services + +#### Deploy PostgreSQL Database + +1. Click "New Service" → "Database" → "PostgreSQL" +2. Railway automatically provisions and configures PostgreSQL +3. Note the connection string from the service variables + +#### Deploy Rivet Engine + +1. Click "New Service" → "Docker Image" +2. Set image: `rivetdev/engine:latest` +3. Configure environment variables: + - `RIVET__POSTGRES__URL=${{Postgres.DATABASE_URL}}` +4. Configure graceful shutdown (see [Graceful Shutdown](#graceful-shutdown) below) + +### Step 3: Deploy Your Application + +Follow the [Railway Quick Start guide](https://docs.railway.com/quick-start) to deploy your repository: + +1. Connect your GitHub account to Railway +2. Select your repository containing your Rivet application +3. Railway will automatically detect and deploy your application +4. Configure environment variables for your application: + - `RIVET_ENDPOINT=${{Rivet.RAILWAY_PRIVATE_DOMAIN}}` - Points to the Rivet Engine service's private domain + +## WebSocket Timeouts + +Rivet uses long-lived WebSocket connections for both client traffic (browsers, SDKs) and envoy traffic (actor hosts connecting back to the engine). Railway's HTTP proxy supports WebSockets, but you must make sure no app-side timeout cuts them off. + +If you front the engine with your own reverse proxy (NGINX, Caddy, etc.) inside the Railway service, raise its idle / read timeout to at least 1 hour (`3600` seconds). The same guidance applies to the Railway service hosting your RivetKit app, since envoys connect to it over WebSocket. + +## Graceful Shutdown + +By default, Railway kills the old deploy 0 seconds after sending `SIGTERM` (see [Railway's docs](https://docs.railway.com/deployments/reference#singleton-deploys)), so in-flight requests are dropped and state flushes can be interrupted on every deploy. Rivet ships with a `SIGTERM` handler that drains cleanly, but it only gets to run if Railway is configured to give it time. + +Configure the following under **Settings → Deploy** on your service: + +- **Draining seconds** — the grace window between `SIGTERM` and `SIGKILL`. This is how long Rivet has to finish in-flight work and flush state. See [Railway's deployment teardown docs](https://docs.railway.com/deployments/deployment-teardown). + +Set draining seconds in the dashboard, via `drainingSeconds` in config-as-code, or via the `RAILWAY_DEPLOYMENT_DRAINING_SECONDS` service variable. A reasonable value is **60 seconds**. + +If your start command is a wrapper process (for example `npm start`, `yarn start`, `pnpm start`, or a shell script), the wrapper becomes PID 1 and swallows the signal — your app never drains and Railway force-kills it at the end of the window. + +- Invoke your binary directly as the start command (for example `node dist/index.js`, not `npm start`). +- Or run `dumb-init` / `tini` as PID 1 in your Dockerfile so signals forward to your process. + +## Next Steps + +- Review the [Production Checklist](/docs/self-hosting/production-checklist) before going live +- See [Configuration](/docs/self-hosting/configuration) for all options + +_Source doc path: /docs/self-hosting/railway_ diff --git a/.agents/skills/live-cursors/reference/self-hosting/render.md b/.agents/skills/live-cursors/reference/self-hosting/render.md new file mode 100644 index 0000000..0bc6d6c --- /dev/null +++ b/.agents/skills/live-cursors/reference/self-hosting/render.md @@ -0,0 +1,131 @@ +# Render Deployment + +> Source: `src/content/docs/self-hosting/render.mdx` +> Canonical URL: https://rivet.dev/docs/self-hosting/render +> Description: Deploy Rivet Engine to Render with managed PostgreSQL and automatic HTTPS, using the experimental PostgreSQL backend. + +--- +PostgreSQL is the recommended backend for multi-node self-hosted deployments today, but it remains experimental. For a production-ready single-node Rivet deployment, use the file system backend (RocksDB-based). Enterprise teams can contact [enterprise support](https://rivet.dev/sales) about FoundationDB for the most scalable production-ready deployment. + +## Prerequisites + +1. A [Render account](https://render.com) +2. A GitHub repository + +## Deploy Rivet Engine + + +### Create the Blueprint Files + + Add these three files to the root of your GitHub repository: + + **render.yaml** + + ```yaml + databases: + - name: rivet-db + plan: basic-256mb + databaseName: rivet + user: rivet + + services: + - type: web + name: rivet-engine + runtime: docker + dockerfilePath: ./Dockerfile.render + plan: starter + healthCheckPath: /health + envVars: + - key: DATABASE_URL + fromDatabase: + name: rivet-db + property: connectionString + - key: RIVET__AUTH__ADMIN_TOKEN + generateValue: true + ``` + + **Dockerfile.render** + + ```dockerfile + FROM rivetdev/engine:latest + + COPY entrypoint.render.sh /entrypoint.render.sh + RUN chmod +x /entrypoint.render.sh + + ENTRYPOINT ["/entrypoint.render.sh"] + ``` + + **entrypoint.render.sh** + + ```bash + #!/bin/sh + + if [ -n "$DATABASE_URL" ]; then + export RIVET__POSTGRES__URL="${DATABASE_URL}?sslmode=disable" + fi + + exec /usr/bin/rivet-engine start + ``` + + Commit and push these files to your repository. + + + +### Deploy to Render + + 1. Go to the [Render Dashboard](https://dashboard.render.com/) + 2. Click **Blueprints** in the left sidebar + 3. Click **New Blueprint Instance** + 4. Connect your GitHub account if you haven't already + 5. Select the repository containing the files from the previous step + 6. Click **Apply** + + Render will automatically create the PostgreSQL database and deploy the Rivet Engine. + + + +### Get Your Admin Token + + 1. Once deployed, go to your **rivet-engine** service in the Render Dashboard + 2. Click the **Environment** tab + 3. Find `RIVET__AUTH__ADMIN_TOKEN` and click the eye icon to reveal the value + 4. Copy this token — you'll need it to access the dashboard + + + +### Access the Rivet Dashboard + + Open your service URL in a browser: + + ``` + https://rivet-engine-xxxx.onrender.com/ui/ + ``` + + Replace `rivet-engine-xxxx` with your actual service name from the Render Dashboard. + + Enter the admin token from the previous step to log in. + + +## WebSocket Timeouts + +Rivet uses long-lived WebSocket connections for both client traffic (browsers, SDKs) and envoy traffic (actor hosts connecting back to the engine). Render's HTTP proxy supports WebSockets, but its default idle timeout will close connections that have no traffic for too long. + +If you front the engine with your own reverse proxy (NGINX, Caddy, etc.) on Render, raise its idle / read timeout to at least 1 hour (`3600` seconds). The same guidance applies to the Render service hosting your RivetKit app, since envoys connect to it over WebSocket. + +## Connecting Your Application + +To connect a RivetKit application to your self-hosted engine, set these environment variables in your app: + +```bash +RIVET_ENDPOINT=https://:@rivet-engine-xxxx.onrender.com +RIVET_PUBLIC_ENDPOINT=https://@rivet-engine-xxxx.onrender.com +``` + +See the [Connect guide](/docs/deploy/custom) for more details on connecting your application. + +## Next Steps + +- Review the [Production Checklist](/docs/self-hosting/production-checklist) before going live +- See [Configuration](/docs/self-hosting/configuration) for all options + +_Source doc path: /docs/self-hosting/render_ diff --git a/.agents/skills/live-cursors/reference/self-hosting/tls.md b/.agents/skills/live-cursors/reference/self-hosting/tls.md new file mode 100644 index 0000000..63573d2 --- /dev/null +++ b/.agents/skills/live-cursors/reference/self-hosting/tls.md @@ -0,0 +1,22 @@ +# TLS & Certificates + +> Source: `src/content/docs/self-hosting/tls.mdx` +> Canonical URL: https://rivet.dev/docs/self-hosting/tls +> Description: How Rivet validates TLS root certificates. + +--- +Public CAs (Let's Encrypt, AWS ACM, and so on) work out of the box. You only need to read this page if you're running behind a corporate or private CA. + +Rivet reads the operating system trust store (`/etc/ssl/certs` on Linux, Keychain on macOS, Schannel on Windows) for all outbound HTTPS. Operator-installed corporate CAs live there and are honored automatically. + +To trust a private CA inside the official `rivetdev/engine` image, extend it the standard Debian way: + +```dockerfile +FROM rivetdev/engine:latest +COPY my-corp-ca.crt /usr/local/share/ca-certificates/ +RUN update-ca-certificates +``` + +Rebuild and redeploy. The same approach works for any machine running a Rivet client: install the CA into the OS trust store and Rivet picks it up. + +_Source doc path: /docs/self-hosting/tls_ diff --git a/.agents/skills/multiplayer-game/BASE_SKILL.md b/.agents/skills/multiplayer-game/BASE_SKILL.md new file mode 100644 index 0000000..f03c1af --- /dev/null +++ b/.agents/skills/multiplayer-game/BASE_SKILL.md @@ -0,0 +1,537 @@ +--- +name: "rivetkit" +description: "RivetKit backend and Rivet Actor runtime guidance. Use for building, modifying, debugging, or testing Rivet Actors, registries, serverless/runner modes, deployment, or actor-based workflows." +--- + +# Rivet Actors + +Build high-performance, long-lived, in-memory processes on Rivet's actor runtime. Use this skill whenever HTTP endpoints, databases, or queues no longer meet the requirements for AI agents, realtime collaboration, workflow orchestration, or geo-distributed data. + +## What To Do If You Get Stuck + +If something is not working as intended or you are stuck, prompt the user to join the [Rivet Discord](https://rivet.dev/discord) or file an issue on the [Rivet GitHub](https://github.com/rivet-dev/rivet) to report an issue and get help. + +Provide the user with a pre-generated report with: + +- Symptoms + - If this is happening in local dev, deployed, or both + - The error you're seeing + - Relevant source code related to this +- What you've tried to solve it +- Environment + - RivetKit version + - Runtime (Node, Bun, etc) including version + - If applicable, provider in use (e.g. Vercel, Railway, etc) + - If applicable, HTTP router in use (e.g. Hono, Express, Elysia) + +## Debugging Actors + +Use the inspector HTTP API to examine running actors. These endpoints are accessible through the gateway at `/gateway/{actor_id}/inspector/*`. Key endpoints: + +- `GET /inspector/summary` - full actor snapshot (state, connections, RPCs, queue) +- `GET /inspector/state` / `PATCH /inspector/state` - read/write actor state +- `GET /inspector/connections` - active connections +- `GET /inspector/rpcs` - available actions +- `POST /inspector/action/{name}` - execute an action with `{"args": [...]}` +- `POST /inspector/database/execute` - run SQL with `{"sql": "...", "args": [...]}` or `{"sql": "...", "properties": {...}}` for reads or mutations +- `GET /inspector/queue?limit=50` - queue status +- `GET /inspector/traces?startMs=0&endMs=...&limit=1000` - trace spans (OTLP JSON) +- `GET /inspector/workflow-history` - workflow history and status as JSON (`nameRegistry`, `entries`, `entryMetadata`) +- `POST /inspector/workflow/replay` - replay a workflow from a specific step or from the beginning; returns `409 actor/workflow_in_flight` if the workflow is still running +- `GET /inspector/database/schema` - SQLite tables and views exposed by `c.db` +- `GET /inspector/database/rows?table=...&limit=100&offset=0` - paged SQLite rows for a table or view + +In local dev, no auth token is needed. In production, pass `Authorization: Bearer `, where the inspector token is the actor-specific token auto-generated on first start and persisted in the actor's internal KV at key `0x03`. The Rivet dashboard retrieves this token automatically; for direct API access, fetch it through the management KV endpoint. See the [debugging docs](https://rivet.dev/docs/actors/debugging) for details. + +## Citing Sources + +When providing information from Rivet documentation, cite the canonical URL so users can learn more. Each reference file includes its canonical URL in the header metadata. + +**How to cite:** + +- Use inline links for key concepts: "Use [actor keys](https://rivet.dev/docs/actors/keys) to uniquely identify instances." +- Add a "Learn more" link after explanations for complex topics + +**Finding canonical URLs:** + +The Reference Map below links to reference files. Each file's header contains: + +``` +> Canonical URL: https://rivet.dev/docs/actors/actions +``` + +Use that canonical URL when citing, not the reference file path. + +**Examples:** + +- Actions → `https://rivet.dev/docs/actors/actions` +- React client → `https://rivet.dev/docs/clients/react` +- Self-hosting on Kubernetes → `https://rivet.dev/docs/self-hosting/kubernetes` + +## Version Check + +Before starting any work, check if the user's project is on the latest version of RivetKit (latest: 2.3.5-rc.1). Look at the `rivetkit` version in the user's `package.json` (check both `dependencies` and `devDependencies`). If the installed version is older than 2.3.5-rc.1, inform the user and suggest upgrading: + +```bash +npm install rivetkit@2.3.5-rc.1 +``` + +If the user also uses `@rivetkit/react`, `@rivetkit/next-js`, or other `@rivetkit/*` client packages, suggest upgrading those too. Outdated versions may have known bugs or missing features that cause issues. + +## First Steps + +1. Install RivetKit (latest: 2.3.5-rc.1) + ```bash + npm install rivetkit@2.3.5-rc.1 + ``` +2. Define a registry with `setup({ use: { /* actors */ } })`. +3. Call `registry.start()` to start the server. For custom HTTP server integration, use `registry.handler()` with a router like Hono. For serverless deployments, use `registry.serve()`. For runner-only mode, use `registry.startEnvoy()`. +4. Verify `/api/rivet/metadata` returns 200 before deploying. +5. Configure Rivet Cloud or self-hosted engine + - You must configure versioning for production builds. This is not needed for local development. See [Versions & Upgrades](https://rivet.dev/docs/actors/versions). +6. Integrate clients (see client guides below for JavaScript, React, or Swift) +7. Prompt the user if they want to deploy. If so, go to Deploying Rivet Backends. + +For more information, read the quickstart guide relevant to the user's project. + +## Project Setup + +### .gitignore + +Every RivetKit project should have a `.gitignore`. Include at minimum: + +``` +node_modules/ +dist/ +.env +``` + +### .dockerignore + +Every project with a Dockerfile should have a `.dockerignore` to keep the image small and avoid leaking secrets: + +``` +node_modules/ +dist/ +.env +.git/ +``` + +### Dockerfile + +Use this as a base Dockerfile for deploying a RivetKit project. The `RIVET_RUNNER_VERSION` build arg is only needed when self-hosting or using a custom runner (not needed for Rivet Compute). It lets Rivet track which version of the actor is running and drain old actors on deploy. See https://rivet.dev/docs/actors/versions for details. + +```dockerfile +FROM node:24-alpine + +ARG RIVET_RUNNER_VERSION +ENV RIVET_RUNNER_VERSION=$RIVET_RUNNER_VERSION + +WORKDIR /app + +COPY package.json package-lock.json ./ +RUN npm ci + +COPY . . +RUN npm run build --if-present + +CMD ["node", "dist/index.js"] +``` + +Build with: + +```bash +docker build --build-arg RIVET_RUNNER_VERSION=$(date +%s) . +``` + +Adjust the `CMD` to match the project's entry point. If the project uses a different output directory or start command, update accordingly. + +## Error Handling Policy + +- Prefer fail-fast behavior by default. +- Avoid `try/catch` unless it is required for a real recovery path, cleanup boundary, or to add actionable context. +- Never swallow errors. If you add a `catch`, you must handle the error explicitly, at minimum by logging it. +- When you cannot recover, log context and rethrow. + +## State vs Vars: Persistence Rules + +**`c.vars` is ephemeral.** Data in `c.vars` is lost on every restart, crash, upgrade, or sleep/wake cycle. Only use `c.vars` for non-serializable objects (e.g., physics engines, WebSocket references, event emitters, caches) or truly transient runtime data (e.g., current input direction that doesn't matter after disconnect). + +**Persistent storage options.** Any data that must survive restarts belongs in one of these, NOT in `c.vars`: + +- **`c.state`** — CBOR-serializable data for small, bounded datasets. Ideal for configuration, counters, small player lists, phase flags, etc. Keep under 128 KB. Do not store unbounded or growing data here (e.g., chat logs, event histories, spawned entity lists that grow without limit). State is read/written as a single blob on every persistence cycle. +- **`c.kv`** — Key-value store for unbounded data. This is what `c.state` uses under the hood. Supports binary values. Use for larger or variable-size data like user inventories, world chunks, file blobs, or any collection that may grow over time. Keys are scoped to the actor instance. +- **`c.db`** — SQLite database for structured or complex data. Use when you need queries, indexes, joins, aggregations, or relational modeling. Ideal for leaderboards, match histories, player pools, or any data that benefits from SQL. + +**Common mistake:** Storing meaningful game/application data in `c.vars` instead of persisting it. For example, if users can spawn objects in a physics simulation, the spawn definitions (position, size, type) must be persisted in `c.state` (or `c.kv` if unbounded), even though the physics engine handles (non-serializable) live in `c.vars`. On restart, `run()` should recreate the runtime objects from the persisted data. + +## Deploying Rivet Backends + +Assume the user is deploying to Rivet Cloud, unless otherwise specified. If user is self-hosting, read the self-hosting guides below. + +1. Verify that Rivet Actors are working in local dev +2. Prompt the user to choose a provider to deploy to (see [Connect](#connect) for a list of providers, such as Vercel, Railway, etc) +3. Follow the deploy guide for that given provider. You will need to instruct the user when you need manual intervention. + +## API Reference + +The RivetKit OpenAPI specification is available in the skill directory at `openapi.json`. This file documents all HTTP endpoints for managing actors. + +## Misc Notes + +- The Rivet domain is rivet.dev, not rivet.gg + +## TypeScript Caveat: Actor Client Inference + +- In multi-file TypeScript projects, bidirectional actor calls can create a circular type dependency when both actors use `c.client()`. +- Symptoms usually include `c.state` becoming `unknown`, actor methods becoming possibly `undefined`, or `TS2322` / `TS2722` errors after the first cross-actor call. +- If an action returns the result of another actor call, prefer an explicit return type annotation on that action instead of relying on inference through `c.client()`. +- If explicit return types are not enough, use a narrower client or registry type for only the actors that action needs. +- As a last resort, pass `unknown` for the registry type and be explicit that this gives up type safety at that call site. + +## Features + +- **Long-Lived, Stateful Compute**: Each unit of compute is like a tiny server that remembers things between requests – no need to re-fetch data from a database or worry about timeouts. Like AWS Lambda, but with memory and no timeouts. +- **Blazing-Fast Reads & Writes**: State is stored on the same machine as your compute, so reads and writes are ultra-fast. No database round trips, no latency spikes. State is persisted to Rivet for long term storage, so it survives server restarts. +- **Realtime**: Update state and broadcast changes in realtime with WebSockets. No external pub/sub systems, no polling – just built-in low-latency events. +- **Infinitely Scalable**: Automatically scale from zero to millions of concurrent actors. Pay only for what you use with instant scaling and no cold starts. +- **Fault Tolerant**: Built-in error handling and recovery. Actors automatically restart on failure while preserving state integrity and continuing operations. + +## When to Use Rivet Actors + +- **AI agents & sandboxes**: multi-step toolchains, conversation memory, sandbox orchestration. +- **Multiplayer or collaborative apps**: CRDT docs, shared cursors, realtime dashboards, chat. +- **Workflow automation**: background jobs, cron, rate limiters, durable queues, backpressure control. +- **Data-intensive backends**: geo-distributed or per-tenant databases, in-memory caches, sharded SQL. +- **Networking workloads**: WebSocket servers, custom protocols, local-first sync, edge fanout. + +## Minimal Project + +### Backend + +**index.ts** + +### Client Docs + +Use the client SDK that matches your app: + +- [JavaScript Client](/docs/clients/javascript) +- [React Client](/docs/clients/react) +- [Swift Client](/docs/clients/swift) + +## Actor Quick Reference + +### In-Memory State + +Persistent data that survives restarts, crashes, and deployments. State is persisted on Rivet Cloud or Rivet self-hosted, so it survives restarts if the current process crashes or exits. + +### Static Initial State + +### Dynamic Initial State + +[Documentation](/docs/actors/state) + +### Keys + +Keys uniquely identify actor instances. Use compound keys (arrays) for hierarchical addressing: + +Don't build keys with string interpolation like `"org:${userId}"` when `userId` contains user data. Use arrays instead to prevent key injection attacks. + +[Documentation](/docs/actors/keys) + +### Input + +Pass initialization data when creating actors. Input is only available in `createState` and `onCreate`, so store it in state if you need it later. + +[Documentation](/docs/actors/input) + +### Temporary Variables + +Temporary data that doesn't survive restarts. Use for non-serializable objects (event emitters, connections, etc). + +### Static Initial Vars + +### Dynamic Initial Vars + +[Documentation](/docs/actors/state) + +### Actions + +Actions are the primary way clients and other actors communicate with an actor. + +[Documentation](/docs/actors/actions) + +### Events & Broadcasts + +Events enable real-time communication from actors to connected clients. + +[Documentation](/docs/actors/events) + +### Connections + +Access the current connection via `c.conn` or all connected clients via `c.conns`. Use `c.conn.id` or `c.conn.state` to securely identify who is calling an action. `c.conn` is only available for actions invoked through a connected client; stateless actor-handle calls run without a connection, so guard against that. Connection state is initialized via `connState` or `createConnState`, which receives parameters passed by the client on connect. + +### Static Connection Initial State + +### Dynamic Connection Initial State + +[Documentation](/docs/actors/connections) + +### Queues + +Use queues to process durable messages in order inside a `run` loop. + +[Documentation](/docs/actors/queues) + +### Workflows + +Use workflows when your `run` logic needs durable, replayable multi-step execution. + +[Documentation](/docs/actors/workflows) + +### Actor-to-Actor Communication + +Actors can call other actors using `c.client()`. + +[Documentation](/docs/actors/communicating-between-actors) + +### Schedule & Cron + +Run one-shot actions after a delay or at a specific time. Use named cron jobs for calendar schedules and fixed intervals. Both persist across sleep, restarts, upgrades, and crashes. + +[Documentation](/docs/actors/schedule) + +### Destroying Actors + +Permanently delete an actor and its state using `c.destroy()`. + +[Documentation](/docs/actors/destroy) + +### Lifecycle Hooks + +Actors support hooks for initialization, background processing, connections, networking, and state changes. Use `run` for long-lived background loops, and use `c.aborted` or `c.abortSignal` for graceful shutdown. + +[Documentation](/docs/actors/lifecycle) + +### Context Types + +When writing helper functions outside the actor definition, use `*ContextOf` to extract the correct context type. Helpers like `ActionContextOf`, `CreateContextOf`, `ConnContextOf`, and `ConnInitContextOf` are exported from `"rivetkit"`. Do not manually define your own context interface. Always derive it from the actor definition. + +[Documentation](/docs/actors/types) + +### Errors + +Use `UserError` to throw errors that are safely returned to clients. Pass `metadata` to include structured data. Other errors are converted to generic "internal error" for security. + +### Actor + +### Client + +[Documentation](/docs/actors/errors) + +### Low-Level HTTP & WebSocket Handlers + +For custom protocols or integrating libraries that need direct access to HTTP `Request`/`Response` or WebSocket connections, use `onRequest` and `onWebSocket`. + +[HTTP Handler Documentation](/docs/actors/request-handler) · [WebSocket Handler Documentation](/docs/actors/websocket-handler) + +### Icons & Names + +Customize how actors appear in the UI with display names and icons. It's recommended to always provide a name and icon to actors in order to make them easier to distinguish in the dashboard. + +```typescript +import { actor } from "rivetkit"; + +const chatRoom = actor({ + options: { + name: "Chat Room", + icon: "💬", // or FontAwesome: "comments", "chart-line", etc. + }, + // ... +}); +``` + +[Documentation](/docs/actors/appearance) + +## Client Documentation + +Find the full client guides here: + +- [JavaScript Client](/docs/clients/javascript) +- [React Client](/docs/clients/react) +- [Swift Client](/docs/clients/swift) + +## Common Patterns + +Actors scale naturally through isolated state and message-passing. Structure your applications with these patterns: + +[Documentation](/docs/actors/design-patterns) + +### Actor Per Entity + +Create one actor per user, document, or room. Use compound keys to scope entities: + +### Coordinator & Data Actors + +**Data actors** handle core logic (chat rooms, game sessions, user data). **Coordinator actors** track and manage collections of data actors—think of them as an index. + +### Run Loop + +Use a `run` loop for continuous background work inside an actor. Process queue messages in order, run logic on intervals, stream AI responses, or coordinate long-running tasks. + +### Workflow Loop + +Use this pattern for long-lived, durable workflows that initialize resources, process commands in a loop, then clean up. + +[Documentation](/docs/actors/workflows) + +### Actions vs Queues + +- **Actions** are not durable. Use them for realtime reads, ephemeral data, and low-latency communication like player input. +- **Queues** are durable. Use them to serialize mutations through the run loop, avoiding race conditions with SQLite and other local state. Callers can still wait for a response from queued work. + +### Authentication, Security, & CORS + +- Validate credentials in `onBeforeConnect` or `createConnState` and throw an error to reject unauthorized connections. +- Use `c.conn.state` to securely identify users in actions rather than trusting action parameters. +- For cross-origin access, validate the request origin in `onBeforeConnect`. + +[Authentication Documentation](/docs/actors/authentication) · [CORS Documentation](/docs/general/cors) + +### Versions & Upgrades + +When deploying new code, set a version number so Rivet can route new actors to the latest runner and optionally drain old ones. Use a build timestamp, git commit count, or CI build number as the version. It is very important to [configure versioning](/docs/actors/versions) before deploying to production. Without versioning, actors can regress by running on older runner versions, and existing actors will never be forced to migrate to new runners. They will continue running indefinitely on the old runners until they exit. + +[Documentation](/docs/actors/versions) + +### Anti-Patterns + +#### Never build a "god" actor + +Do not put all your logic in a single actor. A god actor serializes every operation through one bottleneck, kills parallelism, and makes the entire system fail as a unit. Split into focused actors per entity. + +#### Never create an actor per request + +Actors are long-lived and maintain state across requests. Creating a new actor for every incoming request throws away the core benefit of the model and wastes resources on actor creation and teardown. Use actors for persistent entities and regular functions for stateless work. + +## Reference Map + +### Actors + +- [Access Control](reference/actors/access-control.md) +- [Actions](reference/actors/actions.md) +- [Actor Keys](reference/actors/keys.md) +- [Actor Runtime Socket](reference/actors/actor-runtime-socket.md) +- [Actor Statuses](reference/actors/statuses.md) +- [Authentication](reference/actors/authentication.md) +- [Cloudflare Workers Quickstart](reference/actors/quickstart/cloudflare.md) +- [Communicating Between Actors](reference/actors/communicating-between-actors.md) +- [Connections](reference/actors/connections.md) +- [Custom Inspector Tabs](reference/actors/inspector-tabs.md) +- [Debugging](reference/actors/debugging.md) +- [Design Patterns](reference/actors/design-patterns.md) +- [Destroying Actors](reference/actors/destroy.md) +- [Effect.ts Quickstart (Beta)](reference/actors/quickstart/effect.md) +- [Errors](reference/actors/errors.md) +- [Fetch and WebSocket Handler](reference/actors/fetch-and-websocket-handler.md) +- [Helper Types](reference/actors/helper-types.md) +- [Icons & Names](reference/actors/appearance.md) +- [In-Memory State](reference/actors/state.md) +- [Input Parameters](reference/actors/input.md) +- [Lifecycle](reference/actors/lifecycle.md) +- [Limits](reference/actors/limits.md) +- [Low-Level HTTP Request Handler](reference/actors/request-handler.md) +- [Low-Level KV Storage](reference/actors/kv.md) +- [Low-Level WebSocket Handler](reference/actors/websocket-handler.md) +- [Metadata](reference/actors/metadata.md) +- [Next.js Quickstart](reference/actors/quickstart/next-js.md) +- [Node.js & Bun Quickstart](reference/actors/quickstart/backend.md) +- [Queues & Run Loops](reference/actors/queues.md) +- [React Quickstart](reference/actors/quickstart/react.md) +- [Realtime](reference/actors/events.md) +- [Rust Quickstart (Beta)](reference/actors/quickstart/rust.md) +- [Scaling & Concurrency](reference/actors/scaling.md) +- [Schedule & Cron](reference/actors/schedule.md) +- [Sharing and Joining State](reference/actors/sharing-and-joining-state.md) +- [SQLite](reference/actors/sqlite.md) +- [SQLite + Drizzle](reference/actors/sqlite-drizzle.md) +- [Supabase Functions Quickstart](reference/actors/quickstart/supabase.md) +- [Testing](reference/actors/testing.md) +- [Troubleshooting](reference/actors/troubleshooting.md) +- [Types](reference/actors/types.md) +- [Vanilla HTTP API](reference/actors/http-api.md) +- [Versions & Upgrades](reference/actors/versions.md) +- [Workflows](reference/actors/workflows.md) + +### Cli + +- [CLI](reference/cli.md) + +### Clients + +- [Node.js & Bun](reference/clients/javascript.md) +- [React](reference/clients/react.md) +- [Rust (Beta)](reference/clients/rust.md) +- [Swift](reference/clients/swift.md) +- [SwiftUI](reference/clients/swiftui.md) + +### Cookbook + +- [AI Agent](reference/cookbook/ai-agent.md) +- [Chat Room](reference/cookbook/chat-room.md) +- [Collaborative Text Editor](reference/cookbook/collaborative-text-editor.md) +- [Cron Jobs and Scheduled Tasks](reference/cookbook/cron-jobs.md) +- [Database per Tenant](reference/cookbook/per-tenant-database.md) +- [Deploying Rivet in a VPC or Air-Gapped Network](reference/cookbook/vpc-air-gapped.md) +- [Live Cursors and Presence](reference/cookbook/live-cursors.md) +- [Multiplayer Game](reference/cookbook/multiplayer-game.md) + +### Deploy + +- [Container Runner](reference/deploy/container-runner.md) +- [Deploy To Amazon Web Services Lambda](reference/deploy/aws-lambda.md) +- [Deploying to AWS ECS](reference/deploy/aws-ecs.md) +- [Deploying to Cloudflare Workers](reference/deploy/cloudflare.md) +- [Deploying to Freestyle](reference/deploy/freestyle.md) +- [Deploying to Google Cloud Run](reference/deploy/gcp-cloud-run.md) +- [Deploying to Hetzner](reference/deploy/hetzner.md) +- [Deploying to Kubernetes](reference/deploy/kubernetes.md) +- [Deploying to Railway](reference/deploy/railway.md) +- [Deploying to Rivet Compute](reference/deploy/rivet-compute.md) +- [Deploying to Supabase Functions](reference/deploy/supabase.md) +- [Deploying to Vercel](reference/deploy/vercel.md) +- [Deploying to VMs & Bare Metal](reference/deploy/vm-and-bare-metal.md) + +### General + +- [Actor Configuration](reference/general/actor-configuration.md) +- [Architecture](reference/general/architecture.md) +- [Cross-Origin Resource Sharing](reference/general/cors.md) +- [Documentation for LLMs & AI](reference/general/docs-for-llms.md) +- [Edge Networking](reference/general/edge.md) +- [Endpoints](reference/general/endpoints.md) +- [Environment Variables](reference/general/environment-variables.md) +- [HTTP Server](reference/general/http-server.md) +- [Logging](reference/general/logging.md) +- [Pool Configuration](reference/general/pool-configuration.md) +- [Production Checklist](reference/general/production-checklist.md) +- [Registry Configuration](reference/general/registry-configuration.md) +- [Runtime Modes](reference/general/runtime-modes.md) +- [WASM vs Native SDK](reference/general/wasm-vs-native-sdk.md) + +### Self Hosting + +- [Configuration](reference/self-hosting/configuration.md) +- [Docker Compose](reference/self-hosting/docker-compose.md) +- [Docker Container](reference/self-hosting/docker-container.md) +- [File System](reference/self-hosting/filesystem.md) +- [FoundationDB (Enterprise)](reference/self-hosting/foundationdb.md) +- [Installing Rivet Engine](reference/self-hosting/install.md) +- [Kubernetes](reference/self-hosting/kubernetes.md) +- [Multi-Region](reference/self-hosting/multi-region.md) +- [PostgreSQL](reference/self-hosting/postgres.md) +- [Production Checklist](reference/self-hosting/production-checklist.md) +- [Railway Deployment](reference/self-hosting/railway.md) +- [Render Deployment](reference/self-hosting/render.md) +- [TLS & Certificates](reference/self-hosting/tls.md) + diff --git a/.agents/skills/multiplayer-game/SKILL.md b/.agents/skills/multiplayer-game/SKILL.md new file mode 100644 index 0000000..f000eae --- /dev/null +++ b/.agents/skills/multiplayer-game/SKILL.md @@ -0,0 +1,842 @@ +--- +name: "multiplayer-game" +description: "Pragmatic patterns for building multiplayer games: matchmaking, tick loops, realtime state, interest management, and validation." +--- + +# Multiplayer Game + +**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.** + +## Working Examples + +If you need a reference implementation, read the raw working example code in these templates: + +- [multiplayer-game-patterns](https://github.com/rivet-dev/rivet/tree/main/examples/multiplayer-game-patterns) + + +Patterns for building multiplayer games with RivetKit, intended as a practical checklist you can adapt per genre. + +## Starter Code + +Start with one of the working examples on [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/multiplayer-game-patterns/src/actors/) and adapt it to your game. Do not start from scratch for matchmaking and lifecycle flows. + +| Game Classification | Starter Code | Common Examples | +| --- | --- | --- | +| Battle Royale | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/multiplayer-game-patterns/src/actors/battle-royale/) | Fortnite, Apex Legends, PUBG, Warzone | +| Arena | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/multiplayer-game-patterns/src/actors/arena/) | Call of Duty TDM/FFA, Halo Slayer, Counter-Strike casual, VALORANT unrated, Overwatch Quick Play, Rocket League | +| IO Style | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/multiplayer-game-patterns/src/actors/io-style/) | Agar.io, Slither.io, surviv.io | +| Open World | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/multiplayer-game-patterns/src/actors/open-world/) | Minecraft survival servers, Rust-like worlds, MMO zone/chunk worlds | +| Party | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/multiplayer-game-patterns/src/actors/party/) | Fall Guys private lobbies, custom game rooms, social party sessions | +| Physics 2D | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/multiplayer-game-patterns/src/actors/physics-2d/) | Top-down physics brawlers, 2D arena games, platform fighters | +| Physics 3D | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/multiplayer-game-patterns/src/actors/physics-3d/) | Physics sandbox sessions, 3D arena games, movement playgrounds | +| Ranked | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/multiplayer-game-patterns/src/actors/ranked/) | Chess ladders, competitive card games, duel arena ranked queues | +| Turn-Based | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/multiplayer-game-patterns/src/actors/turn-based/) | Chess correspondence, Words With Friends, async board games | +| Idle | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/multiplayer-game-patterns/src/actors/idle/) | Cookie Clicker, Idle Miner Tycoon, Adventure Capitalist | + +## Server Simulation + +### Game Loop And Tick Rates + +| Pattern | Use When | Implementation Guidance | +| --- | --- | --- | +| Fixed realtime loop | Battle Royale, Arena, IO Style, Open World, Ranked | Run in `run` with `sleep(tickMs)` and exit on `c.aborted`. | +| Action-driven updates | Party, Turn-Based | Mutate and broadcast only on actions/events rather than scheduled ticks. | +| Coarse offline progression | Any mode with idle progression | Use `c.schedule.after(...)` with coarse windows (for example 5 to 15 minutes) and apply catch-up from elapsed wall clock time. | + +### Physics + +Start with custom kinematic logic for simple games. Switch to a full physics engine when you need joints, stacked bodies, high collision density, or complex shapes (rotated polygons, capsules, convex hulls, triangle meshes). + +Pick one engine per simulation. Keep frontend-only libs out of backend simulation paths and treat server state as authoritative. + +| Dimension | Primary Engine | Fallback Engines | Example Code | +| --- | --- | --- | --- | +| 2D | `@dimforge/rapier2d` | `planck-js`, `matter-js` | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/multiplayer-game-patterns/src/actors/physics-2d/) | +| 3D | `@dimforge/rapier3d` | `cannon-es`, `ammo.js` | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/multiplayer-game-patterns/src/actors/physics-3d/) | + +### Spatial Indexing + +For non-physics spatial queries, use a dedicated index instead of naive `O(n^2)` checks: + +| Index Type | Recommendation | +| --- | --- | +| AABB index | For AOI, visibility, and non-collider entities, use `rbush` for dynamic sets or `flatbush` for static-ish sets. | +| Point index | For nearest-neighbor or within-radius queries, use `d3-quadtree`. | + +## Networking & State Sync + +### Netcode + +| Model | When To Use | Implementation | +| --- | --- | --- | +| Hybrid (client movement, server combat) | Shooters, action sports, ranked duels | Client owns movement and sends capped-rate position updates. Server validates for anti-cheat. Combat (projectiles, hits, damage) is fully server-authoritative. | +| Server-authoritative with interpolation | IO Style, persistent worlds | Client sends input commands. Server simulates on fixed ticks and publishes authoritative snapshots. Client interpolates between snapshots. | +| Server-authoritative (basic logic) | Turn-based, event-driven | Server validates and applies discrete actions (turns, phase transitions, votes). Client displays confirmed state. | + +### Realtime Data Model + +- **Snapshots and diffs**: Publish state as events. Send a full snapshot on join/resync, then per-tick diffs for regular updates. +- **Batch per tick**: Keep events small and typed. Batch high-frequency updates per tick. +- **Avoid UI framework state for game updates**: Use `requestAnimationFrame` or a Canvas/Three.js loop for simulation, not React state. Reserve UI framework state for menus, HUD, and forms. +- **Broadcast vs per-connection**: Use `c.broadcast(...)` for shared updates and `conn.send(...)` for private/per-player data. + +### Shared Simulation Logic + +Shared simulation logic runs on both the client and the server. For example, an `applyInput(state, input, dt)` function that integrates velocity and clamps to world bounds can run on the client for prediction and on the server for validation. + +- **Hybrid modes**: Client runs shared movement as primary authority, server runs it for anti-cheat validation. +- **Server-authoritative modes**: Client uses shared logic for interpolation and prediction only. +- **Keep it pure**: Movement integration, input transforms, collision helpers, and constants only. +- **Put shared code in `src/shared/`**: Keep deterministic helpers in `src/shared/sim/*` with no side effects. + +### Interest Management + +Control what each client receives to reduce bandwidth and prevent information leaks. + +#### Per-Player Replication Filters + +- **Filter by relevance**: Send each client only state relevant to that player (proximity, line-of-sight, team, or game phase). +- **Shooters and action games**: Limit replication by proximity and optional field-of-view checks. +- **Server-side only**: Clients should never receive data they should not see. + +#### Sharded Worlds + +- **Partition large worlds**: Use chunk actors keyed by `worldId:chunkX:chunkY`. +- **Subscribe to nearby chunks**: Clients connect only to nearby partitions (for example a 3x3 chunk window). +- **Use sparingly**: Only when the world is large and state-heavy (sandbox builders, MMOs), not as a default for small matches. + +## Backend Infrastructure + +### Persistence + +- **In-memory state**: Best for realtime game state that changes every tick (player positions, inputs, match phase, scores). +- **SQLite (`rivetkit/db`)**: Better for large or table-like state that needs queries, indexes, or long-term persistence (tiles, inventory, matchmaking pools). Serialize DB work through a queue since multiple actions can hit the same actor concurrently. + +### Matchmaking Patterns + +Common building blocks used across the architecture patterns below. + +#### Actor Topology + +| Primitive | Use When | Typical Ownership | +| --- | --- | --- | +| `matchmaker["main"]` + `match[matchId]` | Session-based multiplayer (battle royale, arena, ranked, party, turn-based) | Matchmaker owns discovery/assignment. Match owns lifecycle and gameplay state. | +| `chunk[worldId,chunkX,chunkY]` | Large continuous worlds that need sharding | Each chunk owns local players, chunk state, and local simulation. | +| `world[playerId]` | Per-player progression loops (idle/solo world state) | Per-player resources, buildings, timers, and progression. | +| `player[username]` | Canonical profile/rating reused across matches | Durable player stats (for example rating and win/loss). | +| `leaderboard["main"]` | Shared rankings across many matches/players | Global ordered score rows and top lists. | + +#### Queueing Strategy + +- Multiple players can hit the matchmaker at the same time, so actions like find/create, queue/unqueue, and close need to be serialized through actor queues to avoid races. +- Match-local actions (gameplay, scoring) do not need queueing unless they write back to the matchmaker. + +## Security And Anti-Cheat + +Start with this baseline, then harden further for competitive or high-risk environments. + +### Baseline Checklist + +- **Identity**: Use `c.conn.id` as the authoritative transport identity. Treat `playerId`/`username` in params as untrusted input and bind through server-issued assignment/join tickets. +- **Authorization**: Validate the caller is allowed to mutate the target entity (room membership, turn ownership, host-only actions). +- **Input validation**: Clamp sizes/lengths, validate enums, and validate usernames (length, allowed chars, avoid unbounded Unicode). +- **Rate limiting**: Per-connection rate limits for spammy actions (chat, join/leave, fire, movement updates). +- **State integrity**: Server recomputes derived state (scores, win conditions, placements). Never allow client-authoritative changes to inventory/currency/leaderboard totals. + +### Movement Validation + +For any mode with client-authoritative movement (hybrid flows), clients may send position/rotation updates for smoothness, but the server must: + +- Enforce max delta per update (speed cap) based on elapsed time. +- Reject or clamp teleports. +- Enforce world bounds (and basic collision if applicable). +- Rate limit update frequency (for example 20Hz max). + +## Architecture Patterns + +Each game type below starts with a quick summary table, then details actors and lifecycle. + +### Battle Royale + +| Topic | Summary | +| --- | --- | +| Matchmaking | Immediate routing to the fullest non-started lobby (oldest tie-break); players wait in lobby until capacity, then the match starts. | +| Netcode | Hybrid. Client owns movement, camera, and local prediction. Server owns zone state, projectiles, hit resolution, eliminations, loot, and final placement. | +| Tick Rate | 10 ticks/sec (`100ms`) with a fixed loop for zone progression and lifecycle checks. | +| Physics | Client owns movement with server anti-cheat validation; projectiles, hits, and damage are server-authoritative. Use `@dimforge/rapier3d` for 3D or `@dimforge/rapier2d` for top-down 2D. | + +**Actors** + +- **Key**: `matchmaker["main"]` +- **Responsibility**: Finds or creates lobbies, tracks pending reservations, and maintains occupancy. +- **Actions** + - `findMatch` + - `pendingPlayerConnected` + - `updateMatch` + - `closeMatch` +- **Queues** + - `findMatch` + - `pendingPlayerConnected` + - `updateMatch` + - `closeMatch` +- **State** + - SQLite + - `matches` + - `pending_players` + - `player_count` includes connected and pending players + +- **Key**: `match[matchId]` +- **Responsibility**: Runs lobby/live/finished phases, owns player state, zone progression, and eliminations. +- **Actions** + - `connect` + - Movement and combat actions +- **Queues** + - None +- **State** + - JSON + - `phase` + - `players` + - `zone` + - `eliminations` + - `snapshot data` + +**Lifecycle** + +```mermaid +sequenceDiagram + participant C as Client + participant MM as matchmaker + participant M as match + + C->>MM: findMatch() + alt no open lobby + MM->>M: create(matchId) + end + MM-->>C: {matchId, playerId} + C->>M: connect(playerId) + M->>MM: pendingPlayerConnected(matchId, playerId) + MM-->>M: accepted + Note over M: lobby countdown -> live + M-->>C: snapshot + shoot events + M->>MM: closeMatch(matchId) +``` + +### Arena + +| Topic | Summary | +| --- | --- | +| Matchmaking | Mode-based fixed-capacity queues (`duo`, `squad`, `ffa`) that build only full matches and pre-assign teams (except FFA). | +| Netcode | Hybrid. Client owns movement plus prediction and smoothing. Server owns team or FFA assignment, projectiles, hit resolution, phase transitions, and scoring. | +| Tick Rate | 20 ticks/sec (`50ms`) with a tighter loop for live team and FFA snapshots. | +| Physics | Medium to high intensity; client movement with server validation and server-authoritative combat/entities. | + +**Actors** + +- **Key**: `matchmaker["main"]` +- **Responsibility**: Runs mode queues, builds full matches, assigns teams, and publishes assignments. +- **Actions** + - `queueForMatch` + - `unqueueForMatch` + - `matchCompleted` +- **Queues** + - `queueForMatch` + - `unqueueForMatch` + - `matchCompleted` +- **State** + - SQLite + - `player_pool` + - `matches` + - `assignments` keyed by connection and player + +- **Key**: `match[matchId]` +- **Responsibility**: Runs match phases and in-match player/team state for score and win conditions. +- **Actions** + - `connect` + - Gameplay actions +- **Queues** + - None +- **State** + - JSON + - `phase` + - `players` + - `team assignments` + - `score and win state` + +**Lifecycle** + +```mermaid +sequenceDiagram + participant C as Client + participant MM as matchmaker + participant M as match + + C->>MM: queueForMatch(mode) + Note over MM: enqueue in player_pool + Note over MM: fill when capacity reached + MM->>M: create(matchId, assignments) + Note over MM: persist assignments + MM-->>C: assignmentReady + C->>M: connect(playerId) + Note over M: waiting -> live when all players connect + M->>MM: matchCompleted(matchId) +``` + +### IO Style + +| Topic | Summary | +| --- | --- | +| Matchmaking | Open-lobby routing to the fullest room below capacity; room counts are heartbeated and new lobbies are auto-created when needed. | +| Netcode | Server-authoritative with interpolation. Client sends input intents and interpolates. Server owns movement, bounds, room membership, and canonical snapshots. | +| Tick Rate | 10 ticks/sec (`100ms`) with lightweight periodic room snapshots. | +| Physics | Low to medium intensity; server-authoritative kinematic movement, escalating to a physics engine only when collisions get complex. | + +**Actors** + +- **Key**: `matchmaker["main"]` +- **Responsibility**: Routes players into the fullest open lobby and tracks reservations and occupancy. +- **Actions** + - `findLobby` + - `pendingPlayerConnected` + - `updateMatch` + - `closeMatch` +- **Queues** + - `findLobby` + - `pendingPlayerConnected` + - `updateMatch` + - `closeMatch` +- **State** + - SQLite + - `matches` + - `pending_players` + - Occupancy includes pending reservations + +- **Key**: `match[matchId]` +- **Responsibility**: Runs per-match movement simulation and broadcasts snapshots. +- **Actions** + - `connect` + - `setInput` +- **Queues** + - None +- **State** + - JSON + - `players` + - `inputs` + - `movement state` + - `snapshot cache` + +**Lifecycle** + +```mermaid +sequenceDiagram + participant C as Client + participant MM as matchmaker + participant M as match + + C->>MM: findLobby() + alt no open lobby + MM->>M: create(matchId) + end + MM-->>C: {matchId, playerId} + C->>M: connect(playerId) + M->>MM: pendingPlayerConnected(matchId, playerId) + MM-->>M: accepted + Note over M: fixed tick simulation + M-->>C: snapshot events + M->>MM: closeMatch(matchId) +``` + +### Open World + +| Topic | Summary | +| --- | --- | +| Matchmaking | Client-driven chunk routing from world coordinates, with nearby chunk windows preloaded via adjacent chunk connections. | +| Netcode | Hybrid for sandbox (client movement with validation) or server-authoritative for MMO-like flows. Server owns chunk routing, persistence, and canonical world state. | +| Tick Rate | 10 ticks/sec per chunk actor (`100ms`), so load scales with active chunks. | +| Physics | Medium to high at scale; chunk-local simulation can be server-authoritative (MMO-like) or client movement with server validation (sandbox-like). | + +**Actors** + +- **Key**: `chunk[worldId,chunkX,chunkY]` +- **Responsibility**: Owns chunk-local players, blocks, movement tick, and chunk membership. +- **Actions** + - `connect` + - `enterChunk` + - `addPlayer` + - `setInput` + - `leaveChunk` + - `removePlayer` +- **Queues** + - None +- **State** + - JSON + - `connections` + - `players` + - `blocks` scoped to one chunk key + +**Lifecycle** + +```mermaid +sequenceDiagram + participant C as Client + participant CH as chunk + + Note over C: resolve chunk keys from world position + loop each visible chunk + C->>CH: connect(worldId, chunkX, chunkY, playerId) + Note over CH: store connection metadata + end + C->>CH: enterChunk/addPlayer + loop movement updates + C->>CH: setInput(...) + CH-->>C: snapshot + end + C->>CH: leaveChunk/removePlayer or disconnect + Note over CH: remove membership and metadata +``` + +### Party + +| Topic | Summary | +| --- | --- | +| Matchmaking | Host-created private party flow using party codes and explicit joins. | +| Netcode | Server-authoritative (basic logic). Server owns membership, host permissions, and phase transitions. | +| Tick Rate | No continuous tick; updates are event-driven (`join`, `start`, `finish`). | +| Physics | Low intensity for lobby-first flows; usually no dedicated physics or indexing unless you add realtime mini-games. | + +**Actors** + +- **Key**: `matchmaker["main"]` +- **Responsibility**: Handles party create/join flow, validates join tickets, and tracks party size. +- **Actions** + - `createParty` + - `joinParty` + - `verifyJoin` + - `updatePartySize` + - `closeParty` +- **Queues** + - `createParty` + - `joinParty` + - `verifyJoin` + - `updatePartySize` + - `closeParty` +- **State** + - SQLite + - `parties` + - `join_tickets` for party lookup and join validation + +- **Key**: `match[matchId]` +- **Responsibility**: Owns party members, host role, ready flags, and phase transitions. +- **Actions** + - `connect` + - `startGame` + - `finishGame` +- **Queues** + - None +- **State** + - JSON + - `members` + - `host` + - `ready state` + - `phase` + - `party events` + +**Lifecycle** + +### Host Flow + +```mermaid +sequenceDiagram + participant H as Host Client + participant MM as matchmaker + participant M as match + + H->>MM: createParty() + MM-->>H: {matchId, partyCode, playerId, joinToken} + H->>M: connect(playerId, joinToken) + M->>MM: verifyJoin(...) + MM-->>M: allowed + M->>MM: updatePartySize(playerCount) + H->>M: startGame() / finishGame() + M->>MM: closeParty(matchId) +``` + +### Joiner Flow + +```mermaid +sequenceDiagram + participant J as Joiner Client + participant MM as matchmaker + participant M as match + + J->>MM: joinParty(partyCode) + MM-->>J: {matchId, playerId, joinToken} + J->>M: connect(playerId, joinToken) + M->>MM: verifyJoin(...) + MM-->>M: allowed / denied + M->>MM: updatePartySize(playerCount) +``` + +### Ranked + +| Topic | Summary | +| --- | --- | +| Matchmaking | ELO-based queue pairing with a widening search window as wait time increases. | +| Netcode | Hybrid. Client owns movement with local prediction and interpolation. Server owns projectiles, hit resolution, match results, and rating updates. | +| Tick Rate | 20 ticks/sec (`50ms`) with fixed live ticks for deterministic pacing and broadcast cadence. | +| Physics | Medium to high intensity; client movement with server validation and server-authoritative combat/hit resolution. | + +**Actors** + +- **Key**: `matchmaker["main"]` +- **Responsibility**: Runs rating-based queueing, pairing, assignment persistence, and completion fanout. +- **Actions** + - `queueForMatch` + - `unqueueForMatch` + - `matchCompleted` +- **Queues** + - `queueForMatch` + - `unqueueForMatch` + - `matchCompleted` +- **State** + - SQLite + - `player_pool` + - `matches` + - `assignments` with rating window and connection scoping + +- **Key**: `match[matchId]` +- **Responsibility**: Runs ranked match phase, score, and winner reporting. +- **Actions** + - `connect` + - Gameplay actions +- **Queues** + - None +- **State** + - JSON + - `phase` + - `players` + - `score` + - `winner` + - `completion payload` + +- **Key**: `player[username]` +- **Responsibility**: Stores canonical player MMR and win/loss profile. +- **Actions** + - `initialize` + - `getRating` + - `applyMatchResult` +- **Queues** + - None +- **State** + - JSON + - `rating` + - `wins` + - `losses` + - `match counters` + +- **Key**: `leaderboard["main"]` +- **Responsibility**: Stores and serves top-ranked players. +- **Actions** + - `updatePlayer` +- **Queues** + - None +- **State** + - SQLite + - Leaderboard score rows + - Top-list ordering + +**Lifecycle** + +```mermaid +sequenceDiagram + participant C as Client + participant MM as matchmaker + participant P as player + participant M as match + participant LB as leaderboard + + C->>MM: queueForMatch(username) + MM->>P: initialize/getRating + P-->>MM: rating + Note over MM: store queue row + retry pairing + MM->>M: create(matchId, assigned players) + MM-->>C: assignmentReady + C->>M: connect(username) + M->>MM: matchCompleted(...) + MM->>P: applyMatchResult(...) + MM->>LB: updatePlayer(...) + Note over MM: remove matches + assignments rows +``` + +### Turn-Based + +| Topic | Summary | +| --- | --- | +| Matchmaking | Async private-invite and public-queue pairing in the same pattern. | +| Netcode | Server-authoritative (basic logic). Client can draft moves before submit. Server owns turn ownership, committed move log, turn order, and completion state. | +| Tick Rate | No continuous tick; move submission and turn transitions drive updates. | +| Physics | Very low intensity; no realtime physics loop, just discrete rules validation. Indexing is optional and mostly for board or query convenience at scale. | + +**Actors** + +- **Key**: `matchmaker["main"]` +- **Responsibility**: Handles private invite and public queue pairing for async matches. +- **Actions** + - `createGame` + - `joinByCode` + - `queueForMatch` + - `unqueueForMatch` + - `closeMatch` +- **Queues** + - `createGame` + - `joinByCode` + - `queueForMatch` + - `unqueueForMatch` + - `closeMatch` +- **State** + - SQLite + - `matches` + - `player_pool` + - `assignments` for invite and queue mapping + +- **Key**: `match[matchId]` +- **Responsibility**: Owns board state, turn order, move validation, and final result. +- **Actions** + - `connect` + - `makeMove` +- **Queues** + - None +- **State** + - JSON + - `board` + - `turns` + - `players` + - `connection presence` + - `result` + +**Lifecycle** + +### Public Queue + +```mermaid +sequenceDiagram + participant A as Client A + participant B as Client B + participant MM as matchmaker + participant M as match + + A->>MM: queueForMatch() + B->>MM: queueForMatch() + Note over MM: pair first two queued players + MM->>M: create(matchId) + seed X/O players + MM-->>A: assignment/match info + MM-->>B: assignment/match info + A->>M: connect(playerId) + B->>M: connect(playerId) + A->>M: makeMove() + B->>M: makeMove() + opt all players disconnected for timeout + Note over M: destroy after idle timeout + end + M->>MM: closeMatch(matchId) +``` + +### Private Invite + +```mermaid +sequenceDiagram + participant A as Client A + participant B as Client B + participant MM as matchmaker + participant M as match + + A->>MM: createGame() + MM-->>A: {matchId, playerId, inviteCode} + B->>MM: joinByCode(inviteCode) + MM->>M: create(matchId) + seed X/O players + MM-->>A: assignment/match info + MM-->>B: assignment/match info + A->>M: connect(playerId) + B->>M: connect(playerId) + A->>M: makeMove() + B->>M: makeMove() + M->>MM: closeMatch(matchId) +``` + +### Idle + +| Topic | Summary | +| --- | --- | +| Matchmaking | No matchmaker; each player uses a direct per-player actor and a shared leaderboard actor. | +| Netcode | Server-authoritative (basic logic). Client owns UI and build intent. Server owns resources, production rates, building validation, and leaderboard totals. | +| Tick Rate | No continuous tick; use `c.schedule.after(...)` for coarse intervals and compute offline catch-up from elapsed wall time. | +| Physics | None for standard idle loops; transitions are discrete (`build`, `collect`, `upgrade`) and do not need spatial indexing. | + +**Actors** + +- **Key**: `world[playerId]` +- **Responsibility**: Owns one player's progression, buildings, production scheduling, and state updates. +- **Actions** + - `initialize` + - `build` + - `collectProduction` +- **Queues** + - None +- **State** + - JSON + - Per-player buildings + - `resources` + - `timers` + - `progression state` + +- **Key**: `leaderboard["main"]` +- **Responsibility**: Stores global scores and serves leaderboard updates. +- **Actions** + - `updateScore` +- **Queues** + - `updateScore` +- **State** + - SQLite + - `scores` table keyed by player + - Current leaderboard totals + +**Lifecycle** + +```mermaid +sequenceDiagram + participant C as Client + participant W as world + participant LB as leaderboard + + C->>W: getOrCreate(playerId) + initialize() + Note over W: seed state + schedule collection + W-->>C: stateUpdate + loop gameplay loop + C->>W: build() / collectProduction() + W->>LB: updateScore(...) + Note over LB: upsert scores + LB-->>C: leaderboardUpdate + W-->>C: stateUpdate + end +``` + +## Reference Map + +### Actors + +- [Access Control](reference/actors/access-control.md) +- [Actions](reference/actors/actions.md) +- [Actor Keys](reference/actors/keys.md) +- [Actor Runtime Socket](reference/actors/actor-runtime-socket.md) +- [Actor Statuses](reference/actors/statuses.md) +- [Authentication](reference/actors/authentication.md) +- [Cloudflare Workers Quickstart](reference/actors/quickstart/cloudflare.md) +- [Communicating Between Actors](reference/actors/communicating-between-actors.md) +- [Connections](reference/actors/connections.md) +- [Custom Inspector Tabs](reference/actors/inspector-tabs.md) +- [Debugging](reference/actors/debugging.md) +- [Design Patterns](reference/actors/design-patterns.md) +- [Destroying Actors](reference/actors/destroy.md) +- [Effect.ts Quickstart (Beta)](reference/actors/quickstart/effect.md) +- [Errors](reference/actors/errors.md) +- [Fetch and WebSocket Handler](reference/actors/fetch-and-websocket-handler.md) +- [Helper Types](reference/actors/helper-types.md) +- [Icons & Names](reference/actors/appearance.md) +- [In-Memory State](reference/actors/state.md) +- [Input Parameters](reference/actors/input.md) +- [Lifecycle](reference/actors/lifecycle.md) +- [Limits](reference/actors/limits.md) +- [Low-Level HTTP Request Handler](reference/actors/request-handler.md) +- [Low-Level KV Storage](reference/actors/kv.md) +- [Low-Level WebSocket Handler](reference/actors/websocket-handler.md) +- [Metadata](reference/actors/metadata.md) +- [Next.js Quickstart](reference/actors/quickstart/next-js.md) +- [Node.js & Bun Quickstart](reference/actors/quickstart/backend.md) +- [Queues & Run Loops](reference/actors/queues.md) +- [React Quickstart](reference/actors/quickstart/react.md) +- [Realtime](reference/actors/events.md) +- [Rust Quickstart (Beta)](reference/actors/quickstart/rust.md) +- [Scaling & Concurrency](reference/actors/scaling.md) +- [Schedule & Cron](reference/actors/schedule.md) +- [Sharing and Joining State](reference/actors/sharing-and-joining-state.md) +- [SQLite](reference/actors/sqlite.md) +- [SQLite + Drizzle](reference/actors/sqlite-drizzle.md) +- [Supabase Functions Quickstart](reference/actors/quickstart/supabase.md) +- [Testing](reference/actors/testing.md) +- [Troubleshooting](reference/actors/troubleshooting.md) +- [Types](reference/actors/types.md) +- [Vanilla HTTP API](reference/actors/http-api.md) +- [Versions & Upgrades](reference/actors/versions.md) +- [Workflows](reference/actors/workflows.md) + +### Cli + +- [CLI](reference/cli.md) + +### Clients + +- [Node.js & Bun](reference/clients/javascript.md) +- [React](reference/clients/react.md) +- [Rust (Beta)](reference/clients/rust.md) +- [Swift](reference/clients/swift.md) +- [SwiftUI](reference/clients/swiftui.md) + +### Cookbook + +- [AI Agent](reference/cookbook/ai-agent.md) +- [Chat Room](reference/cookbook/chat-room.md) +- [Collaborative Text Editor](reference/cookbook/collaborative-text-editor.md) +- [Cron Jobs and Scheduled Tasks](reference/cookbook/cron-jobs.md) +- [Database per Tenant](reference/cookbook/per-tenant-database.md) +- [Deploying Rivet in a VPC or Air-Gapped Network](reference/cookbook/vpc-air-gapped.md) +- [Live Cursors and Presence](reference/cookbook/live-cursors.md) +- [Multiplayer Game](reference/cookbook/multiplayer-game.md) + +### Deploy + +- [Container Runner](reference/deploy/container-runner.md) +- [Deploy To Amazon Web Services Lambda](reference/deploy/aws-lambda.md) +- [Deploying to AWS ECS](reference/deploy/aws-ecs.md) +- [Deploying to Cloudflare Workers](reference/deploy/cloudflare.md) +- [Deploying to Freestyle](reference/deploy/freestyle.md) +- [Deploying to Google Cloud Run](reference/deploy/gcp-cloud-run.md) +- [Deploying to Hetzner](reference/deploy/hetzner.md) +- [Deploying to Kubernetes](reference/deploy/kubernetes.md) +- [Deploying to Railway](reference/deploy/railway.md) +- [Deploying to Rivet Compute](reference/deploy/rivet-compute.md) +- [Deploying to Supabase Functions](reference/deploy/supabase.md) +- [Deploying to Vercel](reference/deploy/vercel.md) +- [Deploying to VMs & Bare Metal](reference/deploy/vm-and-bare-metal.md) + +### General + +- [Actor Configuration](reference/general/actor-configuration.md) +- [Architecture](reference/general/architecture.md) +- [Cross-Origin Resource Sharing](reference/general/cors.md) +- [Documentation for LLMs & AI](reference/general/docs-for-llms.md) +- [Edge Networking](reference/general/edge.md) +- [Endpoints](reference/general/endpoints.md) +- [Environment Variables](reference/general/environment-variables.md) +- [HTTP Server](reference/general/http-server.md) +- [Logging](reference/general/logging.md) +- [Pool Configuration](reference/general/pool-configuration.md) +- [Production Checklist](reference/general/production-checklist.md) +- [Registry Configuration](reference/general/registry-configuration.md) +- [Runtime Modes](reference/general/runtime-modes.md) +- [WASM vs Native SDK](reference/general/wasm-vs-native-sdk.md) + +### Self Hosting + +- [Configuration](reference/self-hosting/configuration.md) +- [Docker Compose](reference/self-hosting/docker-compose.md) +- [Docker Container](reference/self-hosting/docker-container.md) +- [File System](reference/self-hosting/filesystem.md) +- [FoundationDB (Enterprise)](reference/self-hosting/foundationdb.md) +- [Installing Rivet Engine](reference/self-hosting/install.md) +- [Kubernetes](reference/self-hosting/kubernetes.md) +- [Multi-Region](reference/self-hosting/multi-region.md) +- [PostgreSQL](reference/self-hosting/postgres.md) +- [Production Checklist](reference/self-hosting/production-checklist.md) +- [Railway Deployment](reference/self-hosting/railway.md) +- [Render Deployment](reference/self-hosting/render.md) +- [TLS & Certificates](reference/self-hosting/tls.md) + diff --git a/.agents/skills/multiplayer-game/index.json b/.agents/skills/multiplayer-game/index.json new file mode 100644 index 0000000..1a52d08 --- /dev/null +++ b/.agents/skills/multiplayer-game/index.json @@ -0,0 +1,694 @@ +{ + "name": "multiplayer-game", + "description": "Pragmatic patterns for building multiplayer games: matchmaking, tick loops, realtime state, interest management, and validation.", + "skill_url": "/metadata/skills/multiplayer-game/SKILL.md", + "generated_at": "2026-07-20T21:43:04.656Z", + "references": [ + { + "name": "actors/access-control", + "title": "Access Control", + "description": "Authorize actions, queue publishes, and event subscriptions with explicit hooks.", + "canonical_url": "https://rivet.dev/docs/actors/access-control", + "reference_url": "/metadata/skills/multiplayer-game/reference/actors/access-control.md" + }, + { + "name": "actors/actions", + "title": "Actions", + "description": "Actions are how your backend, frontend, or other actors can communicate with actors.", + "canonical_url": "https://rivet.dev/docs/actors/actions", + "reference_url": "/metadata/skills/multiplayer-game/reference/actors/actions.md" + }, + { + "name": "general/actor-configuration", + "title": "Actor Configuration", + "description": "This page documents the configuration options available when defining a RivetKit actor. The actor configuration is passed to the `actor()` function.", + "canonical_url": "https://rivet.dev/docs/general/actor-configuration", + "reference_url": "/metadata/skills/multiplayer-game/reference/general/actor-configuration.md" + }, + { + "name": "actors/keys", + "title": "Actor Keys", + "description": "Actor keys uniquely identify actor instances within each actor type. Keys are used for addressing which specific actor to communicate with.", + "canonical_url": "https://rivet.dev/docs/actors/keys", + "reference_url": "/metadata/skills/multiplayer-game/reference/actors/keys.md" + }, + { + "name": "actors/actor-runtime-socket", + "title": "Actor Runtime Socket", + "description": "Access an actor's SQLite database from another local process.", + "canonical_url": "https://rivet.dev/docs/actors/actor-runtime-socket", + "reference_url": "/metadata/skills/multiplayer-game/reference/actors/actor-runtime-socket.md" + }, + { + "name": "actors/statuses", + "title": "Actor Statuses", + "description": "Understand the lifecycle statuses of Rivet Actors, what they mean, how they appear in the API, and how to troubleshoot common issues.", + "canonical_url": "https://rivet.dev/docs/actors/statuses", + "reference_url": "/metadata/skills/multiplayer-game/reference/actors/statuses.md" + }, + { + "name": "cookbook/ai-agent", + "title": "AI Agent", + "description": "Build an AI agent backend with persistent memory: one Rivet Actor per conversation, queued message handling, and streaming LLM responses as realtime events.", + "canonical_url": "https://rivet.dev/cookbook/ai-agent", + "reference_url": "/metadata/skills/multiplayer-game/reference/cookbook/ai-agent.md" + }, + { + "name": "general/architecture", + "title": "Architecture", + "description": "- rivetkit is the typescript library used for both local development & to connect your application to rivet - a rivetkit instance is called a \"runner.\" you can run multiple runners to scale rivetkit horizontally. read omre about runners below.", + "canonical_url": "https://rivet.dev/docs/general/architecture", + "reference_url": "/metadata/skills/multiplayer-game/reference/general/architecture.md" + }, + { + "name": "actors/authentication", + "title": "Authentication", + "description": "Secure your actors with authentication and authorization.", + "canonical_url": "https://rivet.dev/docs/actors/authentication", + "reference_url": "/metadata/skills/multiplayer-game/reference/actors/authentication.md" + }, + { + "name": "cookbook/chat-room", + "title": "Chat Room", + "description": "Build a realtime chat room backend with Rivet Actors: one actor per room, SQLite-backed message history, and WebSocket broadcast to every connected client.", + "canonical_url": "https://rivet.dev/cookbook/chat-room", + "reference_url": "/metadata/skills/multiplayer-game/reference/cookbook/chat-room.md" + }, + { + "name": "cli", + "title": "CLI", + "description": "Reference for the optional rivet CLI: deploy to Rivet Compute and run local dev for serverless platforms.", + "canonical_url": "https://rivet.dev/docs/cli", + "reference_url": "/metadata/skills/multiplayer-game/reference/cli.md" + }, + { + "name": "actors/quickstart/cloudflare", + "title": "Cloudflare Workers Quickstart", + "description": "Set up a Rivet project locally targeting Cloudflare Workers.", + "canonical_url": "https://rivet.dev/docs/actors/quickstart/cloudflare", + "reference_url": "/metadata/skills/multiplayer-game/reference/actors/quickstart/cloudflare.md" + }, + { + "name": "cookbook/collaborative-text-editor", + "title": "Collaborative Text Editor", + "description": "Build a collaborative text editor backend with Yjs CRDTs and Rivet Actors: per-document actors relay sync and awareness updates and persist snapshots.", + "canonical_url": "https://rivet.dev/cookbook/collaborative-text-editor", + "reference_url": "/metadata/skills/multiplayer-game/reference/cookbook/collaborative-text-editor.md" + }, + { + "name": "actors/communicating-between-actors", + "title": "Communicating Between Actors", + "description": "Learn how actors can call other actors and share data", + "canonical_url": "https://rivet.dev/docs/actors/communicating-between-actors", + "reference_url": "/metadata/skills/multiplayer-game/reference/actors/communicating-between-actors.md" + }, + { + "name": "self-hosting/configuration", + "title": "Configuration", + "description": "Rivet Engine can be configured through environment variables or configuration files.", + "canonical_url": "https://rivet.dev/docs/self-hosting/configuration", + "reference_url": "/metadata/skills/multiplayer-game/reference/self-hosting/configuration.md" + }, + { + "name": "actors/connections", + "title": "Connections", + "description": "Connections represent client connections to your actor. They provide a way to handle client authentication, manage connection-specific data, and control the connection lifecycle.", + "canonical_url": "https://rivet.dev/docs/actors/connections", + "reference_url": "/metadata/skills/multiplayer-game/reference/actors/connections.md" + }, + { + "name": "deploy/container-runner", + "title": "Container Runner", + "description": "Run any containerized server as a Rivet Actor.", + "canonical_url": "https://rivet.dev/docs/deploy/container-runner", + "reference_url": "/metadata/skills/multiplayer-game/reference/deploy/container-runner.md" + }, + { + "name": "cookbook/cron-jobs", + "title": "Cron Jobs and Scheduled Tasks", + "description": "Patterns for durable one-shot, calendar, and fixed-interval work on Rivet Actors.", + "canonical_url": "https://rivet.dev/cookbook/cron-jobs", + "reference_url": "/metadata/skills/multiplayer-game/reference/cookbook/cron-jobs.md" + }, + { + "name": "general/cors", + "title": "Cross-Origin Resource Sharing", + "description": "Cross-Origin Resource Sharing (CORS) controls which origins (domains) can access your actors. When actors are exposed to the public internet, proper origin validation is critical to prevent security breaches and denial of service attacks.", + "canonical_url": "https://rivet.dev/docs/general/cors", + "reference_url": "/metadata/skills/multiplayer-game/reference/general/cors.md" + }, + { + "name": "actors/inspector-tabs", + "title": "Custom Inspector Tabs", + "description": "Ship your own UI tabs alongside a Rivet Actor — embedded directly in the dashboard inspector.", + "canonical_url": "https://rivet.dev/docs/actors/inspector-tabs", + "reference_url": "/metadata/skills/multiplayer-game/reference/actors/inspector-tabs.md" + }, + { + "name": "cookbook/per-tenant-database", + "title": "Database per Tenant", + "description": "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.", + "canonical_url": "https://rivet.dev/cookbook/per-tenant-database", + "reference_url": "/metadata/skills/multiplayer-game/reference/cookbook/per-tenant-database.md" + }, + { + "name": "actors/debugging", + "title": "Debugging", + "description": "Inspect and debug running Rivet Actors, runners, and provider configs using management, runner, and actor inspector HTTP APIs.", + "canonical_url": "https://rivet.dev/docs/actors/debugging", + "reference_url": "/metadata/skills/multiplayer-game/reference/actors/debugging.md" + }, + { + "name": "deploy/aws-lambda", + "title": "Deploy To Amazon Web Services Lambda", + "description": "_AWS Lambda is coming soon_", + "canonical_url": "https://rivet.dev/docs/deploy/aws-lambda", + "reference_url": "/metadata/skills/multiplayer-game/reference/deploy/aws-lambda.md" + }, + { + "name": "cookbook/vpc-air-gapped", + "title": "Deploying Rivet in a VPC or Air-Gapped Network", + "description": "Run Rivet entirely inside your own perimeter: single-binary or Docker Compose install, file system storage with no database infrastructure, and no outbound telemetry by default.", + "canonical_url": "https://rivet.dev/cookbook/vpc-air-gapped", + "reference_url": "/metadata/skills/multiplayer-game/reference/cookbook/vpc-air-gapped.md" + }, + { + "name": "deploy/aws-ecs", + "title": "Deploying to AWS ECS", + "description": "Run your backend on Amazon ECS with Fargate.", + "canonical_url": "https://rivet.dev/docs/deploy/aws-ecs", + "reference_url": "/metadata/skills/multiplayer-game/reference/deploy/aws-ecs.md" + }, + { + "name": "deploy/cloudflare", + "title": "Deploying to Cloudflare Workers", + "description": "Deploy an existing Rivet project to Cloudflare Workers.", + "canonical_url": "https://rivet.dev/docs/deploy/cloudflare", + "reference_url": "/metadata/skills/multiplayer-game/reference/deploy/cloudflare.md" + }, + { + "name": "deploy/freestyle", + "title": "Deploying to Freestyle", + "description": "Deploy RivetKit app to Freestyle.sh, a cloud platform for running AI-generated code with built-in security and scalability.", + "canonical_url": "https://rivet.dev/docs/deploy/freestyle", + "reference_url": "/metadata/skills/multiplayer-game/reference/deploy/freestyle.md" + }, + { + "name": "deploy/gcp-cloud-run", + "title": "Deploying to Google Cloud Run", + "description": "Deploy your RivetKit app to Google Cloud Run.", + "canonical_url": "https://rivet.dev/docs/deploy/gcp-cloud-run", + "reference_url": "/metadata/skills/multiplayer-game/reference/deploy/gcp-cloud-run.md" + }, + { + "name": "deploy/hetzner", + "title": "Deploying to Hetzner", + "description": "Please see the VM & Bare Metal guide.", + "canonical_url": "https://rivet.dev/docs/deploy/hetzner", + "reference_url": "/metadata/skills/multiplayer-game/reference/deploy/hetzner.md" + }, + { + "name": "deploy/kubernetes", + "title": "Deploying to Kubernetes", + "description": "Deploy your RivetKit app to any Kubernetes cluster.", + "canonical_url": "https://rivet.dev/docs/deploy/kubernetes", + "reference_url": "/metadata/skills/multiplayer-game/reference/deploy/kubernetes.md" + }, + { + "name": "deploy/railway", + "title": "Deploying to Railway", + "description": "Deploy your RivetKit app to Railway.", + "canonical_url": "https://rivet.dev/docs/deploy/railway", + "reference_url": "/metadata/skills/multiplayer-game/reference/deploy/railway.md" + }, + { + "name": "deploy/rivet-compute", + "title": "Deploying to Rivet Compute", + "description": "Run your backend on Rivet Compute.", + "canonical_url": "https://rivet.dev/docs/deploy/rivet-compute", + "reference_url": "/metadata/skills/multiplayer-game/reference/deploy/rivet-compute.md" + }, + { + "name": "deploy/supabase", + "title": "Deploying to Supabase Functions", + "description": "Deploy an existing Rivet project to Supabase Edge Functions.", + "canonical_url": "https://rivet.dev/docs/deploy/supabase", + "reference_url": "/metadata/skills/multiplayer-game/reference/deploy/supabase.md" + }, + { + "name": "deploy/vercel", + "title": "Deploying to Vercel", + "description": "Deploy your Next.js Rivet app to Vercel.", + "canonical_url": "https://rivet.dev/docs/deploy/vercel", + "reference_url": "/metadata/skills/multiplayer-game/reference/deploy/vercel.md" + }, + { + "name": "deploy/vm-and-bare-metal", + "title": "Deploying to VMs & Bare Metal", + "description": "Deploy your RivetKit app to any Linux VM or bare metal host.", + "canonical_url": "https://rivet.dev/docs/deploy/vm-and-bare-metal", + "reference_url": "/metadata/skills/multiplayer-game/reference/deploy/vm-and-bare-metal.md" + }, + { + "name": "actors/design-patterns", + "title": "Design Patterns", + "description": "Common patterns and anti-patterns for building scalable actor systems.", + "canonical_url": "https://rivet.dev/docs/actors/design-patterns", + "reference_url": "/metadata/skills/multiplayer-game/reference/actors/design-patterns.md" + }, + { + "name": "actors/destroy", + "title": "Destroying Actors", + "description": "Actors can be permanently destroyed. Common use cases include:", + "canonical_url": "https://rivet.dev/docs/actors/destroy", + "reference_url": "/metadata/skills/multiplayer-game/reference/actors/destroy.md" + }, + { + "name": "self-hosting/docker-compose", + "title": "Docker Compose", + "description": "Deploy Rivet Engine with docker-compose for multi-container setups.", + "canonical_url": "https://rivet.dev/docs/self-hosting/docker-compose", + "reference_url": "/metadata/skills/multiplayer-game/reference/self-hosting/docker-compose.md" + }, + { + "name": "self-hosting/docker-container", + "title": "Docker Container", + "description": "Run Rivet Engine in a single Docker container.", + "canonical_url": "https://rivet.dev/docs/self-hosting/docker-container", + "reference_url": "/metadata/skills/multiplayer-game/reference/self-hosting/docker-container.md" + }, + { + "name": "general/docs-for-llms", + "title": "Documentation for LLMs & AI", + "description": "Rivet provides optimized documentation formats specifically designed for Large Language Models (LLMs) and AI integration tools.", + "canonical_url": "https://rivet.dev/docs/general/docs-for-llms", + "reference_url": "/metadata/skills/multiplayer-game/reference/general/docs-for-llms.md" + }, + { + "name": "general/edge", + "title": "Edge Networking", + "description": "Actors automatically run near your users on your provider's global network.", + "canonical_url": "https://rivet.dev/docs/general/edge", + "reference_url": "/metadata/skills/multiplayer-game/reference/general/edge.md" + }, + { + "name": "actors/quickstart/effect", + "title": "Effect.ts Quickstart (Beta)", + "description": "Build a Rivet Actor with the Effect SDK", + "canonical_url": "https://rivet.dev/docs/actors/quickstart/effect", + "reference_url": "/metadata/skills/multiplayer-game/reference/actors/quickstart/effect.md" + }, + { + "name": "general/endpoints", + "title": "Endpoints", + "description": "Configure how your backend connects to Rivet and how clients reach your actors.", + "canonical_url": "https://rivet.dev/docs/general/endpoints", + "reference_url": "/metadata/skills/multiplayer-game/reference/general/endpoints.md" + }, + { + "name": "general/environment-variables", + "title": "Environment Variables", + "description": "This page documents all environment variables that configure RivetKit behavior.", + "canonical_url": "https://rivet.dev/docs/general/environment-variables", + "reference_url": "/metadata/skills/multiplayer-game/reference/general/environment-variables.md" + }, + { + "name": "actors/errors", + "title": "Errors", + "description": "Rivet provides robust error handling with security built in by default. Errors are handled differently based on whether they should be exposed to clients or kept private.", + "canonical_url": "https://rivet.dev/docs/actors/errors", + "reference_url": "/metadata/skills/multiplayer-game/reference/actors/errors.md" + }, + { + "name": "actors/fetch-and-websocket-handler", + "title": "Fetch and WebSocket Handler", + "description": "These docs have moved to [Low-Level WebSocket Handler](/docs/actors/websocket-handler) and [Low-Level Request Handler](/docs/actors/request-handler).", + "canonical_url": "https://rivet.dev/docs/actors/fetch-and-websocket-handler", + "reference_url": "/metadata/skills/multiplayer-game/reference/actors/fetch-and-websocket-handler.md" + }, + { + "name": "self-hosting/filesystem", + "title": "File System", + "description": "The file system backend stores all data on the local disk. This is suitable for single-node deployments, development, and testing.", + "canonical_url": "https://rivet.dev/docs/self-hosting/filesystem", + "reference_url": "/metadata/skills/multiplayer-game/reference/self-hosting/filesystem.md" + }, + { + "name": "self-hosting/foundationdb", + "title": "FoundationDB (Enterprise)", + "description": "FoundationDB is the recommended storage backend for scalable production Rivet deployments.", + "canonical_url": "https://rivet.dev/docs/self-hosting/foundationdb", + "reference_url": "/metadata/skills/multiplayer-game/reference/self-hosting/foundationdb.md" + }, + { + "name": "actors/helper-types", + "title": "Helper Types", + "description": "This page has moved to [Types](/docs/actors/types).", + "canonical_url": "https://rivet.dev/docs/actors/helper-types", + "reference_url": "/metadata/skills/multiplayer-game/reference/actors/helper-types.md" + }, + { + "name": "general/http-server", + "title": "HTTP Server", + "description": "Different ways to run your RivetKit HTTP server.", + "canonical_url": "https://rivet.dev/docs/general/http-server", + "reference_url": "/metadata/skills/multiplayer-game/reference/general/http-server.md" + }, + { + "name": "actors/appearance", + "title": "Icons & Names", + "description": "Customize actors with display names and icons for the Rivet inspector and dashboard.", + "canonical_url": "https://rivet.dev/docs/actors/appearance", + "reference_url": "/metadata/skills/multiplayer-game/reference/actors/appearance.md" + }, + { + "name": "actors/state", + "title": "In-Memory State", + "description": "Actors store state in memory for instant reads and writes. State can be persisted automatically or kept ephemeral.", + "canonical_url": "https://rivet.dev/docs/actors/state", + "reference_url": "/metadata/skills/multiplayer-game/reference/actors/state.md" + }, + { + "name": "actors/input", + "title": "Input Parameters", + "description": "Pass initialization data to actors when creating instances", + "canonical_url": "https://rivet.dev/docs/actors/input", + "reference_url": "/metadata/skills/multiplayer-game/reference/actors/input.md" + }, + { + "name": "self-hosting/install", + "title": "Installing Rivet Engine", + "description": "Install Rivet Engine using Docker, binaries, or a source build.", + "canonical_url": "https://rivet.dev/docs/self-hosting/install", + "reference_url": "/metadata/skills/multiplayer-game/reference/self-hosting/install.md" + }, + { + "name": "self-hosting/kubernetes", + "title": "Kubernetes", + "description": "Deploy production-ready Rivet Engine to Kubernetes with PostgreSQL storage.", + "canonical_url": "https://rivet.dev/docs/self-hosting/kubernetes", + "reference_url": "/metadata/skills/multiplayer-game/reference/self-hosting/kubernetes.md" + }, + { + "name": "actors/lifecycle", + "title": "Lifecycle", + "description": "Learn about actor lifecycle hooks for initialization, state management, and cleanup.", + "canonical_url": "https://rivet.dev/docs/actors/lifecycle", + "reference_url": "/metadata/skills/multiplayer-game/reference/actors/lifecycle.md" + }, + { + "name": "actors/limits", + "title": "Limits", + "description": "Limits and constraints for Rivet Actors.", + "canonical_url": "https://rivet.dev/docs/actors/limits", + "reference_url": "/metadata/skills/multiplayer-game/reference/actors/limits.md" + }, + { + "name": "cookbook/live-cursors", + "title": "Live Cursors and Presence", + "description": "Live cursors and multiplayer presence with Rivet Actors: per-connection cursor state, realtime updates over events or raw WebSockets, and throttling.", + "canonical_url": "https://rivet.dev/cookbook/live-cursors", + "reference_url": "/metadata/skills/multiplayer-game/reference/cookbook/live-cursors.md" + }, + { + "name": "general/logging", + "title": "Logging", + "description": "Actors provide a built-in way to log complex data to the console.", + "canonical_url": "https://rivet.dev/docs/general/logging", + "reference_url": "/metadata/skills/multiplayer-game/reference/general/logging.md" + }, + { + "name": "actors/request-handler", + "title": "Low-Level HTTP Request Handler", + "description": "Actors can handle HTTP requests through the `onRequest` handler.", + "canonical_url": "https://rivet.dev/docs/actors/request-handler", + "reference_url": "/metadata/skills/multiplayer-game/reference/actors/request-handler.md" + }, + { + "name": "actors/kv", + "title": "Low-Level KV Storage", + "description": "Use the built-in key-value store on ActorContext for durable string and binary data alongside actor state.", + "canonical_url": "https://rivet.dev/docs/actors/kv", + "reference_url": "/metadata/skills/multiplayer-game/reference/actors/kv.md" + }, + { + "name": "actors/websocket-handler", + "title": "Low-Level WebSocket Handler", + "description": "Actors can handle WebSocket connections through the `onWebSocket` handler.", + "canonical_url": "https://rivet.dev/docs/actors/websocket-handler", + "reference_url": "/metadata/skills/multiplayer-game/reference/actors/websocket-handler.md" + }, + { + "name": "actors/metadata", + "title": "Metadata", + "description": "Metadata provides information about the currently running actor.", + "canonical_url": "https://rivet.dev/docs/actors/metadata", + "reference_url": "/metadata/skills/multiplayer-game/reference/actors/metadata.md" + }, + { + "name": "self-hosting/multi-region", + "title": "Multi-Region", + "description": "Rivet Engine supports scaling transparently across multiple regions.", + "canonical_url": "https://rivet.dev/docs/self-hosting/multi-region", + "reference_url": "/metadata/skills/multiplayer-game/reference/self-hosting/multi-region.md" + }, + { + "name": "cookbook/multiplayer-game", + "title": "Multiplayer Game", + "description": "Pragmatic patterns for building multiplayer games: matchmaking, tick loops, realtime state, interest management, and validation.", + "canonical_url": "https://rivet.dev/cookbook/multiplayer-game", + "reference_url": "/metadata/skills/multiplayer-game/reference/cookbook/multiplayer-game.md" + }, + { + "name": "actors/quickstart/next-js", + "title": "Next.js Quickstart", + "description": "Get started with Rivet Actors in Next.js", + "canonical_url": "https://rivet.dev/docs/actors/quickstart/next-js", + "reference_url": "/metadata/skills/multiplayer-game/reference/actors/quickstart/next-js.md" + }, + { + "name": "clients/javascript", + "title": "Node.js & Bun", + "description": "Connect JavaScript apps to Rivet Actors.", + "canonical_url": "https://rivet.dev/docs/clients/javascript", + "reference_url": "/metadata/skills/multiplayer-game/reference/clients/javascript.md" + }, + { + "name": "actors/quickstart/backend", + "title": "Node.js & Bun Quickstart", + "description": "Get started with Rivet Actors in Node.js and Bun", + "canonical_url": "https://rivet.dev/docs/actors/quickstart/backend", + "reference_url": "/metadata/skills/multiplayer-game/reference/actors/quickstart/backend.md" + }, + { + "name": "general/pool-configuration", + "title": "Pool Configuration", + "description": "Reference for runner pool configuration, including drain behavior, actor eviction rate limiting, and serverless-specific options.", + "canonical_url": "https://rivet.dev/docs/general/pool-configuration", + "reference_url": "/metadata/skills/multiplayer-game/reference/general/pool-configuration.md" + }, + { + "name": "self-hosting/postgres", + "title": "PostgreSQL", + "description": "Configure PostgreSQL for self-hosted Rivet deployments.", + "canonical_url": "https://rivet.dev/docs/self-hosting/postgres", + "reference_url": "/metadata/skills/multiplayer-game/reference/self-hosting/postgres.md" + }, + { + "name": "general/production-checklist", + "title": "Production Checklist", + "description": "Checklist for deploying Rivet Actors to production.", + "canonical_url": "https://rivet.dev/docs/general/production-checklist", + "reference_url": "/metadata/skills/multiplayer-game/reference/general/production-checklist.md" + }, + { + "name": "self-hosting/production-checklist", + "title": "Production Checklist", + "description": "Checklist for deploying a self-hosted Rivet Engine to production.", + "canonical_url": "https://rivet.dev/docs/self-hosting/production-checklist", + "reference_url": "/metadata/skills/multiplayer-game/reference/self-hosting/production-checklist.md" + }, + { + "name": "actors/queues", + "title": "Queues & Run Loops", + "description": "Use actor-local durable queues for serial run loops and request/response workflows.", + "canonical_url": "https://rivet.dev/docs/actors/queues", + "reference_url": "/metadata/skills/multiplayer-game/reference/actors/queues.md" + }, + { + "name": "self-hosting/railway", + "title": "Railway Deployment", + "description": "Railway provides a simple platform for deploying Rivet Engine with automatic scaling and managed infrastructure.", + "canonical_url": "https://rivet.dev/docs/self-hosting/railway", + "reference_url": "/metadata/skills/multiplayer-game/reference/self-hosting/railway.md" + }, + { + "name": "clients/react", + "title": "React", + "description": "Connect React apps to Rivet Actors.", + "canonical_url": "https://rivet.dev/docs/clients/react", + "reference_url": "/metadata/skills/multiplayer-game/reference/clients/react.md" + }, + { + "name": "actors/quickstart/react", + "title": "React Quickstart", + "description": "Build realtime React applications with Rivet Actors", + "canonical_url": "https://rivet.dev/docs/actors/quickstart/react", + "reference_url": "/metadata/skills/multiplayer-game/reference/actors/quickstart/react.md" + }, + { + "name": "actors/events", + "title": "Realtime", + "description": "Events enable realtime communication from actors to clients. While clients use actions to send data to actors, events allow actors to push updates to connected clients instantly.", + "canonical_url": "https://rivet.dev/docs/actors/events", + "reference_url": "/metadata/skills/multiplayer-game/reference/actors/events.md" + }, + { + "name": "general/registry-configuration", + "title": "Registry Configuration", + "description": "This page documents the configuration options available when setting up a RivetKit registry. The registry configuration is passed to the `setup()` function.", + "canonical_url": "https://rivet.dev/docs/general/registry-configuration", + "reference_url": "/metadata/skills/multiplayer-game/reference/general/registry-configuration.md" + }, + { + "name": "self-hosting/render", + "title": "Render Deployment", + "description": "Deploy Rivet Engine to Render with managed PostgreSQL and automatic HTTPS, using the experimental PostgreSQL backend.", + "canonical_url": "https://rivet.dev/docs/self-hosting/render", + "reference_url": "/metadata/skills/multiplayer-game/reference/self-hosting/render.md" + }, + { + "name": "general/runtime-modes", + "title": "Runtime Modes", + "description": "RivetKit supports two runtime modes for running your actors:", + "canonical_url": "https://rivet.dev/docs/general/runtime-modes", + "reference_url": "/metadata/skills/multiplayer-game/reference/general/runtime-modes.md" + }, + { + "name": "clients/rust", + "title": "Rust (Beta)", + "description": "Connect Rust apps to Rivet Actors.", + "canonical_url": "https://rivet.dev/docs/clients/rust", + "reference_url": "/metadata/skills/multiplayer-game/reference/clients/rust.md" + }, + { + "name": "actors/quickstart/rust", + "title": "Rust Quickstart (Beta)", + "description": "Build a Rivet Actor in Rust", + "canonical_url": "https://rivet.dev/docs/actors/quickstart/rust", + "reference_url": "/metadata/skills/multiplayer-game/reference/actors/quickstart/rust.md" + }, + { + "name": "actors/scaling", + "title": "Scaling & Concurrency", + "description": "This page has moved to [design patterns](/docs/actors/design-patterns).", + "canonical_url": "https://rivet.dev/docs/actors/scaling", + "reference_url": "/metadata/skills/multiplayer-game/reference/actors/scaling.md" + }, + { + "name": "actors/schedule", + "title": "Schedule & Cron", + "description": "Run durable one-shot and recurring actor actions on a schedule.", + "canonical_url": "https://rivet.dev/docs/actors/schedule", + "reference_url": "/metadata/skills/multiplayer-game/reference/actors/schedule.md" + }, + { + "name": "actors/sharing-and-joining-state", + "title": "Sharing and Joining State", + "description": "This page has moved to [design patterns](/docs/actors/design-patterns).", + "canonical_url": "https://rivet.dev/docs/actors/sharing-and-joining-state", + "reference_url": "/metadata/skills/multiplayer-game/reference/actors/sharing-and-joining-state.md" + }, + { + "name": "actors/sqlite", + "title": "SQLite", + "description": "Use embedded SQLite in Rivet Actors with raw SQL queries.", + "canonical_url": "https://rivet.dev/docs/actors/sqlite", + "reference_url": "/metadata/skills/multiplayer-game/reference/actors/sqlite.md" + }, + { + "name": "actors/sqlite-drizzle", + "title": "SQLite + Drizzle", + "description": "Use Drizzle ORM with embedded SQLite in Rivet Actors.", + "canonical_url": "https://rivet.dev/docs/actors/sqlite-drizzle", + "reference_url": "/metadata/skills/multiplayer-game/reference/actors/sqlite-drizzle.md" + }, + { + "name": "actors/quickstart/supabase", + "title": "Supabase Functions Quickstart", + "description": "Set up a Rivet project locally targeting Supabase Edge Functions.", + "canonical_url": "https://rivet.dev/docs/actors/quickstart/supabase", + "reference_url": "/metadata/skills/multiplayer-game/reference/actors/quickstart/supabase.md" + }, + { + "name": "clients/swift", + "title": "Swift", + "description": "Connect Swift apps to Rivet Actors.", + "canonical_url": "https://rivet.dev/docs/clients/swift", + "reference_url": "/metadata/skills/multiplayer-game/reference/clients/swift.md" + }, + { + "name": "clients/swiftui", + "title": "SwiftUI", + "description": "Build SwiftUI apps with Rivet Actors.", + "canonical_url": "https://rivet.dev/docs/clients/swiftui", + "reference_url": "/metadata/skills/multiplayer-game/reference/clients/swiftui.md" + }, + { + "name": "actors/testing", + "title": "Testing", + "description": "Rivet provides a straightforward testing framework to build reliable and maintainable applications. This guide covers how to write effective tests for your actor-based services.", + "canonical_url": "https://rivet.dev/docs/actors/testing", + "reference_url": "/metadata/skills/multiplayer-game/reference/actors/testing.md" + }, + { + "name": "self-hosting/tls", + "title": "TLS & Certificates", + "description": "How Rivet validates TLS root certificates.", + "canonical_url": "https://rivet.dev/docs/self-hosting/tls", + "reference_url": "/metadata/skills/multiplayer-game/reference/self-hosting/tls.md" + }, + { + "name": "actors/troubleshooting", + "title": "Troubleshooting", + "description": "Common issues with Rivet Actors and how to resolve them.", + "canonical_url": "https://rivet.dev/docs/actors/troubleshooting", + "reference_url": "/metadata/skills/multiplayer-game/reference/actors/troubleshooting.md" + }, + { + "name": "actors/types", + "title": "Types", + "description": "TypeScript types for working with Rivet Actors. This page covers context types used in lifecycle hooks and actions, as well as helper types for extracting types from actor definitions.", + "canonical_url": "https://rivet.dev/docs/actors/types", + "reference_url": "/metadata/skills/multiplayer-game/reference/actors/types.md" + }, + { + "name": "actors/http-api", + "title": "Vanilla HTTP API", + "description": "Use the low-level HTTP handler to send and receive requests from actors.", + "canonical_url": "https://rivet.dev/docs/actors/http-api", + "reference_url": "/metadata/skills/multiplayer-game/reference/actors/http-api.md" + }, + { + "name": "actors/versions", + "title": "Versions & Upgrades", + "description": "When you deploy new code, Rivet ensures actors are upgraded seamlessly without downtime.", + "canonical_url": "https://rivet.dev/docs/actors/versions", + "reference_url": "/metadata/skills/multiplayer-game/reference/actors/versions.md" + }, + { + "name": "general/wasm-vs-native-sdk", + "title": "WASM vs Native SDK", + "description": "RivetKit runs your actors on a native or a WebAssembly runtime depending on your platform.", + "canonical_url": "https://rivet.dev/docs/general/wasm-vs-native-sdk", + "reference_url": "/metadata/skills/multiplayer-game/reference/general/wasm-vs-native-sdk.md" + }, + { + "name": "actors/workflows", + "title": "Workflows", + "description": "Build durable, replayable run loops in Rivet Actors with steps, queue waits, timers, and rollback.", + "canonical_url": "https://rivet.dev/docs/actors/workflows", + "reference_url": "/metadata/skills/multiplayer-game/reference/actors/workflows.md" + } + ] +} \ No newline at end of file diff --git a/.agents/skills/multiplayer-game/openapi.json b/.agents/skills/multiplayer-game/openapi.json new file mode 100644 index 0000000..ca482f7 --- /dev/null +++ b/.agents/skills/multiplayer-game/openapi.json @@ -0,0 +1,1311 @@ +{ + "openapi": "3.0.0", + "info": { + "version": "2.2.0", + "title": "RivetKit API" + }, + "components": { + "schemas": {}, + "parameters": {} + }, + "paths": { + "/actors": { + "get": { + "parameters": [ + { + "schema": { + "type": "string" + }, + "required": false, + "name": "name", + "in": "query" + }, + { + "schema": { + "type": "string" + }, + "required": false, + "name": "actor_ids", + "in": "query" + }, + { + "schema": { + "type": "string" + }, + "required": false, + "name": "key", + "in": "query" + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "actors": { + "type": "array", + "items": { + "type": "object", + "properties": { + "actor_id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "key": { + "type": "string" + }, + "namespace_id": { + "type": "string" + }, + "runner_name_selector": { + "type": "string" + }, + "create_ts": { + "type": "number" + }, + "connectable_ts": { + "type": "number", + "nullable": true + }, + "destroy_ts": { + "type": "number", + "nullable": true + }, + "sleep_ts": { + "type": "number", + "nullable": true + }, + "start_ts": { + "type": "number", + "nullable": true + }, + "error": { + "nullable": true + } + }, + "required": [ + "actor_id", + "name", + "key", + "namespace_id", + "runner_name_selector", + "create_ts" + ] + } + } + }, + "required": [ + "actors" + ] + } + } + } + }, + "400": { + "description": "User error" + }, + "500": { + "description": "Internal error" + } + } + }, + "put": { + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "datacenter": { + "type": "string" + }, + "name": { + "type": "string" + }, + "key": { + "type": "string" + }, + "runner_name_selector": { + "type": "string" + }, + "crash_policy": { + "type": "string" + }, + "input": { + "type": "string", + "nullable": true + } + }, + "required": [ + "name", + "key", + "runner_name_selector", + "crash_policy" + ] + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "actor": { + "type": "object", + "properties": { + "actor_id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "key": { + "type": "string" + }, + "namespace_id": { + "type": "string" + }, + "runner_name_selector": { + "type": "string" + }, + "create_ts": { + "type": "number" + }, + "connectable_ts": { + "type": "number", + "nullable": true + }, + "destroy_ts": { + "type": "number", + "nullable": true + }, + "sleep_ts": { + "type": "number", + "nullable": true + }, + "start_ts": { + "type": "number", + "nullable": true + }, + "error": { + "nullable": true + } + }, + "required": [ + "actor_id", + "name", + "key", + "namespace_id", + "runner_name_selector", + "create_ts" + ] + }, + "created": { + "type": "boolean" + } + }, + "required": [ + "actor", + "created" + ] + } + } + } + }, + "400": { + "description": "User error" + }, + "500": { + "description": "Internal error" + } + } + }, + "post": { + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "datacenter": { + "type": "string" + }, + "name": { + "type": "string" + }, + "runner_name_selector": { + "type": "string" + }, + "crash_policy": { + "type": "string" + }, + "key": { + "type": "string", + "nullable": true + }, + "input": { + "type": "string", + "nullable": true + } + }, + "required": [ + "name", + "runner_name_selector", + "crash_policy" + ] + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "actor": { + "type": "object", + "properties": { + "actor_id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "key": { + "type": "string" + }, + "namespace_id": { + "type": "string" + }, + "runner_name_selector": { + "type": "string" + }, + "create_ts": { + "type": "number" + }, + "connectable_ts": { + "type": "number", + "nullable": true + }, + "destroy_ts": { + "type": "number", + "nullable": true + }, + "sleep_ts": { + "type": "number", + "nullable": true + }, + "start_ts": { + "type": "number", + "nullable": true + }, + "error": { + "nullable": true + } + }, + "required": [ + "actor_id", + "name", + "key", + "namespace_id", + "runner_name_selector", + "create_ts" + ] + } + }, + "required": [ + "actor" + ] + } + } + } + }, + "400": { + "description": "User error" + }, + "500": { + "description": "Internal error" + } + } + } + }, + "/actors/names": { + "get": { + "parameters": [ + { + "schema": { + "type": "string" + }, + "required": true, + "name": "namespace", + "in": "query" + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "names": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "metadata": { + "type": "object", + "additionalProperties": { + "nullable": true + } + } + }, + "required": [ + "metadata" + ] + } + } + }, + "required": [ + "names" + ] + } + } + } + }, + "400": { + "description": "User error" + }, + "500": { + "description": "Internal error" + } + } + } + }, + "/actors/{actor_id}/kv/keys/{key}": { + "get": { + "parameters": [ + { + "schema": { + "type": "string" + }, + "required": true, + "name": "actor_id", + "in": "path" + }, + { + "schema": { + "type": "string" + }, + "required": true, + "name": "key", + "in": "path" + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "value": { + "type": "string", + "nullable": true + } + }, + "required": [ + "value" + ] + } + } + } + }, + "400": { + "description": "User error" + }, + "500": { + "description": "Internal error" + } + } + } + }, + "/gateway/{actorId}/health": { + "get": { + "parameters": [ + { + "name": "actorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the actor to target" + } + ], + "responses": { + "200": { + "description": "Health check", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/gateway/{actorId}/action/{action}": { + "post": { + "parameters": [ + { + "name": "actorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the actor to target" + }, + { + "name": "action", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The name of the action to execute" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "args": {} + }, + "required": [ + "args" + ], + "additionalProperties": false + } + } + } + }, + "responses": { + "200": { + "description": "Action executed successfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "output": {} + }, + "required": [ + "output" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "Invalid action" + }, + "500": { + "description": "Internal error" + } + } + } + }, + "/gateway/{actorId}/request/{path}": { + "get": { + "parameters": [ + { + "name": "actorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the actor to target" + }, + { + "name": "path", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The HTTP path to forward to the actor" + } + ], + "responses": { + "200": { + "description": "Response from actor's raw HTTP handler" + } + } + }, + "post": { + "parameters": [ + { + "name": "actorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the actor to target" + }, + { + "name": "path", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The HTTP path to forward to the actor" + } + ], + "responses": { + "200": { + "description": "Response from actor's raw HTTP handler" + } + } + }, + "put": { + "parameters": [ + { + "name": "actorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the actor to target" + }, + { + "name": "path", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The HTTP path to forward to the actor" + } + ], + "responses": { + "200": { + "description": "Response from actor's raw HTTP handler" + } + } + }, + "delete": { + "parameters": [ + { + "name": "actorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the actor to target" + }, + { + "name": "path", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The HTTP path to forward to the actor" + } + ], + "responses": { + "200": { + "description": "Response from actor's raw HTTP handler" + } + } + }, + "patch": { + "parameters": [ + { + "name": "actorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the actor to target" + }, + { + "name": "path", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The HTTP path to forward to the actor" + } + ], + "responses": { + "200": { + "description": "Response from actor's raw HTTP handler" + } + } + }, + "head": { + "parameters": [ + { + "name": "actorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the actor to target" + }, + { + "name": "path", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The HTTP path to forward to the actor" + } + ], + "responses": { + "200": { + "description": "Response from actor's raw HTTP handler" + } + } + }, + "options": { + "parameters": [ + { + "name": "actorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the actor to target" + }, + { + "name": "path", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The HTTP path to forward to the actor" + } + ], + "responses": { + "200": { + "description": "Response from actor's raw HTTP handler" + } + } + } + }, + "/gateway/{actorId}/inspector/state": { + "get": { + "parameters": [ + { + "name": "actorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the actor to target" + }, + { + "name": "Authorization", + "in": "header", + "required": false, + "schema": { + "type": "string" + }, + "description": "Bearer token for inspector authentication. Required in production, optional in development." + } + ], + "responses": { + "200": { + "description": "Current actor state", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "state": {} + } + } + } + } + }, + "401": { + "description": "Unauthorized" + } + } + }, + "patch": { + "parameters": [ + { + "name": "actorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the actor to target" + }, + { + "name": "Authorization", + "in": "header", + "required": false, + "schema": { + "type": "string" + }, + "description": "Bearer token for inspector authentication. Required in production, optional in development." + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "state": {} + }, + "required": [ + "state" + ] + } + } + } + }, + "responses": { + "200": { + "description": "State updated", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "ok": { + "type": "boolean" + } + } + } + } + } + }, + "401": { + "description": "Unauthorized" + } + } + } + }, + "/gateway/{actorId}/inspector/connections": { + "get": { + "parameters": [ + { + "name": "actorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the actor to target" + }, + { + "name": "Authorization", + "in": "header", + "required": false, + "schema": { + "type": "string" + }, + "description": "Bearer token for inspector authentication. Required in production, optional in development." + } + ], + "responses": { + "200": { + "description": "Current actor connections", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "connections": { + "type": "array", + "items": { + "type": "object" + } + } + } + } + } + } + }, + "401": { + "description": "Unauthorized" + } + } + } + }, + "/gateway/{actorId}/inspector/rpcs": { + "get": { + "parameters": [ + { + "name": "actorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the actor to target" + }, + { + "name": "Authorization", + "in": "header", + "required": false, + "schema": { + "type": "string" + }, + "description": "Bearer token for inspector authentication. Required in production, optional in development." + } + ], + "responses": { + "200": { + "description": "Available actor actions/RPCs", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "rpcs": { + "type": "object" + } + } + } + } + } + }, + "401": { + "description": "Unauthorized" + } + } + } + }, + "/gateway/{actorId}/inspector/action/{name}": { + "post": { + "parameters": [ + { + "name": "actorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the actor to target" + }, + { + "name": "name", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The name of the action to execute" + }, + { + "name": "Authorization", + "in": "header", + "required": false, + "schema": { + "type": "string" + }, + "description": "Bearer token for inspector authentication. Required in production, optional in development." + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "args": { + "type": "array", + "items": {} + } + } + } + } + } + }, + "responses": { + "200": { + "description": "Action executed successfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "output": {} + } + } + } + } + }, + "401": { + "description": "Unauthorized" + } + } + } + }, + "/gateway/{actorId}/inspector/queue": { + "get": { + "parameters": [ + { + "name": "actorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the actor to target" + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 50 + }, + "description": "Maximum number of queue messages to return" + }, + { + "name": "Authorization", + "in": "header", + "required": false, + "schema": { + "type": "string" + }, + "description": "Bearer token for inspector authentication. Required in production, optional in development." + } + ], + "responses": { + "200": { + "description": "Queue status", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "size": { + "type": "integer" + }, + "maxSize": { + "type": "integer" + }, + "truncated": { + "type": "boolean" + }, + "messages": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "createdAtMs": { + "type": "integer" + } + } + } + } + } + } + } + } + }, + "401": { + "description": "Unauthorized" + } + } + } + }, + "/gateway/{actorId}/inspector/traces": { + "get": { + "parameters": [ + { + "name": "actorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the actor to target" + }, + { + "name": "startMs", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 0 + }, + "description": "Start of time range in epoch milliseconds" + }, + { + "name": "endMs", + "in": "query", + "required": false, + "schema": { + "type": "integer" + }, + "description": "End of time range in epoch milliseconds. Defaults to now." + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 1000 + }, + "description": "Maximum number of spans to return" + }, + { + "name": "Authorization", + "in": "header", + "required": false, + "schema": { + "type": "string" + }, + "description": "Bearer token for inspector authentication. Required in production, optional in development." + } + ], + "responses": { + "200": { + "description": "Trace spans in OTLP JSON format", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "otlp": { + "type": "object" + }, + "clamped": { + "type": "boolean" + } + } + } + } + } + }, + "401": { + "description": "Unauthorized" + } + } + } + }, + "/gateway/{actorId}/inspector/workflow-history": { + "get": { + "parameters": [ + { + "name": "actorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the actor to target" + }, + { + "name": "Authorization", + "in": "header", + "required": false, + "schema": { + "type": "string" + }, + "description": "Bearer token for inspector authentication. Required in production, optional in development." + } + ], + "responses": { + "200": { + "description": "Workflow history and status", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "history": {}, + "isWorkflowEnabled": { + "type": "boolean" + } + } + } + } + } + }, + "401": { + "description": "Unauthorized" + } + } + } + }, + "/gateway/{actorId}/inspector/workflow/replay": { + "post": { + "parameters": [ + { + "name": "actorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the actor to target" + }, + { + "name": "Authorization", + "in": "header", + "required": false, + "schema": { + "type": "string" + }, + "description": "Bearer token for inspector authentication. Required in production, optional in development." + } + ], + "requestBody": { + "required": false, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "entryId": { + "type": "string" + } + } + } + } + } + }, + "responses": { + "200": { + "description": "Workflow history after scheduling a replay", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "history": {}, + "isWorkflowEnabled": { + "type": "boolean" + } + } + } + } + } + }, + "401": { + "description": "Unauthorized" + } + } + } + }, + "/gateway/{actorId}/inspector/summary": { + "get": { + "parameters": [ + { + "name": "actorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the actor to target" + }, + { + "name": "Authorization", + "in": "header", + "required": false, + "schema": { + "type": "string" + }, + "description": "Bearer token for inspector authentication. Required in production, optional in development." + } + ], + "responses": { + "200": { + "description": "Full actor inspector summary", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "state": {}, + "connections": { + "type": "array", + "items": { + "type": "object" + } + }, + "rpcs": { + "type": "object" + }, + "queueSize": { + "type": "integer" + }, + "isStateEnabled": { + "type": "boolean" + }, + "isDatabaseEnabled": { + "type": "boolean" + }, + "isWorkflowEnabled": { + "type": "boolean" + }, + "workflowHistory": {} + } + } + } + } + }, + "401": { + "description": "Unauthorized" + } + } + } + } + } +} \ No newline at end of file diff --git a/.agents/skills/multiplayer-game/reference/actors/access-control.md b/.agents/skills/multiplayer-game/reference/actors/access-control.md new file mode 100644 index 0000000..02dc4e8 --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/actors/access-control.md @@ -0,0 +1,48 @@ +# Access Control + +> Source: `src/content/docs/actors/access-control.mdx` +> Canonical URL: https://rivet.dev/docs/actors/access-control +> Description: Authorize actions, queue publishes, and event subscriptions with explicit hooks. + +--- +Use access control to decide what authenticated clients are allowed to do. + +This is authorization, not authentication: + +- Use [authentication](/docs/actors/authentication) to identify who is calling. +- Use access-control rules to decide what they can do after connecting. + +## Permission Surfaces + +RivetKit authorization is explicit per surface: + +- `onBeforeConnect` rejects unauthenticated or malformed connections. +- Action handlers (`actions.*`) enforce action permissions. +- `queues..canPublish` allows or denies inbound queue publishes. +- `events..canSubscribe` allows or denies event subscriptions. + +## Fail By Default + +Use deny-by-default rules everywhere: + +1. Keep `onBeforeConnect` strict and reject invalid credentials. +2. In each action, explicitly allow expected roles and throw `forbidden` otherwise. +3. In `canPublish` and `canSubscribe`, return `true` only for allowed roles and end with `return false`. + +## Return Value Contract + +`canPublish` and `canSubscribe` must return a boolean: + +- `true`: allow +- `false`: deny with `forbidden` + +Returning `undefined`, `null`, or any non-boolean throws an internal error. + +## Notes + +- `canPublish` only applies to queue names defined in `queues`. +- Incoming queue messages for undefined queues are ignored and the publish succeeds as completed. +- `canSubscribe` only applies to event names defined in `events`. +- Broadcasting an event not defined in `events` still publishes to subscribers. + +_Source doc path: /docs/actors/access-control_ diff --git a/.agents/skills/multiplayer-game/reference/actors/actions.md b/.agents/skills/multiplayer-game/reference/actors/actions.md new file mode 100644 index 0000000..d00324e --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/actors/actions.md @@ -0,0 +1,98 @@ +# Actions + +> Source: `src/content/docs/actors/actions.mdx` +> Canonical URL: https://rivet.dev/docs/actors/actions +> Description: Actions are how your backend, frontend, or other actors can communicate with actors. + +--- +Actions are very lightweight. They can be called thousands of times per second safely. Actions are executed via HTTP requests or via WebSockets if [using `.connect()`](/docs/actors/connections). + +For advanced use cases that require direct access to HTTP requests or WebSocket connections, see [raw HTTP and WebSocket handling](/docs/actors/fetch-and-websocket-handler). + +By default, actions run in parallel. If you need advanced control over concurrency, use [queues](/docs/actors/queues). + +## Writing Actions + +Actions are defined in the `actions` object when creating an actor: + +Each action receives a context object (commonly named `c`) as its first parameter, which provides access to state, connections, and other utilities. Additional parameters follow after that. + +## Calling Actions + +Actions can be called in different ways depending on your use case: + +### Frontend (createClient) + +Learn more about [communicating with actors from the frontend](/docs/actors/communicating-between-actors). + +### Backend (registry.handler) + +Learn more about [communicating with actors from the backend](/docs/actors/communicating-between-actors). + +### Actor-to-Actor (c.client()) + +Learn more about [communicating between actors](/docs/actors/communicating-between-actors). + +Calling actions from the client are async and require an `await`, even if the action itself is not async. + +### Type Safety + +The actor client includes type safety out of the box. When you use `createClient()`, TypeScript automatically infers action parameter and return types: + +## Error Handling + +Actors provide robust error handling out of the box for actions. + +### User Errors + +`UserError` can be used to return rich error data to the client. You can provide: + +- A human-readable message +- A machine-readable code that's useful for matching errors in a try-catch (optional) +- A metadata object for providing richer error context (optional) + +For example: + +### Internal Errors + +All other errors will return an error with the code `internal_error` to the client. This helps keep your application secure, as errors can sometimes expose sensitive information. + +## Schema Validation + +If passing data to an actor from the frontend, use a library like [Zod](https://zod.dev/) to validate input data. + +For example, to validate action parameters: + +## Streaming Data + +Actions have a single return value. To stream realtime data in response to an action, use [events](/docs/actors/events). + +## Canceling Long-Running Actions + +For operations that should be cancelable on-demand, create your own `AbortController`. Chain it with `c.abortSignal` so actor shutdown also cancels the operation. + +See [Actor Shutdown Abort Signal](/docs/actors/lifecycle#actor-shutdown-abort-signal) for automatically canceling operations when the actor stops. + +## Using `ActionContext` Externally + +When writing complex logic for actions, you may want to extract parts of your implementation into separate helper functions. When doing this, you'll need a way to properly type the context parameter. + +Rivet provides the `ActionContextOf` utility type for exactly this purpose: + +See [types](/docs/actors/types) for more details on using `ActionContextOf` and other utility types. + +## Debugging + +- `GET /inspector/rpcs` lists all available actions on an actor. +- `POST /inspector/action/:name` executes an action with JSON args and returns output. +- In non-dev mode, inspector endpoints require authorization. + +## API Reference + +- [`Actions`](/typedoc/interfaces/rivetkit.mod.Actions.html) - Interface for defining actions +- [`ActionContext`](/typedoc/interfaces/rivetkit.mod.ActionContext.html) - Context available in action handlers +- [`ActorDefinition`](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html) - Interface for defining actors with actions +- [`ActorHandle`](/typedoc/types/rivetkit.client_mod.ActorHandle.html) - Handle for calling actions from client +- [`ActorActionFunction`](/typedoc/types/rivetkit.client_mod.ActorActionFunction.html) - Type for action functions + +_Source doc path: /docs/actors/actions_ diff --git a/.agents/skills/multiplayer-game/reference/actors/actor-runtime-socket.md b/.agents/skills/multiplayer-game/reference/actors/actor-runtime-socket.md new file mode 100644 index 0000000..457472c --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/actors/actor-runtime-socket.md @@ -0,0 +1,62 @@ +# Actor Runtime Socket + +> Source: `src/content/docs/actors/actor-runtime-socket.mdx` +> Canonical URL: https://rivet.dev/docs/actors/actor-runtime-socket +> Description: Access an actor's SQLite database from another local process. + +--- +The Actor Runtime Socket is an advanced Beta API for integrating Rivet Actors with external software without sacrificing performance. It currently exposes SQLite over Unix domain sockets in the native runtime. + +For example, an agentOS agent can use the socket to work with its actor's SQLite database. Software orchestrated by an actor, such as another process or container, can use it to access the actor's runtime resources. Like the Chrome DevTools Protocol exposes a browser to external tools, the Actor Runtime Socket exposes selected actor capabilities to colocated software. + +## Quickstart + +Enable the socket, call `c.actorRuntimeSocket()` to get its path, and pass that path to your process: + +```ts actor.ts +import { spawn } from "node:child_process"; +import { actor } from "rivetkit"; +import { db } from "rivetkit/db"; + +export const example = actor({ + options: { enableActorRuntimeSocket: true }, + db: db({ + onMigrate: (db) => + db.execute("CREATE TABLE IF NOT EXISTS items (value TEXT)"), + }), + actions: { + startWorker: async (c) => { + // Get the Unix socket path external processes use + // to communicate with the actor. + // (Example: /tmp/rivet-actor-runtime.abc/def.sock) + const { path } = await c.actorRuntimeSocket(); + + // Spawn a process that can access the actor's + // SQLite database over the socket. + spawn(process.execPath, ["worker.js"], { + env: { ...process.env, ACTOR_RUNTIME_SOCKET_PATH: path }, + }); + }, + }, +}); +``` + +```ts worker.ts +import { connectRuntimeSocket } from "./generated/runtime-socket-client"; + +const db = await connectRuntimeSocket(process.env.ACTOR_RUNTIME_SOCKET_PATH!); +const rows = await db.query("SELECT value FROM items", []); +console.log(rows); +``` + +The path is valid only for the current actor generation, so get it again after the actor restarts or wakes. Only share it with trusted local processes. + +## Protocol + +The [protocol schema](https://github.com/rivet-dev/rivet/blob/main/engine/sdks/rust/actor-runtime-socket-protocol/schemas/v1.bare) defines the handshake, SQLite requests, values, responses, and errors. Generate codecs using vbare's [TypeScript](https://github.com/rivet-dev/vbare/tree/main/typescript) or [Rust](https://github.com/rivet-dev/vbare/tree/main/rust) tooling, then add a small Unix socket wrapper for its length-prefixed frames. `connectRuntimeSocket` above represents that application-specific wrapper. + +## SQLite transaction interleaving + +Separate socket requests can interleave with SQLite work from the main actor. For multi-request atomic work, begin a transaction with a client-generated `leaseKey`, include that key with each query, and commit or roll back with the same key. Other actor and socket SQL waits until that transaction finishes. + +_Source doc path: /docs/actors/actor-runtime-socket_ diff --git a/.agents/skills/multiplayer-game/reference/actors/appearance.md b/.agents/skills/multiplayer-game/reference/actors/appearance.md new file mode 100644 index 0000000..f60b1a1 --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/actors/appearance.md @@ -0,0 +1,50 @@ +# Icons & Names + +> Source: `src/content/docs/actors/appearance.mdx` +> Canonical URL: https://rivet.dev/docs/actors/appearance +> Description: Customize actors with display names and icons for the Rivet inspector and dashboard. + +--- +# Icons & Names + +Actors can be customized with a display name and icon that appear in the Rivet inspector & dashboard. This helps identify actors at a glance when managing your application. + +## Configuration + +Set the `name` and `icon` properties in your actor's `options`: + +## Icon Formats + +The `icon` property accepts two formats: + +### Emoji + +Use any emoji character directly: + +### FontAwesome Icons + +Use [FontAwesome](https://fontawesome.com/search) icon names without the "fa" prefix: + +## Default Behavior + +If no `icon` is specified, actors display the default actor icon. If no `name` is specified, the actor's registry key (e.g., `chatRoom`, `gameServer`) is displayed instead. + +## Examples + +Here are some common patterns: + +## Advanced: Run Handler Metadata + +For library developers creating reusable run handlers, you can bundle icon and name metadata directly with the `run` property. This allows libraries to provide sensible defaults without requiring users to configure them manually. + +Instead of returning a function from your run handler factory, return an object with `name`, `icon`, and `run`: + +Users can then use this directly: + +This run-handler metadata is currently applied through the registry and serverless metadata paths. The native runtime and inspector config read the actor's `options.name` and `options.icon` directly, so set those explicitly if you need the name or icon to appear everywhere. + +Actor-level `options.name` and `options.icon` always take precedence, allowing users to override library defaults: + +The built-in `workflow()` helper uses this pattern to automatically display the workflow icon for workflow-based actors. + +_Source doc path: /docs/actors/appearance_ diff --git a/.agents/skills/multiplayer-game/reference/actors/authentication.md b/.agents/skills/multiplayer-game/reference/actors/authentication.md new file mode 100644 index 0000000..c456dde --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/actors/authentication.md @@ -0,0 +1,105 @@ +# Authentication + +> Source: `src/content/docs/actors/authentication.mdx` +> Canonical URL: https://rivet.dev/docs/actors/authentication +> Description: Secure your actors with authentication and authorization. + +--- +## Do You Need Authentication? + +### Rivet Cloud + + Actors are private by default on Rivet Cloud. Only requests with the publishable token can interact with actors. + + - **Backend-only actors**: If your publishable token is only included in your backend, then authentication is not necessary. + - **Frontend-accessible actors**: If your publishable token is included in your frontend, then implementing authentication is recommended. + +### Self-Hosted + + Actors are public by default on self-hosted Rivet. Anyone can access them without a token. + + - **Only accessible within private network**: If Rivet is only accessible within your private network, then authentication is not necessary. + - **Rivet exposed to the public internet**: If Rivet is configured to accept traffic from the public internet, then implementing authentication is recommended. + +## Authentication Connections + +Authentication is configured through either: + +- `onBeforeConnect` for simple pass/fail validation +- `createConnState` when you need to access user data in your actions via `c.conn.state` + +## Access Control + +After a connection is authenticated, use [Access Control](/docs/actors/access-control) to enforce authorization: + +- Check permissions in action handlers. +- Use `queues..canPublish` to gate inbound queue publishes. +- Use `events..canSubscribe` to gate event subscriptions. + +### `onBeforeConnect` + +The `onBeforeConnect` hook validates credentials before allowing a connection. Throw an error to reject the connection. + +### `createConnState` + +Use `createConnState` to extract user data from credentials and store it in connection state. This data is accessible in actions via `c.conn.state`. Like `onBeforeConnect`, throwing an error will reject the connection. See [connections](/docs/actors/connections) for more details. + +## Available Auth Data + +Authentication hooks have access to several properties: + +| Property | Description | +|----------|-------------| +| `params` | Custom data passed by the client when connecting (see [connection params](/docs/actors/connections#extracting-data-from-connection-params)) | +| `c.request` | The underlying HTTP request object | +| `c.request.headers` | Request headers for tokens, API keys (does not work for `.connect()`) | +| `c.state` | Actor state for authorization decisions (see [state](/docs/actors/state)) | +| `c.key` | The actor's key (see [keys](/docs/actors/keys)) | + +It's recommended to use `params` instead of `c.request.headers` whenever possible since it works for both HTTP & WebSocket connections. + +## Client Usage + +### Passing Credentials + +Pass authentication data when connecting. Use `getParams` when you need a fresh JWT for every connection or reconnect: + +### Handling Errors + +Authentication errors use the same system as regular errors. See [errors](/docs/actors/errors) for more details. + +## Examples + +### JWT + +Validate JSON Web Tokens and extract user claims: + +### External Auth Provider + +Validate credentials against an external authentication service: + +### Using `c.state` In Authorization + +Access actor state via `c.state` and the actor's key via `c.key` to make authorization decisions: + +### Role-Based Access Control + +Create helper functions for common authorization patterns: + +### Rate Limiting + +Use `c.vars` to track connection attempts and rate limit by user: + +The limits in this example are [ephemeral](/docs/actors/state#ephemeral-variables). If you wish to persist rate limits, you can optionally replace `vars` with `state`. + +### Caching Tokens + +Cache validated tokens in `c.vars` to avoid redundant validation on repeated connections. See [ephemeral variables](/docs/actors/state#ephemeral-variables) for more details. + +## API Reference + +- [`AuthIntent`](/typedoc/types/rivetkit.mod.AuthIntent.html) - Authentication intent type +- [`OnBeforeConnectContext`](/typedoc/interfaces/rivetkit.mod.OnBeforeConnectContext.html) - Context for auth checks +- [`OnConnectContext`](/typedoc/interfaces/rivetkit.mod.OnConnectContext.html) - Context after connection + +_Source doc path: /docs/actors/authentication_ diff --git a/.agents/skills/multiplayer-game/reference/actors/communicating-between-actors.md b/.agents/skills/multiplayer-game/reference/actors/communicating-between-actors.md new file mode 100644 index 0000000..8aeaf39 --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/actors/communicating-between-actors.md @@ -0,0 +1,42 @@ +# Communicating Between Actors + +> Source: `src/content/docs/actors/communicating-between-actors.mdx` +> Canonical URL: https://rivet.dev/docs/actors/communicating-between-actors +> Description: Learn how actors can call other actors and share data + +--- +Actors can communicate with each other using the server-side actor client, enabling complex workflows and data sharing between different actor instances. + +We recommend reading the [clients documentation](/docs/clients) first. This guide focuses specifically on communication between actors. + +## Using the Server-Side Actor Client + +The server-side actor client allows actors to call other actors within the same registry. Access it via `c.client()` in your actor context: + +If two actors call each other and their return types are inferred from the other actor's response, you may hit circular type errors (`TS2322`, `TS2722`, or `c.state` becoming `unknown`). Fix this by writing explicit return types on those actions. + +## Use Cases and Patterns + +### Actor Orchestration + +Use a coordinator actor to manage complex workflows: + +### Data Aggregation + +Collect data from multiple actors: + +### Event-Driven Architecture + +Use connections to listen for events from other actors: + +### Batch Operations + +Process multiple items in parallel: + +## API Reference + +- [`ActorHandle`](/typedoc/types/rivetkit.client_mod.ActorHandle.html) - Handle for calling other actors +- [`Client`](/typedoc/types/rivetkit.mod.Client.html) - Client type for actor communication +- [`ActorAccessor`](/typedoc/interfaces/rivetkit.client_mod.ActorAccessor.html) - Accessor for getting actor handles + +_Source doc path: /docs/actors/communicating-between-actors_ diff --git a/.agents/skills/multiplayer-game/reference/actors/connections.md b/.agents/skills/multiplayer-game/reference/actors/connections.md new file mode 100644 index 0000000..e5f0384 --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/actors/connections.md @@ -0,0 +1,119 @@ +# Connections + +> Source: `src/content/docs/actors/connections.mdx` +> Canonical URL: https://rivet.dev/docs/actors/connections +> Description: Connections represent client connections to your actor. They provide a way to handle client authentication, manage connection-specific data, and control the connection lifecycle. + +--- +For documentation on connecting to actors from clients, see the [Clients documentation](/docs/clients). + +## Parameters + +When clients connect to an actor, they can pass connection parameters that are handled during the connection process. Use `params` for static values, or `getParams` when you need fresh connection data each time a connection opens. + +For example: + +## Connection State + +There are two ways to define an actor's connection state: + + +### connState + + Define connection state as a constant value: + + + + This value will be cloned for every new connection using `structuredClone`. + + + +### createConnState + + Create connection state dynamically with a function called for each connection: + + + + +## Connection Lifecycle + +Each client connection goes through a series of lifecycle hooks that allow you to validate, initialize, and clean up connection-specific resources. + +**On Connect** (per client) + +- `onBeforeConnect` +- `createConnState` +- `onConnect` + +Pending connections are not visible in `c.conns` while `onBeforeConnect` or `createConnState` is running. RivetKit adds the connection to `c.conns` after those hooks succeed and before `onConnect` runs. + +**On Disconnect** (per client) + +- `onDisconnect` + +### `createConnState` and `connState` + +[API Reference](/typedoc/interfaces/rivetkit.mod.CreateConnStateContext.html) + +There are two ways to define the initial state for connections: +1. `connState`: Define a constant object that will be used as the initial state for all connections +2. `createConnState`: A function that dynamically creates initial connection state based on connection parameters. Can be async. + +Connections are not visible in `c.conns` until `createConnState` completes successfully. + +### `onBeforeConnect` + +[API Reference](/typedoc/interfaces/rivetkit.mod.OnBeforeConnectContext.html) + +The `onBeforeConnect` hook is called whenever a new client connects to the actor. Can be async. Clients can pass parameters when connecting, accessible via `params`. This hook is used for connection validation and can throw errors to reject connections. + +The `onBeforeConnect` hook does NOT return connection state - it's used solely for validation. + +Connections are not visible in `c.conns` while `onBeforeConnect` is running. + +Connections cannot interact with the actor until this method completes successfully. Throwing an error will abort the connection. This can be used for authentication, see [Authentication](/docs/actors/authentication) for details. + +### `onConnect` + +[API Reference](/typedoc/interfaces/rivetkit.mod.OnConnectContext.html) + +Executed after the client has successfully connected. Can be async. Receives the connection object as a second parameter. + +By the time `onConnect` runs, the connection is visible in `c.conns`. + +Messages will not be processed for this actor until this hook succeeds. Errors thrown from this hook will cause the client to disconnect. + +### `onDisconnect` + +[API Reference](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html) + +Called when a client disconnects from the actor. Can be async. Receives the connection object as a second parameter. Use this to clean up any connection-specific resources. + +## Connection List + +All active connections can be accessed through the context object's `conns` property. This is a `Map` of all current connections, keyed by connection ID. + +This is frequently used with `conn.send(name, event)` to send messages directly to clients. To send an event to all connections at once, use `c.broadcast()` instead. See [Events](/docs/actors/events) for more details on broadcasting. + +For example: + +`conn.send()` has no effect on [low-level WebSocket connections](/docs/actors/websocket-handler). For low-level WebSockets, use the WebSocket API directly (e.g., `websocket.send()`). + +## Disconnecting clients + +Connections can be disconnected from within an action: + +If you need to wait for the disconnection to complete, you can use `await`: + +This ensures the underlying network connections close cleanly before continuing. + +## API Reference + +- [`Conn`](/typedoc/interfaces/rivetkit.mod.Conn.html) - Connection interface +- [`ConnInitContext`](/typedoc/interfaces/rivetkit.mod.ConnInitContext.html) - Connection initialization context +- [`CreateConnStateContext`](/typedoc/interfaces/rivetkit.mod.CreateConnStateContext.html) - Context for creating connection state +- [`OnBeforeConnectContext`](/typedoc/interfaces/rivetkit.mod.OnBeforeConnectContext.html) - Pre-connection lifecycle hook context +- [`OnConnectContext`](/typedoc/interfaces/rivetkit.mod.OnConnectContext.html) - Post-connection lifecycle hook context +- [`ActorConn`](/typedoc/types/rivetkit.client_mod.ActorConn.html) - Typed connection from client side + +_Source doc path: /docs/actors/connections_ diff --git a/.agents/skills/multiplayer-game/reference/actors/debugging.md b/.agents/skills/multiplayer-game/reference/actors/debugging.md new file mode 100644 index 0000000..409b381 --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/actors/debugging.md @@ -0,0 +1,644 @@ +# Debugging + +> Source: `src/content/docs/actors/debugging.mdx` +> Canonical URL: https://rivet.dev/docs/actors/debugging +> Description: Inspect and debug running Rivet Actors, runners, and provider configs using management, runner, and actor inspector HTTP APIs. + +--- +## Connecting to Rivet + +All debugging endpoints in this guide are available both locally and in production. In local development, the base URL is `http://localhost:6420` with no authentication. In production (Rivet Cloud or self-hosted), you connect to your Rivet Engine endpoint with a token. + +### Setup + +All examples in this guide use these shell variables. Extract them from your `RIVET_ENDPOINT` (`https://:@`): + +```bash +# From RIVET_ENDPOINT=https://my-namespace:sk_abc123@api.rivet.dev +export RIVET_API="https://api.rivet.dev" +export RIVET_NAMESPACE="my-namespace" +export RIVET_TOKEN="sk_abc123" + +# For local development: +# export RIVET_API="http://localhost:6420" +``` + +Rivet Cloud issues two token types: `sk_` (secret key, server-side only) and `pk_` (public key, client-safe). For debugging, always use `sk_`. See [Endpoints](/docs/general/endpoints) for more details. + +## Management API + +The management API runs on the manager base path (default root path) and is used to list, create, and look up actors. + +### Authentication + +| Environment | Authentication | +|---|---| +| **Local development** | No authentication required. All endpoints are accessible without tokens. | +| **Self-hosted engine** | Set `RIVET_TOKEN` to enable authenticated access to restricted endpoints like KV. | +| **Rivet Cloud** | Authentication is enforced by your deployment entrypoint. For manager KV access, use the bearer token header below when enabled. | + +Restricted endpoints (like KV reads) require the `Authorization: Bearer` header when `RIVET_TOKEN` is configured: + +```bash +curl "$RIVET_API/actors/{actor_id}/kv/keys/{base64_key}" \ + -H "Authorization: Bearer $RIVET_TOKEN" +``` + +### List Actors + +```bash +# List all actors with a given name +curl "$RIVET_API/actors?name=my-actor&namespace=$RIVET_NAMESPACE" \ + -H "Authorization: Bearer $RIVET_TOKEN" + +# Look up one actor by key (name is required when key is provided) +curl "$RIVET_API/actors?name=my-actor&key=%5B%22my-key%22%5D&namespace=$RIVET_NAMESPACE" \ + -H "Authorization: Bearer $RIVET_TOKEN" + +# List actors by IDs (comma-separated) +curl "$RIVET_API/actors?actor_ids=id1,id2&namespace=$RIVET_NAMESPACE" \ + -H "Authorization: Bearer $RIVET_TOKEN" +``` + +Rules: + +- `key` requires `name`. +- `actor_ids` cannot be combined with `name` or `key`. + +Returns: + +```json +{ + "actors": [ + { + "actor_id": "abc123", + "name": "my-actor", + "key": "[\"default\"]", + "namespace_id": "default", + "create_ts": 1706000000000 + } + ] +} +``` + +### Create Actor + +`POST /actors` creates a new actor. + +```bash +curl -X POST "$RIVET_API/actors?namespace=$RIVET_NAMESPACE" \ + -H "Authorization: Bearer $RIVET_TOKEN" \ + -H 'Content-Type: application/json' \ + -d '{ + "name": "my-actor", + "runner_name_selector": "default", + "crash_policy": "restart" + }' +``` + +### Create or Get Actor + +`PUT /actors` creates an actor if it does not exist, otherwise returns the existing one. + +```bash +curl -X PUT "$RIVET_API/actors?namespace=$RIVET_NAMESPACE" \ + -H "Authorization: Bearer $RIVET_TOKEN" \ + -H 'Content-Type: application/json' \ + -d '{ + "name": "my-actor", + "key": "[\"default\"]", + "runner_name_selector": "default", + "crash_policy": "restart" + }' +``` + +Returns the actor object with its `actor_id`. + +### List Actor Names + +```bash +curl "$RIVET_API/actors/names?namespace=$RIVET_NAMESPACE" \ + -H "Authorization: Bearer $RIVET_TOKEN" +``` + +Returns all registered actor names and their metadata. + +### Read Actor KV + +Requires authentication (see above). + +```bash +curl "$RIVET_API/actors/{actor_id}/kv/keys/{base64_key}" \ + -H "Authorization: Bearer $RIVET_TOKEN" +``` + +Returns the value stored at the given key in the actor KV compatibility snapshot. After an actor has migrated to SQLite-backed runtime storage, this endpoint is stale for user KV and internal runtime records; it continues to expose the frozen pre-migration KV data. The inspector token key is the exception and remains mirrored for dashboard compatibility. + +See the [OpenAPI spec](https://github.com/rivet-dev/rivet/tree/main/rivetkit-openapi) for the full schema of all management endpoints. + +## Runner API + +Use the runner endpoints to debug scheduler capacity and provider configuration (for example serverless URL, headers, and limits) through the Rivet API. + +### List Runner Names + +```bash +curl "$RIVET_API/runners/names?namespace=$RIVET_NAMESPACE" \ + -H "Authorization: Bearer $RIVET_TOKEN" +``` + +Returns the runner pools available in the namespace: + +```json +{ + "names": ["default", "gpu-workers"], + "pagination": { "cursor": null } +} +``` + +### List Runners in a Pool + +```bash +curl "$RIVET_API/runners?namespace=$RIVET_NAMESPACE&name=default&include_stopped=true&limit=100" \ + -H "Authorization: Bearer $RIVET_TOKEN" +``` + +Useful fields when debugging: + +- `remaining_slots` / `total_slots` for capacity. +- `drain_ts` and `stop_ts` for shutdown behavior. +- `last_ping_ts` and `last_connected_ts` for connectivity. + +### Inspect Provider Config (Runner Config) + +```bash +curl "$RIVET_API/runner-configs?namespace=$RIVET_NAMESPACE&runner_name=default" \ + -H "Authorization: Bearer $RIVET_TOKEN" +``` + +Returns the configured provider settings per datacenter and the latest pool error (if any): + +```json +{ + "runner_configs": { + "default": { + "datacenters": { + "dc-1": { + "serverless": { + "url": "https://your-deployment.example.com/rivet", + "headers": { "Authorization": "Bearer token" }, + "request_lifespan": 55, + "slots_per_runner": 1, + "max_runners": 10 + }, + "runner_pool_error": null + } + } + } + }, + "pagination": { "cursor": null } +} +``` + +`runner_pool_error` mirrors actor scheduling errors such as `serverless_http_error`, `serverless_connection_error`, and `serverless_stream_ended_early`. + +### Check Serverless Provider Health + +Use this to test whether Rivet can reach your serverless provider URL and read runner metadata: + +```bash +curl -X POST "$RIVET_API/runner-configs/serverless-health-check?namespace=$RIVET_NAMESPACE" \ + -H "Authorization: Bearer $RIVET_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "url": "https://your-deployment.example.com/rivet", + "headers": { + "Authorization": "Bearer token" + } + }' +``` + +Possible responses: + +```json +{ "success": { "version": "1.2.3" } } +``` + +```json +{ + "failure": { + "error": { + "message": "non-success status from metadata endpoint", + "details": "received status 503" + } + } +} +``` + +### Refresh Provider Metadata + +If you deploy new actor code or routes and metadata has not updated yet, force a refresh: + +```bash +curl -X POST "$RIVET_API/runner-configs/default/refresh-metadata?namespace=$RIVET_NAMESPACE" \ + -H "Authorization: Bearer $RIVET_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{}' +``` + +## Actor API + +All actor-level endpoints are accessed through the gateway. The gateway routes requests to the correct actor instance using the actor ID in the URL path: + +``` +{RIVET_API}/gateway/{actor_id}/{path} +``` + +The gateway only accepts actor IDs, not names. Use `GET /actors?name=...` from the management API to look up actor IDs first. + +### Authentication + +Standard actor endpoints (health, actions, requests) and inspector endpoints have separate authentication requirements. + +#### Standard Endpoints + +| Environment | Authentication | +|---|---| +| **Local development** | No authentication required. | +| **Self-hosted engine** | The Rivet Engine handles authentication at the gateway level. | +| **Rivet Cloud** | Authentication is handled by the Rivet Cloud platform at the gateway level. | + +#### Inspector Endpoints + +Each actor generates a unique inspector token on first start and persists it in internal SQLite storage. It is also mirrored to legacy KV key `0x03` (base64 `Aw==`) for dashboard compatibility. Pass it as a bearer token in the `Authorization` header. + +Inspector endpoints always require the actor's inspector token, including in local development. There is no local-development bypass. + +| Environment | Authentication | +|---|---| +| **Local development** | Bearer the actor's inspector token in the `Authorization` header. Fetch it through the management KV endpoint (see below). | +| **Self-hosted engine** | Bearer the actor's inspector token in the `Authorization` header. The Rivet dashboard fetches it automatically; for direct API access, fetch it through the management KV endpoint (see below). | +| **Rivet Cloud** | Bearer the actor's inspector token in the `Authorization` header. The Rivet dashboard fetches it automatically; for direct API access, fetch it through the management KV endpoint (see below). | + +```bash +curl "$RIVET_API/gateway/{actor_id}/inspector/summary" \ + -H 'Authorization: Bearer YOUR_INSPECTOR_TOKEN' +``` + +#### Retrieving the Inspector Token + +Each actor generates a unique inspector token on first start and persists it in internal SQLite storage. The token is also mirrored to the legacy KV key below so the Rivet dashboard can continue to retrieve it through the management KV endpoint. This applies in every environment, including local development. + +The inspector token is stored at internal KV key `0x03` (base64: `Aw==`). The response value is also base64-encoded. + +```bash +# Fetch the inspector token for a specific actor +ACTOR_ID="your-actor-id" + +RESPONSE=$(curl -s "$RIVET_API/actors/$ACTOR_ID/kv/keys/Aw==" \ + -H "Authorization: Bearer $RIVET_TOKEN") + +# Extract and decode the base64 value +INSPECTOR_TOKEN=$(echo "$RESPONSE" | jq -r '.value' | base64 -d) + +# Use it to call inspector endpoints +curl "$RIVET_API/gateway/$ACTOR_ID/inspector/summary" \ + -H "Authorization: Bearer $INSPECTOR_TOKEN" +``` + +### Standard Actor Endpoints + +These are the built-in actor endpoints available through the gateway: + +```bash +# Health check +curl $RIVET_API/gateway/{actor_id}/health + +# Metadata +curl $RIVET_API/gateway/{actor_id}/metadata + +# Call an action +curl -X POST $RIVET_API/gateway/{actor_id}/action/myAction \ + -H 'Content-Type: application/json' \ + -d '{"args": [1, 2, 3]}' + +# Send queue message (queue name in path) +curl -X POST $RIVET_API/gateway/{actor_id}/queue/jobs \ + -H 'Content-Type: application/json' \ + -d '{"body":{"id":"job-1"}}' + +# Send queue message and wait for completion (optional timeout in ms) +curl -X POST $RIVET_API/gateway/{actor_id}/queue/jobs \ + -H 'Content-Type: application/json' \ + -d '{"body":{"id":"job-1"},"wait":true,"timeout":5000}' + +# Forward an HTTP request to the actor's onRequest handler +curl $RIVET_API/gateway/{actor_id}/request/my/custom/path +``` + +Queue send responses always include a `status` field: + +```json +{ "status": "completed" } +``` + +The `response` field is only present when the queue handler returns a value: + +```json +{ "status": "completed", "response": { "result": "ok" } } +``` + +If `wait: true` and the timeout is reached, `status` is `"timedOut"`. + +### Inspector Endpoints + +The inspector HTTP API exposes JSON endpoints for querying and modifying actor internals at runtime. These are designed for agent-based debugging and tooling. + +Every inspector endpoint requires the actor's inspector token as a bearer token, including in local development. The examples below omit the `Authorization` header for brevity, but you must add `-H "Authorization: Bearer $INSPECTOR_TOKEN"` to each request. See [Retrieving the Inspector Token](#retrieving-the-inspector-token) above. + +#### Get State + +```bash +curl $RIVET_API/gateway/{actor_id}/inspector/state +``` + +Returns the actor's current persisted state: + +```json +{ + "state": { "count": 42, "users": [] }, + "isStateEnabled": true +} +``` + +#### Set State + +```bash +curl -X PATCH $RIVET_API/gateway/{actor_id}/inspector/state \ + -H 'Content-Type: application/json' \ + -d '{"state": {"count": 0, "users": []}}' +``` + +Returns: + +```json +{ "ok": true } +``` + +#### Get Connections + +```bash +curl $RIVET_API/gateway/{actor_id}/inspector/connections +``` + +Returns all active connections with their params, state, and metadata: + +```json +{ + "connections": [ + { + "type": "websocket", + "id": "conn-id", + "details": { + "type": "websocket", + "params": {}, + "stateEnabled": true, + "state": {}, + "subscriptions": 2, + "isHibernatable": true + } + } + ] +} +``` + +#### Get RPCs + +```bash +curl $RIVET_API/gateway/{actor_id}/inspector/rpcs +``` + +Returns a list of available actions: + +```json +{ "rpcs": ["increment", "getCount"] } +``` + +#### Execute Action + +```bash +curl -X POST $RIVET_API/gateway/{actor_id}/inspector/action/increment \ + -H 'Content-Type: application/json' \ + -d '{"args": [5]}' +``` + +Returns: + +```json +{ "output": 47 } +``` + +#### Get Queue Status + +```bash +curl $RIVET_API/gateway/{actor_id}/inspector/queue?limit=10 +``` + +Returns queue status with messages: + +```json +{ + "size": 3, + "maxSize": 1000, + "truncated": false, + "messages": [ + { "id": 1, "name": "process", "createdAtMs": 1706000000000 } + ] +} +``` + +#### Get Workflow History + +```bash +curl $RIVET_API/gateway/{actor_id}/inspector/workflow-history +``` + +Returns: + +```json +{ + "history": null, + "isWorkflowEnabled": false +} +``` + +#### Get Database Schema + +```bash +curl $RIVET_API/gateway/{actor_id}/inspector/database/schema +``` + +Returns discovered SQLite tables and views when the actor has `c.db` enabled: + +```json +{ + "schema": { + "tables": [ + { + "table": { "schema": "main", "name": "test_data", "type": "table" }, + "columns": [ + { "cid": 0, "name": "id", "type": "", "notnull": 0, "dflt_value": null, "pk": 0 } + ], + "foreignKeys": [], + "records": 2 + } + ] + } +} +``` + +#### Get Database Rows + +```bash +curl "$RIVET_API/gateway/{actor_id}/inspector/database/rows?table=test_data&limit=100&offset=0" +``` + +Returns paged rows for a specific SQLite table or view: + +```json +{ + "rows": [ + { + "id": 1, + "value": "Alice", + "payload": "", + "created_at": 1706000000000 + } + ] +} +``` + +#### Execute SQLite + +Run manual SQL against an actor's SQLite database. This supports both read-only queries and mutations. + +```bash +curl -X POST http://localhost:6420/gateway/{actor_id}/inspector/database/execute \ + -H 'Content-Type: application/json' \ + -d '{ + "sql": "SELECT id, value FROM test_data WHERE value = ? ORDER BY id DESC", + "args": ["alpha"] + }' +``` + +Returns: + +```json +{ + "rows": [ + { "id": 2, "value": "alpha" } + ] +} +``` + +You can also use named SQLite bindings through a `properties` object: + +```bash +curl -X POST http://localhost:6420/gateway/{actor_id}/inspector/database/execute \ + -H 'Content-Type: application/json' \ + -d '{ + "sql": "SELECT id, value FROM test_data WHERE value = :value ORDER BY id DESC", + "properties": { + "value": "alpha" + } + }' +``` + +For mutations, use `RETURNING` if you want rows back. Otherwise the statement still runs and `rows` is empty: + +```bash +curl -X POST http://localhost:6420/gateway/{actor_id}/inspector/database/execute \ + -H 'Content-Type: application/json' \ + -d '{ + "sql": "INSERT INTO test_data (value, created_at) VALUES (?, ?) RETURNING id, value", + "args": ["beta", 1706000000000] + }' +``` + +For workflow-enabled actors, `history` is a JSON object with `nameRegistry`, `entries`, and `entryMetadata`. Step outputs, loop state, and message payloads are decoded from CBOR into normal JSON values. + +#### Replay Workflow From Step + +Reset a workflow to a specific step and restart execution immediately. Omitting `entryId` replays the workflow from the beginning. + +If the workflow is still running when you call replay, the endpoint rejects the request with `409 Conflict` and an `actor/workflow_in_flight` error instead of cancelling the live run for you. + +```bash +curl -X POST http://localhost:6420/gateway/{actor_id}/inspector/workflow/replay \ + -H 'Content-Type: application/json' \ + -d '{"entryId":"workflow-step-id"}' +``` + +Returns the same JSON shape as `/inspector/workflow-history`: + +```json +{ + "history": { + "nameRegistry": ["step-one", "step-two"], + "entries": [], + "entryMetadata": {} + }, + "isWorkflowEnabled": true +} +``` + +While a workflow is in flight, the response shape is: + +```json +{ + "group": "actor", + "code": "workflow_in_flight", + "message": "Workflow replay is unavailable while the workflow is currently in flight.", + "metadata": null +} +``` + +#### Summary + +Get a full snapshot of the actor in a single request: + +```bash +curl $RIVET_API/gateway/{actor_id}/inspector/summary +``` + +Returns: + +```json +{ + "state": { "count": 42 }, + "connections": [], + "rpcs": ["increment", "getCount"], + "queueSize": 0, + "isStateEnabled": true, + "isDatabaseEnabled": false, + "isWorkflowEnabled": false, + "workflowHistory": null +} +``` + +When workflow history is present in `/inspector/summary`, `workflowHistory` is returned as the same decoded JSON shape as `/inspector/workflow-history`. + +### Polling + +Inspector endpoints are safe to poll. For live monitoring, poll at 1-5 second intervals. The `/inspector/summary` endpoint is useful for periodic snapshots since it returns all data in a single request. + +## OpenAPI Spec + +An OpenAPI specification covering many of the management and actor endpoints is available: + +- In the repository at [`rivetkit-openapi/openapi.json`](https://github.com/rivet-dev/rivet/tree/main/rivetkit-openapi) +- Served at `/doc` on the manager when running locally + +The checked-in spec does not yet list every endpoint documented on this page (for example the actor metadata and queue routes and the inspector database routes), so treat this page as the authoritative reference where they differ. + +_Source doc path: /docs/actors/debugging_ diff --git a/.agents/skills/multiplayer-game/reference/actors/design-patterns.md b/.agents/skills/multiplayer-game/reference/actors/design-patterns.md new file mode 100644 index 0000000..77ecb40 --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/actors/design-patterns.md @@ -0,0 +1,163 @@ +# Design Patterns + +> Source: `src/content/docs/actors/design-patterns.mdx` +> Canonical URL: https://rivet.dev/docs/actors/design-patterns +> Description: Common patterns and anti-patterns for building scalable actor systems. + +--- +## How Actors Scale + +Actors are inherently scalable because of how they're designed: + +- **Isolated state:** Each actor manages its own private data. No shared state means no conflicts and no locks, so actors run concurrently without coordination. +- **Actor-to-actor communication:** Actors interact through [actions](/docs/actors/actions) and [events](/docs/actors/events), so they don't need to coordinate access to shared data. This makes it easy to distribute them across machines. +- **Small, focused units:** Each actor handles a limited scope (a single user, document, or chat room), so load naturally spreads across many actors rather than concentrating in one place. +- **Horizontal scaling:** Adding more machines automatically distributes actors across them. + +These properties form the foundation for the patterns described below. + +## Actor Per Entity + +The core pattern is creating one actor per entity in your system. Each actor represents a single user, document, chat room, or other distinct object. This keeps actors small, independent, and easy to scale. + +**Good examples** + +- `User`: Manages user profile, preferences, and authentication +- `Document`: Handles document content, metadata, and versioning +- `ChatRoom`: Manages participants and message history + +**Bad examples** + +- `Application`: Too broad, handles everything +- `DocumentWordCount`: Too granular, should be part of Document actor + +## Coordinator & Data Actors + +Actors scale by splitting state into isolated entities. However, it's common to need to track and coordinate actors in a central place. This is where coordinator actors come in. + +**Data actors** handle the main logic in your application. Examples: chat rooms, user sessions, game lobbies. + +**Coordinator actors** track other actors. Think of them as an index of data actors. Examples: a list of chat rooms, a list of active users, a list of game lobbies. + +**Example: Chat Room Coordinator** + +### Actor + +### Client + +## Sharding + +Sharding splits a single actor's workload across multiple actors based on a key. Use this when one actor can't handle all the load or data for an entity. + +**How it works:** +- Partition data using a shard key (user ID, region, time bucket, or random) +- Requests are routed to shards based on the key +- Shards operate independently without coordination + +**Example: Sharding by Time** + +### Actor + +### Client + +**Example: Random Sharding** + +### Actor + +### Client + +Choose shard keys that distribute load evenly. Note that cross-shard queries require coordination. + +## Fan-In & Fan-Out + +Fan-in and fan-out are patterns for distributing work and aggregating results. + +**Fan-Out**: One actor spawns work across multiple actors. Use for parallel processing or broadcasting updates. + +**Fan-In**: Multiple actors send results to one aggregator. Use for collecting results or reducing data. + +**Example: Map-Reduce** + +### Actor + +### Client + +## Integrating With External Databases & APIs + +Actors can integrate with external resources like databases or external APIs. + +### Loading State + +Load external data during actor initialization using `createVars`. This keeps your actor's persisted state clean while caching expensive lookups. + +Use this when: + +- Fetching user profiles, configs, or permissions from a database +- Loading data that changes externally and shouldn't be persisted +- Caching expensive API calls or computations + +**Example: Loading User Profile** + +### Actor + +### Client + +### Syncing State Changes + +Use `onStateChange` to automatically sync actor state changes to external resources. This hook runs after state changes are flushed, which is coalesced to once per event loop tick rather than once per individual field mutation. + +Use this when: + +- You need to mirror actor state in an external database +- Triggering external side effects when state changes +- Keeping external systems in sync with actor state + +**Example: Syncing to Database** + +### Actor + +### Client + +`onStateChange` is called once per flush with the final coalesced state, ensuring external resources stay in sync. In the `updateEmail` example above, the two synchronous assignments produce a single `onStateChange` call. + +Do not mutate `c.state` inside `onStateChange`; re-entrant state mutation is rejected. + +## Anti-Patterns + +### "God" Actor + +Avoid creating a single actor that handles everything. This defeats the purpose of the actor model and creates a bottleneck. + +**Problem:** +```ts +import { actor } from "rivetkit"; + +// Bad: one actor doing everything +const app = actor({ + state: { users: {}, orders: {}, inventory: {}, analytics: {} }, + actions: { + createUser: (c, user) => { /* ... */ }, + processOrder: (c, order) => { /* ... */ }, + updateInventory: (c, item) => { /* ... */ }, + trackEvent: (c, event) => { /* ... */ }, + }, +}); +``` + +**Solution:** Split into focused actors per entity (User, Order, Inventory, Analytics). + +### Actor-Per-Request + +Actors are designed to maintain state across multiple requests. Creating a new actor for each request wastes resources and loses the benefits of persistent state. + +**Problem:** + +**Solution:** Use actors for entities that persist (users, sessions, documents), not for one-off operations. For stateless request handling, use regular functions. + +## API Reference + +- [`ActorDefinition`](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html) - Interface for pattern examples +- [`ActorContext`](/typedoc/interfaces/rivetkit.mod.ActorContext.html) - Context usage patterns +- [`ActionContext`](/typedoc/interfaces/rivetkit.mod.ActionContext.html) - Action patterns + +_Source doc path: /docs/actors/design-patterns_ diff --git a/.agents/skills/multiplayer-game/reference/actors/destroy.md b/.agents/skills/multiplayer-game/reference/actors/destroy.md new file mode 100644 index 0000000..87e1b76 --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/actors/destroy.md @@ -0,0 +1,71 @@ +# Destroying Actors + +> Source: `src/content/docs/actors/destroy.mdx` +> Canonical URL: https://rivet.dev/docs/actors/destroy +> Description: Actors can be permanently destroyed. Common use cases include: + +--- +- User account deletion +- Ending a user session +- Closing a room or game +- Cleaning up temporary resources +- GDPR/compliance data removal + +Actors sleep when idle, so destruction is only needed to permanently remove data — not to save compute. + +## Destroying An Actor + +### Destroy via Action + +To destroy an actor, use `c.destroy()` like this: + +### Destroy via HTTP + +Send a DELETE request to destroy an actor. This requires a token for authentication: + +- **Rivet Cloud**: Use the service key (`sk_*`) from your `RIVET_ENDPOINT` URL. Find this in the dashboard under **Settings > Advanced > Backend Configuration** (e.g. the `sk_...` portion of `https://default:sk_abc123@api.rivet.dev`). +- **Self-hosted**: Use an admin token. + +```typescript +const actorId = "your-actor-id"; +const namespace = "default"; +const token = "your-token"; + +await fetch(`https://api.rivet.dev/actors/${actorId}?namespace=${namespace}`, { + method: "DELETE", + headers: { + Authorization: `Bearer ${token}`, + }, +}); +``` + +```bash +curl -X DELETE "https://api.rivet.dev/actors/{actorId}?namespace={namespace}" \ + -H "Authorization: Bearer {token}" +``` + +To find the actor ID, you can list actors first: + +```bash +curl "https://api.rivet.dev/actors?namespace={namespace}" \ + -H "Authorization: Bearer {token}" +``` + +### Destroy via Dashboard + +To destroy an actor via the dashboard, navigate to the actor and press the red "X" in the top right. + +## Lifecycle Hook + +Once destroyed, the `onDestroy` hook will be called. This can be used to clean up resources related to the actor. For example: + +## Accessing Actor After Destroy + +Once an actor is destroyed, any subsequent requests to it will fail with an `actor.not_found` error (`{ group: "actor", code: "not_found" }`). The actor's state is permanently deleted. + +## API Reference + +- [`ActorHandle`](/typedoc/types/rivetkit.client_mod.ActorHandle.html) - Has destroy methods +- [`ActorContext`](/typedoc/interfaces/rivetkit.mod.ActorContext.html) - Context during destruction + +_Source doc path: /docs/actors/destroy_ diff --git a/.agents/skills/multiplayer-game/reference/actors/errors.md b/.agents/skills/multiplayer-game/reference/actors/errors.md new file mode 100644 index 0000000..af24c4c --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/actors/errors.md @@ -0,0 +1,81 @@ +# Errors + +> Source: `src/content/docs/actors/errors.mdx` +> Canonical URL: https://rivet.dev/docs/actors/errors +> Description: Rivet provides robust error handling with security built in by default. Errors are handled differently based on whether they should be exposed to clients or kept private. + +--- +There are two types of errors: + +- **UserError**: Thrown from actors and safely returned to clients with full details +- **Internal errors**: All other errors that are converted to a generic error message for security + +## Throwing and Catching Errors + +`UserError` lets you throw custom errors that will be safely returned to the client. + +Throw a `UserError` with just a message: + +### Actor + +### Client (Connection) + +### Client (Stateless) + +## Error Codes + +Use error codes for explicit error matching in try-catch blocks: + +### Actor + +### Client (Connection) + +### Client (Stateless) + +## Errors With Metadata + +Include metadata to provide additional context for rich error handling: + +### Actor + +### Client (Connection) + +### Client (Stateless) + +## Internal Errors + +All errors that are not UserError instances are automatically converted to a generic "internal error" response. This prevents accidentally leaking sensitive information like stack traces, database details, or internal system information. + +### Actor + +### Client (Connection) + +### Client (Stateless) + +### Server-Side Logging + +**All internal errors are logged server-side with full details.** When an internal error occurs, the complete error message, stack trace, and context are written to your server logs. This is where you should look first when debugging internal errors in production. + +The client receives only a generic "Internal error" message for security, but you can find the full error details in your server logs including: + +- Complete error message +- Stack trace +- Request context (actor ID, action name, connection ID, etc.) +- Timestamp + +**Always check your server logs to see the actual error details when debugging internal errors.** + +### Exposing Errors to Clients (Development Only) + +**Warning:** Only enable error exposure in development environments. In production, this will leak sensitive internal details to clients. + +For faster debugging during development, you can expose internal error details to clients by setting `RIVET_EXPOSE_ERRORS=1`. + +With error exposure enabled, clients will see the full error message instead of the generic "Internal error" response: + +## API Reference + +- [`UserError`](/typedoc/classes/rivetkit.actor_errors.UserError.html) - User-facing error class +- [`ActorError`](/typedoc/classes/rivetkit.client_mod.ActorError.html) - Errors received by the client + +_Source doc path: /docs/actors/errors_ diff --git a/.agents/skills/multiplayer-game/reference/actors/events.md b/.agents/skills/multiplayer-game/reference/actors/events.md new file mode 100644 index 0000000..36e2c69 --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/actors/events.md @@ -0,0 +1,143 @@ +# Realtime + +> Source: `src/content/docs/actors/events.mdx` +> Canonical URL: https://rivet.dev/docs/actors/events +> Description: Events enable realtime communication from actors to clients. While clients use actions to send data to actors, events allow actors to push updates to connected clients instantly. + +--- +Events can be sent to clients connected using `.connect()`. They have no effect on [low-level WebSocket connections](/docs/actors/websocket-handler). + +## Publishing Events from Actors + +### Broadcasting to All Clients + +Define event names and payload types with `events` and `event()`, then use `c.broadcast(eventName, ...args)` to send events to all connected clients: + +### Sending to Specific Connections + +Send events to individual connections using `conn.send(eventName, ...args)`: + +Send events to all connections except the sender: + +## Subscribing to Events from Clients + +Clients must establish a connection to receive events from actors. Use `.connect()` to create a persistent connection, then listen for events. + +### Basic Event Subscription + +Use `connection.on(eventName, callback)` to listen for events: + +```tsx React @nocheck +import { useState } from "react"; +import { useActor } from "./rivetkit"; + +function ChatRoom() { + const [messages, setMessages] = useState>([]); + + const chatRoom = useActor({ + name: "chatRoom", + key: ["general"] + }); + + // Listen for events + chatRoom.useEvent("messageReceived", (message) => { + setMessages(prev => [...prev, message]); + }); + + // ...rest of component... +} +``` + +### One-time Event Listeners + +Use `connection.once(eventName, callback)` for events that should only trigger once: + +```tsx React @nocheck +import { useState, useEffect } from "react"; +import { useActor } from "./rivetkit"; + +function GameLobby() { + const [gameStarted, setGameStarted] = useState(false); + + const gameRoom = useActor({ + name: "gameRoom", + key: ["room-456"], + params: { + playerId: "player-789", + role: "player" + } + }); + + // Listen for game start (only once) + useEffect(() => { + if (!gameRoom.connection) return; + + const handleGameStart = () => { + console.log('Game has started!'); + setGameStarted(true); + }; + + gameRoom.connection.once('gameStarted', handleGameStart); + }, [gameRoom.connection]); + + // ...rest of component... +} +``` + +### Removing Event Listeners + +Use the callback returned from `.on()` to remove event listeners: + +```tsx React @nocheck +import { useState, useEffect } from "react"; +import { useActor } from "./rivetkit"; + +function ConditionalListener() { + const [isListening, setIsListening] = useState(false); + const [messages, setMessages] = useState([]); + + const chatRoom = useActor({ + name: "chatRoom", + key: ["general"] + }); + + useEffect(() => { + if (!chatRoom.connection || !isListening) return; + + // Add listener + const unsubscribe = chatRoom.connection.on('messageReceived', (message) => { + setMessages(prev => [...prev, message.text]); + }); + + // Cleanup - remove listener when component unmounts or listening stops + return () => { + unsubscribe(); + }; + }, [chatRoom.connection, isListening]); + + // ...rest of component... +} +``` + +## Debugging + +- `GET /inspector/connections` shows active connections and connection metadata. +- Use this to confirm clients are connected before expecting broadcasts. +- `GET /inspector/summary` provides connections, RPCs, and queue size in one response. +- In non-dev mode, inspector endpoints require authorization. + +## More About Connections + +For more details on actor connections, including connection lifecycle, authentication, and advanced connection patterns, see the [Connections documentation](/docs/actors/connections). + +## API Reference + +- [`RivetEvent`](/typedoc/interfaces/rivetkit.mod.RivetEvent.html) - Base event interface +- [`RivetMessageEvent`](/typedoc/interfaces/rivetkit.mod.RivetMessageEvent.html) - Message event type +- [`RivetCloseEvent`](/typedoc/interfaces/rivetkit.mod.RivetCloseEvent.html) - Close event type +- [`UniversalEvent`](/typedoc/interfaces/rivetkit.mod.UniversalEvent.html) - Universal event type +- [`UniversalMessageEvent`](/typedoc/interfaces/rivetkit.mod.UniversalMessageEvent.html) - Universal message event +- [`UniversalErrorEvent`](/typedoc/interfaces/rivetkit.mod.UniversalErrorEvent.html) - Universal error event +- [`EventUnsubscribe`](/typedoc/types/rivetkit.client_mod.EventUnsubscribe.html) - Unsubscribe function type + +_Source doc path: /docs/actors/events_ diff --git a/.agents/skills/multiplayer-game/reference/actors/fetch-and-websocket-handler.md b/.agents/skills/multiplayer-game/reference/actors/fetch-and-websocket-handler.md new file mode 100644 index 0000000..b8b0509 --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/actors/fetch-and-websocket-handler.md @@ -0,0 +1,10 @@ +# Fetch and WebSocket Handler + +> Source: `src/content/docs/actors/fetch-and-websocket-handler.mdx` +> Canonical URL: https://rivet.dev/docs/actors/fetch-and-websocket-handler +> Description: These docs have moved to [Low-Level WebSocket Handler](/docs/actors/websocket-handler) and [Low-Level Request Handler](/docs/actors/request-handler). + +--- + + +_Source doc path: /docs/actors/fetch-and-websocket-handler_ diff --git a/.agents/skills/multiplayer-game/reference/actors/helper-types.md b/.agents/skills/multiplayer-game/reference/actors/helper-types.md new file mode 100644 index 0000000..55555c7 --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/actors/helper-types.md @@ -0,0 +1,10 @@ +# Helper Types + +> Source: `src/content/docs/actors/helper-types.mdx` +> Canonical URL: https://rivet.dev/docs/actors/helper-types +> Description: This page has moved to [Types](/docs/actors/types). + +--- + + +_Source doc path: /docs/actors/helper-types_ diff --git a/.agents/skills/multiplayer-game/reference/actors/http-api.md b/.agents/skills/multiplayer-game/reference/actors/http-api.md new file mode 100644 index 0000000..1d8d5be --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/actors/http-api.md @@ -0,0 +1,10 @@ +# Vanilla HTTP API + +> Source: `src/content/docs/actors/http-api.mdx` +> Canonical URL: https://rivet.dev/docs/actors/http-api +> Description: Use the low-level HTTP handler to send and receive requests from actors. + +--- +TODO + +_Source doc path: /docs/actors/http-api_ diff --git a/.agents/skills/multiplayer-game/reference/actors/input.md b/.agents/skills/multiplayer-game/reference/actors/input.md new file mode 100644 index 0000000..c5c5dde --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/actors/input.md @@ -0,0 +1,51 @@ +# Input Parameters + +> Source: `src/content/docs/actors/input.mdx` +> Canonical URL: https://rivet.dev/docs/actors/input +> Description: Pass initialization data to actors when creating instances + +--- +Actors can receive input parameters when created, allowing for flexible initialization and configuration. Input is passed during actor creation and is available in lifecycle hooks. + +## Passing Input to Actors + +Input is provided when creating actor instances using the `input` property: + +## Accessing Input in Lifecycle Hooks + +Input is available as the second argument to the `createState` and `onCreate` lifecycle hooks: + +## Input Validation + +You can validate input parameters in the `createState` or `onCreate` hooks: + +## Input vs Connection Parameters + +Input parameters are different from connection parameters: + +- **Input**: + - Passed when creating the actor instance + - Use for actor-wide configuration + - Available in lifecycle hooks +- **Connection parameters**: + - Passed when connecting to an existing actor + - Used for connection-specific configuration + - Available in connection hooks + +## Input Best Practices + +### Use Type Safety + +Define input types to ensure type safety: + +### Store Input in State + +Input is only available in `createState` and `onCreate` lifecycle hooks. If you need to access input data later (in actions, timers, or other hooks), store it in the actor's state during creation. This is the recommended pattern because input shapes can evolve over time, and persisting input in state ensures you always have access to the values the actor was created with: + +## API Reference + +- [`CreateOptions`](/typedoc/interfaces/rivetkit.client_mod.CreateOptions.html) - Options for creating actors +- [`CreateRequest`](/typedoc/types/rivetkit.client_mod.CreateRequest.html) - Request type for creation +- [`ActorDefinition`](/typedoc/classes/rivetkit.mod.ActorDefinition.html) - Actor definition returned by `actor()` + +_Source doc path: /docs/actors/input_ diff --git a/.agents/skills/multiplayer-game/reference/actors/inspector-tabs.md b/.agents/skills/multiplayer-game/reference/actors/inspector-tabs.md new file mode 100644 index 0000000..34de206 --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/actors/inspector-tabs.md @@ -0,0 +1,251 @@ +# Custom Inspector Tabs + +> Source: `src/content/docs/actors/inspector-tabs.mdx` +> Canonical URL: https://rivet.dev/docs/actors/inspector-tabs +> Description: Ship your own UI tabs alongside a Rivet Actor — embedded directly in the dashboard inspector. + +--- +Custom inspector tabs let you embed your own UI directly in the Rivet +dashboard, next to the built-in tabs. Declare a tab on your actor, point +it at a folder of static files, and the dashboard picks it up +automatically. + +Common uses: + +- Domain-specific debugging panels (queue depth, connection maps, log + filters). +- Operational tools (admin buttons, drain controls, snapshot uploaders). +- Any author-defined view that ships with your actor. + +A runnable example lives at +[`examples/inspector-tabs`](https://github.com/rivet-dev/rivet/tree/main/examples/inspector-tabs). + +## Quickstart + +Declare a tab on your actor: + +Drop an `index.html` in the `source` directory: + +```html + + + + + + + +

Counter:

+ + + +``` + +Open the dashboard and the "Counter" tab appears alongside the built-ins. + +## Configuration + +Each entry in `inspector.tabs[]` is either a **custom tab** or a +**hide modifier** for a built-in. + +### Custom tab + +```ts @nocheck +{ + id: string, // URL-safe id: /^[A-Za-z0-9_-]+$/ + label: string, // Shown in the tab strip + source: string, // Directory of static assets + icon?: string, // Optional icon id +} +``` + +- **`id`** — used as the URL segment and tab-strip key. Cannot collide + with a built-in id (`workflow`, `database`, `state`, `queue`, + `connections`, `console`). +- **`source`** — directory of static files. The bytes you put there are + the bytes the browser sees. Point it at a Vite/webpack `dist/` and + any framework works (React, Vue, Svelte, vanilla — all fine). +- **`icon`** — one of `workflow`, `database`, `state`, `queue`, `plug`, + `terminal`, `tag`, `logs`. Anything else falls back to a neutral icon. + +### Hide a built-in tab + +```ts @nocheck +{ + id: "workflow" | "database" | "state" | "queue" | "connections" | "console", + hidden: true, +} +``` + +Use this to clean up the strip when the actor doesn't use a given +subsystem — e.g. a counter actor with no queues: + +```ts +inspector: { tabs: [{ id: "queue", hidden: true }] } +``` + +Misconfigurations (missing directory, duplicate id, invalid characters, +empty label) throw at registry construction, so problems show up +immediately. + +## Talking to the dashboard + +The tab loads in an iframe and communicates with the dashboard via +`postMessage`. The contract is small. + +### From the dashboard + +The dashboard sends an `init` message on load and again whenever the +inspector token rotates. Always overwrite the cached token when it +arrives. + +```ts @nocheck +{ + type: "init", + v: 1, + actorId: string, + authToken: string, // Per-actor inspector bearer token + theme?: "light" | "dark", + activeTab?: string, // For multi-view tabs +} +``` + +Multi-view tabs can read the optional `activeTab` field on `init` to +seed their initial sub-view. The dashboard does not send a separate +message when the user switches custom tabs — it navigates the iframe +`src` instead, so the tab reloads and receives a fresh `init`. + +### From the tab + +Send `ready` once your message listener is registered: + +```ts @nocheck +{ type: "ready", v: 1 } +``` + +If a fetch returns 401, the token has rotated. Ask the dashboard for a +fresh one and wait for the next `init` — don't retry with the stale +token: + +```ts @nocheck +{ type: "token-refresh-needed", v: 1 } +``` + +### Security check + +Always reject messages whose `event.origin` doesn't match the +`?shellOrigin=` URL parameter. Without this check, any page that frames +your tab could forge an `init` and feed you a fake token. + +### TypeScript types + +If you build the tab with TypeScript, the message and response types +are exported as types-only: + +## Reading state and calling actions + +The tab can hit any inspector endpoint with the supplied bearer token. +Use relative paths so the tab doesn't need to know the engine origin or +actor id: + +```js +fetch("../../state", { headers: { Authorization: `Bearer ${token}` } }); +fetch("../../action/increment", { method: "POST", headers: { ... }, body: ... }); +fetch("../../rpcs", { headers: { Authorization: `Bearer ${token}` } }); +fetch("../../connections", { headers: { Authorization: `Bearer ${token}` } }); +fetch("../../queue", { headers: { Authorization: `Bearer ${token}` } }); +``` + +The action body shape is `{ args: [...] }` — the array is passed as +positional arguments to the action. + +Full endpoint reference: +[Debugging → Inspector Endpoints](/docs/actors/debugging#inspector-endpoints). + +For high-frequency UIs, prefer the inspector WebSocket +(`/inspector/connect`) over polling. + +## Styling + +A shared stylesheet matching the dashboard's design tokens is served at +`../../tab.css`: + +```html + +``` + +It defines `--rivet-*` tokens for colors, spacing, radius, and +typography, plus sensible defaults so a bare tab looks at home without +custom CSS: + +```css +.my-card { + background: var(--rivet-card); + color: var(--rivet-foreground); + border: 1px solid var(--rivet-border); + border-radius: var(--rivet-radius-md); + padding: var(--rivet-space-4); +} +``` + +Toggle dark mode by adding the `dark` class to `` — the dashboard +sends the active theme in the `init` message. + +Color tokens come in both pre-wrapped (`--rivet-card`) and raw HSL +(`--rivet-card-raw`) forms, so you can compose with alpha: + +```css +.overlay { background: hsl(var(--rivet-background-raw) / 0.6); } +``` + +You're free to skip the stylesheet entirely and bring your own. + +## Security + +The tab runs in an iframe at the engine origin and can call any +inspector endpoint with the supplied token. Treat the bundle like any +author code that ships with your actor: + +- **Don't inline secrets.** The bundle is fetchable by anyone who can + reach the actor. +- **Always validate `event.origin`.** Reject inbound messages from + anywhere other than the dashboard origin. +- **Never retry silently on 401.** Post `token-refresh-needed` and wait + for a fresh `init`. + +## See also + +- [Debugging](/docs/actors/debugging) — full inspector HTTP API +- [Actions](/docs/actors/actions) — actions your tab can invoke +- [State](/docs/actors/state) — state your tab can read +- [`examples/inspector-tabs`](https://github.com/rivet-dev/rivet/tree/main/examples/inspector-tabs) — runnable example + +_Source doc path: /docs/actors/inspector-tabs_ diff --git a/.agents/skills/multiplayer-game/reference/actors/keys.md b/.agents/skills/multiplayer-game/reference/actors/keys.md new file mode 100644 index 0000000..b8f1614 --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/actors/keys.md @@ -0,0 +1,51 @@ +# Actor Keys + +> Source: `src/content/docs/actors/keys.mdx` +> Canonical URL: https://rivet.dev/docs/actors/keys +> Description: Actor keys uniquely identify actor instances within each actor type. Keys are used for addressing which specific actor to communicate with. + +--- +## Key Format + +Actor keys can be either a string or an array of strings: + +### Compound Keys & User Data + +Array keys are useful when you need compound keys with user-provided data. Using arrays makes adding user data safe by preventing key injection attacks: + +This allows you to create hierarchical addressing schemes and organize actors by multiple dimensions. + +Don't build keys using string interpolation like `"foo:${userId}:bar"` when `userId` contains user data. If a user provides a value containing the delimiter (`:` in this example), it can break your key structure and cause key injection attacks. + +### Omitting Keys + +You can create actors without specifying a key in situations where there is a singleton actor (i.e. only one actor of a given type). For example: + +This pattern should be avoided, since a singleton actor usually means you have a single actor serving all traffic & your application will not scale. See [scaling documentation](/docs/actors/scaling) for more information. + +### Key Uniqueness + +Keys are unique within each actor name. Different actor types can use the same key: + +## Accessing Keys in Metadata + +Access the actor's key within the actor using the [metadata](/docs/actors/metadata) API: + +## Configuration Examples + +### Simple Configuration with Keys + +Use keys to provide basic actor configuration: + +### Complex Configuration with Input + +For more complex configuration, use [input parameters](/docs/actors/input): + +## API Reference + +- [`ActorKey`](/typedoc/types/rivetkit.mod.ActorKey.html) - Key type for actors +- [`ActorQuery`](/typedoc/types/rivetkit.mod.ActorQuery.html) - Query type using keys +- [`GetOptions`](/typedoc/interfaces/rivetkit.client_mod.GetOptions.html) - Options for getting by key +- [`QueryOptions`](/typedoc/interfaces/rivetkit.client_mod.QueryOptions.html) - Options for querying + +_Source doc path: /docs/actors/keys_ diff --git a/.agents/skills/multiplayer-game/reference/actors/kv.md b/.agents/skills/multiplayer-game/reference/actors/kv.md new file mode 100644 index 0000000..384769b --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/actors/kv.md @@ -0,0 +1,44 @@ +# Low-Level KV Storage + +> Source: `src/content/docs/actors/kv.mdx` +> Canonical URL: https://rivet.dev/docs/actors/kv +> Description: Use the built-in key-value store on ActorContext for durable string and binary data alongside actor state. + +--- +KV is deprecated. It is a low-level escape hatch kept for backward compatibility. For new actors, prefer [in-memory state](/docs/actors/state) for small serializable values or [SQLite](/docs/actors/sqlite) for larger or queryable data. + +Every Rivet Actor includes a lightweight key-value store on `c.kv`. It is useful for dynamic keys, blobs, or data that does not fit well in structured state. + +If your data has a known schema, prefer [state](/docs/actors/state). KV is best for flexible or user-defined keys. + +## Basic Usage + +Keys and values default to `text`, so you can use strings without extra options. + +## Value Types + +You can store binary values by passing `Uint8Array` or `ArrayBuffer`. Use `type` on both reads and writes to get the right value type: `binary` for `Uint8Array` and `arrayBuffer` for `ArrayBuffer`. + +TypeScript returns a concrete type based on the option you pass in: + +## Key Types + +Keys accept either `string` or `Uint8Array`. String keys are encoded as UTF-8 by default. + +When listing by prefix, you can control how keys are decoded with `keyType`. Returned keys have the prefix removed. + +If you use binary keys, set `keyType: "binary"` so the returned keys stay as `Uint8Array`. + +## Range Operations + +Use `listRange(start, end)` to read an arbitrary half-open range `[start, end)`. Use `deleteRange(start, end)` to clear that same range efficiently. + +## Batch Operations + +KV supports batch operations for efficiency. `batchPut` and `batchGet` work on raw `Uint8Array` keys and values, so encode strings before passing them in. + +## API Reference + +- [`ActorContext`](/typedoc/interfaces/rivetkit.mod.ActorContext.html) - `c.kv` is available on the context + +_Source doc path: /docs/actors/kv_ diff --git a/.agents/skills/multiplayer-game/reference/actors/lifecycle.md b/.agents/skills/multiplayer-game/reference/actors/lifecycle.md new file mode 100644 index 0000000..89955bb --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/actors/lifecycle.md @@ -0,0 +1,388 @@ +# Lifecycle + +> Source: `src/content/docs/actors/lifecycle.mdx` +> Canonical URL: https://rivet.dev/docs/actors/lifecycle +> Description: Learn about actor lifecycle hooks for initialization, state management, and cleanup. + +--- +# Lifecycle + +Actors follow a well-defined lifecycle with hooks at each stage. Understanding these hooks is essential for proper initialization, state management, and cleanup. + +## Lifecycle + +Actors transition through several states during their lifetime. Each transition triggers specific hooks that let you initialize resources, manage connections, and clean up state. + +``` +Loading ──Start──▶ Ready ──spawn driver──▶ Started + │ + ┌────────────────────────────────────────┤ + │ │ + │ idle timer + can_sleep │ Destroy command + ▼ ▼ + SleepGrace ─── grace window closes ──▶ Destroying + │ │ + ▼ │ + SleepFinalize ──── stop sequence ───────────┤ + ▼ + Terminated +``` + +**On Create** (runs once per actor) + +1. `onMigrate` +2. `createState` +3. `onCreate` +4. `createVars` +5. `onWake` +6. `run` (background, does not block) + +**On Destroy** + +1. `onDestroy` + +**On Wake** (after sleep, restart, or crash) + +1. `onMigrate` +2. `createVars` +3. `onWake` +4. `run` (background, does not block) + +**On Sleep** (after idle period) + +1. Wait for `run` to complete (with timeout) +2. `onSleep` + +**On Connect** (per client) + +1. `onBeforeConnect` +2. `createConnState` +3. `onConnect` + +**On Disconnect** (per client) + +1. `onDisconnect` + +**On Inbound Action Invoke** (per action call) + +1. Action handler executes + +**On Inbound Queue Publish** (per `handle.send(...)`) + +1. `queues..canPublish` (if defined) +2. Queue message is enqueued + +**On Event Subscription Request** (per subscribe request) + +1. `events..canSubscribe` (if defined) +2. Subscription is applied + +## Lifecycle Hooks + +Actor lifecycle hooks are defined as functions in the actor configuration. + +### `state` + +The `state` constant defines the initial state of the actor. See [state documentation](/docs/actors/state) for more information. + +### `onMigrate` + +[API Reference](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html) + +The `onMigrate` hook runs on every actor start, before `createState`, `onCreate`, `createVars`, and `onWake`. Can be async. It runs early so that database migrations are applied before any other lifecycle hook accesses the database. The second parameter is `true` when the actor is being created for the first time. + +### `createState` + +[API Reference](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html) + +The `createState` function dynamically initializes state based on input. Called only once when the actor is first created. Can be async. See [state documentation](/docs/actors/state) for more information. + +### `vars` + +The `vars` constant defines ephemeral variables for the actor. These variables are not persisted and are useful for storing runtime-only data. The value for `vars` must be clonable via `structuredClone`. See [ephemeral variables documentation](/docs/actors/state#ephemeral-variables) for more information. + +### `createVars` + +[API Reference](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html) + +The `createVars` function dynamically initializes ephemeral variables. Can be async. Use this when you need to initialize values at runtime. See [ephemeral variables documentation](/docs/actors/state#ephemeral-variables) for more information. + +### `onCreate` + +[API Reference](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html) + +The `onCreate` hook is called when the actor is first created. Can be async. Use this hook for initialization logic that doesn't affect the initial state. + +### `onDestroy` + +[API Reference](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html) + +The `onDestroy` hook is called when the actor is being permanently destroyed. Can be async. Use this for final cleanup operations like closing external connections, releasing resources, or performing any last-minute state persistence. + +The actor is still fully functional when `onDestroy` runs. You can access the database, broadcast events, call `waitUntil`, send queue messages, and use `schedule.after`. State mutations made during `onDestroy` are persisted before the actor is torn down. + +### `onWake` + +[API Reference](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html) + +This hook is called any time the actor is started (e.g. after restarting, upgrading code, or crashing). Can be async. + +This is called after the actor has been initialized but before any connections are accepted. + +Use this hook to set up any resources or start any background tasks, such as `setInterval`. + +### `onSleep` + +[API Reference](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html) + +This hook is called when the actor is going to sleep. Can be async. Use this to clean up resources, close connections, or perform any shutdown operations. + +The actor is still fully functional when `onSleep` runs. You can access the database, broadcast events, call `waitUntil`, send queue messages, and use `schedule.after`. State mutations made during `onSleep` are persisted before the actor finishes sleeping. + +This hook may not always be called in situations like crashes or forced terminations. Don't rely on it for critical cleanup operations. + +Not supported on Cloudflare Workers. + +### `run` + +[API Reference](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html) + +The `run` hook is called after the actor starts and runs in the background without blocking actor startup. This is ideal for long-running background tasks like: + +- Reading from message queues in a loop +- Tick loops for periodic work +- Custom workflow logic +- Background processing + +The handler exposes `c.aborted` for loop checks and `c.abortSignal` for canceling operations when the actor is stopping. You should always check or listen for shutdown to exit gracefully. + +**Important behavior:** +- The actor may go to sleep at any time during the `run` handler. Wrap work that must keep the actor awake with `c.keepAwake(promise)` to block idle sleep until the promise settles. +- If the `run` handler exits (returns), the actor follows its normal idle sleep timeout once it becomes idle +- If the `run` handler throws an error, the actor logs the error and then follows its normal idle sleep timeout once it becomes idle +- On shutdown, `c.abortSignal` fires so the `run` handler can exit within the graceful shutdown window. + +Finite `run` handlers leave the actor alive after they finish. If you want a one shot task to clean itself up when it is done, call `c.destroy()` before returning. + +### `onStateChange` + +[API Reference](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html) + +Called whenever the actor's state changes. Cannot be async. This is often used to broadcast state updates. + +Do not mutate `c.state` inside `onStateChange`; re-entrant state mutation is rejected. + +### `createConnState` and `connState` + +[API Reference](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html) + +There are two ways to define the initial state for connections: +1. `connState`: Define a constant object that will be used as the initial state for all connections +2. `createConnState`: A function that dynamically creates initial connection state based on connection parameters. Can be async. + +### `onBeforeConnect` + +[API Reference](/typedoc/interfaces/rivetkit.mod.BeforeConnectContext.html) + +The `onBeforeConnect` hook is called whenever a new client connects to the actor. Can be async. Clients can pass parameters when connecting, accessible via `params`. This hook is used for connection validation and can throw errors to reject connections. + +The `onBeforeConnect` hook does NOT return connection state - it's used solely for validation. + +Connections cannot interact with the actor until this method completes successfully. Throwing an error will abort the connection. This can be used for authentication - see [Authentication](/docs/actors/authentication) for details. + +### `onConnect` + +[API Reference](/typedoc/interfaces/rivetkit.mod.ConnectContext.html) + +Executed after the client has successfully connected. Can be async. Receives the connection object as a second parameter. + +Messages will not be processed for this actor until this hook succeeds. Errors thrown from this hook will cause the client to disconnect. + +### `canPublish` and `canSubscribe` + +[API Reference](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html) + +Use schema-level hooks to authorize queue publishes and event subscriptions. Both hooks can be async and must return booleans: + +- `queues..canPublish` runs before inbound queue publishes. +- `events..canSubscribe` runs before inbound event subscription requests. + +For actions, enforce authorization directly inside each action handler. + +Use deny-by-default rules for each hook and return `false` unless explicitly allowed. See [Access Control](/docs/actors/access-control) for full guidance. + +### `onDisconnect` + +[API Reference](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html) + +Called when a client disconnects from the actor. Can be async. Receives the connection object as a second parameter. Use this to clean up any connection-specific resources. + +### `onRequest` + +[API Reference](/typedoc/interfaces/rivetkit.mod.RequestContext.html) + +The `onRequest` hook handles HTTP requests sent to your actor at `/actors/{actorName}/http/*` endpoints. Can be async. It receives the request context and a standard `Request` object, and should return a `Response` object. + +See [Request Handler](/docs/actors/request-handler) for more details. + +### `onWebSocket` + +[API Reference](/typedoc/interfaces/rivetkit.mod.WebSocketContext.html) + +The `onWebSocket` hook handles WebSocket connections to your actor. Can be async. It receives the actor context and a `WebSocket` object. Use this to set up WebSocket event listeners and handle real-time communication. + +See [WebSocket Handler](/docs/actors/websocket-handler) for more details. + +### `onBeforeActionResponse` + +[API Reference](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html) + +The `onBeforeActionResponse` hook is called before sending an action response to the client. Can be async. Use this hook to modify or transform the output of an action before it's sent to the client. This is useful for formatting responses, adding metadata, or applying transformations to the output. + +## Sleeping + +Actors automatically sleep after a period of inactivity to free up resources. When a request arrives for a sleeping actor, it wakes up, restores its state, and handles the request. + +### When Actors Sleep + +#### Idle Timeout + +An actor is considered idle and eligible to sleep when **all** of the following are true: + +- No active HTTP requests +- No active connections (unless they are hibernatable WebSockets) +- No active `run` handler (unless it is waiting on a queue) +- No outstanding `c.keepAwake(promise)` promises +- No pending disconnect callbacks +- No async `onWebSocket` event handlers (eg `open`, `message`, `close`) still running + +Once the actor becomes idle, the sleep timer starts. After `sleepTimeout` (default 30 seconds) of continuous inactivity, the actor begins the sleep process. Any activity resets the timer. + +Outbound requests (e.g. `fetch` calls) do not count as activity and will not keep the actor awake. Wrap them with `c.waitUntil()` if they must complete before the actor sleeps. + +#### Upgrades & Eviction + +The platform may force an actor to migrate to a new machine during version upgrades or when a serverless request is about to timeout. The same [shutdown sequence](#shutdown-sequence) runs, then the actor is rescheduled on a new machine and wakes up with its persisted state. + +Use `onSleep`, `waitUntil`, or `keepAwake` to control the length of the grace period before the actor moves to another machine. + +### Manual Lifecycle Controls + +You can also trigger lifecycle transitions from the Rivet Cloud API. These endpoints are useful for operational workflows, debugging, and forcing an actor to move through the same sleep or reschedule path that the platform would normally trigger. + +```bash +curl -X POST \ + "https://cloud-api.rivet.dev/actors/$ACTOR_ID/sleep?namespace=$NAMESPACE" \ + -H "Authorization: Bearer $RIVET_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{}' +``` + +```bash +curl -X POST \ + "https://cloud-api.rivet.dev/actors/$ACTOR_ID/reschedule?namespace=$NAMESPACE" \ + -H "Authorization: Bearer $RIVET_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{}' +``` + +`/sleep` asks the actor to enter the normal sleep shutdown sequence. `/reschedule` asks the platform to allocate the actor again, which is useful after crashes or when you need to force a fresh placement. Both endpoints require the actor ID and namespace. + +### Skip Ready Wait + +The gateway normally holds requests until the actor is ready. The actor is not ready during startup (before `onWake` finishes) or during the sleep grace period (while `onSleep` and `waitUntil` are running). Probes and readiness checks can opt out with `skipReadyWait` to reach the actor's `onRequest` or `onWebSocket` handler in either window. + +See [Skip Ready Wait](/docs/clients/javascript#skip-ready-wait) on the JavaScript client page for usage. + +### Keeping the Actor Awake + +RivetKit gives you two primitives for holding the actor awake across background work. Both take a `Promise` and differ in how they interact with idle sleep and the grace period. + +| Method | Accepts | Blocks idle sleep | Blocks grace finalize | Use case | +| --- | --- | --- | --- | --- | +| `c.keepAwake(promise)` | `Promise` (returns same promise) | Yes | Yes | Critical work that must keep the actor running end to end (for example a turn in a game, an ongoing tool call). | +| `c.waitUntil(promise)` | `Promise` (returns void) | No | Yes | Best-effort finalization work that may complete during the grace window (for example analytics flushes, cleanup writes). | + +`c.keepAwake(promise)` is the preferred primitive for long-running work the actor should not sleep through. It holds a keep-awake counter until the promise settles, which blocks both idle sleep and the grace finalize step. The promise is returned unchanged, so you can `await` it if you need the value. + +`setPreventSleep(enabled)` is deprecated and now a no-op. Wrap the work you want to keep alive with `c.keepAwake(promise)` instead. + +### On Sleep Hook + +The [`onSleep`](#onsleep) hook runs during shutdown for cleanup like clearing intervals or closing connections. It is best-effort and will not run if the actor crashes. + +### Wait Before Sleep + +`c.waitUntil(promise)` registers a background promise that must resolve before the actor finishes sleeping. Use this to flush data or finish in-flight work during shutdown without blocking the main execution flow. + +The actor waits up to `sleepGracePeriod` for graceful sleep work during the [shutdown sequence](#shutdown-sequence). That single budget covers `onSleep`, `waitUntil`, `keepAwake`, async raw WebSocket handlers such as `message` and `close`. By default, this graceful sleep window is 15 seconds total. If the timeout is exceeded, the actor proceeds with sleep anyway. + +### Sleep Timeouts + +| Option | Default | Description | +|--------|---------|-------------| +| `sleepTimeout` | 30 seconds | Time of inactivity before the actor begins sleeping. | +| `sleepGracePeriod` | 15 seconds | Total graceful shutdown window for hooks, `waitUntil`, `keepAwake`, async raw WebSocket handlers, and disconnects. | + +Rivet enforces a hard limit of **30 minutes** for the entire stop process. These can be configured in the [options](#options). + +### WebSocket Hibernation + +WebSocket connections are preserved across sleep cycles by default and transparently migrated to the new actor instance. Client stays connected and sees no interruption. Actor migration is very fast, realtime workloads are not interrupted. + +### Shutdown Sequence + +When an actor sleeps or is destroyed, it enters the graceful shutdown window: + +1. `c.abortSignal` fires and `c.aborted` becomes `true`. New connections and dispatch are rejected. Alarm timeouts are cancelled. On sleep, scheduled events are persisted and will be re-armed when the actor wakes. +2. `onSleep` or `onDestroy` and `onDisconnect` for each closing connection run during the same window. User `waitUntil` promises and async raw WebSocket handlers are drained. Hibernatable WebSocket connections are preserved on sleep and closed on destroy. +3. Once graceful work has completed, state is saved and final cleanup runs. + +The entire window is bounded by `sleepGracePeriod` on both sleep and destroy. Defaults to 15 seconds. If the window is exceeded, the actor proceeds to state save anyway. + +#### Graceful shutdown window + +During steps 1 through 6, the actor is still fully functional. Database access, `broadcast`, `waitUntil`, `queue.send`, and `schedule.after` all work. State mutations are persisted at step 7. Actions invoked by pre-existing connections or lifecycle hooks continue to execute normally. + +New connections and raw WebSocket upgrades are rejected as soon as the shutdown sequence begins. New requests that arrive during shutdown are held until the actor wakes up again. The caller does not need to retry. + +If `schedule.after` is called during shutdown, the event is persisted so it survives the sleep/wake cycle, but no local timeout is scheduled. The event will fire after the actor wakes. + +In-flight actions are **not** waited on during shutdown. If an action must complete before the actor stops, wrap the critical work with `c.waitUntil()`. + +## Options + +The `options` object allows you to configure various timeouts and behaviors for your actor. + +| Option | Default | Description | +|--------|---------|-------------| +| `createVarsTimeout` | 5000ms | Timeout for `createVars` function | +| `createConnStateTimeout` | 5000ms | Timeout for `createConnState` function | +| `onConnectTimeout` | 5000ms | Timeout for `onConnect` hook | +| `sleepGracePeriod` | 15000ms | Total graceful shutdown window for both sleep and destroy | +| `stateSaveInterval` | 1000ms | Interval for persisting state | +| `actionTimeout` | 60000ms | Timeout for action execution | +| `connectionLivenessTimeout` | 2500ms | Timeout for connection liveness check | +| `connectionLivenessInterval` | 5000ms | Interval for connection liveness check | +| `sleepTimeout` | 30000ms | Time before actor sleeps due to inactivity | +| `canHibernateWebSocket` | false | Whether WebSockets can hibernate (experimental) | + +## Advanced + +### Actor Shutdown Abort Signal + +The `c.abortSignal` provides an `AbortSignal` that fires when the actor is stopping, and `c.aborted` is the shorthand boolean for loop checks. Use these to cancel ongoing operations when the actor sleeps or is destroyed. + +The abort signal fires at the very start of the [shutdown sequence](#shutdown-sequence), before `onSleep` or `onDestroy` runs. This means `c.aborted` is already `true` inside those lifecycle hooks. The signal fires early so that the `run` handler can exit promptly, but the actor remains fully functional throughout the graceful shutdown window. + +See [Canceling Long-Running Actions](/docs/actors/actions#canceling-long-running-actions) for manually canceling operations on-demand. + +### Using `ActorContext` Type Externally + +When extracting logic from lifecycle hooks or actions into external functions, you'll often need to define the type of the context parameter. Rivet provides helper types that make it easy to extract and pass these context types to external functions. + +See [Types](/docs/actors/types) for more details on using `ActorContextOf`. + +## Full Example + +_Source doc path: /docs/actors/lifecycle_ diff --git a/.agents/skills/multiplayer-game/reference/actors/limits.md b/.agents/skills/multiplayer-game/reference/actors/limits.md new file mode 100644 index 0000000..d69c889 --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/actors/limits.md @@ -0,0 +1,169 @@ +# Limits + +> Source: `src/content/docs/actors/limits.mdx` +> Canonical URL: https://rivet.dev/docs/actors/limits +> Description: Limits and constraints for Rivet Actors. + +--- +This page documents the limits for Rivet Actors. + +There are two types of limits: + +- **Soft Limit**: Application-level limit, configurable in RivetKit. These cannot exceed the hard limit. +- **Hard Limit**: Infrastructure-level limit that cannot be configured. + +Soft limits are configured in two places. Registry-level WebSocket message-size limits are passed to `setup`: + +Per-actor limits such as queue sizes and lifecycle timeouts are passed to `actor(...)` via `options`: + +## Limits + +### WebSocket + +These limits affect actions that use `.connect()` and [low-level WebSockets](/docs/actors/websocket-handler). + +| Name | Soft Limit | Hard Limit | Description | +|------|------------|------------|-------------| +| Max incoming message size | 64 KiB | 32 MiB | Maximum size of incoming WebSocket messages. Soft limit configurable via `maxIncomingMessageSize`; hard limit configurable in self-hosted engine config via `guard.websocket_max_message_size`. | +| Max outgoing message size | 1 MiB | 32 MiB | Maximum size of outgoing WebSocket messages. Soft limit configurable via `maxOutgoingMessageSize`; hard limit configurable in self-hosted engine config via `guard.websocket_max_message_size`. | +| Max WebSocket frame size | — | 32 MiB | Maximum size of a single WebSocket frame accepted by Rivet's transport edge. Configurable in self-hosted engine config via `guard.websocket_max_frame_size`. This is not configurable by browser clients and is not negotiated during the WebSocket handshake. | +| WebSocket open timeout | — | 15 seconds | Time allowed for WebSocket connection to be established, including `onBeforeConnect` and `createConnState` hooks. Connection is closed if exceeded. | +| Message ack timeout | — | 30 seconds | Time allowed for message acknowledgment before connection is closed. Only relevant in the case of a network issue and does not affect your application. | + +### Hibernating WebSocket + +Hibernating WebSockets allow actors to sleep while keeping client connections alive. All WebSocket limits above also apply to hibernating WebSockets. See [WebSocket Hibernation](/docs/actors/websocket-handler#web-socket-hibernation) for details. + +| Name | Soft Limit | Hard Limit | Description | +|------|------------|------------|-------------| +| Max pending buffer size | — | 128 MiB | Total size of all buffered messages per connection while actor is sleeping. | +| Max pending message count | — | 65,535 | Maximum number of buffered messages per connection while actor is sleeping. | +| Hibernation timeout | — | 90 seconds | Maximum time an actor has to wake up before the client is disconnected. Only relevant if something is wrong with starting actors. | + +### HTTP + +These limits affect actions that do not use `.connect()` and [low-level HTTP requests](/docs/actors/request-handler). + +| Name | Soft Limit | Hard Limit | Description | +|------|------------|------------|-------------| +| Max request body size | — | 20 MiB | Maximum size of HTTP request bodies. | +| Max response body size | — | 20 MiB | Maximum size of HTTP response bodies. | +| Request timeout | 60 seconds | — | Maximum time for an `onRequest` handler to complete. Defaults to `actionTimeout`; configure with `actionTimeout`. | + +### Networking + +| Name | Soft Limit | Hard Limit | Description | +|------|------------|------------|-------------| +| Connection ping timeout | — | 30 seconds | Connection is closed if a ping is not acknowledged within this time. Applies to both HTTP and WebSocket. Only relevant in the case of a network issue and does not affect your application. | + +### Queue + +| Name | Soft Limit | Hard Limit | Description | +|------|------------|------------|-------------| +| Max queue size | 1,000 messages | — | Maximum number of messages in the queue before new messages are rejected. Configurable via `maxQueueSize`. | +| Max queue message size | 64 KiB | 128 KiB (effective) | Maximum size of each individual queue message. Configurable via `maxQueueMessageSize`. Actual payload is slightly lower after queue serialization overhead. | + +### Actor KV Storage + +These limits apply to the low-level KV storage interface powering Rivet Actors. They likely do not affect your application, but are documented for completeness. + +| Name | Soft Limit | Hard Limit | Description | +|------|------------|------------|-------------| +| Max key size | — | 2 KiB | Maximum size of a single key. | +| Max value size | — | 128 KiB | Maximum size of a single value. | +| Max keys per operation | — | 128 | Maximum number of keys in a single batch get/put/delete operation. Does not apply to range operations (`listRange`, `deleteRange`). | +| Max batch put payload size | — | 976 KiB | Maximum total size of all key-value pairs in a single batch put operation. | +| Max storage size per actor | — | 10 GiB | Maximum total KV storage size for a single actor. | +| List default limit | — | 16,384 | Default maximum number of keys returned by a list operation. | + +### Actor SQLite Storage + +These limits apply to the [SQLite database](/docs/actors/state#sqlite-database) available via `this.sql`. SQLite data is stored through the KV layer, so the storage limit is shared with KV storage. + +| Name | Soft Limit | Hard Limit | Description | +|------|------------|------------|-------------| +| Max storage size per actor | — | 10 GiB | Maximum total storage size for a single actor. This limit is shared with KV storage. | +| Max dirty data per commit | — | 1,310,720 bytes | Maximum raw SQLite page data that can be written in a single commit. This is 320 dirty pages at 4 KiB per page. | +| Transaction deadline | 60 seconds | — | Safety backstop for `db.transaction()`. Configure per transaction with `{ timeout: milliseconds }`; there is no product-level maximum. | +| Transaction coordinator queue | — | 128 operations | Maximum admitted SQLite operations waiting behind an active coordinated transaction. Additional operations fail with `sqlite.transaction_queue_full`. | + +SQLite commit deltas are compressed and split into internal chunks before storage, but all chunks for one commit are published atomically. The commit limit is therefore based on raw dirty page bytes before compression, not the compressed delta size. + +Avoid `VACUUM` inside actor databases. It rewrites the database in one transaction and can exceed the dirty-page commit limit even when ordinary incremental writes fit. + +### Actor Runtime Socket + +The Beta [Actor Runtime Socket](/docs/actors/actor-runtime-socket) negotiates its active frame limit during the handshake. + +| Name | Soft Limit | Hard Limit | Description | +|------|------------|------------|-------------| +| Frame payload size | 32 MiB | — | Default maximum request or response payload. Self-hosted native runtimes can configure `RIVET_ACTOR_RUNTIME_SOCKET_MAX_FRAME_BYTES`; oversized responses return `ResponseTooLarge`. | + +### KV Preloading + +When an actor starts, the engine can pre-fetch KV data declared in the actor name metadata and deliver it alongside the start command. This removes round-trips to storage during actor startup. RivetKit emits the preload manifest from its own key layout and exposes per-actor overrides via `options`. Operators can still enforce a global cap in the [engine config](/docs/self-hosting/configuration) with `pegboard.preload_max_total_bytes`. In serverless mode, this data is serialized into the `/api/rivet/start` request body along with actor config and protocol metadata, so the accepted body size must be larger than the preload budget. RivetKit defaults `serverless.maxStartPayloadBytes` to 16 MiB to leave margin for the default 1 MiB preload budget and larger SQLite startup page preloads. + +| Name | Soft Limit | Hard Limit | Description | +|------|------------|------------|-------------| +| Max total preload size | 1 MiB | — | Maximum total size of all preloaded KV data sent with the start command. Configurable via `pegboard.preload_max_total_bytes`. Setting to 0 disables all preloading. | +| Max workflow preload size | 128 KiB | — | Default maximum size of preloaded workflow data for RivetKit actors. Configurable per actor via `options.preloadMaxWorkflowBytes`. Setting to 0 disables workflow preloading for that actor. | +| Max connections preload size | 64 KiB | — | Default maximum size of preloaded connection data for RivetKit actors. Configurable per actor via `options.preloadMaxConnectionsBytes`. Setting to 0 disables connections preloading for that actor. | + +### Actor Input + +See [Actor Input](/docs/actors/input) for details. + +| Name | Soft Limit | Hard Limit | Description | +|------|------------|------------|-------------| +| Max actor input size | — | 4 MiB | Maximum size of the input passed when creating an actor. | +| Max connection params size | — | 4 KiB | Maximum size of connection parameters passed when connecting to an actor. | +| Max actor key component size | — | 128 bytes | Maximum size of each component in an actor key array. | +| Max actor key total size | — | 1,024 bytes | Maximum total size of the serialized actor key string. | +| Max actor name length | — | 64 characters | Maximum length for actor and project identifiers. | + +### Rate Limiting + +| Name | Soft Limit | Hard Limit | Description | +|------|------------|------------|-------------| +| Rate limit | — | 1200 requests/minute | Default rate limit per actor per IP address with a 1 minute time bucket. | +| Max in-flight requests | — | 32 | Default maximum concurrent requests to an actor per IP address. | + +### Timeouts + +| Name | Soft Limit | Hard Limit | Description | +|------|------------|------------|-------------| +| Action timeout | 60 seconds | — | Timeout for RPC actions. Configurable via `actionTimeout`. | +| On request timeout | 60 seconds | — | Timeout for raw `onRequest` handlers. Defaults to `actionTimeout`; configure with `actionTimeout`. | +| On before connect timeout | 5 seconds | — | Timeout for the `onBeforeConnect` hook. Configurable via `onBeforeConnectTimeout`. | +| Create vars timeout | 5 seconds | — | Timeout for `createVars` hook. Configurable via `createVarsTimeout`. | +| Create conn state timeout | 5 seconds | — | Timeout for `createConnState` hook. Configurable via `createConnStateTimeout`. | +| On connect timeout | 5 seconds | — | Timeout for `onConnect` hook. Configurable via `onConnectTimeout`. | +| On migrate timeout | 30 seconds | — | Timeout for `onMigrate` hook. Configurable via `onMigrateTimeout`. | +| Sleep grace period | 15 seconds | — | Total graceful shutdown budget for both sleep and destroy. Covers `onSleep`/`onDestroy`, run handler shutdown, `waitUntil`, `keepAwake`, async raw WebSocket handlers, and connection cleanup. Configurable via `sleepGracePeriod`. | +| Sleep timeout | 30 seconds | — | Time of inactivity before actor hibernates. Configurable via `sleepTimeout`. | +| State save interval | 1 second | — | Interval between automatic state saves. Configurable via `stateSaveInterval`. | +| Connection liveness timeout | 2.5 seconds | — | Time a stateful connection can miss liveness checks before RivetKit treats it as unhealthy. Configurable via `connectionLivenessTimeout`. | +| Connection liveness interval | 5 seconds | — | Interval between stateful connection liveness checks. Configurable via `connectionLivenessInterval`. | + +### Serverless Shutdown + +These timeouts control how actors are shut down when a serverless request reaches its lifespan limit. See [Shutdown Sequence](/docs/general/runtime-modes#shutdown-sequence) for details. + +| Name | Soft Limit | Hard Limit | Description | +|------|------------|------------|-------------| +| Request lifespan | 3600 seconds (60 min) | — | Total lifespan of a serverless request before drain begins. Configurable via `requestLifespan` in [`configurePool`](/docs/general/registry-configuration). | +| Runner config drain grace period | — | 30 minutes | Time a serverless runner reserves for actors to stop gracefully. Configurable via `drainGracePeriod` in [`configurePool`](/docs/general/registry-configuration). | +| Engine serverless drain fallback | — | 10 seconds | Engine-level fallback used when no per-runner config applies. Configurable via [engine config](/docs/self-hosting/configuration) (`pegboard.serverless_drain_grace_period`). | + +### Actor Lifecycle + +| Name | Soft Limit | Hard Limit | Description | +|------|------------|------------|-------------| +| Actor start threshold | — | 30 seconds | Maximum time for an actor to start before it is considered lost and rescheduled. | +| Actor stop threshold | — | 30 minutes | Maximum time for an actor to stop before it is considered lost. | + +## Increasing Limits + +These limits are sane defaults designed to protect your application from exploits and accidental runaway bugs. If you have a use case that requires different limits, [contact us](https://rivet.dev/contact) to discuss your requirements. + +_Source doc path: /docs/actors/limits_ diff --git a/.agents/skills/multiplayer-game/reference/actors/metadata.md b/.agents/skills/multiplayer-game/reference/actors/metadata.md new file mode 100644 index 0000000..d3dcc24 --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/actors/metadata.md @@ -0,0 +1,39 @@ +# Metadata + +> Source: `src/content/docs/actors/metadata.mdx` +> Canonical URL: https://rivet.dev/docs/actors/metadata +> Description: Metadata provides information about the currently running actor. + +--- +## Actor ID + +Get the unique instance ID of the actor: + +## Actor Name + +Get the actor type name: + +This is useful when you need to know which actor type is running, especially if you have generic utility functions that are shared between different actor implementations. + +## Actor Key + +Get the actor key used to identify this actor instance: + +The key is used to route requests to the correct actor instance and can include parameters passed when creating the actor. + +Learn more about using keys for actor addressing and configuration in the [keys documentation](/docs/actors/keys). + +## Region + +Region can be accessed from the context object via `c.region`. + +`c.region` is only supported on Rivet at the moment. + +## Example Usage + +## API Reference + +- [`ActorDefinition`](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html) - Interface for defining metadata +- [`CreateOptions`](/typedoc/interfaces/rivetkit.client_mod.CreateOptions.html) - Options for creating an actor, including `region` and `input` + +_Source doc path: /docs/actors/metadata_ diff --git a/.agents/skills/multiplayer-game/reference/actors/queues.md b/.agents/skills/multiplayer-game/reference/actors/queues.md new file mode 100644 index 0000000..cf75e0b --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/actors/queues.md @@ -0,0 +1,120 @@ +# Queues & Run Loops + +> Source: `src/content/docs/actors/queues.mdx` +> Canonical URL: https://rivet.dev/docs/actors/queues +> Description: Use actor-local durable queues for serial run loops and request/response workflows. + +--- +## What are queues? + +- **Realtime**: messages are delivered to a live actor as soon as possible. +- **Durable**: messages are persisted and survive actor sleep/restart. +- **Request/response**: clients can wait for a queue completion response. +- **Scalable**: queues absorb large bursts and handle heavy backpressure safely. +- **Local per actor**: each actor instance has its own queue storage (scoped by actor key/id). + +Queues are commonly referred to as "mailboxes" in other actor frameworks. + +## What are queues good for? + +- Great for any task that changes actor state. +- Helps avoid race conditions by handling work in order. +- Makes complex behavior easier to organize. + +## Basic queue + +This is the default pattern. Define queue names in `queues`, process them in `run`, and publish from the client with `handle.send(...)`. + +## Completable messages + +Use this when you want explicit completion/ack semantics but do not need to return data. + +- `message.complete()` resolves a sender waiting on `wait: true` (or `enqueueAndWait`). It does not change durability: messages are removed from queue storage when they are received, not when they are completed. +- If processing fails before `message.complete()`, the message is not redelivered, and any waiting sender times out instead of receiving a completion. +- `status: "timedOut"` means sender timeout elapsed before `message.complete(...)`. + +## Request/reply pattern + +Use this when the sender needs data back from queued work. + +## Queue messages from within an actor + +Queueing is useful from inside actor logic too, not just from clients. + +- Use actions as entrypoints, then enqueue into the run loop to keep mutations serialized. +- You can also call `c.queue.send(...)` from other parts of `run` when needed. +- `c.queue.send(...)` confirms durable enqueue. It does not wait for processing to finish. + +## Defining queue schemas + +You can define queue types with `queue()` or with schema objects. Schema objects support [Standard Schema](https://standardschema.dev/) validators, including [Zod](https://zod.dev/). + +## Pull messages with `next` and `nextBatch` + +Use `next` when you want to wait for one queue message. +Use `nextBatch` when you want to wait for multiple queue messages. + +- Waits until messages are available unless timeout is hit. +- Omit `timeout` to wait indefinitely. + +## Poll messages + +Use `tryNext` when you need one non-blocking read. +Use `tryNextBatch` for non-blocking batch reads. + +- Returns immediately and never waits. + +## Abort signals + +Use `signal` when your receive loop needs external cancellation semantics in addition to actor shutdown behavior. + +## Multiple queues + +Multiple queues let you separate message flows by purpose. By default, receive calls race across all queues when `names` is not specified. In this pattern, prompt messages run through a streaming loop while stop messages act as control signals on a separate receive path. + +Use `iter({ names: ["prompt"] })` as the main stream and `next({ names: ["stop"] })` as a stop signal. + +## Sleeping behavior + +If an actor has a `run` handler, it does not sleep while that handler is actively doing work. It only can sleep when the run loop is blocked waiting for queue entries (for example inside `iter(...)` or `next(...)`). + +This means you can run normal code in `run` without worrying about sleep interrupting it mid-call. + +## Debugging + +- `GET /inspector/queue?limit=50` returns queue size and pending message metadata. +- `GET /inspector/summary` includes `queueSize` for quick queue health checks. +- `POST /queue/:name` with `wait: true` is useful to verify completable/request-response behavior. +- In non-dev mode, inspector endpoints require authorization. + +## Recommendations + +- Actions are for getting data, queue entries are for mutating data. +- Implement connection auth in `onBeforeConnect`. See [Authentication](/docs/actors/authentication). +- Route most state changes through one queue loop so ordering stays predictable. +- If you need more complex multi-step run loops, consider using workflows. +- Use `c.aborted` and `c.abortSignal` for actor shutdown. Use your own `AbortController` for earlier loop cancellation. +- Add `timeout` when callers need bounded wait behavior. +- Use `wait: true` only when the caller actually needs a response. + +## Pitfalls + +### Avoid `wait: true` between actors + +`wait: true` blocks the sender's run loop until the receiver finishes. Between actors, this adds unnecessary overhead and risks deadlocks, especially if the target actor needs to communicate back. If an actor sends a `wait: true` message to *itself*, it is a guaranteed deadlock because the run loop is already busy processing the current message. + +Reserve `wait: true` for external callers (HTTP handlers, CLI tools, client apps). For actor-to-actor communication, send a queue message to the other actor without `wait: true`, then have that actor send a queue message back when the work is done. + +## Tips + +### Message TTL + +Every queue message includes a `createdAt` timestamp. Use this to skip or discard stale messages in your run loop: + +### Delayed delivery + +Use [`c.schedule`](/docs/actors/schedule) to enqueue messages at a future time instead of processing them immediately: + +See [Schedule](/docs/actors/schedule) for the full scheduling API. + +_Source doc path: /docs/actors/queues_ diff --git a/.agents/skills/multiplayer-game/reference/actors/quickstart/backend.md b/.agents/skills/multiplayer-game/reference/actors/quickstart/backend.md new file mode 100644 index 0000000..86cbdf2 --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/actors/quickstart/backend.md @@ -0,0 +1,64 @@ +# Node.js & Bun Quickstart + +> Source: `src/content/docs/actors/quickstart/backend.mdx` +> Canonical URL: https://rivet.dev/docs/actors/quickstart/backend +> Description: Get started with Rivet Actors in Node.js and Bun + +--- +Prefer to start from a complete project? See the runnable [`hello-world`](https://github.com/rivet-dev/rivet/tree/main/examples/hello-world) example. + +## Steps + +### Add Rivet Skill to Coding Agent (Optional) + +If you're using an AI coding assistant (like Claude Code, Cursor, Windsurf, etc.), add Rivet skills for enhanced development assistance: + +```sh +npx skills add rivet-dev/skills +``` + +### Install Rivet + +```sh +npm install rivetkit +``` + +### Create Actors and Start Server + +Create a file with your actors, set up the registry, and start the server: + +### Run Server + +```sh Node.js +npx tsx --watch index.ts +``` + +```sh Bun +bun --watch index.ts +``` + +```sh Deno +deno run --allow-net --allow-read --allow-env --watch index.ts +``` + +Your server is now running on `http://localhost:6420`. Clients connect directly to the Rivet Engine on this port. + +Visit [http://localhost:6420](http://localhost:6420) in your browser (or point your AI agent at it) to open the Rivet developer tools and inspect your actors live. + +### Connect To The Rivet Actor + +This code can run either in your frontend or within your backend: + +### TypeScript + +See the [JavaScript client documentation](/docs/clients/javascript) for more information. + +### React + +See the [React documentation](/docs/clients/react) for more information. + +### Deploy + +## Configuration Options + +_Source doc path: /docs/actors/quickstart/backend_ diff --git a/.agents/skills/multiplayer-game/reference/actors/quickstart/cloudflare.md b/.agents/skills/multiplayer-game/reference/actors/quickstart/cloudflare.md new file mode 100644 index 0000000..e39cbe4 --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/actors/quickstart/cloudflare.md @@ -0,0 +1,165 @@ +# Cloudflare Workers Quickstart + +> Source: `src/content/docs/actors/quickstart/cloudflare.mdx` +> Canonical URL: https://rivet.dev/docs/actors/quickstart/cloudflare +> Description: Set up a Rivet project locally targeting Cloudflare Workers. + +--- +Set up a Rivet project locally that runs on Cloudflare Workers. The `@rivetkit/cloudflare-workers` package wires the WebAssembly runtime for you. + +Prefer to start from a complete project? See the runnable [`hello-world-cloudflare-workers`](https://github.com/rivet-dev/rivet/tree/main/examples/hello-world-cloudflare-workers) example (with [Hono](https://github.com/rivet-dev/rivet/tree/main/examples/hello-world-cloudflare-workers-hono) and [raw router](https://github.com/rivet-dev/rivet/tree/main/examples/hello-world-cloudflare-workers-raw) variants). + +## Steps + +### Install Packages + +```sh +npm install rivetkit @rivetkit/cloudflare-workers +npm install --save-dev wrangler +``` + +### Configure Wrangler + +```toml wrangler.toml +name = "rivetkit-cloudflare" +main = "src/index.ts" +compatibility_date = "2025-04-01" +compatibility_flags = ["nodejs_compat"] +``` + +The `nodejs_compat` flag is required so the runtime can read its connection config from `process.env`. + +### Create the Worker + +`createHandler` serves the Rivet manager API on `/api/rivet`. Pick how you want to handle your own routes: + +- **Default**: Rivet handles everything. +- **Hono**: Mount a [Hono](https://hono.dev) app for your routes (`npm install hono`). +- **Raw**: Provide a `fetch` and route requests yourself. + +```ts Default @nocheck +import { actor } from "rivetkit"; +import { createHandler } from "@rivetkit/cloudflare-workers"; + +const counter = actor({ + state: { count: 0 }, + actions: { + increment: (c, amount = 1) => { + c.state.count += amount; + return c.state.count; + }, + }, +}); + +export default createHandler({ use: { counter } }); +``` + +```ts Hono @nocheck +import { actor } from "rivetkit"; +import { createClient } from "rivetkit/client"; +import { createHandler, setup } from "@rivetkit/cloudflare-workers"; +import { Hono } from "hono"; + +const counter = actor({ + state: { count: 0 }, + actions: { + increment: (c, amount = 1) => { + c.state.count += amount; + return c.state.count; + }, + }, +}); + +// `setup` returns a typed registry, so the client below is fully typed. +const registry = setup({ use: { counter } }); + +const app = new Hono(); + +app.get("/", (c) => c.text("Hello from Hono + Rivet Actors!")); + +app.post("/increment/:name", async (c) => { + // `createClient` reads RIVET_ENDPOINT from the environment (passed by `rivet + // dev`, or set as a Worker secret in production). + const client = createClient(); + const count = await client.counter.getOrCreate(c.req.param("name")).increment(1); + return c.json({ count }); +}); + +// Rivet keeps `/api/rivet`; Hono handles everything else. +export default createHandler(registry, { fetch: app.fetch }); +``` + +```ts Raw @nocheck +import { actor } from "rivetkit"; +import { createClient } from "rivetkit/client"; +import { createHandler, setup } from "@rivetkit/cloudflare-workers"; + +const counter = actor({ + state: { count: 0 }, + actions: { + increment: (c, amount = 1) => { + c.state.count += amount; + return c.state.count; + }, + }, +}); + +// `setup` returns a typed registry, so the client below is fully typed. +const registry = setup({ use: { counter } }); + +// Rivet keeps `/api/rivet`; your `fetch` handles everything else. +export default createHandler(registry, { + fetch: async (request: Request) => { + const url = new URL(request.url); + + if (url.pathname === "/") { + return new Response("Hello from a raw Rivet Worker router!"); + } + + const increment = url.pathname.match(/^\/increment\/(.+)$/); + if (request.method === "POST" && increment) { + // `createClient` reads RIVET_ENDPOINT from the environment (passed by + // `rivet dev`, or set as a Worker secret in production). + const client = createClient(); + const count = await client.counter.getOrCreate(increment[1]).increment(1); + return Response.json({ count }); + } + + return new Response("Not found", { status: 404 }); + }, +}); +``` + +### Run Locally + +Start Rivet. The CLI runs the local engine and spawns `wrangler dev` for you: + +```sh +npx @rivetkit/cli dev --provider cloudflare +``` + +Visit [http://localhost:6420](http://localhost:6420) in your browser (or point your AI agent at it) to open the Rivet developer tools and inspect your actors live. + +### Connect To The Rivet Actor + +This code can run either in your frontend or within your backend. It connects directly to the local engine on `http://localhost:6420`: + +### TypeScript + +See the [JavaScript client documentation](/docs/clients/javascript) for more information. + +### React + +See the [React documentation](/docs/clients/react) for more information. + +### Deploy + +Ready to ship? See [Deploying to Cloudflare Workers](/docs/deploy/cloudflare). + +## Related + +- [Quickstart](/docs/actors/quickstart) +- [Deploying to Cloudflare Workers](/docs/deploy/cloudflare) +- [SQLite](/docs/actors/sqlite) + +_Source doc path: /docs/actors/quickstart/cloudflare_ diff --git a/.agents/skills/multiplayer-game/reference/actors/quickstart/effect.md b/.agents/skills/multiplayer-game/reference/actors/quickstart/effect.md new file mode 100644 index 0000000..fc252e9 --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/actors/quickstart/effect.md @@ -0,0 +1,239 @@ +# Effect.ts Quickstart (Beta) + +> Source: `src/content/docs/actors/quickstart/effect.mdx` +> Canonical URL: https://rivet.dev/docs/actors/quickstart/effect +> Description: Build a Rivet Actor with the Effect SDK + +--- +Effect support is in beta. The `@rivetkit/effect` API may change between releases. See the [`hello-world-effect`](https://github.com/rivet-dev/rivet/tree/main/examples/hello-world-effect) and [`chat-room-effect`](https://github.com/rivet-dev/rivet/tree/main/examples/chat-room-effect) examples for complete runnable projects. + +## Steps + +### Install Rivet + +Add `rivetkit`, the Effect SDK, and its Effect peers: + +```sh +npm install rivetkit @rivetkit/effect effect @effect/platform-node +``` + +### Define Your Actor + +Split each actor into a public contract and a server-only implementation so the contract can be imported from client code without leaking server details. + +The contract declares the actor and its actions. Actions are standalone values with explicit [`effect/Schema`](https://effect.website/docs/schema/introduction/) payloads and successes, validated end to end: + +```ts src/actors/counter/api.ts @nocheck +import { Action, Actor } from "@rivetkit/effect"; +import { Schema } from "effect"; + +export const Increment = Action.make("Increment", { + payload: { amount: Schema.Number }, + success: Schema.Number, +}); + +export const GetCount = Action.make("GetCount", { + success: Schema.Number, +}); + +export const Counter = Actor.make("Counter", { + actions: [Increment, GetCount], +}); +``` + +The implementation registers the actor with `.toLayer`. The wake function runs once when the actor awakes and returns the action handlers. Persisted state is accessed through a `SubscriptionRef`-like `State` API: + +```ts src/actors/counter/live.ts @nocheck +import { Actor, State } from "@rivetkit/effect"; +import { Effect, Schema } from "effect"; +import { Counter } from "./api.ts"; + +export const CounterLive = Counter.toLayer( + Effect.fnUntraced(function* ({ rawRivetkitContext, state }) { + return Counter.of({ + Increment: Effect.fnUntraced(function* ({ payload }) { + const next = yield* State.updateAndGet(state, (current) => ({ + count: current.count + payload.amount, + })).pipe(Effect.orDie); + + // Broadcast the new value to every connected client. + rawRivetkitContext.broadcast("newCount", next.count); + + return next.count; + }), + GetCount: () => + State.get(state).pipe( + Effect.map((current) => current.count), + Effect.orDie, + ), + }); + }), + { + state: { + schema: Schema.Struct({ count: Schema.Number }), + initialValue: () => ({ count: 0 }), + }, + name: "Counter", + icon: "calculator", + }, +); +``` + +### Serve The Registry + +Compose the actor layers and serve them with `Registry.serve`. `Registry.layer()` reads engine config from the environment, and the actor layer is provided a `Client` so actors can call other actors: + +```ts src/main.ts @nocheck +import { NodeRuntime } from "@effect/platform-node"; +import { Client, Registry } from "@rivetkit/effect"; +import { Layer } from "effect"; +import { CounterLive } from "./actors/counter/live.ts"; + +const endpoint = process.env.RIVET_ENDPOINT ?? "http://127.0.0.1:6420"; + +const ActorsLayer = CounterLive.pipe(Layer.provide(Client.layer({ endpoint }))); + +const MainLayer = Registry.serve(ActorsLayer).pipe(Layer.provide(Registry.layer())); + +// Keeps the layer alive. Tears down on SIGINT/SIGTERM. +Layer.launch(MainLayer).pipe(NodeRuntime.runMain); +``` + +### Run The Server + +Set `RIVET_RUN_ENGINE=1` to spawn a local Rivet Engine alongside the server. The engine binary is downloaded and cached the first time you run, so there is nothing else to install: + +```sh +RIVET_RUN_ENGINE=1 npx tsx --watch src/main.ts +``` + +Your server now connects to the Rivet Engine on `http://localhost:6420`. Clients connect directly to the engine on this port. + +Visit [http://localhost:6420](http://localhost:6420) in your browser (or point your AI agent at it) to open the Rivet developer tools and inspect your actors live. + +To point at a remote engine instead, set `RIVET_ENDPOINT=https://...` and omit `RIVET_RUN_ENGINE`. + +### Connect To The Rivet Actor + +This code can run either in your frontend or within your backend: + +### Effect + +The Effect client imports the same actor contract from your registry. `Counter.client` yields a typed accessor backed by the client layer: + +```ts src/client.ts @nocheck +import { NodeRuntime } from "@effect/platform-node"; +import { Client } from "@rivetkit/effect"; +import { Effect } from "effect"; +import { Counter } from "./actors/counter/api.ts"; + +const program = Effect.gen(function* () { + const counter = (yield* Counter.client).getOrCreate("my-counter"); + + const count = yield* counter.Increment({ amount: 3 }); + yield* Effect.log(`New count: ${count}`); + + const total = yield* counter.GetCount(); + yield* Effect.log(`Total: ${total}`); +}); + +const ClientLayer = Client.layer({ endpoint: "http://localhost:6420" }); + +program.pipe(Effect.provide(ClientLayer), NodeRuntime.runMain); +``` + +With the server still running, start the client in another terminal: + +```sh +npx tsx src/client.ts +``` + +See the [`chat-room-effect`](https://github.com/rivet-dev/rivet/tree/main/examples/chat-room-effect) example for a larger project with typed errors and actor-to-actor calls. + +### TypeScript + +A plain RivetKit client can call your Effect actor by name through the same engine. Actor and action names are resolved at runtime, so the client is untyped here: + +```ts client.ts @nocheck +import { createClient } from "rivetkit/client"; + +const client = createClient("http://localhost:6420"); + +const counter = client.Counter.getOrCreate(["my-counter"]); + +const count = await counter.Increment({ amount: 3 }); +console.log("New count:", count); +``` + +See the [JavaScript client documentation](/docs/clients/javascript) for more information. + +### Deploy + +## Feature Support + +The Effect SDK wraps the most common actor features with typed, schema-validated APIs. Everything else is still fully usable through the raw RivetKit context (see [Raw Escape Hatch](#raw-escape-hatch) below), so no feature is off limits, it just isn't typed yet. + +| Feature | Effect-native API | Access | +| --- | --- | --- | +| Actor contract & actions | `Actor.make`, `Action.make` | Typed | +| Persisted state | `State.get` / `set` / `update` / `updateAndGet` / `changes` | Typed | +| Typed client | `Actor.client`, `Client.layer` | Typed | +| Typed errors | `RivetError` | Typed | +| Logging | `RivetLogger` | Typed | +| Sleep request | `Actor.Sleep` | Typed | +| Actor address (`actorId` / `name` / `key`) | `Actor.CurrentAddress` | Typed | +| Registry serve / test / web handler | `Registry` | Typed | +| [Events & broadcast](/docs/actors/events) | Not yet wrapped | `rawRivetkitContext.broadcast(...)` | +| [Schedule](/docs/actors/schedule) | Not yet wrapped | `rawRivetkitContext.schedule.*` | +| [Embedded SQLite](/docs/actors/sqlite) | Not yet wrapped | `rawRivetkitContext.db.execute(...)` | +| [Destroy](/docs/actors/lifecycle) | Not yet wrapped | `rawRivetkitContext.destroy()` | +| Queues, connections, vars, alarms | Not yet wrapped | `rawRivetkitContext.*` | +| Lifecycle hooks (`onSleep` / `onDestroy`) | Not yet wrapped | `rawRivetkitContext.*` | +| Raw HTTP / WebSocket handlers | Not yet wrapped | `rawRivetkitContext.*` | + +### Raw Escape Hatch + +Every wake function receives `rawRivetkitContext`, the underlying RivetKit [actor context](/docs/actors). Reach for it to use any feature that does not have a typed wrapper yet. The typed `state` argument and the raw context point at the same actor, so you can mix both: + +```ts src/actors/counter/live.ts @nocheck +export const CounterLive = Counter.toLayer( + Effect.fnUntraced(function* ({ rawRivetkitContext, state }) { + return Counter.of({ + Increment: Effect.fnUntraced(function* ({ payload }) { + // Typed state wrapper + const next = yield* State.updateAndGet(state, (current) => ({ + count: current.count + payload.amount, + })).pipe(Effect.orDie); + + // Untyped features run through the raw context + rawRivetkitContext.broadcast("newCount", next.count); + rawRivetkitContext.schedule.after(1_000, "tick", {}); + + return next.count; + }), + }); + }), + { + state: { + schema: Schema.Struct({ count: Schema.Number }), + initialValue: () => ({ count: 0 }), + }, + name: "Counter", + }, +); +``` + +Calls through `rawRivetkitContext` are not validated by `effect/Schema` and their payloads are typed as they are in the base RivetKit API. + +## Next Steps + + +- [Actions](/docs/actors/actions) — Define the RPC surface clients call on your actor. + + +- [State](/docs/actors/state) — Persist and load actor state across sleeps and restarts. + + +- [Events](/docs/actors/events) — Broadcast realtime updates to connected clients. + +_Source doc path: /docs/actors/quickstart/effect_ diff --git a/.agents/skills/multiplayer-game/reference/actors/quickstart/next-js.md b/.agents/skills/multiplayer-game/reference/actors/quickstart/next-js.md new file mode 100644 index 0000000..f621606 --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/actors/quickstart/next-js.md @@ -0,0 +1,117 @@ +# Next.js Quickstart + +> Source: `src/content/docs/actors/quickstart/next-js.mdx` +> Canonical URL: https://rivet.dev/docs/actors/quickstart/next-js +> Description: Get started with Rivet Actors in Next.js + +--- +### Add Rivet Skill to Coding Agent (Optional) + +If you're using an AI coding assistant (like Claude Code, Cursor, Windsurf, etc.), add Rivet skills for enhanced development assistance: + +```sh +npx skills add rivet-dev/skills +``` + +### Create a Next.js App + +```sh +npx create-next-app@latest my-app +cd my-app +``` + +### Install RivetKit + +### Create an Actor + +Create a file at `src/rivet/registry.ts` with a simple counter actor: + +### Setup Rivet API route + +Create a file at `src/app/api/rivet/[...all]/route.ts` to setup the API routes: + +```ts src/app/api/rivet/[...all]/route.ts @nocheck +import { toNextHandler } from "@rivetkit/next-js"; +import { registry } from "@/rivet/registry"; + +export const maxDuration = 300; + +export const { GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS } = toNextHandler(registry); +``` + +### Use the Actor in a component + +Create a Counter component and add it to your page: + +```tsx src/components/Counter.tsx @nocheck +"use client"; + +import { createRivetKit } from "@rivetkit/next-js/client"; +import type { registry } from "@/rivet/registry"; +import { useState } from "react"; + +export const { useActor } = createRivetKit({ + endpoint: + process.env.NEXT_PUBLIC_RIVET_ENDPOINT ?? "http://localhost:3000/api/rivet", + namespace: process.env.NEXT_PUBLIC_RIVET_NAMESPACE, + token: process.env.NEXT_PUBLIC_RIVET_TOKEN, +}); + +export function Counter() { + const [count, setCount] = useState(0); + + // Get or create a counter actor for the key "my-counter" + const counter = useActor({ + name: "counter", + key: ["my-counter"] + }); + + // Listen to realtime events + counter.useEvent("newCount", (x: number) => setCount(x)); + + const increment = async () => { + // Call actions + await counter.connection?.increment(1); + }; + + return ( +
+

Count: {count}

+ +
+ ); +} +``` + +```tsx src/app/page.tsx @nocheck +import { Counter } from "@/components/Counter"; + +export default function Home() { + return ( +
+

My App

+ +
+ ); +} +``` + +For information about the Next.js client API, see the [React Client API Reference](/docs/clients/react). + +### Run Locally + +Start the Next.js dev server. Rivet auto-starts a local engine alongside it: + +```sh +npm run dev +``` + +Open `http://localhost:3000` in your browser to use the app. + +Visit [http://localhost:6420](http://localhost:6420) in your browser (or point your AI agent at it) to open the Rivet developer tools and inspect your actors live. + +### Deploy to Vercel + +See the [Vercel deployment guide](/docs/deploy/vercel) for detailed instructions on deploying your Rivet app to Vercel. + +_Source doc path: /docs/actors/quickstart/next-js_ diff --git a/.agents/skills/multiplayer-game/reference/actors/quickstart/react.md b/.agents/skills/multiplayer-game/reference/actors/quickstart/react.md new file mode 100644 index 0000000..8722fed --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/actors/quickstart/react.md @@ -0,0 +1,82 @@ +# React Quickstart + +> Source: `src/content/docs/actors/quickstart/react.mdx` +> Canonical URL: https://rivet.dev/docs/actors/quickstart/react +> Description: Build realtime React applications with Rivet Actors + +--- +## Steps + +### Add Rivet Skill to Coding Agent (Optional) + +If you're using an AI coding assistant (like Claude Code, Cursor, Windsurf, etc.), add Rivet skills for enhanced development assistance: + +```sh +npx skills add rivet-dev/skills +``` + +### Install Dependencies + +```sh +npm install rivetkit @rivetkit/react +``` + +### Create Backend Actor and Start Server + +Create your actor registry on the backend and start the server: + +### Create React Frontend + +Set up your React application: + +For detailed information about the React client API, see the [React Client API Reference](/docs/clients/react). + +### Setup Vite Configuration + +Configure Vite for development: + +```ts vite.config.ts @nocheck +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' + +export default defineConfig({ + plugins: [react()], + server: { + port: 5173, + }, +}) +``` + +### Run Your Application + +Start both the backend and frontend: + +**Terminal 1**: Start the backend + +```sh Node.js +npx tsx --watch backend/index.ts +``` + +```sh Bun +bun --watch backend/index.ts +``` + +```sh Deno +deno run --allow-net --allow-read --allow-env --watch backend/index.ts +``` + +**Terminal 2**: Start the frontend + +```sh Frontend +npx vite +``` + +Open `http://localhost:5173` in your browser. Try opening multiple tabs to see realtime sync in action. + +Visit [http://localhost:6420](http://localhost:6420) in your browser (or point your AI agent at it) to open the Rivet developer tools and inspect your actors live. + +### Deploy + +## Configuration Options + +_Source doc path: /docs/actors/quickstart/react_ diff --git a/.agents/skills/multiplayer-game/reference/actors/quickstart/rust.md b/.agents/skills/multiplayer-game/reference/actors/quickstart/rust.md new file mode 100644 index 0000000..1e55471 --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/actors/quickstart/rust.md @@ -0,0 +1,249 @@ +# Rust Quickstart (Beta) + +> Source: `src/content/docs/actors/quickstart/rust.mdx` +> Canonical URL: https://rivet.dev/docs/actors/quickstart/rust +> Description: Build a Rivet Actor in Rust + +--- +Rust support is in beta. The supported public Rust API is `rivetkit` and `rivetkit-client`; lower-level crates are internal implementation details and do not carry a stability guarantee. See the full API reference on [docs.rs/rivetkit](https://docs.rs/rivetkit), or the runnable [`hello-world-rust`](https://github.com/rivet-dev/rivet/tree/main/examples/hello-world-rust) example. + +## Steps + +### Add Rivet + +Add the `rivetkit` crate and its companions: + +```sh +cargo add rivetkit anyhow async-trait +cargo add serde --features derive +cargo add tokio --features full +``` + +### Define Your Actor + +Put the actor in `src/lib.rs` so both your server and your client can share the same types. An actor is a type that implements `Actor`, plus one `Handles` implementation for each action. Persisted state lives in `type State`; ephemeral runtime state is just fields on your actor struct. + +```rust src/lib.rs +use std::{future::Future, pin::Pin, sync::Arc}; + +use async_trait::async_trait; +use rivetkit::prelude::*; +use serde::{Deserialize, Serialize}; + +type BoxFuture = Pin> + Send>>; + +pub struct Counter; + +#[derive(Default, Serialize, Deserialize)] +pub struct CounterState { + pub count: i64, +} + +#[derive(Serialize, Deserialize)] +pub struct Increment { + pub amount: i64, +} + +impl Action for Increment { + type Output = i64; + + const NAME: &'static str = "increment"; +} + +#[derive(Serialize, Deserialize)] +pub struct NewCount { + pub count: i64, +} + +impl Event for NewCount { + const NAME: &'static str = "newCount"; +} + +#[async_trait] +impl Actor for Counter { + type State = CounterState; + type Input = (); + type Actions = (Increment,); + type Events = (NewCount,); + type Queue = (); + type ConnParams = (); + type ConnState = (); + type Action = action::Raw; + + async fn create_state(_ctx: &Ctx, _input: Self::Input) -> Result { + Ok(CounterState::default()) + } + + async fn create(_ctx: &Ctx) -> Result { + Ok(Self) + } +} + +impl Handles for Counter { + type Future = BoxFuture; + + fn handle(self: Arc, ctx: Ctx, action: Increment) -> Self::Future { + Box::pin(async move { + let count = { + let mut state = ctx.state_mut(); + state.count += action.amount; + state.count + }; + ctx.emit(NewCount { count })?; + Ok(count) + }) + } +} + +pub fn registry() -> Registry { + let mut registry = Registry::new(); + registry.register_actor::("counter"); + registry +} +``` + +### Serve The Registry + +Your `src/main.rs` just starts the registry from the library: + +```rust src/main.rs +#[tokio::main] +async fn main() -> anyhow::Result<()> { + counter::registry().start().await +} +``` + +Replace `counter` with your crate name (the package `name` in `Cargo.toml`, with dashes as underscores). + +### Run The Server + +The Rust runtime connects to the Rivet Engine. Setting `RIVETKIT_ENGINE_AUTO_DOWNLOAD=1` lets the runtime download and cache a matching engine binary the first time you run, so there is nothing else to install: + +```sh +RIVETKIT_ENGINE_AUTO_DOWNLOAD=1 cargo run +``` + +Your server now connects to the Rivet Engine on `http://localhost:6420`. Clients connect directly to the engine on this port. + +Visit [http://localhost:6420](http://localhost:6420) in your browser (or point your AI agent at it) to open the Rivet developer tools and inspect your actors live. + +Already have an engine binary? Set `RIVET_ENGINE_BINARY_PATH=/path/to/rivet-engine` to point at it instead. If you are working inside the [Rivet monorepo](https://github.com/rivet-dev/rivet), a local `cargo build -p rivet-engine` is discovered automatically from `target/debug`. + +### Connect To The Rivet Actor + +This code can run either in your frontend or within your backend: + +### Rust + +Add a `src/bin/client.rs` that imports the same actor types from your library. There is no need to redefine the actor on the client. + +```rust src/bin/client.rs +use counter::{Counter, Increment, NewCount}; +use rivetkit::{ + client::{Client, ClientConfig}, + prelude::*, + TypedClientExt, +}; + +#[tokio::main] +async fn main() -> Result<()> { + let client = Client::new(ClientConfig::new("http://localhost:6420").namespace("default")); + + let counter = client.get_or_create_typed_default::("counter", ["my-counter"])?; + let count = counter.send(Increment { amount: 3 }).await?; + println!("New count: {count}"); + + let connection = counter.connect(); + connection + .on::(|event| println!("Count changed: {}", event.count)) + .await; + connection.send(Increment { amount: 1 }).await?; + + Ok(()) +} +``` + +With the server still running, start the client in another terminal: + +```sh +cargo run --bin client +``` + +See the [`hello-world-rust`](https://github.com/rivet-dev/rivet/tree/main/examples/hello-world-rust) example for a complete runnable counter. + +### TypeScript + +A TypeScript client can call your Rust actor by name through the same engine. Actor and action names are resolved at runtime, so the client is untyped here: + +```ts client.ts @nocheck +import { createClient } from "rivetkit/client"; + +const client = createClient("http://localhost:6420"); + +const counter = client.counter.getOrCreate(["my-counter"]); + +const counterConnection = counter.connect(); +counterConnection.on("newCount", (event) => { + console.log("Event count:", event.count); +}); + +const count = await counterConnection.increment(3); +console.log("New count:", count); + +await counterConnection.increment(1); +``` + +See the [JavaScript client documentation](/docs/clients/javascript) for more information. + +### React + +```tsx Counter.tsx @nocheck +import { createRivetKit } from "@rivetkit/react"; +import { useState } from "react"; + +const { useActor } = createRivetKit("http://localhost:6420"); + +function Counter() { + const [count, setCount] = useState(0); + + const counter = useActor({ + name: "counter", + key: ["my-counter"], + }); + + const increment = async () => { + await counter.connection?.increment(1); + }; + + counter.useEvent("newCount", (event) => { + setCount(event.count); + }); + + return ( +
+

Count: {count}

+ +
+ ); +} +``` + +See the [React documentation](/docs/clients/react) for more information. + +### Deploy + +## Next Steps + + +- [API Reference](https://docs.rs/rivetkit) — Full `rivetkit` crate documentation on docs.rs. + + +- [Actions](/docs/actors/actions) — Define the RPC surface clients call on your actor. + + +- [State](/docs/actors/state) — Persist and load actor state across sleeps and restarts. + + +- [Events](/docs/actors/events) — Broadcast realtime updates to connected clients. + +_Source doc path: /docs/actors/quickstart/rust_ diff --git a/.agents/skills/multiplayer-game/reference/actors/quickstart/supabase.md b/.agents/skills/multiplayer-game/reference/actors/quickstart/supabase.md new file mode 100644 index 0000000..20b59d0 --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/actors/quickstart/supabase.md @@ -0,0 +1,109 @@ +# Supabase Functions Quickstart + +> Source: `src/content/docs/actors/quickstart/supabase.mdx` +> Canonical URL: https://rivet.dev/docs/actors/quickstart/supabase +> Description: Set up a Rivet project locally targeting Supabase Edge Functions. + +--- +Set up a Rivet project locally that runs on Supabase Edge Functions. The `@rivetkit/supabase` package wires the WebAssembly runtime for you. + +Prefer to start from a complete project? See the runnable [`hello-world-supabase-functions`](https://github.com/rivet-dev/rivet/tree/main/examples/hello-world-supabase-functions) example. + +## Steps + +### Prerequisites + +- [Node.js](https://nodejs.org/) +- [Supabase CLI](https://supabase.com/docs/guides/cli) +- Docker, for Supabase's local Edge Runtime + +The CLI runs the local Rivet engine as a bundled native binary, so Docker is only needed for Supabase itself. A Supabase project is only needed to deploy. + +### Create the Function + +```sh +npx supabase functions new rivet +``` + +Add the packages used by the function: + +```sh +npm install rivetkit @rivetkit/supabase +``` + +### Configure the Function + +Call `serve` from `@rivetkit/supabase`. It loads the WebAssembly runtime and serves the Rivet handler. + +```ts supabase/functions/rivet/index.ts @nocheck +import { actor } from "rivetkit"; +import { serve, setup } from "@rivetkit/supabase"; + +const counter = actor({ + state: { count: 0 }, + actions: { + increment: (c, amount = 1) => { + c.state.count += amount; + return c.state.count; + }, + }, +}); + +// `setup` returns a typed registry, so a client can type itself with +// `typeof registry`. +export const registry = setup({ use: { counter } }); + +await serve(registry); +``` + +Add a `deno.json` next to the function so the deploy bundles only the WebAssembly runtime. It points `rivetkit` at the pre-bundled `@rivetkit/supabase`, keeping the deploy small. Without it, the deploy pulls Rivet's native engine and 413s. + +```json supabase/functions/rivet/deno.json +{ + "imports": { + "rivetkit": "npm:@rivetkit/supabase", + "@rivetkit/supabase": "npm:@rivetkit/supabase" + } +} +``` + +Your function code keeps importing from `rivetkit` as usual. The import map only changes how Deno resolves it at bundle time. + +### Run Locally + +Start Rivet. The CLI runs the local engine, spawns `supabase functions serve` for you, and populates the connection values: + +```sh +npx @rivetkit/cli dev --provider supabase +``` + +Visit [http://localhost:6420](http://localhost:6420) in your browser (or point your AI agent at it) to open the Rivet developer tools and inspect your actors live. + +### Call the Actor + +Connect to your actor from a client. This connects directly to the local engine on `http://localhost:6420`: + +```ts client.ts @nocheck +import { createClient } from "rivetkit/client"; +import type { registry } from "./supabase/functions/rivet/index"; + +const client = createClient("http://localhost:6420"); + +const counter = client.counter.getOrCreate(["my-counter"]); +const count = await counter.increment(3); +console.log("New count:", count); +``` + +See the [JavaScript client documentation](/docs/clients/javascript) for more information. + +### Deploy + +Ready to ship? See [Deploying to Supabase Functions](/docs/deploy/supabase). + +## Related + +- [Quickstart](/docs/actors/quickstart) +- [Deploying to Supabase Functions](/docs/deploy/supabase) +- [SQLite](/docs/actors/sqlite) + +_Source doc path: /docs/actors/quickstart/supabase_ diff --git a/.agents/skills/multiplayer-game/reference/actors/request-handler.md b/.agents/skills/multiplayer-game/reference/actors/request-handler.md new file mode 100644 index 0000000..0536b9c --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/actors/request-handler.md @@ -0,0 +1,79 @@ +# Low-Level HTTP Request Handler + +> Source: `src/content/docs/actors/request-handler.mdx` +> Canonical URL: https://rivet.dev/docs/actors/request-handler +> Description: Actors can handle HTTP requests through the `onRequest` handler. + +--- +For most use cases, [actions](/docs/actors/actions) provide high-level API powered by HTTP that's easier to work with than low-level HTTP. However, low-level handlers are required when implementing custom use cases or integrating external libraries that need direct access to the underlying HTTP `Request`/`Response` objects or WebSocket connections. + +## Handling HTTP Requests + +The `onRequest` handler processes HTTP requests sent to your actor. It receives the actor context and a standard `Request` object and returns a `Response` object. + +### Raw HTTP + +### Hono + +See also the [raw fetch handler example](https://github.com/rivet-dev/rivet/tree/main/examples/raw-fetch-handler). + +## Sending Requests To Actors + +### Via RivetKit Client + +Use the `.fetch()` method on an actor handle to send HTTP requests to the actor's `onRequest` handler. This can be executed from either your frontend or backend. + +### Via getGatewayUrl + +Use `.getGatewayUrl()` to get the raw gateway URL for the actor. This is useful when you need to use the URL with external tools or custom HTTP clients. + +### Via HTTP API + +This handler can be accessed with raw HTTP using `https://api.rivet.dev/gateway/{actorId}/request/{...path}`. + +For example, to call `POST /increment` on the counter actor above: + +```bash +curl -X POST "https://api.rivet.dev/gateway/{actorId}/request/increment" \ + -H "x-rivet-token: {token}" +``` + +The request is routed to the actor's `onRequest` handler where: + +- `request.method` is `"POST"` +- `request.url` ends with `/increment` (the path after `/request/`) +- Headers, body, and other request properties are passed through unchanged + +See the [HTTP API reference](/docs/actors/http-api) for more information on HTTP routing and authentication. + +### Via Proxying Requests + +You can proxy HTTP requests from your own server to actor handlers using the RivetKit client. This is useful when you need to add custom authentication, rate limiting, or request transformation before forwarding to actors. + +## Connection & Lifecycle Hooks + +`onRequest` will trigger the `onBeforeConnect`, `onConnect`, and `onDisconnect` hooks. Read more about [lifecycle hooks](/docs/actors/lifecycle). + +Requests in flight will be listed in `c.conns`. Read more about [connections](/docs/actors/connections). + +## WinterTC Compliance + +The `onRequest` handler is WinterTC compliant and will work with existing libraries using the standard `Request` and `Response` types. + +## Limitations + +- Does not support streaming responses & server-sent events at the moment. See the [tracking issue](https://github.com/rivet-dev/rivet/issues/3529). +- `OPTIONS` requests currently are handled by Rivet and are not passed to `onRequest` + +## Advanced + +### Skip Ready Wait + +Requests are normally held at the gateway until the actor is ready. Pass `skipReadyWait: true` on `handle.fetch()` to deliver immediately, including while the actor is still starting or in the [sleep grace period](/docs/actors/lifecycle#shutdown-sequence). See [Skip Ready Wait](/docs/clients/javascript#skip-ready-wait) for details. + +## API Reference + +- [`RequestContext`](/typedoc/interfaces/rivetkit.mod.RequestContext.html) - Context for HTTP request handlers +- [`ActorDefinition`](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html) - Interface for defining request handlers + +_Source doc path: /docs/actors/request-handler_ diff --git a/.agents/skills/multiplayer-game/reference/actors/scaling.md b/.agents/skills/multiplayer-game/reference/actors/scaling.md new file mode 100644 index 0000000..b597115 --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/actors/scaling.md @@ -0,0 +1,10 @@ +# Scaling & Concurrency + +> Source: `src/content/docs/actors/scaling.mdx` +> Canonical URL: https://rivet.dev/docs/actors/scaling +> Description: This page has moved to [design patterns](/docs/actors/design-patterns). + +--- + + +_Source doc path: /docs/actors/scaling_ diff --git a/.agents/skills/multiplayer-game/reference/actors/schedule.md b/.agents/skills/multiplayer-game/reference/actors/schedule.md new file mode 100644 index 0000000..fc9c631 --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/actors/schedule.md @@ -0,0 +1,190 @@ +# Schedule & Cron + +> Source: `src/content/docs/actors/schedule.mdx` +> Canonical URL: https://rivet.dev/docs/actors/schedule +> Description: Run durable one-shot and recurring actor actions on a schedule. + +--- +Rivet Actor schedules invoke actions on the same actor and survive sleep, restarts, upgrades, and crashes. Use one-shot schedules for delayed work, cron jobs for calendar-based work, and fixed intervals for frequent jobs. + +## Quick start + +```ts After +const reminders = actor({ + onCreate: async (c) => { + await c.schedule.after(30_000, "sendReminder", "reminder-123"); + }, + actions: { + sendReminder: (_c, reminderId: string) => { + console.log("Sending reminder", reminderId); + }, + }, +}); +``` + +```ts Cron +const reports = actor({ + onCreate: async (c) => { + await c.cron.set({ + name: "daily-report", + expression: "0 9 * * *", + action: "runReport", + args: ["sales"], // Optional. + timezone: "America/Los_Angeles", // Optional; defaults to UTC. + maxHistory: 100, // Optional; defaults to 100. Set to 0 to disable. + }); + }, + actions: { + runReport: (_c, report: string) => { + console.log("Running report", report); + }, + }, +}); +``` + +```ts Every +const cache = actor({ + onCreate: async (c) => { + await c.cron.every({ + name: "refresh-cache", + interval: 60_000, // Minimum 5 seconds. + action: "refreshCache", + args: ["products"], // Optional. + maxHistory: 100, // Optional; defaults to 100. Set to 0 to disable. + }); + }, + actions: { + refreshCache: (_c, cache: string) => { + console.log("Refreshing cache", cache); + }, + }, +}); +``` + +## Sleep Between Runs + +[Actors do not need to stay awake](/docs/actors/lifecycle#sleeping) while waiting for a scheduled action. Once an actor becomes idle, it can sleep normally. Sleeping does not pause or remove its schedules. Rivet wakes the actor when the next action is due, whether that is seconds or arbitrarily far in the future. + +Recurring Cron and fixed-interval schedules continue until they are updated or deleted. One-shot schedules remain pending until their target time. + +## One-shot schedules + +### Run after a delay + +Runs once after the given delay in milliseconds and returns the generated schedule ID. + +```ts +const id = await c.schedule.after(5_000, "sendReminder", reminderId); +``` + +### Run at a specific time + +Runs once at a Unix timestamp in milliseconds. + +```ts +const id = await c.schedule.at(Date.parse("2026-08-01T09:00:00Z"), "openSale", saleId); +``` + +### Inspect and cancel schedules + +```ts +const schedule = await c.schedule.get(id); +const pending = await c.schedule.list(); +const cancelled = await c.schedule.cancel(id); +``` + +## Recurring jobs + +### Run on a cron schedule + +Cron jobs use standard five-field expressions. Names are unique per actor, and reusing a name updates the existing job. The timezone defaults to UTC. + +```ts +await c.cron.set({ + name: "daily-report", + expression: "0 9 * * *", + action: "runReport", + args: ["sales"], // Optional. + timezone: "America/Los_Angeles", // Optional; defaults to UTC. + maxHistory: 100, // Optional; defaults to 100. Set to 0 to disable. +}); +``` + +### Run at a fixed interval + +Fixed-interval jobs keep their cadence anchored to scheduled deadlines, so action runtime does not introduce drift. Names are unique per actor, and reusing a name updates the existing job. The minimum interval is 5 seconds. + +```ts +await c.cron.every({ + name: "presence-sweep", + interval: 15_000, // Minimum 5 seconds. + action: "sweepPresence", + args: [], // Optional. + maxHistory: 25, // Optional; defaults to 100. Set to 0 to disable. +}); +``` + +### Update and delete jobs + +Submitting a recurring job with an existing name updates it. Changing its cadence calculates a new next run, while changing only its action, arguments, or history settings preserves the existing cadence. + +```ts +const job = await c.cron.get("daily-report"); +const jobs = await c.cron.list(); +const deleted = await c.cron.delete("daily-report"); +``` + +### View run history + +```ts +const recent = await c.cron.history("daily-report", { + limit: 20, // Optional. +}); +``` + +Each entry's `result` is `running`, `ok`, `error`, or `skipped`. Jobs retain 100 entries by default. Set maximum history to zero to disable and clear history, or choose up to 1,000 entries. An actor retains at most 10,000 history entries across all jobs. + +## Run information + +When a schedule invokes an action, it appends a ScheduledFireInfo object after the configured action arguments. + +```ts +import type { ScheduledFireInfo } from "rivetkit"; + +const reports = actor({ + onCreate: async (c) => { + await c.cron.set({ + name: "daily-report", + expression: "0 9 * * *", + action: "runReport", + args: ["sales"], // Optional. + }); + }, + actions: { + runReport: ( + _c, + report: string, + fire: ScheduledFireInfo, + ) => { + console.log( + report, + fire.kind, + fire.id, + fire.name, + fire.scheduledAt, + fire.firedAt, + ); + }, + }, +}); +``` + +## Execution behavior + +- **Durability**: Schedules survive actor sleep, restarts, upgrades, and crashes. See [Sleep Between Runs](#sleep-between-runs). +- **Failures**: Failed runs are not retried immediately. A failed recurring run continues at the next normal cadence; a failed one-shot is complete after its attempted invocation. Cron failures are available in [run history](#view-run-history), so you can build a custom retry mechanism. +- **Idempotency**: Scheduled actions should be idempotent so manually retrying a failed or interrupted run is safe. +- **Overlaps**: Overlapping recurring runs are skipped. +- **Missing actions**: Recurring jobs are deleted when their action no longer exists. + +_Source doc path: /docs/actors/schedule_ diff --git a/.agents/skills/multiplayer-game/reference/actors/sharing-and-joining-state.md b/.agents/skills/multiplayer-game/reference/actors/sharing-and-joining-state.md new file mode 100644 index 0000000..1fe0683 --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/actors/sharing-and-joining-state.md @@ -0,0 +1,10 @@ +# Sharing and Joining State + +> Source: `src/content/docs/actors/sharing-and-joining-state.mdx` +> Canonical URL: https://rivet.dev/docs/actors/sharing-and-joining-state +> Description: This page has moved to [design patterns](/docs/actors/design-patterns). + +--- + + +_Source doc path: /docs/actors/sharing-and-joining-state_ diff --git a/.agents/skills/multiplayer-game/reference/actors/sqlite-drizzle.md b/.agents/skills/multiplayer-game/reference/actors/sqlite-drizzle.md new file mode 100644 index 0000000..edc157a --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/actors/sqlite-drizzle.md @@ -0,0 +1,248 @@ +# SQLite + Drizzle + +> Source: `src/content/docs/actors/sqlite-drizzle.mdx` +> Canonical URL: https://rivet.dev/docs/actors/sqlite-drizzle +> Description: Use Drizzle ORM with embedded SQLite in Rivet Actors. + +--- +Use Drizzle when you want typed schema, typed queries, and generated migrations on top of actor-local SQLite. + +For a high-level overview of where to store actor data, see [State & Storage](/docs/actors/state). + +## What is Drizzle good for? + +- **Typed schema**: define tables in TypeScript and get typed query results. +- **Typed query builder**: write SQL-like queries with autocompletion. +- **Migration workflow**: generate SQL migration files from schema changes. +- **Raw SQL escape hatch**: use `c.db.execute(...)` for direct SQLite when needed. + +## Project structure + +Use one folder per actor database: + +```txt +src/ + actors/ + todo-list/ + index.ts + schema.ts + drizzle.config.ts + drizzle/ + 0000_init.sql + migrations.js + migrations.d.ts + meta/ + _journal.json +``` + +- `index.ts` is the actor implementation. +- `drizzle/` holds the SQL migrations (`*.sql`) and `meta/_journal.json` generated by `drizzle-kit`. +- `migrations.js` is a small RivetKit glue file you maintain by hand. It imports the journal and each `*.sql` file and exports a `{ journal, migrations }` object keyed by migration (for example `m0000`). Add a new entry here whenever `db:generate` produces a new migration. +- Commit the generated migration files and `migrations.js` to source control. + +## Basic setup + +```json package.json +{ + "scripts": { + "db:generate": "find src/actors -name drizzle.config.ts -exec drizzle-kit generate --config {} \\;" + }, + "dependencies": { + "rivetkit": "*", + "drizzle-orm": "^0.44.2" + }, + "devDependencies": { + "drizzle-kit": "^0.31.2" + } +} +``` + +```ts vite.config.ts @nocheck +import { defineConfig, type Plugin } from "vite"; +import { readFileSync } from "node:fs"; + +function sqlRawPlugin(): Plugin { + return { + name: "sql-raw", + transform(_code, id) { + if (id.endsWith(".sql")) { + const content = readFileSync(id, "utf-8"); + return { code: `export default ${JSON.stringify(content)};` }; + } + }, + }; +} + +export default defineConfig({ + plugins: [sqlRawPlugin()], +}); +``` + +```ts drizzle.config.ts @nocheck +import { defineConfig } from "rivetkit/db/drizzle"; + +export default defineConfig({ + schema: "./src/actors/todo-list/schema.ts", + out: "./src/actors/todo-list/drizzle", +}); +``` + +```sql 0000_init.sql +CREATE TABLE `todos` ( + `id` integer PRIMARY KEY AUTOINCREMENT NOT NULL, + `title` text NOT NULL, + `created_at` integer NOT NULL +); +``` + +```json _journal.json +{ + "version": "7", + "dialect": "sqlite", + "entries": [ + { + "idx": 0, + "version": "7", + "when": 1735689600000, + "tag": "0000_init", + "breakpoints": true + } + ] +} +``` + +```ts migrations.js @nocheck +import journal from "./meta/_journal.json"; +import m0000 from "./0000_init.sql"; + +export default { + journal, + migrations: { + m0000, + }, +}; +``` + +```ts index.ts @nocheck +import { actor } from "rivetkit"; +import { db } from "rivetkit/db/drizzle"; +import migrations from "./drizzle/migrations.js"; +import { schema, todos } from "./schema.ts"; + +export const todoList = actor({ + db: db({ schema, migrations }), + actions: { + addTodo: async (c, title: string) => { + const rows = await c.db + .insert(todos) + .values({ title, createdAt: Date.now() }) + .returning(); + return rows[0]; + }, + getTodos: async (c) => { + return await c.db.select().from(todos).orderBy(todos.id); + }, + getTodoCount: async (c) => { + const rows = (await c.db.execute( + "SELECT COUNT(*) AS count FROM todos", + )) as { count: number }[]; + return rows[0]?.count ?? 0; + }, + }, +}); +``` + +```ts index.ts @nocheck +import { setup } from "rivetkit"; +import { todoList } from "./todo-list/index.ts"; + +export const registry = setup({ use: { todoList } }); +registry.start(); +``` + +```ts client.ts @nocheck +import { createClient } from "rivetkit/client"; +import type { registry } from "./index"; + +const client = createClient("http://localhost:6420"); +const todoList = client.todoList.getOrCreate(["main"]); + +await todoList.addTodo("Write Drizzle docs"); + +const todos = await todoList.getTodos(); +const count = await todoList.getTodoCount(); + +console.log(todos, count); +``` + +## Queries + +### Query builder + +Use Drizzle's typed query APIs for most reads and writes. + +```ts @nocheck +import { eq } from "drizzle-orm"; + +await c.db.insert(todos).values({ title, createdAt: Date.now() }); + +const rows = await c.db + .select() + .from(todos) + .where(eq(todos.title, title)); +``` + +### Raw SQL + +`rivetkit/db/drizzle` also exposes raw SQLite access through `c.db.execute(...)`. + +```ts @nocheck +await c.db.execute( + "CREATE INDEX IF NOT EXISTS idx_todos_created_at ON todos(created_at)", +); +``` + +## Queues + +Use queues for ordered mutations and keep actions read-only. Import `queue` alongside `actor` from `rivetkit`. + +```ts @nocheck +import { actor, queue } from "rivetkit"; + +// ... + +queues: { + addTodo: queue<{ title: string }>(), +}, +run: async (c) => { + for await (const message of c.queue.iter()) { + if (message.name === "addTodo") { + await c.db.insert(todos).values({ + title: message.body.title, + createdAt: Date.now(), + }); + } + } +}, +actions: { + getTodos: async (c) => await c.db.select().from(todos), +}, +``` + +## Recommendations + +- Prefer Drizzle query APIs for app code and use raw SQL for advanced SQLite features. +- Keep one `drizzle.config.ts` per actor folder. +- Re-run `db:generate` after schema changes and commit generated migration files. +- Use queues for writes and actions for reads. +- Keep related writes in one action or queue message to reduce interleaved query risk. + +## Read more + +- [Drizzle SQLite quickstart](https://orm.drizzle.team/docs/get-started-sqlite) +- [Drizzle `drizzle-kit generate`](https://orm.drizzle.team/docs/drizzle-kit-generate) +- [Drizzle + Cloudflare D1](https://orm.drizzle.team/docs/deploy-cloudflare-d1) +- [Drizzle + Cloudflare Durable Objects](https://orm.drizzle.team/docs/deploy-cloudflare-do) +- [Cloudflare Durable Objects SQLite storage](https://developers.cloudflare.com/durable-objects/api/sqlite-storage-api/) + +_Source doc path: /docs/actors/sqlite-drizzle_ diff --git a/.agents/skills/multiplayer-game/reference/actors/sqlite.md b/.agents/skills/multiplayer-game/reference/actors/sqlite.md new file mode 100644 index 0000000..cc42bf3 --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/actors/sqlite.md @@ -0,0 +1,127 @@ +# SQLite + +> Source: `src/content/docs/actors/sqlite.mdx` +> Canonical URL: https://rivet.dev/docs/actors/sqlite +> Description: Use embedded SQLite in Rivet Actors with raw SQL queries. + +--- +For a high-level overview of where to store actor data, including when to use `c.state` versus SQLite, see [State & Storage](/docs/actors/state). + +## What is SQLite? + +- **Database per actor**: each actor instance has its own SQLite database, scoped to that actor. +- **High performance**: Rivet Actors keep compute and storage together, so queries avoid network round trips to an external database. +- **Larger-than-memory storage**: SQLite stores data on disk, so you can work with datasets that do not fit in actor memory. +- **Embedded relational database**: use tables, indexes, and SQL queries directly inside actor logic. + +### SQLite features + +- **Indexes**: speed up lookups on frequently queried fields. +- **Search and filtering**: use `WHERE`, `LIKE`, and `ORDER BY` instead of manual in-memory loops. +- **Relationships**: use multiple tables and `JOIN` queries for connected data. +- **Constraints**: use primary keys, unique constraints, and foreign keys for data integrity. +- **Transactions**: apply multiple writes atomically when changes must stay consistent. + +## Raw SQL vs ORM (Drizzle) + +Rivet supports both raw SQL and [Drizzle](https://orm.drizzle.team/) for actor-local SQLite. + +Use **raw SQL** when you want direct query control and minimal abstraction. + +```ts @nocheck +await c.db.execute("INSERT INTO todos (title) VALUES (?)", title); +const rows = await c.db.execute("SELECT id, title FROM todos ORDER BY id DESC"); +``` + +Use **Drizzle** when you want typed schema and typed query APIs. + +```ts @nocheck +await c.vars.drizzle.insert(todos).values({ title }); +const rows = await c.vars.drizzle.select().from(todos).orderBy(desc(todos.id)); +``` + +You can mix both in the same actor. + +For Drizzle setup, see [SQLite + Drizzle](/docs/actors/sqlite-drizzle). + +## Basic setup + +Define `db: db({ onMigrate })` on your actor, create your schema in `onMigrate`, and execute SQL with `c.db.execute(...)`. + +RivetKit wraps `onMigrate` in a SQLite savepoint, so migration steps are atomic. If `onMigrate` throws, all SQL run by that hook is rolled back before the actor starts. + +## Queries + +`c.db.execute(...)` returns an array of row objects for `SELECT` queries. + +```ts @nocheck +const rows = await c.db.execute( + "SELECT id, title FROM todos WHERE title LIKE ?", + `%${query}%`, +); +``` + +### Parameterized queries + +Use `?` placeholders for dynamic values and pass parameters in order after the SQL string. + +```ts @nocheck +await c.db.execute("INSERT INTO todos (title) VALUES (?)", title); +``` + +You can also use named SQLite bindings by passing a single properties object. + +```ts @nocheck +const rows = await c.db.execute( + "SELECT id, title FROM todos WHERE title = :title", + { title: "Write SQLite docs" }, +); +``` + +### Transactions + +Use transactions when multiple writes must succeed or fail together. + +```ts @nocheck +await c.db.transaction(async (tx) => { + await tx.execute("INSERT INTO todos (title) VALUES (?)", title); + await tx.execute( + "INSERT INTO comments (todo_id, body) VALUES (last_insert_rowid(), ?)", + body, + ); +}); +``` + +RivetKit commits when the callback resolves and rolls back when it throws. Other transactions and ordinary actor SQL queue in FIFO order until the callback finishes. Transactions have a 60-second safety timeout by default; increase it for legitimately long work with `{ timeout: 120_000 }`. + +Always use the callback's `tx` value inside the transaction. Starting another transaction or using the outer `c.db` from the callback waits behind the active transaction and eventually reaches the safety timeout; the resulting error points to this possible deadlock. + +Manual `BEGIN`/`COMMIT` calls remain supported for compatibility, but cannot protect against interleaving callers. RivetKit logs a warning recommending `db.transaction()`. Set `warnOnManualTransactions: false` in `db(...)` to disable the warning; the warning itself mentions this flag. + +## Queues + +It's recommended to use queues for mutations and actions for read-only queries. This is the same code structure as the basic setup, but mutation writes are routed through queues. + +## Debugging + +- `GET /inspector/summary` includes `isDatabaseEnabled` so you can confirm SQLite is configured. +- `GET /inspector/database/schema` returns the tables and views discovered in the actor's SQLite database. +- `GET /inspector/database/rows?table=...&limit=100&offset=0` returns paged rows for a specific table or view. +- `POST /inspector/database/execute` lets you run ad-hoc SQL for debugging and data fixes with positional `args` or named `properties`. +- Keep a small read-only action for quick query verification while debugging. +- In non-dev mode, inspector endpoints require authorization. + +## Recommendations + +- Keep schema creation and migration steps in `onMigrate`; RivetKit runs them atomically inside a SQLite savepoint. +- Use `?` placeholders for dynamic values. +- Prefer queue-driven writes for ordered or background work. +- Use transactions for related multi-step mutations when atomicity matters. + +## Read more + +- [SQLite + Drizzle in Rivet Actors](/docs/actors/sqlite-drizzle) +- [SQLite docs](https://sqlite.org/docs.html) +- [SQLite SQL language reference](https://sqlite.org/lang.html) + +_Source doc path: /docs/actors/sqlite_ diff --git a/.agents/skills/multiplayer-game/reference/actors/state.md b/.agents/skills/multiplayer-game/reference/actors/state.md new file mode 100644 index 0000000..559b49c --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/actors/state.md @@ -0,0 +1,270 @@ +# In-Memory State + +> Source: `src/content/docs/actors/state.mdx` +> Canonical URL: https://rivet.dev/docs/actors/state +> Description: Actors store state in memory for instant reads and writes. State can be persisted automatically or kept ephemeral. + +--- +## Types of State + +There are three ways to store data in an actor, depending on what it looks like and whether it needs to survive restarts. + +### Durable + +Simple, serializable data on `c.state` that is automatically persisted and restored across restarts. The default starting point. + +### Ephemeral + +Live objects on `c.vars` like database connections, API clients, and event emitters, or data loaded from an external source. Never persisted. + +```typescript @nocheck External database +import { actor } from "rivetkit"; +import { Pool } from "pg"; + +// One shared pool for the whole process, created once and reused by every actor +const pool = new Pool({ connectionString: process.env.DATABASE_URL }); + +const userActor = actor({ + state: { profile: null as Record | null }, + + // Load this actor's row from the shared pool on each start + createVars: async (c) => { + const { rows } = await pool.query("SELECT * FROM users WHERE id = $1", [c.key[0]]); + return { profile: rows[0] }; + }, + + actions: { + updateEmail: async (c, email: string) => { + await pool.query("UPDATE users SET email = $1 WHERE id = $2", [email, c.key[0]]); + } + } +}); +``` + +### SQLite + +Rivet also provides an embedded SQLite database (`c.db`) for when your data needs to be queried, requires safe schema migrations, or grows too large to hold in memory. See [SQLite](/docs/actors/sqlite). + +```typescript @nocheck Basic +import { actor } from "rivetkit"; +import { db } from "rivetkit/db"; + +const todoList = actor({ + db: db({ + onMigrate: async (db) => { + await db.execute(` + CREATE TABLE IF NOT EXISTS todos ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + title TEXT NOT NULL + ); + `); + }, + }), + + actions: { + add: async (c, title: string) => { + await c.db.execute("INSERT INTO todos (title) VALUES (?)", title); + }, + + list: async (c) => { + return (await c.db.execute( + "SELECT id, title FROM todos ORDER BY id DESC", + )) as { id: number; title: string }[]; + }, + }, +}); +``` + +```typescript @nocheck Load into memory +import { actor } from "rivetkit"; +import { db } from "rivetkit/db"; + +const counter = actor({ + db: db({ + onMigrate: async (db) => { + await db.execute(` + CREATE TABLE IF NOT EXISTS counter ( + id INTEGER PRIMARY KEY CHECK (id = 1), + count INTEGER NOT NULL + ); + `); + await db.execute("INSERT OR IGNORE INTO counter (id, count) VALUES (1, 0)"); + }, + }), + + // Load the count from SQLite into memory on every start + createVars: async (c) => { + const rows = (await c.db.execute( + "SELECT count FROM counter WHERE id = 1", + )) as { count: number }[]; + return { count: rows[0].count }; + }, + + actions: { + get: (c) => c.vars.count, + + increment: async (c) => { + // Update the in-memory value and write it back to SQLite + c.vars.count += 1; + await c.db.execute("UPDATE counter SET count = ? WHERE id = 1", c.vars.count); + return c.vars.count; + }, + }, +}); +``` + +## State Isolation + +Each actor's state is fully isolated. Other actors and clients can't touch it directly; all reads and writes go through the actor's own [Actions](/docs/actors/actions). To share state across actors, see [sharing and joining state](/docs/actors/sharing-and-joining-state). + +## Durable State + +`c.state` lives in memory and is persisted automatically, so reads and writes have no added latency while the data still survives sleeps, restarts, upgrades, and crashes. Use it for small, simple values like counters, flags, and small maps. + +`createState` runs once when the actor is first created. On later starts, state is loaded from storage instead of recreated. See [Lifecycle](/docs/actors/lifecycle). + +### When state saves + +Mutating `c.state` schedules a save automatically. Rapid mutations are batched into a single write on a throttle (`stateSaveInterval`, default 1 second). Reads never trigger a save, saves aren't tied to action or handler boundaries, and state is also flushed when the actor sleeps or shuts down. + +To force a save mid-action, call `c.saveState()`: + +- `c.saveState({ immediate: true })` writes immediately and resolves once the write completes. +- `c.saveState()` schedules a throttled save and returns right away, without waiting for the write. + +Force an immediate save before a risky side effect so a crash can't lose progress: + +### Supported types + +State must be serializable. + +- `null`, `undefined`, `boolean`, `string`, `number`, `BigInt` +- `Date`, `RegExp`, `Error` +- `ArrayBuffer` and typed arrays (`Uint8Array`, `Int8Array`, `Float32Array`, etc.) +- `Map`, `Set`, `Array` +- Plain objects + +When data grows large or needs querying, store it in [Embedded SQLite](#embedded-sqlite) instead. + +## Ephemeral State + +`c.vars` holds data that exists only while the actor runs and is never saved. Use it for live objects that can't be serialized (connections, clients, emitters) or for data loaded from an external source. Most actors use both: `state` for durable data, `vars` for live objects. + +`createVars` runs on every actor start, unlike `createState` which runs once. That makes it the place to open connections and load data each time the actor wakes. + +### Runtime objects + +Build non-serializable objects in `createVars` and use them from actions: + +### Loading from external sources + +Create the connection pool once at module scope and share it across all actors, then use `createVars` (which can be `async`) to load this actor's data from it on each start: + +```typescript @nocheck +import { actor } from "rivetkit"; +import { Pool } from "pg"; + +// One shared pool for the whole process, not one per actor +const pool = new Pool({ connectionString: process.env.DATABASE_URL }); + +const profile = actor({ + state: { cachedName: "" }, + + createVars: async (c) => { + const { rows } = await pool.query("SELECT * FROM users WHERE id = $1", [c.key[0]]); + return { user: rows[0] }; + }, + + actions: { + updateEmail: async (c, email: string) => { + await pool.query("UPDATE users SET email = $1 WHERE id = $2", [email, c.key[0]]); + } + } +}); +``` + +When the actor owns its data, prefer [durable state](#durable-state) or [SQLite](#embedded-sqlite), which need no external infrastructure. + +### Cleanup + +`vars` is dropped when the actor stops, but per-actor resources like timers, subscriptions, and dedicated connections aren't cleaned up for you. Release them in `onSleep` and `onDestroy`. A shared pool stays open for the whole process, so don't close it per actor. + +```typescript @nocheck +const poller = actor({ + state: { ticks: 0 }, + + // Per-actor timer started on each wake + createVars: (c) => ({ timer: setInterval(() => c.state.ticks++, 5000) }), + + // Clear it before the actor sleeps or is destroyed + onSleep: (c) => clearInterval(c.vars.timer), + onDestroy: (c) => clearInterval(c.vars.timer), + + actions: { /* ... */ } +}); +``` + +## Embedded SQLite + +`c.db` is a SQLite database scoped to each actor and stored on disk. Use it for queryable, relational, or larger-than-memory data. Because compute and storage live together, queries run locally with no network round trips. + +A common pattern is to treat SQLite as the source of truth and keep a working copy in `c.vars`: load rows in `createVars`, serve reads from memory, and write changes back to `c.db`. + +```typescript @nocheck +import { actor } from "rivetkit"; +import { db } from "rivetkit/db"; + +const leaderboard = actor({ + db: db({ + onMigrate: async (db) => { + await db.execute(` + CREATE TABLE IF NOT EXISTS scores ( + player TEXT PRIMARY KEY, + score INTEGER NOT NULL + ); + `); + }, + }), + + // Load the table into memory once per start + createVars: async (c) => { + const rows = (await c.db.execute("SELECT player, score FROM scores")) as { + player: string; + score: number; + }[]; + return { scores: new Map(rows.map((r) => [r.player, r.score])) }; + }, + + actions: { + top: (c) => [...c.vars.scores].sort((a, b) => b[1] - a[1]).slice(0, 10), + + record: async (c, player: string, score: number) => { + c.vars.scores.set(player, score); + // Write through to SQLite + await c.db.execute( + "INSERT INTO scores (player, score) VALUES (?, ?) ON CONFLICT(player) DO UPDATE SET score = ?", + player, score, score, + ); + }, + }, +}); +``` + +For the full query API, schema migrations, transactions, and the Drizzle ORM, see: + +- [SQLite](/docs/actors/sqlite): raw SQL against the embedded per-actor database. +- [SQLite + Drizzle](/docs/actors/sqlite-drizzle): typed schema and query APIs. + +## Debugging + +- `GET /inspector/state` returns the actor's current state and `isStateEnabled`. +- `PATCH /inspector/state` lets you set state directly while debugging. +- In non-dev mode, inspector endpoints require authorization. + +## API Reference + +- [`CreateContext`](/typedoc/types/rivetkit.mod.CreateContext.html) - Context available during actor state creation +- [`ActorContext`](/typedoc/interfaces/rivetkit.mod.ActorContext.html) - Context available throughout actor lifecycle +- [`ActorDefinition`](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html) - Interface for defining actors with state + +_Source doc path: /docs/actors/state_ diff --git a/.agents/skills/multiplayer-game/reference/actors/statuses.md b/.agents/skills/multiplayer-game/reference/actors/statuses.md new file mode 100644 index 0000000..cccf52a --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/actors/statuses.md @@ -0,0 +1,36 @@ +# Actor Statuses + +> Source: `src/content/docs/actors/statuses.mdx` +> Canonical URL: https://rivet.dev/docs/actors/statuses +> Description: Understand the lifecycle statuses of Rivet Actors, what they mean, how they appear in the API, and how to troubleshoot common issues. + +--- +## Statuses + +These are the statuses you can see in the dashboard for each actor. + +| Status | Description | +|---|---| +| **Starting** | The actor has been created but has not yet become connectable. | +| **Running** | The actor is live and accepting connections. | +| **Destroyed** | The actor has been gracefully destroyed. | +| **Crashed** | The actor failed to start or encountered a fatal error. See [Troubleshooting](/docs/actors/troubleshooting#actor-status-is-crashed) for common failure reasons. | +| **Sleeping** | The actor has been put to sleep from inactivity. It will be woken up automatically when a new request arrives. | +| **Pending** | The actor is waiting to be allocated to a runner. This happens when no runner is available to handle the actor. See [Troubleshooting](/docs/actors/troubleshooting#actor-status-is-pending) for common causes. | +| **Crash Loop Backoff** | The actor failed to allocate and is waiting to retry with a backoff. This typically means repeated allocation failures. The backoff prevents overloading your infrastructure in the case of a widespread misconfiguration in your backend. See [Troubleshooting](/docs/actors/troubleshooting#actor-status-is-crashed) for common failure reasons. | + +## API Representation + +The actor object returned by the full engine API (used by the dashboard) includes the following timestamp fields used to derive status: + +| Field | Description | +|---|---| +| `createTs` | When the actor was first created. Always present. | +| `connectableTs` | When the actor became connectable. Null if not yet running. | +| `destroyTs` | When the actor was destroyed. | +| `sleepTs` | When the actor entered a sleeping state. | +| `pendingAllocationTs` | When the actor started waiting for an allocation. | +| `rescheduleTs` | When the actor will retry allocation after a failure. | +| `error` | Error details if the actor failed. | + +_Source doc path: /docs/actors/statuses_ diff --git a/.agents/skills/multiplayer-game/reference/actors/testing.md b/.agents/skills/multiplayer-game/reference/actors/testing.md new file mode 100644 index 0000000..1a30fff --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/actors/testing.md @@ -0,0 +1,51 @@ +# Testing + +> Source: `src/content/docs/actors/testing.mdx` +> Canonical URL: https://rivet.dev/docs/actors/testing +> Description: Rivet provides a straightforward testing framework to build reliable and maintainable applications. This guide covers how to write effective tests for your actor-based services. + +--- +## Setup + +To set up testing with Rivet: + +```bash +# Install Vitest +npm install -D vitest + +# Run tests +npm test +``` + +## Basic Testing Setup + +Rivet includes a test helper called `setupTest` that starts your registry in test mode and returns a client connected to it. This allows for fast, isolated tests without external dependencies. + +## Testing Actor State + +State persists within each test, allowing you to verify that your actor correctly maintains state between operations. + +## Testing Events + +For actors that emit events, you can verify events are correctly triggered by subscribing to them: + +## Testing Schedules + +Rivet's schedule functionality can be tested by scheduling work and waiting for it to run: + +Use a short schedule and `expect.poll` the action's observable result. Vitest's fake date and fake JavaScript timers do not advance RivetKit's scheduler, which runs outside the test's JavaScript timer queue. + +## Best Practices + +1. **Isolate tests**: Each test should run independently, avoiding shared state. +2. **Test edge cases**: Verify how your actor handles invalid inputs, concurrent operations, and error conditions. +3. **Test scheduled operations**: Poll observable state or output with a bounded timeout instead of sleeping for an exact duration. +4. **Use realistic data**: Test with data that resembles production scenarios. + +`setupTest` starts the registry and disposes the returned client when the test finishes, so you can focus on writing effective tests for your business logic. + +## API Reference + +- [`setupTest`](/typedoc/functions/rivetkit.test_mod.setupTest.html) - Test setup helper function + +_Source doc path: /docs/actors/testing_ diff --git a/.agents/skills/multiplayer-game/reference/actors/troubleshooting.md b/.agents/skills/multiplayer-game/reference/actors/troubleshooting.md new file mode 100644 index 0000000..65b234d --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/actors/troubleshooting.md @@ -0,0 +1,154 @@ +# Troubleshooting + +> Source: `src/content/docs/actors/troubleshooting.mdx` +> Canonical URL: https://rivet.dev/docs/actors/troubleshooting +> Description: Common issues with Rivet Actors and how to resolve them. + +--- +## Common Steps + +Before diving into specific errors, try these general troubleshooting steps: + +- Check your server logs for `level=ERROR` or `level=WARN` messages. +- Check if any of your backend processes have crashed or restarted unexpectedly. +- If you need more diagnostics, set `RIVET_LOG_LEVEL=DEBUG` for verbose logging. See [Logging](/docs/general/logging) for more options. + +## Reporting Issues + +If you're stuck, reach out on [Discord](https://rivet.dev/discord) or file an issue on [GitHub](https://github.com/rivet-dev/rivet/issues). + +When reporting, please include: + +- **Symptoms** + - Whether this is happening in local dev, deployed, or both + - The error you're seeing (screenshot or error message) + - Relevant source code related to the issue +- **What you've tried to solve it** +- **Environment** + - RivetKit version + - Runtime (Node, Bun, etc.) including version + - If applicable, provider in use (e.g. Vercel, Railway, Cloudflare) + - If applicable, HTTP router in use (e.g. Hono, Express, Elysia) + +## Actor status is crashed + +See [Actor Statuses](/docs/actors/statuses) for more about this status. + +The dashboard will show the specific failure reason. Common errors include: + +### `crashed` + +The actor's `run` handler threw an unhandled exception or exited unexpectedly. Check your actor logs for the error message and stack trace. + +### `no_capacity` + +No server was available to run your actor. The cause depends on your [runtime mode](/docs/general/runtime-modes): + +**Serverless**: + +- Your provider configuration does not have the region enabled that the actor is trying to run in. This is uncommon and usually only happens if an actor was created and then the provider config was updated to remove the region. +- There is an issue connecting to your backend. If the engine is hitting `/api/rivet/start` and failing, check your backend logs for errors. + +**Runners**: + +- You don't have enough runners online. Check your runner list in the dashboard to verify runners are visible and connected. +- Your runners are full. Each runner has a limited number of actor slots (configured via `RIVET_TOTAL_SLOTS`, default: 100,000). Check the dashboard to see if your runners have available capacity and scale up if needed. + +### `runner_no_response` + +The server running your actor did not respond in time. This can happen if your server is overloaded or experienced a network issue. Try restarting your server or checking its health. + +### `runner_connection_lost` + +The server running your actor lost its connection to Rivet. This is usually caused by a network interruption or your server restarting. + +### `runner_draining_timeout` + +Your server is shutting down and the actor did not finish in time. Consider handling graceful shutdown in your actor or increasing your shutdown timeout. + +### `concurrent_actor_limit_reached` + +The actor could not be allocated because the concurrent actor limit was reached. Reduce the number of concurrently running actors or increase your limit. + +### `no_envoys` + +No server was available to run your actor. This is equivalent to `no_capacity` on the current allocation path. See the `no_capacity` section above for the causes and fixes for your [runtime mode](/docs/general/runtime-modes). + +### `envoy_no_response` + +The server running your actor did not respond in time. This can happen if your server is overloaded or experienced a network issue. Try restarting your server or checking its health. + +### `envoy_connection_lost` + +The server running your actor lost its connection to Rivet. This is usually caused by a network interruption or your server restarting. + +### `serverless_http_error` + +Your serverless endpoint returned an HTTP error. Common causes: + +- Your backend is returning an error before the actor can start. Check your server logs. +- Your endpoint is behind authentication or a firewall that is blocking Rivet's requests. +- Your serverless function crashed during startup. Check your platform's function logs (e.g. Vercel, Cloudflare). + +### `serverless_connection_error` + +Rivet was unable to connect to your serverless endpoint. Check that: + +- Your backend is deployed and the endpoint URL is correct. +- Your server is publicly reachable from the internet. +- There are no DNS or firewall issues blocking the connection. + +### `serverless_stream_ended_early` + +The connection to your serverless endpoint was terminated before the actor finished. This usually means your serverless function hit its execution time limit. Ensure that your Rivet provider's request lifespan is configured to match the max duration of your serverless platform. + +### `serverless_invalid_sse_payload` + +Rivet received an unexpected response from your serverless endpoint. This typically means something is intercepting or modifying the request before it reaches your RivetKit handler. Check that: + +- Your server routes requests to `registry.start()`, `registry.serve()`, or `registry.handler()` correctly. +- No middleware is modifying the request or response body. + +### `internal_error` + +An unexpected error occurred within Rivet. If this persists, please [contact support](https://rivet.dev/docs). + +## Actors crashing immediately on startup + +If your actors are being created and then immediately destroyed or crashing, there is likely an error being thrown in your `createState` or `onCreate` lifecycle hooks. These hooks run during actor initialization before the actor is marked as ready. + +Check your server logs for the error message and stack trace. Common causes include: + +- An exception thrown in `createState` (e.g. invalid input, failed validation, or a runtime error when computing initial state) +- An exception thrown in `onCreate` (e.g. a failing external API call, missing configuration, or invalid setup logic) + +Fix the error in the relevant lifecycle hook and redeploy. + +## Actors not upgrading to new code + +If your actors are still running old code after deploying a new version, your [versioning](/docs/actors/versions) is likely not configured correctly. + +Without versioning, Rivet has no way to distinguish old deployments from new ones. The behavior depends on your [runtime mode](/docs/general/runtime-modes): + +- **Serverless**: Old requests may still be open from the previous deployment, so actors continue running on the old version's connection until those requests close. +- **Runners**: The old runner container is still running and will continue accepting new actors. New actors may be scheduled on the old runner instead of the new one. + +To fix this, configure a version number in your [registry configuration](/docs/general/registry-configuration). When a new version is deployed, Rivet will allocate new actors to the latest version and optionally drain old actors to migrate them. + +## Actor status is pending + +See [Actor Statuses](/docs/actors/statuses) for more about this status. + +An actor stays in "pending" status when Rivet is waiting for a server to accept it. The cause depends on your [runtime mode](/docs/general/runtime-modes): + +**Serverless**: + +- A region may have been removed from your provider configuration while an existing actor still lives in that region. The actor has no available server to start on because no provider serves that region anymore. Re-add the region to your provider config or destroy the affected actors. +- Check your backend logs for errors on the `/api/rivet/start` endpoint. + +**Runners**: + +- You don't have enough runners online. Check the dashboard to verify your runners are connected. +- Your runners may be at capacity. Check the dashboard to see if runners have available actor slots and scale up if needed. + +_Source doc path: /docs/actors/troubleshooting_ diff --git a/.agents/skills/multiplayer-game/reference/actors/types.md b/.agents/skills/multiplayer-game/reference/actors/types.md new file mode 100644 index 0000000..3e8f9b0 --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/actors/types.md @@ -0,0 +1,46 @@ +# Types + +> Source: `src/content/docs/actors/types.mdx` +> Canonical URL: https://rivet.dev/docs/actors/types +> Description: TypeScript types for working with Rivet Actors. This page covers context types used in lifecycle hooks and actions, as well as helper types for extracting types from actor definitions. + +--- +## Context Types + +Context types define what properties and methods are available in different parts of the actor lifecycle. + +### Extracting Context Types + +When writing helper functions that work with actor contexts, use context extractor types like `CreateContextOf` or `ActionContextOf` to extract the appropriate context type from your actor definition. + +### All Context Types + +Each lifecycle hook and handler has a corresponding `*ContextOf` type, exported from `"rivetkit"`. Pass `typeof myActor` as the type parameter. + +| Hook / Handler | Context Type | +|---|---| +| `createState` | `CreateContextOf` | +| `onCreate` | `CreateContextOf` | +| `createVars` | `CreateVarsContextOf` | +| `createConnState` | `CreateConnStateContextOf` | +| `onBeforeConnect` | `BeforeConnectContextOf` | +| `onConnect` | `ConnectContextOf` | +| `onDisconnect` | `DisconnectContextOf` | +| `onDestroy` | `DestroyContextOf` | +| `onMigrate` | `MigrateContextOf` | +| `onWake` | `WakeContextOf` | +| `onSleep` | `SleepContextOf` | +| `onStateChange` | `StateChangeContextOf` | +| `onBeforeActionResponse` | `BeforeActionResponseContextOf` | +| `actions.*` | `ActionContextOf` | +| `run` | `RunContextOf` | +| `workflow` orchestration helpers (root, `loop`, `try`, `race`, `join` branches) | `WorkflowContextOf` | +| `workflow` `step` / `tryStep` run + rollback helpers | `WorkflowStepContextOf` | +| `onRequest` | `RequestContextOf` | +| `onWebSocket` | `WebSocketContextOf` | + +`ActorContextOf`, `ConnContextOf`, and `ConnInitContextOf` are general-purpose base context types useful for helper functions that don't correspond to a specific hook. + +Workflow context extractors are exported from both `"rivetkit"` and `"rivetkit/workflow"`. + +_Source doc path: /docs/actors/types_ diff --git a/.agents/skills/multiplayer-game/reference/actors/versions.md b/.agents/skills/multiplayer-game/reference/actors/versions.md new file mode 100644 index 0000000..1b45129 --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/actors/versions.md @@ -0,0 +1,241 @@ +# Versions & Upgrades + +> Source: `src/content/docs/actors/versions.mdx` +> Canonical URL: https://rivet.dev/docs/actors/versions +> Description: When you deploy new code, Rivet ensures actors are upgraded seamlessly without downtime. + +--- +## How Versions Work + +Each runner has a **version number**. When you deploy new code with a new version, Rivet handles the transition automatically: + +- **New actors go to the newest version**: When allocating actors, Rivet always prefers runners with the highest version number +- **Multiple versions can coexist**: Old actors continue running on old versions while new actors are created on the new version +- **Drain old actors**: When enabled, a runner connecting with a newer version number will gracefully stop old actors to be rescheduled to the new version + +Versions are not configured by default. See [Registry Configuration](/docs/general/registry-configuration) to learn how to configure the runner version. + +`RIVET_ENVOY_VERSION` is only needed when self-hosting or using a custom runner. Rivet Compute handles versioning automatically. + +### Example Scenario + +### Drain Enabled + +When a new version is deployed, existing actors are gracefully stopped on the old runner and rescheduled onto the new version. + +```mermaid +sequenceDiagram + participant R1 as Runner v1 + participant R2 as Runner v2 + + Note over R1: Currently running + Note over R2: Deployed + R2->>R1: Drain old actors + R1->>R2: Reschedule actors + Note over R1: Shut down when all actors stopped +``` + +### Drain Disabled + +When a new version is deployed, both versions coexist. New actors are created on the new version while existing actors continue running on the old version until. + +```mermaid +sequenceDiagram + participant R1 as Runner v1 + participant R2 as Runner v2 + + Note over R1: Currently running + Note over R2: Deployed + Note over R1: Actor 1 sleeps from inactivity + Note over R2: Actor 1 wakes up when prompted +``` + +## Configuration + +### Setting the Version + +Configure the runner version using an environment variable or programmatically: + +```bash {{"title": "Environment Variable"}} +RIVET_ENVOY_VERSION=2 +``` + +The version **must** be set at build time, not at runtime. Do not use `Date.now()` or similar runtime values in your registry setup code. This would assign a different version every time the server starts, causing actors to be drained and rescheduled on every restart instead of only on new deployments. + +### Example Configurations + +We recommend injecting a build-time value that increments with every deployment. Here are concrete examples for common setups: + +### Dockerfile + +Generate the version at build time and bake it into the image as an environment variable: + +```bash @nocheck +docker build --build-arg RIVET_ENVOY_VERSION=$(date +%s) . +``` + +```dockerfile @nocheck +FROM node:20-slim +ARG RIVET_ENVOY_VERSION +ENV RIVET_ENVOY_VERSION=$RIVET_ENVOY_VERSION +WORKDIR /app +COPY . . +RUN npm install && npm run build +CMD ["node", "dist/server.js"] +``` + +All containers from this image will share the same version. + +### Next.js + +Set the version in `next.config.ts`. Next.js evaluates this file once at build time and inlines the value into the bundle: + +```typescript @nocheck +import type { NextConfig } from "next"; + +const nextConfig: NextConfig = { + env: { + RIVET_ENVOY_VERSION: String(Math.floor(Date.now() / 1000)), + }, +}; + +export default nextConfig; +``` + +### Vite + +Use `define` in your Vite config. This is evaluated once at build time and inlined into the bundle: + +```typescript @nocheck +import { defineConfig } from "vite"; + +export default defineConfig({ + define: { + "process.env.RIVET_ENVOY_VERSION": JSON.stringify( + String(Math.floor(Date.now() / 1000)) + ), + }, +}); +``` + +### CI/CD + +Set the version from your CI pipeline: + +```yaml @nocheck +# GitHub Actions +env: + RIVET_ENVOY_VERSION: ${{ github.run_number }} +``` + +```bash @nocheck +# Railway / Render / generic CI +export RIVET_ENVOY_VERSION=$(date +%s) +``` + +```bash @nocheck +# Git commit count +export RIVET_ENVOY_VERSION=$(git rev-list --count HEAD) +``` + +### Build Script + +Generate a version file during your build step and import it: + +```json @nocheck +{ + "scripts": { + "build": "echo \"export const BUILD_VERSION = $(date +%s);\" > src/build-version.ts && tsc" + } +} +``` + +```typescript @nocheck +import { actor, setup } from "rivetkit"; +import { BUILD_VERSION } from "./build-version"; + +const myActor = actor({ state: {}, actions: {} }); + +const registry = setup({ + use: { myActor }, + envoy: { + version: BUILD_VERSION, + }, +}); +``` + +### Drain on Version Upgrade + +The `drainOnVersionUpgrade` option controls whether old actors are stopped when a new version is deployed. This is configured in the Rivet dashboard under your runner configuration. See [Pool Configuration](/docs/general/pool-configuration) for the full set of pool options, including how to rate-limit actor eviction during the drain. + +| Value | Behavior | +|-------|----------| +| `false` | Old actors continue running. New actors go to new version. Versions coexist. | +| `true` (default) | Old actors receive stop signal and have 30m to finish gracefully. | + +## Upgrading Actor State + +When you deploy a new version, existing actors may need to handle schema changes in their persisted data. + +### SQLite (recommended for complex schemas) + +**Drizzle (recommended)** + +Use [Drizzle](/docs/actors/sqlite-drizzle) for typed schemas with generated migrations. Drizzle generates versioned `.sql` migration files from your TypeScript schema and applies them in order automatically. This is the recommended approach when your schema evolves frequently. + +**Raw SQL** + +For actors using [raw SQLite](/docs/actors/sqlite), migrations run automatically via the `onMigrate` hook on every actor start. RivetKit wraps the hook in a SQLite savepoint, so the migration is fully atomic. Use SQLite's `user_version` pragma to track which migrations have run: + +### In-memory state (`c.state`) + +If you use `c.state` for persistence, you are responsible for handling schema changes yourself. If you add, remove, or rename fields between versions, your code must handle the old shape gracefully. + +**Manual defaults in `onWake`** + +Apply defaults for missing fields: + +**Zod schema coercion** + +Use [Zod](https://zod.dev/) to parse persisted state on wake. Zod's `.default()` fills in missing fields automatically, so old actor state is coerced to the current schema: + +For anything beyond simple defaults, consider moving to [SQLite](/docs/actors/sqlite) where you get proper migration tooling. + +## Advanced + +### How Version Upgrade Detection Works + +When `drainOnVersionUpgrade` is enabled, Rivet uses two mechanisms to detect version changes: + +- **New runner connection**: When a runner connects with a newer version number, the engine immediately drains all older runners with the same name. This is the primary mechanism for [runner mode](/docs/general/runtime-modes) deployments. +- **Metadata polling** (serverless only): In [serverless mode](/docs/general/runtime-modes), runners periodically poll the engine to check for newer versions and self-drain if one is found. This ensures old runners drain even if no new requests trigger a runner connection. + +### SIGTERM Handling + +When a runner process receives SIGTERM, it gracefully stops all actors before exiting: + +- Each actor's `onSleep` hook is called, giving it time to save state +- Actors are rescheduled to other available runners +- The runner waits up to **30 minutes** for all actors to finish stopping +- If the process is force-killed before actors finish (e.g. SIGKILL), actors are rescheduled with a crash backoff penalty instead of a clean handoff + +Actors have a maximum of 30 minutes to clean up during shutdown. Ensure your platform's drain grace period is at most 30 minutes. + +### Shutdown Timeouts + +Several timeouts control how long each part of the shutdown process can take: + +| Timeout | Default | Description | Configuration | +|---------|---------|-------------|---------------| +| `actor_stop_threshold` | 30m | Engine-side limit on how long each actor has to stop before being marked lost | [Engine config](/docs/self-hosting/configuration) (`pegboard.actor_stop_threshold`) | +| `sleepGracePeriod` | 15s | Total graceful sleep budget for `onSleep`, `waitUntil`, `keepAwake`, and async raw WebSocket handlers | [Actor options](/docs/actors/lifecycle#options) | +| `runner_lost_threshold` | 15s | Fallback detection if the runner dies without graceful shutdown | [Engine config](/docs/self-hosting/configuration) (`pegboard.runner_lost_threshold`) | + +Rivet has a max shutdown grace period of 30 minutes that cannot be configured. + +## Related + +- [Runtime Modes](/docs/general/runtime-modes): Serverless vs runner deployment modes +- [Lifecycle](/docs/actors/lifecycle): Actor lifecycle hooks including `onSleep` + +_Source doc path: /docs/actors/versions_ diff --git a/.agents/skills/multiplayer-game/reference/actors/websocket-handler.md b/.agents/skills/multiplayer-game/reference/actors/websocket-handler.md new file mode 100644 index 0000000..f94499d --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/actors/websocket-handler.md @@ -0,0 +1,201 @@ +# Low-Level WebSocket Handler + +> Source: `src/content/docs/actors/websocket-handler.mdx` +> Canonical URL: https://rivet.dev/docs/actors/websocket-handler +> Description: Actors can handle WebSocket connections through the `onWebSocket` handler. + +--- +For most use cases, [actions](/docs/actors/actions) and [events](/docs/actors/events) provide high-level connection handling powered by WebSockets that's easier to work with than low-level WebSockets. However, low-level handlers are required when implementing custom use cases. + +## Handling WebSocket Connections + +The `onWebSocket` handler manages low-level WebSocket connections. It receives the actor context and a `WebSocket` object. + +See also the [raw WebSocket handler example](https://github.com/rivet-dev/rivet/tree/main/examples/raw-websocket-handler). + +## Connecting To Actors + +### Via RivetKit Client + +Use the `.webSocket()` method on an actor handle to open a WebSocket connection to the actor's `onWebSocket` handler. This can be executed from either your frontend or backend. + +```typescript index.ts @hide @nocheck +import { actor, setup } from "rivetkit"; + +export const chat = actor({ + state: { messages: [] as string[] }, + onWebSocket: (c, websocket) => { + websocket.addEventListener("message", (event) => { + c.state.messages.push(event.data as string); + }); + }, + actions: {} +}); + +export const registry = setup({ use: { chat } }); +registry.start(); +``` + +```typescript client.ts @nocheck +import { createClient } from "rivetkit/client"; +import type { registry } from "./index"; + +const client = createClient("http://localhost:6420"); + +const actor = client.chat.getOrCreate(["my-chat"]); + +// Open WebSocket connection +const ws = await actor.webSocket("/"); + +// Listen for messages +ws.addEventListener("message", (event) => { + const message = JSON.parse(event.data as string); + console.log("Received:", message); +}); + +// Send messages +ws.send(JSON.stringify({ type: "chat", text: "Hello!" })); +``` + +The `.webSocket()` method returns a standard WebSocket. + +### Via getGatewayUrl + +Use `.getGatewayUrl()` to get the raw gateway URL for the actor. This is useful when you need to use the URL with external tools or custom WebSocket clients. + +```typescript index.ts @hide @nocheck +import { actor, setup } from "rivetkit"; + +export const chat = actor({ + state: { messages: [] as string[] }, + onWebSocket: (c, websocket) => { + websocket.addEventListener("message", (event) => { + c.state.messages.push(event.data as string); + }); + }, + actions: {} +}); + +export const registry = setup({ use: { chat } }); +registry.start(); +``` + +```typescript client.ts @nocheck +import { createClient } from "rivetkit/client"; +import type { registry } from "./index"; + +const client = createClient("http://localhost:6420"); + +const actor = client.chat.getOrCreate(["my-chat"]); + +// Get the raw gateway URL +const gatewayUrl = await actor.getGatewayUrl(); +// gatewayUrl = "https://...rivet.dev/..." + +// Convert to WebSocket URL and connect +const wsUrl = gatewayUrl.replace("http://", "ws://").replace("https://", "wss://"); +const ws = new WebSocket(`${wsUrl}/websocket/`); + +ws.addEventListener("message", (event) => { + const message = JSON.parse(event.data as string); + console.log("Received:", message); +}); + +ws.addEventListener("open", () => { + ws.send(JSON.stringify({ type: "chat", text: "Hello!" })); +}); +``` + +### Via HTTP API + +This handler can be accessed with raw WebSockets using `wss://api.rivet.dev/gateway/{actorId}@{token}/websocket/{...path}`. + +For example, to connect to the chat actor above: + +```typescript +// Replace with your actor ID and token +const actorId = "your-actor-id"; +const token = "your-token"; + +const ws = new WebSocket( + `wss://api.rivet.dev/gateway/${actorId}@${token}/websocket/` +); + +ws.addEventListener("message", (event) => { + const message = JSON.parse(event.data as string); + console.log("Received:", message); +}); + +ws.addEventListener("open", () => { + ws.send(JSON.stringify({ type: "chat", text: "Hello!" })); +}); +``` + +```bash +wscat -c "wss://api.rivet.dev/gateway/{actorId}@{token}/websocket/" +``` + +The path after `/websocket/` is passed to your `onWebSocket` handler and can be used to route to different functionality within your actor. For example, to connect with a custom path `/admin`: + +```typescript +// Replace with your actor ID and token +const actorId = "your-actor-id"; +const token = "your-token"; + +const ws = new WebSocket( + `wss://api.rivet.dev/gateway/${actorId}@${token}/websocket/admin` +); +``` + +```bash +wscat -c "wss://api.rivet.dev/gateway/{actorId}@{token}/websocket/admin" +``` + +See the [HTTP API reference](/docs/actors/http-api) for more information on WebSocket routing and authentication. + +### Via Proxying Connections + +You can proxy WebSocket connections from your own server to actor handlers using the RivetKit client. This is useful when you need to add custom authentication or connection management before forwarding to actors. + +See also the [raw WebSocket handler with proxy example](https://github.com/rivet-dev/rivet/tree/main/examples/raw-websocket-handler-proxy). + +## Connection & Lifecycle Hooks + +`onWebSocket` will trigger the `onBeforeConnect`, `onConnect`, and `onDisconnect` hooks. Read more about [lifecycle hooks](/docs/actors/lifecycle). + +Open WebSockets will be listed in `c.conns`. `conn.send` and `c.broadcast` have no effect on low-level WebSocket connections. Read more about [connections](/docs/actors/connections). + +## WinterTC Compliance + +The `onWebSocket` handler uses standard WebSocket APIs and will work with existing libraries expecting WinterTC-compliant WebSocket objects. + +## Advanced + +## WebSocket Hibernation + +WebSocket hibernation allows actors to go to sleep while keeping WebSocket connections alive. Actors automatically wake up when a message is received or the connection closes. + +Enable hibernation by setting `canHibernateWebSocket: true`. You can also pass a function `(request) => boolean` for conditional control. + +Since `open` only fires once when the client first connects, use `c.conn.state` to store per-connection data that persists across sleep cycles. See [connections](/docs/actors/connections) for more details. + +### Accessing the Request + +The underlying HTTP request is available via `c.request`. This is useful for accessing the path or query parameters. + +### Skip Ready Wait + +Connections are normally held at the gateway until the actor is ready. Pass `skipReadyWait: true` on `handle.webSocket()` to connect immediately, including while the actor is still starting or in the [sleep grace period](/docs/actors/lifecycle#shutdown-sequence). See [Skip Ready Wait](/docs/clients/javascript#skip-ready-wait) for details. + +### Async Handlers + +The `onWebSocket` handler can be async, allowing you to perform async code before setting up event listeners: + +## API Reference + +- [`WebSocketContext`](/typedoc/interfaces/rivetkit.mod.WebSocketContext.html) - Context for WebSocket handlers +- [`UniversalWebSocket`](/typedoc/interfaces/rivetkit.mod.UniversalWebSocket.html) - Universal WebSocket interface +- [`handleRawWebSocketHandler`](/typedoc/functions/rivetkit.mod.handleRawWebSocketHandler.html) - Function to handle raw WebSocket +- [`UpgradeWebSocketArgs`](/typedoc/interfaces/rivetkit.mod.UpgradeWebSocketArgs.html) - Arguments for WebSocket upgrade + +_Source doc path: /docs/actors/websocket-handler_ diff --git a/.agents/skills/multiplayer-game/reference/actors/workflows.md b/.agents/skills/multiplayer-game/reference/actors/workflows.md new file mode 100644 index 0000000..9c59975 --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/actors/workflows.md @@ -0,0 +1,228 @@ +# Workflows + +> Source: `src/content/docs/actors/workflows.mdx` +> Canonical URL: https://rivet.dev/docs/actors/workflows +> Description: Build durable, replayable run loops in Rivet Actors with steps, queue waits, timers, and rollback. + +--- +Use workflows for durable, multi-step execution with replay safety. + +## What are workflows? + +A workflow is a durable, replayable run handler for a Rivet Actor. + +- Survives restarts: workflow progress is saved automatically. +- Re-runs safely: replay follows the same recorded steps. +- Event-driven: workflows can pause for queue messages, then continue. + +## Getting started + +### Simple workflow + +Use this when you need a short multi-step sequence. + +### Loops + +This is the recommended workflow shape for most actor workloads. + +- Use a queue wait inside the loop to receive the next unit of work. +- Keep actor state changes in a single workflow loop. +- This gives you one durable workflow that manages all actor progress. + +### Setup & teardown + +Use this when the workflow should initialize resources, process queued commands, then clean up. + +## Features + +### Queue + +Use this for fire-and-forget commands where the client does not need a reply. + +Use the `Loops` example above as the baseline pattern. + +### Request/response (using queue) + +Use this when the caller needs a response from queued processing. + +### Timers + +Use queue messages as the trigger source, then sleep durably inside the workflow. + +### Join + +Use `join` when several independent tasks can run in parallel. + +### Race + +Use `race` when you need first-winner behavior. + +### Timeouts + +Use step timeouts and retries for slow or flaky dependencies. + +Step timeouts are critical by default and fail immediately. Set `retryOnTimeout: true` if a timeout should retry like any other error using `maxRetries`. + +Workflows use roll-forward semantics everywhere. When a step throws, any `state` or `vars` mutations it made before failing are never rolled back, whether the step retries or the failure is caught by `tryStep` or `try`. The next attempt observes whatever the failed attempt already wrote, so write steps idempotently: check before you increment, or move the mutation after the fallible work. + +### Handling terminal failures as data + +Use `tryStep` when a step failure should produce data instead of failing the whole workflow. + +Use `try` when you want to recover from terminal `step`, `join`, or `race` failures inside a named block. + +```ts +async function runPaymentFlow(ctx: any) { + return await ctx.try("payment-flow", async (blockCtx: any) => { + const auth = await blockCtx.step("authorize", async (blockCtx) => + authorizeOrder("order-123"), + ); + const capture = await blockCtx.step("capture", async (blockCtx) => + captureOrder("order-123"), + ); + return { auth, capture }; + }); +} + +async function authorizeOrder(orderId: string): Promise { + return `auth-${orderId}`; +} + +async function captureOrder(orderId: string): Promise { + return `capture-${orderId}`; +} +``` + +- `tryStep` and `try` only catch terminal failures. Retry backoff, sleeps, queue waits, eviction, and history divergence still rethrow. +- Catching a failure does not undo it. `state` and `vars` mutations made before the failure remain visible after `tryStep` or `try` returns, so use explicit compensating steps when a caught failure needs cleanup. +- `RollbackError` is not caught by default. Pass `catch: ["rollback"]` when you want rollback failures returned as data. + +### Error hooks + +Use `onError` when you want a best-effort notification for workflow failures. + +- Step failures include the attempt number, retry counts, whether the step will retry, and the next retry delay. +- Workflow failures also include terminal errors outside steps, such as rollback failures or code/history mismatches. +- The hook is observational. It is not part of workflow replay, so use it for logging, metrics, or updating non-critical actor state. +- This is also a good place to forward workflow failures to Sentry or another error reporting pipeline. + +### Rollback + +Use rollback checkpoints before steps that have compensating actions. + +## Patterns + +### Store workflow progress in state + broadcast + +Store progress in `state` so replay and recovery always restore it. Broadcast state changes so clients can render progress in realtime. + +### Cron (queue-driven) + +Rivet scheduling triggers actions. For cron-like workflows, use a small scheduled action as a bridge that enqueues work, then process that work in the workflow loop. + +These are common workflow shapes used in production systems. + +### Queue-driven worker + +Use this when external systems enqueue work and the actor should process each item durably. + +### Setup & teardown + +Use this when you need one-time initialization before a long-lived loop, plus cleanup when the actor stops sleeping or is destroyed. + +### Human approval gate + +Use this when an operation must pause for a user or system decision before continuing. + +### Fan-out / fan-in (join) + +Use this when independent work items can run in parallel and you need a single merged result. + +### Batch drainer + +Use this when throughput matters and handling one message at a time is too expensive. + +### Coordinator -> worker RPC + +Use this when one actor orchestrates work by calling actions on other actors. + +### Request/response over queue (async RPC) + +Use this when you want decoupled actor-to-actor communication with durable waits and explicit completion. + +### Scatter-gather across actors + +Use this when multiple actors can process independent parts of a request in parallel, then return a merged response. + +### Timeout + fallback actor + +Use this when a primary actor call might be slow or unavailable and you need a deterministic fallback path. + +### Cross-actor saga (compensating actions) + +Use this when a workflow spans multiple actors and each side effect may need compensation. + +### Signal-driven control loop + +Use this when workflow progress should be triggered by commands/events instead of fixed polling intervals. + +### Poll + backoff loop + +Use this when an external dependency has variable availability and retries should slow down after failures. + +### Child worker orchestration + +Use this when one workflow coordinates many child workers (actors or worker workflows) and manages their lifecycle. + +### Bounded drain + concurrency cap + +Use this when inbound work can spike and you need predictable per-iteration limits. + +### Versioned workflow evolution + +Use this when workflow structure changes across deployments and old histories must still replay. + +### Version gates with `getVersion` + +Use `ctx.getVersion(name, latest)` to branch behavior when you change a workflow's logic while old instances are still in flight. It returns the version this instance is pinned to at that point: + +- A fresh instance resolves to `latest`. +- An instance that already executed past this point under older code resolves to `1` (the implicit floor). + +The resolved version is recorded in history, so replays are deterministic and each instance stays on the branch it started on. Inside a loop, every iteration resolves independently, so in-flight iterations finish on the old branch while new iterations pick up `latest`. + +`latest` must be an integer `>= 1`, and the gate name must be unique within its scope like any other entry. Once every old instance has drained, retire the gate by replacing the call with `ctx.removed(name, "version_check")`. + +### Checkpoint-friendly loop design + +Use this when you need reliable replay and resume semantics across crashes and restarts. + +## Migrations + +- Keep workflow entry names stable once deployed. +- If an old entry was removed or renamed, call `ctx.removed(name, originalType)`. +- To change behavior at a point while old instances are still running, gate it with `ctx.getVersion(name, latest)` (see [Version gates](#version-gates-with-getversion)). +- This keeps replay compatible across deployments. + +## Step-only access to actor APIs + +`state`, `vars`, `db`, `client()`, and connection/event APIs are only valid inside `ctx.step(...)` callbacks. + +Use non-step workflow code for orchestration only: queue waits, sleeps, loops, joins, races, and rollback boundaries. Keep actor-local side effects in steps. + +## Debugging + +- `GET /inspector/workflow-history` returns workflow history status for an actor. +- Response includes `isWorkflowEnabled` and `history`. +- In non-dev mode, inspector endpoints require authorization. + +## Recommendations + +- Prefer queue-driven loops for long-lived workflows. +- Structure long-lived workflows with setup and teardown around the main loop. +- Keep actor state changes and side effects inside steps. +- Store workflow progress in `state` and broadcast updates as progress changes. +- Use timeouts and rollback for external side effects. +- Write step bodies idempotently. `state` and `vars` mutations from a failed attempt are never rolled back, whether the step retries or `tryStep`/`try` catches the failure. + +_Source doc path: /docs/actors/workflows_ diff --git a/.agents/skills/multiplayer-game/reference/cli.md b/.agents/skills/multiplayer-game/reference/cli.md new file mode 100644 index 0000000..34880ea --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/cli.md @@ -0,0 +1,103 @@ +# CLI + +> Source: `src/content/docs/cli.mdx` +> Canonical URL: https://rivet.dev/docs/cli +> Description: Reference for the optional rivet CLI: deploy to Rivet Compute and run local dev for serverless platforms. + +--- +The `rivet` CLI (`@rivetkit/cli`) is optional. You only need it for: + +| Use case | Command | +| --- | --- | +| Deploy to Rivet Compute | `rivet deploy` | +| Local dev for serverless platforms (Cloudflare, Supabase) | `rivet dev --provider ` | + +Run it with your package runner: + +```bash +npx @rivetkit/cli +``` + +| Command | Description | +| --- | --- | +| `rivet dev` | Run a local engine and your handler's dev server. | +| `rivet deploy` | Build and deploy the project to Rivet Cloud. | +| `rivet engine` | Run the bundled `rivet-engine` binary directly. | +| `rivet setup-ci` | Install the GitHub Actions deploy workflow. | + +## `rivet dev` + +Starts a local engine, spawns your dev server, and registers the serverless runner pointing at it. The engine keeps running across restarts; Ctrl-C stops only the dev server. + +```bash +rivet dev [--provider ] [--port N] [--fn-name NAME] [--url URL] [-- ...] +``` + +`--provider` selects how the dev server is launched. Anything after `--` is appended to the preset command (or is the command to run when no provider is set). + +| `--provider` | Spawns | Port | +| --- | --- | --- | +| _(omitted)_ | your `-- ` (needs `--port`) | from `--port` | +| `serverless` | your `-- ` (gets `PORT`) | auto | +| `cloudflare` | `wrangler dev` | `8787` | +| `supabase` | `supabase functions serve` | `54321` | +| `none` | nothing (engine only) | — | + +For `cloudflare`, the CLI also passes the engine endpoint as `--var RIVET_ENDPOINT:...`, so the Worker connects back with no `wrangler.toml` config. + +| Flag | Description | +| --- | --- | +| `--port` | Handler port. Required without a provider unless `--url` is set. | +| `--fn-name` | Supabase function name (default `rivet`). | +| `--url` | Explicit handler URL, overriding port and path. | +| `--engine-binary` | Path to a `rivet-engine` binary. | + +## `rivet deploy` + +Builds and pushes your project's Docker image and upserts the managed pool, printing the dashboard URL. See [Deploying to Rivet Compute](/docs/deploy/rivet-compute). + +```bash +rivet deploy --token cloud_api_xxxxx +``` + +The token is saved to `~/.rivet/credentials` (also read from `RIVET_CLOUD_TOKEN`), so later deploys can omit it. + +| Flag | Default | Description | +| --- | --- | --- | +| `--token` | env / credentials | Rivet Cloud API token. | +| `--namespace` | `production` | Cloud namespace. | +| `--project` / `--org` | from token | Override project/org. | +| `--dockerfile` | `Dockerfile` | Dockerfile to build. | +| `--build-context` | `.` | Docker build context. | +| `--env KEY=VAL` | — | Environment override, repeatable. | +| `--image` | project slug | Image repository name. | +| `--tag` | git short SHA | Image tag. | + +## `rivet engine` + +Runs the bundled `rivet-engine` binary directly, against the same local database and ports as `rivet dev`. Arguments are forwarded verbatim. + +```bash +rivet engine nuke # wipe local engine state +rivet engine wf list # inspect workflows +``` + +## `rivet setup-ci` + +Installs `.github/workflows/rivet-deploy.yml`, which deploys to Rivet Cloud on push and pull request. Add `--force` to overwrite. Then set the token secret: + +```bash +gh secret set RIVET_CLOUD_TOKEN +``` + +## Engine binary resolution + +`rivet dev` and `rivet engine` resolve the `rivet-engine` binary from, in order: `--engine-binary`, `RIVET_ENGINE_BINARY_PATH`, a binary bundled next to the CLI, a local `target/{debug,release}` build, then an auto-downloaded release. Set `RIVETKIT_ENGINE_AUTO_DOWNLOAD=0` to require a local binary. + +## Related + +- [Deploying to Rivet Compute](/docs/deploy/rivet-compute) +- [Cloudflare Workers Quickstart](/docs/actors/quickstart/cloudflare) +- [Supabase Functions Quickstart](/docs/actors/quickstart/supabase) + +_Source doc path: /docs/cli_ diff --git a/.agents/skills/multiplayer-game/reference/clients/javascript.md b/.agents/skills/multiplayer-game/reference/clients/javascript.md new file mode 100644 index 0000000..689d79b --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/clients/javascript.md @@ -0,0 +1,120 @@ +# Node.js & Bun + +> Source: `src/content/docs/clients/javascript.mdx` +> Canonical URL: https://rivet.dev/docs/clients/javascript +> Description: Connect JavaScript apps to Rivet Actors. + +--- +## Getting Started + +See the [backend quickstart guide](/docs/actors/quickstart/backend) for getting started. + +## Minimal Client + +## Stateless vs Stateful + +## Getting Actors + +## Connection Parameters + +Use `params` for static connection parameters. Use `getParams` when the value can change between connection attempts, such as refreshing a JWT before each `.connect()` or reconnect. + +## Subscribing to Events + +## Connection Lifecycle + +## Low-Level HTTP & WebSocket + +For actors that implement `onRequest` or `onWebSocket`, call them directly: + +```ts @nocheck +import { createClient } from "rivetkit/client"; + +const client = createClient(); +const handle = client.chatRoom.getOrCreate(["general"]); + +const response = await handle.fetch("history"); +const history = await response.json(); + +const ws = await handle.webSocket("stream"); +ws.addEventListener("message", (event) => { + console.log("message:", event.data); +}); +ws.send("hello"); +``` + +## Calling from Backend + +## Error Handling + +## Concepts + +### Keys + +Keys uniquely identify actor instances. Use compound keys (arrays) for hierarchical addressing: + +Don't build keys with string interpolation like `"org:${userId}"` when `userId` contains user data. Use arrays instead to prevent key injection attacks. + +### Environment Variables + +`createClient()` automatically reads: + +- `RIVET_ENDPOINT` (endpoint) +- `RIVET_NAMESPACE` +- `RIVET_TOKEN` +- `RIVET_RUNNER` + +Defaults to `http://localhost:6420` when unset. RivetKit runs on port 6420 by default. + +### Endpoint Format + +Endpoints support URL auth syntax: + +``` +https://namespace:token@api.rivet.dev +``` + +You can also pass the endpoint without auth and provide `RIVET_NAMESPACE` and `RIVET_TOKEN` separately. For serverless deployments, use your app's `/api/rivet` URL. See [Endpoints](/docs/general/endpoints#url-auth-syntax) for details. + +## Advanced + +### Skip Ready Wait + +Requests are normally held at the gateway until the actor is ready to accept traffic. An actor is not ready while it's still starting (before `onWake` finishes) or while it's in the [sleep grace period](/docs/actors/lifecycle#shutdown-sequence) (running `onSleep`, `waitUntil`, and pending disconnects). + +Pass `skipReadyWait: true` on the [low-level HTTP and WebSocket APIs](#low-level-http--websocket) to deliver immediately and reach the actor's `onRequest` / `onWebSocket` handler in either window: + +```ts @nocheck +import { createClient } from "rivetkit/client"; + +const client = createClient(); +const handle = client.chatRoom.getOrCreate(["general"]); + +const response = await handle.fetch("/healthz", { + skipReadyWait: true, +}); + +const ws = await handle.webSocket("probe", undefined, { + skipReadyWait: true, +}); +``` + +Requests can still return transient lifecycle or gateway errors. Retry once the actor is available again. + +- `actor.stopping`: the actor has fully stopped, i.e. the sleep grace period has ended but it has not yet restarted. +- `guard.actor_stopped_while_waiting`: the request reached the actor tunnel, but the actor stopped before the gateway received a response. +- `guard.tunnel_request_aborted`: the actor tunnel aborted the request before a response started. +- `guard.tunnel_message_timeout`: the gateway dropped the in-flight tunnel request after its tunnel message timeout. +- `guard.tunnel_response_closed`: the actor tunnel closed before sending a response. +- `guard.gateway_response_start_timeout`: the gateway timed out waiting for the actor response to start. + +## API Reference + +**Package:** [rivetkit](https://www.npmjs.com/package/rivetkit) + +See the [RivetKit client overview](/docs/clients). + +- [`createClient`](/typedoc/functions/rivetkit.client_mod.createClient.html) - Create a client +- [`Client`](/typedoc/types/rivetkit.mod.Client.html) - Client type + +_Source doc path: /docs/clients/javascript_ diff --git a/.agents/skills/multiplayer-game/reference/clients/react.md b/.agents/skills/multiplayer-game/reference/clients/react.md new file mode 100644 index 0000000..9b976e2 --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/clients/react.md @@ -0,0 +1,88 @@ +# React + +> Source: `src/content/docs/clients/react.mdx` +> Canonical URL: https://rivet.dev/docs/clients/react +> Description: Connect React apps to Rivet Actors. + +--- +## Getting Started + +See the [React quickstart guide](/docs/actors/quickstart/react) for getting started. + +## Install + +## Minimal Client + +## Stateless vs Stateful + +## Getting Actors + +## Connection Parameters + +## Subscribing to Events + +## Connection Lifecycle + +## Low-Level HTTP & WebSocket + +Use the JavaScript client for raw HTTP and WebSocket access: + +```tsx @nocheck +import { createClient } from "rivetkit/client"; + +const client = createClient(); +const handle = client.chatRoom.getOrCreate(["general"]); + +const response = await handle.fetch("history"); +const history = await response.json(); + +const ws = await handle.webSocket("stream"); +ws.addEventListener("message", (event) => { + console.log("message:", event.data); +}); +ws.send("hello"); +``` + +## Calling from Backend + +Use the JavaScript client on your backend (Node.js/Bun). See the [JavaScript client docs](/docs/clients/javascript). + +## Error Handling + +## Concepts + +### Keys + +Keys uniquely identify actor instances. Use compound keys (arrays) for hierarchical addressing: + +Don't build keys with string interpolation like `"org:${userId}"` when `userId` contains user data. Use arrays instead to prevent key injection attacks. + +### Environment Variables + +`createRivetKit()` (and the underlying `createClient()` instance) automatically read: + +- `RIVET_ENDPOINT` +- `RIVET_NAMESPACE` +- `RIVET_TOKEN` +- `RIVET_RUNNER` + +Defaults to `http://localhost:6420` when unset. RivetKit runs on port 6420 by default. + +### Endpoint Format + +Endpoints support URL auth syntax: + +``` +https://namespace:token@api.rivet.dev +``` + +You can also pass the endpoint without auth and provide `RIVET_NAMESPACE` and `RIVET_TOKEN` separately. For serverless deployments, use your app's `/api/rivet` URL. See [Endpoints](/docs/general/endpoints#url-auth-syntax) for details. + +## API Reference + +**Package:** [@rivetkit/react](https://www.npmjs.com/package/@rivetkit/react) + +- [`createRivetKit`](/docs/clients/react) - Create hooks for React +- [`useActor`](/docs/clients/react) - Hook for actor instances + +_Source doc path: /docs/clients/react_ diff --git a/.agents/skills/multiplayer-game/reference/clients/rust.md b/.agents/skills/multiplayer-game/reference/clients/rust.md new file mode 100644 index 0000000..32bebfc --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/clients/rust.md @@ -0,0 +1,367 @@ +# Rust (Beta) + +> Source: `src/content/docs/clients/rust.mdx` +> Canonical URL: https://rivet.dev/docs/clients/rust +> Description: Connect Rust apps to Rivet Actors. + +--- +Rust support is in beta. The supported public Rust API is `rivetkit` and `rivetkit::client`; lower-level crates are internal implementation details and do not carry a stability guarantee. See the full API reference on [docs.rs/rivetkit](https://docs.rs/rivetkit), or the runnable [`hello-world-rust`](https://github.com/rivet-dev/rivet/tree/main/examples/hello-world-rust) example. + +## Getting Started + +See the [Rust quickstart guide](/docs/actors/quickstart/rust) for getting started. + +## Install + +Add the `rivetkit` crate and its companions: + +```sh +cargo add rivetkit anyhow async-trait +cargo add serde --features derive +cargo add tokio --features full +``` + +The Rust client is strongly typed. It shares the same action and event types as your actor, so define your actor in `src/lib.rs` and import those types from both your server and your client. There is no need to redefine the actor on the client. See [Define Your Actor](/docs/actors/quickstart/rust#define-your-actor) in the quickstart for the actor definition this page builds on. + +## Minimal Client + +```rust @nocheck +use counter::{Counter, Increment}; +use rivetkit::{ + client::{Client, ClientConfig}, + prelude::*, + TypedClientExt, +}; + +#[tokio::main] +async fn main() -> Result<()> { + let client = Client::new(ClientConfig::new("http://localhost:6420").namespace("default")); + + let counter = client.get_or_create_typed_default::("counter", ["my-counter"])?; + let count = counter.send(Increment { amount: 1 }).await?; + println!("New count: {count}"); + + Ok(()) +} +``` + +`counter` here is your crate name (the package `name` in `Cargo.toml`, with dashes as underscores). `Counter` and `Increment` are the types you defined alongside your actor. + +## Stateless vs Stateful + +```rust @nocheck +use counter::{Counter, Increment, NewCount}; +use rivetkit::{ + client::{Client, ClientConfig}, + prelude::*, + TypedClientExt, +}; + +#[tokio::main] +async fn main() -> Result<()> { + let client = Client::new(ClientConfig::new("http://localhost:6420").namespace("default")); + let counter = client.get_or_create_typed_default::("counter", ["my-counter"])?; + + // Stateless: each call is independent + counter.send(Increment { amount: 1 }).await?; + + // Stateful: keep a connection open for realtime events + let connection = counter.connect(); + connection + .on::(|event| println!("count: {}", event.count)) + .await; + connection.send(Increment { amount: 1 }).await?; + + connection.disconnect().await; + Ok(()) +} +``` + +A stateless call on the handle opens a short-lived request per action. A connection keeps a WebSocket open so you can receive events and reuse it across calls. + +## Getting Actors + +```rust @nocheck +use counter::Counter; +use rivetkit::{ + client::{Client, ClientConfig, GetOrCreateOptions}, + prelude::*, + TypedClientExt, +}; +use serde_json::json; + +#[tokio::main] +async fn main() -> Result<()> { + let client = Client::new(ClientConfig::new("http://localhost:6420").namespace("default")); + + // Get or create an actor + let room = client.get_or_create_typed_default::("counter", ["room-42"])?; + + // Get an existing actor handle (fails when used if the actor does not exist) + let existing = client.get_typed_default::("counter", ["room-42"])?; + + // Create a new actor with input + let created = client.get_or_create_typed::( + "counter", + ["game-1"], + GetOrCreateOptions { + create_with_input: Some(json!({ "mode": "ranked" })), + ..Default::default() + }, + )?; + + // Get an actor handle by ID + let by_id = client.get_for_id("counter", "actor-id", Default::default())?; + + // Resolve the actor ID + let resolved_id = room.inner().resolve().await?; + println!("Resolved ID: {resolved_id}"); + + Ok(()) +} +``` + +`get_typed_default` / `get_or_create_typed_default` use default options. The non-default variants (`get_typed` / `get_or_create_typed`) take `GetOptions` / `GetOrCreateOptions` for connection parameters, input, and region. + +## Connection Parameters + +Pass connection parameters through the handle options. They are delivered to the actor's `create_conn_state` callback: + +```rust @nocheck +use counter::Counter; +use rivetkit::{ + client::{Client, ClientConfig, GetOrCreateOptions}, + prelude::*, + TypedClientExt, +}; +use serde_json::json; + +#[tokio::main] +async fn main() -> Result<()> { + let client = Client::new(ClientConfig::new("http://localhost:6420").namespace("default")); + + let chat = client.get_or_create_typed::( + "counter", + ["general"], + GetOrCreateOptions { + params: Some(json!({ "authToken": "jwt-token-here" })), + ..Default::default() + }, + )?; + + let connection = chat.connect(); + connection.disconnect().await; + Ok(()) +} +``` + +## Subscribing to Events + +`on` registers a typed callback for an event and returns once the subscription is registered: + +```rust @nocheck +use counter::{Counter, NewCount}; +use rivetkit::{ + client::{Client, ClientConfig}, + prelude::*, + TypedClientExt, +}; + +#[tokio::main] +async fn main() -> Result<()> { + let client = Client::new(ClientConfig::new("http://localhost:6420").namespace("default")); + let connection = client + .get_or_create_typed_default::("counter", ["general"])? + .connect(); + + connection + .on::(|event| println!("count changed: {}", event.count)) + .await; + + Ok(()) +} +``` + +Event callbacks are synchronous and run for every matching event. The actor's emitted event type (here `NewCount`) is decoded into the typed value for you. + +## Connection Lifecycle + +The lower-level connection exposes lifecycle callbacks and the current status. Reach it with `connection.inner()`: + +```rust @nocheck +use counter::Counter; +use rivetkit::{ + client::{Client, ClientConfig}, + prelude::*, + TypedClientExt, +}; + +#[tokio::main] +async fn main() -> Result<()> { + let client = Client::new(ClientConfig::new("http://localhost:6420").namespace("default")); + let connection = client + .get_or_create_typed_default::("counter", ["general"])? + .connect(); + let inner = connection.inner().clone(); + + inner.on_open(|| println!("connected")).await; + inner.on_close(|| println!("disconnected")).await; + inner.on_error(|message| eprintln!("error: {message}")).await; + inner + .on_status_change(|status| println!("status: {status:?}")) + .await; + + println!("current status: {:?}", inner.conn_status()); + + connection.disconnect().await; + Ok(()) +} +``` + +`ConnectionStatus` is one of `Idle`, `Connecting`, `Connected`, or `Disconnected`. Connections reconnect automatically with backoff until you call `disconnect`. + +## Low-Level HTTP & WebSocket + +For actors that implement `on_request` or `on_websocket`, call them directly on the untyped handle (`handle.inner()`). `fetch` returns a `reqwest::Response`, and `web_socket` returns a `tokio_tungstenite` stream. This example also needs a few extra crates: + +```sh +cargo add futures-util tokio-tungstenite +cargo add reqwest --features json +``` + +```rust @nocheck +use counter::Counter; +use futures_util::{SinkExt, StreamExt}; +use reqwest::{header::HeaderMap, Method}; +use rivetkit::{ + client::{Client, ClientConfig}, + prelude::*, + TypedClientExt, +}; +use tokio_tungstenite::tungstenite::Message; + +#[tokio::main] +async fn main() -> Result<()> { + let client = Client::new(ClientConfig::new("http://localhost:6420").namespace("default")); + let handle = client.get_or_create_typed_default::("counter", ["general"])?; + + // Raw HTTP request + let response = handle + .inner() + .fetch("history", Method::GET, HeaderMap::new(), None) + .await?; + let history: Vec = response.json().await?; + println!("history: {history:?}"); + + // Raw WebSocket connection + let mut ws = handle.inner().web_socket("stream", None).await?; + ws.send(Message::text("hello")).await?; + if let Some(message) = ws.next().await { + println!("received: {:?}", message?); + } + + Ok(()) +} +``` + +## Calling from Backend + +The client is a normal Tokio type, so you can hold it in your backend (Axum, Actix, etc.) and call actors from request handlers. The client is `Clone` and cheap to share: + +```rust @nocheck +use counter::{Counter, Increment}; +use rivetkit::{ + client::{Client, ClientConfig}, + prelude::*, + TypedClientExt, +}; + +async fn increment(client: Client) -> Result { + let counter = client.get_or_create_typed_default::("counter", ["server-counter"])?; + let count = counter.send(Increment { amount: 1 }).await?; + Ok(count) +} +``` + +## Error Handling + +Action and connection calls return `anyhow::Result`. Actor-side errors surface as an `anyhow::Error` carrying the error group, code, message, and metadata: + +```rust @nocheck +use counter::{Counter, Increment}; +use rivetkit::{ + client::{Client, ClientConfig}, + prelude::*, + TypedClientExt, +}; + +#[tokio::main] +async fn main() -> Result<()> { + let client = Client::new(ClientConfig::new("http://localhost:6420").namespace("default")); + let counter = client.get_or_create_typed_default::("counter", ["my-counter"])?; + + match counter.send(Increment { amount: 1 }).await { + Ok(count) => println!("count: {count}"), + Err(error) => eprintln!("action failed: {error:#}"), + } + + Ok(()) +} +``` + +## Concepts + +### Keys + +Keys uniquely identify actor instances. Use compound keys (arrays) for hierarchical addressing: + +```rust @nocheck +use counter::Counter; +use rivetkit::{ + client::{Client, ClientConfig}, + prelude::*, + TypedClientExt, +}; + +#[tokio::main] +async fn main() -> Result<()> { + let client = Client::new(ClientConfig::new("http://localhost:6420").namespace("default")); + + // Compound key: [org, room] + let room = client.get_or_create_typed_default::("counter", ["org-acme", "general"])?; + let actor_id = room.inner().resolve().await?; + println!("Actor ID: {actor_id}"); + + Ok(()) +} +``` + +Keys accept arrays of `&str` or `String` (`["org-acme", "general"]`). Don't build keys with string interpolation like `format!("org:{user_id}")` when `user_id` contains user data. Use arrays instead to prevent key injection attacks. + +### Configuration + +`ClientConfig::new(endpoint)` is a builder. The endpoint is always required; there is no default. Common options: + +```rust @nocheck +use rivetkit::client::ClientConfig; + +let config = ClientConfig::new("http://localhost:6420") + .namespace("default") + .token("pk_...") + .pool_name("my-pool") + .header("x-custom", "value"); +``` + +- `namespace` - target namespace (defaults to the engine's configured namespace). +- `token` - authentication token for the engine. +- `pool_name` - runner pool to target. +- `header` / `headers` - extra HTTP headers sent with each request. +- `max_input_size` - cap on encoded action input size. + +For serverless deployments, set the endpoint to your app's `/api/rivet` URL. See [Endpoints](/docs/general/endpoints) for details. + +## API Reference + +See the full client API documentation on [docs.rs/rivetkit-client](https://docs.rs/rivetkit-client/latest/rivetkit_client/). + +_Source doc path: /docs/clients/rust_ diff --git a/.agents/skills/multiplayer-game/reference/clients/swift.md b/.agents/skills/multiplayer-game/reference/clients/swift.md new file mode 100644 index 0000000..c760645 --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/clients/swift.md @@ -0,0 +1,454 @@ +# Swift + +> Source: `src/content/docs/clients/swift.mdx` +> Canonical URL: https://rivet.dev/docs/clients/swift +> Description: Connect Swift apps to Rivet Actors. + +--- +## Install + +Add the Swift package dependency and import `RivetKitClient`: + +```swift +// Package.swift +dependencies: [ + .package(url: "https://github.com/rivet-dev/rivetkit-swift", from: "2.0.0") +] + +targets: [ + .target( + name: "MyApp", + dependencies: [ + .product(name: "RivetKitClient", package: "rivetkit-swift") + ] + ) +] +``` + +## Minimal Client + +### Endpoint URL + +```swift +import RivetKitClient + +let config = try ClientConfig( + endpoint: "https://my-namespace:pk_...@api.rivet.dev" +) +let client = RivetKitClient(config: config) + +let handle = client.getOrCreate("counter", ["my-counter"]) +let count: Int = try await handle.action("increment", 1, as: Int.self) +``` + +### Explicit Fields + +```swift +import RivetKitClient + +let config = try ClientConfig( + endpoint: "https://api.rivet.dev", + namespace: "my-namespace", + token: "pk_..." +) +let client = RivetKitClient(config: config) + +let handle = client.getOrCreate("counter", ["my-counter"]) +let count: Int = try await handle.action("increment", 1, as: Int.self) +``` + +## Stateless vs Stateful + +```swift +import RivetKitClient + +let config = try ClientConfig(endpoint: "http://localhost:6420") +let client = RivetKitClient(config: config) + +let handle = client.getOrCreate("counter", ["my-counter"]) + +// Stateless: each call is independent +let current: Int = try await handle.action("getCount", as: Int.self) +print("Current count: \(current)") + +// Stateful: keep a connection open for realtime events +let conn = handle.connect() + +// Subscribe to events using AsyncStream +let eventTask = Task { + for await count in await conn.events("count", as: Int.self) { + print("Event: \(count)") + } +} + +_ = try await conn.action("increment", 1, as: Int.self) + +eventTask.cancel() +await conn.dispose() +await client.dispose() +``` + +## Getting Actors + +```swift +import RivetKitClient + +struct GameInput: Encodable { + let mode: String +} + +let config = try ClientConfig(endpoint: "http://localhost:6420") +let client = RivetKitClient(config: config) + +// Get or create an actor +let room = client.getOrCreate("chatRoom", ["room-42"]) + +// Get an existing actor (fails if not found) +let existing = client.get("chatRoom", ["room-42"]) + +// Create a new actor with input +let created = try await client.create( + "game", + ["game-1"], + options: CreateOptions(input: GameInput(mode: "ranked")) +) + +// Get actor by ID +let byId = client.getForId("chatRoom", "actor-id") + +// Resolve actor ID +let resolvedId = try await room.resolve() +print("Resolved ID: \(resolvedId)") + +await client.dispose() +``` + +Actions support positional overloads for 0–5 args: + +```swift +import RivetKitClient + +let config = try ClientConfig(endpoint: "http://localhost:6420") +let client = RivetKitClient(config: config) +let handle = client.getOrCreate("counter", ["my-counter"]) + +let count: Int = try await handle.action("getCount") +let updated: String = try await handle.action("rename", "new-name") +let ok: Bool = try await handle.action("setScore", "user-1", 42) + +print("Count: \(count), Updated: \(updated), OK: \(ok)") +await client.dispose() +``` + +If you need more than 5 arguments, use the raw JSON fallback: + +```swift +import RivetKitClient + +let config = try ClientConfig(endpoint: "http://localhost:6420") +let client = RivetKitClient(config: config) +let handle = client.getOrCreate("counter", ["my-counter"]) + +let args: [JSONValue] = [ + .string("user-1"), + .number(.int(42)), + .string("extra"), + .string("more"), + .string("args"), + .string("here") +] +let ok: Bool = try await handle.action("setScore", args: args, as: Bool.self) +print("OK: \(ok)") + +await client.dispose() +``` + +## Connection Parameters + +```swift +import RivetKitClient + +struct ConnParams: Encodable { + let authToken: String +} + +let config = try ClientConfig(endpoint: "http://localhost:6420") +let client = RivetKitClient(config: config) + +let chat = client.getOrCreate( + "chatRoom", + ["general"], + options: GetOrCreateOptions(params: ConnParams(authToken: "jwt-token-here")) +) + +let conn = chat.connect() + +// Use the connection... +for await status in await conn.statusChanges() { + print("Status: \(status.rawValue)") + if status == .connected { + break + } +} + +await conn.dispose() +await client.dispose() +``` + +## Subscribing to Events + +```swift +import RivetKitClient + +let config = try ClientConfig(endpoint: "http://localhost:6420") +let client = RivetKitClient(config: config) +let conn = client.getOrCreate("chatRoom", ["general"]).connect() + +// Subscribe to events using AsyncStream +let messageTask = Task { + for await (from, body) in await conn.events("message", as: (String, String).self) { + print("\(from): \(body)") + } +} + +// For one-time events, break after receiving +let gameOverTask = Task { + for await _ in await conn.events("gameOver", as: Void.self) { + print("done") + break + } +} + +// Let it run for a bit +try await Task.sleep(for: .seconds(5)) + +// Cancel when done +messageTask.cancel() +gameOverTask.cancel() +await conn.dispose() +await client.dispose() +``` + +Event streams support 0–5 typed arguments. If you need raw values or more than 5 arguments, use `JSONValue`: + +```swift +import RivetKitClient + +let config = try ClientConfig(endpoint: "http://localhost:6420") +let client = RivetKitClient(config: config) +let conn = client.getOrCreate("chatRoom", ["general"]).connect() + +let rawTask = Task { + for await args in await conn.events("message") { + print(args) + } +} + +try await Task.sleep(for: .seconds(5)) +rawTask.cancel() +await conn.dispose() +await client.dispose() +``` + +## Connection Lifecycle + +```swift +import RivetKitClient + +let config = try ClientConfig(endpoint: "http://localhost:6420") +let client = RivetKitClient(config: config) +let conn = client.getOrCreate("chatRoom", ["general"]).connect() + +// Monitor status changes (immediately yields current status) +let statusTask = Task { + for await status in await conn.statusChanges() { + print("status: \(status.rawValue)") + } +} + +// Monitor errors +let errorTask = Task { + for await error in await conn.errors() { + print("error: \(error.group).\(error.code)") + } +} + +// Monitor open/close events +let openTask = Task { + for await _ in await conn.opens() { + print("connected") + } +} + +let closeTask = Task { + for await _ in await conn.closes() { + print("disconnected") + } +} + +// Check current status +let current = await conn.currentStatus +print("Current status: \(current.rawValue)") + +// Let it run for a bit +try await Task.sleep(for: .seconds(5)) + +// Cleanup +statusTask.cancel() +errorTask.cancel() +openTask.cancel() +closeTask.cancel() +await conn.dispose() +await client.dispose() +``` + +## Low-Level HTTP & WebSocket + +For actors that implement `onRequest` or `onWebSocket`, you can call them directly: + +```swift +import RivetKitClient + +let config = try ClientConfig(endpoint: "http://localhost:6420") +let client = RivetKitClient(config: config) +let handle = client.getOrCreate("chatRoom", ["general"]) + +// Raw HTTP request +let response = try await handle.fetch("history") +let history: [String] = try response.json([String].self) +print("History: \(history)") + +// Raw WebSocket connection +let websocket = try await handle.websocket(path: "stream") +try await websocket.send(text: "hello") +let message = try await websocket.receive() +print("Received: \(message)") + +await client.dispose() +``` + +## Calling from Backend + +Use the same client in server-side Swift (Vapor, Hummingbird, etc.): + +```swift +import RivetKitClient + +let config = try ClientConfig(endpoint: "http://localhost:6420") +let client = RivetKitClient(config: config) + +let handle = client.getOrCreate("counter", ["server-counter"]) +let count: Int = try await handle.action("increment", 1, as: Int.self) +print("Count: \(count)") + +await client.dispose() +``` + +## Error Handling + +```swift +import RivetKitClient + +let config = try ClientConfig(endpoint: "http://localhost:6420") +let client = RivetKitClient(config: config) + +do { + _ = try await client.getOrCreate("user", ["user-123"]) + .action("updateUsername", "ab", as: String.self) +} catch let error as ActorError { + print("Error code: \(error.code)") + print("Metadata: \(String(describing: error.metadata))") +} + +await client.dispose() +``` + +If you need an untyped response, you can decode to `JSONValue`: + +```swift +import RivetKitClient + +let config = try ClientConfig(endpoint: "http://localhost:6420") +let client = RivetKitClient(config: config) +let handle = client.getOrCreate("data", ["raw"]) + +let value: JSONValue = try await handle.action("getRawPayload") +print("Raw value: \(value)") + +await client.dispose() +``` + +## Concepts + +### Keys + +Keys uniquely identify actor instances. Use compound keys (arrays) for hierarchical addressing: + +```swift +import RivetKitClient + +let config = try ClientConfig(endpoint: "http://localhost:6420") +let client = RivetKitClient(config: config) + +// Use compound keys for hierarchical addressing +let room = client.getOrCreate("chatRoom", ["org-acme", "general"]) +let actorId = try await room.resolve() +print("Actor ID: \(actorId)") + +await client.dispose() +``` + +Don't build keys with string interpolation like `"org:\(userId)"` when `userId` contains user data. Use arrays instead to prevent key injection attacks. + +### Environment Variables + +`ClientConfig` reads optional values from environment variables: + +- `RIVET_NAMESPACE` - Namespace (can also be in endpoint URL) +- `RIVET_TOKEN` - Authentication token (can also be in endpoint URL) +- `RIVET_RUNNER` - Runner name (defaults to `"default"`) + +The `endpoint` parameter is always required. There is no default endpoint. + +### Endpoint Format + +Endpoints support URL auth syntax: + +``` +https://namespace:token@api.rivet.dev +``` + +You can also pass the endpoint without auth and provide `RIVET_NAMESPACE` and `RIVET_TOKEN` separately. For serverless deployments, set the endpoint to your app's `/api/rivet` URL. See [Endpoints](/docs/general/endpoints#url-auth-syntax) for details. + +## API Reference + +### Client +- `RivetKitClient(config:)` - Create a client with a config +- `ClientConfig` - Configure endpoint, namespace, and token +- `client.get()` / `getOrCreate()` / `getForId()` / `create()` - Get actor handles +- `client.dispose()` - Dispose the client and all connections + +### ActorHandle +- `handle.action(name, args..., as:)` - Stateless action call +- `handle.connect()` - Create a stateful connection +- `handle.resolve()` - Get the actor ID +- `handle.getGatewayUrl()` - Get the raw gateway URL +- `handle.fetch(path, request:)` - Raw HTTP request +- `handle.websocket(path:)` - Raw WebSocket connection + +### ActorConnection +- `conn.action(name, args..., as:)` - Action call over WebSocket +- `conn.events(name, as:)` - AsyncStream of typed events +- `conn.statusChanges()` - AsyncStream of status changes +- `conn.errors()` - AsyncStream of connection errors +- `conn.opens()` - AsyncStream that yields on connection open +- `conn.closes()` - AsyncStream that yields on connection close +- `conn.currentStatus` - Current connection status +- `conn.dispose()` - Close the connection + +### Types +- `ActorConnStatus` - Connection status enum (`.idle`, `.connecting`, `.connected`, `.disconnected`, `.disposed`) +- `ActorError` - Typed actor errors with `group`, `code`, `message`, `metadata` +- `JSONValue` - Raw JSON value for untyped responses + +_Source doc path: /docs/clients/swift_ diff --git a/.agents/skills/multiplayer-game/reference/clients/swiftui.md b/.agents/skills/multiplayer-game/reference/clients/swiftui.md new file mode 100644 index 0000000..4ae65d8 --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/clients/swiftui.md @@ -0,0 +1,359 @@ +# SwiftUI + +> Source: `src/content/docs/clients/swiftui.mdx` +> Canonical URL: https://rivet.dev/docs/clients/swiftui +> Description: Build SwiftUI apps with Rivet Actors. + +--- +## Install + +Add the Swift package dependency and import `RivetKitSwiftUI`: + +```swift +// Package.swift +dependencies: [ + .package(url: "https://github.com/rivet-dev/rivetkit-swift", from: "2.0.0") +] + +targets: [ + .target( + name: "MyApp", + dependencies: [ + .product(name: "RivetKitSwiftUI", package: "rivetkit-swift") + ] + ) +] +``` + +`RivetKitSwiftUI` re-exports `RivetKitClient` and `SwiftUI`, so a single import covers both. + +## Minimal Client + +```swift HelloWorldApp.swift +import RivetKitSwiftUI +import SwiftUI + +@main +struct HelloWorldApp: App { + var body: some Scene { + WindowGroup { + ContentView() + .rivetKit(endpoint: "https://my-namespace:pk_...@api.rivet.dev") + } + } +} +``` + +```swift ContentView.swift +import RivetKitSwiftUI +import SwiftUI + +struct ContentView: View { + @Actor("counter", key: ["my-counter"]) private var counter + @State private var count = 0 + + var body: some View { + VStack(spacing: 16) { + Text("\(count)") + .font(.system(size: 64, weight: .bold, design: .rounded)) + + Button("Increment") { + counter.send("increment", 1) + } + .disabled(!counter.isConnected) + } + .task { + count = (try? await counter.action("getCount")) ?? 0 + } + .onActorEvent(counter, "newCount") { (newCount: Int) in + count = newCount + } + } +} +``` + +## Actor Options + +The `@Actor` property wrapper always uses get-or-create semantics and accepts: + +- `name` (required) +- `key` as `String` or `[String]` (required) +- `params` (optional connection parameters) +- `createWithInput` (optional creation input) +- `createInRegion` (optional creation hint) +- `enabled` (toggle connection lifecycle) + +```swift +import RivetKitSwiftUI +import SwiftUI + +struct ConnParams: Encodable { + let authToken: String +} + +struct ChatView: View { + @Actor( + "chatRoom", + key: ["general"], + params: ConnParams(authToken: "jwt-token"), + enabled: true + ) private var chat + + var body: some View { + Text("Chat: \(chat.connStatus.rawValue)") + } +} +``` + +## Actions + +```swift +import RivetKitSwiftUI +import SwiftUI + +struct CounterView: View { + @Actor("counter", key: ["my-counter"]) private var counter + @State private var count = 0 + @State private var name = "" + + var body: some View { + VStack { + Text("Count: \(count)") + Text("Name: \(name)") + + Button("Fetch") { + Task { + count = try await counter.action("getCount") + name = try await counter.action("rename", "new-name") + } + } + + Button("Increment") { + counter.send("increment", 1) + } + } + } +} +``` + +## Subscribing to Events + +```swift +import RivetKitSwiftUI +import SwiftUI + +struct GameView: View { + @Actor("game", key: ["game-1"]) private var game + @State private var count = 0 + @State private var isGameOver = false + + var body: some View { + VStack { + Text("Count: \(count)") + if isGameOver { + Text("Game Over!") + } + } + .onActorEvent(game, "newCount") { (newCount: Int) in + count = newCount + } + .onActorEvent(game, "gameOver") { + isGameOver = true + } + } +} +``` + +## Async Event Streams + +```swift +import RivetKitSwiftUI +import SwiftUI + +struct ChatView: View { + @Actor("chatRoom", key: ["general"]) private var chat + @State private var messages: [String] = [] + + var body: some View { + List(messages, id: \.self) { message in + Text(message) + } + .task { + for await message in chat.events("message", as: String.self) { + messages.append(message) + } + } + } +} +``` + +## Connection Status + +```swift +import RivetKitSwiftUI +import SwiftUI + +struct StatusView: View { + @Actor("counter", key: ["my-counter"]) private var counter + @State private var count = 0 + + var body: some View { + VStack { + Text("Status: \(counter.connStatus.rawValue)") + + if counter.connStatus == .connected { + Text("Connected!") + .foregroundStyle(.green) + } + + Button("Fetch via Handle") { + Task { + if let handle = counter.handle { + count = try await handle.action("getCount", as: Int.self) + } + } + } + .disabled(!counter.isConnected) + } + } +} +``` + +## Error Handling + +```swift +import RivetKitSwiftUI +import SwiftUI + +struct UserView: View { + @Actor("user", key: ["user-123"]) private var user + @State private var errorMessage: String? + @State private var username = "" + + var body: some View { + VStack { + TextField("Username", text: $username) + + Button("Update Username") { + Task { + do { + let _: String = try await user.action("updateUsername", username) + } catch let error as ActorError { + errorMessage = "\(error.code): \(String(describing: error.metadata))" + } + } + } + + if let errorMessage { + Text(errorMessage) + .foregroundStyle(.red) + } + } + .onActorError(user) { error in + errorMessage = "\(error.group).\(error.code): \(error.message)" + } + } +} +``` + +## Concepts + +### Keys + +Keys uniquely identify actor instances. Use compound keys (arrays) for hierarchical addressing: + +```swift +import RivetKitSwiftUI +import SwiftUI + +struct OrgChatView: View { + @Actor("chatRoom", key: ["org-acme", "general"]) private var room + + var body: some View { + Text("Room: \(room.connStatus.rawValue)") + } +} +``` + +Don't build keys with string interpolation like `"org:\(userId)"` when `userId` contains user data. Use arrays instead to prevent key injection attacks. + +### Environment Configuration + +Call `.rivetKit(endpoint:)` or `.rivetKit(client:)` once at the root of your view tree: + +```swift +// With endpoint string (recommended for most apps) +@main +struct MyApp: App { + var body: some Scene { + WindowGroup { + ContentView() + .rivetKit(endpoint: "https://my-namespace:pk_...@api.rivet.dev") + } + } +} + +// With custom client (for advanced configuration) +@main +struct MyApp: App { + private let client = RivetKitClient( + config: try! ClientConfig(endpoint: "https://api.rivet.dev", token: "pk_...") + ) + + var body: some Scene { + WindowGroup { + ContentView() + .rivetKit(client: client) + } + } +} +``` + +When using `.rivetKit(endpoint:)`, the client is created once and cached per endpoint. When using `.rivetKit(client:)`, store the client as a property on `App` (not inside `body`) since SwiftUI can call `body` multiple times. + +### Environment Variables + +`ClientConfig` reads optional values from environment variables: + +- `RIVET_NAMESPACE` - Namespace (can also be in endpoint URL) +- `RIVET_TOKEN` - Authentication token (can also be in endpoint URL) +- `RIVET_RUNNER` - Runner name (defaults to `"default"`) + +The endpoint is always required. There is no default endpoint. + +### Endpoint Format + +Endpoints support URL auth syntax: + +``` +https://namespace:token@api.rivet.dev +``` + +You can also pass the endpoint without auth and provide `RIVET_NAMESPACE` and `RIVET_TOKEN` separately. For serverless deployments, set the endpoint to your app's `/api/rivet` URL. See [Endpoints](/docs/general/endpoints#url-auth-syntax) for details. + +## API Reference + +### Property Wrapper +- `@Actor(name, key:, params:, createWithInput:, createInRegion:, enabled:)` - SwiftUI property wrapper for actor connections + +### View Modifiers +- `.rivetKit(endpoint:)` - Configure client with an endpoint URL (creates cached client) +- `.rivetKit(client:)` - Configure client with a custom instance +- `.onActorEvent(actor, event) { ... }` - Subscribe to actor events (supports 0–5 typed args) +- `.onActorError(actor) { error in ... }` - Handle actor errors + +### ActorObservable +- `actor.action(name, args..., as:)` - Async action call +- `actor.send(name, args...)` - Fire-and-forget action +- `actor.events(name, as:)` - AsyncStream of typed events +- `actor.connStatus` - Current connection status +- `actor.isConnected` - Whether connected +- `actor.handle` - Underlying `ActorHandle` (optional) +- `actor.connection` - Underlying `ActorConnection` (optional) +- `actor.error` - Most recent error (optional) + +### Types +- `ActorConnStatus` - Connection status enum (`.idle`, `.connecting`, `.connected`, `.disconnected`, `.disposed`) +- `ActorError` - Typed actor errors with `group`, `code`, `message`, `metadata` + +_Source doc path: /docs/clients/swiftui_ diff --git a/.agents/skills/multiplayer-game/reference/cookbook/ai-agent.md b/.agents/skills/multiplayer-game/reference/cookbook/ai-agent.md new file mode 100644 index 0000000..1adb285 --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/cookbook/ai-agent.md @@ -0,0 +1,127 @@ +# AI Agent + +> Source: `src/content/cookbook/ai-agent.mdx` +> Canonical URL: https://rivet.dev/cookbook/ai-agent +> Description: Build an AI agent backend with persistent memory: one Rivet Actor per conversation, queued message handling, and streaming LLM responses as realtime events. + +--- +Patterns for building AI agent backends with RivetKit, where each conversation is one Rivet Actor that owns its memory, its message queue, and its streaming output. + +## Starter Code + +Start with one of the working examples on [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/ai-agent) and adapt it. The sections below describe the flagship `ai-agent` example unless a variant is called out explicitly. + +| Variant | Starter Code | Use When | +| --- | --- | --- | +| Queue-driven AI SDK agent | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/ai-agent) | You want a streaming chat agent where each conversation keeps its own persistent memory and processes one message at a time. | +| Sandbox coding agent | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/sandbox-coding-agent) | The agent should run a coding agent (Codex by default) inside an isolated [sandbox](/docs/actors/sandbox) via Docker, Daytona, or E2B. | +| Durable streams agent (experimental) | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/experimental-durable-streams-ai-agent) | You want replayable, restart-safe prompt and response delivery through durable streams instead of actor state and events. | + +## Conversation Memory + +Use one actor per conversation, keyed by a conversation or agent id (see [Actor Keys](/docs/actors/keys)). The agent actor's persistent [state](/docs/actors/state) is the conversation memory: in the `ai-agent` example, `messages` and `status` live in JSON actor state and survive sleep and restarts with no external database. Every model call rebuilds the prompt from `c.state.messages` plus a system prompt, so memory and inference input are the same data. + +| Variant | Where Memory Lives | Persisted State Fields | +| --- | --- | --- | +| `ai-agent` | JSON actor state | `messages`, `status` | +| `sandbox-coding-agent` | JSON actor state plus the sandbox ACP session | `messages`, `status`, `sessionId` | +| `experimental-durable-streams-ai-agent` | Durable streams; the actor stores only its conversation id and a read cursor | `conversationId`, `promptStreamOffset` | + +## Message Handling + +In the `ai-agent` example, the client pushes user input onto the agent's `message` [queue](/docs/actors/queues) with `agent.connection.send("message", { text, sender })`. This is a queue push, not an action call. The actor's `run` hook (see [Lifecycle](/docs/actors/lifecycle)) consumes the queue serially with `for await (const queued of c.queue.iter())`. + +Serial queue consumption is the per-conversation concurrency guarantee: at most one in-flight model call per actor, with no extra locking. The `status` field (`thinking` while a model call is in flight) is UI signal only; the run loop is the actual lock. The loop also checks `c.aborted` inside the token stream so shutdown exits gracefully. + +| Variant | Message Ingress | Serialization Guarantee | +| --- | --- | --- | +| `ai-agent` | `message` queue pushed via `connection.send` | `run` hook pops one queued message at a time with `c.queue.iter()`. | +| `sandbox-coding-agent` | `sendMessage` [action](/docs/actors/actions), no queue | Each call awaits the sandbox round trip before broadcasting the result. | +| `experimental-durable-streams-ai-agent` | Durable prompt stream long-polled from `onWake` | `promptStreamOffset` is persisted per chunk, so restarts resume without reprocessing prompts. | + +## Streaming Responses + +The `ai-agent` actor broadcasts a `response` [event](/docs/actors/events) for every model text delta. The payload carries `messageId`, the per-token `delta`, the cumulative `content`, and a `done` flag (plus `error` on failure), so clients can either append deltas or idempotently replace the message by `messageId` using `content`. The example frontend replaces by `messageId`, which tolerates dropped events. The terminal broadcast has an empty `delta`, the full `content`, and `done: true`. + +Because the assistant message object lives in `c.state.messages` and is mutated in place during streaming, partial content persists if the actor restarts mid-stream. The example broadcasts once per AI SDK delta with no throttling; batching or throttling deltas is a recommended extension for high-traffic deployments, not something the example implements. + +Variant differences: `sandbox-coding-agent` sends a single `response` broadcast with `done: true` after the sandbox finishes (no incremental streaming), and `experimental-durable-streams-ai-agent` appends per-token chunks to a durable response stream, then broadcasts `responseComplete` or `responseError`. + +## Architecture + +| Topic | Summary | +| --- | --- | +| Topology | `agentManager["primary"]` singleton directory plus one `agent[agentId]` actor per conversation. | +| Ingress | Client pushes `AgentQueueMessage` payloads onto the agent's `message` queue with `connection.send`. | +| Streaming | One `response` broadcast per model delta, terminal broadcast with `done: true`. | +| Memory | Full transcript and status in JSON actor state; no external database. | + +The manager creates `AgentInfo` records and warms each agent through [actor-to-actor communication](/docs/actors/communicating-between-actors): `createAgent` calls `c.client()`, then `client.agent.getOrCreate([info.id])` and awaits `getStatus()` so the conversation actor exists before the client connects. The sandbox variant extends this topology with a `codingSandbox` actor that shares the agent's key (`codingSandbox.getOrCreate([c.key[0]])`), so the agent-to-sandbox mapping is implicit in the key space. + +**Actors** + +- **Key**: `agentManager["primary"]` +- **Responsibility**: Directory actor. Creates `AgentInfo` records, lists agents, and warms each agent actor via `c.client()`. +- **Actions** + - `createAgent` + - `listAgents` +- **Queues** + - None +- **State** + - JSON + - `agents` + +- **Key**: `agent[agentId]` +- **Responsibility**: One actor per conversation. Holds the full message history and status, consumes queued user messages in its `run` loop, calls the model via the AI SDK, and broadcasts streaming deltas. +- **Actions** + - `getHistory` + - `getStatus` +- **Queues** + - `message` +- **Events** + - `messageAdded` + - `status` + - `response` +- **State** + - JSON + - `messages` + - `status` + +**Lifecycle** + +```mermaid +sequenceDiagram + participant C as Client + participant AM as agentManager + participant A as agent + participant LLM as Model API + + C->>AM: createAgent(name) + AM->>A: getOrCreate([info.id]) + getStatus() + AM-->>C: AgentInfo + C->>A: connection.send("message", {text, sender}) + Note over A: run loop pops queue via c.queue.iter() + A-->>C: messageAdded (user message) + A-->>C: messageAdded (assistant placeholder) + A-->>C: status (thinking) + A->>LLM: streamText(system prompt + history) + loop each text delta + LLM-->>A: delta + A-->>C: response {messageId, delta, content, done: false} + end + A-->>C: response {delta: "", content, done: true} + A-->>C: status (idle) +``` + +## Security Checklist + +The examples ship without auth so they stay minimal. Apply this baseline before exposing an agent backend. + +- **API keys stay server-side**: `OPENAI_API_KEY` (or `ANTHROPIC_API_KEY`) is read by the AI SDK inside the actor process. The key never reaches the browser; clients only talk to the actor over RivetKit. The sandbox variant forwards keys into the sandbox env, never to the client. +- **Add authentication**: The examples have no auth, so anyone who reaches the server can create agents, list them, and message any agent whose key they can guess. Add `onBeforeConnect` or `createConnState` checks with scoped tokens as a recommended extension. See [Authentication](/docs/actors/authentication). +- **Validate and rate-limit queue payloads**: The example only skips bodies without a string `text`. Enforce payload size limits, schema validation, and per-connection rate limits as a recommended extension. +- **Derive sender identity server-side**: The example trusts the client-supplied `sender` field verbatim. Bind sender identity to the authenticated connection instead. +- **Cap or trim message history**: The example sends the full transcript on every model call with no cap. Trim or summarize old messages as a recommended extension so prompts and state stay bounded. +- **Set cost ceilings per conversation**: Add per-agent token budgets and quotas as a recommended extension. The sandbox variant runs real compute, so also enforce per-user sandbox quotas and restrict sandbox network egress. + +_Source doc path: /cookbook/ai-agent_ diff --git a/.agents/skills/multiplayer-game/reference/cookbook/chat-room.md b/.agents/skills/multiplayer-game/reference/cookbook/chat-room.md new file mode 100644 index 0000000..cb8dbe4 --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/cookbook/chat-room.md @@ -0,0 +1,114 @@ +# Chat Room + +> Source: `src/content/cookbook/chat-room.mdx` +> Canonical URL: https://rivet.dev/cookbook/chat-room +> Description: Build a realtime chat room backend with Rivet Actors: one actor per room, SQLite-backed message history, and WebSocket broadcast to every connected client. + +--- +Patterns for building a chat room backend with RivetKit: room-scoped actors, persistent message history, and realtime delivery over WebSocket connections. + +## Starter Code + +Start with the working example on [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/chat-room) and adapt it. The backend is a single `chatRoom` actor; the frontend is a React app using `@rivetkit/react` (see the [React quickstart](/docs/actors/quickstart/react)). + +| Topic | Summary | +| --- | --- | +| Room model | One `chatRoom` actor per room key. The frontend defaults the key to `general`; typing a different room name connects to a different actor. | +| History | SQLite `messages` table created in `db({ onMigrate })`, read back with `ORDER BY id ASC`. | +| Delivery | `sendMessage` inserts the row, then broadcasts a typed `newMessage` event to every connected client. | +| Identity | None in the example. `sender` is a plain action argument; production should bind identity to the connection. | + +## Room-Per-Actor Model + +Each room is one Rivet Actor instance, addressed by [key](/docs/actors/keys). The client calls `useActor({ name: "chatRoom", key: [roomId] })`, which gets-or-creates the actor for that room. This gives you: + +- **Isolation**: each room's history and connections are fully scoped to its key. Switching the room input re-keys the hook and connects to a different actor with separate history. +- **A single serialized writer**: all `sendMessage` calls for one room run through one actor, so message ordering is consistent without locks. The SQLite `AUTOINCREMENT` id is the canonical order, which is why `getHistory` sorts by `id` rather than by timestamp. +- **Natural scaling**: rooms spread across the cluster independently. A hot room does not slow down other rooms. + +## Message History Storage + +This example stores history in the actor's SQLite database, not in JSON state. Pick based on history size and query needs: + +| Approach | Use When | Implementation Guidance | +| --- | --- | --- | +| [SQLite](/docs/actors/sqlite) (what this example uses) | Large or long-lived history that needs ordering, caps, pagination, or search | Create the `messages` table in `db({ onMigrate })`, insert with parameterized queries (`c.db.execute("INSERT ... VALUES (?, ?, ?)", ...)`), and read with `ORDER BY id ASC`. History survives actor sleep and scales past what you want in memory. | +| [JSON state](/docs/actors/state) | Small recent history, for example the last 50 to 100 messages | Push onto a `messages` array in actor state and trim to a cap on every send. Simplest option, but the whole history lives in memory and there is no query layer, so it only fits bounded recent-history use cases. | + +## Broadcast Delivery + +New messages reach connected clients through a typed [event](/docs/actors/events): + +- The actor declares `events: { newMessage: event() }`, where `Message` is `{ sender, text, timestamp }`. +- The `sendMessage` [action](/docs/actors/actions) builds the message with a server-side `Date.now()` timestamp, inserts it into the `messages` table, then calls `c.broadcast("newMessage", message)` and returns the message to the caller. +- Each client subscribes with `useEvent("newMessage", ...)` and appends to its local list. The sender renders its own message through the same broadcast path as everyone else, so all clients stay on one code path. +- History load is connection-gated: once the connection is ready, the client calls `getHistory()` once to render the backlog, then relies on events for everything after. + +Use `c.broadcast(...)` for room-wide messages. For private or per-recipient payloads (such as DMs inside a room), send on the individual connection instead, which is a recommended extension beyond this example. + +## Typing Indicators And Presence (Extension) + +The example does not implement typing indicators, presence, or join/leave handling of any kind. There is no `createConnState`, `onConnect`, or `onDisconnect` in the code. If you need them, add them as ephemeral [connection](/docs/actors/connections) behavior: + +- **Keep it ephemeral**: store the username and typing flag in per-connection state, never in SQLite or persisted actor state. Presence is derived from live connections and should disappear with them. +- **Broadcast on change only**: emit a typing event when a user starts or stops typing, and a presence event from `onConnect` / `onDisconnect`, rather than polling or ticking. +- **Expire on the client**: clear a typing indicator after a short client-side timeout so a dropped connection never leaves a stuck "is typing" row. + +## Per-User Inbox (Extension) + +For offline delivery, DMs, unread counts, or notification fanout, add a `userInbox[userId]` actor per user. This is an extension beyond the example: + +- The room actor forwards each message to the inbox actor of every member via [actor-to-actor calls](/docs/actors/communicating-between-actors), so users who are not connected to the room still accumulate messages. +- The inbox actor owns per-user unread state and serves it when the user comes online, independent of which rooms they are in. +- DMs become a degenerate room: either a `chatRoom` keyed by the sorted pair of user ids, or direct inbox-to-inbox delivery if you do not need shared history semantics. + +## Actors + +- **Key**: `chatRoom[roomId]` +- **Responsibility**: Owns one chat room. Persists the room's message history in its SQLite database and broadcasts each new message to every connected client. +- **Actions** + - `sendMessage` + - `getHistory` +- **Queues** + - None +- **Events** + - `newMessage` +- **State** + - SQLite + - `messages` table: `id` (autoincrement primary key), `sender`, `text`, `timestamp` + +## Lifecycle + +```mermaid +sequenceDiagram + participant A as Client A + participant B as Client B + participant R as chatRoom + + A->>R: connect with key [roomId] + Note over R: every start runs onMigrate (CREATE TABLE IF NOT EXISTS messages) + A->>R: getHistory() + R-->>A: Message[] ordered by id + B->>R: connect with key [roomId] + B->>R: getHistory() + R-->>B: Message[] ordered by id + A->>R: sendMessage(sender, text) + Note over R: INSERT row with server timestamp + R-->>A: newMessage (broadcast) + R-->>B: newMessage (broadcast) + A->>R: disconnect + Note over R: history stays in SQLite for the next connection +``` + +## Security Checklist + +The example is intentionally minimal and skips all of the following. Add them before production: + +- **Auth before join**: any client can join any room by knowing its name, and `sender` is arbitrary client input on every call. Validate a token during [connection auth](/docs/actors/authentication), bind identity to [connection state](/docs/actors/connections), and check room membership before serving history. Never trust a sender name passed as an action argument. +- **Message length clamps**: the example accepts empty messages and has no length limit. Trim server-side, reject empty text, and clamp to a maximum length. +- **Per-connection rate limiting**: rate limit `sendMessage` per connection to stop spam and broadcast amplification. +- **Server-side timestamps and ids**: the example already does this correctly. `timestamp` comes from `Date.now()` inside the action and `id` from SQLite `AUTOINCREMENT`. Keep it that way; never accept client-supplied timestamps or ids. +- **History caps**: `getHistory` returns every row with no limit. Add a `LIMIT` plus pagination, and prune or archive old rows so a long-lived room cannot grow unbounded. +- **Parameterized queries**: the example already inserts with `?` placeholders. Keep all user-supplied text out of SQL string interpolation. + +_Source doc path: /cookbook/chat-room_ diff --git a/.agents/skills/multiplayer-game/reference/cookbook/collaborative-text-editor.md b/.agents/skills/multiplayer-game/reference/cookbook/collaborative-text-editor.md new file mode 100644 index 0000000..cacbc1f --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/cookbook/collaborative-text-editor.md @@ -0,0 +1,157 @@ +# Collaborative Text Editor + +> Source: `src/content/cookbook/collaborative-text-editor.mdx` +> Canonical URL: https://rivet.dev/cookbook/collaborative-text-editor +> Description: Build a collaborative text editor backend with Yjs CRDTs and Rivet Actors: per-document actors relay sync and awareness updates and persist snapshots. + +--- +Patterns for building a Yjs server on RivetKit: CRDT document sync, presence and cursors, and snapshot persistence, with one Rivet Actor per document acting as a relay. + +## Starter Code + +Start with the working example on [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/collaborative-document) and adapt it to your editor. It ships a React frontend with a plain textarea, remote cursor overlays, and a workspace document index. + +| Use Case | Starter Code | Common Examples | +| --- | --- | --- | +| Shared document editing | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/collaborative-document) | Notion-style docs, shared notes, pair-writing tools, form co-editing | + +## CRDT vs OT + +Two families of algorithms solve concurrent text editing. The choice decides what your server has to do. + +| Dimension | CRDT (Yjs) | Operational Transformation | +| --- | --- | --- | +| Conflict resolution model | Commutative merges. Updates apply in any order on any peer and converge to the same result. | Server transforms each operation against every concurrent operation. Correctness depends on a central sequencer. | +| Offline support | Strong. Clients keep editing locally and merge buffered updates on reconnect. | Weak. Long-lived divergence makes transformation chains complex and fragile. | +| Server role | Relay plus persistence. The server applies opaque updates and rebroadcasts them. It never needs to understand document semantics. | Authoritative transformer. The server must implement transformation logic for every operation type. | +| Library maturity | Yjs is mature and widely deployed, with bindings for ProseMirror, CodeMirror, Monaco, and others. | Production-grade implementations are mostly proprietary (Google Docs) or aging (ShareDB). | + +The example uses Yjs because CRDTs let the server stay a relay-style Rivet Actor. The actor applies each incoming update to a server-side `Y.Doc` so it can persist the merged state and serve late joiners, but it never transforms operations or arbitrates conflicts. Ordering does not matter because Yjs merges are commutative. + +## Document Actor Model + +| Topic | Summary | +| --- | --- | +| Topology | One `document[workspaceId, documentId]` actor per document plus one `documentList[workspaceId]` coordinator per workspace. | +| Sync model | Each client holds a local `Y.Doc`. The document actor relays incremental Yjs updates as broadcast [events](/docs/actors/events) and keeps a server-side merged copy in vars. | +| Persistence | Full merged Yjs snapshot overwritten in one binary [actor KV](/docs/actors/kv) key (`yjs:doc`) on every sync update. Document metadata lives in JSON [state](/docs/actors/state). | +| Queues | None. The example is purely [actions](/docs/actors/actions) plus broadcast events. | +| Presence | Yjs Awareness relayed through the same `applyUpdate` action. Per-connection `connState` tracks asserted awareness clientIds for disconnect cleanup. | + +The two-actor split follows the coordinator pattern from [Design Patterns](/docs/actors/design-patterns): the coordinator owns discovery and creation, and each document actor owns one document's realtime state. Multi-part [keys](/docs/actors/keys) scope both actors to a workspace. + +**Actors** + +- **Key**: `document[workspaceId, documentId]` +- **Responsibility**: Applies incoming sync and awareness updates to a server-side `Y.Doc` and `Awareness`, persists the merged Yjs snapshot to actor KV, and broadcasts updates to all connected collaborators. +- **Actions** + - `getContent` + - `applyUpdate` + - `getAwareness` +- **Queues** + - None +- **State** + - JSON metadata only: `title`, `createdAt`, `updatedAt` + - Binary KV key `yjs:doc` holding the full merged Yjs snapshot + - Ephemeral vars: the live `Y.Doc` and `Awareness`, created in `createVars` and rehydrated from KV on actor start + - Per-connection `connState`: `clientIds` of awareness clients asserted by that connection + +- **Key**: `documentList[workspaceId]` +- **Responsibility**: Coordinator for one workspace. Creates document actors through the actor-to-actor client and maintains the index of document summaries. +- **Actions** + - `createDocument` + - `listDocuments` + - `deleteDocument` +- **Queues** + - None +- **State** + - JSON + - `documents` array of `DocumentSummary` entries (`id`, `title`, `createdAt`, `updatedAt`) + +The coordinator's `createDocument` generates a UUID, then explicitly creates the document actor with `c.client()` and passes `{ title, createdAt }` as creation [input](/docs/actors/input), which the document actor's `createState` consumes. See [Communicating Between Actors](/docs/actors/communicating-between-actors) for the actor-to-actor client. + +## Update Relay + +A single `applyUpdate(update, kind, clientId?)` action handles both update kinds. Updates cross the action boundary as `number[]` byte arrays and are converted back to `Uint8Array` on each side. + +| Kind | Server Applies To | Persists | Broadcasts | +| --- | --- | --- | --- | +| `"sync"` | `c.vars.doc` via `Y.applyUpdate` with origin `"client"` | Full merged snapshot to KV key `yjs:doc`, then bumps `updatedAt` | `sync` event carrying the incremental update | +| `"awareness"` | `c.vars.awareness` via `applyAwarenessUpdate` with origin `"client"` | Nothing. Presence is ephemeral. | `awareness` event carrying the update | + +Note the asymmetry on the sync branch: the broadcast carries only the small incremental update, while the KV write stores the full merged document re-encoded with `Y.encodeStateAsUpdate`. + +Yjs origin tags are the echo guards that keep the relay loop-free: + +| Origin Tag | Set Where | Effect | +| --- | --- | --- | +| `"local"` | Client edits inside `doc.transact(..., "local")` | The client's update listener fires and sends `applyUpdate` to the actor. | +| `"client"` | Server applying an incoming update to its `Y.Doc` or `Awareness` | Marks the change as client-originated on the server copy. | +| `"remote"` | Client applying broadcast events or initial sync data | Update listeners early-return on `"remote"`, so a client never re-sends its own echo. | + +On connect or reconnect, the client calls `getContent` and `getAwareness`, then applies both results to its local `Y.Doc` and `Awareness` with origin `"remote"`. After that, every change flows through `applyUpdate` and the broadcast events. + +## Awareness And Presence + +Presence (user names, colors, cursor positions) rides on the Yjs Awareness protocol instead of actor state: + +- Clients set presence with `awareness.setLocalStateField` for the `user` and `cursor` fields. The awareness update listener encodes the change and sends `applyUpdate(update, "awareness", awareness.clientID)`. +- The actor records each asserted `clientId` in that connection's `connState.clientIds`, applies the update to the server-side `Awareness`, and broadcasts the `awareness` event to all peers. See [Connections](/docs/actors/connections) for per-connection state. +- `onDisconnect` reads the connection's `clientIds`, calls `removeAwarenessStates` on the server-side `Awareness`, and broadcasts the encoded removal so every remaining client drops the departed user's cursor. See [Lifecycle](/docs/actors/lifecycle) for the hook. + +Because the actor tracks which awareness clientIds belong to which connection, presence cleanup is automatic on disconnect with no client cooperation required. + +## Persistence And Compaction + +The example persists with a full-snapshot overwrite: on every `"sync"` update, the actor re-encodes the entire merged document with `Y.encodeStateAsUpdate` and overwrites the single binary KV key `yjs:doc`. There is no append-only update log and no separate compaction job. Compaction is implicit because `Y.encodeStateAsUpdate` emits one compact merged representation of the document, so Yjs merge semantics keep the stored blob compact on their own. + +| Property | Full-Snapshot Overwrite (the example) | +| --- | --- | +| Write cost | One full-document KV write per sync update, so every keystroke rewrites the whole blob. | +| Read cost | One binary KV read in `createVars` rehydrates the document on actor start. | +| Crash safety | The last completed `applyUpdate` is durable. No log replay needed. | +| Sweet spot | Small to medium documents where simplicity beats write amplification. | + +**Recommended extension (not in the example)**: for large documents or very high edit rates, switch to appending incremental updates to a KV update log and writing a merged snapshot only periodically (for example every N updates). Boot becomes snapshot plus log replay, and the snapshot write becomes the explicit compaction step that truncates the log. Adopt this only when full-snapshot writes become the measured bottleneck or the blob approaches KV value size limits. + +## Lifecycle + +```mermaid +sequenceDiagram + participant A as Client A + participant B as Client B + participant DL as documentList + participant D as document + + A->>DL: listDocuments() + A->>DL: createDocument(title) + DL->>D: create([workspaceId, documentId], input) + DL-->>A: DocumentSummary + A->>D: connect + B->>D: connect + Note over D: createVars rehydrates Y.Doc from KV "yjs:doc" + A->>D: getContent() + getAwareness() + D-->>A: encoded doc + awareness state + Note over A: local edit with origin "local" + A->>D: applyUpdate(update, "sync") + Note over D: apply with origin "client", overwrite KV snapshot + D-->>B: sync event (incremental update) + Note over B: apply with origin "remote", no echo + A->>D: applyUpdate(update, "awareness", clientId) + D-->>B: awareness event + B-->>D: disconnect + Note over D: onDisconnect removes B's awareness clientIds + D-->>A: awareness event (removal) +``` + +## Security Checklist + +The example ships with no authentication or authorization. Harden it with this baseline before production. None of these are implemented in the example. + +- **Authenticate before connect**: Anyone who knows or guesses a workspace ID can connect, and because `useActor` implicitly getOrCreates, connecting with a nonexistent workspace ID silently creates a blank `documentList` coordinator. Add connection auth so unauthenticated clients never reach an actor. See [Authentication](/docs/actors/authentication). +- **Per-document access control**: Validate that the authenticated user is allowed to access the specific `[workspaceId, documentId]` key, not just any document. +- **Cap and rate limit `applyUpdate`**: Update payloads are unvalidated `number[]` arrays with no size limit, and the example client sends one action per keystroke and per cursor move with zero throttling. Enforce payload size caps and per-connection rate limits on the server, and debounce on the client. +- **Do not trust client-asserted awareness clientIds**: The `clientId` argument to `applyUpdate` is client-supplied and trusted as-is. Derive or verify presence identity from connection-scoped server state instead. +- **Destroy actors and KV on delete**: `deleteDocument` only filters the entry out of the coordinator's index. The document actor and its KV snapshot are orphaned. On delete, also destroy the document actor and its storage, with a permission check on who may delete. + +_Source doc path: /cookbook/collaborative-text-editor_ diff --git a/.agents/skills/multiplayer-game/reference/cookbook/cron-jobs.md b/.agents/skills/multiplayer-game/reference/cookbook/cron-jobs.md new file mode 100644 index 0000000..21c7657 --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/cookbook/cron-jobs.md @@ -0,0 +1,69 @@ +# Cron Jobs and Scheduled Tasks + +> Source: `src/content/cookbook/cron-jobs.mdx` +> Canonical URL: https://rivet.dev/cookbook/cron-jobs +> Description: Patterns for durable one-shot, calendar, and fixed-interval work on Rivet Actors. + +--- +Rivet Actor schedules are durable actor-local timers. They survive actor sleep, restarts, upgrades, deploys, and crashes without a separate cron service. + +## Choose a schedule type + +| API | Use it for | +| --- | --- | +| `c.schedule.after(delayMs, action, ...args)` | One-time work after a relative delay. | +| `c.schedule.at(timestamp, action, ...args)` | One-time work at an exact Unix timestamp in milliseconds. | +| `c.cron.set({ ... })` | Named calendar recurrence in an IANA timezone. | +| `c.cron.every({ ... })` | Named fixed intervals of at least 5 seconds. | + +All callbacks are ordinary actions on the same actor. Keep the action name fixed in your code rather than accepting an arbitrary action name from a client. + +See [Schedule & Cron](/docs/actors/schedule) for the full API, history, cancellation, failure behavior, and limits. + +## Calendar job + +Use `cron.set` instead of manually re-arming a one-shot action: + +Install fixed background jobs in `onCreate` so setup runs once per actor. The job name remains an upsert key, so a later `cron.set` call updates the existing job rather than creating a duplicate. `cron.set` also handles timezone and daylight-saving transitions. + +## Fixed-interval job + +Use `cron.every` for frequent work such as presence sweeps or cache refreshes: + +```ts +await c.cron.every({ + name: "presence-sweep", + interval: 15_000, // Minimum 5 seconds. + action: "sweepPresence", + maxHistory: 25, +}); +``` + +Intervals remain anchored to scheduled deadlines rather than drifting by the action's runtime. If a previous run is still active, the overlapping occurrence is skipped. + +## Cancellation and updates + +Keep the ID returned by a one-shot schedule when it may need cancellation: + +```ts +const id = await c.schedule.after(60_000, "expireSession", sessionId); +await c.schedule.cancel(id); +``` + +Recurring jobs are managed by name: + +```ts +await c.cron.delete("presence-sweep"); +``` + +Calling `cron.set` or `cron.every` again with the same name replaces its configuration. + +## Failure and idempotency + +Keep scheduled actions idempotent when duplicate work would be harmful. See [Execution behavior](/docs/actors/schedule#execution-behavior) for retry behavior and workflow guidance. + +## Topology + +Use a singleton actor key for one global job, such as `jobs["daily-report"]`. Use an actor per user or resource for isolated reminders, trials, billing periods, or other per-entity schedules. + +_Source doc path: /cookbook/cron-jobs_ diff --git a/.agents/skills/multiplayer-game/reference/cookbook/live-cursors.md b/.agents/skills/multiplayer-game/reference/cookbook/live-cursors.md new file mode 100644 index 0000000..71c94ab --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/cookbook/live-cursors.md @@ -0,0 +1,152 @@ +# Live Cursors and Presence + +> Source: `src/content/cookbook/live-cursors.mdx` +> Canonical URL: https://rivet.dev/cookbook/live-cursors +> Description: Live cursors and multiplayer presence with Rivet Actors: per-connection cursor state, realtime updates over events or raw WebSockets, and throttling. + +--- +Patterns for building live cursors, multiplayer presence, and realtime cursor sharing with RivetKit. One room actor fans cursor positions out to every connected client, keyed per room with [actor keys](/docs/actors/keys). + +## Starter Code + +Start with one of the two working variants on GitHub. Both implement the same collaborative cursor canvas with persistent text labels; they differ only in transport. + +| Variant | Starter Code | Transport | Presence Storage | +| --- | --- | --- | --- | +| `cursors` | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/cursors) | Typed [actions](/docs/actors/actions) and [events](/docs/actors/events) over the RivetKit connection | `connState` per connection | +| `cursors-raw-websocket` | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/cursors-raw-websocket) | Raw [`onWebSocket` handler](/docs/actors/websocket-handler) with a custom JSON message protocol | Socket map in `createVars` | + +Use `cursors` by default: typed actions, typed events, and automatic connection tracking cover most apps with less code. Use `cursors-raw-websocket` when you need full control of the wire format, for example a custom JSON or binary protocol, or clients that do not use the RivetKit client library. + +## Connection State vs Persistent State + +Presence is ephemeral by definition. A cursor position is only meaningful while its connection is alive, so it belongs in per-connection storage, not in persistent actor state. Persistent state is reserved for data that must survive disconnects and actor restarts. + +| Data | Where It Lives | Why | +| --- | --- | --- | +| Cursor position | `connState` (`cursors`) or the `createVars` socket map (`cursors-raw-websocket`) | Scoped to one connection and discarded with it. Stale presence cannot accumulate in storage. | +| Text labels (`textLabels`) | Persistent actor `state` in both variants | Canvas content must survive disconnects and actor restarts. | + +In the `cursors` variant, `updateCursor` writes `c.conn.state.cursor` and `getRoomState` rebuilds the presence snapshot by iterating `c.conns.values()`, so the cursor map is always derived from live connections rather than stored. See [Connections](/docs/actors/connections) for `connState` and [State](/docs/actors/state) for persistence semantics. + +## Presence Lifecycle + +- **Join**: The `cursors-raw-websocket` variant pushes an `init` message with the current `{ cursors, textLabels }` snapshot as soon as a socket connects. The `cursors` variant has no explicit join broadcast; the client calls the `getRoomState` action once after connecting to seed its local maps, and peers first see a new user on that user's first `cursorMoved` broadcast. +- **Move**: Every `updateCursor` call writes the connection's presence entry, then broadcasts `cursorMoved` to all connections, including the sender. +- **Leave**: The `cursors` variant handles leave in `onDisconnect`, broadcasting `cursorRemoved` with the connection's last cursor. The raw variant does the same from the socket `close` listener, then deletes the session from the `vars.websockets` map. Clients delete that user from their local cursor map, so stale cursors disappear the moment a tab closes. + +See [Lifecycle](/docs/actors/lifecycle) for `onDisconnect` and `createVars`. + +## Update Throttling + +Neither example throttles. Both frontends send a cursor update on every raw `mousemove` event with no debounce or interval cap. That is fine for a demo, but a fast mouse on a high-refresh display can emit hundreds of events per second per user. The patterns below are recommended production hardening on top of the starter code, not something the examples implement. + +| Layer | Pattern | Guidance | +| --- | --- | --- | +| Client (smoothness) | Throttle to 20-30Hz | Sample the latest pointer position every 33-50ms and send only that. Drop intermediate moves, but always flush the final position so cursors settle at the true location. Interpolate between received positions on the rendering side. | +| Server (enforcement) | Per-connection rate limit | Track the last accepted update timestamp per connection and drop or coalesce updates arriving faster than your cap. Client throttles are cooperative; the actor is the enforcement boundary. | + +## Actors + +- **Key**: `cursorRoom[roomId]` (the frontend defaults `roomId` to `"general"`) +- **Responsibility**: Holds per-connection cursor presence in `connState`, persists shared text labels in actor state, and broadcasts cursor and text updates to all connections. +- **Actions** + - `updateCursor` + - `updateText` + - `removeText` + - `getRoomState` +- **Events** + - `cursorMoved` + - `cursorRemoved` + - `textUpdated` + - `textRemoved` +- **Queues** + - None +- **State** + - JSON + - `textLabels` (persistent) + - `connState.cursor` per connection (ephemeral) + +- **Key**: `cursorRoom[roomId]` (resolved via `client.cursorRoom.getOrCreate(roomId)`) +- **Responsibility**: Exposes a raw WebSocket endpoint, tracks live sockets and their cursors in a `createVars` map keyed by a `sessionId` query parameter, persists text labels, and manually fans JSON frames out to every socket. +- **Actions** + - `getOrCreate` (stub returning `{ status: "ok" }`; the frontend resolves the actor ID with the client handle's `getOrCreate(roomId).resolve()`, which creates the actor without dispatching this action) + - `getRoomState` +- **Queues** + - None +- **State** + - JSON + - `textLabels` (persistent) + - `vars.websockets` map of `sessionId` to socket and cursor (in-memory, lost on restart) + +The raw variant defines no RivetKit events. Its message names are `type` fields on raw JSON frames: + +| Direction | Message `type` | Payload | +| --- | --- | --- | +| Client to server | `updateCursor` | `{ userId, x, y }` | +| Client to server | `updateText` | `{ id, userId, text, x, y }` | +| Client to server | `removeText` | `{ id }` | +| Server to client | `init` | `{ cursors, textLabels }` snapshot on connect | +| Server to client | `cursorMoved`, `textUpdated`, `textRemoved`, `cursorRemoved` | The corresponding cursor, label, or ID payload | + +## Lifecycle + +### cursors (Actions + Events) + +```mermaid +sequenceDiagram + participant A as Client A + participant R as cursorRoom + participant B as Other Clients + + A->>R: connect via useActor (cursorRoom[roomId]) + A->>R: getRoomState() + R-->>A: {cursors, textLabels} + loop every mouse move + A->>R: updateCursor(userId, x, y) + Note over R: write c.conn.state.cursor + R-->>B: cursorMoved (broadcast) + end + A->>R: updateText(id, userId, text, x, y) + Note over R: upsert persistent state.textLabels + R-->>B: textUpdated (broadcast) + Note over A: tab closes + Note over R: onDisconnect reads conn.state.cursor + R-->>B: cursorRemoved (broadcast) +``` + +### cursors-raw-websocket + +```mermaid +sequenceDiagram + participant A as Client A + participant R as cursorRoom + participant B as Other Clients + + A->>R: getOrCreate(roomId).resolve() + R-->>A: actorId + A->>R: open WebSocket /gateway/{actorId}/websocket?sessionId=... + Note over R: close 1008 if sessionId is missing + Note over R: store socket in vars.websockets + R-->>A: init {cursors, textLabels} + loop every mouse move + A->>R: {type: "updateCursor"} frame + Note over R: update session cursor in vars + R-->>B: cursorMoved frame + end + Note over A: socket closes + R-->>B: cursorRemoved frame + Note over R: delete session from vars.websockets +``` + +## Security Checklist + +Both examples ship without authentication so the presence pattern stays readable. Everything below is recommended hardening for production, not behavior the examples implement. + +- **Identity**: Bind presence identity to the connection (`c.conn.id` in the actions variant, a server-generated session ID in the raw variant). Never trust a client-supplied `userId`; in the examples it is a random client-generated string, so any client can impersonate or remove any cursor. +- **Authorization**: Authorize label mutations by owner. In the examples, `updateText` accepts arbitrary `id` and `userId` arguments and `removeText` accepts an arbitrary `id`, so any client can edit or delete any label. +- **Input validation**: Clamp `x` and `y` to canvas bounds, cap text label length, and cap the total `textLabels` count so persistent state cannot grow unbounded. +- **Rate limiting**: Enforce a per-connection cap on `updateCursor` (for example 30Hz) and on label writes, as described in [Update Throttling](#update-throttling). +- **Protocol strictness (raw variant)**: Validate message shape before use and close the socket on malformed JSON instead of logging and continuing. Reject duplicate `sessionId` values rather than silently overwriting another session's socket entry. + +_Source doc path: /cookbook/live-cursors_ diff --git a/.agents/skills/multiplayer-game/reference/cookbook/multiplayer-game.md b/.agents/skills/multiplayer-game/reference/cookbook/multiplayer-game.md new file mode 100644 index 0000000..425b113 --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/cookbook/multiplayer-game.md @@ -0,0 +1,713 @@ +# Multiplayer Game + +> Source: `src/content/cookbook/multiplayer-game.mdx` +> Canonical URL: https://rivet.dev/cookbook/multiplayer-game +> Description: Pragmatic patterns for building multiplayer games: matchmaking, tick loops, realtime state, interest management, and validation. + +--- +Patterns for building multiplayer games with RivetKit, intended as a practical checklist you can adapt per genre. + +## Starter Code + +Start with one of the working examples on [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/multiplayer-game-patterns/src/actors/) and adapt it to your game. Do not start from scratch for matchmaking and lifecycle flows. + +| Game Classification | Starter Code | Common Examples | +| --- | --- | --- | +| Battle Royale | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/multiplayer-game-patterns/src/actors/battle-royale/) | Fortnite, Apex Legends, PUBG, Warzone | +| Arena | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/multiplayer-game-patterns/src/actors/arena/) | Call of Duty TDM/FFA, Halo Slayer, Counter-Strike casual, VALORANT unrated, Overwatch Quick Play, Rocket League | +| IO Style | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/multiplayer-game-patterns/src/actors/io-style/) | Agar.io, Slither.io, surviv.io | +| Open World | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/multiplayer-game-patterns/src/actors/open-world/) | Minecraft survival servers, Rust-like worlds, MMO zone/chunk worlds | +| Party | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/multiplayer-game-patterns/src/actors/party/) | Fall Guys private lobbies, custom game rooms, social party sessions | +| Physics 2D | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/multiplayer-game-patterns/src/actors/physics-2d/) | Top-down physics brawlers, 2D arena games, platform fighters | +| Physics 3D | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/multiplayer-game-patterns/src/actors/physics-3d/) | Physics sandbox sessions, 3D arena games, movement playgrounds | +| Ranked | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/multiplayer-game-patterns/src/actors/ranked/) | Chess ladders, competitive card games, duel arena ranked queues | +| Turn-Based | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/multiplayer-game-patterns/src/actors/turn-based/) | Chess correspondence, Words With Friends, async board games | +| Idle | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/multiplayer-game-patterns/src/actors/idle/) | Cookie Clicker, Idle Miner Tycoon, Adventure Capitalist | + +## Server Simulation + +### Game Loop And Tick Rates + +| Pattern | Use When | Implementation Guidance | +| --- | --- | --- | +| Fixed realtime loop | Battle Royale, Arena, IO Style, Open World, Ranked | Run in `run` with `sleep(tickMs)` and exit on `c.aborted`. | +| Action-driven updates | Party, Turn-Based | Mutate and broadcast only on actions/events rather than scheduled ticks. | +| Coarse offline progression | Any mode with idle progression | Use `c.schedule.after(...)` with coarse windows (for example 5 to 15 minutes) and apply catch-up from elapsed wall clock time. | + +### Physics + +Start with custom kinematic logic for simple games. Switch to a full physics engine when you need joints, stacked bodies, high collision density, or complex shapes (rotated polygons, capsules, convex hulls, triangle meshes). + +Pick one engine per simulation. Keep frontend-only libs out of backend simulation paths and treat server state as authoritative. + +| Dimension | Primary Engine | Fallback Engines | Example Code | +| --- | --- | --- | --- | +| 2D | `@dimforge/rapier2d` | `planck-js`, `matter-js` | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/multiplayer-game-patterns/src/actors/physics-2d/) | +| 3D | `@dimforge/rapier3d` | `cannon-es`, `ammo.js` | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/multiplayer-game-patterns/src/actors/physics-3d/) | + +### Spatial Indexing + +For non-physics spatial queries, use a dedicated index instead of naive `O(n^2)` checks: + +| Index Type | Recommendation | +| --- | --- | +| AABB index | For AOI, visibility, and non-collider entities, use `rbush` for dynamic sets or `flatbush` for static-ish sets. | +| Point index | For nearest-neighbor or within-radius queries, use `d3-quadtree`. | + +## Networking & State Sync + +### Netcode + +| Model | When To Use | Implementation | +| --- | --- | --- | +| Hybrid (client movement, server combat) | Shooters, action sports, ranked duels | Client owns movement and sends capped-rate position updates. Server validates for anti-cheat. Combat (projectiles, hits, damage) is fully server-authoritative. | +| Server-authoritative with interpolation | IO Style, persistent worlds | Client sends input commands. Server simulates on fixed ticks and publishes authoritative snapshots. Client interpolates between snapshots. | +| Server-authoritative (basic logic) | Turn-based, event-driven | Server validates and applies discrete actions (turns, phase transitions, votes). Client displays confirmed state. | + +### Realtime Data Model + +- **Snapshots and diffs**: Publish state as events. Send a full snapshot on join/resync, then per-tick diffs for regular updates. +- **Batch per tick**: Keep events small and typed. Batch high-frequency updates per tick. +- **Avoid UI framework state for game updates**: Use `requestAnimationFrame` or a Canvas/Three.js loop for simulation, not React state. Reserve UI framework state for menus, HUD, and forms. +- **Broadcast vs per-connection**: Use `c.broadcast(...)` for shared updates and `conn.send(...)` for private/per-player data. + +### Shared Simulation Logic + +Shared simulation logic runs on both the client and the server. For example, an `applyInput(state, input, dt)` function that integrates velocity and clamps to world bounds can run on the client for prediction and on the server for validation. + +- **Hybrid modes**: Client runs shared movement as primary authority, server runs it for anti-cheat validation. +- **Server-authoritative modes**: Client uses shared logic for interpolation and prediction only. +- **Keep it pure**: Movement integration, input transforms, collision helpers, and constants only. +- **Put shared code in `src/shared/`**: Keep deterministic helpers in `src/shared/sim/*` with no side effects. + +### Interest Management + +Control what each client receives to reduce bandwidth and prevent information leaks. + +#### Per-Player Replication Filters + +- **Filter by relevance**: Send each client only state relevant to that player (proximity, line-of-sight, team, or game phase). +- **Shooters and action games**: Limit replication by proximity and optional field-of-view checks. +- **Server-side only**: Clients should never receive data they should not see. + +#### Sharded Worlds + +- **Partition large worlds**: Use chunk actors keyed by `worldId:chunkX:chunkY`. +- **Subscribe to nearby chunks**: Clients connect only to nearby partitions (for example a 3x3 chunk window). +- **Use sparingly**: Only when the world is large and state-heavy (sandbox builders, MMOs), not as a default for small matches. + +## Backend Infrastructure + +### Persistence + +- **In-memory state**: Best for realtime game state that changes every tick (player positions, inputs, match phase, scores). +- **SQLite (`rivetkit/db`)**: Better for large or table-like state that needs queries, indexes, or long-term persistence (tiles, inventory, matchmaking pools). Serialize DB work through a queue since multiple actions can hit the same actor concurrently. + +### Matchmaking Patterns + +Common building blocks used across the architecture patterns below. + +#### Actor Topology + +| Primitive | Use When | Typical Ownership | +| --- | --- | --- | +| `matchmaker["main"]` + `match[matchId]` | Session-based multiplayer (battle royale, arena, ranked, party, turn-based) | Matchmaker owns discovery/assignment. Match owns lifecycle and gameplay state. | +| `chunk[worldId,chunkX,chunkY]` | Large continuous worlds that need sharding | Each chunk owns local players, chunk state, and local simulation. | +| `world[playerId]` | Per-player progression loops (idle/solo world state) | Per-player resources, buildings, timers, and progression. | +| `player[username]` | Canonical profile/rating reused across matches | Durable player stats (for example rating and win/loss). | +| `leaderboard["main"]` | Shared rankings across many matches/players | Global ordered score rows and top lists. | + +#### Queueing Strategy + +- Multiple players can hit the matchmaker at the same time, so actions like find/create, queue/unqueue, and close need to be serialized through actor queues to avoid races. +- Match-local actions (gameplay, scoring) do not need queueing unless they write back to the matchmaker. + +## Security And Anti-Cheat + +Start with this baseline, then harden further for competitive or high-risk environments. + +### Baseline Checklist + +- **Identity**: Use `c.conn.id` as the authoritative transport identity. Treat `playerId`/`username` in params as untrusted input and bind through server-issued assignment/join tickets. +- **Authorization**: Validate the caller is allowed to mutate the target entity (room membership, turn ownership, host-only actions). +- **Input validation**: Clamp sizes/lengths, validate enums, and validate usernames (length, allowed chars, avoid unbounded Unicode). +- **Rate limiting**: Per-connection rate limits for spammy actions (chat, join/leave, fire, movement updates). +- **State integrity**: Server recomputes derived state (scores, win conditions, placements). Never allow client-authoritative changes to inventory/currency/leaderboard totals. + +### Movement Validation + +For any mode with client-authoritative movement (hybrid flows), clients may send position/rotation updates for smoothness, but the server must: + +- Enforce max delta per update (speed cap) based on elapsed time. +- Reject or clamp teleports. +- Enforce world bounds (and basic collision if applicable). +- Rate limit update frequency (for example 20Hz max). + +## Architecture Patterns + +Each game type below starts with a quick summary table, then details actors and lifecycle. + +### Battle Royale + +| Topic | Summary | +| --- | --- | +| Matchmaking | Immediate routing to the fullest non-started lobby (oldest tie-break); players wait in lobby until capacity, then the match starts. | +| Netcode | Hybrid. Client owns movement, camera, and local prediction. Server owns zone state, projectiles, hit resolution, eliminations, loot, and final placement. | +| Tick Rate | 10 ticks/sec (`100ms`) with a fixed loop for zone progression and lifecycle checks. | +| Physics | Client owns movement with server anti-cheat validation; projectiles, hits, and damage are server-authoritative. Use `@dimforge/rapier3d` for 3D or `@dimforge/rapier2d` for top-down 2D. | + +**Actors** + +- **Key**: `matchmaker["main"]` +- **Responsibility**: Finds or creates lobbies, tracks pending reservations, and maintains occupancy. +- **Actions** + - `findMatch` + - `pendingPlayerConnected` + - `updateMatch` + - `closeMatch` +- **Queues** + - `findMatch` + - `pendingPlayerConnected` + - `updateMatch` + - `closeMatch` +- **State** + - SQLite + - `matches` + - `pending_players` + - `player_count` includes connected and pending players + +- **Key**: `match[matchId]` +- **Responsibility**: Runs lobby/live/finished phases, owns player state, zone progression, and eliminations. +- **Actions** + - `connect` + - Movement and combat actions +- **Queues** + - None +- **State** + - JSON + - `phase` + - `players` + - `zone` + - `eliminations` + - `snapshot data` + +**Lifecycle** + +```mermaid +sequenceDiagram + participant C as Client + participant MM as matchmaker + participant M as match + + C->>MM: findMatch() + alt no open lobby + MM->>M: create(matchId) + end + MM-->>C: {matchId, playerId} + C->>M: connect(playerId) + M->>MM: pendingPlayerConnected(matchId, playerId) + MM-->>M: accepted + Note over M: lobby countdown -> live + M-->>C: snapshot + shoot events + M->>MM: closeMatch(matchId) +``` + +### Arena + +| Topic | Summary | +| --- | --- | +| Matchmaking | Mode-based fixed-capacity queues (`duo`, `squad`, `ffa`) that build only full matches and pre-assign teams (except FFA). | +| Netcode | Hybrid. Client owns movement plus prediction and smoothing. Server owns team or FFA assignment, projectiles, hit resolution, phase transitions, and scoring. | +| Tick Rate | 20 ticks/sec (`50ms`) with a tighter loop for live team and FFA snapshots. | +| Physics | Medium to high intensity; client movement with server validation and server-authoritative combat/entities. | + +**Actors** + +- **Key**: `matchmaker["main"]` +- **Responsibility**: Runs mode queues, builds full matches, assigns teams, and publishes assignments. +- **Actions** + - `queueForMatch` + - `unqueueForMatch` + - `matchCompleted` +- **Queues** + - `queueForMatch` + - `unqueueForMatch` + - `matchCompleted` +- **State** + - SQLite + - `player_pool` + - `matches` + - `assignments` keyed by connection and player + +- **Key**: `match[matchId]` +- **Responsibility**: Runs match phases and in-match player/team state for score and win conditions. +- **Actions** + - `connect` + - Gameplay actions +- **Queues** + - None +- **State** + - JSON + - `phase` + - `players` + - `team assignments` + - `score and win state` + +**Lifecycle** + +```mermaid +sequenceDiagram + participant C as Client + participant MM as matchmaker + participant M as match + + C->>MM: queueForMatch(mode) + Note over MM: enqueue in player_pool + Note over MM: fill when capacity reached + MM->>M: create(matchId, assignments) + Note over MM: persist assignments + MM-->>C: assignmentReady + C->>M: connect(playerId) + Note over M: waiting -> live when all players connect + M->>MM: matchCompleted(matchId) +``` + +### IO Style + +| Topic | Summary | +| --- | --- | +| Matchmaking | Open-lobby routing to the fullest room below capacity; room counts are heartbeated and new lobbies are auto-created when needed. | +| Netcode | Server-authoritative with interpolation. Client sends input intents and interpolates. Server owns movement, bounds, room membership, and canonical snapshots. | +| Tick Rate | 10 ticks/sec (`100ms`) with lightweight periodic room snapshots. | +| Physics | Low to medium intensity; server-authoritative kinematic movement, escalating to a physics engine only when collisions get complex. | + +**Actors** + +- **Key**: `matchmaker["main"]` +- **Responsibility**: Routes players into the fullest open lobby and tracks reservations and occupancy. +- **Actions** + - `findLobby` + - `pendingPlayerConnected` + - `updateMatch` + - `closeMatch` +- **Queues** + - `findLobby` + - `pendingPlayerConnected` + - `updateMatch` + - `closeMatch` +- **State** + - SQLite + - `matches` + - `pending_players` + - Occupancy includes pending reservations + +- **Key**: `match[matchId]` +- **Responsibility**: Runs per-match movement simulation and broadcasts snapshots. +- **Actions** + - `connect` + - `setInput` +- **Queues** + - None +- **State** + - JSON + - `players` + - `inputs` + - `movement state` + - `snapshot cache` + +**Lifecycle** + +```mermaid +sequenceDiagram + participant C as Client + participant MM as matchmaker + participant M as match + + C->>MM: findLobby() + alt no open lobby + MM->>M: create(matchId) + end + MM-->>C: {matchId, playerId} + C->>M: connect(playerId) + M->>MM: pendingPlayerConnected(matchId, playerId) + MM-->>M: accepted + Note over M: fixed tick simulation + M-->>C: snapshot events + M->>MM: closeMatch(matchId) +``` + +### Open World + +| Topic | Summary | +| --- | --- | +| Matchmaking | Client-driven chunk routing from world coordinates, with nearby chunk windows preloaded via adjacent chunk connections. | +| Netcode | Hybrid for sandbox (client movement with validation) or server-authoritative for MMO-like flows. Server owns chunk routing, persistence, and canonical world state. | +| Tick Rate | 10 ticks/sec per chunk actor (`100ms`), so load scales with active chunks. | +| Physics | Medium to high at scale; chunk-local simulation can be server-authoritative (MMO-like) or client movement with server validation (sandbox-like). | + +**Actors** + +- **Key**: `chunk[worldId,chunkX,chunkY]` +- **Responsibility**: Owns chunk-local players, blocks, movement tick, and chunk membership. +- **Actions** + - `connect` + - `enterChunk` + - `addPlayer` + - `setInput` + - `leaveChunk` + - `removePlayer` +- **Queues** + - None +- **State** + - JSON + - `connections` + - `players` + - `blocks` scoped to one chunk key + +**Lifecycle** + +```mermaid +sequenceDiagram + participant C as Client + participant CH as chunk + + Note over C: resolve chunk keys from world position + loop each visible chunk + C->>CH: connect(worldId, chunkX, chunkY, playerId) + Note over CH: store connection metadata + end + C->>CH: enterChunk/addPlayer + loop movement updates + C->>CH: setInput(...) + CH-->>C: snapshot + end + C->>CH: leaveChunk/removePlayer or disconnect + Note over CH: remove membership and metadata +``` + +### Party + +| Topic | Summary | +| --- | --- | +| Matchmaking | Host-created private party flow using party codes and explicit joins. | +| Netcode | Server-authoritative (basic logic). Server owns membership, host permissions, and phase transitions. | +| Tick Rate | No continuous tick; updates are event-driven (`join`, `start`, `finish`). | +| Physics | Low intensity for lobby-first flows; usually no dedicated physics or indexing unless you add realtime mini-games. | + +**Actors** + +- **Key**: `matchmaker["main"]` +- **Responsibility**: Handles party create/join flow, validates join tickets, and tracks party size. +- **Actions** + - `createParty` + - `joinParty` + - `verifyJoin` + - `updatePartySize` + - `closeParty` +- **Queues** + - `createParty` + - `joinParty` + - `verifyJoin` + - `updatePartySize` + - `closeParty` +- **State** + - SQLite + - `parties` + - `join_tickets` for party lookup and join validation + +- **Key**: `match[matchId]` +- **Responsibility**: Owns party members, host role, ready flags, and phase transitions. +- **Actions** + - `connect` + - `startGame` + - `finishGame` +- **Queues** + - None +- **State** + - JSON + - `members` + - `host` + - `ready state` + - `phase` + - `party events` + +**Lifecycle** + +### Host Flow + +```mermaid +sequenceDiagram + participant H as Host Client + participant MM as matchmaker + participant M as match + + H->>MM: createParty() + MM-->>H: {matchId, partyCode, playerId, joinToken} + H->>M: connect(playerId, joinToken) + M->>MM: verifyJoin(...) + MM-->>M: allowed + M->>MM: updatePartySize(playerCount) + H->>M: startGame() / finishGame() + M->>MM: closeParty(matchId) +``` + +### Joiner Flow + +```mermaid +sequenceDiagram + participant J as Joiner Client + participant MM as matchmaker + participant M as match + + J->>MM: joinParty(partyCode) + MM-->>J: {matchId, playerId, joinToken} + J->>M: connect(playerId, joinToken) + M->>MM: verifyJoin(...) + MM-->>M: allowed / denied + M->>MM: updatePartySize(playerCount) +``` + +### Ranked + +| Topic | Summary | +| --- | --- | +| Matchmaking | ELO-based queue pairing with a widening search window as wait time increases. | +| Netcode | Hybrid. Client owns movement with local prediction and interpolation. Server owns projectiles, hit resolution, match results, and rating updates. | +| Tick Rate | 20 ticks/sec (`50ms`) with fixed live ticks for deterministic pacing and broadcast cadence. | +| Physics | Medium to high intensity; client movement with server validation and server-authoritative combat/hit resolution. | + +**Actors** + +- **Key**: `matchmaker["main"]` +- **Responsibility**: Runs rating-based queueing, pairing, assignment persistence, and completion fanout. +- **Actions** + - `queueForMatch` + - `unqueueForMatch` + - `matchCompleted` +- **Queues** + - `queueForMatch` + - `unqueueForMatch` + - `matchCompleted` +- **State** + - SQLite + - `player_pool` + - `matches` + - `assignments` with rating window and connection scoping + +- **Key**: `match[matchId]` +- **Responsibility**: Runs ranked match phase, score, and winner reporting. +- **Actions** + - `connect` + - Gameplay actions +- **Queues** + - None +- **State** + - JSON + - `phase` + - `players` + - `score` + - `winner` + - `completion payload` + +- **Key**: `player[username]` +- **Responsibility**: Stores canonical player MMR and win/loss profile. +- **Actions** + - `initialize` + - `getRating` + - `applyMatchResult` +- **Queues** + - None +- **State** + - JSON + - `rating` + - `wins` + - `losses` + - `match counters` + +- **Key**: `leaderboard["main"]` +- **Responsibility**: Stores and serves top-ranked players. +- **Actions** + - `updatePlayer` +- **Queues** + - None +- **State** + - SQLite + - Leaderboard score rows + - Top-list ordering + +**Lifecycle** + +```mermaid +sequenceDiagram + participant C as Client + participant MM as matchmaker + participant P as player + participant M as match + participant LB as leaderboard + + C->>MM: queueForMatch(username) + MM->>P: initialize/getRating + P-->>MM: rating + Note over MM: store queue row + retry pairing + MM->>M: create(matchId, assigned players) + MM-->>C: assignmentReady + C->>M: connect(username) + M->>MM: matchCompleted(...) + MM->>P: applyMatchResult(...) + MM->>LB: updatePlayer(...) + Note over MM: remove matches + assignments rows +``` + +### Turn-Based + +| Topic | Summary | +| --- | --- | +| Matchmaking | Async private-invite and public-queue pairing in the same pattern. | +| Netcode | Server-authoritative (basic logic). Client can draft moves before submit. Server owns turn ownership, committed move log, turn order, and completion state. | +| Tick Rate | No continuous tick; move submission and turn transitions drive updates. | +| Physics | Very low intensity; no realtime physics loop, just discrete rules validation. Indexing is optional and mostly for board or query convenience at scale. | + +**Actors** + +- **Key**: `matchmaker["main"]` +- **Responsibility**: Handles private invite and public queue pairing for async matches. +- **Actions** + - `createGame` + - `joinByCode` + - `queueForMatch` + - `unqueueForMatch` + - `closeMatch` +- **Queues** + - `createGame` + - `joinByCode` + - `queueForMatch` + - `unqueueForMatch` + - `closeMatch` +- **State** + - SQLite + - `matches` + - `player_pool` + - `assignments` for invite and queue mapping + +- **Key**: `match[matchId]` +- **Responsibility**: Owns board state, turn order, move validation, and final result. +- **Actions** + - `connect` + - `makeMove` +- **Queues** + - None +- **State** + - JSON + - `board` + - `turns` + - `players` + - `connection presence` + - `result` + +**Lifecycle** + +### Public Queue + +```mermaid +sequenceDiagram + participant A as Client A + participant B as Client B + participant MM as matchmaker + participant M as match + + A->>MM: queueForMatch() + B->>MM: queueForMatch() + Note over MM: pair first two queued players + MM->>M: create(matchId) + seed X/O players + MM-->>A: assignment/match info + MM-->>B: assignment/match info + A->>M: connect(playerId) + B->>M: connect(playerId) + A->>M: makeMove() + B->>M: makeMove() + opt all players disconnected for timeout + Note over M: destroy after idle timeout + end + M->>MM: closeMatch(matchId) +``` + +### Private Invite + +```mermaid +sequenceDiagram + participant A as Client A + participant B as Client B + participant MM as matchmaker + participant M as match + + A->>MM: createGame() + MM-->>A: {matchId, playerId, inviteCode} + B->>MM: joinByCode(inviteCode) + MM->>M: create(matchId) + seed X/O players + MM-->>A: assignment/match info + MM-->>B: assignment/match info + A->>M: connect(playerId) + B->>M: connect(playerId) + A->>M: makeMove() + B->>M: makeMove() + M->>MM: closeMatch(matchId) +``` + +### Idle + +| Topic | Summary | +| --- | --- | +| Matchmaking | No matchmaker; each player uses a direct per-player actor and a shared leaderboard actor. | +| Netcode | Server-authoritative (basic logic). Client owns UI and build intent. Server owns resources, production rates, building validation, and leaderboard totals. | +| Tick Rate | No continuous tick; use `c.schedule.after(...)` for coarse intervals and compute offline catch-up from elapsed wall time. | +| Physics | None for standard idle loops; transitions are discrete (`build`, `collect`, `upgrade`) and do not need spatial indexing. | + +**Actors** + +- **Key**: `world[playerId]` +- **Responsibility**: Owns one player's progression, buildings, production scheduling, and state updates. +- **Actions** + - `initialize` + - `build` + - `collectProduction` +- **Queues** + - None +- **State** + - JSON + - Per-player buildings + - `resources` + - `timers` + - `progression state` + +- **Key**: `leaderboard["main"]` +- **Responsibility**: Stores global scores and serves leaderboard updates. +- **Actions** + - `updateScore` +- **Queues** + - `updateScore` +- **State** + - SQLite + - `scores` table keyed by player + - Current leaderboard totals + +**Lifecycle** + +```mermaid +sequenceDiagram + participant C as Client + participant W as world + participant LB as leaderboard + + C->>W: getOrCreate(playerId) + initialize() + Note over W: seed state + schedule collection + W-->>C: stateUpdate + loop gameplay loop + C->>W: build() / collectProduction() + W->>LB: updateScore(...) + Note over LB: upsert scores + LB-->>C: leaderboardUpdate + W-->>C: stateUpdate + end +``` + +_Source doc path: /cookbook/multiplayer-game_ diff --git a/.agents/skills/multiplayer-game/reference/cookbook/per-tenant-database.md b/.agents/skills/multiplayer-game/reference/cookbook/per-tenant-database.md new file mode 100644 index 0000000..e3c5616 --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/cookbook/per-tenant-database.md @@ -0,0 +1,119 @@ +# Database per Tenant + +> Source: `src/content/cookbook/per-tenant-database.mdx` +> Canonical URL: https://rivet.dev/cookbook/per-tenant-database +> Description: 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. + +--- +Patterns 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. + +## Starter Code + +Start with the working example on [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/per-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. + +| Topic | Summary | +| --- | --- | +| Isolation | One `companyDatabase` actor per tenant, keyed by company name. Switching tenants swaps the entire dataset. | +| State | JSON actor state holding `employees` and `projects` arrays plus timestamps. No SQLite, no queues, no scheduling. | +| Realtime | Every write action mutates state, then broadcasts a typed event (`employeeAdded`, `projectAdded`) to all connected clients of that tenant. | +| Auth | None. The sign-in screen is cosmetic. Production guidance is in the [security checklist](#security-checklist). | + +## The Isolation Model + +The 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: + +- **One actor per tenant**: `companyDatabase[tenantId]` addresses exactly one actor instance. Two tenants can never share an actor. +- **One dataset per tenant**: All reads and writes go through that actor's [state](/docs/actors/state), 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. +- **No key injection**: Keys are arrays, not interpolated strings. `key: [tenantId]` cannot be escaped the way `"tenant:" + tenantId` string concatenation can. See [Keys](/docs/actors/keys). + +The example's test ([tests/per-tenant-database.test.ts](https://github.com/rivet-dev/rivet/tree/main/examples/per-tenant-database/tests/per-tenant-database.test.ts)) proves the isolation: data written to `companyDatabase["Alpha Co"]` never appears in `companyDatabase["Beta Co"]`. + +## Choosing a State Backend + +The example uses plain JSON actor state. The same key-equals-tenant model works with any actor state backend. + +| Backend | Use When | Docs | Working Code | +| --- | --- | --- | --- | +| JSON actor state | Small datasets, simple reads, whole dataset fits comfortably in memory. What the example uses. | [State](/docs/actors/state) | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/per-tenant-database) | +| Actor SQLite (`rivetkit/db`) | Tables, indexes, SQL queries, larger-than-memory data, per-tenant relational schema. | [SQLite](/docs/actors/sqlite) | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/kitchen-sink/src/actors/state/sqlite-raw.ts) | +| SQLite + Drizzle | Typed schema, query builder, and generated migration files on top of actor SQLite. | [SQLite + Drizzle](/docs/actors/sqlite-drizzle) | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/kitchen-sink/src/actors/state/sqlite-drizzle/) | + +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. + +## Migrations + +The per-tenant example has no migrations because JSON state has no schema. When you adopt SQLite, migrations run per tenant database: + +- **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](/docs/actors/sqlite). +- **Drizzle**: `drizzle-kit` generates migration files from your typed schema, and `db({ schema, migrations })` applies them when the actor wakes. See [SQLite + Drizzle](/docs/actors/sqlite-drizzle). + +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. + +## Tenant Id Must Come From Auth + +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): + +- Derive the tenant id from a verified credential, such as a JWT claim, never from user input. +- Validate the credential against `c.key` in `onBeforeConnect` (pass/fail) or `createConnState` (store the verified user on connection state). See [Authentication](/docs/actors/authentication) and [Connections](/docs/actors/connections). +- Add per-action permission checks on top of connection-level auth. See [Access Control](/docs/actors/access-control). + +## Actors + +- **Key**: `companyDatabase[companyName]` (single-element array key; `c.key[0]` is the company name) +- **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. +- **Actions** + - `addEmployee` + - `listEmployees` + - `addProject` + - `listProjects` + - `getStats` +- **Queues** + - None +- **Events** + - `employeeAdded` + - `projectAdded` +- **State** + - JSON + - `company_name` + - `employees` + - `projects` + - `created_at` + - `updated_at` + +Every 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](/docs/actors/actions) and [Events](/docs/actors/events). + +## Lifecycle + +```mermaid +sequenceDiagram + participant A as Tenant A client + participant DA as companyDatabase A + participant B as Tenant B client + participant DB as companyDatabase B + + Note over A: authenticate and derive tenant id + A->>DA: connect with key [tenantA] + Note over DA: createState seeds company_name, employees, projects + A->>DA: listEmployees() + listProjects() + getStats() + A->>DA: addEmployee(name, role) + DA-->>A: employeeAdded event + B->>DB: connect with key [tenantB] + Note over DB: separate actor, separate dataset + B->>DB: listEmployees() + DB-->>B: tenant B data only +``` + +In 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. + +## Security Checklist + +The example ships with none of these. Apply all of them before production. + +- **Tenant identity**: Derive the tenant id from a verified JWT claim, never from a client-supplied string. +- **Connection validation**: In `onBeforeConnect` or `createConnState`, verify the credential's tenant claim matches `c.key` and reject mismatches. +- **Per-action authorization**: Check the caller's role before mutating actions (`addEmployee`, `addProject`), not just at connect time. See [Access Control](/docs/actors/access-control). +- **Input validation**: Clamp name and role lengths and validate enums. The example only trims input and substitutes fallback defaults. +- **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. +- **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](/docs/actors/sqlite) when the dataset outgrows memory. + +_Source doc path: /cookbook/per-tenant-database_ diff --git a/.agents/skills/multiplayer-game/reference/cookbook/vpc-air-gapped.md b/.agents/skills/multiplayer-game/reference/cookbook/vpc-air-gapped.md new file mode 100644 index 0000000..f7d87a3 --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/cookbook/vpc-air-gapped.md @@ -0,0 +1,136 @@ +# Deploying Rivet in a VPC or Air-Gapped Network + +> Source: `src/content/cookbook/vpc-air-gapped.mdx` +> Canonical URL: https://rivet.dev/cookbook/vpc-air-gapped +> Description: Run Rivet entirely inside your own perimeter: single-binary or Docker Compose install, file system storage with no database infrastructure, and no outbound telemetry by default. + +--- +Patterns for running self-hosted Rivet inside a private network: a VPC without internet egress, an on-premises rack, or a fully air-gapped environment. The engine is one service, the recommended single-node storage backend is the local file system, and the engine makes no outbound connections by default. Self-hosting is the only Rivet deployment model that supports air-gapped networks; see the [Self-Hosting Overview](/docs/self-hosting) for the full comparison with BYOC. + +## What Runs Inside the Perimeter + +A self-hosted deployment has three components, all of which live inside your network: + +| Component | Role | Inside the perimeter | +| --- | --- | --- | +| Your backend | Your application server, including the runner that executes actor code | Yes | +| Rivet Engine | Orchestration service that manages actor lifecycle, routes messages, and serves the dashboard and APIs | Yes | +| Storage | Persistence for actor state. Local file system for single-node, PostgreSQL or FoundationDB for multi-node | Yes | + +There is no license server, no Rivet Cloud account, and no callback to `rivet.dev`. Clients inside the perimeter reach actors through the engine's gateway over your private network. See [Architecture](/docs/self-hosting#architecture). + +## Single-Binary Install + +The engine compiles to a single `rivet-engine` binary. Build it from source outside the perimeter, then copy the binary across the boundary: + +```bash +git clone https://github.com/rivet-dev/rivet.git +cd rivet +cargo build --release -p rivet-engine +# Copy target/release/rivet-engine into the perimeter. +``` + +Prebuilt binaries are coming soon; see [Installing Rivet Engine](/docs/self-hosting/install) for current options. + +Run it with the file system backend, which stores everything on local disk and is the production-ready choice for single-node deployments. The [File System](/docs/self-hosting/filesystem) docs list air-gapped environments as a primary use case because it needs no database infrastructure: + +```bash +RIVET__database__file_system__path="/var/lib/rivet/data" ./rivet-engine +``` + +Configuration can also come from files. The engine discovers config at `/etc/rivet/config.json` on Linux (JSON, JSON5, JSONC, YAML, and YML are all supported), and `--config` overrides the path. Environment variables use the `RIVET__` prefix with `__` as the separator. See [Configuration](/docs/self-hosting/configuration). + +The engine serves its own dashboard on port `6420`, so inspection and namespace management work with nothing but a browser inside the perimeter. + +## Docker Compose Deployment + +For Docker hosts without registry access, move the engine image across the boundary the standard way: + +```bash +# Outside the perimeter. +docker pull rivetdev/engine:latest +docker save rivetdev/engine:latest -o rivet-engine.tar +# Inside the perimeter. +docker load -i rivet-engine.tar +``` + +Then run the engine and your app together in one Compose file: + +```yaml +services: + rivet-engine: + image: rivetdev/engine:latest + ports: + - "6420:6420" + volumes: + - rivet-data:/data + environment: + RIVET__FILE_SYSTEM__PATH: "/data" + restart: unless-stopped + + my-app: + build: . + environment: + RIVET_ENDPOINT: "http://default:admin@rivet-engine:6420" + depends_on: + - rivet-engine + restart: unless-stopped + +volumes: + rivet-data: +``` + +`RIVET_ENDPOINT` uses the format `http://namespace:token@host:port` and tells your app to connect to the engine as a runner instead of running standalone. After both services start, register your runner with the engine through the dashboard or its API. The full walkthrough, including PostgreSQL setup for multi-node deployments, is in [Docker Compose](/docs/self-hosting/docker-compose). + +## No Outbound Telemetry + +The engine exports traces and metrics only when you opt in with OpenTelemetry. Export is disabled unless `RIVET_OTEL_ENABLED=1` is set, and the export target defaults to a local collector at `http://localhost:4317`. With no configuration, nothing crosses the perimeter. + +When you want observability, keep it inside the network: + +- Set `RIVET_OTEL_ENABLED=1` and point `RIVET_OTEL_GRPC_ENDPOINT` at a collector you run inside the perimeter. +- Adjust `RIVET_OTEL_SAMPLER_RATIO` to control trace sampling. +- Use the engine's health endpoint for liveness and readiness probes. + +See the [Production Checklist](/docs/self-hosting/production-checklist) for monitoring guidance. + +## Embedding Rivet in a Customer's Environment + +If you ship software that runs inside your customers' VPCs, the same setup turns Rivet into an internal component of your product rather than a service your customers must reach over the internet: + +- **Ship the engine next to your app.** Add `rivetdev/engine` to the Compose file or chart you already deliver. Your app finds it over the private network via `RIVET_ENDPOINT`, so one artifact deploys the whole stack. +- **One namespace per install.** The endpoint URL carries the namespace and token (`http://namespace:token@host:port`), so a single image works across customer deployments. See [Endpoints](/docs/general/endpoints). +- **Generate a strong admin token per install.** Replace the default token and keep it server-side. Never include the admin token in `RIVET_PUBLIC_ENDPOINT` or anywhere clients can read it. +- **Public endpoint only when needed.** `RIVET_PUBLIC_ENDPOINT` with a public (`pk_`) token is only required when browser clients connect to actors in [serverless runtime mode](/docs/general/runtime-modes). Backend-only deployments can skip it entirely. +- **TLS at the customer's edge.** Terminate TLS with the customer's reverse proxy or load balancer in front of the engine. + +## Scaling Past One Node + +| Backend | Use when | Status | +| --- | --- | --- | +| [File System](/docs/self-hosting/filesystem) (RocksDB-based) | Single-node deployments, including air-gapped installs | Production-ready, single node only | +| [PostgreSQL](/docs/self-hosting/postgres) | Multi-node deployments | Recommended for multi-node today, but experimental | +| FoundationDB | Largest production deployments | [Enterprise](/sales) | + +For multi-node deployments, run two or more engine nodes behind a load balancer and add NATS for pub/sub, which replaces the default PostgreSQL `LISTEN`/`NOTIFY` path at high throughput. Neither is needed for a single-node file system install. See the [Production Checklist](/docs/self-hosting/production-checklist). + +## Perimeter Checklist + +- **Admin token**: Generate a strong, random token for engine authentication and verify it is not exposed to clients. +- **TLS termination**: Encrypt connections to the engine via a reverse proxy or load balancer. +- **No public exposure**: Keep port `6420` reachable only from inside the perimeter unless clients outside it genuinely need access. +- **Health checks**: Configure liveness and readiness probes against the engine health endpoint. +- **Telemetry**: Leave OpenTelemetry export off, or point it at a collector inside the network. +- **Backups**: With the file system backend, back up the data directory. With PostgreSQL, configure automated backups and failover. + +## Full Configuration + +- [Self-Hosting Overview](/docs/self-hosting) for architecture and the self-host vs BYOC comparison +- [Installing Rivet Engine](/docs/self-hosting/install) for Docker, binary, and source installs +- [Docker Container](/docs/self-hosting/docker-container) and [Docker Compose](/docs/self-hosting/docker-compose) for container deployments +- [Kubernetes](/docs/self-hosting/kubernetes) for cluster deployments +- [Configuration](/docs/self-hosting/configuration) for every option and the full JSON schema +- [Endpoints](/docs/general/endpoints) for connecting your backend and clients +- [Production Checklist](/docs/self-hosting/production-checklist) before going live + +_Source doc path: /cookbook/vpc-air-gapped_ diff --git a/.agents/skills/multiplayer-game/reference/deploy/aws-ecs.md b/.agents/skills/multiplayer-game/reference/deploy/aws-ecs.md new file mode 100644 index 0000000..ab381ce --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/deploy/aws-ecs.md @@ -0,0 +1,13 @@ +# Deploying to AWS ECS + +> Source: `src/content/docs/deploy/aws-ecs.mdx` +> Canonical URL: https://rivet.dev/docs/deploy/aws-ecs +> Description: Run your backend on Amazon ECS with Fargate. + +--- +See the [AWS ECS template](https://github.com/rivet-dev/template-aws-ecs) for deployment instructions: + +- [Deploy with Terraform](https://github.com/rivet-dev/template-aws-ecs#option-a-deploy-with-terraform) +- [Deploy with AWS CLI](https://github.com/rivet-dev/template-aws-ecs#option-b-deploy-with-aws-cli) + +_Source doc path: /docs/deploy/aws-ecs_ diff --git a/.agents/skills/multiplayer-game/reference/deploy/aws-lambda.md b/.agents/skills/multiplayer-game/reference/deploy/aws-lambda.md new file mode 100644 index 0000000..f1327cc --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/deploy/aws-lambda.md @@ -0,0 +1,10 @@ +# Deploy To Amazon Web Services Lambda + +> Source: `src/content/docs/deploy/aws-lambda.mdx` +> Canonical URL: https://rivet.dev/docs/deploy/aws-lambda +> Description: _AWS Lambda is coming soon_ + +--- + + +_Source doc path: /docs/deploy/aws-lambda_ diff --git a/.agents/skills/multiplayer-game/reference/deploy/cloudflare.md b/.agents/skills/multiplayer-game/reference/deploy/cloudflare.md new file mode 100644 index 0000000..970078e --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/deploy/cloudflare.md @@ -0,0 +1,53 @@ +# Deploying to Cloudflare Workers + +> Source: `src/content/docs/deploy/cloudflare.mdx` +> Canonical URL: https://rivet.dev/docs/deploy/cloudflare +> Description: Deploy an existing Rivet project to Cloudflare Workers. + +--- +This guide covers deploying an existing Rivet project to Cloudflare Workers. + +## Prerequisites + +- [Cloudflare account](https://dash.cloudflare.com/) +- [`wrangler`](https://developers.cloudflare.com/workers/wrangler/) configured for your account +- A Rivet namespace from the [Rivet Dashboard](https://dashboard.rivet.dev/) or a self-hosted Rivet Engine + +## Steps + +### Set up your project + +Follow the [Cloudflare Workers Quickstart](/docs/actors/quickstart/cloudflare) to set up your project locally. + +### Configure Wrangler + +Set your Rivet connection values as Worker variables. Find `RIVET_ENDPOINT` and `RIVET_PUBLIC_ENDPOINT` in the [Rivet Dashboard](https://dashboard.rivet.dev/) under **Settings → Namespace → Advanced → Backend Configuration** and copy them in. + +```toml wrangler.toml +name = "rivetkit-cloudflare" +main = "src/index.ts" +compatibility_date = "2025-04-01" +compatibility_flags = ["nodejs_compat"] + +[vars] +RIVET_ENDPOINT = "https://your-namespace:sk_...@api.rivet.dev" +RIVET_PUBLIC_ENDPOINT = "https://your-namespace@api.rivet.dev" +``` + +### Deploy + +```sh +npx wrangler deploy +``` + +### Register the Serverless Runner URL + +After deploy, set the Worker URL with the `/api/rivet` path as the serverless runner URL in Rivet. + +## Related + +- [Cloudflare Workers Quickstart](/docs/actors/quickstart/cloudflare) +- [Deploying to Supabase Functions](/docs/deploy/supabase) +- [SQLite](/docs/actors/sqlite) + +_Source doc path: /docs/deploy/cloudflare_ diff --git a/.agents/skills/multiplayer-game/reference/deploy/container-runner.md b/.agents/skills/multiplayer-game/reference/deploy/container-runner.md new file mode 100644 index 0000000..7bb0c5a --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/deploy/container-runner.md @@ -0,0 +1,95 @@ +# Container Runner + +> Source: `src/content/docs/deploy/container-runner.mdx` +> Canonical URL: https://rivet.dev/docs/deploy/container-runner +> Description: Run any containerized server as a Rivet Actor. + +--- +The container runner (`rivet-container-runner`) is an adapter for running arbitrary containers as Rivet Actors. Use it for non-RivetKit workloads such as Unity or Godot dedicated game servers and batch jobs like FFmpeg transcoding. + +## Steps + +### Prerequisites + +- A containerized server (a Unity or Godot dedicated server, a plain Node process, or any HTTP/WebSocket server) +- Access to the [Rivet Cloud](https://dashboard.rivet.dev/) or a [self-hosted Rivet Engine](/docs/general/self-hosting) +- Docker running locally + +### Install in Your Container + +Download the static binary from Rivet's release artifacts in your Dockerfile and set it as the entrypoint, passing your server's launch command after `--`: + +```dockerfile @nocheck +FROM debian:bookworm-slim +RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates curl && rm -rf /var/lib/apt/lists/* + +# Install the Rivet container runner. +RUN curl -fsSL https://releases.rivet.dev/rivet/latest/container-runner/rivet-container-runner-x86_64-unknown-linux-musl \ + -o /usr/local/bin/rivet-container-runner \ + && chmod +x /usr/local/bin/rivet-container-runner + +# Your server binary and assets. +COPY build/ /game/ +WORKDIR /game + +ENTRYPOINT ["rivet-container-runner", "--", "./GameServer", "-batchmode", "-nographics", "-logFile", "-"] +``` + +Artifacts are published for `x86_64-unknown-linux-musl` and `aarch64-unknown-linux-musl`. The binaries are fully static, so they run in any Linux base image, including `scratch`. Pin a version by replacing `latest` with a release version, for example `https://releases.rivet.dev/rivet/2.3.3/container-runner/rivet-container-runner-x86_64-unknown-linux-musl`. + +### Deploy + +Deploy the image to [Rivet Compute](/docs/deploy/rivet-compute) with the CLI. For game servers, configure the pool with one actor per instance: + +```bash +npx @rivetkit/cli deploy \ + --token "$RIVET_CLOUD_TOKEN" \ + --instance-request-concurrency 1 \ + --dockerfile Dockerfile +``` + +### Create Actors and Connect Clients + +Create actors against the pool's runner (`default`) and connect clients through the gateway URL shown in the dashboard. WebSocket clients connect at the bare gateway URL with the `rivet` WebSocket subprotocol. + +## How It Works + +1. The engine cold-starts your container and calls `POST /api/rivet/start` on the port it injects as `RIVET_PORT`. +2. The runner spawns your server as a child process with `PORT` set to the child port, waits for the port to open, and reports the actor as running. +3. Gateway traffic for the actor arrives over Rivet's tunnel and is proxied to `127.0.0.1:`. WebSocket clients connect at the bare gateway URL with the `rivet` WebSocket subprotocol. Raw HTTP reaches the child under the `/request/*` prefix on the actor surface (the prefix is stripped before proxying); other paths are reserved for the runtime's own endpoints. +4. Child stdout and stderr are re-emitted with an `[actorId=... key=...]` prefix so actor logs are attributed in the dashboard. +5. When an actor stops, the runner sends its child `SIGTERM`, escalates to `SIGKILL` after a grace period, and exits the process once no actors remain. + +## Configuration + +All flags can also be set through environment variables: + +| Flag | Environment variable | Default | Description | +| --- | --- | --- | --- | +| `--port` | `RIVET_PORT` / `PORT` | `8080` | Serverless front-door HTTP port. Rivet Compute injects `RIVET_PORT` automatically. | +| `--child-port` | `CHILD_PORT` | `7770` | First local child port; each actor's child gets the next free port at or above this, exported to the child as `PORT`. | +| `--actor-name` | `RIVET_ACTOR_NAME` | `game` | Actor name this runner serves. | +| `--runner-version` | `RIVET_RUNNER_VERSION` | `1` | Version reported to the engine, used to drain old runners on deploy. | +| `--base-path` | `RIVET_SERVERLESS_BASE_PATH` | `/api/rivet` | Base path the engine calls for serverless start. | +| `--stop-grace-secs` | `RIVET_STOP_GRACE_SECS` | `25` | `SIGTERM` to `SIGKILL` grace period when stopping the child. Capped to a few seconds when the platform itself is reclaiming the instance, so shutdown fits inside the platform's own kill window. | +| `--readiness-timeout-secs` | `RIVET_READINESS_TIMEOUT_SECS` | `30` | How long to wait for the child's port to open before failing the start. | + +### Per-Actor Input + +The actor's `input` payload can override the launch spec per actor. All fields are optional and fall back to the entrypoint command. RivetKit clients pass this object directly; when creating actors through the raw engine API, encode it as CBOR before base64-encoding the `input` field: + +```json +{ + "command": ["./GameServer", "-batchmode"], + "args": ["-extra-flag"], + "env": { "MATCH_MODE": "ranked" } +} +``` + +`command` replaces the entrypoint command template, `args` are appended to it, and `env` adds environment variables for the child. + +## Source and Examples + +The runner and a full end-to-end example, including a Unity FishNet demo project and a local test harness, live in the Rivet repository under [`container-runner/`](https://github.com/rivet-dev/rivet/tree/main/container-runner). + +_Source doc path: /docs/deploy/container-runner_ diff --git a/.agents/skills/multiplayer-game/reference/deploy/freestyle.md b/.agents/skills/multiplayer-game/reference/deploy/freestyle.md new file mode 100644 index 0000000..eb6ed57 --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/deploy/freestyle.md @@ -0,0 +1,136 @@ +# Deploying to Freestyle + +> Source: `src/content/docs/deploy/freestyle.mdx` +> Canonical URL: https://rivet.dev/docs/deploy/freestyle +> Description: Deploy RivetKit app to Freestyle.sh, a cloud platform for running AI-generated code with built-in security and scalability. + +--- +Freestyle provides built-in security for running untrusted AI-generated code, making it ideal for AI agent applications. Using Rivet, it is easy to deploy your vibe-coded or user-provided RivetKit backends straight to Freestyle. + +- [Freestyle + Rivet](https://github.com/rivet-dev/rivet/tree/main/examples/freestyle) — Complete example of deploying RivetKit app to Freestyle.sh. + +## Setup + +### Install packages + +Install RivetKit and Hono and create your registry: + +```bash +npm install rivetkit hono +``` + +### Configure serverless driver + +Update your server code to run the registry serverless with Deno. + +```typescript index.ts @hide +import { actor, setup } from "rivetkit"; + +export const counter = actor({ + state: { count: 0 }, + actions: { + increment: (c, x: number) => { + c.state.count += x; + return c.state.count; + }, + }, +}); + +export const registry = setup({ + use: { counter }, +}); +registry.start(); +``` + +```typescript server.ts @nocheck +import { registry } from "./index"; + +// Freestyle uses Deno under the hood for web deployments +// @ts-ignore Deno is a Freestyle runtime global +Deno.serve((request: Request) => registry.handler(request)); +``` + +The `Deno.serve` API is provided by Freestyle's runtime environment. The `@ts-ignore` comment suppresses TypeScript errors for this platform-specific API. + +### Deploy to Freestyle + +Deploy your application to Freestyle with the correct configuration. Create a deployment script or add this to your existing deployment process: + +```typescript @nocheck +const FREESTYLE_DOMAIN = "my-domain.style.dev"; // Change to your desired Freestyle domain + +declare const freestyle: any; +declare const buildDir: string; + +const res = await freestyle.deployWeb(buildDir, { + envVars: { + FREESTYLE_ENDPOINT: `https://${FREESTYLE_DOMAIN}`, + RIVET_RUNNER_KIND: "serverless", + // For self-hosted instances: + // RIVET_ENDPOINT: "http://127.0.0.1:6420", + RIVET_ENDPOINT: "api.rivet.dev", + }, + timeout: 60 * 5, // Increases max request lifetime on the runner + entrypoint: "server.ts", // File which starts serverless runner + domains: [FREESTYLE_DOMAIN], + build: false, +}); +``` + +Details on `buildDir` and other settings are available on [Freestyle docs](https://docs.freestyle.sh/web/web). + +Run this deployment script to push your application to Freestyle. + +**Deployment Configuration:** + +- `timeout: 60 * 5` - Set timeout to 5 minutes for actor operations - it's important to keep this high +- `entrypoint: "server.ts"` - Entry point file with your serverless setup +- `domains` - Your Freestyle domain(s) +- `build: false` - Disable build if you're pre-building your assets + +### Configure runner + +Update the runner configuration on the Rivet side to connect with your Freestyle deployment. Create a configuration script and run it after your Freestyle deployment is live: + +```typescript @nocheck +import { RivetClient } from "@rivetkit/engine-api-full"; + +const rivet = new RivetClient({ + environment: "https://api.rivet.dev", + token: process.env.RIVET_API_TOKEN, +}); + +const FREESTYLE_DOMAIN = "my-domain.style.dev"; // Change to your desired Freestyle domain +const RIVET_NAMESPACE = "my-rivet-namespace"; // Change to your Rivet namespace + +await rivet.runnerConfigsUpsert("freestyle-runner", { + namespace: RIVET_NAMESPACE, + datacenters: { + default: { + serverless: { + url: `https://${FREESTYLE_DOMAIN}/start`, + runnersMargin: 1, + minRunners: 1, + maxRunners: 1, + slotsPerRunner: 1, + // Must be shorter than Freestyle request `timeout` config + requestLifespan: 60 * 5 - 5, + }, + }, + }, +}); +``` + +Execute this configuration script to register your Freestyle deployment with Rivet. + +**Runner Configuration:** + +- `url` - Freestyle deployment URL with `/start` endpoint +- `runnersMargin` - Buffer of runners to maintain +- `minRunners/maxRunners` - Scaling limits +- `slotsPerRunner` - Concurrent actors per runner +- `requestLifespan` - Request timeout (slightly less than Freestyle timeout) + +Once executed, Rivet will be connected to your Freestyle serverless instance. + +_Source doc path: /docs/deploy/freestyle_ diff --git a/.agents/skills/multiplayer-game/reference/deploy/gcp-cloud-run.md b/.agents/skills/multiplayer-game/reference/deploy/gcp-cloud-run.md new file mode 100644 index 0000000..f140950 --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/deploy/gcp-cloud-run.md @@ -0,0 +1,74 @@ +# Deploying to Google Cloud Run + +> Source: `src/content/docs/deploy/gcp-cloud-run.mdx` +> Canonical URL: https://rivet.dev/docs/deploy/gcp-cloud-run +> Description: Deploy your RivetKit app to Google Cloud Run. + +--- +## Steps + +### Prerequisites + +- Google Cloud project with Cloud Run and Artifact Registry enabled +- `gcloud` CLI authenticated (`gcloud auth login`) and project set (`gcloud config set project YOUR_PROJECT`) +- Artifact Registry repository or Container Registry enabled +- Your RivetKit app + - If you don't have one, see the [Quickstart](/docs/actors/quickstart) page or our [Examples](https://github.com/rivet-dev/rivet/tree/main/examples) +- Access to the [Rivet Cloud](https://dashboard.rivet.dev/) or a [self-hosted Rivet Engine](/docs/general/self-hosting) + +### Package Your App + +Create a `Dockerfile` in your project root: + +```dockerfile +FROM node:20-alpine +WORKDIR /app +COPY package*.json ./ +RUN npm ci --omit=dev +COPY . . +ENV PORT=8080 +CMD ["node", "server.js"] +``` + +### Build and Push the Image + +Use Cloud Build to build and push the image. Replace the region and repository with your own. + +```bash +gcloud builds submit --tag us-central1-docker.pkg.dev/YOUR_PROJECT/rivetkit-app/rivetkit-app:latest +``` + +### Set Environment Variables + +After creating your project on the Rivet dashboard, select Google Cloud Run as your provider. You'll be provided `RIVET_ENDPOINT` and `RIVET_PUBLIC_ENDPOINT` environment variables to use when deploying. + +### Deploy to Cloud Run + +Deploy the service to Cloud Run, passing the Rivet environment variables. Adjust the region and image as needed. + +```bash +gcloud run deploy rivetkit-app \ + --image us-central1-docker.pkg.dev/YOUR_PROJECT/rivetkit-app/rivetkit-app:latest \ + --region us-central1 \ + --allow-unauthenticated \ + --min-instances 1 \ + --set-env-vars RIVET_ENDPOINT=,RIVET_PUBLIC_ENDPOINT= +``` + +### Connect to Rivet + +1. After deployment, note the service URL (e.g. `https://rivetkit-app-xxxxx-uc.a.run.app`) +2. On the Rivet dashboard, paste your URL with the `/api/rivet` path into the connect form (e.g. `https://rivetkit-app-xxxxx-uc.a.run.app/api/rivet`) +3. Click "Done" + +### Verify + +Confirm the service is running: + +```bash +gcloud run services describe rivetkit-app --region us-central1 --format 'value(status.conditions[?type="Ready"].status)' +``` + +Your app should appear as connected on the Rivet dashboard once the service reports ready. + +_Source doc path: /docs/deploy/gcp-cloud-run_ diff --git a/.agents/skills/multiplayer-game/reference/deploy/hetzner.md b/.agents/skills/multiplayer-game/reference/deploy/hetzner.md new file mode 100644 index 0000000..5b61024 --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/deploy/hetzner.md @@ -0,0 +1,10 @@ +# Deploying to Hetzner + +> Source: `src/content/docs/deploy/hetzner.mdx` +> Canonical URL: https://rivet.dev/docs/deploy/hetzner +> Description: Please see the VM & Bare Metal guide. + +--- + + +_Source doc path: /docs/deploy/hetzner_ diff --git a/.agents/skills/multiplayer-game/reference/deploy/kubernetes.md b/.agents/skills/multiplayer-game/reference/deploy/kubernetes.md new file mode 100644 index 0000000..43496a9 --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/deploy/kubernetes.md @@ -0,0 +1,121 @@ +# Deploying to Kubernetes + +> Source: `src/content/docs/deploy/kubernetes.mdx` +> Canonical URL: https://rivet.dev/docs/deploy/kubernetes +> Description: Deploy your RivetKit app to any Kubernetes cluster. + +--- +## Steps + +### Prerequisites + +- A Kubernetes cluster with `kubectl` access (EKS, GKE, k3s, etc.) +- Container registry credentials (Docker Hub, GHCR, GCR, etc.) +- Your RivetKit app + - If you don't have one, see the [Quickstart](/docs/actors/quickstart) page or our [Examples](https://github.com/rivet-dev/rivet/tree/main/examples) +- Access to the [Rivet Cloud](https://dashboard.rivet.dev/) or a [self-hosted Rivet Engine](/docs/general/self-hosting) + +### Package Your App + +Create a `Dockerfile` in your project root: + +```dockerfile +FROM node:20-alpine +WORKDIR /app +COPY package*.json ./ +RUN npm ci --omit=dev +COPY . . +ENV PORT=8080 +CMD ["node", "server.js"] +``` + +### Build and Push the Image + +```bash +docker build -t registry.example.com/your-team/rivetkit-app:latest . +docker push registry.example.com/your-team/rivetkit-app:latest +``` + +Replace `registry.example.com/your-team` with your registry path. Auth with `docker login` first if needed. + +### Set Environment Variables + +After creating your project on the Rivet dashboard, select Kubernetes as your provider. You'll be provided `RIVET_ENDPOINT` and `RIVET_PUBLIC_ENDPOINT` environment variables. + +Create a `rivetkit-secrets.yaml` for your environment variables: + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: rivetkit-secrets +type: Opaque +stringData: + RIVET_ENDPOINT: + RIVET_PUBLIC_ENDPOINT: +``` + +### Deploy to Kubernetes + +Create a `deployment.yaml`: + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: rivetkit-app +spec: + replicas: 1 + selector: + matchLabels: + app: rivetkit-app + template: + metadata: + labels: + app: rivetkit-app + spec: + # Allow enough time for actors to gracefully stop on SIGTERM. + # The runner waits up to 30m for actors to finish. + # Add buffer for runner shutdown overhead after actors stop. + # See: /docs/actors/versions#graceful-shutdown-sigterm + terminationGracePeriodSeconds: 2100 + containers: + - name: rivetkit-app + image: registry.example.com/your-team/rivetkit-app:latest + envFrom: + - secretRef: + name: rivetkit-secrets +``` + +Apply both manifests: + +```bash +kubectl apply -f rivetkit-secrets.yaml +kubectl apply -f deployment.yaml +``` + +### Connect to Rivet + +1. Add a `Service` and Ingress to expose your app externally (e.g. `my-app.example.com`) +2. On the Rivet dashboard, paste your domain with the `/api/rivet` path into the connect form (e.g. `https://my-app.example.com/api/rivet`) +3. Click "Done" + +Rivet envoys connect to your app over long-lived WebSockets, and your app's clients (browsers, SDKs) do the same. Default Ingress and cloud load balancer idle timeouts (typically 30 to 60 seconds) drop these connections and cause reconnect storms. + +Raise the idle / read / send timeout on every Ingress and load balancer in front of your app to at least 1 hour (`3600` seconds). Examples: + +- **NGINX Ingress**: `nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"` and `nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"` +- **AWS Load Balancer Controller (ALB)**: `alb.ingress.kubernetes.io/load-balancer-attributes: idle_timeout.timeout_seconds=3600` +- **GCE Ingress (GKE)**: set `timeoutSec: 3600` on the `BackendConfig` referenced by the Service. + +### Verify + +Check that the pod is running: + +```bash +kubectl get pods -l app=rivetkit-app +``` + +Your app should appear as connected on the Rivet dashboard once the pod is ready. + +_Source doc path: /docs/deploy/kubernetes_ diff --git a/.agents/skills/multiplayer-game/reference/deploy/railway.md b/.agents/skills/multiplayer-game/reference/deploy/railway.md new file mode 100644 index 0000000..640ac37 --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/deploy/railway.md @@ -0,0 +1,45 @@ +# Deploying to Railway + +> Source: `src/content/docs/deploy/railway.mdx` +> Canonical URL: https://rivet.dev/docs/deploy/railway +> Description: Deploy your RivetKit app to Railway. + +--- +## Steps + +### Prerequisites + +- [Railway account](https://railway.app) +- Your RivetKit app + - If you don't have one, see the [Quickstart](/docs/actors/quickstart) page or our [Examples](https://github.com/rivet-dev/rivet/tree/main/examples) +- Access to the [Rivet Cloud](https://dashboard.rivet.dev/) or a [self-hosted Rivet Engine](/docs/general/self-hosting) + +### Deploy to Railway + +1. Connect your GitHub account to Railway +2. Select your repository containing your RivetKit app +3. Railway will automatically detect and deploy your app + +See [Railway's deployment docs](https://docs.railway.com/quick-start) for more details. + +### Set Environment Variables + +After creating your project on the Rivet dashboard, select Railway as your provider. You'll be provided `RIVET_ENDPOINT` and `RIVET_PUBLIC_ENDPOINT` environment variables to add to your Railway service. + +See [Railway's environment variables docs](https://docs.railway.com/guides/variables#service-variables) for more details. + +### Connect to Rivet + +1. In your Railway project, go to **Settings > Networking** +2. Click **Create Custom Domain** then **Create Domain** to generate a Railway domain (e.g. `my-app.railway.app`) +3. On the Rivet dashboard, paste your domain with the `/api/rivet` path into the connect form (e.g. `https://my-app.railway.app/api/rivet`) +4. Click "Done" + +Rivet envoys connect to your app over long-lived WebSockets, and your app's clients (browsers, SDKs) do the same. Railway's HTTP proxy supports WebSockets, but if you front your app with your own reverse proxy (NGINX, Caddy, etc.) inside the Railway service, raise its idle / read timeout to at least 1 hour (`3600` seconds). Default proxy timeouts (typically 30 to 60 seconds) drop these connections and cause reconnect storms. + +### Configure Sleeping & Multi-Region (Optional) + +- [Enable App Sleeping](https://docs.railway.com/reference/app-sleeping) to reduce costs when idle +- [Configure Multi-Region](https://docs.railway.com/reference/deployment-regions) to deploy closer to your users + +_Source doc path: /docs/deploy/railway_ diff --git a/.agents/skills/multiplayer-game/reference/deploy/rivet-compute.md b/.agents/skills/multiplayer-game/reference/deploy/rivet-compute.md new file mode 100644 index 0000000..69507aa --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/deploy/rivet-compute.md @@ -0,0 +1,144 @@ +# Deploying to Rivet Compute + +> Source: `src/content/docs/deploy/rivet-compute.mdx` +> Canonical URL: https://rivet.dev/docs/deploy/rivet-compute +> Description: Run your backend on Rivet Compute. + +--- +Using an AI coding agent? Open **Connect** on the [Rivet dashboard](https://dashboard.rivet.dev), select **Rivet Cloud**, and paste the one-shot prompt into your agent and have it connect with Rivet Compute for you. + +## Steps + +### Prerequisites + +- Your RivetKit app + - If you don't have one, see the [Quickstart](/docs/actors/quickstart) page or our [Examples](https://github.com/rivet-dev/rivet/tree/main/examples) +- A [Rivet Cloud](https://dashboard.rivet.dev) account and project +- Docker running locally + +### Create a Dockerfile + +Add a `Dockerfile` to your project root that builds and runs your RivetKit server: + +```dockerfile @nocheck +FROM node:24-alpine +WORKDIR /app +COPY package*.json ./ +RUN npm ci --omit=dev +COPY . . +CMD ["node", "src/server.js"] +``` + +### Get Your Cloud Token + +1. Open the [Rivet dashboard](https://dashboard.rivet.dev) and navigate to your project +2. Click **Connect** and select **Rivet Cloud** +3. Copy the **`RIVET_CLOUD_TOKEN`** value shown + +### Deploy + +Run the deploy command from your project root. The token is saved to `~/.rivet/credentials`, so later deploys can omit it. + +```bash +npx @rivetkit/cli deploy --token cloud_api_xxxxx +``` + +The CLI resolves your project from the token, builds and pushes your Docker image to Rivet's built-in registry, upserts the managed pool, and prints the deployment URL on stdout when the pool is ready. + +### Optionally Add CI + +After local deploys work, install the GitHub Actions workflow that deploys on every push and pull request: + +```bash +npx @rivetkit/cli setup-ci +``` + +This writes `.github/workflows/rivet-deploy.yml`. Add your token as a repository secret to enable it: + +```bash +gh secret set RIVET_CLOUD_TOKEN +``` + +The workflow creates production and pull-request namespaces, posts preview links, and cleans up PR namespaces when pull requests close. See the [CLI reference](/docs/cli) for all commands. + +### Monitor Deployment + +The dashboard shows live status as Rivet Compute provisions your backend: + +| Status | Description | +| --- | --- | +| Initializing | Starting the runtime environment | +| Deploying | Pulling and launching your container | +| Binding | Connecting the runner to the network | +| Ready | Deployment complete | + +Once the status reaches **Ready**, your backend is live and actors are available for connections. + +If you are an agent monitoring the deployment via API rather than the dashboard, poll the managed-pool endpoint on the Cloud API. + +The `RIVET_CLOUD_TOKEN` secret is a `cloud_api_*` management token scoped to the Cloud API at `cloud-api.rivet.dev`. Use it for `Authorization: Bearer ...` against the Cloud API. Do not confuse it with a `pk_*` publishable key, which is scoped to the Rivet Engine API at `api.rivet.dev` and will 401 against this endpoint. + +Substitute `$CLOUD_API_URL` (typically `https://cloud-api.rivet.dev`), `$PROJECT`, `$ORG`, `$CLOUD_NAMESPACE`, and `$CLOUD_TOKEN`. + +Poll every 5 seconds until `status` is `ready`. Stop and investigate if `status` is `error`. + +```bash +curl -s "$CLOUD_API_URL/projects/$PROJECT/namespaces/$CLOUD_NAMESPACE/managed-pools/default?org=$ORG" -H "Authorization: Bearer $CLOUD_TOKEN" +``` + +## Checking Logs + +Use the CLI to read your deployment's logs. By default `rivet logs` prints the last 100 lines from the `production` namespace, oldest to newest, then exits. + +```bash +npx @rivetkit/cli logs +``` + +The CLI resolves your token the same way `deploy` does (the `--token` flag, then the `RIVET_CLOUD_TOKEN` environment variable, then `~/.rivet/credentials`). + +### Follow logs live + +Pass `--follow` (`-f`) to stream new logs as they arrive instead of fetching history: + +```bash +npx @rivetkit/cli logs --follow +``` + +Each formatted line is printed as ` [] `: + +``` +2026-06-16T18:26:51.160Z [INFO] eu-central-1 server listening on port 3000 +2026-06-17T11:24:20.425Z [ERROR] us-east-1 failed to connect to upstream +``` + +A few examples: + +```bash +# Last 200 lines from a specific namespace +npx @rivetkit/cli logs --namespace production -n 200 + +# Live tail, only lines containing "error" +npx @rivetkit/cli logs --follow --contains error + +# JSON output piped to jq +npx @rivetkit/cli logs -n 50 --json | jq . +``` + +## Troubleshooting + +If the status stays in **Initializing** for more than a few minutes, verify that: + +- The `RIVET_CLOUD_TOKEN` secret is correctly set in your GitHub repository +- The GitHub Actions workflow completed without errors — check the run logs + +If the status shows **Error**, check that your container starts successfully and does not exit immediately (you can check this with container logs). Common causes: + +- The server file is not calling `registry.start()` +- A runtime crash on startup — test the image locally with `docker run` +- The server is not listening on the `RIVET_PORT` environment variable (RivetKit reads `RIVET_PORT`, defaulting to `3000`) + +## Pricing + +You are billed for the compute resources your deployment uses while it is running. + +_Source doc path: /docs/deploy/rivet-compute_ diff --git a/.agents/skills/multiplayer-game/reference/deploy/supabase.md b/.agents/skills/multiplayer-game/reference/deploy/supabase.md new file mode 100644 index 0000000..fa98436 --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/deploy/supabase.md @@ -0,0 +1,50 @@ +# Deploying to Supabase Functions + +> Source: `src/content/docs/deploy/supabase.mdx` +> Canonical URL: https://rivet.dev/docs/deploy/supabase +> Description: Deploy an existing Rivet project to Supabase Edge Functions. + +--- +This guide covers deploying an existing Rivet project to Supabase Edge Functions. + +## Prerequisites + +- [Supabase project](https://supabase.com/) +- [Supabase CLI](https://supabase.com/docs/guides/cli) configured for your project +- A Rivet namespace from the [Rivet Dashboard](https://dashboard.rivet.dev/) or a self-hosted Rivet Engine + +## Steps + +### Set up your project + +Follow the [Supabase Functions Quickstart](/docs/actors/quickstart/supabase) to set up your project locally. + +### Set Secrets + +Set your Rivet connection values as Supabase secrets. Find `RIVET_ENDPOINT` and `RIVET_PUBLIC_ENDPOINT` in the [Rivet Dashboard](https://dashboard.rivet.dev/) under **Settings → Namespace → Advanced → Backend Configuration** and copy them in. + +```sh +npx supabase secrets set \ + RIVET_ENDPOINT=https://your-namespace:sk_...@api.rivet.dev \ + RIVET_PUBLIC_ENDPOINT=https://your-namespace@api.rivet.dev +``` + +### Deploy + +Make sure the function has the `deno.json` import map from the [quickstart](/docs/actors/quickstart/supabase) that points `rivetkit` at `@rivetkit/supabase`. It keeps the deploy to the WebAssembly runtime; without it the deploy pulls Rivet's native engine and fails with a `413` (request too large). + +```sh +npx supabase functions deploy rivet +``` + +### Register the Serverless Runner URL + +After deploy, set the function URL with the `/api/rivet` path as the serverless runner URL in Rivet. For a function named `rivet`, this is usually `https://your-project.functions.supabase.co/functions/v1/rivet/api/rivet`. + +## Related + +- [Supabase Functions Quickstart](/docs/actors/quickstart/supabase) +- [Deploying to Cloudflare Workers](/docs/deploy/cloudflare) +- [SQLite](/docs/actors/sqlite) + +_Source doc path: /docs/deploy/supabase_ diff --git a/.agents/skills/multiplayer-game/reference/deploy/vercel.md b/.agents/skills/multiplayer-game/reference/deploy/vercel.md new file mode 100644 index 0000000..e8b5ba3 --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/deploy/vercel.md @@ -0,0 +1,138 @@ +# Deploying to Vercel + +> Source: `src/content/docs/deploy/vercel.mdx` +> Canonical URL: https://rivet.dev/docs/deploy/vercel +> Description: Deploy your Next.js Rivet app to Vercel. + +--- +This guide assumes a Next.js app. + +## Prerequisites + +- [Vercel account](https://vercel.com/) +- A Next.js Rivet app +- Access to the [Rivet Cloud](https://dashboard.rivet.dev/) or a [self-hosted Rivet Engine](/docs/general/self-hosting) + +## Steps + +### Set up your project + +Follow the [Next.js Quickstart](/docs/actors/quickstart/next-js) to set up your project. + +### Verify Your Project Structure + +Your Next.js project should have the following structure: + +- `src/app/api/rivet/[...all]/route.ts`: RivetKit route handler +- `src/rivet/registry.ts`: Actor definitions and registry + +The route handler sets `maxDuration` to extend the serverless function timeout so long-lived actor requests are not cut short: + +```ts src/app/api/rivet/[...all]/route.ts @nocheck +import { toNextHandler } from "@rivetkit/next-js"; +import { registry } from "@/rivet/registry"; + +export const maxDuration = 300; + +export const { GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS } = toNextHandler(registry); +``` + +### Set Environment Variables + +Set `RIVET_ENDPOINT` and `RIVET_PUBLIC_ENDPOINT` in your Vercel project settings using the URL auth format: + +``` +RIVET_ENDPOINT=https://my-namespace:sk_****@api.rivet.dev +RIVET_PUBLIC_ENDPOINT=https://my-namespace:pk_****@api.rivet.dev +``` + +`RIVET_ENDPOINT` uses the secret token for server-side access. `RIVET_PUBLIC_ENDPOINT` uses the publishable token and tells the metadata endpoint what connection info to provide to clients. + +### Deploy to Vercel + +1. Connect your GitHub repository to Vercel +2. Vercel will deploy your app + +### Configure Preview Deployments (Recommended) + +Add a GitHub action to automatically create isolated Rivet namespaces for each PR: + +1. Add these secrets to your GitHub repository: + - `RIVET_CLOUD_TOKEN`: Get from [Rivet Dashboard](https://dashboard.rivet.dev) → Settings → Advanced → Cloud API Tokens + - `VERCEL_TOKEN`: Get from [Vercel Account Settings](https://vercel.com/account/tokens) + +2. Create `.github/workflows/rivet-preview.yml`: + +```yaml +name: Rivet Preview + +on: + pull_request: + types: [opened, synchronize, reopened] + push: + branches: [main] + +concurrency: + group: rivet-preview-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + rivet-preview: + runs-on: ubuntu-latest + permissions: + pull-requests: write + steps: + - uses: rivet-dev/preview-namespace-action@v1 + with: + platform: vercel + rivet-token: ${{ secrets.RIVET_CLOUD_TOKEN }} + vercel-token: ${{ secrets.VERCEL_TOKEN }} +``` + +## Troubleshooting + +``` +Error: ENOENT: no such file or directory, mkdir '.../rivetkit/.../state' +``` + +**Cause:** The `RIVET_ENDPOINT` environment variable is not configured. RivetKit falls back to the file system driver, which fails in Vercel's read-only serverless environment. + +**Solution:** Ensure `RIVET_ENDPOINT` is set with your Rivet endpoint using the URL auth format: + +``` +RIVET_ENDPOINT=https://my-namespace:sk_****@api.rivet.dev +``` + +If using the [preview-namespace-action](https://github.com/rivet-dev/preview-namespace-action), this is configured automatically. + +The `/api/rivet/metadata` endpoint returns data but `clientEndpoint`, `clientNamespace`, and `clientToken` are missing. + +**Cause:** The `RIVET_PUBLIC_ENDPOINT` environment variable is not configured. This tells the metadata endpoint what connection info to provide to clients. + +**Solution:** Set `RIVET_PUBLIC_ENDPOINT` with the publishable token (for client access): + +``` +RIVET_PUBLIC_ENDPOINT=https://my-namespace:pk_****@api.rivet.dev +``` + +If using the [preview-namespace-action](https://github.com/rivet-dev/preview-namespace-action), this is configured automatically. + +Rivet fails to connect to your Vercel deployment with a 401 error mentioning "Authentication Required". + +**Cause:** [Vercel Deployment Protection](https://vercel.com/docs/security/deployment-protection) is blocking requests from Rivet. + +**Solution:** + +1. Create a bypass secret in your Vercel project settings +2. In Rivet, go to **Settings > Providers** +3. Click the three dots on your provider and select **Edit** +4. Click **Add Header** and add `x-vercel-protection-bypass` with your bypass secret + +If using the [preview-namespace-action](https://github.com/rivet-dev/preview-namespace-action), this is configured automatically. + +## Related + +- [Next.js Quickstart](/docs/actors/quickstart/next-js) +- [Self-Hosting](/docs/general/self-hosting) + +_Source doc path: /docs/deploy/vercel_ diff --git a/.agents/skills/multiplayer-game/reference/deploy/vm-and-bare-metal.md b/.agents/skills/multiplayer-game/reference/deploy/vm-and-bare-metal.md new file mode 100644 index 0000000..6a123f9 --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/deploy/vm-and-bare-metal.md @@ -0,0 +1,96 @@ +# Deploying to VMs & Bare Metal + +> Source: `src/content/docs/deploy/vm-and-bare-metal.mdx` +> Canonical URL: https://rivet.dev/docs/deploy/vm-and-bare-metal +> Description: Deploy your RivetKit app to any Linux VM or bare metal host. + +--- +## Steps + +### Prerequisites + +- A Linux VM or bare metal server with SSH access +- Your RivetKit app + - If you don't have one, see the [Quickstart](/docs/actors/quickstart) page or our [Examples](https://github.com/rivet-dev/rivet/tree/main/examples) +- Access to the [Rivet Cloud](https://dashboard.rivet.dev/) or a [self-hosted Rivet Engine](/docs/general/self-hosting) + +### Upload Your App + +- Build your RivetKit app locally +- Copy the build output to your server (example): + +```bash +scp -r ./dist user@server:/opt/rivetkit-app +``` + +Place the files somewhere readable by the service user, such as `/opt/rivetkit-app`. + +### Set Environment Variables + +After creating your project on the Rivet dashboard, select VM & Bare Metal as your provider. You'll be provided `RIVET_ENDPOINT` and `RIVET_PUBLIC_ENDPOINT` environment variables to use in the next step. + +### Create the systemd Service + +Create `/etc/systemd/system/rivetkit-app.service`: + +```ini +[Unit] +Description=RivetKit App +After=network.target + +[Service] +Type=simple +WorkingDirectory=/opt/rivetkit-app +ExecStart=/usr/bin/node server.js +Restart=on-failure +Environment=RIVET_ENDPOINT= +Environment=RIVET_PUBLIC_ENDPOINT= + +[Install] +WantedBy=multi-user.target +``` + +Replace the environment values with those from the Rivet dashboard and adjust paths to match your deployment. + +### Start the Service + +Reload systemd units and start the service: + +```bash +sudo systemctl daemon-reload +sudo systemctl enable --now rivetkit-app.service +``` + +### Connect to Rivet + +1. Ensure your server is accessible via a public URL (e.g. `https://my-app.example.com`) +2. On the Rivet dashboard, paste your URL with the `/api/rivet` path into the connect form (e.g. `https://my-app.example.com/api/rivet`) +3. Click "Done" + +If you front your app with a reverse proxy or load balancer (NGINX, HAProxy, Caddy, AWS ALB/NLB, etc.), raise its idle / read timeout to at least 1 hour (`3600` seconds). Rivet envoys connect to your app over long-lived WebSockets, and clients (browsers, SDKs) do the same. Default proxy timeouts (typically 30 to 60 seconds) drop these connections and cause reconnect storms. + +Examples: + +- **NGINX**: `proxy_read_timeout 3600s;` and `proxy_send_timeout 3600s;` in the relevant `location` block. +- **HAProxy**: `timeout client 1h` and `timeout server 1h` in the matching frontend / backend. +- **AWS ALB / NLB**: set the load balancer attribute `idle_timeout.timeout_seconds = 3600`. + +## Operating + +### Restart + +Restart the service after deploying new builds or environment changes: + +```bash +sudo systemctl restart rivetkit-app.service +``` + +### Logs + +Follow realtime logs when debugging: + +```bash +sudo journalctl -u rivetkit-app.service -f +``` + +_Source doc path: /docs/deploy/vm-and-bare-metal_ diff --git a/.agents/skills/multiplayer-game/reference/general/actor-configuration.md b/.agents/skills/multiplayer-game/reference/general/actor-configuration.md new file mode 100644 index 0000000..72bc6fd --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/general/actor-configuration.md @@ -0,0 +1,19 @@ +# Actor Configuration + +> Source: `src/content/docs/general/actor-configuration.mdx` +> Canonical URL: https://rivet.dev/docs/general/actor-configuration +> Description: This page documents the configuration options available when defining a RivetKit actor. The actor configuration is passed to the `actor()` function. + +--- +## Basic Example + +## Configuration Reference + +## Related + +- [Registry Configuration](/docs/general/registry-configuration): Configure the RivetKit registry +- [State](/docs/actors/state): Managing actor state +- [Actions](/docs/actors/actions): Defining actor actions +- [Lifecycle](/docs/actors/lifecycle): Actor lifecycle hooks + +_Source doc path: /docs/general/actor-configuration_ diff --git a/.agents/skills/multiplayer-game/reference/general/architecture.md b/.agents/skills/multiplayer-game/reference/general/architecture.md new file mode 100644 index 0000000..3da2912 --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/general/architecture.md @@ -0,0 +1,452 @@ +# Architecture + +> Source: `src/content/docs/general/architecture.mdx` +> Canonical URL: https://rivet.dev/docs/general/architecture +> Description: - rivetkit is the typescript library used for both local development & to connect your application to rivet - a rivetkit instance is called a "runner." you can run multiple runners to scale rivetkit horizontally. read omre about runners below. + +--- +## 3 ways of running + +### rivetkit + +- rivetkit is the typescript library used for both local development & to connect your application to rivet +- a rivetkit instance is called a "runner." you can run multiple runners to scale rivetkit horizontally. read more about runners below. + +#### local development + +- in local development, rivetkit provides a full actor environment for single-node deployments + +#### drivers + +- rivetkit supports multiple drivers. currently supports: file system (default in local dev), memory, rivet engine (used for rivet cloud & self-hosting), cloudflare durable objects (does not rely on rivet engine) +- drivers are very flexible to enable you to write your actors once and plug in to any system that fits your architecture adequately +- see the driver interface + - actordriver https://github.com/rivet-dev/rivet/blob/eeb01fc4d9ca0e06f2e740d267bd53280ca7330e/rivetkit-typescript/packages/rivetkit/src/actor/driver.ts + - managerdriver https://github.com/rivet-dev/rivet/blob/eeb01fc4d9ca0e06f2e740d267bd53280ca7330e/rivetkit-typescript/packages/rivetkit/src/manager/driver.ts + +### rivet cloud + +- provides multi-region and highest performance out of the box +- accessible at dashboard.rivet.dev and the api is avialble at api.rivet.dev + +### rivet self-hosted + +- available as a standalone rust binary or a docker contianer +- can be configured to persist to postgres or rocksdb +- can scale horiziontally across multipe nodes and can scale across multiple regions +- see [self-hosting docs](/docs/self-hosting/) + +## actors + +- Actors for long-lived processes with durable state, realtime, and hibernate when not in use. read more about actors at a high level at (link to actors/index) + +### actor-per-entity + +- actors are designed to have an actor-per-entity +- you can think about actors a bit like objects in object-oriented programming where each is responsible for their own state and expose methods (ie actions in our case) +- examples include + - actor per user + - actor per user session + - actor per document + - actor per game room + - actor per tenant + - actor per rate limit topic + +### architecting for scale + +- actors scale by: + - having isolated state to each actor that combines compute and storage for in-memory reads and writes + - communication is standardized based on actions & events + - scale horizontally +- read more about scaling at [Design Patterns](/docs/actors/design-patterns) + +### horizontal scaling + +- actors can run across multiple rivetkit runners. this is orchestrated by rivetkit itself. + +### lifecycle + +actors have create, destroy, wake, and sleep lifecycle hooks that you can implement to modify behavior. see the lifecycle docs for reference on actor lifecycel hook sequences + +### actor sleeping + +- actors sleep when not in use +- an actor is considered not in use when there are no active network connections to the actor (or the network connections are hibernatable websockets, see below) and there are no actions in flight +- actors have a sleep timeout (configured in `options.sleepTimeout`) that decides how long to keep the actor in memory with no recent activity +- sleep can be held off for the lifetime of a promise with `c.keepAwake(promise)` +- see the [sleeping docs](/docs/actors/lifecycle#sleeping) for full details + +### wake events + +- actors can wake to any of the follwoing events: + - network requests + - websocket messages + - alarms (see scheduling docs) + +### live actor migration + +- live actor migrations lets your application ugprade, crash, or hot reload cahnges without interruption to your user or application (including websockets) +- this is powered by hibernating websockets for live websocket migraiton & our fault tolerance mechanism (read more below) + +### coldstart performance + +- actors have negligible coldstart performance. the code to run the actor is already started (ie the runner), so creating/starting an actor is incredibly cheap. +- creating new actors with a key requires some overhead to communicate with other regions in order to reserve the actor's key (see below). actors can be created without keys with near-0 latency. + +### multi-region, globally unique actor keys + +- actors can optionally have a globally unique "key" +- when creating an actor with a key +- this system is highly optimized to reduce wan round trips using per-key Paxos with a custom database called Epoxy (https://github.com/rivet-dev/rivet/tree/main/engine/packages/epoxy) +- limitation: when creating an actor with a given key, that key will always be pinned to that region even if the actor is destroyed. creating a new actor with the same key will always live in the same region. +- see the actors keys document + +### input + +- actors have input data that can be passed to them when constructed +- this is similar to apssing data to a constructor in an object + +### generic parameters + +actor definitions include the following generic parameters that you'll see frequently in the code: + +- state +- conn state +- conn params +- ephemeral variables +- input data +- (experimental) database connector + +### persistence + +- state automatically flushes to storage intelligently +- to force a state flush and wait for it to finish, call (TODO: look this up in state document) +- read more about state persistence in the state document (link to document) +- state is stored in the same place as where the actor lives. loading an actor in to memory has comparable performance to network attached storage, and once in memory, has performance of any standard in-memory read/write like a variable. + +### scheduling & alarms + +- actors have a scheduling api to be able to wake up at any time in the indefinite future +- think of this like setTimeout but without a max timeout +- rivet is responsible for waking the actor when this timeout wakes + +### ephemeral variables + +- actors have the ability to create ephemrla variables for things that you do not want to be persisted with the actor's state +- this is useful for non-serializable data like a utility class like a pubsubs erver or something (TODO extra info) +- link to ephemeral variables docs + +### actions + +- for stateless clients, actions are sent as http requests via `POST /gateway/{actor id}/actions/{action name}` +- for stateful clients, actions are sent as websocket messages + +### events & subscriptions + +- events are sent as websocket messages + +### error handling + +- this is different than fault tolerance: + - error handling is a user error + - fault tolerance is something goes wrong that your applciation was not built to handle (ie hard crash, oom, network error) +- rivet provdies a special UserError class to throw custom errors that will be returned to the client +- all other errors are returned as a generic "internal error" +- this is becuase leaking error deatils is a common security hole, so we default to expose-nothing errors + +### logging + +- rivet uses pino for logging +- we expose a scoped child logger for each actor at `c.log` that automatically logs the actor id + key +- this allows you to search lgos easily by actor id without having to log the actor id frequently +- logs can be configured via the `RIVET_LOG_LEVEL` env var + +### fault tolerance + +- actors are fault tolerant, meaning that the host machine can crash and the actors will proceed to operate as if nothing happened +- runners maintain a socket with rivet engine. when this socket closes or takes to long to ping, actors will reschedule +- hibernating websockets (enabled by default) will live-migrate to the new actor as if nothing happened + +### crash policy + +- there are 3 crash policies: sleep, restart, and destroyed + - sleep (default, usually the option you want): + - when to use: actors that need high-performance in-memory logic. + - when not to use: you need this actor running at all times no matter what, even if idle + - examples: (list commone xamples) + - destroy: + - when to use: actors that need to run once until completion. on crash, do not try to reschedule. + - when not to use: if you want your actor to have fault tolerance and be able to run transaprenlty to the underlying runner + - examples: batch jobs, image conversions, ephemeral jobs, (TODO come up with better eaxmples) + - restart: + - when to use: actors that should be running at all times + - when not to use: if you don't absolutely need something running at all times, since this consumes needless compute resources. considure using the scheduling api instead. + - examples: maintain outbound sockets, daemons, always-running jobs, (TODO come up with better examples) + +the behavior for each is described below: + +| Event | Restart | Sleep | Destroy | +|------------------------------|--------------|--------------|--------------| +| Graceful exit (StopCode::Ok) | Destroy | Destroy | Destroy | +| Crash (non-Ok exit) | Reschedule | Sleep | Destroy | +| Lost (runner disappeared) | Reschedule | Sleep | Destroy | +| Lost + force_reschedule | Reschedule | Reschedule | Reschedule | +| GoingAway (runner draining) | Reschedule | Sleep | Destroy | +| No capacity (allocation) | Queue (wait) | Sleep | Queue (wait) | +| No capacity + serverless | Queue (wait) | Queue (wait) | Queue (wait) | +| Wake signal (while sleeping) | Reschedule | Reschedule | Reschedule | + +### inspector + +- actors provide an inspector api to implement the: + - repl + - state read/write + - network inspector + - event log +- this is impelmented over a websocket over bare + +### http api + +- see the http api document on actors + +### multi-region + +- actors can be scheduled across multiple regions +- each actor has an actor id which embeds which region it lives in +- networking is automatically routed to the region that an actor lives in +- limitation: actors curretnly cannot migrate across regions + +### backpressure + +#### no runner capacity + +- this is how actors with different crash policies behave when when there's backpressure: + - sleep = sleeps (sheds load by not rescheduling) + - restart = queues + - destroy = queues +- see the above matrix for more details on actor crash policy on how it handles no capacity. + +- the actor queue is built to withstand high amounts of backpressure on rivet, so queueing actors is fine here +- a large queue means it'll take more time for your application to process the queue to catch up with demand when it comes online. + +#### per-actor cpu & networking exhaustion + +- actors are isolated, so they each have their own individual bottleneck. you can think of this like a process thread where each thread can only do so much. +- there is no durable message queue/"mailbox" for actors. if the actor cannot respond in time, then the request is dropped. +- if an actor exhauses its cpu or networking, then the runner +- returns service unavailble (503) if the actor fails to respond to a request in time +- there is no hard cap on the networking or cpu usage for each actor at the moment +- if your actor is resource intensive, it's common to use a separate mailbox actor to act as a queue + +## runners + +### regular vs serverless runners + +there are 2 types of runners: + +- regular: these are standard nodejs processes connected to rivet that rivet can orchestrate actors to and send network requests to at any time +- serverless: rivet works with serverless platforms. when an actor is created, it has a request-per-actor model where it opens a long-running request on the serverless platofrm to run a given actor. + +### runner pool + +- runners are pooled together by sharing a common name (ie "default") +- when an actor is created, it chooses the pool by selecting the runner name to run on +- rivet will automatically load balance actors across these runners + +### runner key + +- not relevnat for serverless runners +- each runner has a unique key that it provides when connecting. keys are unique to the instace the runner is running on and should be the same if the runner is restarted. +- this can be the: machine's ip, k8s pod name, etc +- if there is an existing runner connected with a given key, the runner will disconnect the old runner and replace it +- rivet is designed to handle network partitions by waiting for runners to miss a ping, indicating it's no longer alive. however, often times runners restart immediately after a hard crash and reconnect. in this case, the runner will reconnect on restart and terminate the old runner in order to prevent further actors from scheduling to the crashed runner. + +### capacity + +- not relevnat for serverless runners +- each runner can be assigned a capacity of how many actors it can run +- rivet will schedule with spread (not binpacking) in order to spread load over actors + +#### usefulness of capacity = 1 + +- setting a capacity of 1 is helpful for situations where you have cpu-intensive apps that should not run with any other actors +- examples include game servers, ffmpeg jobs, etc + +### versions & upgrading code + +- each runner has a version index +- actors are always scheduled to the highest verison index (see runner priority below) +- this means that when a new runner is deployed: + 1. runners with higher index come online + 2. actors schedule to the highest index, stop scheduling to the older index + 3. old index runners start draining and migrating actors to new index + 4. all old runners are now shut down +- websocekts are live migrated to the new version when upgrading using hibernating websockets to users see no hiccup in their applications +- this is important because actors should never downgrade their runner. they should always move to a newer version of code in order to prevent corruption. + +### runner scheduling prioroty + +- actors are scheduled to runners sorted by priority of (version DESC, remaining capacity ASC) + +### multi-region + +TODO + +### shutdown sequence + +- runner shutdown is important to ensure that actors do not get unexpectedly terminated when either: + - upgrading your applciation and taking down old pods + - scaling down your runners horizontally (ie from an hpa) + - pressing ctrl-c when in development +- on shutdown: + 1. tell rivet the runner is stopping + 2. rivet tells all the actors on this runner to migrate + 3. runner waits for all actors to finish migrating + 4. runner exits process + +### reconnection + +- runners can handle temporary network partitions +- they'll automatically reconnect and replay missed commands/events between rivet and the runner +- this happens transparenlty to the user +- if disconnected for too long (indicating a network partition), the runner will shut itself down and exit + +### autoscaling + +- not relevant to serverless +- runners currently autoscale on cpu. more intelligent scaling is coming soon. +- tune your runner total slots capacity accordingly +- it's up to you to configure your hpa/etc to work like this. see the Connect guides (link to index page) for reference on hwo to configure this. + +### serverless timeouts + +- serverless runners take in to account the maximum run duration of the serverless platform +- the runners will mgirate actors to a new request before the request times out +- this is completely transparent to you and the user because of the fault tolerance and websocket migraiton characteristics +- it's common for actors to go sleep before hitting the serverless timeout + +## networking + +### web standards + +- everything in rivet is built on webstandards by default +- nothing in rivet requires you to use our sdk, our sdks are meant to be a convenience. it's built to be as easy to use raw http/websocket endpoints from scratch. +- actions, events, etc are all built on simple, well-documented http/websocket under the hood (link to openapi & asyncapi docs). +- you can use low-level request handlers (lnk to dock) and low-level weboscket handlers (link to doc) to handle low-level primtivies yourself + +### encoding + +- rivetkit's action/events api supports communicating via [VBARE](link to github repo, see the blog post for the link), CBOR, or JSON + - VBARE: high-perofrmance & compact, optimal use case + - CBOR: descent encoding/decoding perf + portable libraries, good for implemnting high-ish performance on other platforms + - JSON: good for fast implementations & debugging (easy to read) + +### tunneling + +- when a runner connects it opens a tunnel to rivet to allow incoming traffic + - this is simila to systems like tailscale, ngrok, or other vpns + - we do this for security & configuraiton simplicity since it means that you don't have to manage exposing your rivetkit applications' networkig to rivet. instead, anything that can open a socket to rivet can accept inbound traffic to actors. + +### gateway + +- incoming traffic to actors come to the Rivet gateway and are routed to the appropriate runner +- the rivet gateway automatically handles: + - multi-region routing to route traffic to the correct reigon for an actor + - automatically waking the actor if needed + - sending traffic over the runner + +### hibernating websockets + +- hibernating web sockets are a core component of live actor migration & fault tolerance. it allows us to maintain an open websocket while the actor crashes, upgrades, or moves to another runner. +TODO: copy the rest of this from low-level webosckets document and rephrase + +### actor health endpoin + +- actors provide a simple, utility health endpoint at `/health` that lets you check if your actor is reachable (e.g. `curl https://api.rivet.dev/gateway/{actor id}/health`) + +## multi-region + +### networking + +- actors may live in different regions than inbound requests +- Rivet uses the Epoxy (link again) system to handle global routing to route traffic to the correct region with high performance +- this is completely transparent to you. your app sends traffic to https://api.rivet.dev/gateway/* and it automatically routes to the correct actor in the appropriate region + +### globally unique actor keys + +- actor keys are globally unique to be able to benefit from multi-region capabilities without any extra work +- see more about globally uniuqe actor keys above +- see the actor keys document + +### regional endpoints + +- each reigon has a regional endpoint +- this endpoint is used specifically for connecting runners (for example https://api-us-east-1.rivet.dev), opt to use api.rivet.dev for all other traffic +- runners are required to connect to the regional endpoints + - this is because runners are sensitive to latency to the rivet regional datacenter + - we add datacenters regularly so each runner needs to be pinned to a single datacenter in order to ensure your availble datacneter list doesn't change sporadically without your consent + +### persistence + +- data is always persisted in the same region that is written +- this is important for minimal coldstarts & data locality laws + +## namespaces + +- rivet provides namespaces to run multiple actor systems in isolation +- this makes it really easy to have prod/staging environments or completely different applications running on the same rivet instance +- when you connect to rivet, you can specify which namespace you're connecting to +- self-hotsed rivet defaults to namespace "default" +- rivet cloud provdies isolated tokens for each namespace + +## manager api + +- rivet provides a standard rest api for managing actors + - useful endoints include: + - get /actors + - delete /actors/{} + - get /actors/names -> get all actor types available + +## comparison to prior art for actors + +### runtime + +- there are very few serious actor implementation targeted at the javascirpt eocsystem. rivet is arguably the most serious open-source actor implementation for typescript out there. + +### library vs orchestrator + +- some actor systems opt to be purely a library while rivet opts to have an orchestrator (i.e. the single rust binary) +- this lets us to a lot of things other actor systems can't: + - separating orchestration, persistence, and proxy lets us isoalte the core to be incredibly reliable while the fast-changing applications that ocnnect to rivet can be more error-prone safely. with a library, the blast radius of your application also affects the entire actor system. + - support for serverless platforms to benefit from cost, multi-region, blitz scaling, and relibaiblity benefits + - optimize fault tolerance since we can make more assumtions about application state when the rivet core does not crash and your app does + +### scheduling + +actors is a loose term, but there are generally 2 types of schedulign in practice: + +- ephemeral actors + - examples: erlang/otp, akka, swift + - provides no persistence or sleeping mechanism by defualt + - relies on supervisors for managing persistence +- [virtual actors](https://www.microsoft.com/en-us/research/project/orleans-virtual-actors/) + - an extension of the actor pattern that provides actors that can hibernate ("sleep") when not in use + - examples: orleans, dapr, durable objects + +rivet has similarities with both to provide more flexibility: + +- crash policies provdie for 3 types of actors: + - sleep -> most similar to virutal actors + - restart -> most similar to ephemeral actors but with a supervisor to auto-restart, however still has a durable queue ot handle backpressure + - crash -> most similar to traditional actors but with no supervisor to restart, however still has a durable queue to handle backpressure + +### communication + +- many actor frameworks use inbox patterns (think: queue-per-actor) to handle sending messages between actors +- there is no callback mechanims, instead you need to send messages back to the actual actor +- rivet opts to behave like web standards instead of using the message pattern + - actors can impelment the inbox pattern optionally + - but we provide lower-level networking to be able to be compatible with more techniologies + - rivet assumes the same serial concurerntly that other actors do (by the nature of javascript being single-threaded) but we allow you to run promises in parallel or handl eyour own concurrency control (which some other actor frameworks might require a spawning new actor to do) + +_Source doc path: /docs/general/architecture_ diff --git a/.agents/skills/multiplayer-game/reference/general/cors.md b/.agents/skills/multiplayer-game/reference/general/cors.md new file mode 100644 index 0000000..330fe38 --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/general/cors.md @@ -0,0 +1,18 @@ +# Cross-Origin Resource Sharing + +> Source: `src/content/docs/general/cors.mdx` +> Canonical URL: https://rivet.dev/docs/general/cors +> Description: Cross-Origin Resource Sharing (CORS) controls which origins (domains) can access your actors. When actors are exposed to the public internet, proper origin validation is critical to prevent security breaches and denial of service attacks. + +--- +Unlike stateless HTTP APIs that use CORS headers, Rivet Actors are stateful and support persistent WebSocket connections. Since WebSockets don't natively support CORS, we validate origins manually in the `onBeforeConnect` hook before connections may open. + +## Implementing Origin Restrictions + +To implement origin restrictions on Rivet Actors, use the `onBeforeConnect` hook to verify the request. + +To catch the error on the client, use the following code: + + See tracking issue for [configuring CORS per-actor on the gateway](https://github.com/rivet-dev/rivet/issues/3539) that will remove the need to implement origin restrictions in `onBforeRequest`. + +_Source doc path: /docs/general/cors_ diff --git a/.agents/skills/multiplayer-game/reference/general/docs-for-llms.md b/.agents/skills/multiplayer-game/reference/general/docs-for-llms.md new file mode 100644 index 0000000..db0c216 --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/general/docs-for-llms.md @@ -0,0 +1,51 @@ +# Documentation for LLMs & AI + +> Source: `src/content/docs/general/docs-for-llms.mdx` +> Canonical URL: https://rivet.dev/docs/general/docs-for-llms +> Description: Rivet provides optimized documentation formats specifically designed for Large Language Models (LLMs) and AI integration tools. + +--- +## Skills (Recommended) + +For AI coding assistants like Claude Code, Cursor, or Windsurf, install Rivet skills for the best development experience: + +```sh +npx skills add rivet-dev/skills +``` + +Skills provide your AI assistant with Rivet-specific knowledge, best practices, and code patterns directly in your project context. + +## Available Formats + +### `llms.txt` (Condensed) +A condensed version of the documentation perfect for quick reference and context-aware AI assistance. + +**Access:** /llms.txt + +This format includes: +- Key concepts and features +- Essential getting started information +- Summaries of main functionality +- Optimized for token efficiency + +### `llms-full.txt` (Complete) +The complete documentation in a single file, ideal for comprehensive AI assistance and in-depth analysis. + +**Access:** /llms-full.txt + +This format includes: +- Complete documentation content +- All examples and detailed explanations +- Full API references and guides +- Suitable for complex queries and comprehensive understanding + +## Access Pages As Markdown + +Each documentation page is also available as clean markdown by appending `.md` to any documentation URL path. + +For example: + +- Original URL: `https://rivet.dev/docs/actors` +- Markdown URL: `https://rivet.dev/docs/actors.md` + +_Source doc path: /docs/general/docs-for-llms_ diff --git a/.agents/skills/multiplayer-game/reference/general/edge.md b/.agents/skills/multiplayer-game/reference/general/edge.md new file mode 100644 index 0000000..814dde2 --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/general/edge.md @@ -0,0 +1,24 @@ +# Edge Networking + +> Source: `src/content/docs/general/edge.mdx` +> Canonical URL: https://rivet.dev/docs/general/edge +> Description: Actors automatically run near your users on your provider's global network. + +--- +At the moment, edge networking is only supported on Rivet Cloud & Cloudflare Workers. More self-hosted platforms are on the roadmap. + +## Region selection + +### Automatic region selection + +By default, actors will choose the nearest region based on the client's location. + +Under the hood, Rivet and Cloudflare use [Anycast routing](https://en.wikipedia.org/wiki/Anycast) to automatically find the best location for the client to connect to without relying on a slow manual pinging process. + +### Manual region selection + +The region an actor is created in can be overridden using region options: + +See [Create Manage Actors](/docs/actors/communicating-between-actors) for more information. + +_Source doc path: /docs/general/edge_ diff --git a/.agents/skills/multiplayer-game/reference/general/endpoints.md b/.agents/skills/multiplayer-game/reference/general/endpoints.md new file mode 100644 index 0000000..5c376e1 --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/general/endpoints.md @@ -0,0 +1,98 @@ +# Endpoints + +> Source: `src/content/docs/general/endpoints.mdx` +> Canonical URL: https://rivet.dev/docs/general/endpoints +> Description: Configure how your backend connects to Rivet and how clients reach your actors. + +--- +## Local Development + +No configuration is needed for local development. RivetKit runs entirely on your local machine without any extra configuration to run Rivet Actors. + +## Production Deployment + +When deploying to production, you need to configure endpoints so your backend can communicate with Rivet Engine and clients can reach your actors. + +Diagram showing Client connecting to Rivet, which connects to Your Backend + +### Private Endpoint + +The private endpoint tells your backend where to find the Rivet Engine. + +### Environment Variable + +```bash +RIVET_ENDPOINT=https://my-namespace:sk_xxxxx@api.rivet.dev +``` + +### Config + +### Public Endpoint + +The public endpoint tells clients where to connect to reach your actors. + +This endpoint and token will be exposed to the internet. Use a public token (`pk_`), not your secret token (`sk_`). + +The public endpoint is only required if using the [serverless runtime mode](/docs/general/runtime-modes#runners) and if you have a frontend using RivetKit. + +### Environment Variable + +```bash +RIVET_PUBLIC_ENDPOINT=https://my-namespace:pk_xxxxx@api.rivet.dev +``` + +### Config + +## Advanced + +### URL Auth Syntax + +Endpoint URLs support embedding namespace and token directly in the URL: + +``` +https://namespace:token@host/path +``` + +This is the recommended approach for simplicity. Alternatively, you can use separate environment variables: + +```bash +RIVET_ENDPOINT=https://api.rivet.dev +RIVET_NAMESPACE=my-namespace +RIVET_TOKEN=sk_xxxxx +``` + +### Security + +In serverless mode, the private endpoint is used to validate that requests to `GET /api/rivet/start` are coming from your trusted Rivet endpoint. If the private endpoint is not configured, anyone can run a self-hosted instance of Rivet and connect to your backend from any endpoint. + +### How Clients Connect + +This flow applies to [serverless runtime mode](/docs/general/runtime-modes#serverless). For [runner runtime mode](/docs/general/runtime-modes#runners) or [clients configured to connect directly to Rivet](/docs/clients/javascript), clients connect directly to Rivet and this metadata flow is not needed. + +When a client connects to your serverless application, it follows this flow: + +1. Client makes a request to `https://my-app.example.com/api/rivet/metadata` +2. Your app returns the public endpoint configuration: + ```json + { + "clientEndpoint": "https://api.rivet.dev", + "clientNamespace": "my-namespace", + "clientToken": "pk_xxxxx" + } + ``` +3. Client caches these values and uses them for subsequent requests +4. Client connects to `https://api.rivet.dev/gateway/{actor}`, which routes requests to your actors + +This indirection exists because Rivet acts as a gateway between clients and your actors. This is because Rivet handles routing, load balancing, and actor lifecycle management of actors. + +## Reference + +| Environment Variable | Config Option | Description | +|---------------------|---------------|-------------| +| `RIVET_ENDPOINT` | `endpoint` | Rivet Engine URL for your backend | +| `RIVET_NAMESPACE` | `namespace` | Namespace for actor isolation | +| `RIVET_TOKEN` | `token` | Authentication token for engine connection | +| `RIVET_PUBLIC_ENDPOINT` | `serverless.publicEndpoint` | Client-facing endpoint | +| `RIVET_PUBLIC_TOKEN` | `serverless.publicToken` | Client-facing token | + +_Source doc path: /docs/general/endpoints_ diff --git a/.agents/skills/multiplayer-game/reference/general/environment-variables.md b/.agents/skills/multiplayer-game/reference/general/environment-variables.md new file mode 100644 index 0000000..2a86e98 --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/general/environment-variables.md @@ -0,0 +1,87 @@ +# Environment Variables + +> Source: `src/content/docs/general/environment-variables.mdx` +> Canonical URL: https://rivet.dev/docs/general/environment-variables +> Description: This page documents all environment variables that configure RivetKit behavior. + +--- +## Connection + +| Environment Variable | Description | +|---------------------|-------------| +| `RIVET_ENDPOINT` | Endpoint URL to connect to Rivet Engine. Supports [URL auth syntax](/docs/general/endpoints#url-auth-syntax). | +| `RIVET_TOKEN` | Authentication token for Rivet Engine | +| `RIVET_NAMESPACE` | Namespace to use (default: "default") | + +## Public Endpoint + +These variables configure how clients connect to your actors. + +| Environment Variable | Description | +|---------------------|-------------| +| `RIVET_PUBLIC_ENDPOINT` | Public endpoint for client connections. Supports [URL auth syntax](/docs/general/endpoints#url-auth-syntax). | +| `RIVET_PUBLIC_TOKEN` | Public token for client authentication | + +## Runner Configuration + +| Environment Variable | Description | +|---------------------|-------------| +| `RIVET_RUNNER` | Runner name (default: "default") | +| `RIVET_RUNNER_VERSION` | Version number for the runner. See [Versions & Upgrades](/docs/actors/versions). | +| `RIVET_RUNNER_KIND` | Type of runner | +| `RIVET_TOTAL_SLOTS` | Total actor slots available (default: 100000) | + +## Engine + +| Environment Variable | Description | +|---------------------|-------------| +| `RIVET_RUN_ENGINE` | Set to `1` to spawn the engine process | +| `RIVET_RUN_ENGINE_HOST` | Host to bind the spawned local engine process to. Defaults to `127.0.0.1`. | +| `RIVET_RUN_ENGINE_PORT` | Port to bind the spawned local engine process to. Defaults to `6420`. | +| `RIVET_RUN_ENGINE_VERSION` | Version of engine to download | + +## Inspector + +| Environment Variable | Description | +|---------------------|-------------| +| `RIVET_INSPECTOR_DISABLE` | Set to `1` to disable the inspector | + +## Metrics + +| Environment Variable | Description | +|---------------------|-------------| +| `_RIVET_METRICS_TOKEN` | Bearer token that gates the per-actor Prometheus `/metrics` endpoint. When unset, `/metrics` is disabled. | + +## Experimental + +| Environment Variable | Description | +|---------------------|-------------| +| `RIVET_EXPERIMENTAL_OTEL` | Set to `1` to enable experimental OTel tracing in Rivet Actors | + +## Storage + +| Environment Variable | Description | +|---------------------|-------------| +| `RIVETKIT_RUNTIME` | Runtime binding to use for RivetKit core: `auto`, `native`, or `wasm`. Defaults to `auto`. | +| `RIVETKIT_STORAGE_PATH` | Overrides the default file-system storage path used by RivetKit when using the default driver. | + +## Lifecycle + +| Environment Variable | Description | +|---------------------|-------------| +| `RIVETKIT_RUNTIME_MODE` | Controls how `registry.start()` runs. Accepted values are `envoy` and `serverless`; any other explicit value errors. Defaults to `envoy`: opens a long-lived WebSocket to the engine (Mode A). Set to `serverless` to bind an HTTP listener via `registry.listen()` (Mode B). | +| `RIVETKIT_PUBLIC_DIR` | Directory of static assets to serve alongside the framework routes when calling `registry.listen()`. Used as a fallback when `opts.publicDir` is not passed. On auto-listen via `registry.start()`, defaults to `/public` when this env var is unset. | +| `RIVET_PORT` | Port the listener binds when calling `registry.listen()` without an explicit `opts.port`. Must be an integer between 1 and 65535. Defaults to `3000`. | + +## Logging + +| Environment Variable | Description | +|---------------------|-------------| +| `RIVET_LOG_LEVEL` | Log level: `trace`, `debug`, `info`, `warn`, `error`, `fatal`, `silent` | +| `RIVET_LOG_TARGET` | Set to `1` to include log target | +| `RIVET_LOG_TIMESTAMP` | Set to `1` to include timestamps | +| `RIVET_LOG_MESSAGE` | Set to `1` to include message formatting | +| `RIVET_LOG_ERROR_STACK` | Set to `1` to include error stack traces | +| `RIVET_LOG_HEADERS` | Set to `1` to log request headers | + +_Source doc path: /docs/general/environment-variables_ diff --git a/.agents/skills/multiplayer-game/reference/general/http-server.md b/.agents/skills/multiplayer-game/reference/general/http-server.md new file mode 100644 index 0000000..c3a386c --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/general/http-server.md @@ -0,0 +1,96 @@ +# HTTP Server + +> Source: `src/content/docs/general/http-server.mdx` +> Canonical URL: https://rivet.dev/docs/general/http-server +> Description: Different ways to run your RivetKit HTTP server. + +--- +## Methods of Running Your Server + +### registry.start() + +The simplest way to run your server. Starts a local RivetKit server, serves static files from a `public` directory, and starts the actor runner: + +Run with `npx tsx --watch index.ts` (Node.js), `bun --watch index.ts` (Bun), or `deno run --allow-net --allow-read --allow-env --watch index.ts` (Deno). Clients connect to the Rivet Engine on `http://localhost:6420`. + +### With Fetch Handlers + +A [fetch handler](https://wintercg.org/) is a function that takes a `Request` and returns a `Response`. This is the standard pattern used by Cloudflare Workers, Deno Deploy, Bun, and other modern runtimes. + +Use `registry.serve()` to get a fetch handler: + +To integrate with a router like [Hono](https://hono.dev/) or [Elysia](https://elysiajs.com/), use `registry.handler()`: + +### Hono + +### Elysia + +Then run your server: + +```bash Node.js +npx tsx --watch server.ts +``` + +```bash Bun +bun --watch server.ts +``` + +```bash Deno +deno run --allow-net --allow-read --allow-env --watch server.ts +``` + +### Explicit HTTP Server + +If you need to explicitly start the HTTP server instead of using the fetch handler pattern: + +### Node.js (Hono) + +Using Hono with `@hono/node-server`: + +### Node.js (Adapter) + +Using `@whatwg-node/server` to adapt the fetch handler to Node's HTTP server: + +```ts server.ts @nocheck +import { actor, setup } from "rivetkit"; +import { createServer } from "node:http"; +import { createServerAdapter } from "@whatwg-node/server"; + +const myActor = actor({ state: {}, actions: {} }); +const registry = setup({ use: { myActor } }); + +const handler = createServerAdapter(registry.serve().fetch); +const server = createServer(handler); +server.listen(3000); +``` + +### Bun + +Using Bun's native server: + +```ts server.ts @nocheck +import { actor, setup } from "rivetkit"; + +const myActor = actor({ state: {}, actions: {} }); +const registry = setup({ use: { myActor } }); + +Bun.serve({ + port: 3000, + fetch: (request: Request) => registry.handler(request), +}); +``` + +### Deno + +Using Deno's native server: + +```ts server.ts @nocheck +import { actor, setup } from "rivetkit"; + +const myActor = actor({ state: {}, actions: {} }); +const registry = setup({ use: { myActor } }); + +Deno.serve({ port: 3000 }, (request: Request) => registry.handler(request)); +``` + +_Source doc path: /docs/general/http-server_ diff --git a/.agents/skills/multiplayer-game/reference/general/logging.md b/.agents/skills/multiplayer-game/reference/general/logging.md new file mode 100644 index 0000000..f7ce9eb --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/general/logging.md @@ -0,0 +1,74 @@ +# Logging + +> Source: `src/content/docs/general/logging.mdx` +> Canonical URL: https://rivet.dev/docs/general/logging +> Description: Actors provide a built-in way to log complex data to the console. + +--- +Using the context's log object (`c.log`) allows you to log complex data using structured logging. + +Using the actor logging API is completely optional. + +## Log levels + +There are 7 log levels: + +| Level | Call | Description | +| ------ | ------------------------------- | ---------------------------------------------------------------- | +| Fatal | `c.log.fatal(message, ...args);` | Critical errors that prevent core functionality | +| Error | `c.log.error(message, ...args);` | Errors that affect functionality but allow continued operation | +| Warn | `c.log.warn(message, ...args);` | Potentially harmful situations that should be addressed | +| Info | `c.log.info(message, ...args);` | General information about significant events & state changes | +| Debug | `c.log.debug(message, ...args);` | Detailed debugging information, usually used in development | +| Trace | `c.log.trace(message, ...args);` | Very detailed debugging information, usually for tracing flow | +| Silent | N/A | Disables all logging output | + +## Structured logging + +The built-in logging API (using `c.log`) provides structured logging to let you log key-value +pairs instead of raw strings. Structured logs are readable by both machines & +humans to make them easier to parse & search. + +When using `c.log`, the actor's name, key, and actor ID are automatically included in every log output. This makes it easy to filter and trace logs by specific actors in production environments. + +### Examples + +The logging system is built on [Pino](https://getpino.io/#/docs/api?id=logger), a high-performance structured logger for Node.js. + +## Configuration + +### Environment Variables + +You can configure logging behavior using environment variables: + +| Variable | Description | Values | Default | +| -------- | ----------- | ------ | ------- | +| `RIVET_LOG_LEVEL` | Sets the minimum log level to display | `trace`, `debug`, `info`, `warn`, `error`, `fatal`, `silent` | `warn` | +| `RIVET_LOG_TARGET` | Include the module name that logged the message | `1` to enable, `0` to disable | `0` | +| `RIVET_LOG_TIMESTAMP` | Include timestamp in log output | `1` to enable, `0` to disable | `0` | +| `RIVET_LOG_MESSAGE` | Enable detailed message logging for debugging | `1` to enable, `0` to disable | `0` | +| `RIVET_LOG_ERROR_STACK` | Include stack traces in error output | `1` to enable, `0` to disable | `0` | +| `RIVET_LOG_HEADERS` | Log HTTP headers in requests | `1` to enable, `0` to disable | `0` | + +Example: +```bash +RIVET_LOG_LEVEL=debug RIVET_LOG_TARGET=1 RIVET_LOG_TIMESTAMP=1 node server.js +``` + +### Log Level + +You can configure the log level programmatically when setting up your registry: + +### Custom Pino Logger + +You can also provide a custom Pino base logger for more advanced logging configurations: + +If using a custom base logger, you must manually configure your own log level in the Pino logger. + +For more advanced Pino configuration options, see the [Pino API documentation](https://getpino.io/#/docs/api?id=export). + +### Disable Welcome Message + +You can disable the default RivetKit welcome message with: + +_Source doc path: /docs/general/logging_ diff --git a/.agents/skills/multiplayer-game/reference/general/pool-configuration.md b/.agents/skills/multiplayer-game/reference/general/pool-configuration.md new file mode 100644 index 0000000..21b00f1 --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/general/pool-configuration.md @@ -0,0 +1,119 @@ +# Pool Configuration + +> Source: `src/content/docs/general/pool-configuration.mdx` +> Canonical URL: https://rivet.dev/docs/general/pool-configuration +> Description: Reference for runner pool configuration, including drain behavior, actor eviction rate limiting, and serverless-specific options. + +--- +A **runner pool** is the set of runners Rivet manages for a given runner name within a namespace. The pool configuration controls how runners are scaled, drained on version upgrades, and how quickly actors are evicted from drained runners. + +There are two pool kinds: + +- **`normal`** — runners connect to the engine themselves (for example a long-running process started by you, Docker, or Kubernetes). The engine does not start or stop runners. +- **`serverless`** — the engine calls an HTTP endpoint to wake runners on demand. Used by serverless platforms (Vercel, Cloudflare Workers, Freestyle, etc.). See [Runtime Modes](/docs/general/runtime-modes). + +## Setting the Configuration + +Configure a pool from the [Rivet dashboard](https://dashboard.rivet.dev) under your namespace's runner settings. The dashboard is the recommended way to manage pool configuration. + +You can also set pool configuration directly through the API or the TypeScript SDK: + +```typescript SDK @nocheck +import { RivetClient } from "@rivetkit/engine-api-full"; + +const rivet = new RivetClient({ + environment: "https://api.rivet.dev", + token: process.env.RIVET_TOKEN!, +}); + +await rivet.runnerConfigsUpsert("default", { + namespace: "default", + datacenters: { + default: { + serverless: { + url: "https://my-app.example.com/api/rivet", + requestLifespan: 60 * 15, + actorEvictionDelay: 5, + actorEvictionPeriod: 60, + actorEvictionRate: 1, + }, + }, + }, +}); +``` + +```bash curl +curl -X PUT "https://api.rivet.dev/runner-configs/default?namespace=default" \ + -H "Authorization: Bearer $RIVET_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "datacenters": { + "default": { + "serverless": { + "url": "https://my-app.example.com/api/rivet", + "request_lifespan": 900, + "actor_eviction_delay": 5, + "actor_eviction_period": 60, + "actor_eviction_rate": 1 + } + } + } + }' +``` + +The HTTP API uses `snake_case`. The TypeScript SDK uses `camelCase`. The field names below use `camelCase`. + +## Common Options + +These options apply to both `normal` and `serverless` pools. + +| Option | Type | Default | Description | +|---|---|---|---| +| `drainOnVersionUpgrade` | `bool` | `true` | When a new runner version is deployed, stop old runners and migrate their actors. See [Versions & Upgrades](/docs/actors/versions#drain-on-version-upgrade). | +| `actorEvictionDelay` | `u32` (seconds) | `0` | Delay before actor eviction begins after a drain is triggered. Gives clients time to receive the drain signal before migrations start. | +| `actorEvictionPeriod` | `u32` (seconds) | `0` | Window over which evictions are batched. Used together with `actorEvictionRate` to smooth eviction load on the rest of the pool. | +| `actorEvictionRate` | `f32` (actors/sec) | `1.0` | Maximum number of actors evicted per second once eviction is underway. Set higher for fast cutovers, lower to spread reschedule load. | +| `metadata` | `object` | — | Arbitrary JSON metadata attached to the pool. Useful for tagging and dashboards. | + +### Tuning eviction rate limiting + +Eviction kicks in whenever a runner is drained — most commonly during a version upgrade with `drainOnVersionUpgrade: true`, but also when a runner disconnects ungracefully or is replaced. + +A typical tuning starts from: + +- `actorEvictionDelay: 5` — five-second head start so clients see the drain notification before migrations begin. +- `actorEvictionPeriod: 60` — batch over a one-minute window. +- `actorEvictionRate: 1` — evict one actor per second. + +Increase `actorEvictionRate` for small pools where a full cutover finishes in seconds. Decrease it for large pools to avoid bursts of reschedule traffic. + +## Serverless-Only Options + +These options only apply when `kind: "serverless"`. + +| Option | Type | Default | Description | +|---|---|---|---| +| `url` | `string` | — | HTTP endpoint the engine calls to wake a runner. | +| `headers` | `map` | `{}` | Additional headers sent with the wake request (for example an auth token). | +| `requestLifespan` | `u32` (seconds) | — | Total lifespan of a serverless request before drain begins. Must be shorter than your platform's request timeout. | +| `maxConcurrentActors` | `u64` | `1000` | Soft cap on concurrent actors hosted across the pool. | +| `drainGracePeriod` | `u32` (seconds) | `1800` (30 min) | Time a serverless runner reserves at the end of its lifespan for actors to stop gracefully. | +| `metadataPollInterval` | `u64` (ms) | engine default | How often each runner re-fetches pool metadata to detect new versions. | + +### Deprecated options + +The following options still parse for backwards compatibility but should not be used in new configurations: + +- `slotsPerRunner` +- `minRunners` +- `maxRunners` +- `runnersMargin` + +## Related + +- [Runtime Modes](/docs/general/runtime-modes) — runner vs serverless behavior. +- [Versions & Upgrades](/docs/actors/versions) — how `drainOnVersionUpgrade` interacts with version detection. +- [Limits](/docs/actors/limits) — request lifespan and drain grace period limits. +- [Debugging](/docs/actors/debugging) — inspect a pool configuration via the API. + +_Source doc path: /docs/general/pool-configuration_ diff --git a/.agents/skills/multiplayer-game/reference/general/production-checklist.md b/.agents/skills/multiplayer-game/reference/general/production-checklist.md new file mode 100644 index 0000000..1ebe257 --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/general/production-checklist.md @@ -0,0 +1,98 @@ +# Production Checklist + +> Source: `src/content/docs/general/production-checklist.mdx` +> Canonical URL: https://rivet.dev/docs/general/production-checklist +> Description: Checklist for deploying Rivet Actors to production. + +--- +We recommend passing this page to your coding agent to verify your configuration before deploying. + +## Environment + +- **Set `NODE_ENV=production`.** Ensures optimized performance and disables development-only behavior. +- **Ensure log level is not set to debug.** Leave `RIVET_LOG_LEVEL` at its default or explicitly set it to `warn` to avoid excessive logging. See [Logging](/docs/general/logging). +- **Do not set `RIVET_EXPOSE_ERRORS=1` in production.** This exposes internal error details to clients. It is automatically enabled when `NODE_ENV=development`. See [Errors](/docs/actors/errors). + +## Runtime Mode + +- **Configure a runner version.** Required for graceful upgrades and draining of old actors. Applies to both serverless and runner modes. See [Versions & Upgrades](/docs/actors/versions). + +### Serverless + +- **Check platform timeouts.** Rivet handles migration between invocations automatically, but shorter timeouts increase migration frequency. See [Timeouts](/docs/general/runtime-modes#timeouts). +- **Verify `/api/rivet/start` body size limits.** Serverless actor starts carry actor config and preloaded KV or SQLite startup data in the request body. Keep `serverless.maxStartPayloadBytes` and your platform or proxy body limit at **16 MiB or higher**, or lower the preload budget if your platform cannot accept that size. See [Limits](/docs/actors/limits#kv-preloading). +- **Configure max runners.** Go to Settings > Providers > Edit Provider > Max Runners to set the limit. The default is 100,000 runners. This is effectively your max actor count. +- **Verify your platform rate limit accommodates your actor create and wake frequency.** Actor start requests are sent from Rivet's servers, so they all originate from the same IP. Per-IP rate limits will throttle the engine well before they would throttle real end-user traffic. Size your platform's rate limit to your peak actor create and wake rate, not your end-user request rate. +- **Set the per-instance max concurrent actor limit.** Each serverless instance hosts one actor per in-flight `/api/rivet/start` request, so your platform's per-instance concurrency (e.g. GCP Cloud Run `--concurrency`, AWS Lambda reserved concurrency, Vercel `maxDuration` + concurrency) directly caps actors per instance. Pick a value based on per-actor memory and CPU; the platform autoscales out additional instances once existing ones hit the cap. +- **Tune `requestLifespan` to your platform's hard request timeout.** `requestLifespan` (default `3600`, 60 minutes) is the total lifespan of each serverless request before actors migrate to a fresh instance. Set it just below your platform's hard timeout (e.g. `295` for Vercel Hobby, `3595` for Vercel Pro, `840` for Cloud Run's 15-min cap). Configure via [`configurePool`](/docs/general/registry-configuration). See [Timeouts](/docs/general/runtime-modes#timeouts). +- **Tune `drainGracePeriod` to cover graceful actor shutdown.** Time reserved at the end of `requestLifespan` for actors to stop gracefully before the request is forcibly closed. Default is 30 minutes from the engine; lower it for short-lived stateless actors, raise it if your actors do non-trivial cleanup or final SQLite writes. Configure via [`configurePool`](/docs/general/registry-configuration). See [Limits](/docs/actors/limits). + +### Runner + +- **Set a graceful shutdown period of at least 35 minutes.** Runners need up to 30 minutes to drain actors during upgrades, plus buffer for shutdown overhead. In Kubernetes, set `terminationGracePeriodSeconds: 2100` on the pod spec. + +## Actors + +### Design Patterns + +- **Do not use god actors.** Avoid putting all logic into a single actor type. See [Design Patterns](/docs/actors/design-patterns). +- **Do not use actor-per-request patterns.** Avoid creating a new actor for each request. See [Design Patterns](/docs/actors/design-patterns). + +### Lifecycle + +- **Do not rely on `onSleep` for critical cleanup.** `onSleep` is not called during crashes or forced terminations. See [Lifecycle](/docs/actors/lifecycle). + +### State + +- **Verify `c.state` does not grow unbounded.** Avoid using arrays or objects that grow over time in state. Use [SQLite](/docs/actors/sqlite) for unbounded or append-heavy data instead. +- **Verify actor data does not exceed 10 GB.** Contact [enterprise support](https://rivet.dev/sales) if you need more storage. +- **Use input parameters and `createState` for actor initialization.** See [Input Parameters](/docs/actors/input). + +### Events + +- **Use `conn.send()` instead of `c.broadcast()` for private events.** `c.broadcast()` sends to all connected clients. Use `conn.send()` to send events to a specific connection. See [Realtime](/docs/actors/events). + +### Actions + +- **Review action timeout configuration.** The default `actionTimeout` is 60 seconds. Increase it if you have long-running actions like API calls or file processing. See [Actor Configuration](/docs/general/actor-configuration). +- **Review message size limits.** The default `maxIncomingMessageSize` is 64 KiB and `maxOutgoingMessageSize` is 1 MiB. Increase if your actors send or receive large JSON payloads. See [Registry Configuration](/docs/general/registry-configuration). + +### Queues + +- **Review queue limits.** The default `maxQueueSize` is 1,000 messages and `maxQueueMessageSize` is 64 KiB. Increase if you expect burst traffic or large queue payloads. See [Actor Configuration](/docs/general/actor-configuration). +- **Ensure queue handlers are idempotent.** If processing fails before `message.complete()`, the message will be retried. See [Queues](/docs/actors/queues). + +### Workflows + +- **Verify workflows do not generate infinite steps.** Use `ctx.loop` to avoid creating unbounded step histories. See [Workflows](/docs/actors/workflows). + +## Security + +### Authentication + +- **Validate connections in `createConnState` or `onBeforeConnect`.** Do not trust client input without validation. See [Authentication](/docs/actors/authentication). + +### CORS + +- **Configure CORS for production.** Restrict allowed origins instead of allowing all. See [CORS](/docs/general/cors). + +### Tokens (Rivet Cloud) + +- **Use `pk_*` tokens for `RIVET_PUBLIC_ENDPOINT`.** Public tokens are safe to expose to clients. +- **Use `sk_*` tokens for `RIVET_ENDPOINT`.** Secret tokens should only be used server-side. +- **Do not leak your secret token.** Never expose `sk_*` tokens in client-side code, public repositories, or browser environments. See [Endpoints](/docs/general/endpoints). +- **Verify you're connecting to the correct region.** Use the nearest datacenter endpoint (e.g. `api-us-west-1.rivet.dev`) for lowest latency. + +### Access Control + +Access control is only needed if you want granular permissions for different clients. For most use cases, basic authentication in `onBeforeConnect` or `createConnState` is sufficient. + +- **Use deny-by-default rules.** Reject unknown roles in `onBeforeConnect`, action handlers, `canPublish`, and `canSubscribe`. See [Access Control](/docs/actors/access-control). +- **Authorize actions explicitly.** Check the caller's role in each action handler and throw `forbidden` for unauthorized access. +- **Gate event subscriptions and queue publishes.** Use `canSubscribe` and `canPublish` hooks to restrict which clients can subscribe to events or publish to queues. + +## Clients + +- **Dispose connections and/or client when not in use (JavaScript client).** Call `conn.dispose()` or `client.dispose()` when no longer needed to free resources. React and SwiftUI clients handle this automatically. See [Connection Lifecycle](/docs/clients/javascript#connection-lifecycle). + +_Source doc path: /docs/general/production-checklist_ diff --git a/.agents/skills/multiplayer-game/reference/general/registry-configuration.md b/.agents/skills/multiplayer-game/reference/general/registry-configuration.md new file mode 100644 index 0000000..3574c4a --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/general/registry-configuration.md @@ -0,0 +1,32 @@ +# Registry Configuration + +> Source: `src/content/docs/general/registry-configuration.mdx` +> Canonical URL: https://rivet.dev/docs/general/registry-configuration +> Description: This page documents the configuration options available when setting up a RivetKit registry. The registry configuration is passed to the `setup()` function. + +--- +## Example Configurations + +### Basic Setup + +### Connecting to Rivet Engine + +## Starting Your App + +After configuring your registry, start it: + +See [Runtime Modes](/docs/general/runtime-modes) for details on when to use each mode. + +## Environment Variables + +Many configuration options can be set via environment variables. See [Environment Variables](/docs/general/environment-variables) for a complete reference. + +## Configuration Reference + +## Related + +- [Actor Configuration](/docs/general/actor-configuration): Configure individual actors +- [HTTP Server Setup](/docs/general/http-server): Set up HTTP routing and middleware +- [Architecture](/docs/general/architecture): Understand how RivetKit works + +_Source doc path: /docs/general/registry-configuration_ diff --git a/.agents/skills/multiplayer-game/reference/general/runtime-modes.md b/.agents/skills/multiplayer-game/reference/general/runtime-modes.md new file mode 100644 index 0000000..43ba986 --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/general/runtime-modes.md @@ -0,0 +1,99 @@ +# Runtime Modes + +> Source: `src/content/docs/general/runtime-modes.mdx` +> Canonical URL: https://rivet.dev/docs/general/runtime-modes +> Description: RivetKit supports two runtime modes for running your actors: + +--- +- **Serverless**: Default mode. Responds to HTTP requests and scales automatically. +- **Runners**: Background processes without HTTP endpoints. Only needed for advanced scenarios. + +## Serverless + +Serverless is the default and recommended mode. Rivet sends HTTP requests to your backend to run actor logic, allowing your infrastructure to scale automatically. + +### Benefits + +- **Platform support**: Works with serverless platforms (Vercel, Cloudflare Workers, etc.) +- **Scale to zero**: No cost when idle +- **Edge deployments**: Easier to deploy to edge locations +- **Preview deployments**: Integrates with preview deployments on platforms like Vercel and Railway +- **Efficient autoscaling**: Request-based autoscaling can be faster and more efficient than CPU-based autoscaling depending on the platform + +### Example + +See [Server Setup](/docs/general/http-server/) for more configuration options. + +### Architecture + +When a client creates an actor, it sends a request to the Rivet Engine. The engine then calls `GET /api/rivet/start` on your serverless backend to run the actor. + +Serverless architecture diagram + +### Advanced + +#### Endpoints + +Rivet exposes the following endpoints: + +- `GET /api/rivet/metadata`: Validates configuration +- `GET /api/rivet/start`: Runs an actor + +You should never call these endpoints yourself, this is included purely for comprehension of how Rivet works under the hood. + +#### Timeouts + +Serverless platforms like Vercel have function timeouts. Rivet handles this automatically by migrating actors between function invocations, preserving state through `ctx.state`. Write your code as if it runs forever, Rivet handles the rest. + +Read more about [how we handle timeouts](/blog/2025-10-20-how-we-built-websocket-servers-for-vercel-functions/#timeouts-and-failover). + +#### Shutdown Sequence + +Each serverless request has a configurable lifespan (`requestLifespan`, default: 60 minutes). Set this to match your platform's function timeout (e.g. `requestLifespan: 3600` for Vercel Pro). + +When the request nears its lifespan, the engine reserves a grace period (`serverless_drain_grace_period`, default: 10 seconds) at the end to gracefully stop actors. For example, with a 3600-second lifespan, actors begin stopping at 3590 seconds. After the full lifespan elapses, the connection is forcibly closed and any remaining actors are rescheduled. + +See [Limits](/docs/actors/limits#serverless-shutdown) for configuration details. + +## Runners + +Runners run actors as long-running background processes without exposing an HTTP endpoint. + +### When to Use Runners + +- **No HTTP server**: Your app does not or cannot expose an HTTP server +- **No load balancer**: You don't have a load balancer to distribute HTTP requests across your servers +- **Custom scaling**: You have custom scaling requirements + +### Example + +The runner runs in the background, ready to run actors. + +### Architecture + +On startup, your backend calls `registry.startEnvoy()` which opens a persistent connection to the Rivet Engine. When a client creates an actor, the engine sends a command through this connection to start the actor on your backend. + +Runners architecture diagram + +### Configuration + +#### Runner Pool + +Use `RIVET_RUNNER` to assign runners to a pool. This lets you control which runners handle specific actors. + +```bash +RIVET_RUNNER=gpu-workers +``` + +See [Pool Configuration](/docs/general/pool-configuration) for how pools are scaled, drained on version upgrades, and rate-limited during actor eviction. + +## Comparison + +| Mode | Method | Use Case | +|------|--------|----------| +| Auto | `registry.start()` | Simplest setup. Starts server, serves static files, and runs actors. | +| Serverless | `registry.serve()` | Fetch handler for serverless platforms | +| Serverless | `registry.handler()` | Integrating with existing routers (Hono, Elysia, etc.) | +| Runner | `registry.startEnvoy()` | Long-running processes without HTTP endpoints | + +_Source doc path: /docs/general/runtime-modes_ diff --git a/.agents/skills/multiplayer-game/reference/general/wasm-vs-native-sdk.md b/.agents/skills/multiplayer-game/reference/general/wasm-vs-native-sdk.md new file mode 100644 index 0000000..18cdd13 --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/general/wasm-vs-native-sdk.md @@ -0,0 +1,79 @@ +# WASM vs Native SDK + +> Source: `src/content/docs/general/wasm-vs-native-sdk.mdx` +> Canonical URL: https://rivet.dev/docs/general/wasm-vs-native-sdk +> Description: RivetKit runs your actors on a native or a WebAssembly runtime depending on your platform. + +--- +RivetKit ships two runtimes for executing actor logic. Most projects use the +native runtime automatically. Edge and serverless platforms that cannot run +native binaries use the WebAssembly runtime. + +- **Native**: Default. Runs on Node.js and Bun via native bindings. Best performance. +- **WebAssembly (wasm)**: Runs anywhere a `WebAssembly.Module` can be instantiated, including Cloudflare Workers, Supabase Edge Functions, and Deno Deploy. + +## Native runtime + +The native runtime is the default and requires no configuration. It loads +platform-specific native bindings for the best performance and full feature +support on Node.js and Bun. + +## WebAssembly runtime + +Edge platforms run on isolates (V8, Deno) that cannot load native binaries, so +RivetKit provides a WebAssembly build of its core runtime in the +`@rivetkit/rivetkit-wasm` package. You select it with `runtime: "wasm"` and pass +the wasm bindings and binary through the `wasm` option. + +### Recommended: use a platform package + +For supported platforms, use the dedicated package instead of wiring the wasm +runtime by hand. They install the wasm runtime, load the binary, and (on +Cloudflare) provide the outbound WebSocket the engine tunnel needs. + +- **Cloudflare Workers**: [`@rivetkit/cloudflare-workers`](/docs/actors/quickstart/cloudflare) +- **Supabase Edge Functions**: [`@rivetkit/supabase`](/docs/actors/quickstart/supabase) + +```typescript @nocheck +// Cloudflare Workers +import { actor } from "rivetkit"; +import { createHandler } from "@rivetkit/cloudflare-workers"; + +const counter = actor({ state: { count: 0 }, actions: {} }); + +export default createHandler({ use: { counter } }); +``` + +### Manual configuration + +For platforms without a dedicated package, configure the wasm runtime directly. +Pass the bindings from `@rivetkit/rivetkit-wasm` and the wasm binary as +`initInput`. `initInput` accepts a `Uint8Array`, `URL`, `Response`, or a +`WebAssembly.Module`. + +```typescript @nocheck +import { actor, setup } from "rivetkit"; +import * as wasmBindings from "@rivetkit/rivetkit-wasm"; +import wasmModule from "@rivetkit/rivetkit-wasm/rivetkit_wasm_bg.wasm"; + +const counter = actor({ state: { count: 0 }, actions: {} }); + +const registry = setup({ + runtime: "wasm", + wasm: { bindings: wasmBindings, initInput: wasmModule }, + use: { counter }, +}); +``` + +On platforms without an outbound `new WebSocket(url)` constructor (such as +Cloudflare Workers), you must also install a fetch-based `globalThis.WebSocket` +shim so the actor can open its tunnel to the engine. The platform packages handle +this for you, which is why they are recommended. + +## Related + +- [Runtime Modes](/docs/general/runtime-modes) +- [Cloudflare Workers Quickstart](/docs/actors/quickstart/cloudflare) +- [Supabase Functions Quickstart](/docs/actors/quickstart/supabase) + +_Source doc path: /docs/general/wasm-vs-native-sdk_ diff --git a/.agents/skills/multiplayer-game/reference/self-hosting/configuration.md b/.agents/skills/multiplayer-game/reference/self-hosting/configuration.md new file mode 100644 index 0000000..a71d16e --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/self-hosting/configuration.md @@ -0,0 +1,74 @@ +# Configuration + +> Source: `src/content/docs/self-hosting/configuration.mdx` +> Canonical URL: https://rivet.dev/docs/self-hosting/configuration +> Description: Rivet Engine can be configured through environment variables or configuration files. + +--- +The full JSON Schema for the configuration is available at [/docs/engine-config-schema.json](/docs/engine-config-schema.json). + +## Configuration Sources + +Rivet supports JSON, JSON5, JSONC, YAML, YML, and environment variable configurations. + +**Environment Variables** + +Use the `RIVET__` prefix with `__` as separator to configure properties in the config. For example: set the `RIVET__database__postgres__url` environment variable for `database.postgres.url`. + +**Configuration Paths** + +Configuration files are automatically discovered in platform-specific directories: + +- Linux: `/etc/rivet/config.json` +- macOS: `/Library/Application Support/rivet/config.json` +- Windows: `C:\ProgramData\rivet\config.json` + +**Multiple Files** + +Multiple configuration files in the same directory are loaded and merged together. For example: `/etc/rivet/config.json` and `/etc/rivet/database.json` will be merged together. + +**Override Configuration Path** + +You can override the default configuration path using the `--config` flag: + +```bash +# Load from a specific file +rivet-engine --config /path/to/config.json + +# Load from a directory +rivet-engine --config /etc/rivet + +# Load multiple paths (merged in order) +rivet-engine --config /etc/rivet/base.json --config /etc/rivet/override.json +``` + +## Configuration Reference + +### Pegboard Envoy Load Balancing + +`pegboard.envoy_load_balancer` supports the `hash` strategy for hash-ring-based envoy selection: + +```json +{ + "pegboard": { + "envoy_load_balancer": { + "hash": { + "virtual_nodes": 8, + "samples": 2, + "max_scan": 16, + "use_snapshot_read": true + } + } + } +} +``` + +Use `samples: 1` for a uniform random pick that skips slot reads. Use `samples >= 2` for power-of-K choices over envoy slot counts. Treat `virtual_nodes` as an operational invariant once envoys have registered. + +## Related + +- RivetKit actor runtime persistence lives in SQLite. Existing actor KV data is imported into SQLite the first time an actor wakes on the migrated runtime, then the original KV data is left frozen for downgrade safety. +- [PostgreSQL](/docs/self-hosting/postgres): Configure the experimental PostgreSQL backend +- [File System](/docs/self-hosting/filesystem): Configure file system storage for development + +_Source doc path: /docs/self-hosting/configuration_ diff --git a/.agents/skills/multiplayer-game/reference/self-hosting/docker-compose.md b/.agents/skills/multiplayer-game/reference/self-hosting/docker-compose.md new file mode 100644 index 0000000..8f2cea1 --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/self-hosting/docker-compose.md @@ -0,0 +1,188 @@ +# Docker Compose + +> Source: `src/content/docs/self-hosting/docker-compose.mdx` +> Canonical URL: https://rivet.dev/docs/self-hosting/docker-compose +> Description: Deploy Rivet Engine with docker-compose for multi-container setups. + +--- +## Quick Start + +### Create docker-compose.yaml + +Create a `docker-compose.yaml` in your project root: + +```yaml +services: + rivet-engine: + image: rivetdev/engine:latest + ports: + - "6420:6420" + volumes: + - rivet-data:/data + environment: + RIVET__FILE_SYSTEM__PATH: "/data" + restart: unless-stopped + +volumes: + rivet-data: +``` + +### Start the engine + +```bash +docker-compose up -d +``` + +## Connecting Your Project + +Once the engine is running, add your app as a service in the same Compose file. + +### Create your server + +Follow the [quickstart](/docs/actors/quickstart/backend) to create a working server with actors. + +### Create a Dockerfile + +Create a `Dockerfile` in your project root: + +```dockerfile @nocheck +FROM node:22-slim +WORKDIR /app +COPY package.json pnpm-lock.yaml ./ +RUN corepack enable && pnpm install --frozen-lockfile +COPY . . +RUN pnpm build +CMD ["node", "dist/index.js"] +``` + +### Add your app to docker-compose.yaml + +Update your `docker-compose.yaml` to include your app alongside the engine: + +```yaml +services: + rivet-engine: + image: rivetdev/engine:latest + ports: + - "6420:6420" + volumes: + - rivet-data:/data + environment: + RIVET__FILE_SYSTEM__PATH: "/data" + restart: unless-stopped + + my-app: + build: . + environment: + RIVET_ENDPOINT: "http://default:admin@rivet-engine:6420" + depends_on: + - rivet-engine + restart: unless-stopped + +volumes: + rivet-data: +``` + +`RIVET_ENDPOINT` tells your app to connect to the engine as a runner instead of running standalone. The URL uses the format `http://namespace:token@host:port`. Inside the Docker network, your app reaches the engine at `rivet-engine:6420`. See [Endpoints](/docs/general/endpoints) for all options. + +### Start the services + +```bash +docker-compose up -d +``` + +### Register your runner with the engine + +### Dashboard + +1. Open the Rivet Engine dashboard at `http://localhost:6420`. +2. Enter your admin token when prompted. +3. In the namespace sidebar, click **Settings**. +4. Click **Add Provider**, then choose **Custom**. +5. Click **Next** (these settings can be changed later). +6. Click **Next** (you can safely skip the env ar step for Docker Compose). +5. Go to **Confirm Connection**, enter your app endpoint (`http://my-app:6420/api/rivet`), then click **Add**. + +### CLI (curl) + +Register your runner programmatically via the engine API: + +```bash @nocheck +curl -X PUT "http://localhost:6420/runner-configs/default?namespace=default" \ + -H "Content-Type: application/json" \ + -d '{ + "datacenters": { + "default": { + "normal": {} + } + } + }' +``` + +## Configuration + +### Config File + +Mount a JSON configuration file in your `docker-compose.yaml`: + +```yaml +services: + rivet-engine: + image: rivetdev/engine:latest + ports: + - "6420:6420" + volumes: + - ./rivet-config.json:/etc/rivet/config.json:ro + - rivet-data:/data + restart: unless-stopped + +volumes: + rivet-data: +``` + +Create `rivet-config.json` in the same directory as your `docker-compose.yaml`. See the [Configuration](/docs/self-hosting/configuration) docs for all available options and the full [JSON Schema](/docs/engine-config-schema.json). + +```json +{ + "postgres": { + "url": "postgresql://rivet:password@postgres:5432/rivet" + } +} +``` + +### Postgres Setup + +PostgreSQL is the recommended backend for multi-node self-hosted deployments today, but it remains experimental. For a production-ready single-node Rivet deployment, use the file system backend (RocksDB-based). Enterprise teams can contact [enterprise support](https://rivet.dev/sales) about FoundationDB for the most scalable production-ready deployment. + +```yaml +services: + postgres: + image: postgres:15 + environment: + POSTGRES_DB: rivet + POSTGRES_USER: rivet + POSTGRES_PASSWORD: rivet_password + volumes: + - postgres-data:/var/lib/postgresql/data + restart: unless-stopped + + rivet-engine: + image: rivetdev/engine:latest + ports: + - "6420:6420" + environment: + RIVET__POSTGRES__URL: postgresql://rivet:rivet_password@postgres:5432/rivet + depends_on: + - postgres + restart: unless-stopped + +volumes: + postgres-data: +``` + +## Next Steps + +- Review the [Production Checklist](/docs/self-hosting/production-checklist) before going live +- See [Configuration](/docs/self-hosting/configuration) for all options + +_Source doc path: /docs/self-hosting/docker-compose_ diff --git a/.agents/skills/multiplayer-game/reference/self-hosting/docker-container.md b/.agents/skills/multiplayer-game/reference/self-hosting/docker-container.md new file mode 100644 index 0000000..e6983ae --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/self-hosting/docker-container.md @@ -0,0 +1,150 @@ +# Docker Container + +> Source: `src/content/docs/self-hosting/docker-container.mdx` +> Canonical URL: https://rivet.dev/docs/self-hosting/docker-container +> Description: Run Rivet Engine in a single Docker container. + +--- +## Quick Start + +### Start the engine + +```bash +docker run -d \ + --name rivet-engine \ + -p 6420:6420 \ + -v rivet-data:/data \ + -e RIVET__FILE_SYSTEM__PATH="/data" \ + rivetdev/engine:latest +``` + +### Verify the engine is running + +```bash +curl http://localhost:6420/health +``` + +## Connecting Your Project + +Once the engine is running, connect your app to it. + +### Create your server + +Follow the [quickstart](/docs/actors/quickstart/backend) to create a working server with actors. + +### Start your app with RIVET_ENDPOINT + +Set `RIVET_ENDPOINT` to tell your app to connect to the engine as a runner instead of running standalone: + +```bash @nocheck +RIVET_ENDPOINT="http://default:admin@host.docker.internal:6420" npm start +``` + +If running your app outside of Docker, use `localhost` instead of `host.docker.internal`: + +```bash @nocheck +RIVET_ENDPOINT="http://default:admin@localhost:6420" npm start +``` + +See [Endpoints](/docs/general/endpoints) for all options. + +### Register your runner with the engine + +### Dashboard + +1. Open the Rivet Engine dashboard at `http://localhost:6420`. +2. Enter your admin token when prompted. +3. In the namespace sidebar, click **Settings**. +4. Click **Add Provider**, then choose **Custom**. +5. Click **Next**. +6. Go to **Confirm Connection**, enter your app endpoint, then click **Add**. + +### CLI (curl) + +Register your runner programmatically via the engine API: + +```bash @nocheck +curl -X PUT "http://localhost:6420/runner-configs/default?namespace=default" \ + -H "Content-Type: application/json" \ + -d '{ + "datacenters": { + "default": { + "normal": {} + } + } + }' +``` + +## Configuration + +### Environment Variables + +Configure Rivet using environment variables: + +```bash +docker run -d \ + --name rivet-engine \ + -p 6420:6420 \ + -v rivet-data:/data \ + -e RIVET__FILE_SYSTEM__PATH="/data" \ + -e RIVET__POSTGRES__URL="postgresql://postgres:password@localhost:5432/db" \ + rivetdev/engine:latest +``` + +### Config File + +Mount a JSON configuration file: + +```bash +docker run -d \ + --name rivet-engine \ + -p 6420:6420 \ + -v rivet-data:/data \ + -v $(pwd)/rivet-config.json:/etc/rivet/config.json:ro \ + rivetdev/engine:latest +``` + +Create `rivet-config.json` in your working directory. See the [Configuration](/docs/self-hosting/configuration) docs for all available options and the full [JSON Schema](/docs/engine-config-schema.json). + +```json +{ + "postgres": { + "url": "postgresql://postgres:password@localhost:5432/db" + } +} +``` + +### Postgres Setup + +PostgreSQL is the recommended backend for multi-node self-hosted deployments today, but it remains experimental. For a production-ready single-node Rivet deployment, use the file system backend (RocksDB-based). Enterprise teams can contact [enterprise support](https://rivet.dev/sales) about FoundationDB for the most scalable production-ready deployment. + +```bash +# Create network +docker network create rivet-net + +# Run PostgreSQL +docker run -d \ + --name postgres \ + --network rivet-net \ + -e POSTGRES_DB=rivet \ + -e POSTGRES_USER=rivet \ + -e POSTGRES_PASSWORD=rivet_password \ + -v postgres-data:/var/lib/postgresql/data \ + postgres:15 + +# Run Rivet Engine +docker run -d \ + --name rivet-engine \ + --network rivet-net \ + -p 6420:6420 \ + -e RIVET__POSTGRES__URL="postgresql://rivet:rivet_password@postgres:5432/rivet" \ + rivetdev/engine +``` + +## Next Steps + +- Review the [Production Checklist](/docs/self-hosting/production-checklist) before going live +- Use [Docker Compose](/docs/self-hosting/docker-compose) for multi-container setups +- See [Configuration](/docs/self-hosting/configuration) for all options + +_Source doc path: /docs/self-hosting/docker-container_ diff --git a/.agents/skills/multiplayer-game/reference/self-hosting/filesystem.md b/.agents/skills/multiplayer-game/reference/self-hosting/filesystem.md new file mode 100644 index 0000000..30f7bdb --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/self-hosting/filesystem.md @@ -0,0 +1,47 @@ +# File System + +> Source: `src/content/docs/self-hosting/filesystem.mdx` +> Canonical URL: https://rivet.dev/docs/self-hosting/filesystem +> Description: The file system backend stores all data on the local disk. This is suitable for single-node deployments, development, and testing. + +--- +For a production-ready single-node Rivet deployment, use the file system backend (RocksDB-based); for multi-node deployments, PostgreSQL is the recommended backend today but remains experimental as we evaluate the best fit for scalability and performance, and Enterprise teams can contact [enterprise support](https://rivet.dev/sales) about FoundationDB. + +## Configuration + +```json Configuration-file +{ + "database": { + "file_system": { + "path": "/var/lib/rivet/data" + } + } +} +``` + +```bash Environment-variables +RIVET__database__file_system__path="/var/lib/rivet/data" +``` + +## Default Paths + +If no path is specified, Rivet uses platform-specific default locations: + +- Linux: `~/.local/share/rivet-engine/db` +- macOS: `~/Library/Application Support/rivet-engine/db` +- Windows: `%APPDATA%\rivet-engine\db` + +When running in a container or as a service, the path defaults to `./data/db` relative to the working directory. + +## When to Use File System + +The file system backend is ideal for: + +- Local development +- Single-node deployments +- Testing and prototyping +- Air-gapped environments without database infrastructure + +If you need a production-ready Rivet deployment today, use this backend for smaller single-node setups; for multi-node deployments, PostgreSQL is the recommended backend today though still experimental, and Enterprise teams can contact [enterprise support](https://rivet.dev/sales) about FoundationDB for the most scalable production-ready deployment. + +_Source doc path: /docs/self-hosting/filesystem_ diff --git a/.agents/skills/multiplayer-game/reference/self-hosting/foundationdb.md b/.agents/skills/multiplayer-game/reference/self-hosting/foundationdb.md new file mode 100644 index 0000000..b8331bb --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/self-hosting/foundationdb.md @@ -0,0 +1,35 @@ +# FoundationDB (Enterprise) + +> Source: `src/content/docs/self-hosting/foundationdb.mdx` +> Canonical URL: https://rivet.dev/docs/self-hosting/foundationdb +> Description: FoundationDB is the recommended storage backend for scalable production Rivet deployments. + +--- +FoundationDB requires an enterprise license. Contact [enterprise support](https://rivet.dev/sales) for setup guidance and access. + +## Overview + +FoundationDB is the recommended storage backend for scalable, production-ready Rivet deployments. It is a distributed, ordered key-value store originally built by Apple. + +FoundationDB powers some of the largest infrastructure in the world: + +- **Apple**: iCloud and other Apple services +- **Snowflake**: Cloud data platform metadata layer +- **Datadog**: Observability and monitoring platform +- **Tigris Data**: Globally distributed object storage + +Its strict serializability guarantees, fault tolerance, and ability to scale linearly across nodes make it the ideal backend for Rivet's actor state and orchestration layer. + +## Why FoundationDB + +| | RocksDB (File System) | PostgreSQL | FoundationDB | +|---|---|---|---| +| **Scalability** | Single node | Primary/replica failover | Linear horizontal scaling | +| **Fault tolerance** | None | Primary/replica failover | Automatic recovery with no data loss | +| **Production readiness** | Development and small deployments | Experimental for multi-node | Battle-tested at global scale | + +## Getting Started + +FoundationDB configuration and cluster setup are handled as part of enterprise onboarding. Contact [enterprise support](https://rivet.dev/sales) to get started. + +_Source doc path: /docs/self-hosting/foundationdb_ diff --git a/.agents/skills/multiplayer-game/reference/self-hosting/install.md b/.agents/skills/multiplayer-game/reference/self-hosting/install.md new file mode 100644 index 0000000..344be8e --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/self-hosting/install.md @@ -0,0 +1,32 @@ +# Installing Rivet Engine + +> Source: `src/content/docs/self-hosting/install.mdx` +> Canonical URL: https://rivet.dev/docs/self-hosting/install +> Description: Install Rivet Engine using Docker, binaries, or a source build. + +--- +For more options: + +- [Docker Container](/docs/self-hosting/docker-container) for persistent storage, configuration, and production setups +- [Docker Compose](/docs/self-hosting/docker-compose) for multi-container deployments with PostgreSQL + +## Docker + +```bash +docker run -p 6420:6420 rivetdev/engine +``` + +## Prebuilt Binaries + + Prebuilt binaries coming soon + +## Build From Source + +```bash +git clone https://github.com/rivet-dev/rivet.git +cd rivet +cargo build --release -p rivet-engine +./target/release/rivet-engine +``` + +_Source doc path: /docs/self-hosting/install_ diff --git a/.agents/skills/multiplayer-game/reference/self-hosting/kubernetes.md b/.agents/skills/multiplayer-game/reference/self-hosting/kubernetes.md new file mode 100644 index 0000000..a2c619c --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/self-hosting/kubernetes.md @@ -0,0 +1,236 @@ +# Kubernetes + +> Source: `src/content/docs/self-hosting/kubernetes.mdx` +> Canonical URL: https://rivet.dev/docs/self-hosting/kubernetes +> Description: Deploy production-ready Rivet Engine to Kubernetes with PostgreSQL storage. + +--- +PostgreSQL is the recommended backend for multi-node self-hosted deployments today, but it remains experimental. For a production-ready single-node Rivet deployment, use the file system backend (RocksDB-based). Enterprise teams can contact [enterprise support](https://rivet.dev/sales) about FoundationDB for the most scalable production-ready deployment. + +## Prerequisites + +- Kubernetes cluster +- `kubectl` configured +- [Metrics server](https://github.com/kubernetes-sigs/metrics-server) (required for HPA) — included by default in most distributions (k3d, GKE, EKS, AKS) + +## Deploy Rivet Engine + + +### Download Manifests + + Download the `self-host/k8s/engine` directory from the Rivet repository: + + ```bash + npx giget@latest gh:rivet-dev/rivet/self-host/k8s/engine rivet-k8s + cd rivet-k8s + ``` + + + +### Configure Engine + + In `02-engine-configmap.yaml`, set `public_url` to your engine's external URL. + + + +### Configure PostgreSQL + + In `11-postgres-secret.yaml`, update the PostgreSQL password. See [Using a Managed PostgreSQL Service](#using-a-managed-postgresql-service) for external databases. + + + +### Configure Admin Token + + Generate a secure admin token and save it somewhere safe: + + ```bash + openssl rand -hex 32 + ``` + + Create the namespace and store the token as a Kubernetes secret: + + ```bash + kubectl create namespace rivet-engine + kubectl -n rivet-engine create secret generic rivet-secrets --from-literal=admin-token=YOUR_TOKEN_HERE + ``` + + + +### Deploy + + ```bash + # Apply all manifests + kubectl apply -f . + + # Wait for all pods to be ready + kubectl -n rivet-engine wait --for=condition=ready pod -l app=nats --timeout=300s + kubectl -n rivet-engine wait --for=condition=ready pod -l app=postgres --timeout=300s + kubectl -n rivet-engine wait --for=condition=ready pod -l app=rivet-engine --timeout=300s + + # Verify all pods are running + kubectl -n rivet-engine get pods + ``` + + + +### Access the Engine + + Visit `/ui` on your `public_url` to access the dashboard. + + +## Connecting Your Project + +### Create your server + +Follow the [quickstart](/docs/actors/quickstart/backend) to create a working server with actors. + +### Create Kubernetes manifests + +Create these manifest files for your app: + +```yaml deployment.yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: rivetkit-app + namespace: your-namespace +spec: + replicas: 1 + selector: + matchLabels: + app: rivetkit-app + template: + metadata: + labels: + app: rivetkit-app + spec: + # Allow enough time for actors to gracefully stop on SIGTERM. + # The runner waits up to 30m for actors to finish. + # Add buffer for runner shutdown overhead after actors stop. + # See: /docs/actors/versions#graceful-shutdown-sigterm + terminationGracePeriodSeconds: 2100 + containers: + - name: rivetkit-app + image: registry.example.com/your-team/rivetkit-app:latest + envFrom: + - secretRef: + name: rivetkit-secrets +``` + +```yaml service.yaml +apiVersion: v1 +kind: Service +metadata: + name: rivetkit-app + namespace: your-namespace +spec: + selector: + app: rivetkit-app + ports: + - name: http + port: 8080 + targetPort: 8080 +``` + +### Configure the endpoint + +Create `rivetkit-secrets.yaml` with `RIVET_ENDPOINT` pointing to the engine. This tells your app to connect as a runner instead of running standalone. See [Endpoints](/docs/general/endpoints) for all options. + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: rivetkit-secrets + namespace: your-namespace +type: Opaque +stringData: + RIVET_ENDPOINT: http://my-app:your-admin-token@your-engine.example.com +``` + +### Deploy your app + +```bash +kubectl apply -f rivetkit-secrets.yaml +kubectl apply -f deployment.yaml +kubectl apply -f service.yaml +``` + +### Register your runner with the engine + +### Dashboard + +1. Open the Rivet Engine dashboard in your browser. +2. Enter your admin token when prompted. +3. In the namespace sidebar, click **Settings**. +4. Click **Add Provider**, then choose **Custom**. +5. Click **Next**. +6. Go to **Confirm Connection**, enter your app endpoint (e.g. `http://rivetkit-app.your-namespace:8080/api/rivet`), then click **Add**. + +### CLI (curl) + +Register your runner programmatically via the engine API: + +```bash @nocheck +curl -X PUT "https://your-engine.example.com/runner-configs/default?namespace=default" \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer your-admin-token" \ + -d '{ + "datacenters": { + "default": { + "normal": {} + } + } + }' +``` + +## Advanced + +### Using a Managed PostgreSQL Service + +If you prefer to use a managed PostgreSQL service (e.g. Amazon RDS, Cloud SQL, Azure Database) instead of the bundled Postgres deployment: + +- Update the `postgres.url` connection string in `02-engine-configmap.yaml` to point to your managed instance +- Delete the bundled PostgreSQL manifests: + - `10-postgres-configmap.yaml` + - `11-postgres-secret.yaml` + - `12-postgres-statefulset.yaml` + - `13-postgres-service.yaml` + +### Applying Configuration Updates + +When making subsequent changes to `02-engine-configmap.yaml`, restart the engine pods to pick up the new configuration: + +```bash +kubectl apply -f 02-engine-configmap.yaml +kubectl -n rivet-engine rollout restart deployment/rivet-engine +``` + +### Ingress and Load Balancer Timeouts + +Rivet uses long-lived WebSocket connections for both client traffic (browsers, SDKs) and envoy traffic (actor hosts connecting back to the engine). Default Ingress and cloud load balancer idle timeouts (typically 30 to 60 seconds) drop these connections and cause reconnect storms. + +Raise the idle / read / send timeout on every Ingress and load balancer in front of the engine to at least 1 hour (`3600` seconds). Examples: + +- **NGINX Ingress** (annotations on the Ingress): + + ```yaml + nginx.ingress.kubernetes.io/proxy-read-timeout: "3600" + nginx.ingress.kubernetes.io/proxy-send-timeout: "3600" + ``` + +- **AWS Load Balancer Controller (ALB)**: + + ```yaml + alb.ingress.kubernetes.io/load-balancer-attributes: idle_timeout.timeout_seconds=3600 + ``` + +- **GCE Ingress (GKE)**: set `timeoutSec: 3600` on the `BackendConfig` referenced by the Service. + +The same guidance applies to the load balancer fronting your RivetKit app, since envoys connect to it over WebSocket. + +## Next Steps + +- Review the [Production Checklist](/docs/self-hosting/production-checklist) before going live +- See [Configuration](/docs/self-hosting/configuration) for all engine config options + +_Source doc path: /docs/self-hosting/kubernetes_ diff --git a/.agents/skills/multiplayer-game/reference/self-hosting/multi-region.md b/.agents/skills/multiplayer-game/reference/self-hosting/multi-region.md new file mode 100644 index 0000000..2ecca85 --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/self-hosting/multi-region.md @@ -0,0 +1,10 @@ +# Multi-Region + +> Source: `src/content/docs/self-hosting/multi-region.mdx` +> Canonical URL: https://rivet.dev/docs/self-hosting/multi-region +> Description: Rivet Engine supports scaling transparently across multiple regions. + +--- +Documentation coming soon + +_Source doc path: /docs/self-hosting/multi-region_ diff --git a/.agents/skills/multiplayer-game/reference/self-hosting/postgres.md b/.agents/skills/multiplayer-game/reference/self-hosting/postgres.md new file mode 100644 index 0000000..e935246 --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/self-hosting/postgres.md @@ -0,0 +1,192 @@ +# PostgreSQL + +> Source: `src/content/docs/self-hosting/postgres.mdx` +> Canonical URL: https://rivet.dev/docs/self-hosting/postgres +> Description: Configure PostgreSQL for self-hosted Rivet deployments. + +--- +PostgreSQL is the recommended backend for multi-node self-hosted deployments today, but it remains experimental. For a production-ready single-node Rivet deployment, use the file system backend (RocksDB-based). Enterprise teams can contact [enterprise support](https://rivet.dev/sales) about FoundationDB for the most scalable production-ready deployment. + +## Basic Configuration + +```json Configuration-file +{ + "database": { + "postgres": { + "url": "postgresql://user:password@host:5432/database" + } + } +} +``` + +```bash Environment-variables +RIVET__database__postgres__url="postgresql://user:password@host:5432/database" +``` + +## Managed Postgres Compatibility + +Some hosted PostgreSQL platforms require additional configuration due to platform-specific restrictions. + +### PlanetScale + +Use direct connection (not connection pooler). + +```json Configuration-file +{ + "database": { + "postgres": { + "url": "postgresql://pscale_api_.:@.pg.psdb.cloud:5432/postgres?sslmode=require", + "unstable_disable_lock_customization": true + } + } +} +``` + +```bash Environment-variables +RIVET__database__postgres__url="postgresql://pscale_api_.:@.pg.psdb.cloud:5432/postgres?sslmode=require" +RIVET__database__postgres__unstable_disable_lock_customization=true +``` + +### Supabase + +Use direct connection on port `5432` (not connection pooler). + +#### Without SSL + +```json Configuration-file +{ + "database": { + "postgres": { + "url": "postgresql://postgres:@db..supabase.co:5432/postgres?sslmode=disable", + "unstable_disable_lock_customization": true + } + } +} +``` + +```bash Environment-variables +RIVET__database__postgres__url="postgresql://postgres:@db..supabase.co:5432/postgres?sslmode=disable" +RIVET__database__postgres__unstable_disable_lock_customization=true +``` + +#### With SSL + +Download the root certificate from your Supabase dashboard and specify its path. See [Supabase SSL Enforcement](https://supabase.com/docs/guides/platform/ssl-enforcement) for details. + +```json Configuration-file +{ + "database": { + "postgres": { + "url": "postgresql://postgres:@db..supabase.co:5432/postgres?sslmode=require", + "unstable_disable_lock_customization": true, + "ssl": { + "root_cert_path": "/path/to/supabase-ca.crt" + } + } + } +} +``` + +```bash Environment-variables +RIVET__database__postgres__url="postgresql://postgres:@db..supabase.co:5432/postgres?sslmode=require" +RIVET__database__postgres__unstable_disable_lock_customization=true +RIVET__database__postgres__ssl__root_cert_path="/path/to/supabase-ca.crt" +``` + +## SSL/TLS Support + +To enable SSL for Postgres, add `sslmode=require` to your PostgreSQL connection URL: + +```json Configuration-file +{ + "database": { + "postgres": { + "url": "postgresql://user:password@host.example.com:5432/database?sslmode=require" + } + } +} +``` + +```bash Environment-variables +RIVET__database__postgres__url="postgresql://user:password@host.example.com:5432/database?sslmode=require" +``` + +The `sslmode` parameter controls TLS usage: + +- `disable`: Do not use TLS +- `prefer`: Use TLS if available, otherwise connect without TLS (default) +- `require`: Require TLS connection (fails if TLS is not available) + +To verify the server certificate against a CA or verify the hostname, use custom SSL certificates (see below). + +### Custom SSL Certificates + +For databases using custom certificate authorities (e.g., Supabase) or requiring client certificate authentication, you can specify certificate paths in the configuration: + +```json Configuration-file +{ + "database": { + "postgres": { + "url": "postgresql://user:password@host:5432/database?sslmode=require", + "ssl": { + "root_cert_path": "/path/to/root-ca.crt", + "client_cert_path": "/path/to/client.crt", + "client_key_path": "/path/to/client.key" + } + } + } +} +``` + +```bash Environment-variables +RIVET__database__postgres__url="postgresql://user:password@host:5432/database?sslmode=require" +RIVET__database__postgres__ssl__root_cert_path="/path/to/root-ca.crt" +RIVET__database__postgres__ssl__client_cert_path="/path/to/client.crt" +RIVET__database__postgres__ssl__client_key_path="/path/to/client.key" +``` + +| Parameter | Description | PostgreSQL Equivalent | +|-----------|-------------|----------------------| +| `root_cert_path` | Path to the root certificate file for verifying the server's certificate | `sslrootcert` | +| `client_cert_path` | Path to the client certificate file for client certificate authentication | `sslcert` | +| `client_key_path` | Path to the client private key file for client certificate authentication | `sslkey` | + +All SSL paths are optional. If not specified, Rivet uses the default system root certificates from Mozilla's root certificate store. + +## Do Not Use Connection Poolers + +Rivet requires direct PostgreSQL connections for session-level features and does not support connection poolers. + +Do not use: + +- PgBouncer +- Supavisor +- AWS RDS Proxy + +## Troubleshooting + +### Permission Denied Errors + +If you see errors like: + +``` +ERROR: permission denied to set parameter "deadlock_timeout" +ERROR: current transaction is aborted, commands ignored until end of transaction block +``` + +Add `unstable_disable_lock_customization: true` to your configuration: + +```json +{ + "database": { + "postgres": { + "url": "postgresql://...", + "unstable_disable_lock_customization": true + } + } +} +``` + +This disables Rivet's attempt to set `lock_timeout = 0` and `deadlock_timeout = 10ms`. Since `lock_timeout` defaults to `0` in PostgreSQL, skipping these settings is safe. Deadlock detection will use the default `1s` timeout instead of `10ms`. + +_Source doc path: /docs/self-hosting/postgres_ diff --git a/.agents/skills/multiplayer-game/reference/self-hosting/production-checklist.md b/.agents/skills/multiplayer-game/reference/self-hosting/production-checklist.md new file mode 100644 index 0000000..3cefa5a --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/self-hosting/production-checklist.md @@ -0,0 +1,53 @@ +# Production Checklist + +> Source: `src/content/docs/self-hosting/production-checklist.mdx` +> Canonical URL: https://rivet.dev/docs/self-hosting/production-checklist +> Description: Checklist for deploying a self-hosted Rivet Engine to production. + +--- +We recommend passing this page to your coding agent to verify your configuration before deploying. + +PostgreSQL is the recommended backend for multi-node self-hosted deployments today, but it remains experimental. For a production-ready single-node Rivet deployment, use the file system backend (RocksDB-based). Enterprise teams can contact [enterprise support](https://rivet.dev/sales) about FoundationDB for the most scalable production-ready deployment. + +Also review the [general production checklist](/docs/general/production-checklist). + +## Security + +- **Validate that you have an admin token configured.** Generate a strong, random token for engine authentication. See [Configuration](/docs/self-hosting/configuration). +- **Verify your admin token is not exposed publicly.** Do not include the admin token in `RIVET_PUBLIC_ENDPOINT` or anywhere accessible to clients. See [Endpoints](/docs/general/endpoints#public-endpoint). +- **Configure TLS termination.** Ensure connections to the engine are encrypted via a reverse proxy or load balancer. + +## Resources + +- **Set container resource limits.** Recommended at least 1 CPU and 2 GB of RAM per Rivet Engine instance. +- **Configure health checks.** Set up liveness and readiness probes on port `6421` at `/health`. Recommended timeout of 5 seconds. + +## Scaling + +- **Configure autoscaling for the Rivet Engine.** Set target CPU utilization to 70% and memory to 80% to ensure headroom for traffic spikes. In Kubernetes, this is configured via a Horizontal Pod Autoscaler (HPA). +- **Use 2+ engine nodes for redundancy.** Running a single engine node is a single point of failure. Deploy at least two engine instances behind a load balancer. +- **RocksDB only supports a single node.** Do not run multiple RocksDB nodes. For a production-ready single-node Rivet deployment, use the file system backend (RocksDB-based). For multi-node deployments, PostgreSQL is the recommended backend today, though it remains experimental as we evaluate the best fit for scalability and performance. +- **Validate the rate limit on your serverless actor host.** Actor start requests are sent from your engine instances, so they all originate from a small set of IPs. Per-IP rate limits on the actor host will throttle the engine before they would throttle end-user traffic. Size the limit to your peak actor create and wake rate, and configure platform max concurrency (e.g. on GCP Cloud Run) to match your expected concurrent actor count. + +## PostgreSQL + +- **PostgreSQL is recommended for multi-node deployments, but remains experimental.** Validate the deployment carefully before rollout. +- **Configure automated backups.** Set up regular backups for your PostgreSQL database to prevent data loss. +- **Configure failover.** Set up a standby replica with automatic failover to ensure high availability. +- **Use FoundationDB for the most scalable production-ready deployments.** FoundationDB provides the best performance, scalability, and uptime for Rivet. Contact [enterprise support](https://rivet.dev/sales) for FoundationDB guidance. + +## NATS + +- **Use NATS for pub/sub (recommended).** By default, Rivet uses PostgreSQL `LISTEN`/`NOTIFY` for pub/sub which has limited throughput. NATS significantly improves performance for high-traffic deployments. This is not needed if using RocksDB. See [Configuration](/docs/self-hosting/configuration). +- **Deploy 2+ NATS replicas.** Run at least two NATS replicas for high availability. + +## Monitoring + +- **Configure OpenTelemetry.** The Rivet Engine supports exporting traces and metrics via OpenTelemetry. Set `RIVET_OTEL_ENABLED=1` and `RIVET_OTEL_GRPC_ENDPOINT` to your collector endpoint (defaults to `http://localhost:4317`). Adjust `RIVET_OTEL_SAMPLER_RATIO` to control trace sampling (defaults to `0.001`). See [Configuration](/docs/self-hosting/configuration). +- **Set up alerts for critical metrics.** Monitor engine CPU, memory, request latency, and error rates. Configure alerts to notify your team before issues become outages. + +## Enterprise + +- **Contact [enterprise support](https://rivet.dev/sales) for production-ready deployments.** We can help with architecture review, scaling guidance, and FoundationDB support. + +_Source doc path: /docs/self-hosting/production-checklist_ diff --git a/.agents/skills/multiplayer-game/reference/self-hosting/railway.md b/.agents/skills/multiplayer-game/reference/self-hosting/railway.md new file mode 100644 index 0000000..c4bb922 --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/self-hosting/railway.md @@ -0,0 +1,99 @@ +# Railway Deployment + +> Source: `src/content/docs/self-hosting/railway.mdx` +> Canonical URL: https://rivet.dev/docs/self-hosting/railway +> Description: Railway provides a simple platform for deploying Rivet Engine with automatic scaling and managed infrastructure. + +--- +## Video Tutorial + +PostgreSQL is the recommended backend for multi-node self-hosted deployments today, but it remains experimental. For a production-ready single-node Rivet deployment, use the file system backend (RocksDB-based). Enterprise teams can contact [enterprise support](https://rivet.dev/sales) about FoundationDB for the most scalable production-ready deployment. + +## Quick Deploy + +Choose the template that best fits your needs: + +| **Rivet Template** | **Rivet Starter** | +|-------------------|-------------------| +| [![Deploy on Railway](https://railway.com/button.svg)](https://railway.com/deploy/rivet?referralCode=RC7bza&utm_medium=integration&utm_source=template&utm_campaign=generic) | [![Deploy Rivet Starter](https://railway.com/button.svg)](https://railway.com/deploy/rivet-starter) | +| **Blank template** to start fresh | **Complete example** with chat app | +| - Rivet Engine | - Pre-configured Rivet Engine | +| - PostgreSQL database | - Example chat application with Actors | +| - Basic configuration | - PostgreSQL database | +| - Manual setup required | - Rivet Inspector for debugging | +| | - Ready to run immediately | + +You can also use the [Rivet Railway template](https://github.com/rivet-dev/template-railway) as a starting point for your application. + +After deploying either template, you can find the `RIVET__AUTH__ADMIN_TOKEN` under the **Variables** tab in the Railway dashboard. This token is required to access the Rivet Inspector. + +## Manual Deployment + +### Prerequisites + +1. [Railway account](https://railway.app) +2. [Railway CLI](https://docs.railway.app/develop/cli) (optional) + +### Step 1: Create New Project + +```bash +# Using Railway CLI +railway init + +# Or create via dashboard +# https://railway.app/new +``` + +### Step 2: Add Services + +#### Deploy PostgreSQL Database + +1. Click "New Service" → "Database" → "PostgreSQL" +2. Railway automatically provisions and configures PostgreSQL +3. Note the connection string from the service variables + +#### Deploy Rivet Engine + +1. Click "New Service" → "Docker Image" +2. Set image: `rivetdev/engine:latest` +3. Configure environment variables: + - `RIVET__POSTGRES__URL=${{Postgres.DATABASE_URL}}` +4. Configure graceful shutdown (see [Graceful Shutdown](#graceful-shutdown) below) + +### Step 3: Deploy Your Application + +Follow the [Railway Quick Start guide](https://docs.railway.com/quick-start) to deploy your repository: + +1. Connect your GitHub account to Railway +2. Select your repository containing your Rivet application +3. Railway will automatically detect and deploy your application +4. Configure environment variables for your application: + - `RIVET_ENDPOINT=${{Rivet.RAILWAY_PRIVATE_DOMAIN}}` - Points to the Rivet Engine service's private domain + +## WebSocket Timeouts + +Rivet uses long-lived WebSocket connections for both client traffic (browsers, SDKs) and envoy traffic (actor hosts connecting back to the engine). Railway's HTTP proxy supports WebSockets, but you must make sure no app-side timeout cuts them off. + +If you front the engine with your own reverse proxy (NGINX, Caddy, etc.) inside the Railway service, raise its idle / read timeout to at least 1 hour (`3600` seconds). The same guidance applies to the Railway service hosting your RivetKit app, since envoys connect to it over WebSocket. + +## Graceful Shutdown + +By default, Railway kills the old deploy 0 seconds after sending `SIGTERM` (see [Railway's docs](https://docs.railway.com/deployments/reference#singleton-deploys)), so in-flight requests are dropped and state flushes can be interrupted on every deploy. Rivet ships with a `SIGTERM` handler that drains cleanly, but it only gets to run if Railway is configured to give it time. + +Configure the following under **Settings → Deploy** on your service: + +- **Draining seconds** — the grace window between `SIGTERM` and `SIGKILL`. This is how long Rivet has to finish in-flight work and flush state. See [Railway's deployment teardown docs](https://docs.railway.com/deployments/deployment-teardown). + +Set draining seconds in the dashboard, via `drainingSeconds` in config-as-code, or via the `RAILWAY_DEPLOYMENT_DRAINING_SECONDS` service variable. A reasonable value is **60 seconds**. + +If your start command is a wrapper process (for example `npm start`, `yarn start`, `pnpm start`, or a shell script), the wrapper becomes PID 1 and swallows the signal — your app never drains and Railway force-kills it at the end of the window. + +- Invoke your binary directly as the start command (for example `node dist/index.js`, not `npm start`). +- Or run `dumb-init` / `tini` as PID 1 in your Dockerfile so signals forward to your process. + +## Next Steps + +- Review the [Production Checklist](/docs/self-hosting/production-checklist) before going live +- See [Configuration](/docs/self-hosting/configuration) for all options + +_Source doc path: /docs/self-hosting/railway_ diff --git a/.agents/skills/multiplayer-game/reference/self-hosting/render.md b/.agents/skills/multiplayer-game/reference/self-hosting/render.md new file mode 100644 index 0000000..0bc6d6c --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/self-hosting/render.md @@ -0,0 +1,131 @@ +# Render Deployment + +> Source: `src/content/docs/self-hosting/render.mdx` +> Canonical URL: https://rivet.dev/docs/self-hosting/render +> Description: Deploy Rivet Engine to Render with managed PostgreSQL and automatic HTTPS, using the experimental PostgreSQL backend. + +--- +PostgreSQL is the recommended backend for multi-node self-hosted deployments today, but it remains experimental. For a production-ready single-node Rivet deployment, use the file system backend (RocksDB-based). Enterprise teams can contact [enterprise support](https://rivet.dev/sales) about FoundationDB for the most scalable production-ready deployment. + +## Prerequisites + +1. A [Render account](https://render.com) +2. A GitHub repository + +## Deploy Rivet Engine + + +### Create the Blueprint Files + + Add these three files to the root of your GitHub repository: + + **render.yaml** + + ```yaml + databases: + - name: rivet-db + plan: basic-256mb + databaseName: rivet + user: rivet + + services: + - type: web + name: rivet-engine + runtime: docker + dockerfilePath: ./Dockerfile.render + plan: starter + healthCheckPath: /health + envVars: + - key: DATABASE_URL + fromDatabase: + name: rivet-db + property: connectionString + - key: RIVET__AUTH__ADMIN_TOKEN + generateValue: true + ``` + + **Dockerfile.render** + + ```dockerfile + FROM rivetdev/engine:latest + + COPY entrypoint.render.sh /entrypoint.render.sh + RUN chmod +x /entrypoint.render.sh + + ENTRYPOINT ["/entrypoint.render.sh"] + ``` + + **entrypoint.render.sh** + + ```bash + #!/bin/sh + + if [ -n "$DATABASE_URL" ]; then + export RIVET__POSTGRES__URL="${DATABASE_URL}?sslmode=disable" + fi + + exec /usr/bin/rivet-engine start + ``` + + Commit and push these files to your repository. + + + +### Deploy to Render + + 1. Go to the [Render Dashboard](https://dashboard.render.com/) + 2. Click **Blueprints** in the left sidebar + 3. Click **New Blueprint Instance** + 4. Connect your GitHub account if you haven't already + 5. Select the repository containing the files from the previous step + 6. Click **Apply** + + Render will automatically create the PostgreSQL database and deploy the Rivet Engine. + + + +### Get Your Admin Token + + 1. Once deployed, go to your **rivet-engine** service in the Render Dashboard + 2. Click the **Environment** tab + 3. Find `RIVET__AUTH__ADMIN_TOKEN` and click the eye icon to reveal the value + 4. Copy this token — you'll need it to access the dashboard + + + +### Access the Rivet Dashboard + + Open your service URL in a browser: + + ``` + https://rivet-engine-xxxx.onrender.com/ui/ + ``` + + Replace `rivet-engine-xxxx` with your actual service name from the Render Dashboard. + + Enter the admin token from the previous step to log in. + + +## WebSocket Timeouts + +Rivet uses long-lived WebSocket connections for both client traffic (browsers, SDKs) and envoy traffic (actor hosts connecting back to the engine). Render's HTTP proxy supports WebSockets, but its default idle timeout will close connections that have no traffic for too long. + +If you front the engine with your own reverse proxy (NGINX, Caddy, etc.) on Render, raise its idle / read timeout to at least 1 hour (`3600` seconds). The same guidance applies to the Render service hosting your RivetKit app, since envoys connect to it over WebSocket. + +## Connecting Your Application + +To connect a RivetKit application to your self-hosted engine, set these environment variables in your app: + +```bash +RIVET_ENDPOINT=https://:@rivet-engine-xxxx.onrender.com +RIVET_PUBLIC_ENDPOINT=https://@rivet-engine-xxxx.onrender.com +``` + +See the [Connect guide](/docs/deploy/custom) for more details on connecting your application. + +## Next Steps + +- Review the [Production Checklist](/docs/self-hosting/production-checklist) before going live +- See [Configuration](/docs/self-hosting/configuration) for all options + +_Source doc path: /docs/self-hosting/render_ diff --git a/.agents/skills/multiplayer-game/reference/self-hosting/tls.md b/.agents/skills/multiplayer-game/reference/self-hosting/tls.md new file mode 100644 index 0000000..63573d2 --- /dev/null +++ b/.agents/skills/multiplayer-game/reference/self-hosting/tls.md @@ -0,0 +1,22 @@ +# TLS & Certificates + +> Source: `src/content/docs/self-hosting/tls.mdx` +> Canonical URL: https://rivet.dev/docs/self-hosting/tls +> Description: How Rivet validates TLS root certificates. + +--- +Public CAs (Let's Encrypt, AWS ACM, and so on) work out of the box. You only need to read this page if you're running behind a corporate or private CA. + +Rivet reads the operating system trust store (`/etc/ssl/certs` on Linux, Keychain on macOS, Schannel on Windows) for all outbound HTTPS. Operator-installed corporate CAs live there and are honored automatically. + +To trust a private CA inside the official `rivetdev/engine` image, extend it the standard Debian way: + +```dockerfile +FROM rivetdev/engine:latest +COPY my-corp-ca.crt /usr/local/share/ca-certificates/ +RUN update-ca-certificates +``` + +Rebuild and redeploy. The same approach works for any machine running a Rivet client: install the CA into the OS trust store and Rivet picks it up. + +_Source doc path: /docs/self-hosting/tls_ diff --git a/.agents/skills/per-tenant-database/BASE_SKILL.md b/.agents/skills/per-tenant-database/BASE_SKILL.md new file mode 100644 index 0000000..f03c1af --- /dev/null +++ b/.agents/skills/per-tenant-database/BASE_SKILL.md @@ -0,0 +1,537 @@ +--- +name: "rivetkit" +description: "RivetKit backend and Rivet Actor runtime guidance. Use for building, modifying, debugging, or testing Rivet Actors, registries, serverless/runner modes, deployment, or actor-based workflows." +--- + +# Rivet Actors + +Build high-performance, long-lived, in-memory processes on Rivet's actor runtime. Use this skill whenever HTTP endpoints, databases, or queues no longer meet the requirements for AI agents, realtime collaboration, workflow orchestration, or geo-distributed data. + +## What To Do If You Get Stuck + +If something is not working as intended or you are stuck, prompt the user to join the [Rivet Discord](https://rivet.dev/discord) or file an issue on the [Rivet GitHub](https://github.com/rivet-dev/rivet) to report an issue and get help. + +Provide the user with a pre-generated report with: + +- Symptoms + - If this is happening in local dev, deployed, or both + - The error you're seeing + - Relevant source code related to this +- What you've tried to solve it +- Environment + - RivetKit version + - Runtime (Node, Bun, etc) including version + - If applicable, provider in use (e.g. Vercel, Railway, etc) + - If applicable, HTTP router in use (e.g. Hono, Express, Elysia) + +## Debugging Actors + +Use the inspector HTTP API to examine running actors. These endpoints are accessible through the gateway at `/gateway/{actor_id}/inspector/*`. Key endpoints: + +- `GET /inspector/summary` - full actor snapshot (state, connections, RPCs, queue) +- `GET /inspector/state` / `PATCH /inspector/state` - read/write actor state +- `GET /inspector/connections` - active connections +- `GET /inspector/rpcs` - available actions +- `POST /inspector/action/{name}` - execute an action with `{"args": [...]}` +- `POST /inspector/database/execute` - run SQL with `{"sql": "...", "args": [...]}` or `{"sql": "...", "properties": {...}}` for reads or mutations +- `GET /inspector/queue?limit=50` - queue status +- `GET /inspector/traces?startMs=0&endMs=...&limit=1000` - trace spans (OTLP JSON) +- `GET /inspector/workflow-history` - workflow history and status as JSON (`nameRegistry`, `entries`, `entryMetadata`) +- `POST /inspector/workflow/replay` - replay a workflow from a specific step or from the beginning; returns `409 actor/workflow_in_flight` if the workflow is still running +- `GET /inspector/database/schema` - SQLite tables and views exposed by `c.db` +- `GET /inspector/database/rows?table=...&limit=100&offset=0` - paged SQLite rows for a table or view + +In local dev, no auth token is needed. In production, pass `Authorization: Bearer `, where the inspector token is the actor-specific token auto-generated on first start and persisted in the actor's internal KV at key `0x03`. The Rivet dashboard retrieves this token automatically; for direct API access, fetch it through the management KV endpoint. See the [debugging docs](https://rivet.dev/docs/actors/debugging) for details. + +## Citing Sources + +When providing information from Rivet documentation, cite the canonical URL so users can learn more. Each reference file includes its canonical URL in the header metadata. + +**How to cite:** + +- Use inline links for key concepts: "Use [actor keys](https://rivet.dev/docs/actors/keys) to uniquely identify instances." +- Add a "Learn more" link after explanations for complex topics + +**Finding canonical URLs:** + +The Reference Map below links to reference files. Each file's header contains: + +``` +> Canonical URL: https://rivet.dev/docs/actors/actions +``` + +Use that canonical URL when citing, not the reference file path. + +**Examples:** + +- Actions → `https://rivet.dev/docs/actors/actions` +- React client → `https://rivet.dev/docs/clients/react` +- Self-hosting on Kubernetes → `https://rivet.dev/docs/self-hosting/kubernetes` + +## Version Check + +Before starting any work, check if the user's project is on the latest version of RivetKit (latest: 2.3.5-rc.1). Look at the `rivetkit` version in the user's `package.json` (check both `dependencies` and `devDependencies`). If the installed version is older than 2.3.5-rc.1, inform the user and suggest upgrading: + +```bash +npm install rivetkit@2.3.5-rc.1 +``` + +If the user also uses `@rivetkit/react`, `@rivetkit/next-js`, or other `@rivetkit/*` client packages, suggest upgrading those too. Outdated versions may have known bugs or missing features that cause issues. + +## First Steps + +1. Install RivetKit (latest: 2.3.5-rc.1) + ```bash + npm install rivetkit@2.3.5-rc.1 + ``` +2. Define a registry with `setup({ use: { /* actors */ } })`. +3. Call `registry.start()` to start the server. For custom HTTP server integration, use `registry.handler()` with a router like Hono. For serverless deployments, use `registry.serve()`. For runner-only mode, use `registry.startEnvoy()`. +4. Verify `/api/rivet/metadata` returns 200 before deploying. +5. Configure Rivet Cloud or self-hosted engine + - You must configure versioning for production builds. This is not needed for local development. See [Versions & Upgrades](https://rivet.dev/docs/actors/versions). +6. Integrate clients (see client guides below for JavaScript, React, or Swift) +7. Prompt the user if they want to deploy. If so, go to Deploying Rivet Backends. + +For more information, read the quickstart guide relevant to the user's project. + +## Project Setup + +### .gitignore + +Every RivetKit project should have a `.gitignore`. Include at minimum: + +``` +node_modules/ +dist/ +.env +``` + +### .dockerignore + +Every project with a Dockerfile should have a `.dockerignore` to keep the image small and avoid leaking secrets: + +``` +node_modules/ +dist/ +.env +.git/ +``` + +### Dockerfile + +Use this as a base Dockerfile for deploying a RivetKit project. The `RIVET_RUNNER_VERSION` build arg is only needed when self-hosting or using a custom runner (not needed for Rivet Compute). It lets Rivet track which version of the actor is running and drain old actors on deploy. See https://rivet.dev/docs/actors/versions for details. + +```dockerfile +FROM node:24-alpine + +ARG RIVET_RUNNER_VERSION +ENV RIVET_RUNNER_VERSION=$RIVET_RUNNER_VERSION + +WORKDIR /app + +COPY package.json package-lock.json ./ +RUN npm ci + +COPY . . +RUN npm run build --if-present + +CMD ["node", "dist/index.js"] +``` + +Build with: + +```bash +docker build --build-arg RIVET_RUNNER_VERSION=$(date +%s) . +``` + +Adjust the `CMD` to match the project's entry point. If the project uses a different output directory or start command, update accordingly. + +## Error Handling Policy + +- Prefer fail-fast behavior by default. +- Avoid `try/catch` unless it is required for a real recovery path, cleanup boundary, or to add actionable context. +- Never swallow errors. If you add a `catch`, you must handle the error explicitly, at minimum by logging it. +- When you cannot recover, log context and rethrow. + +## State vs Vars: Persistence Rules + +**`c.vars` is ephemeral.** Data in `c.vars` is lost on every restart, crash, upgrade, or sleep/wake cycle. Only use `c.vars` for non-serializable objects (e.g., physics engines, WebSocket references, event emitters, caches) or truly transient runtime data (e.g., current input direction that doesn't matter after disconnect). + +**Persistent storage options.** Any data that must survive restarts belongs in one of these, NOT in `c.vars`: + +- **`c.state`** — CBOR-serializable data for small, bounded datasets. Ideal for configuration, counters, small player lists, phase flags, etc. Keep under 128 KB. Do not store unbounded or growing data here (e.g., chat logs, event histories, spawned entity lists that grow without limit). State is read/written as a single blob on every persistence cycle. +- **`c.kv`** — Key-value store for unbounded data. This is what `c.state` uses under the hood. Supports binary values. Use for larger or variable-size data like user inventories, world chunks, file blobs, or any collection that may grow over time. Keys are scoped to the actor instance. +- **`c.db`** — SQLite database for structured or complex data. Use when you need queries, indexes, joins, aggregations, or relational modeling. Ideal for leaderboards, match histories, player pools, or any data that benefits from SQL. + +**Common mistake:** Storing meaningful game/application data in `c.vars` instead of persisting it. For example, if users can spawn objects in a physics simulation, the spawn definitions (position, size, type) must be persisted in `c.state` (or `c.kv` if unbounded), even though the physics engine handles (non-serializable) live in `c.vars`. On restart, `run()` should recreate the runtime objects from the persisted data. + +## Deploying Rivet Backends + +Assume the user is deploying to Rivet Cloud, unless otherwise specified. If user is self-hosting, read the self-hosting guides below. + +1. Verify that Rivet Actors are working in local dev +2. Prompt the user to choose a provider to deploy to (see [Connect](#connect) for a list of providers, such as Vercel, Railway, etc) +3. Follow the deploy guide for that given provider. You will need to instruct the user when you need manual intervention. + +## API Reference + +The RivetKit OpenAPI specification is available in the skill directory at `openapi.json`. This file documents all HTTP endpoints for managing actors. + +## Misc Notes + +- The Rivet domain is rivet.dev, not rivet.gg + +## TypeScript Caveat: Actor Client Inference + +- In multi-file TypeScript projects, bidirectional actor calls can create a circular type dependency when both actors use `c.client()`. +- Symptoms usually include `c.state` becoming `unknown`, actor methods becoming possibly `undefined`, or `TS2322` / `TS2722` errors after the first cross-actor call. +- If an action returns the result of another actor call, prefer an explicit return type annotation on that action instead of relying on inference through `c.client()`. +- If explicit return types are not enough, use a narrower client or registry type for only the actors that action needs. +- As a last resort, pass `unknown` for the registry type and be explicit that this gives up type safety at that call site. + +## Features + +- **Long-Lived, Stateful Compute**: Each unit of compute is like a tiny server that remembers things between requests – no need to re-fetch data from a database or worry about timeouts. Like AWS Lambda, but with memory and no timeouts. +- **Blazing-Fast Reads & Writes**: State is stored on the same machine as your compute, so reads and writes are ultra-fast. No database round trips, no latency spikes. State is persisted to Rivet for long term storage, so it survives server restarts. +- **Realtime**: Update state and broadcast changes in realtime with WebSockets. No external pub/sub systems, no polling – just built-in low-latency events. +- **Infinitely Scalable**: Automatically scale from zero to millions of concurrent actors. Pay only for what you use with instant scaling and no cold starts. +- **Fault Tolerant**: Built-in error handling and recovery. Actors automatically restart on failure while preserving state integrity and continuing operations. + +## When to Use Rivet Actors + +- **AI agents & sandboxes**: multi-step toolchains, conversation memory, sandbox orchestration. +- **Multiplayer or collaborative apps**: CRDT docs, shared cursors, realtime dashboards, chat. +- **Workflow automation**: background jobs, cron, rate limiters, durable queues, backpressure control. +- **Data-intensive backends**: geo-distributed or per-tenant databases, in-memory caches, sharded SQL. +- **Networking workloads**: WebSocket servers, custom protocols, local-first sync, edge fanout. + +## Minimal Project + +### Backend + +**index.ts** + +### Client Docs + +Use the client SDK that matches your app: + +- [JavaScript Client](/docs/clients/javascript) +- [React Client](/docs/clients/react) +- [Swift Client](/docs/clients/swift) + +## Actor Quick Reference + +### In-Memory State + +Persistent data that survives restarts, crashes, and deployments. State is persisted on Rivet Cloud or Rivet self-hosted, so it survives restarts if the current process crashes or exits. + +### Static Initial State + +### Dynamic Initial State + +[Documentation](/docs/actors/state) + +### Keys + +Keys uniquely identify actor instances. Use compound keys (arrays) for hierarchical addressing: + +Don't build keys with string interpolation like `"org:${userId}"` when `userId` contains user data. Use arrays instead to prevent key injection attacks. + +[Documentation](/docs/actors/keys) + +### Input + +Pass initialization data when creating actors. Input is only available in `createState` and `onCreate`, so store it in state if you need it later. + +[Documentation](/docs/actors/input) + +### Temporary Variables + +Temporary data that doesn't survive restarts. Use for non-serializable objects (event emitters, connections, etc). + +### Static Initial Vars + +### Dynamic Initial Vars + +[Documentation](/docs/actors/state) + +### Actions + +Actions are the primary way clients and other actors communicate with an actor. + +[Documentation](/docs/actors/actions) + +### Events & Broadcasts + +Events enable real-time communication from actors to connected clients. + +[Documentation](/docs/actors/events) + +### Connections + +Access the current connection via `c.conn` or all connected clients via `c.conns`. Use `c.conn.id` or `c.conn.state` to securely identify who is calling an action. `c.conn` is only available for actions invoked through a connected client; stateless actor-handle calls run without a connection, so guard against that. Connection state is initialized via `connState` or `createConnState`, which receives parameters passed by the client on connect. + +### Static Connection Initial State + +### Dynamic Connection Initial State + +[Documentation](/docs/actors/connections) + +### Queues + +Use queues to process durable messages in order inside a `run` loop. + +[Documentation](/docs/actors/queues) + +### Workflows + +Use workflows when your `run` logic needs durable, replayable multi-step execution. + +[Documentation](/docs/actors/workflows) + +### Actor-to-Actor Communication + +Actors can call other actors using `c.client()`. + +[Documentation](/docs/actors/communicating-between-actors) + +### Schedule & Cron + +Run one-shot actions after a delay or at a specific time. Use named cron jobs for calendar schedules and fixed intervals. Both persist across sleep, restarts, upgrades, and crashes. + +[Documentation](/docs/actors/schedule) + +### Destroying Actors + +Permanently delete an actor and its state using `c.destroy()`. + +[Documentation](/docs/actors/destroy) + +### Lifecycle Hooks + +Actors support hooks for initialization, background processing, connections, networking, and state changes. Use `run` for long-lived background loops, and use `c.aborted` or `c.abortSignal` for graceful shutdown. + +[Documentation](/docs/actors/lifecycle) + +### Context Types + +When writing helper functions outside the actor definition, use `*ContextOf` to extract the correct context type. Helpers like `ActionContextOf`, `CreateContextOf`, `ConnContextOf`, and `ConnInitContextOf` are exported from `"rivetkit"`. Do not manually define your own context interface. Always derive it from the actor definition. + +[Documentation](/docs/actors/types) + +### Errors + +Use `UserError` to throw errors that are safely returned to clients. Pass `metadata` to include structured data. Other errors are converted to generic "internal error" for security. + +### Actor + +### Client + +[Documentation](/docs/actors/errors) + +### Low-Level HTTP & WebSocket Handlers + +For custom protocols or integrating libraries that need direct access to HTTP `Request`/`Response` or WebSocket connections, use `onRequest` and `onWebSocket`. + +[HTTP Handler Documentation](/docs/actors/request-handler) · [WebSocket Handler Documentation](/docs/actors/websocket-handler) + +### Icons & Names + +Customize how actors appear in the UI with display names and icons. It's recommended to always provide a name and icon to actors in order to make them easier to distinguish in the dashboard. + +```typescript +import { actor } from "rivetkit"; + +const chatRoom = actor({ + options: { + name: "Chat Room", + icon: "💬", // or FontAwesome: "comments", "chart-line", etc. + }, + // ... +}); +``` + +[Documentation](/docs/actors/appearance) + +## Client Documentation + +Find the full client guides here: + +- [JavaScript Client](/docs/clients/javascript) +- [React Client](/docs/clients/react) +- [Swift Client](/docs/clients/swift) + +## Common Patterns + +Actors scale naturally through isolated state and message-passing. Structure your applications with these patterns: + +[Documentation](/docs/actors/design-patterns) + +### Actor Per Entity + +Create one actor per user, document, or room. Use compound keys to scope entities: + +### Coordinator & Data Actors + +**Data actors** handle core logic (chat rooms, game sessions, user data). **Coordinator actors** track and manage collections of data actors—think of them as an index. + +### Run Loop + +Use a `run` loop for continuous background work inside an actor. Process queue messages in order, run logic on intervals, stream AI responses, or coordinate long-running tasks. + +### Workflow Loop + +Use this pattern for long-lived, durable workflows that initialize resources, process commands in a loop, then clean up. + +[Documentation](/docs/actors/workflows) + +### Actions vs Queues + +- **Actions** are not durable. Use them for realtime reads, ephemeral data, and low-latency communication like player input. +- **Queues** are durable. Use them to serialize mutations through the run loop, avoiding race conditions with SQLite and other local state. Callers can still wait for a response from queued work. + +### Authentication, Security, & CORS + +- Validate credentials in `onBeforeConnect` or `createConnState` and throw an error to reject unauthorized connections. +- Use `c.conn.state` to securely identify users in actions rather than trusting action parameters. +- For cross-origin access, validate the request origin in `onBeforeConnect`. + +[Authentication Documentation](/docs/actors/authentication) · [CORS Documentation](/docs/general/cors) + +### Versions & Upgrades + +When deploying new code, set a version number so Rivet can route new actors to the latest runner and optionally drain old ones. Use a build timestamp, git commit count, or CI build number as the version. It is very important to [configure versioning](/docs/actors/versions) before deploying to production. Without versioning, actors can regress by running on older runner versions, and existing actors will never be forced to migrate to new runners. They will continue running indefinitely on the old runners until they exit. + +[Documentation](/docs/actors/versions) + +### Anti-Patterns + +#### Never build a "god" actor + +Do not put all your logic in a single actor. A god actor serializes every operation through one bottleneck, kills parallelism, and makes the entire system fail as a unit. Split into focused actors per entity. + +#### Never create an actor per request + +Actors are long-lived and maintain state across requests. Creating a new actor for every incoming request throws away the core benefit of the model and wastes resources on actor creation and teardown. Use actors for persistent entities and regular functions for stateless work. + +## Reference Map + +### Actors + +- [Access Control](reference/actors/access-control.md) +- [Actions](reference/actors/actions.md) +- [Actor Keys](reference/actors/keys.md) +- [Actor Runtime Socket](reference/actors/actor-runtime-socket.md) +- [Actor Statuses](reference/actors/statuses.md) +- [Authentication](reference/actors/authentication.md) +- [Cloudflare Workers Quickstart](reference/actors/quickstart/cloudflare.md) +- [Communicating Between Actors](reference/actors/communicating-between-actors.md) +- [Connections](reference/actors/connections.md) +- [Custom Inspector Tabs](reference/actors/inspector-tabs.md) +- [Debugging](reference/actors/debugging.md) +- [Design Patterns](reference/actors/design-patterns.md) +- [Destroying Actors](reference/actors/destroy.md) +- [Effect.ts Quickstart (Beta)](reference/actors/quickstart/effect.md) +- [Errors](reference/actors/errors.md) +- [Fetch and WebSocket Handler](reference/actors/fetch-and-websocket-handler.md) +- [Helper Types](reference/actors/helper-types.md) +- [Icons & Names](reference/actors/appearance.md) +- [In-Memory State](reference/actors/state.md) +- [Input Parameters](reference/actors/input.md) +- [Lifecycle](reference/actors/lifecycle.md) +- [Limits](reference/actors/limits.md) +- [Low-Level HTTP Request Handler](reference/actors/request-handler.md) +- [Low-Level KV Storage](reference/actors/kv.md) +- [Low-Level WebSocket Handler](reference/actors/websocket-handler.md) +- [Metadata](reference/actors/metadata.md) +- [Next.js Quickstart](reference/actors/quickstart/next-js.md) +- [Node.js & Bun Quickstart](reference/actors/quickstart/backend.md) +- [Queues & Run Loops](reference/actors/queues.md) +- [React Quickstart](reference/actors/quickstart/react.md) +- [Realtime](reference/actors/events.md) +- [Rust Quickstart (Beta)](reference/actors/quickstart/rust.md) +- [Scaling & Concurrency](reference/actors/scaling.md) +- [Schedule & Cron](reference/actors/schedule.md) +- [Sharing and Joining State](reference/actors/sharing-and-joining-state.md) +- [SQLite](reference/actors/sqlite.md) +- [SQLite + Drizzle](reference/actors/sqlite-drizzle.md) +- [Supabase Functions Quickstart](reference/actors/quickstart/supabase.md) +- [Testing](reference/actors/testing.md) +- [Troubleshooting](reference/actors/troubleshooting.md) +- [Types](reference/actors/types.md) +- [Vanilla HTTP API](reference/actors/http-api.md) +- [Versions & Upgrades](reference/actors/versions.md) +- [Workflows](reference/actors/workflows.md) + +### Cli + +- [CLI](reference/cli.md) + +### Clients + +- [Node.js & Bun](reference/clients/javascript.md) +- [React](reference/clients/react.md) +- [Rust (Beta)](reference/clients/rust.md) +- [Swift](reference/clients/swift.md) +- [SwiftUI](reference/clients/swiftui.md) + +### Cookbook + +- [AI Agent](reference/cookbook/ai-agent.md) +- [Chat Room](reference/cookbook/chat-room.md) +- [Collaborative Text Editor](reference/cookbook/collaborative-text-editor.md) +- [Cron Jobs and Scheduled Tasks](reference/cookbook/cron-jobs.md) +- [Database per Tenant](reference/cookbook/per-tenant-database.md) +- [Deploying Rivet in a VPC or Air-Gapped Network](reference/cookbook/vpc-air-gapped.md) +- [Live Cursors and Presence](reference/cookbook/live-cursors.md) +- [Multiplayer Game](reference/cookbook/multiplayer-game.md) + +### Deploy + +- [Container Runner](reference/deploy/container-runner.md) +- [Deploy To Amazon Web Services Lambda](reference/deploy/aws-lambda.md) +- [Deploying to AWS ECS](reference/deploy/aws-ecs.md) +- [Deploying to Cloudflare Workers](reference/deploy/cloudflare.md) +- [Deploying to Freestyle](reference/deploy/freestyle.md) +- [Deploying to Google Cloud Run](reference/deploy/gcp-cloud-run.md) +- [Deploying to Hetzner](reference/deploy/hetzner.md) +- [Deploying to Kubernetes](reference/deploy/kubernetes.md) +- [Deploying to Railway](reference/deploy/railway.md) +- [Deploying to Rivet Compute](reference/deploy/rivet-compute.md) +- [Deploying to Supabase Functions](reference/deploy/supabase.md) +- [Deploying to Vercel](reference/deploy/vercel.md) +- [Deploying to VMs & Bare Metal](reference/deploy/vm-and-bare-metal.md) + +### General + +- [Actor Configuration](reference/general/actor-configuration.md) +- [Architecture](reference/general/architecture.md) +- [Cross-Origin Resource Sharing](reference/general/cors.md) +- [Documentation for LLMs & AI](reference/general/docs-for-llms.md) +- [Edge Networking](reference/general/edge.md) +- [Endpoints](reference/general/endpoints.md) +- [Environment Variables](reference/general/environment-variables.md) +- [HTTP Server](reference/general/http-server.md) +- [Logging](reference/general/logging.md) +- [Pool Configuration](reference/general/pool-configuration.md) +- [Production Checklist](reference/general/production-checklist.md) +- [Registry Configuration](reference/general/registry-configuration.md) +- [Runtime Modes](reference/general/runtime-modes.md) +- [WASM vs Native SDK](reference/general/wasm-vs-native-sdk.md) + +### Self Hosting + +- [Configuration](reference/self-hosting/configuration.md) +- [Docker Compose](reference/self-hosting/docker-compose.md) +- [Docker Container](reference/self-hosting/docker-container.md) +- [File System](reference/self-hosting/filesystem.md) +- [FoundationDB (Enterprise)](reference/self-hosting/foundationdb.md) +- [Installing Rivet Engine](reference/self-hosting/install.md) +- [Kubernetes](reference/self-hosting/kubernetes.md) +- [Multi-Region](reference/self-hosting/multi-region.md) +- [PostgreSQL](reference/self-hosting/postgres.md) +- [Production Checklist](reference/self-hosting/production-checklist.md) +- [Railway Deployment](reference/self-hosting/railway.md) +- [Render Deployment](reference/self-hosting/render.md) +- [TLS & Certificates](reference/self-hosting/tls.md) + diff --git a/.agents/skills/per-tenant-database/SKILL.md b/.agents/skills/per-tenant-database/SKILL.md new file mode 100644 index 0000000..00f4ea4 --- /dev/null +++ b/.agents/skills/per-tenant-database/SKILL.md @@ -0,0 +1,248 @@ +--- +name: "per-tenant-database" +description: "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." +--- + +# Database per Tenant + +**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.** + +## Working Examples + +If you need a reference implementation, read the raw working example code in these templates: + +- [per-tenant-database](https://github.com/rivet-dev/rivet/tree/main/examples/per-tenant-database) + + +Patterns 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. + +## Starter Code + +Start with the working example on [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/per-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. + +| Topic | Summary | +| --- | --- | +| Isolation | One `companyDatabase` actor per tenant, keyed by company name. Switching tenants swaps the entire dataset. | +| State | JSON actor state holding `employees` and `projects` arrays plus timestamps. No SQLite, no queues, no scheduling. | +| Realtime | Every write action mutates state, then broadcasts a typed event (`employeeAdded`, `projectAdded`) to all connected clients of that tenant. | +| Auth | None. The sign-in screen is cosmetic. Production guidance is in the [security checklist](#security-checklist). | + +## The Isolation Model + +The 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: + +- **One actor per tenant**: `companyDatabase[tenantId]` addresses exactly one actor instance. Two tenants can never share an actor. +- **One dataset per tenant**: All reads and writes go through that actor's [state](/docs/actors/state), 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. +- **No key injection**: Keys are arrays, not interpolated strings. `key: [tenantId]` cannot be escaped the way `"tenant:" + tenantId` string concatenation can. See [Keys](/docs/actors/keys). + +The example's test ([tests/per-tenant-database.test.ts](https://github.com/rivet-dev/rivet/tree/main/examples/per-tenant-database/tests/per-tenant-database.test.ts)) proves the isolation: data written to `companyDatabase["Alpha Co"]` never appears in `companyDatabase["Beta Co"]`. + +## Choosing a State Backend + +The example uses plain JSON actor state. The same key-equals-tenant model works with any actor state backend. + +| Backend | Use When | Docs | Working Code | +| --- | --- | --- | --- | +| JSON actor state | Small datasets, simple reads, whole dataset fits comfortably in memory. What the example uses. | [State](/docs/actors/state) | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/per-tenant-database) | +| Actor SQLite (`rivetkit/db`) | Tables, indexes, SQL queries, larger-than-memory data, per-tenant relational schema. | [SQLite](/docs/actors/sqlite) | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/kitchen-sink/src/actors/state/sqlite-raw.ts) | +| SQLite + Drizzle | Typed schema, query builder, and generated migration files on top of actor SQLite. | [SQLite + Drizzle](/docs/actors/sqlite-drizzle) | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/kitchen-sink/src/actors/state/sqlite-drizzle/) | + +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. + +## Migrations + +The per-tenant example has no migrations because JSON state has no schema. When you adopt SQLite, migrations run per tenant database: + +- **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](/docs/actors/sqlite). +- **Drizzle**: `drizzle-kit` generates migration files from your typed schema, and `db({ schema, migrations })` applies them when the actor wakes. See [SQLite + Drizzle](/docs/actors/sqlite-drizzle). + +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. + +## Tenant Id Must Come From Auth + +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): + +- Derive the tenant id from a verified credential, such as a JWT claim, never from user input. +- Validate the credential against `c.key` in `onBeforeConnect` (pass/fail) or `createConnState` (store the verified user on connection state). See [Authentication](/docs/actors/authentication) and [Connections](/docs/actors/connections). +- Add per-action permission checks on top of connection-level auth. See [Access Control](/docs/actors/access-control). + +## Actors + +- **Key**: `companyDatabase[companyName]` (single-element array key; `c.key[0]` is the company name) +- **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. +- **Actions** + - `addEmployee` + - `listEmployees` + - `addProject` + - `listProjects` + - `getStats` +- **Queues** + - None +- **Events** + - `employeeAdded` + - `projectAdded` +- **State** + - JSON + - `company_name` + - `employees` + - `projects` + - `created_at` + - `updated_at` + +Every 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](/docs/actors/actions) and [Events](/docs/actors/events). + +## Lifecycle + +```mermaid +sequenceDiagram + participant A as Tenant A client + participant DA as companyDatabase A + participant B as Tenant B client + participant DB as companyDatabase B + + Note over A: authenticate and derive tenant id + A->>DA: connect with key [tenantA] + Note over DA: createState seeds company_name, employees, projects + A->>DA: listEmployees() + listProjects() + getStats() + A->>DA: addEmployee(name, role) + DA-->>A: employeeAdded event + B->>DB: connect with key [tenantB] + Note over DB: separate actor, separate dataset + B->>DB: listEmployees() + DB-->>B: tenant B data only +``` + +In 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. + +## Security Checklist + +The example ships with none of these. Apply all of them before production. + +- **Tenant identity**: Derive the tenant id from a verified JWT claim, never from a client-supplied string. +- **Connection validation**: In `onBeforeConnect` or `createConnState`, verify the credential's tenant claim matches `c.key` and reject mismatches. +- **Per-action authorization**: Check the caller's role before mutating actions (`addEmployee`, `addProject`), not just at connect time. See [Access Control](/docs/actors/access-control). +- **Input validation**: Clamp name and role lengths and validate enums. The example only trims input and substitutes fallback defaults. +- **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. +- **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](/docs/actors/sqlite) when the dataset outgrows memory. + +## Reference Map + +### Actors + +- [Access Control](reference/actors/access-control.md) +- [Actions](reference/actors/actions.md) +- [Actor Keys](reference/actors/keys.md) +- [Actor Runtime Socket](reference/actors/actor-runtime-socket.md) +- [Actor Statuses](reference/actors/statuses.md) +- [Authentication](reference/actors/authentication.md) +- [Cloudflare Workers Quickstart](reference/actors/quickstart/cloudflare.md) +- [Communicating Between Actors](reference/actors/communicating-between-actors.md) +- [Connections](reference/actors/connections.md) +- [Custom Inspector Tabs](reference/actors/inspector-tabs.md) +- [Debugging](reference/actors/debugging.md) +- [Design Patterns](reference/actors/design-patterns.md) +- [Destroying Actors](reference/actors/destroy.md) +- [Effect.ts Quickstart (Beta)](reference/actors/quickstart/effect.md) +- [Errors](reference/actors/errors.md) +- [Fetch and WebSocket Handler](reference/actors/fetch-and-websocket-handler.md) +- [Helper Types](reference/actors/helper-types.md) +- [Icons & Names](reference/actors/appearance.md) +- [In-Memory State](reference/actors/state.md) +- [Input Parameters](reference/actors/input.md) +- [Lifecycle](reference/actors/lifecycle.md) +- [Limits](reference/actors/limits.md) +- [Low-Level HTTP Request Handler](reference/actors/request-handler.md) +- [Low-Level KV Storage](reference/actors/kv.md) +- [Low-Level WebSocket Handler](reference/actors/websocket-handler.md) +- [Metadata](reference/actors/metadata.md) +- [Next.js Quickstart](reference/actors/quickstart/next-js.md) +- [Node.js & Bun Quickstart](reference/actors/quickstart/backend.md) +- [Queues & Run Loops](reference/actors/queues.md) +- [React Quickstart](reference/actors/quickstart/react.md) +- [Realtime](reference/actors/events.md) +- [Rust Quickstart (Beta)](reference/actors/quickstart/rust.md) +- [Scaling & Concurrency](reference/actors/scaling.md) +- [Schedule & Cron](reference/actors/schedule.md) +- [Sharing and Joining State](reference/actors/sharing-and-joining-state.md) +- [SQLite](reference/actors/sqlite.md) +- [SQLite + Drizzle](reference/actors/sqlite-drizzle.md) +- [Supabase Functions Quickstart](reference/actors/quickstart/supabase.md) +- [Testing](reference/actors/testing.md) +- [Troubleshooting](reference/actors/troubleshooting.md) +- [Types](reference/actors/types.md) +- [Vanilla HTTP API](reference/actors/http-api.md) +- [Versions & Upgrades](reference/actors/versions.md) +- [Workflows](reference/actors/workflows.md) + +### Cli + +- [CLI](reference/cli.md) + +### Clients + +- [Node.js & Bun](reference/clients/javascript.md) +- [React](reference/clients/react.md) +- [Rust (Beta)](reference/clients/rust.md) +- [Swift](reference/clients/swift.md) +- [SwiftUI](reference/clients/swiftui.md) + +### Cookbook + +- [AI Agent](reference/cookbook/ai-agent.md) +- [Chat Room](reference/cookbook/chat-room.md) +- [Collaborative Text Editor](reference/cookbook/collaborative-text-editor.md) +- [Cron Jobs and Scheduled Tasks](reference/cookbook/cron-jobs.md) +- [Database per Tenant](reference/cookbook/per-tenant-database.md) +- [Deploying Rivet in a VPC or Air-Gapped Network](reference/cookbook/vpc-air-gapped.md) +- [Live Cursors and Presence](reference/cookbook/live-cursors.md) +- [Multiplayer Game](reference/cookbook/multiplayer-game.md) + +### Deploy + +- [Container Runner](reference/deploy/container-runner.md) +- [Deploy To Amazon Web Services Lambda](reference/deploy/aws-lambda.md) +- [Deploying to AWS ECS](reference/deploy/aws-ecs.md) +- [Deploying to Cloudflare Workers](reference/deploy/cloudflare.md) +- [Deploying to Freestyle](reference/deploy/freestyle.md) +- [Deploying to Google Cloud Run](reference/deploy/gcp-cloud-run.md) +- [Deploying to Hetzner](reference/deploy/hetzner.md) +- [Deploying to Kubernetes](reference/deploy/kubernetes.md) +- [Deploying to Railway](reference/deploy/railway.md) +- [Deploying to Rivet Compute](reference/deploy/rivet-compute.md) +- [Deploying to Supabase Functions](reference/deploy/supabase.md) +- [Deploying to Vercel](reference/deploy/vercel.md) +- [Deploying to VMs & Bare Metal](reference/deploy/vm-and-bare-metal.md) + +### General + +- [Actor Configuration](reference/general/actor-configuration.md) +- [Architecture](reference/general/architecture.md) +- [Cross-Origin Resource Sharing](reference/general/cors.md) +- [Documentation for LLMs & AI](reference/general/docs-for-llms.md) +- [Edge Networking](reference/general/edge.md) +- [Endpoints](reference/general/endpoints.md) +- [Environment Variables](reference/general/environment-variables.md) +- [HTTP Server](reference/general/http-server.md) +- [Logging](reference/general/logging.md) +- [Pool Configuration](reference/general/pool-configuration.md) +- [Production Checklist](reference/general/production-checklist.md) +- [Registry Configuration](reference/general/registry-configuration.md) +- [Runtime Modes](reference/general/runtime-modes.md) +- [WASM vs Native SDK](reference/general/wasm-vs-native-sdk.md) + +### Self Hosting + +- [Configuration](reference/self-hosting/configuration.md) +- [Docker Compose](reference/self-hosting/docker-compose.md) +- [Docker Container](reference/self-hosting/docker-container.md) +- [File System](reference/self-hosting/filesystem.md) +- [FoundationDB (Enterprise)](reference/self-hosting/foundationdb.md) +- [Installing Rivet Engine](reference/self-hosting/install.md) +- [Kubernetes](reference/self-hosting/kubernetes.md) +- [Multi-Region](reference/self-hosting/multi-region.md) +- [PostgreSQL](reference/self-hosting/postgres.md) +- [Production Checklist](reference/self-hosting/production-checklist.md) +- [Railway Deployment](reference/self-hosting/railway.md) +- [Render Deployment](reference/self-hosting/render.md) +- [TLS & Certificates](reference/self-hosting/tls.md) + diff --git a/.agents/skills/per-tenant-database/index.json b/.agents/skills/per-tenant-database/index.json new file mode 100644 index 0000000..521f98b --- /dev/null +++ b/.agents/skills/per-tenant-database/index.json @@ -0,0 +1,694 @@ +{ + "name": "per-tenant-database", + "description": "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.", + "skill_url": "/metadata/skills/per-tenant-database/SKILL.md", + "generated_at": "2026-07-20T21:43:04.674Z", + "references": [ + { + "name": "actors/access-control", + "title": "Access Control", + "description": "Authorize actions, queue publishes, and event subscriptions with explicit hooks.", + "canonical_url": "https://rivet.dev/docs/actors/access-control", + "reference_url": "/metadata/skills/per-tenant-database/reference/actors/access-control.md" + }, + { + "name": "actors/actions", + "title": "Actions", + "description": "Actions are how your backend, frontend, or other actors can communicate with actors.", + "canonical_url": "https://rivet.dev/docs/actors/actions", + "reference_url": "/metadata/skills/per-tenant-database/reference/actors/actions.md" + }, + { + "name": "general/actor-configuration", + "title": "Actor Configuration", + "description": "This page documents the configuration options available when defining a RivetKit actor. The actor configuration is passed to the `actor()` function.", + "canonical_url": "https://rivet.dev/docs/general/actor-configuration", + "reference_url": "/metadata/skills/per-tenant-database/reference/general/actor-configuration.md" + }, + { + "name": "actors/keys", + "title": "Actor Keys", + "description": "Actor keys uniquely identify actor instances within each actor type. Keys are used for addressing which specific actor to communicate with.", + "canonical_url": "https://rivet.dev/docs/actors/keys", + "reference_url": "/metadata/skills/per-tenant-database/reference/actors/keys.md" + }, + { + "name": "actors/actor-runtime-socket", + "title": "Actor Runtime Socket", + "description": "Access an actor's SQLite database from another local process.", + "canonical_url": "https://rivet.dev/docs/actors/actor-runtime-socket", + "reference_url": "/metadata/skills/per-tenant-database/reference/actors/actor-runtime-socket.md" + }, + { + "name": "actors/statuses", + "title": "Actor Statuses", + "description": "Understand the lifecycle statuses of Rivet Actors, what they mean, how they appear in the API, and how to troubleshoot common issues.", + "canonical_url": "https://rivet.dev/docs/actors/statuses", + "reference_url": "/metadata/skills/per-tenant-database/reference/actors/statuses.md" + }, + { + "name": "cookbook/ai-agent", + "title": "AI Agent", + "description": "Build an AI agent backend with persistent memory: one Rivet Actor per conversation, queued message handling, and streaming LLM responses as realtime events.", + "canonical_url": "https://rivet.dev/cookbook/ai-agent", + "reference_url": "/metadata/skills/per-tenant-database/reference/cookbook/ai-agent.md" + }, + { + "name": "general/architecture", + "title": "Architecture", + "description": "- rivetkit is the typescript library used for both local development & to connect your application to rivet - a rivetkit instance is called a \"runner.\" you can run multiple runners to scale rivetkit horizontally. read omre about runners below.", + "canonical_url": "https://rivet.dev/docs/general/architecture", + "reference_url": "/metadata/skills/per-tenant-database/reference/general/architecture.md" + }, + { + "name": "actors/authentication", + "title": "Authentication", + "description": "Secure your actors with authentication and authorization.", + "canonical_url": "https://rivet.dev/docs/actors/authentication", + "reference_url": "/metadata/skills/per-tenant-database/reference/actors/authentication.md" + }, + { + "name": "cookbook/chat-room", + "title": "Chat Room", + "description": "Build a realtime chat room backend with Rivet Actors: one actor per room, SQLite-backed message history, and WebSocket broadcast to every connected client.", + "canonical_url": "https://rivet.dev/cookbook/chat-room", + "reference_url": "/metadata/skills/per-tenant-database/reference/cookbook/chat-room.md" + }, + { + "name": "cli", + "title": "CLI", + "description": "Reference for the optional rivet CLI: deploy to Rivet Compute and run local dev for serverless platforms.", + "canonical_url": "https://rivet.dev/docs/cli", + "reference_url": "/metadata/skills/per-tenant-database/reference/cli.md" + }, + { + "name": "actors/quickstart/cloudflare", + "title": "Cloudflare Workers Quickstart", + "description": "Set up a Rivet project locally targeting Cloudflare Workers.", + "canonical_url": "https://rivet.dev/docs/actors/quickstart/cloudflare", + "reference_url": "/metadata/skills/per-tenant-database/reference/actors/quickstart/cloudflare.md" + }, + { + "name": "cookbook/collaborative-text-editor", + "title": "Collaborative Text Editor", + "description": "Build a collaborative text editor backend with Yjs CRDTs and Rivet Actors: per-document actors relay sync and awareness updates and persist snapshots.", + "canonical_url": "https://rivet.dev/cookbook/collaborative-text-editor", + "reference_url": "/metadata/skills/per-tenant-database/reference/cookbook/collaborative-text-editor.md" + }, + { + "name": "actors/communicating-between-actors", + "title": "Communicating Between Actors", + "description": "Learn how actors can call other actors and share data", + "canonical_url": "https://rivet.dev/docs/actors/communicating-between-actors", + "reference_url": "/metadata/skills/per-tenant-database/reference/actors/communicating-between-actors.md" + }, + { + "name": "self-hosting/configuration", + "title": "Configuration", + "description": "Rivet Engine can be configured through environment variables or configuration files.", + "canonical_url": "https://rivet.dev/docs/self-hosting/configuration", + "reference_url": "/metadata/skills/per-tenant-database/reference/self-hosting/configuration.md" + }, + { + "name": "actors/connections", + "title": "Connections", + "description": "Connections represent client connections to your actor. They provide a way to handle client authentication, manage connection-specific data, and control the connection lifecycle.", + "canonical_url": "https://rivet.dev/docs/actors/connections", + "reference_url": "/metadata/skills/per-tenant-database/reference/actors/connections.md" + }, + { + "name": "deploy/container-runner", + "title": "Container Runner", + "description": "Run any containerized server as a Rivet Actor.", + "canonical_url": "https://rivet.dev/docs/deploy/container-runner", + "reference_url": "/metadata/skills/per-tenant-database/reference/deploy/container-runner.md" + }, + { + "name": "cookbook/cron-jobs", + "title": "Cron Jobs and Scheduled Tasks", + "description": "Patterns for durable one-shot, calendar, and fixed-interval work on Rivet Actors.", + "canonical_url": "https://rivet.dev/cookbook/cron-jobs", + "reference_url": "/metadata/skills/per-tenant-database/reference/cookbook/cron-jobs.md" + }, + { + "name": "general/cors", + "title": "Cross-Origin Resource Sharing", + "description": "Cross-Origin Resource Sharing (CORS) controls which origins (domains) can access your actors. When actors are exposed to the public internet, proper origin validation is critical to prevent security breaches and denial of service attacks.", + "canonical_url": "https://rivet.dev/docs/general/cors", + "reference_url": "/metadata/skills/per-tenant-database/reference/general/cors.md" + }, + { + "name": "actors/inspector-tabs", + "title": "Custom Inspector Tabs", + "description": "Ship your own UI tabs alongside a Rivet Actor — embedded directly in the dashboard inspector.", + "canonical_url": "https://rivet.dev/docs/actors/inspector-tabs", + "reference_url": "/metadata/skills/per-tenant-database/reference/actors/inspector-tabs.md" + }, + { + "name": "cookbook/per-tenant-database", + "title": "Database per Tenant", + "description": "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.", + "canonical_url": "https://rivet.dev/cookbook/per-tenant-database", + "reference_url": "/metadata/skills/per-tenant-database/reference/cookbook/per-tenant-database.md" + }, + { + "name": "actors/debugging", + "title": "Debugging", + "description": "Inspect and debug running Rivet Actors, runners, and provider configs using management, runner, and actor inspector HTTP APIs.", + "canonical_url": "https://rivet.dev/docs/actors/debugging", + "reference_url": "/metadata/skills/per-tenant-database/reference/actors/debugging.md" + }, + { + "name": "deploy/aws-lambda", + "title": "Deploy To Amazon Web Services Lambda", + "description": "_AWS Lambda is coming soon_", + "canonical_url": "https://rivet.dev/docs/deploy/aws-lambda", + "reference_url": "/metadata/skills/per-tenant-database/reference/deploy/aws-lambda.md" + }, + { + "name": "cookbook/vpc-air-gapped", + "title": "Deploying Rivet in a VPC or Air-Gapped Network", + "description": "Run Rivet entirely inside your own perimeter: single-binary or Docker Compose install, file system storage with no database infrastructure, and no outbound telemetry by default.", + "canonical_url": "https://rivet.dev/cookbook/vpc-air-gapped", + "reference_url": "/metadata/skills/per-tenant-database/reference/cookbook/vpc-air-gapped.md" + }, + { + "name": "deploy/aws-ecs", + "title": "Deploying to AWS ECS", + "description": "Run your backend on Amazon ECS with Fargate.", + "canonical_url": "https://rivet.dev/docs/deploy/aws-ecs", + "reference_url": "/metadata/skills/per-tenant-database/reference/deploy/aws-ecs.md" + }, + { + "name": "deploy/cloudflare", + "title": "Deploying to Cloudflare Workers", + "description": "Deploy an existing Rivet project to Cloudflare Workers.", + "canonical_url": "https://rivet.dev/docs/deploy/cloudflare", + "reference_url": "/metadata/skills/per-tenant-database/reference/deploy/cloudflare.md" + }, + { + "name": "deploy/freestyle", + "title": "Deploying to Freestyle", + "description": "Deploy RivetKit app to Freestyle.sh, a cloud platform for running AI-generated code with built-in security and scalability.", + "canonical_url": "https://rivet.dev/docs/deploy/freestyle", + "reference_url": "/metadata/skills/per-tenant-database/reference/deploy/freestyle.md" + }, + { + "name": "deploy/gcp-cloud-run", + "title": "Deploying to Google Cloud Run", + "description": "Deploy your RivetKit app to Google Cloud Run.", + "canonical_url": "https://rivet.dev/docs/deploy/gcp-cloud-run", + "reference_url": "/metadata/skills/per-tenant-database/reference/deploy/gcp-cloud-run.md" + }, + { + "name": "deploy/hetzner", + "title": "Deploying to Hetzner", + "description": "Please see the VM & Bare Metal guide.", + "canonical_url": "https://rivet.dev/docs/deploy/hetzner", + "reference_url": "/metadata/skills/per-tenant-database/reference/deploy/hetzner.md" + }, + { + "name": "deploy/kubernetes", + "title": "Deploying to Kubernetes", + "description": "Deploy your RivetKit app to any Kubernetes cluster.", + "canonical_url": "https://rivet.dev/docs/deploy/kubernetes", + "reference_url": "/metadata/skills/per-tenant-database/reference/deploy/kubernetes.md" + }, + { + "name": "deploy/railway", + "title": "Deploying to Railway", + "description": "Deploy your RivetKit app to Railway.", + "canonical_url": "https://rivet.dev/docs/deploy/railway", + "reference_url": "/metadata/skills/per-tenant-database/reference/deploy/railway.md" + }, + { + "name": "deploy/rivet-compute", + "title": "Deploying to Rivet Compute", + "description": "Run your backend on Rivet Compute.", + "canonical_url": "https://rivet.dev/docs/deploy/rivet-compute", + "reference_url": "/metadata/skills/per-tenant-database/reference/deploy/rivet-compute.md" + }, + { + "name": "deploy/supabase", + "title": "Deploying to Supabase Functions", + "description": "Deploy an existing Rivet project to Supabase Edge Functions.", + "canonical_url": "https://rivet.dev/docs/deploy/supabase", + "reference_url": "/metadata/skills/per-tenant-database/reference/deploy/supabase.md" + }, + { + "name": "deploy/vercel", + "title": "Deploying to Vercel", + "description": "Deploy your Next.js Rivet app to Vercel.", + "canonical_url": "https://rivet.dev/docs/deploy/vercel", + "reference_url": "/metadata/skills/per-tenant-database/reference/deploy/vercel.md" + }, + { + "name": "deploy/vm-and-bare-metal", + "title": "Deploying to VMs & Bare Metal", + "description": "Deploy your RivetKit app to any Linux VM or bare metal host.", + "canonical_url": "https://rivet.dev/docs/deploy/vm-and-bare-metal", + "reference_url": "/metadata/skills/per-tenant-database/reference/deploy/vm-and-bare-metal.md" + }, + { + "name": "actors/design-patterns", + "title": "Design Patterns", + "description": "Common patterns and anti-patterns for building scalable actor systems.", + "canonical_url": "https://rivet.dev/docs/actors/design-patterns", + "reference_url": "/metadata/skills/per-tenant-database/reference/actors/design-patterns.md" + }, + { + "name": "actors/destroy", + "title": "Destroying Actors", + "description": "Actors can be permanently destroyed. Common use cases include:", + "canonical_url": "https://rivet.dev/docs/actors/destroy", + "reference_url": "/metadata/skills/per-tenant-database/reference/actors/destroy.md" + }, + { + "name": "self-hosting/docker-compose", + "title": "Docker Compose", + "description": "Deploy Rivet Engine with docker-compose for multi-container setups.", + "canonical_url": "https://rivet.dev/docs/self-hosting/docker-compose", + "reference_url": "/metadata/skills/per-tenant-database/reference/self-hosting/docker-compose.md" + }, + { + "name": "self-hosting/docker-container", + "title": "Docker Container", + "description": "Run Rivet Engine in a single Docker container.", + "canonical_url": "https://rivet.dev/docs/self-hosting/docker-container", + "reference_url": "/metadata/skills/per-tenant-database/reference/self-hosting/docker-container.md" + }, + { + "name": "general/docs-for-llms", + "title": "Documentation for LLMs & AI", + "description": "Rivet provides optimized documentation formats specifically designed for Large Language Models (LLMs) and AI integration tools.", + "canonical_url": "https://rivet.dev/docs/general/docs-for-llms", + "reference_url": "/metadata/skills/per-tenant-database/reference/general/docs-for-llms.md" + }, + { + "name": "general/edge", + "title": "Edge Networking", + "description": "Actors automatically run near your users on your provider's global network.", + "canonical_url": "https://rivet.dev/docs/general/edge", + "reference_url": "/metadata/skills/per-tenant-database/reference/general/edge.md" + }, + { + "name": "actors/quickstart/effect", + "title": "Effect.ts Quickstart (Beta)", + "description": "Build a Rivet Actor with the Effect SDK", + "canonical_url": "https://rivet.dev/docs/actors/quickstart/effect", + "reference_url": "/metadata/skills/per-tenant-database/reference/actors/quickstart/effect.md" + }, + { + "name": "general/endpoints", + "title": "Endpoints", + "description": "Configure how your backend connects to Rivet and how clients reach your actors.", + "canonical_url": "https://rivet.dev/docs/general/endpoints", + "reference_url": "/metadata/skills/per-tenant-database/reference/general/endpoints.md" + }, + { + "name": "general/environment-variables", + "title": "Environment Variables", + "description": "This page documents all environment variables that configure RivetKit behavior.", + "canonical_url": "https://rivet.dev/docs/general/environment-variables", + "reference_url": "/metadata/skills/per-tenant-database/reference/general/environment-variables.md" + }, + { + "name": "actors/errors", + "title": "Errors", + "description": "Rivet provides robust error handling with security built in by default. Errors are handled differently based on whether they should be exposed to clients or kept private.", + "canonical_url": "https://rivet.dev/docs/actors/errors", + "reference_url": "/metadata/skills/per-tenant-database/reference/actors/errors.md" + }, + { + "name": "actors/fetch-and-websocket-handler", + "title": "Fetch and WebSocket Handler", + "description": "These docs have moved to [Low-Level WebSocket Handler](/docs/actors/websocket-handler) and [Low-Level Request Handler](/docs/actors/request-handler).", + "canonical_url": "https://rivet.dev/docs/actors/fetch-and-websocket-handler", + "reference_url": "/metadata/skills/per-tenant-database/reference/actors/fetch-and-websocket-handler.md" + }, + { + "name": "self-hosting/filesystem", + "title": "File System", + "description": "The file system backend stores all data on the local disk. This is suitable for single-node deployments, development, and testing.", + "canonical_url": "https://rivet.dev/docs/self-hosting/filesystem", + "reference_url": "/metadata/skills/per-tenant-database/reference/self-hosting/filesystem.md" + }, + { + "name": "self-hosting/foundationdb", + "title": "FoundationDB (Enterprise)", + "description": "FoundationDB is the recommended storage backend for scalable production Rivet deployments.", + "canonical_url": "https://rivet.dev/docs/self-hosting/foundationdb", + "reference_url": "/metadata/skills/per-tenant-database/reference/self-hosting/foundationdb.md" + }, + { + "name": "actors/helper-types", + "title": "Helper Types", + "description": "This page has moved to [Types](/docs/actors/types).", + "canonical_url": "https://rivet.dev/docs/actors/helper-types", + "reference_url": "/metadata/skills/per-tenant-database/reference/actors/helper-types.md" + }, + { + "name": "general/http-server", + "title": "HTTP Server", + "description": "Different ways to run your RivetKit HTTP server.", + "canonical_url": "https://rivet.dev/docs/general/http-server", + "reference_url": "/metadata/skills/per-tenant-database/reference/general/http-server.md" + }, + { + "name": "actors/appearance", + "title": "Icons & Names", + "description": "Customize actors with display names and icons for the Rivet inspector and dashboard.", + "canonical_url": "https://rivet.dev/docs/actors/appearance", + "reference_url": "/metadata/skills/per-tenant-database/reference/actors/appearance.md" + }, + { + "name": "actors/state", + "title": "In-Memory State", + "description": "Actors store state in memory for instant reads and writes. State can be persisted automatically or kept ephemeral.", + "canonical_url": "https://rivet.dev/docs/actors/state", + "reference_url": "/metadata/skills/per-tenant-database/reference/actors/state.md" + }, + { + "name": "actors/input", + "title": "Input Parameters", + "description": "Pass initialization data to actors when creating instances", + "canonical_url": "https://rivet.dev/docs/actors/input", + "reference_url": "/metadata/skills/per-tenant-database/reference/actors/input.md" + }, + { + "name": "self-hosting/install", + "title": "Installing Rivet Engine", + "description": "Install Rivet Engine using Docker, binaries, or a source build.", + "canonical_url": "https://rivet.dev/docs/self-hosting/install", + "reference_url": "/metadata/skills/per-tenant-database/reference/self-hosting/install.md" + }, + { + "name": "self-hosting/kubernetes", + "title": "Kubernetes", + "description": "Deploy production-ready Rivet Engine to Kubernetes with PostgreSQL storage.", + "canonical_url": "https://rivet.dev/docs/self-hosting/kubernetes", + "reference_url": "/metadata/skills/per-tenant-database/reference/self-hosting/kubernetes.md" + }, + { + "name": "actors/lifecycle", + "title": "Lifecycle", + "description": "Learn about actor lifecycle hooks for initialization, state management, and cleanup.", + "canonical_url": "https://rivet.dev/docs/actors/lifecycle", + "reference_url": "/metadata/skills/per-tenant-database/reference/actors/lifecycle.md" + }, + { + "name": "actors/limits", + "title": "Limits", + "description": "Limits and constraints for Rivet Actors.", + "canonical_url": "https://rivet.dev/docs/actors/limits", + "reference_url": "/metadata/skills/per-tenant-database/reference/actors/limits.md" + }, + { + "name": "cookbook/live-cursors", + "title": "Live Cursors and Presence", + "description": "Live cursors and multiplayer presence with Rivet Actors: per-connection cursor state, realtime updates over events or raw WebSockets, and throttling.", + "canonical_url": "https://rivet.dev/cookbook/live-cursors", + "reference_url": "/metadata/skills/per-tenant-database/reference/cookbook/live-cursors.md" + }, + { + "name": "general/logging", + "title": "Logging", + "description": "Actors provide a built-in way to log complex data to the console.", + "canonical_url": "https://rivet.dev/docs/general/logging", + "reference_url": "/metadata/skills/per-tenant-database/reference/general/logging.md" + }, + { + "name": "actors/request-handler", + "title": "Low-Level HTTP Request Handler", + "description": "Actors can handle HTTP requests through the `onRequest` handler.", + "canonical_url": "https://rivet.dev/docs/actors/request-handler", + "reference_url": "/metadata/skills/per-tenant-database/reference/actors/request-handler.md" + }, + { + "name": "actors/kv", + "title": "Low-Level KV Storage", + "description": "Use the built-in key-value store on ActorContext for durable string and binary data alongside actor state.", + "canonical_url": "https://rivet.dev/docs/actors/kv", + "reference_url": "/metadata/skills/per-tenant-database/reference/actors/kv.md" + }, + { + "name": "actors/websocket-handler", + "title": "Low-Level WebSocket Handler", + "description": "Actors can handle WebSocket connections through the `onWebSocket` handler.", + "canonical_url": "https://rivet.dev/docs/actors/websocket-handler", + "reference_url": "/metadata/skills/per-tenant-database/reference/actors/websocket-handler.md" + }, + { + "name": "actors/metadata", + "title": "Metadata", + "description": "Metadata provides information about the currently running actor.", + "canonical_url": "https://rivet.dev/docs/actors/metadata", + "reference_url": "/metadata/skills/per-tenant-database/reference/actors/metadata.md" + }, + { + "name": "self-hosting/multi-region", + "title": "Multi-Region", + "description": "Rivet Engine supports scaling transparently across multiple regions.", + "canonical_url": "https://rivet.dev/docs/self-hosting/multi-region", + "reference_url": "/metadata/skills/per-tenant-database/reference/self-hosting/multi-region.md" + }, + { + "name": "cookbook/multiplayer-game", + "title": "Multiplayer Game", + "description": "Pragmatic patterns for building multiplayer games: matchmaking, tick loops, realtime state, interest management, and validation.", + "canonical_url": "https://rivet.dev/cookbook/multiplayer-game", + "reference_url": "/metadata/skills/per-tenant-database/reference/cookbook/multiplayer-game.md" + }, + { + "name": "actors/quickstart/next-js", + "title": "Next.js Quickstart", + "description": "Get started with Rivet Actors in Next.js", + "canonical_url": "https://rivet.dev/docs/actors/quickstart/next-js", + "reference_url": "/metadata/skills/per-tenant-database/reference/actors/quickstart/next-js.md" + }, + { + "name": "clients/javascript", + "title": "Node.js & Bun", + "description": "Connect JavaScript apps to Rivet Actors.", + "canonical_url": "https://rivet.dev/docs/clients/javascript", + "reference_url": "/metadata/skills/per-tenant-database/reference/clients/javascript.md" + }, + { + "name": "actors/quickstart/backend", + "title": "Node.js & Bun Quickstart", + "description": "Get started with Rivet Actors in Node.js and Bun", + "canonical_url": "https://rivet.dev/docs/actors/quickstart/backend", + "reference_url": "/metadata/skills/per-tenant-database/reference/actors/quickstart/backend.md" + }, + { + "name": "general/pool-configuration", + "title": "Pool Configuration", + "description": "Reference for runner pool configuration, including drain behavior, actor eviction rate limiting, and serverless-specific options.", + "canonical_url": "https://rivet.dev/docs/general/pool-configuration", + "reference_url": "/metadata/skills/per-tenant-database/reference/general/pool-configuration.md" + }, + { + "name": "self-hosting/postgres", + "title": "PostgreSQL", + "description": "Configure PostgreSQL for self-hosted Rivet deployments.", + "canonical_url": "https://rivet.dev/docs/self-hosting/postgres", + "reference_url": "/metadata/skills/per-tenant-database/reference/self-hosting/postgres.md" + }, + { + "name": "general/production-checklist", + "title": "Production Checklist", + "description": "Checklist for deploying Rivet Actors to production.", + "canonical_url": "https://rivet.dev/docs/general/production-checklist", + "reference_url": "/metadata/skills/per-tenant-database/reference/general/production-checklist.md" + }, + { + "name": "self-hosting/production-checklist", + "title": "Production Checklist", + "description": "Checklist for deploying a self-hosted Rivet Engine to production.", + "canonical_url": "https://rivet.dev/docs/self-hosting/production-checklist", + "reference_url": "/metadata/skills/per-tenant-database/reference/self-hosting/production-checklist.md" + }, + { + "name": "actors/queues", + "title": "Queues & Run Loops", + "description": "Use actor-local durable queues for serial run loops and request/response workflows.", + "canonical_url": "https://rivet.dev/docs/actors/queues", + "reference_url": "/metadata/skills/per-tenant-database/reference/actors/queues.md" + }, + { + "name": "self-hosting/railway", + "title": "Railway Deployment", + "description": "Railway provides a simple platform for deploying Rivet Engine with automatic scaling and managed infrastructure.", + "canonical_url": "https://rivet.dev/docs/self-hosting/railway", + "reference_url": "/metadata/skills/per-tenant-database/reference/self-hosting/railway.md" + }, + { + "name": "clients/react", + "title": "React", + "description": "Connect React apps to Rivet Actors.", + "canonical_url": "https://rivet.dev/docs/clients/react", + "reference_url": "/metadata/skills/per-tenant-database/reference/clients/react.md" + }, + { + "name": "actors/quickstart/react", + "title": "React Quickstart", + "description": "Build realtime React applications with Rivet Actors", + "canonical_url": "https://rivet.dev/docs/actors/quickstart/react", + "reference_url": "/metadata/skills/per-tenant-database/reference/actors/quickstart/react.md" + }, + { + "name": "actors/events", + "title": "Realtime", + "description": "Events enable realtime communication from actors to clients. While clients use actions to send data to actors, events allow actors to push updates to connected clients instantly.", + "canonical_url": "https://rivet.dev/docs/actors/events", + "reference_url": "/metadata/skills/per-tenant-database/reference/actors/events.md" + }, + { + "name": "general/registry-configuration", + "title": "Registry Configuration", + "description": "This page documents the configuration options available when setting up a RivetKit registry. The registry configuration is passed to the `setup()` function.", + "canonical_url": "https://rivet.dev/docs/general/registry-configuration", + "reference_url": "/metadata/skills/per-tenant-database/reference/general/registry-configuration.md" + }, + { + "name": "self-hosting/render", + "title": "Render Deployment", + "description": "Deploy Rivet Engine to Render with managed PostgreSQL and automatic HTTPS, using the experimental PostgreSQL backend.", + "canonical_url": "https://rivet.dev/docs/self-hosting/render", + "reference_url": "/metadata/skills/per-tenant-database/reference/self-hosting/render.md" + }, + { + "name": "general/runtime-modes", + "title": "Runtime Modes", + "description": "RivetKit supports two runtime modes for running your actors:", + "canonical_url": "https://rivet.dev/docs/general/runtime-modes", + "reference_url": "/metadata/skills/per-tenant-database/reference/general/runtime-modes.md" + }, + { + "name": "clients/rust", + "title": "Rust (Beta)", + "description": "Connect Rust apps to Rivet Actors.", + "canonical_url": "https://rivet.dev/docs/clients/rust", + "reference_url": "/metadata/skills/per-tenant-database/reference/clients/rust.md" + }, + { + "name": "actors/quickstart/rust", + "title": "Rust Quickstart (Beta)", + "description": "Build a Rivet Actor in Rust", + "canonical_url": "https://rivet.dev/docs/actors/quickstart/rust", + "reference_url": "/metadata/skills/per-tenant-database/reference/actors/quickstart/rust.md" + }, + { + "name": "actors/scaling", + "title": "Scaling & Concurrency", + "description": "This page has moved to [design patterns](/docs/actors/design-patterns).", + "canonical_url": "https://rivet.dev/docs/actors/scaling", + "reference_url": "/metadata/skills/per-tenant-database/reference/actors/scaling.md" + }, + { + "name": "actors/schedule", + "title": "Schedule & Cron", + "description": "Run durable one-shot and recurring actor actions on a schedule.", + "canonical_url": "https://rivet.dev/docs/actors/schedule", + "reference_url": "/metadata/skills/per-tenant-database/reference/actors/schedule.md" + }, + { + "name": "actors/sharing-and-joining-state", + "title": "Sharing and Joining State", + "description": "This page has moved to [design patterns](/docs/actors/design-patterns).", + "canonical_url": "https://rivet.dev/docs/actors/sharing-and-joining-state", + "reference_url": "/metadata/skills/per-tenant-database/reference/actors/sharing-and-joining-state.md" + }, + { + "name": "actors/sqlite", + "title": "SQLite", + "description": "Use embedded SQLite in Rivet Actors with raw SQL queries.", + "canonical_url": "https://rivet.dev/docs/actors/sqlite", + "reference_url": "/metadata/skills/per-tenant-database/reference/actors/sqlite.md" + }, + { + "name": "actors/sqlite-drizzle", + "title": "SQLite + Drizzle", + "description": "Use Drizzle ORM with embedded SQLite in Rivet Actors.", + "canonical_url": "https://rivet.dev/docs/actors/sqlite-drizzle", + "reference_url": "/metadata/skills/per-tenant-database/reference/actors/sqlite-drizzle.md" + }, + { + "name": "actors/quickstart/supabase", + "title": "Supabase Functions Quickstart", + "description": "Set up a Rivet project locally targeting Supabase Edge Functions.", + "canonical_url": "https://rivet.dev/docs/actors/quickstart/supabase", + "reference_url": "/metadata/skills/per-tenant-database/reference/actors/quickstart/supabase.md" + }, + { + "name": "clients/swift", + "title": "Swift", + "description": "Connect Swift apps to Rivet Actors.", + "canonical_url": "https://rivet.dev/docs/clients/swift", + "reference_url": "/metadata/skills/per-tenant-database/reference/clients/swift.md" + }, + { + "name": "clients/swiftui", + "title": "SwiftUI", + "description": "Build SwiftUI apps with Rivet Actors.", + "canonical_url": "https://rivet.dev/docs/clients/swiftui", + "reference_url": "/metadata/skills/per-tenant-database/reference/clients/swiftui.md" + }, + { + "name": "actors/testing", + "title": "Testing", + "description": "Rivet provides a straightforward testing framework to build reliable and maintainable applications. This guide covers how to write effective tests for your actor-based services.", + "canonical_url": "https://rivet.dev/docs/actors/testing", + "reference_url": "/metadata/skills/per-tenant-database/reference/actors/testing.md" + }, + { + "name": "self-hosting/tls", + "title": "TLS & Certificates", + "description": "How Rivet validates TLS root certificates.", + "canonical_url": "https://rivet.dev/docs/self-hosting/tls", + "reference_url": "/metadata/skills/per-tenant-database/reference/self-hosting/tls.md" + }, + { + "name": "actors/troubleshooting", + "title": "Troubleshooting", + "description": "Common issues with Rivet Actors and how to resolve them.", + "canonical_url": "https://rivet.dev/docs/actors/troubleshooting", + "reference_url": "/metadata/skills/per-tenant-database/reference/actors/troubleshooting.md" + }, + { + "name": "actors/types", + "title": "Types", + "description": "TypeScript types for working with Rivet Actors. This page covers context types used in lifecycle hooks and actions, as well as helper types for extracting types from actor definitions.", + "canonical_url": "https://rivet.dev/docs/actors/types", + "reference_url": "/metadata/skills/per-tenant-database/reference/actors/types.md" + }, + { + "name": "actors/http-api", + "title": "Vanilla HTTP API", + "description": "Use the low-level HTTP handler to send and receive requests from actors.", + "canonical_url": "https://rivet.dev/docs/actors/http-api", + "reference_url": "/metadata/skills/per-tenant-database/reference/actors/http-api.md" + }, + { + "name": "actors/versions", + "title": "Versions & Upgrades", + "description": "When you deploy new code, Rivet ensures actors are upgraded seamlessly without downtime.", + "canonical_url": "https://rivet.dev/docs/actors/versions", + "reference_url": "/metadata/skills/per-tenant-database/reference/actors/versions.md" + }, + { + "name": "general/wasm-vs-native-sdk", + "title": "WASM vs Native SDK", + "description": "RivetKit runs your actors on a native or a WebAssembly runtime depending on your platform.", + "canonical_url": "https://rivet.dev/docs/general/wasm-vs-native-sdk", + "reference_url": "/metadata/skills/per-tenant-database/reference/general/wasm-vs-native-sdk.md" + }, + { + "name": "actors/workflows", + "title": "Workflows", + "description": "Build durable, replayable run loops in Rivet Actors with steps, queue waits, timers, and rollback.", + "canonical_url": "https://rivet.dev/docs/actors/workflows", + "reference_url": "/metadata/skills/per-tenant-database/reference/actors/workflows.md" + } + ] +} \ No newline at end of file diff --git a/.agents/skills/per-tenant-database/openapi.json b/.agents/skills/per-tenant-database/openapi.json new file mode 100644 index 0000000..ca482f7 --- /dev/null +++ b/.agents/skills/per-tenant-database/openapi.json @@ -0,0 +1,1311 @@ +{ + "openapi": "3.0.0", + "info": { + "version": "2.2.0", + "title": "RivetKit API" + }, + "components": { + "schemas": {}, + "parameters": {} + }, + "paths": { + "/actors": { + "get": { + "parameters": [ + { + "schema": { + "type": "string" + }, + "required": false, + "name": "name", + "in": "query" + }, + { + "schema": { + "type": "string" + }, + "required": false, + "name": "actor_ids", + "in": "query" + }, + { + "schema": { + "type": "string" + }, + "required": false, + "name": "key", + "in": "query" + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "actors": { + "type": "array", + "items": { + "type": "object", + "properties": { + "actor_id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "key": { + "type": "string" + }, + "namespace_id": { + "type": "string" + }, + "runner_name_selector": { + "type": "string" + }, + "create_ts": { + "type": "number" + }, + "connectable_ts": { + "type": "number", + "nullable": true + }, + "destroy_ts": { + "type": "number", + "nullable": true + }, + "sleep_ts": { + "type": "number", + "nullable": true + }, + "start_ts": { + "type": "number", + "nullable": true + }, + "error": { + "nullable": true + } + }, + "required": [ + "actor_id", + "name", + "key", + "namespace_id", + "runner_name_selector", + "create_ts" + ] + } + } + }, + "required": [ + "actors" + ] + } + } + } + }, + "400": { + "description": "User error" + }, + "500": { + "description": "Internal error" + } + } + }, + "put": { + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "datacenter": { + "type": "string" + }, + "name": { + "type": "string" + }, + "key": { + "type": "string" + }, + "runner_name_selector": { + "type": "string" + }, + "crash_policy": { + "type": "string" + }, + "input": { + "type": "string", + "nullable": true + } + }, + "required": [ + "name", + "key", + "runner_name_selector", + "crash_policy" + ] + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "actor": { + "type": "object", + "properties": { + "actor_id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "key": { + "type": "string" + }, + "namespace_id": { + "type": "string" + }, + "runner_name_selector": { + "type": "string" + }, + "create_ts": { + "type": "number" + }, + "connectable_ts": { + "type": "number", + "nullable": true + }, + "destroy_ts": { + "type": "number", + "nullable": true + }, + "sleep_ts": { + "type": "number", + "nullable": true + }, + "start_ts": { + "type": "number", + "nullable": true + }, + "error": { + "nullable": true + } + }, + "required": [ + "actor_id", + "name", + "key", + "namespace_id", + "runner_name_selector", + "create_ts" + ] + }, + "created": { + "type": "boolean" + } + }, + "required": [ + "actor", + "created" + ] + } + } + } + }, + "400": { + "description": "User error" + }, + "500": { + "description": "Internal error" + } + } + }, + "post": { + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "datacenter": { + "type": "string" + }, + "name": { + "type": "string" + }, + "runner_name_selector": { + "type": "string" + }, + "crash_policy": { + "type": "string" + }, + "key": { + "type": "string", + "nullable": true + }, + "input": { + "type": "string", + "nullable": true + } + }, + "required": [ + "name", + "runner_name_selector", + "crash_policy" + ] + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "actor": { + "type": "object", + "properties": { + "actor_id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "key": { + "type": "string" + }, + "namespace_id": { + "type": "string" + }, + "runner_name_selector": { + "type": "string" + }, + "create_ts": { + "type": "number" + }, + "connectable_ts": { + "type": "number", + "nullable": true + }, + "destroy_ts": { + "type": "number", + "nullable": true + }, + "sleep_ts": { + "type": "number", + "nullable": true + }, + "start_ts": { + "type": "number", + "nullable": true + }, + "error": { + "nullable": true + } + }, + "required": [ + "actor_id", + "name", + "key", + "namespace_id", + "runner_name_selector", + "create_ts" + ] + } + }, + "required": [ + "actor" + ] + } + } + } + }, + "400": { + "description": "User error" + }, + "500": { + "description": "Internal error" + } + } + } + }, + "/actors/names": { + "get": { + "parameters": [ + { + "schema": { + "type": "string" + }, + "required": true, + "name": "namespace", + "in": "query" + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "names": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "metadata": { + "type": "object", + "additionalProperties": { + "nullable": true + } + } + }, + "required": [ + "metadata" + ] + } + } + }, + "required": [ + "names" + ] + } + } + } + }, + "400": { + "description": "User error" + }, + "500": { + "description": "Internal error" + } + } + } + }, + "/actors/{actor_id}/kv/keys/{key}": { + "get": { + "parameters": [ + { + "schema": { + "type": "string" + }, + "required": true, + "name": "actor_id", + "in": "path" + }, + { + "schema": { + "type": "string" + }, + "required": true, + "name": "key", + "in": "path" + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "value": { + "type": "string", + "nullable": true + } + }, + "required": [ + "value" + ] + } + } + } + }, + "400": { + "description": "User error" + }, + "500": { + "description": "Internal error" + } + } + } + }, + "/gateway/{actorId}/health": { + "get": { + "parameters": [ + { + "name": "actorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the actor to target" + } + ], + "responses": { + "200": { + "description": "Health check", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/gateway/{actorId}/action/{action}": { + "post": { + "parameters": [ + { + "name": "actorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the actor to target" + }, + { + "name": "action", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The name of the action to execute" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "args": {} + }, + "required": [ + "args" + ], + "additionalProperties": false + } + } + } + }, + "responses": { + "200": { + "description": "Action executed successfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "output": {} + }, + "required": [ + "output" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "Invalid action" + }, + "500": { + "description": "Internal error" + } + } + } + }, + "/gateway/{actorId}/request/{path}": { + "get": { + "parameters": [ + { + "name": "actorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the actor to target" + }, + { + "name": "path", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The HTTP path to forward to the actor" + } + ], + "responses": { + "200": { + "description": "Response from actor's raw HTTP handler" + } + } + }, + "post": { + "parameters": [ + { + "name": "actorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the actor to target" + }, + { + "name": "path", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The HTTP path to forward to the actor" + } + ], + "responses": { + "200": { + "description": "Response from actor's raw HTTP handler" + } + } + }, + "put": { + "parameters": [ + { + "name": "actorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the actor to target" + }, + { + "name": "path", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The HTTP path to forward to the actor" + } + ], + "responses": { + "200": { + "description": "Response from actor's raw HTTP handler" + } + } + }, + "delete": { + "parameters": [ + { + "name": "actorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the actor to target" + }, + { + "name": "path", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The HTTP path to forward to the actor" + } + ], + "responses": { + "200": { + "description": "Response from actor's raw HTTP handler" + } + } + }, + "patch": { + "parameters": [ + { + "name": "actorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the actor to target" + }, + { + "name": "path", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The HTTP path to forward to the actor" + } + ], + "responses": { + "200": { + "description": "Response from actor's raw HTTP handler" + } + } + }, + "head": { + "parameters": [ + { + "name": "actorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the actor to target" + }, + { + "name": "path", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The HTTP path to forward to the actor" + } + ], + "responses": { + "200": { + "description": "Response from actor's raw HTTP handler" + } + } + }, + "options": { + "parameters": [ + { + "name": "actorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the actor to target" + }, + { + "name": "path", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The HTTP path to forward to the actor" + } + ], + "responses": { + "200": { + "description": "Response from actor's raw HTTP handler" + } + } + } + }, + "/gateway/{actorId}/inspector/state": { + "get": { + "parameters": [ + { + "name": "actorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the actor to target" + }, + { + "name": "Authorization", + "in": "header", + "required": false, + "schema": { + "type": "string" + }, + "description": "Bearer token for inspector authentication. Required in production, optional in development." + } + ], + "responses": { + "200": { + "description": "Current actor state", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "state": {} + } + } + } + } + }, + "401": { + "description": "Unauthorized" + } + } + }, + "patch": { + "parameters": [ + { + "name": "actorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the actor to target" + }, + { + "name": "Authorization", + "in": "header", + "required": false, + "schema": { + "type": "string" + }, + "description": "Bearer token for inspector authentication. Required in production, optional in development." + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "state": {} + }, + "required": [ + "state" + ] + } + } + } + }, + "responses": { + "200": { + "description": "State updated", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "ok": { + "type": "boolean" + } + } + } + } + } + }, + "401": { + "description": "Unauthorized" + } + } + } + }, + "/gateway/{actorId}/inspector/connections": { + "get": { + "parameters": [ + { + "name": "actorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the actor to target" + }, + { + "name": "Authorization", + "in": "header", + "required": false, + "schema": { + "type": "string" + }, + "description": "Bearer token for inspector authentication. Required in production, optional in development." + } + ], + "responses": { + "200": { + "description": "Current actor connections", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "connections": { + "type": "array", + "items": { + "type": "object" + } + } + } + } + } + } + }, + "401": { + "description": "Unauthorized" + } + } + } + }, + "/gateway/{actorId}/inspector/rpcs": { + "get": { + "parameters": [ + { + "name": "actorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the actor to target" + }, + { + "name": "Authorization", + "in": "header", + "required": false, + "schema": { + "type": "string" + }, + "description": "Bearer token for inspector authentication. Required in production, optional in development." + } + ], + "responses": { + "200": { + "description": "Available actor actions/RPCs", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "rpcs": { + "type": "object" + } + } + } + } + } + }, + "401": { + "description": "Unauthorized" + } + } + } + }, + "/gateway/{actorId}/inspector/action/{name}": { + "post": { + "parameters": [ + { + "name": "actorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the actor to target" + }, + { + "name": "name", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The name of the action to execute" + }, + { + "name": "Authorization", + "in": "header", + "required": false, + "schema": { + "type": "string" + }, + "description": "Bearer token for inspector authentication. Required in production, optional in development." + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "args": { + "type": "array", + "items": {} + } + } + } + } + } + }, + "responses": { + "200": { + "description": "Action executed successfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "output": {} + } + } + } + } + }, + "401": { + "description": "Unauthorized" + } + } + } + }, + "/gateway/{actorId}/inspector/queue": { + "get": { + "parameters": [ + { + "name": "actorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the actor to target" + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 50 + }, + "description": "Maximum number of queue messages to return" + }, + { + "name": "Authorization", + "in": "header", + "required": false, + "schema": { + "type": "string" + }, + "description": "Bearer token for inspector authentication. Required in production, optional in development." + } + ], + "responses": { + "200": { + "description": "Queue status", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "size": { + "type": "integer" + }, + "maxSize": { + "type": "integer" + }, + "truncated": { + "type": "boolean" + }, + "messages": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "createdAtMs": { + "type": "integer" + } + } + } + } + } + } + } + } + }, + "401": { + "description": "Unauthorized" + } + } + } + }, + "/gateway/{actorId}/inspector/traces": { + "get": { + "parameters": [ + { + "name": "actorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the actor to target" + }, + { + "name": "startMs", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 0 + }, + "description": "Start of time range in epoch milliseconds" + }, + { + "name": "endMs", + "in": "query", + "required": false, + "schema": { + "type": "integer" + }, + "description": "End of time range in epoch milliseconds. Defaults to now." + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 1000 + }, + "description": "Maximum number of spans to return" + }, + { + "name": "Authorization", + "in": "header", + "required": false, + "schema": { + "type": "string" + }, + "description": "Bearer token for inspector authentication. Required in production, optional in development." + } + ], + "responses": { + "200": { + "description": "Trace spans in OTLP JSON format", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "otlp": { + "type": "object" + }, + "clamped": { + "type": "boolean" + } + } + } + } + } + }, + "401": { + "description": "Unauthorized" + } + } + } + }, + "/gateway/{actorId}/inspector/workflow-history": { + "get": { + "parameters": [ + { + "name": "actorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the actor to target" + }, + { + "name": "Authorization", + "in": "header", + "required": false, + "schema": { + "type": "string" + }, + "description": "Bearer token for inspector authentication. Required in production, optional in development." + } + ], + "responses": { + "200": { + "description": "Workflow history and status", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "history": {}, + "isWorkflowEnabled": { + "type": "boolean" + } + } + } + } + } + }, + "401": { + "description": "Unauthorized" + } + } + } + }, + "/gateway/{actorId}/inspector/workflow/replay": { + "post": { + "parameters": [ + { + "name": "actorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the actor to target" + }, + { + "name": "Authorization", + "in": "header", + "required": false, + "schema": { + "type": "string" + }, + "description": "Bearer token for inspector authentication. Required in production, optional in development." + } + ], + "requestBody": { + "required": false, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "entryId": { + "type": "string" + } + } + } + } + } + }, + "responses": { + "200": { + "description": "Workflow history after scheduling a replay", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "history": {}, + "isWorkflowEnabled": { + "type": "boolean" + } + } + } + } + } + }, + "401": { + "description": "Unauthorized" + } + } + } + }, + "/gateway/{actorId}/inspector/summary": { + "get": { + "parameters": [ + { + "name": "actorId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the actor to target" + }, + { + "name": "Authorization", + "in": "header", + "required": false, + "schema": { + "type": "string" + }, + "description": "Bearer token for inspector authentication. Required in production, optional in development." + } + ], + "responses": { + "200": { + "description": "Full actor inspector summary", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "state": {}, + "connections": { + "type": "array", + "items": { + "type": "object" + } + }, + "rpcs": { + "type": "object" + }, + "queueSize": { + "type": "integer" + }, + "isStateEnabled": { + "type": "boolean" + }, + "isDatabaseEnabled": { + "type": "boolean" + }, + "isWorkflowEnabled": { + "type": "boolean" + }, + "workflowHistory": {} + } + } + } + } + }, + "401": { + "description": "Unauthorized" + } + } + } + } + } +} \ No newline at end of file diff --git a/.agents/skills/per-tenant-database/reference/actors/access-control.md b/.agents/skills/per-tenant-database/reference/actors/access-control.md new file mode 100644 index 0000000..02dc4e8 --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/actors/access-control.md @@ -0,0 +1,48 @@ +# Access Control + +> Source: `src/content/docs/actors/access-control.mdx` +> Canonical URL: https://rivet.dev/docs/actors/access-control +> Description: Authorize actions, queue publishes, and event subscriptions with explicit hooks. + +--- +Use access control to decide what authenticated clients are allowed to do. + +This is authorization, not authentication: + +- Use [authentication](/docs/actors/authentication) to identify who is calling. +- Use access-control rules to decide what they can do after connecting. + +## Permission Surfaces + +RivetKit authorization is explicit per surface: + +- `onBeforeConnect` rejects unauthenticated or malformed connections. +- Action handlers (`actions.*`) enforce action permissions. +- `queues..canPublish` allows or denies inbound queue publishes. +- `events..canSubscribe` allows or denies event subscriptions. + +## Fail By Default + +Use deny-by-default rules everywhere: + +1. Keep `onBeforeConnect` strict and reject invalid credentials. +2. In each action, explicitly allow expected roles and throw `forbidden` otherwise. +3. In `canPublish` and `canSubscribe`, return `true` only for allowed roles and end with `return false`. + +## Return Value Contract + +`canPublish` and `canSubscribe` must return a boolean: + +- `true`: allow +- `false`: deny with `forbidden` + +Returning `undefined`, `null`, or any non-boolean throws an internal error. + +## Notes + +- `canPublish` only applies to queue names defined in `queues`. +- Incoming queue messages for undefined queues are ignored and the publish succeeds as completed. +- `canSubscribe` only applies to event names defined in `events`. +- Broadcasting an event not defined in `events` still publishes to subscribers. + +_Source doc path: /docs/actors/access-control_ diff --git a/.agents/skills/per-tenant-database/reference/actors/actions.md b/.agents/skills/per-tenant-database/reference/actors/actions.md new file mode 100644 index 0000000..d00324e --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/actors/actions.md @@ -0,0 +1,98 @@ +# Actions + +> Source: `src/content/docs/actors/actions.mdx` +> Canonical URL: https://rivet.dev/docs/actors/actions +> Description: Actions are how your backend, frontend, or other actors can communicate with actors. + +--- +Actions are very lightweight. They can be called thousands of times per second safely. Actions are executed via HTTP requests or via WebSockets if [using `.connect()`](/docs/actors/connections). + +For advanced use cases that require direct access to HTTP requests or WebSocket connections, see [raw HTTP and WebSocket handling](/docs/actors/fetch-and-websocket-handler). + +By default, actions run in parallel. If you need advanced control over concurrency, use [queues](/docs/actors/queues). + +## Writing Actions + +Actions are defined in the `actions` object when creating an actor: + +Each action receives a context object (commonly named `c`) as its first parameter, which provides access to state, connections, and other utilities. Additional parameters follow after that. + +## Calling Actions + +Actions can be called in different ways depending on your use case: + +### Frontend (createClient) + +Learn more about [communicating with actors from the frontend](/docs/actors/communicating-between-actors). + +### Backend (registry.handler) + +Learn more about [communicating with actors from the backend](/docs/actors/communicating-between-actors). + +### Actor-to-Actor (c.client()) + +Learn more about [communicating between actors](/docs/actors/communicating-between-actors). + +Calling actions from the client are async and require an `await`, even if the action itself is not async. + +### Type Safety + +The actor client includes type safety out of the box. When you use `createClient()`, TypeScript automatically infers action parameter and return types: + +## Error Handling + +Actors provide robust error handling out of the box for actions. + +### User Errors + +`UserError` can be used to return rich error data to the client. You can provide: + +- A human-readable message +- A machine-readable code that's useful for matching errors in a try-catch (optional) +- A metadata object for providing richer error context (optional) + +For example: + +### Internal Errors + +All other errors will return an error with the code `internal_error` to the client. This helps keep your application secure, as errors can sometimes expose sensitive information. + +## Schema Validation + +If passing data to an actor from the frontend, use a library like [Zod](https://zod.dev/) to validate input data. + +For example, to validate action parameters: + +## Streaming Data + +Actions have a single return value. To stream realtime data in response to an action, use [events](/docs/actors/events). + +## Canceling Long-Running Actions + +For operations that should be cancelable on-demand, create your own `AbortController`. Chain it with `c.abortSignal` so actor shutdown also cancels the operation. + +See [Actor Shutdown Abort Signal](/docs/actors/lifecycle#actor-shutdown-abort-signal) for automatically canceling operations when the actor stops. + +## Using `ActionContext` Externally + +When writing complex logic for actions, you may want to extract parts of your implementation into separate helper functions. When doing this, you'll need a way to properly type the context parameter. + +Rivet provides the `ActionContextOf` utility type for exactly this purpose: + +See [types](/docs/actors/types) for more details on using `ActionContextOf` and other utility types. + +## Debugging + +- `GET /inspector/rpcs` lists all available actions on an actor. +- `POST /inspector/action/:name` executes an action with JSON args and returns output. +- In non-dev mode, inspector endpoints require authorization. + +## API Reference + +- [`Actions`](/typedoc/interfaces/rivetkit.mod.Actions.html) - Interface for defining actions +- [`ActionContext`](/typedoc/interfaces/rivetkit.mod.ActionContext.html) - Context available in action handlers +- [`ActorDefinition`](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html) - Interface for defining actors with actions +- [`ActorHandle`](/typedoc/types/rivetkit.client_mod.ActorHandle.html) - Handle for calling actions from client +- [`ActorActionFunction`](/typedoc/types/rivetkit.client_mod.ActorActionFunction.html) - Type for action functions + +_Source doc path: /docs/actors/actions_ diff --git a/.agents/skills/per-tenant-database/reference/actors/actor-runtime-socket.md b/.agents/skills/per-tenant-database/reference/actors/actor-runtime-socket.md new file mode 100644 index 0000000..457472c --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/actors/actor-runtime-socket.md @@ -0,0 +1,62 @@ +# Actor Runtime Socket + +> Source: `src/content/docs/actors/actor-runtime-socket.mdx` +> Canonical URL: https://rivet.dev/docs/actors/actor-runtime-socket +> Description: Access an actor's SQLite database from another local process. + +--- +The Actor Runtime Socket is an advanced Beta API for integrating Rivet Actors with external software without sacrificing performance. It currently exposes SQLite over Unix domain sockets in the native runtime. + +For example, an agentOS agent can use the socket to work with its actor's SQLite database. Software orchestrated by an actor, such as another process or container, can use it to access the actor's runtime resources. Like the Chrome DevTools Protocol exposes a browser to external tools, the Actor Runtime Socket exposes selected actor capabilities to colocated software. + +## Quickstart + +Enable the socket, call `c.actorRuntimeSocket()` to get its path, and pass that path to your process: + +```ts actor.ts +import { spawn } from "node:child_process"; +import { actor } from "rivetkit"; +import { db } from "rivetkit/db"; + +export const example = actor({ + options: { enableActorRuntimeSocket: true }, + db: db({ + onMigrate: (db) => + db.execute("CREATE TABLE IF NOT EXISTS items (value TEXT)"), + }), + actions: { + startWorker: async (c) => { + // Get the Unix socket path external processes use + // to communicate with the actor. + // (Example: /tmp/rivet-actor-runtime.abc/def.sock) + const { path } = await c.actorRuntimeSocket(); + + // Spawn a process that can access the actor's + // SQLite database over the socket. + spawn(process.execPath, ["worker.js"], { + env: { ...process.env, ACTOR_RUNTIME_SOCKET_PATH: path }, + }); + }, + }, +}); +``` + +```ts worker.ts +import { connectRuntimeSocket } from "./generated/runtime-socket-client"; + +const db = await connectRuntimeSocket(process.env.ACTOR_RUNTIME_SOCKET_PATH!); +const rows = await db.query("SELECT value FROM items", []); +console.log(rows); +``` + +The path is valid only for the current actor generation, so get it again after the actor restarts or wakes. Only share it with trusted local processes. + +## Protocol + +The [protocol schema](https://github.com/rivet-dev/rivet/blob/main/engine/sdks/rust/actor-runtime-socket-protocol/schemas/v1.bare) defines the handshake, SQLite requests, values, responses, and errors. Generate codecs using vbare's [TypeScript](https://github.com/rivet-dev/vbare/tree/main/typescript) or [Rust](https://github.com/rivet-dev/vbare/tree/main/rust) tooling, then add a small Unix socket wrapper for its length-prefixed frames. `connectRuntimeSocket` above represents that application-specific wrapper. + +## SQLite transaction interleaving + +Separate socket requests can interleave with SQLite work from the main actor. For multi-request atomic work, begin a transaction with a client-generated `leaseKey`, include that key with each query, and commit or roll back with the same key. Other actor and socket SQL waits until that transaction finishes. + +_Source doc path: /docs/actors/actor-runtime-socket_ diff --git a/.agents/skills/per-tenant-database/reference/actors/appearance.md b/.agents/skills/per-tenant-database/reference/actors/appearance.md new file mode 100644 index 0000000..f60b1a1 --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/actors/appearance.md @@ -0,0 +1,50 @@ +# Icons & Names + +> Source: `src/content/docs/actors/appearance.mdx` +> Canonical URL: https://rivet.dev/docs/actors/appearance +> Description: Customize actors with display names and icons for the Rivet inspector and dashboard. + +--- +# Icons & Names + +Actors can be customized with a display name and icon that appear in the Rivet inspector & dashboard. This helps identify actors at a glance when managing your application. + +## Configuration + +Set the `name` and `icon` properties in your actor's `options`: + +## Icon Formats + +The `icon` property accepts two formats: + +### Emoji + +Use any emoji character directly: + +### FontAwesome Icons + +Use [FontAwesome](https://fontawesome.com/search) icon names without the "fa" prefix: + +## Default Behavior + +If no `icon` is specified, actors display the default actor icon. If no `name` is specified, the actor's registry key (e.g., `chatRoom`, `gameServer`) is displayed instead. + +## Examples + +Here are some common patterns: + +## Advanced: Run Handler Metadata + +For library developers creating reusable run handlers, you can bundle icon and name metadata directly with the `run` property. This allows libraries to provide sensible defaults without requiring users to configure them manually. + +Instead of returning a function from your run handler factory, return an object with `name`, `icon`, and `run`: + +Users can then use this directly: + +This run-handler metadata is currently applied through the registry and serverless metadata paths. The native runtime and inspector config read the actor's `options.name` and `options.icon` directly, so set those explicitly if you need the name or icon to appear everywhere. + +Actor-level `options.name` and `options.icon` always take precedence, allowing users to override library defaults: + +The built-in `workflow()` helper uses this pattern to automatically display the workflow icon for workflow-based actors. + +_Source doc path: /docs/actors/appearance_ diff --git a/.agents/skills/per-tenant-database/reference/actors/authentication.md b/.agents/skills/per-tenant-database/reference/actors/authentication.md new file mode 100644 index 0000000..c456dde --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/actors/authentication.md @@ -0,0 +1,105 @@ +# Authentication + +> Source: `src/content/docs/actors/authentication.mdx` +> Canonical URL: https://rivet.dev/docs/actors/authentication +> Description: Secure your actors with authentication and authorization. + +--- +## Do You Need Authentication? + +### Rivet Cloud + + Actors are private by default on Rivet Cloud. Only requests with the publishable token can interact with actors. + + - **Backend-only actors**: If your publishable token is only included in your backend, then authentication is not necessary. + - **Frontend-accessible actors**: If your publishable token is included in your frontend, then implementing authentication is recommended. + +### Self-Hosted + + Actors are public by default on self-hosted Rivet. Anyone can access them without a token. + + - **Only accessible within private network**: If Rivet is only accessible within your private network, then authentication is not necessary. + - **Rivet exposed to the public internet**: If Rivet is configured to accept traffic from the public internet, then implementing authentication is recommended. + +## Authentication Connections + +Authentication is configured through either: + +- `onBeforeConnect` for simple pass/fail validation +- `createConnState` when you need to access user data in your actions via `c.conn.state` + +## Access Control + +After a connection is authenticated, use [Access Control](/docs/actors/access-control) to enforce authorization: + +- Check permissions in action handlers. +- Use `queues..canPublish` to gate inbound queue publishes. +- Use `events..canSubscribe` to gate event subscriptions. + +### `onBeforeConnect` + +The `onBeforeConnect` hook validates credentials before allowing a connection. Throw an error to reject the connection. + +### `createConnState` + +Use `createConnState` to extract user data from credentials and store it in connection state. This data is accessible in actions via `c.conn.state`. Like `onBeforeConnect`, throwing an error will reject the connection. See [connections](/docs/actors/connections) for more details. + +## Available Auth Data + +Authentication hooks have access to several properties: + +| Property | Description | +|----------|-------------| +| `params` | Custom data passed by the client when connecting (see [connection params](/docs/actors/connections#extracting-data-from-connection-params)) | +| `c.request` | The underlying HTTP request object | +| `c.request.headers` | Request headers for tokens, API keys (does not work for `.connect()`) | +| `c.state` | Actor state for authorization decisions (see [state](/docs/actors/state)) | +| `c.key` | The actor's key (see [keys](/docs/actors/keys)) | + +It's recommended to use `params` instead of `c.request.headers` whenever possible since it works for both HTTP & WebSocket connections. + +## Client Usage + +### Passing Credentials + +Pass authentication data when connecting. Use `getParams` when you need a fresh JWT for every connection or reconnect: + +### Handling Errors + +Authentication errors use the same system as regular errors. See [errors](/docs/actors/errors) for more details. + +## Examples + +### JWT + +Validate JSON Web Tokens and extract user claims: + +### External Auth Provider + +Validate credentials against an external authentication service: + +### Using `c.state` In Authorization + +Access actor state via `c.state` and the actor's key via `c.key` to make authorization decisions: + +### Role-Based Access Control + +Create helper functions for common authorization patterns: + +### Rate Limiting + +Use `c.vars` to track connection attempts and rate limit by user: + +The limits in this example are [ephemeral](/docs/actors/state#ephemeral-variables). If you wish to persist rate limits, you can optionally replace `vars` with `state`. + +### Caching Tokens + +Cache validated tokens in `c.vars` to avoid redundant validation on repeated connections. See [ephemeral variables](/docs/actors/state#ephemeral-variables) for more details. + +## API Reference + +- [`AuthIntent`](/typedoc/types/rivetkit.mod.AuthIntent.html) - Authentication intent type +- [`OnBeforeConnectContext`](/typedoc/interfaces/rivetkit.mod.OnBeforeConnectContext.html) - Context for auth checks +- [`OnConnectContext`](/typedoc/interfaces/rivetkit.mod.OnConnectContext.html) - Context after connection + +_Source doc path: /docs/actors/authentication_ diff --git a/.agents/skills/per-tenant-database/reference/actors/communicating-between-actors.md b/.agents/skills/per-tenant-database/reference/actors/communicating-between-actors.md new file mode 100644 index 0000000..8aeaf39 --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/actors/communicating-between-actors.md @@ -0,0 +1,42 @@ +# Communicating Between Actors + +> Source: `src/content/docs/actors/communicating-between-actors.mdx` +> Canonical URL: https://rivet.dev/docs/actors/communicating-between-actors +> Description: Learn how actors can call other actors and share data + +--- +Actors can communicate with each other using the server-side actor client, enabling complex workflows and data sharing between different actor instances. + +We recommend reading the [clients documentation](/docs/clients) first. This guide focuses specifically on communication between actors. + +## Using the Server-Side Actor Client + +The server-side actor client allows actors to call other actors within the same registry. Access it via `c.client()` in your actor context: + +If two actors call each other and their return types are inferred from the other actor's response, you may hit circular type errors (`TS2322`, `TS2722`, or `c.state` becoming `unknown`). Fix this by writing explicit return types on those actions. + +## Use Cases and Patterns + +### Actor Orchestration + +Use a coordinator actor to manage complex workflows: + +### Data Aggregation + +Collect data from multiple actors: + +### Event-Driven Architecture + +Use connections to listen for events from other actors: + +### Batch Operations + +Process multiple items in parallel: + +## API Reference + +- [`ActorHandle`](/typedoc/types/rivetkit.client_mod.ActorHandle.html) - Handle for calling other actors +- [`Client`](/typedoc/types/rivetkit.mod.Client.html) - Client type for actor communication +- [`ActorAccessor`](/typedoc/interfaces/rivetkit.client_mod.ActorAccessor.html) - Accessor for getting actor handles + +_Source doc path: /docs/actors/communicating-between-actors_ diff --git a/.agents/skills/per-tenant-database/reference/actors/connections.md b/.agents/skills/per-tenant-database/reference/actors/connections.md new file mode 100644 index 0000000..e5f0384 --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/actors/connections.md @@ -0,0 +1,119 @@ +# Connections + +> Source: `src/content/docs/actors/connections.mdx` +> Canonical URL: https://rivet.dev/docs/actors/connections +> Description: Connections represent client connections to your actor. They provide a way to handle client authentication, manage connection-specific data, and control the connection lifecycle. + +--- +For documentation on connecting to actors from clients, see the [Clients documentation](/docs/clients). + +## Parameters + +When clients connect to an actor, they can pass connection parameters that are handled during the connection process. Use `params` for static values, or `getParams` when you need fresh connection data each time a connection opens. + +For example: + +## Connection State + +There are two ways to define an actor's connection state: + + +### connState + + Define connection state as a constant value: + + + + This value will be cloned for every new connection using `structuredClone`. + + + +### createConnState + + Create connection state dynamically with a function called for each connection: + + + + +## Connection Lifecycle + +Each client connection goes through a series of lifecycle hooks that allow you to validate, initialize, and clean up connection-specific resources. + +**On Connect** (per client) + +- `onBeforeConnect` +- `createConnState` +- `onConnect` + +Pending connections are not visible in `c.conns` while `onBeforeConnect` or `createConnState` is running. RivetKit adds the connection to `c.conns` after those hooks succeed and before `onConnect` runs. + +**On Disconnect** (per client) + +- `onDisconnect` + +### `createConnState` and `connState` + +[API Reference](/typedoc/interfaces/rivetkit.mod.CreateConnStateContext.html) + +There are two ways to define the initial state for connections: +1. `connState`: Define a constant object that will be used as the initial state for all connections +2. `createConnState`: A function that dynamically creates initial connection state based on connection parameters. Can be async. + +Connections are not visible in `c.conns` until `createConnState` completes successfully. + +### `onBeforeConnect` + +[API Reference](/typedoc/interfaces/rivetkit.mod.OnBeforeConnectContext.html) + +The `onBeforeConnect` hook is called whenever a new client connects to the actor. Can be async. Clients can pass parameters when connecting, accessible via `params`. This hook is used for connection validation and can throw errors to reject connections. + +The `onBeforeConnect` hook does NOT return connection state - it's used solely for validation. + +Connections are not visible in `c.conns` while `onBeforeConnect` is running. + +Connections cannot interact with the actor until this method completes successfully. Throwing an error will abort the connection. This can be used for authentication, see [Authentication](/docs/actors/authentication) for details. + +### `onConnect` + +[API Reference](/typedoc/interfaces/rivetkit.mod.OnConnectContext.html) + +Executed after the client has successfully connected. Can be async. Receives the connection object as a second parameter. + +By the time `onConnect` runs, the connection is visible in `c.conns`. + +Messages will not be processed for this actor until this hook succeeds. Errors thrown from this hook will cause the client to disconnect. + +### `onDisconnect` + +[API Reference](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html) + +Called when a client disconnects from the actor. Can be async. Receives the connection object as a second parameter. Use this to clean up any connection-specific resources. + +## Connection List + +All active connections can be accessed through the context object's `conns` property. This is a `Map` of all current connections, keyed by connection ID. + +This is frequently used with `conn.send(name, event)` to send messages directly to clients. To send an event to all connections at once, use `c.broadcast()` instead. See [Events](/docs/actors/events) for more details on broadcasting. + +For example: + +`conn.send()` has no effect on [low-level WebSocket connections](/docs/actors/websocket-handler). For low-level WebSockets, use the WebSocket API directly (e.g., `websocket.send()`). + +## Disconnecting clients + +Connections can be disconnected from within an action: + +If you need to wait for the disconnection to complete, you can use `await`: + +This ensures the underlying network connections close cleanly before continuing. + +## API Reference + +- [`Conn`](/typedoc/interfaces/rivetkit.mod.Conn.html) - Connection interface +- [`ConnInitContext`](/typedoc/interfaces/rivetkit.mod.ConnInitContext.html) - Connection initialization context +- [`CreateConnStateContext`](/typedoc/interfaces/rivetkit.mod.CreateConnStateContext.html) - Context for creating connection state +- [`OnBeforeConnectContext`](/typedoc/interfaces/rivetkit.mod.OnBeforeConnectContext.html) - Pre-connection lifecycle hook context +- [`OnConnectContext`](/typedoc/interfaces/rivetkit.mod.OnConnectContext.html) - Post-connection lifecycle hook context +- [`ActorConn`](/typedoc/types/rivetkit.client_mod.ActorConn.html) - Typed connection from client side + +_Source doc path: /docs/actors/connections_ diff --git a/.agents/skills/per-tenant-database/reference/actors/debugging.md b/.agents/skills/per-tenant-database/reference/actors/debugging.md new file mode 100644 index 0000000..409b381 --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/actors/debugging.md @@ -0,0 +1,644 @@ +# Debugging + +> Source: `src/content/docs/actors/debugging.mdx` +> Canonical URL: https://rivet.dev/docs/actors/debugging +> Description: Inspect and debug running Rivet Actors, runners, and provider configs using management, runner, and actor inspector HTTP APIs. + +--- +## Connecting to Rivet + +All debugging endpoints in this guide are available both locally and in production. In local development, the base URL is `http://localhost:6420` with no authentication. In production (Rivet Cloud or self-hosted), you connect to your Rivet Engine endpoint with a token. + +### Setup + +All examples in this guide use these shell variables. Extract them from your `RIVET_ENDPOINT` (`https://:@`): + +```bash +# From RIVET_ENDPOINT=https://my-namespace:sk_abc123@api.rivet.dev +export RIVET_API="https://api.rivet.dev" +export RIVET_NAMESPACE="my-namespace" +export RIVET_TOKEN="sk_abc123" + +# For local development: +# export RIVET_API="http://localhost:6420" +``` + +Rivet Cloud issues two token types: `sk_` (secret key, server-side only) and `pk_` (public key, client-safe). For debugging, always use `sk_`. See [Endpoints](/docs/general/endpoints) for more details. + +## Management API + +The management API runs on the manager base path (default root path) and is used to list, create, and look up actors. + +### Authentication + +| Environment | Authentication | +|---|---| +| **Local development** | No authentication required. All endpoints are accessible without tokens. | +| **Self-hosted engine** | Set `RIVET_TOKEN` to enable authenticated access to restricted endpoints like KV. | +| **Rivet Cloud** | Authentication is enforced by your deployment entrypoint. For manager KV access, use the bearer token header below when enabled. | + +Restricted endpoints (like KV reads) require the `Authorization: Bearer` header when `RIVET_TOKEN` is configured: + +```bash +curl "$RIVET_API/actors/{actor_id}/kv/keys/{base64_key}" \ + -H "Authorization: Bearer $RIVET_TOKEN" +``` + +### List Actors + +```bash +# List all actors with a given name +curl "$RIVET_API/actors?name=my-actor&namespace=$RIVET_NAMESPACE" \ + -H "Authorization: Bearer $RIVET_TOKEN" + +# Look up one actor by key (name is required when key is provided) +curl "$RIVET_API/actors?name=my-actor&key=%5B%22my-key%22%5D&namespace=$RIVET_NAMESPACE" \ + -H "Authorization: Bearer $RIVET_TOKEN" + +# List actors by IDs (comma-separated) +curl "$RIVET_API/actors?actor_ids=id1,id2&namespace=$RIVET_NAMESPACE" \ + -H "Authorization: Bearer $RIVET_TOKEN" +``` + +Rules: + +- `key` requires `name`. +- `actor_ids` cannot be combined with `name` or `key`. + +Returns: + +```json +{ + "actors": [ + { + "actor_id": "abc123", + "name": "my-actor", + "key": "[\"default\"]", + "namespace_id": "default", + "create_ts": 1706000000000 + } + ] +} +``` + +### Create Actor + +`POST /actors` creates a new actor. + +```bash +curl -X POST "$RIVET_API/actors?namespace=$RIVET_NAMESPACE" \ + -H "Authorization: Bearer $RIVET_TOKEN" \ + -H 'Content-Type: application/json' \ + -d '{ + "name": "my-actor", + "runner_name_selector": "default", + "crash_policy": "restart" + }' +``` + +### Create or Get Actor + +`PUT /actors` creates an actor if it does not exist, otherwise returns the existing one. + +```bash +curl -X PUT "$RIVET_API/actors?namespace=$RIVET_NAMESPACE" \ + -H "Authorization: Bearer $RIVET_TOKEN" \ + -H 'Content-Type: application/json' \ + -d '{ + "name": "my-actor", + "key": "[\"default\"]", + "runner_name_selector": "default", + "crash_policy": "restart" + }' +``` + +Returns the actor object with its `actor_id`. + +### List Actor Names + +```bash +curl "$RIVET_API/actors/names?namespace=$RIVET_NAMESPACE" \ + -H "Authorization: Bearer $RIVET_TOKEN" +``` + +Returns all registered actor names and their metadata. + +### Read Actor KV + +Requires authentication (see above). + +```bash +curl "$RIVET_API/actors/{actor_id}/kv/keys/{base64_key}" \ + -H "Authorization: Bearer $RIVET_TOKEN" +``` + +Returns the value stored at the given key in the actor KV compatibility snapshot. After an actor has migrated to SQLite-backed runtime storage, this endpoint is stale for user KV and internal runtime records; it continues to expose the frozen pre-migration KV data. The inspector token key is the exception and remains mirrored for dashboard compatibility. + +See the [OpenAPI spec](https://github.com/rivet-dev/rivet/tree/main/rivetkit-openapi) for the full schema of all management endpoints. + +## Runner API + +Use the runner endpoints to debug scheduler capacity and provider configuration (for example serverless URL, headers, and limits) through the Rivet API. + +### List Runner Names + +```bash +curl "$RIVET_API/runners/names?namespace=$RIVET_NAMESPACE" \ + -H "Authorization: Bearer $RIVET_TOKEN" +``` + +Returns the runner pools available in the namespace: + +```json +{ + "names": ["default", "gpu-workers"], + "pagination": { "cursor": null } +} +``` + +### List Runners in a Pool + +```bash +curl "$RIVET_API/runners?namespace=$RIVET_NAMESPACE&name=default&include_stopped=true&limit=100" \ + -H "Authorization: Bearer $RIVET_TOKEN" +``` + +Useful fields when debugging: + +- `remaining_slots` / `total_slots` for capacity. +- `drain_ts` and `stop_ts` for shutdown behavior. +- `last_ping_ts` and `last_connected_ts` for connectivity. + +### Inspect Provider Config (Runner Config) + +```bash +curl "$RIVET_API/runner-configs?namespace=$RIVET_NAMESPACE&runner_name=default" \ + -H "Authorization: Bearer $RIVET_TOKEN" +``` + +Returns the configured provider settings per datacenter and the latest pool error (if any): + +```json +{ + "runner_configs": { + "default": { + "datacenters": { + "dc-1": { + "serverless": { + "url": "https://your-deployment.example.com/rivet", + "headers": { "Authorization": "Bearer token" }, + "request_lifespan": 55, + "slots_per_runner": 1, + "max_runners": 10 + }, + "runner_pool_error": null + } + } + } + }, + "pagination": { "cursor": null } +} +``` + +`runner_pool_error` mirrors actor scheduling errors such as `serverless_http_error`, `serverless_connection_error`, and `serverless_stream_ended_early`. + +### Check Serverless Provider Health + +Use this to test whether Rivet can reach your serverless provider URL and read runner metadata: + +```bash +curl -X POST "$RIVET_API/runner-configs/serverless-health-check?namespace=$RIVET_NAMESPACE" \ + -H "Authorization: Bearer $RIVET_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "url": "https://your-deployment.example.com/rivet", + "headers": { + "Authorization": "Bearer token" + } + }' +``` + +Possible responses: + +```json +{ "success": { "version": "1.2.3" } } +``` + +```json +{ + "failure": { + "error": { + "message": "non-success status from metadata endpoint", + "details": "received status 503" + } + } +} +``` + +### Refresh Provider Metadata + +If you deploy new actor code or routes and metadata has not updated yet, force a refresh: + +```bash +curl -X POST "$RIVET_API/runner-configs/default/refresh-metadata?namespace=$RIVET_NAMESPACE" \ + -H "Authorization: Bearer $RIVET_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{}' +``` + +## Actor API + +All actor-level endpoints are accessed through the gateway. The gateway routes requests to the correct actor instance using the actor ID in the URL path: + +``` +{RIVET_API}/gateway/{actor_id}/{path} +``` + +The gateway only accepts actor IDs, not names. Use `GET /actors?name=...` from the management API to look up actor IDs first. + +### Authentication + +Standard actor endpoints (health, actions, requests) and inspector endpoints have separate authentication requirements. + +#### Standard Endpoints + +| Environment | Authentication | +|---|---| +| **Local development** | No authentication required. | +| **Self-hosted engine** | The Rivet Engine handles authentication at the gateway level. | +| **Rivet Cloud** | Authentication is handled by the Rivet Cloud platform at the gateway level. | + +#### Inspector Endpoints + +Each actor generates a unique inspector token on first start and persists it in internal SQLite storage. It is also mirrored to legacy KV key `0x03` (base64 `Aw==`) for dashboard compatibility. Pass it as a bearer token in the `Authorization` header. + +Inspector endpoints always require the actor's inspector token, including in local development. There is no local-development bypass. + +| Environment | Authentication | +|---|---| +| **Local development** | Bearer the actor's inspector token in the `Authorization` header. Fetch it through the management KV endpoint (see below). | +| **Self-hosted engine** | Bearer the actor's inspector token in the `Authorization` header. The Rivet dashboard fetches it automatically; for direct API access, fetch it through the management KV endpoint (see below). | +| **Rivet Cloud** | Bearer the actor's inspector token in the `Authorization` header. The Rivet dashboard fetches it automatically; for direct API access, fetch it through the management KV endpoint (see below). | + +```bash +curl "$RIVET_API/gateway/{actor_id}/inspector/summary" \ + -H 'Authorization: Bearer YOUR_INSPECTOR_TOKEN' +``` + +#### Retrieving the Inspector Token + +Each actor generates a unique inspector token on first start and persists it in internal SQLite storage. The token is also mirrored to the legacy KV key below so the Rivet dashboard can continue to retrieve it through the management KV endpoint. This applies in every environment, including local development. + +The inspector token is stored at internal KV key `0x03` (base64: `Aw==`). The response value is also base64-encoded. + +```bash +# Fetch the inspector token for a specific actor +ACTOR_ID="your-actor-id" + +RESPONSE=$(curl -s "$RIVET_API/actors/$ACTOR_ID/kv/keys/Aw==" \ + -H "Authorization: Bearer $RIVET_TOKEN") + +# Extract and decode the base64 value +INSPECTOR_TOKEN=$(echo "$RESPONSE" | jq -r '.value' | base64 -d) + +# Use it to call inspector endpoints +curl "$RIVET_API/gateway/$ACTOR_ID/inspector/summary" \ + -H "Authorization: Bearer $INSPECTOR_TOKEN" +``` + +### Standard Actor Endpoints + +These are the built-in actor endpoints available through the gateway: + +```bash +# Health check +curl $RIVET_API/gateway/{actor_id}/health + +# Metadata +curl $RIVET_API/gateway/{actor_id}/metadata + +# Call an action +curl -X POST $RIVET_API/gateway/{actor_id}/action/myAction \ + -H 'Content-Type: application/json' \ + -d '{"args": [1, 2, 3]}' + +# Send queue message (queue name in path) +curl -X POST $RIVET_API/gateway/{actor_id}/queue/jobs \ + -H 'Content-Type: application/json' \ + -d '{"body":{"id":"job-1"}}' + +# Send queue message and wait for completion (optional timeout in ms) +curl -X POST $RIVET_API/gateway/{actor_id}/queue/jobs \ + -H 'Content-Type: application/json' \ + -d '{"body":{"id":"job-1"},"wait":true,"timeout":5000}' + +# Forward an HTTP request to the actor's onRequest handler +curl $RIVET_API/gateway/{actor_id}/request/my/custom/path +``` + +Queue send responses always include a `status` field: + +```json +{ "status": "completed" } +``` + +The `response` field is only present when the queue handler returns a value: + +```json +{ "status": "completed", "response": { "result": "ok" } } +``` + +If `wait: true` and the timeout is reached, `status` is `"timedOut"`. + +### Inspector Endpoints + +The inspector HTTP API exposes JSON endpoints for querying and modifying actor internals at runtime. These are designed for agent-based debugging and tooling. + +Every inspector endpoint requires the actor's inspector token as a bearer token, including in local development. The examples below omit the `Authorization` header for brevity, but you must add `-H "Authorization: Bearer $INSPECTOR_TOKEN"` to each request. See [Retrieving the Inspector Token](#retrieving-the-inspector-token) above. + +#### Get State + +```bash +curl $RIVET_API/gateway/{actor_id}/inspector/state +``` + +Returns the actor's current persisted state: + +```json +{ + "state": { "count": 42, "users": [] }, + "isStateEnabled": true +} +``` + +#### Set State + +```bash +curl -X PATCH $RIVET_API/gateway/{actor_id}/inspector/state \ + -H 'Content-Type: application/json' \ + -d '{"state": {"count": 0, "users": []}}' +``` + +Returns: + +```json +{ "ok": true } +``` + +#### Get Connections + +```bash +curl $RIVET_API/gateway/{actor_id}/inspector/connections +``` + +Returns all active connections with their params, state, and metadata: + +```json +{ + "connections": [ + { + "type": "websocket", + "id": "conn-id", + "details": { + "type": "websocket", + "params": {}, + "stateEnabled": true, + "state": {}, + "subscriptions": 2, + "isHibernatable": true + } + } + ] +} +``` + +#### Get RPCs + +```bash +curl $RIVET_API/gateway/{actor_id}/inspector/rpcs +``` + +Returns a list of available actions: + +```json +{ "rpcs": ["increment", "getCount"] } +``` + +#### Execute Action + +```bash +curl -X POST $RIVET_API/gateway/{actor_id}/inspector/action/increment \ + -H 'Content-Type: application/json' \ + -d '{"args": [5]}' +``` + +Returns: + +```json +{ "output": 47 } +``` + +#### Get Queue Status + +```bash +curl $RIVET_API/gateway/{actor_id}/inspector/queue?limit=10 +``` + +Returns queue status with messages: + +```json +{ + "size": 3, + "maxSize": 1000, + "truncated": false, + "messages": [ + { "id": 1, "name": "process", "createdAtMs": 1706000000000 } + ] +} +``` + +#### Get Workflow History + +```bash +curl $RIVET_API/gateway/{actor_id}/inspector/workflow-history +``` + +Returns: + +```json +{ + "history": null, + "isWorkflowEnabled": false +} +``` + +#### Get Database Schema + +```bash +curl $RIVET_API/gateway/{actor_id}/inspector/database/schema +``` + +Returns discovered SQLite tables and views when the actor has `c.db` enabled: + +```json +{ + "schema": { + "tables": [ + { + "table": { "schema": "main", "name": "test_data", "type": "table" }, + "columns": [ + { "cid": 0, "name": "id", "type": "", "notnull": 0, "dflt_value": null, "pk": 0 } + ], + "foreignKeys": [], + "records": 2 + } + ] + } +} +``` + +#### Get Database Rows + +```bash +curl "$RIVET_API/gateway/{actor_id}/inspector/database/rows?table=test_data&limit=100&offset=0" +``` + +Returns paged rows for a specific SQLite table or view: + +```json +{ + "rows": [ + { + "id": 1, + "value": "Alice", + "payload": "", + "created_at": 1706000000000 + } + ] +} +``` + +#### Execute SQLite + +Run manual SQL against an actor's SQLite database. This supports both read-only queries and mutations. + +```bash +curl -X POST http://localhost:6420/gateway/{actor_id}/inspector/database/execute \ + -H 'Content-Type: application/json' \ + -d '{ + "sql": "SELECT id, value FROM test_data WHERE value = ? ORDER BY id DESC", + "args": ["alpha"] + }' +``` + +Returns: + +```json +{ + "rows": [ + { "id": 2, "value": "alpha" } + ] +} +``` + +You can also use named SQLite bindings through a `properties` object: + +```bash +curl -X POST http://localhost:6420/gateway/{actor_id}/inspector/database/execute \ + -H 'Content-Type: application/json' \ + -d '{ + "sql": "SELECT id, value FROM test_data WHERE value = :value ORDER BY id DESC", + "properties": { + "value": "alpha" + } + }' +``` + +For mutations, use `RETURNING` if you want rows back. Otherwise the statement still runs and `rows` is empty: + +```bash +curl -X POST http://localhost:6420/gateway/{actor_id}/inspector/database/execute \ + -H 'Content-Type: application/json' \ + -d '{ + "sql": "INSERT INTO test_data (value, created_at) VALUES (?, ?) RETURNING id, value", + "args": ["beta", 1706000000000] + }' +``` + +For workflow-enabled actors, `history` is a JSON object with `nameRegistry`, `entries`, and `entryMetadata`. Step outputs, loop state, and message payloads are decoded from CBOR into normal JSON values. + +#### Replay Workflow From Step + +Reset a workflow to a specific step and restart execution immediately. Omitting `entryId` replays the workflow from the beginning. + +If the workflow is still running when you call replay, the endpoint rejects the request with `409 Conflict` and an `actor/workflow_in_flight` error instead of cancelling the live run for you. + +```bash +curl -X POST http://localhost:6420/gateway/{actor_id}/inspector/workflow/replay \ + -H 'Content-Type: application/json' \ + -d '{"entryId":"workflow-step-id"}' +``` + +Returns the same JSON shape as `/inspector/workflow-history`: + +```json +{ + "history": { + "nameRegistry": ["step-one", "step-two"], + "entries": [], + "entryMetadata": {} + }, + "isWorkflowEnabled": true +} +``` + +While a workflow is in flight, the response shape is: + +```json +{ + "group": "actor", + "code": "workflow_in_flight", + "message": "Workflow replay is unavailable while the workflow is currently in flight.", + "metadata": null +} +``` + +#### Summary + +Get a full snapshot of the actor in a single request: + +```bash +curl $RIVET_API/gateway/{actor_id}/inspector/summary +``` + +Returns: + +```json +{ + "state": { "count": 42 }, + "connections": [], + "rpcs": ["increment", "getCount"], + "queueSize": 0, + "isStateEnabled": true, + "isDatabaseEnabled": false, + "isWorkflowEnabled": false, + "workflowHistory": null +} +``` + +When workflow history is present in `/inspector/summary`, `workflowHistory` is returned as the same decoded JSON shape as `/inspector/workflow-history`. + +### Polling + +Inspector endpoints are safe to poll. For live monitoring, poll at 1-5 second intervals. The `/inspector/summary` endpoint is useful for periodic snapshots since it returns all data in a single request. + +## OpenAPI Spec + +An OpenAPI specification covering many of the management and actor endpoints is available: + +- In the repository at [`rivetkit-openapi/openapi.json`](https://github.com/rivet-dev/rivet/tree/main/rivetkit-openapi) +- Served at `/doc` on the manager when running locally + +The checked-in spec does not yet list every endpoint documented on this page (for example the actor metadata and queue routes and the inspector database routes), so treat this page as the authoritative reference where they differ. + +_Source doc path: /docs/actors/debugging_ diff --git a/.agents/skills/per-tenant-database/reference/actors/design-patterns.md b/.agents/skills/per-tenant-database/reference/actors/design-patterns.md new file mode 100644 index 0000000..77ecb40 --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/actors/design-patterns.md @@ -0,0 +1,163 @@ +# Design Patterns + +> Source: `src/content/docs/actors/design-patterns.mdx` +> Canonical URL: https://rivet.dev/docs/actors/design-patterns +> Description: Common patterns and anti-patterns for building scalable actor systems. + +--- +## How Actors Scale + +Actors are inherently scalable because of how they're designed: + +- **Isolated state:** Each actor manages its own private data. No shared state means no conflicts and no locks, so actors run concurrently without coordination. +- **Actor-to-actor communication:** Actors interact through [actions](/docs/actors/actions) and [events](/docs/actors/events), so they don't need to coordinate access to shared data. This makes it easy to distribute them across machines. +- **Small, focused units:** Each actor handles a limited scope (a single user, document, or chat room), so load naturally spreads across many actors rather than concentrating in one place. +- **Horizontal scaling:** Adding more machines automatically distributes actors across them. + +These properties form the foundation for the patterns described below. + +## Actor Per Entity + +The core pattern is creating one actor per entity in your system. Each actor represents a single user, document, chat room, or other distinct object. This keeps actors small, independent, and easy to scale. + +**Good examples** + +- `User`: Manages user profile, preferences, and authentication +- `Document`: Handles document content, metadata, and versioning +- `ChatRoom`: Manages participants and message history + +**Bad examples** + +- `Application`: Too broad, handles everything +- `DocumentWordCount`: Too granular, should be part of Document actor + +## Coordinator & Data Actors + +Actors scale by splitting state into isolated entities. However, it's common to need to track and coordinate actors in a central place. This is where coordinator actors come in. + +**Data actors** handle the main logic in your application. Examples: chat rooms, user sessions, game lobbies. + +**Coordinator actors** track other actors. Think of them as an index of data actors. Examples: a list of chat rooms, a list of active users, a list of game lobbies. + +**Example: Chat Room Coordinator** + +### Actor + +### Client + +## Sharding + +Sharding splits a single actor's workload across multiple actors based on a key. Use this when one actor can't handle all the load or data for an entity. + +**How it works:** +- Partition data using a shard key (user ID, region, time bucket, or random) +- Requests are routed to shards based on the key +- Shards operate independently without coordination + +**Example: Sharding by Time** + +### Actor + +### Client + +**Example: Random Sharding** + +### Actor + +### Client + +Choose shard keys that distribute load evenly. Note that cross-shard queries require coordination. + +## Fan-In & Fan-Out + +Fan-in and fan-out are patterns for distributing work and aggregating results. + +**Fan-Out**: One actor spawns work across multiple actors. Use for parallel processing or broadcasting updates. + +**Fan-In**: Multiple actors send results to one aggregator. Use for collecting results or reducing data. + +**Example: Map-Reduce** + +### Actor + +### Client + +## Integrating With External Databases & APIs + +Actors can integrate with external resources like databases or external APIs. + +### Loading State + +Load external data during actor initialization using `createVars`. This keeps your actor's persisted state clean while caching expensive lookups. + +Use this when: + +- Fetching user profiles, configs, or permissions from a database +- Loading data that changes externally and shouldn't be persisted +- Caching expensive API calls or computations + +**Example: Loading User Profile** + +### Actor + +### Client + +### Syncing State Changes + +Use `onStateChange` to automatically sync actor state changes to external resources. This hook runs after state changes are flushed, which is coalesced to once per event loop tick rather than once per individual field mutation. + +Use this when: + +- You need to mirror actor state in an external database +- Triggering external side effects when state changes +- Keeping external systems in sync with actor state + +**Example: Syncing to Database** + +### Actor + +### Client + +`onStateChange` is called once per flush with the final coalesced state, ensuring external resources stay in sync. In the `updateEmail` example above, the two synchronous assignments produce a single `onStateChange` call. + +Do not mutate `c.state` inside `onStateChange`; re-entrant state mutation is rejected. + +## Anti-Patterns + +### "God" Actor + +Avoid creating a single actor that handles everything. This defeats the purpose of the actor model and creates a bottleneck. + +**Problem:** +```ts +import { actor } from "rivetkit"; + +// Bad: one actor doing everything +const app = actor({ + state: { users: {}, orders: {}, inventory: {}, analytics: {} }, + actions: { + createUser: (c, user) => { /* ... */ }, + processOrder: (c, order) => { /* ... */ }, + updateInventory: (c, item) => { /* ... */ }, + trackEvent: (c, event) => { /* ... */ }, + }, +}); +``` + +**Solution:** Split into focused actors per entity (User, Order, Inventory, Analytics). + +### Actor-Per-Request + +Actors are designed to maintain state across multiple requests. Creating a new actor for each request wastes resources and loses the benefits of persistent state. + +**Problem:** + +**Solution:** Use actors for entities that persist (users, sessions, documents), not for one-off operations. For stateless request handling, use regular functions. + +## API Reference + +- [`ActorDefinition`](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html) - Interface for pattern examples +- [`ActorContext`](/typedoc/interfaces/rivetkit.mod.ActorContext.html) - Context usage patterns +- [`ActionContext`](/typedoc/interfaces/rivetkit.mod.ActionContext.html) - Action patterns + +_Source doc path: /docs/actors/design-patterns_ diff --git a/.agents/skills/per-tenant-database/reference/actors/destroy.md b/.agents/skills/per-tenant-database/reference/actors/destroy.md new file mode 100644 index 0000000..87e1b76 --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/actors/destroy.md @@ -0,0 +1,71 @@ +# Destroying Actors + +> Source: `src/content/docs/actors/destroy.mdx` +> Canonical URL: https://rivet.dev/docs/actors/destroy +> Description: Actors can be permanently destroyed. Common use cases include: + +--- +- User account deletion +- Ending a user session +- Closing a room or game +- Cleaning up temporary resources +- GDPR/compliance data removal + +Actors sleep when idle, so destruction is only needed to permanently remove data — not to save compute. + +## Destroying An Actor + +### Destroy via Action + +To destroy an actor, use `c.destroy()` like this: + +### Destroy via HTTP + +Send a DELETE request to destroy an actor. This requires a token for authentication: + +- **Rivet Cloud**: Use the service key (`sk_*`) from your `RIVET_ENDPOINT` URL. Find this in the dashboard under **Settings > Advanced > Backend Configuration** (e.g. the `sk_...` portion of `https://default:sk_abc123@api.rivet.dev`). +- **Self-hosted**: Use an admin token. + +```typescript +const actorId = "your-actor-id"; +const namespace = "default"; +const token = "your-token"; + +await fetch(`https://api.rivet.dev/actors/${actorId}?namespace=${namespace}`, { + method: "DELETE", + headers: { + Authorization: `Bearer ${token}`, + }, +}); +``` + +```bash +curl -X DELETE "https://api.rivet.dev/actors/{actorId}?namespace={namespace}" \ + -H "Authorization: Bearer {token}" +``` + +To find the actor ID, you can list actors first: + +```bash +curl "https://api.rivet.dev/actors?namespace={namespace}" \ + -H "Authorization: Bearer {token}" +``` + +### Destroy via Dashboard + +To destroy an actor via the dashboard, navigate to the actor and press the red "X" in the top right. + +## Lifecycle Hook + +Once destroyed, the `onDestroy` hook will be called. This can be used to clean up resources related to the actor. For example: + +## Accessing Actor After Destroy + +Once an actor is destroyed, any subsequent requests to it will fail with an `actor.not_found` error (`{ group: "actor", code: "not_found" }`). The actor's state is permanently deleted. + +## API Reference + +- [`ActorHandle`](/typedoc/types/rivetkit.client_mod.ActorHandle.html) - Has destroy methods +- [`ActorContext`](/typedoc/interfaces/rivetkit.mod.ActorContext.html) - Context during destruction + +_Source doc path: /docs/actors/destroy_ diff --git a/.agents/skills/per-tenant-database/reference/actors/errors.md b/.agents/skills/per-tenant-database/reference/actors/errors.md new file mode 100644 index 0000000..af24c4c --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/actors/errors.md @@ -0,0 +1,81 @@ +# Errors + +> Source: `src/content/docs/actors/errors.mdx` +> Canonical URL: https://rivet.dev/docs/actors/errors +> Description: Rivet provides robust error handling with security built in by default. Errors are handled differently based on whether they should be exposed to clients or kept private. + +--- +There are two types of errors: + +- **UserError**: Thrown from actors and safely returned to clients with full details +- **Internal errors**: All other errors that are converted to a generic error message for security + +## Throwing and Catching Errors + +`UserError` lets you throw custom errors that will be safely returned to the client. + +Throw a `UserError` with just a message: + +### Actor + +### Client (Connection) + +### Client (Stateless) + +## Error Codes + +Use error codes for explicit error matching in try-catch blocks: + +### Actor + +### Client (Connection) + +### Client (Stateless) + +## Errors With Metadata + +Include metadata to provide additional context for rich error handling: + +### Actor + +### Client (Connection) + +### Client (Stateless) + +## Internal Errors + +All errors that are not UserError instances are automatically converted to a generic "internal error" response. This prevents accidentally leaking sensitive information like stack traces, database details, or internal system information. + +### Actor + +### Client (Connection) + +### Client (Stateless) + +### Server-Side Logging + +**All internal errors are logged server-side with full details.** When an internal error occurs, the complete error message, stack trace, and context are written to your server logs. This is where you should look first when debugging internal errors in production. + +The client receives only a generic "Internal error" message for security, but you can find the full error details in your server logs including: + +- Complete error message +- Stack trace +- Request context (actor ID, action name, connection ID, etc.) +- Timestamp + +**Always check your server logs to see the actual error details when debugging internal errors.** + +### Exposing Errors to Clients (Development Only) + +**Warning:** Only enable error exposure in development environments. In production, this will leak sensitive internal details to clients. + +For faster debugging during development, you can expose internal error details to clients by setting `RIVET_EXPOSE_ERRORS=1`. + +With error exposure enabled, clients will see the full error message instead of the generic "Internal error" response: + +## API Reference + +- [`UserError`](/typedoc/classes/rivetkit.actor_errors.UserError.html) - User-facing error class +- [`ActorError`](/typedoc/classes/rivetkit.client_mod.ActorError.html) - Errors received by the client + +_Source doc path: /docs/actors/errors_ diff --git a/.agents/skills/per-tenant-database/reference/actors/events.md b/.agents/skills/per-tenant-database/reference/actors/events.md new file mode 100644 index 0000000..36e2c69 --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/actors/events.md @@ -0,0 +1,143 @@ +# Realtime + +> Source: `src/content/docs/actors/events.mdx` +> Canonical URL: https://rivet.dev/docs/actors/events +> Description: Events enable realtime communication from actors to clients. While clients use actions to send data to actors, events allow actors to push updates to connected clients instantly. + +--- +Events can be sent to clients connected using `.connect()`. They have no effect on [low-level WebSocket connections](/docs/actors/websocket-handler). + +## Publishing Events from Actors + +### Broadcasting to All Clients + +Define event names and payload types with `events` and `event()`, then use `c.broadcast(eventName, ...args)` to send events to all connected clients: + +### Sending to Specific Connections + +Send events to individual connections using `conn.send(eventName, ...args)`: + +Send events to all connections except the sender: + +## Subscribing to Events from Clients + +Clients must establish a connection to receive events from actors. Use `.connect()` to create a persistent connection, then listen for events. + +### Basic Event Subscription + +Use `connection.on(eventName, callback)` to listen for events: + +```tsx React @nocheck +import { useState } from "react"; +import { useActor } from "./rivetkit"; + +function ChatRoom() { + const [messages, setMessages] = useState>([]); + + const chatRoom = useActor({ + name: "chatRoom", + key: ["general"] + }); + + // Listen for events + chatRoom.useEvent("messageReceived", (message) => { + setMessages(prev => [...prev, message]); + }); + + // ...rest of component... +} +``` + +### One-time Event Listeners + +Use `connection.once(eventName, callback)` for events that should only trigger once: + +```tsx React @nocheck +import { useState, useEffect } from "react"; +import { useActor } from "./rivetkit"; + +function GameLobby() { + const [gameStarted, setGameStarted] = useState(false); + + const gameRoom = useActor({ + name: "gameRoom", + key: ["room-456"], + params: { + playerId: "player-789", + role: "player" + } + }); + + // Listen for game start (only once) + useEffect(() => { + if (!gameRoom.connection) return; + + const handleGameStart = () => { + console.log('Game has started!'); + setGameStarted(true); + }; + + gameRoom.connection.once('gameStarted', handleGameStart); + }, [gameRoom.connection]); + + // ...rest of component... +} +``` + +### Removing Event Listeners + +Use the callback returned from `.on()` to remove event listeners: + +```tsx React @nocheck +import { useState, useEffect } from "react"; +import { useActor } from "./rivetkit"; + +function ConditionalListener() { + const [isListening, setIsListening] = useState(false); + const [messages, setMessages] = useState([]); + + const chatRoom = useActor({ + name: "chatRoom", + key: ["general"] + }); + + useEffect(() => { + if (!chatRoom.connection || !isListening) return; + + // Add listener + const unsubscribe = chatRoom.connection.on('messageReceived', (message) => { + setMessages(prev => [...prev, message.text]); + }); + + // Cleanup - remove listener when component unmounts or listening stops + return () => { + unsubscribe(); + }; + }, [chatRoom.connection, isListening]); + + // ...rest of component... +} +``` + +## Debugging + +- `GET /inspector/connections` shows active connections and connection metadata. +- Use this to confirm clients are connected before expecting broadcasts. +- `GET /inspector/summary` provides connections, RPCs, and queue size in one response. +- In non-dev mode, inspector endpoints require authorization. + +## More About Connections + +For more details on actor connections, including connection lifecycle, authentication, and advanced connection patterns, see the [Connections documentation](/docs/actors/connections). + +## API Reference + +- [`RivetEvent`](/typedoc/interfaces/rivetkit.mod.RivetEvent.html) - Base event interface +- [`RivetMessageEvent`](/typedoc/interfaces/rivetkit.mod.RivetMessageEvent.html) - Message event type +- [`RivetCloseEvent`](/typedoc/interfaces/rivetkit.mod.RivetCloseEvent.html) - Close event type +- [`UniversalEvent`](/typedoc/interfaces/rivetkit.mod.UniversalEvent.html) - Universal event type +- [`UniversalMessageEvent`](/typedoc/interfaces/rivetkit.mod.UniversalMessageEvent.html) - Universal message event +- [`UniversalErrorEvent`](/typedoc/interfaces/rivetkit.mod.UniversalErrorEvent.html) - Universal error event +- [`EventUnsubscribe`](/typedoc/types/rivetkit.client_mod.EventUnsubscribe.html) - Unsubscribe function type + +_Source doc path: /docs/actors/events_ diff --git a/.agents/skills/per-tenant-database/reference/actors/fetch-and-websocket-handler.md b/.agents/skills/per-tenant-database/reference/actors/fetch-and-websocket-handler.md new file mode 100644 index 0000000..b8b0509 --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/actors/fetch-and-websocket-handler.md @@ -0,0 +1,10 @@ +# Fetch and WebSocket Handler + +> Source: `src/content/docs/actors/fetch-and-websocket-handler.mdx` +> Canonical URL: https://rivet.dev/docs/actors/fetch-and-websocket-handler +> Description: These docs have moved to [Low-Level WebSocket Handler](/docs/actors/websocket-handler) and [Low-Level Request Handler](/docs/actors/request-handler). + +--- + + +_Source doc path: /docs/actors/fetch-and-websocket-handler_ diff --git a/.agents/skills/per-tenant-database/reference/actors/helper-types.md b/.agents/skills/per-tenant-database/reference/actors/helper-types.md new file mode 100644 index 0000000..55555c7 --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/actors/helper-types.md @@ -0,0 +1,10 @@ +# Helper Types + +> Source: `src/content/docs/actors/helper-types.mdx` +> Canonical URL: https://rivet.dev/docs/actors/helper-types +> Description: This page has moved to [Types](/docs/actors/types). + +--- + + +_Source doc path: /docs/actors/helper-types_ diff --git a/.agents/skills/per-tenant-database/reference/actors/http-api.md b/.agents/skills/per-tenant-database/reference/actors/http-api.md new file mode 100644 index 0000000..1d8d5be --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/actors/http-api.md @@ -0,0 +1,10 @@ +# Vanilla HTTP API + +> Source: `src/content/docs/actors/http-api.mdx` +> Canonical URL: https://rivet.dev/docs/actors/http-api +> Description: Use the low-level HTTP handler to send and receive requests from actors. + +--- +TODO + +_Source doc path: /docs/actors/http-api_ diff --git a/.agents/skills/per-tenant-database/reference/actors/input.md b/.agents/skills/per-tenant-database/reference/actors/input.md new file mode 100644 index 0000000..c5c5dde --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/actors/input.md @@ -0,0 +1,51 @@ +# Input Parameters + +> Source: `src/content/docs/actors/input.mdx` +> Canonical URL: https://rivet.dev/docs/actors/input +> Description: Pass initialization data to actors when creating instances + +--- +Actors can receive input parameters when created, allowing for flexible initialization and configuration. Input is passed during actor creation and is available in lifecycle hooks. + +## Passing Input to Actors + +Input is provided when creating actor instances using the `input` property: + +## Accessing Input in Lifecycle Hooks + +Input is available as the second argument to the `createState` and `onCreate` lifecycle hooks: + +## Input Validation + +You can validate input parameters in the `createState` or `onCreate` hooks: + +## Input vs Connection Parameters + +Input parameters are different from connection parameters: + +- **Input**: + - Passed when creating the actor instance + - Use for actor-wide configuration + - Available in lifecycle hooks +- **Connection parameters**: + - Passed when connecting to an existing actor + - Used for connection-specific configuration + - Available in connection hooks + +## Input Best Practices + +### Use Type Safety + +Define input types to ensure type safety: + +### Store Input in State + +Input is only available in `createState` and `onCreate` lifecycle hooks. If you need to access input data later (in actions, timers, or other hooks), store it in the actor's state during creation. This is the recommended pattern because input shapes can evolve over time, and persisting input in state ensures you always have access to the values the actor was created with: + +## API Reference + +- [`CreateOptions`](/typedoc/interfaces/rivetkit.client_mod.CreateOptions.html) - Options for creating actors +- [`CreateRequest`](/typedoc/types/rivetkit.client_mod.CreateRequest.html) - Request type for creation +- [`ActorDefinition`](/typedoc/classes/rivetkit.mod.ActorDefinition.html) - Actor definition returned by `actor()` + +_Source doc path: /docs/actors/input_ diff --git a/.agents/skills/per-tenant-database/reference/actors/inspector-tabs.md b/.agents/skills/per-tenant-database/reference/actors/inspector-tabs.md new file mode 100644 index 0000000..34de206 --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/actors/inspector-tabs.md @@ -0,0 +1,251 @@ +# Custom Inspector Tabs + +> Source: `src/content/docs/actors/inspector-tabs.mdx` +> Canonical URL: https://rivet.dev/docs/actors/inspector-tabs +> Description: Ship your own UI tabs alongside a Rivet Actor — embedded directly in the dashboard inspector. + +--- +Custom inspector tabs let you embed your own UI directly in the Rivet +dashboard, next to the built-in tabs. Declare a tab on your actor, point +it at a folder of static files, and the dashboard picks it up +automatically. + +Common uses: + +- Domain-specific debugging panels (queue depth, connection maps, log + filters). +- Operational tools (admin buttons, drain controls, snapshot uploaders). +- Any author-defined view that ships with your actor. + +A runnable example lives at +[`examples/inspector-tabs`](https://github.com/rivet-dev/rivet/tree/main/examples/inspector-tabs). + +## Quickstart + +Declare a tab on your actor: + +Drop an `index.html` in the `source` directory: + +```html + + + + + + + +

Counter:

+ + + +``` + +Open the dashboard and the "Counter" tab appears alongside the built-ins. + +## Configuration + +Each entry in `inspector.tabs[]` is either a **custom tab** or a +**hide modifier** for a built-in. + +### Custom tab + +```ts @nocheck +{ + id: string, // URL-safe id: /^[A-Za-z0-9_-]+$/ + label: string, // Shown in the tab strip + source: string, // Directory of static assets + icon?: string, // Optional icon id +} +``` + +- **`id`** — used as the URL segment and tab-strip key. Cannot collide + with a built-in id (`workflow`, `database`, `state`, `queue`, + `connections`, `console`). +- **`source`** — directory of static files. The bytes you put there are + the bytes the browser sees. Point it at a Vite/webpack `dist/` and + any framework works (React, Vue, Svelte, vanilla — all fine). +- **`icon`** — one of `workflow`, `database`, `state`, `queue`, `plug`, + `terminal`, `tag`, `logs`. Anything else falls back to a neutral icon. + +### Hide a built-in tab + +```ts @nocheck +{ + id: "workflow" | "database" | "state" | "queue" | "connections" | "console", + hidden: true, +} +``` + +Use this to clean up the strip when the actor doesn't use a given +subsystem — e.g. a counter actor with no queues: + +```ts +inspector: { tabs: [{ id: "queue", hidden: true }] } +``` + +Misconfigurations (missing directory, duplicate id, invalid characters, +empty label) throw at registry construction, so problems show up +immediately. + +## Talking to the dashboard + +The tab loads in an iframe and communicates with the dashboard via +`postMessage`. The contract is small. + +### From the dashboard + +The dashboard sends an `init` message on load and again whenever the +inspector token rotates. Always overwrite the cached token when it +arrives. + +```ts @nocheck +{ + type: "init", + v: 1, + actorId: string, + authToken: string, // Per-actor inspector bearer token + theme?: "light" | "dark", + activeTab?: string, // For multi-view tabs +} +``` + +Multi-view tabs can read the optional `activeTab` field on `init` to +seed their initial sub-view. The dashboard does not send a separate +message when the user switches custom tabs — it navigates the iframe +`src` instead, so the tab reloads and receives a fresh `init`. + +### From the tab + +Send `ready` once your message listener is registered: + +```ts @nocheck +{ type: "ready", v: 1 } +``` + +If a fetch returns 401, the token has rotated. Ask the dashboard for a +fresh one and wait for the next `init` — don't retry with the stale +token: + +```ts @nocheck +{ type: "token-refresh-needed", v: 1 } +``` + +### Security check + +Always reject messages whose `event.origin` doesn't match the +`?shellOrigin=` URL parameter. Without this check, any page that frames +your tab could forge an `init` and feed you a fake token. + +### TypeScript types + +If you build the tab with TypeScript, the message and response types +are exported as types-only: + +## Reading state and calling actions + +The tab can hit any inspector endpoint with the supplied bearer token. +Use relative paths so the tab doesn't need to know the engine origin or +actor id: + +```js +fetch("../../state", { headers: { Authorization: `Bearer ${token}` } }); +fetch("../../action/increment", { method: "POST", headers: { ... }, body: ... }); +fetch("../../rpcs", { headers: { Authorization: `Bearer ${token}` } }); +fetch("../../connections", { headers: { Authorization: `Bearer ${token}` } }); +fetch("../../queue", { headers: { Authorization: `Bearer ${token}` } }); +``` + +The action body shape is `{ args: [...] }` — the array is passed as +positional arguments to the action. + +Full endpoint reference: +[Debugging → Inspector Endpoints](/docs/actors/debugging#inspector-endpoints). + +For high-frequency UIs, prefer the inspector WebSocket +(`/inspector/connect`) over polling. + +## Styling + +A shared stylesheet matching the dashboard's design tokens is served at +`../../tab.css`: + +```html + +``` + +It defines `--rivet-*` tokens for colors, spacing, radius, and +typography, plus sensible defaults so a bare tab looks at home without +custom CSS: + +```css +.my-card { + background: var(--rivet-card); + color: var(--rivet-foreground); + border: 1px solid var(--rivet-border); + border-radius: var(--rivet-radius-md); + padding: var(--rivet-space-4); +} +``` + +Toggle dark mode by adding the `dark` class to `` — the dashboard +sends the active theme in the `init` message. + +Color tokens come in both pre-wrapped (`--rivet-card`) and raw HSL +(`--rivet-card-raw`) forms, so you can compose with alpha: + +```css +.overlay { background: hsl(var(--rivet-background-raw) / 0.6); } +``` + +You're free to skip the stylesheet entirely and bring your own. + +## Security + +The tab runs in an iframe at the engine origin and can call any +inspector endpoint with the supplied token. Treat the bundle like any +author code that ships with your actor: + +- **Don't inline secrets.** The bundle is fetchable by anyone who can + reach the actor. +- **Always validate `event.origin`.** Reject inbound messages from + anywhere other than the dashboard origin. +- **Never retry silently on 401.** Post `token-refresh-needed` and wait + for a fresh `init`. + +## See also + +- [Debugging](/docs/actors/debugging) — full inspector HTTP API +- [Actions](/docs/actors/actions) — actions your tab can invoke +- [State](/docs/actors/state) — state your tab can read +- [`examples/inspector-tabs`](https://github.com/rivet-dev/rivet/tree/main/examples/inspector-tabs) — runnable example + +_Source doc path: /docs/actors/inspector-tabs_ diff --git a/.agents/skills/per-tenant-database/reference/actors/keys.md b/.agents/skills/per-tenant-database/reference/actors/keys.md new file mode 100644 index 0000000..b8f1614 --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/actors/keys.md @@ -0,0 +1,51 @@ +# Actor Keys + +> Source: `src/content/docs/actors/keys.mdx` +> Canonical URL: https://rivet.dev/docs/actors/keys +> Description: Actor keys uniquely identify actor instances within each actor type. Keys are used for addressing which specific actor to communicate with. + +--- +## Key Format + +Actor keys can be either a string or an array of strings: + +### Compound Keys & User Data + +Array keys are useful when you need compound keys with user-provided data. Using arrays makes adding user data safe by preventing key injection attacks: + +This allows you to create hierarchical addressing schemes and organize actors by multiple dimensions. + +Don't build keys using string interpolation like `"foo:${userId}:bar"` when `userId` contains user data. If a user provides a value containing the delimiter (`:` in this example), it can break your key structure and cause key injection attacks. + +### Omitting Keys + +You can create actors without specifying a key in situations where there is a singleton actor (i.e. only one actor of a given type). For example: + +This pattern should be avoided, since a singleton actor usually means you have a single actor serving all traffic & your application will not scale. See [scaling documentation](/docs/actors/scaling) for more information. + +### Key Uniqueness + +Keys are unique within each actor name. Different actor types can use the same key: + +## Accessing Keys in Metadata + +Access the actor's key within the actor using the [metadata](/docs/actors/metadata) API: + +## Configuration Examples + +### Simple Configuration with Keys + +Use keys to provide basic actor configuration: + +### Complex Configuration with Input + +For more complex configuration, use [input parameters](/docs/actors/input): + +## API Reference + +- [`ActorKey`](/typedoc/types/rivetkit.mod.ActorKey.html) - Key type for actors +- [`ActorQuery`](/typedoc/types/rivetkit.mod.ActorQuery.html) - Query type using keys +- [`GetOptions`](/typedoc/interfaces/rivetkit.client_mod.GetOptions.html) - Options for getting by key +- [`QueryOptions`](/typedoc/interfaces/rivetkit.client_mod.QueryOptions.html) - Options for querying + +_Source doc path: /docs/actors/keys_ diff --git a/.agents/skills/per-tenant-database/reference/actors/kv.md b/.agents/skills/per-tenant-database/reference/actors/kv.md new file mode 100644 index 0000000..384769b --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/actors/kv.md @@ -0,0 +1,44 @@ +# Low-Level KV Storage + +> Source: `src/content/docs/actors/kv.mdx` +> Canonical URL: https://rivet.dev/docs/actors/kv +> Description: Use the built-in key-value store on ActorContext for durable string and binary data alongside actor state. + +--- +KV is deprecated. It is a low-level escape hatch kept for backward compatibility. For new actors, prefer [in-memory state](/docs/actors/state) for small serializable values or [SQLite](/docs/actors/sqlite) for larger or queryable data. + +Every Rivet Actor includes a lightweight key-value store on `c.kv`. It is useful for dynamic keys, blobs, or data that does not fit well in structured state. + +If your data has a known schema, prefer [state](/docs/actors/state). KV is best for flexible or user-defined keys. + +## Basic Usage + +Keys and values default to `text`, so you can use strings without extra options. + +## Value Types + +You can store binary values by passing `Uint8Array` or `ArrayBuffer`. Use `type` on both reads and writes to get the right value type: `binary` for `Uint8Array` and `arrayBuffer` for `ArrayBuffer`. + +TypeScript returns a concrete type based on the option you pass in: + +## Key Types + +Keys accept either `string` or `Uint8Array`. String keys are encoded as UTF-8 by default. + +When listing by prefix, you can control how keys are decoded with `keyType`. Returned keys have the prefix removed. + +If you use binary keys, set `keyType: "binary"` so the returned keys stay as `Uint8Array`. + +## Range Operations + +Use `listRange(start, end)` to read an arbitrary half-open range `[start, end)`. Use `deleteRange(start, end)` to clear that same range efficiently. + +## Batch Operations + +KV supports batch operations for efficiency. `batchPut` and `batchGet` work on raw `Uint8Array` keys and values, so encode strings before passing them in. + +## API Reference + +- [`ActorContext`](/typedoc/interfaces/rivetkit.mod.ActorContext.html) - `c.kv` is available on the context + +_Source doc path: /docs/actors/kv_ diff --git a/.agents/skills/per-tenant-database/reference/actors/lifecycle.md b/.agents/skills/per-tenant-database/reference/actors/lifecycle.md new file mode 100644 index 0000000..89955bb --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/actors/lifecycle.md @@ -0,0 +1,388 @@ +# Lifecycle + +> Source: `src/content/docs/actors/lifecycle.mdx` +> Canonical URL: https://rivet.dev/docs/actors/lifecycle +> Description: Learn about actor lifecycle hooks for initialization, state management, and cleanup. + +--- +# Lifecycle + +Actors follow a well-defined lifecycle with hooks at each stage. Understanding these hooks is essential for proper initialization, state management, and cleanup. + +## Lifecycle + +Actors transition through several states during their lifetime. Each transition triggers specific hooks that let you initialize resources, manage connections, and clean up state. + +``` +Loading ──Start──▶ Ready ──spawn driver──▶ Started + │ + ┌────────────────────────────────────────┤ + │ │ + │ idle timer + can_sleep │ Destroy command + ▼ ▼ + SleepGrace ─── grace window closes ──▶ Destroying + │ │ + ▼ │ + SleepFinalize ──── stop sequence ───────────┤ + ▼ + Terminated +``` + +**On Create** (runs once per actor) + +1. `onMigrate` +2. `createState` +3. `onCreate` +4. `createVars` +5. `onWake` +6. `run` (background, does not block) + +**On Destroy** + +1. `onDestroy` + +**On Wake** (after sleep, restart, or crash) + +1. `onMigrate` +2. `createVars` +3. `onWake` +4. `run` (background, does not block) + +**On Sleep** (after idle period) + +1. Wait for `run` to complete (with timeout) +2. `onSleep` + +**On Connect** (per client) + +1. `onBeforeConnect` +2. `createConnState` +3. `onConnect` + +**On Disconnect** (per client) + +1. `onDisconnect` + +**On Inbound Action Invoke** (per action call) + +1. Action handler executes + +**On Inbound Queue Publish** (per `handle.send(...)`) + +1. `queues..canPublish` (if defined) +2. Queue message is enqueued + +**On Event Subscription Request** (per subscribe request) + +1. `events..canSubscribe` (if defined) +2. Subscription is applied + +## Lifecycle Hooks + +Actor lifecycle hooks are defined as functions in the actor configuration. + +### `state` + +The `state` constant defines the initial state of the actor. See [state documentation](/docs/actors/state) for more information. + +### `onMigrate` + +[API Reference](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html) + +The `onMigrate` hook runs on every actor start, before `createState`, `onCreate`, `createVars`, and `onWake`. Can be async. It runs early so that database migrations are applied before any other lifecycle hook accesses the database. The second parameter is `true` when the actor is being created for the first time. + +### `createState` + +[API Reference](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html) + +The `createState` function dynamically initializes state based on input. Called only once when the actor is first created. Can be async. See [state documentation](/docs/actors/state) for more information. + +### `vars` + +The `vars` constant defines ephemeral variables for the actor. These variables are not persisted and are useful for storing runtime-only data. The value for `vars` must be clonable via `structuredClone`. See [ephemeral variables documentation](/docs/actors/state#ephemeral-variables) for more information. + +### `createVars` + +[API Reference](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html) + +The `createVars` function dynamically initializes ephemeral variables. Can be async. Use this when you need to initialize values at runtime. See [ephemeral variables documentation](/docs/actors/state#ephemeral-variables) for more information. + +### `onCreate` + +[API Reference](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html) + +The `onCreate` hook is called when the actor is first created. Can be async. Use this hook for initialization logic that doesn't affect the initial state. + +### `onDestroy` + +[API Reference](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html) + +The `onDestroy` hook is called when the actor is being permanently destroyed. Can be async. Use this for final cleanup operations like closing external connections, releasing resources, or performing any last-minute state persistence. + +The actor is still fully functional when `onDestroy` runs. You can access the database, broadcast events, call `waitUntil`, send queue messages, and use `schedule.after`. State mutations made during `onDestroy` are persisted before the actor is torn down. + +### `onWake` + +[API Reference](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html) + +This hook is called any time the actor is started (e.g. after restarting, upgrading code, or crashing). Can be async. + +This is called after the actor has been initialized but before any connections are accepted. + +Use this hook to set up any resources or start any background tasks, such as `setInterval`. + +### `onSleep` + +[API Reference](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html) + +This hook is called when the actor is going to sleep. Can be async. Use this to clean up resources, close connections, or perform any shutdown operations. + +The actor is still fully functional when `onSleep` runs. You can access the database, broadcast events, call `waitUntil`, send queue messages, and use `schedule.after`. State mutations made during `onSleep` are persisted before the actor finishes sleeping. + +This hook may not always be called in situations like crashes or forced terminations. Don't rely on it for critical cleanup operations. + +Not supported on Cloudflare Workers. + +### `run` + +[API Reference](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html) + +The `run` hook is called after the actor starts and runs in the background without blocking actor startup. This is ideal for long-running background tasks like: + +- Reading from message queues in a loop +- Tick loops for periodic work +- Custom workflow logic +- Background processing + +The handler exposes `c.aborted` for loop checks and `c.abortSignal` for canceling operations when the actor is stopping. You should always check or listen for shutdown to exit gracefully. + +**Important behavior:** +- The actor may go to sleep at any time during the `run` handler. Wrap work that must keep the actor awake with `c.keepAwake(promise)` to block idle sleep until the promise settles. +- If the `run` handler exits (returns), the actor follows its normal idle sleep timeout once it becomes idle +- If the `run` handler throws an error, the actor logs the error and then follows its normal idle sleep timeout once it becomes idle +- On shutdown, `c.abortSignal` fires so the `run` handler can exit within the graceful shutdown window. + +Finite `run` handlers leave the actor alive after they finish. If you want a one shot task to clean itself up when it is done, call `c.destroy()` before returning. + +### `onStateChange` + +[API Reference](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html) + +Called whenever the actor's state changes. Cannot be async. This is often used to broadcast state updates. + +Do not mutate `c.state` inside `onStateChange`; re-entrant state mutation is rejected. + +### `createConnState` and `connState` + +[API Reference](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html) + +There are two ways to define the initial state for connections: +1. `connState`: Define a constant object that will be used as the initial state for all connections +2. `createConnState`: A function that dynamically creates initial connection state based on connection parameters. Can be async. + +### `onBeforeConnect` + +[API Reference](/typedoc/interfaces/rivetkit.mod.BeforeConnectContext.html) + +The `onBeforeConnect` hook is called whenever a new client connects to the actor. Can be async. Clients can pass parameters when connecting, accessible via `params`. This hook is used for connection validation and can throw errors to reject connections. + +The `onBeforeConnect` hook does NOT return connection state - it's used solely for validation. + +Connections cannot interact with the actor until this method completes successfully. Throwing an error will abort the connection. This can be used for authentication - see [Authentication](/docs/actors/authentication) for details. + +### `onConnect` + +[API Reference](/typedoc/interfaces/rivetkit.mod.ConnectContext.html) + +Executed after the client has successfully connected. Can be async. Receives the connection object as a second parameter. + +Messages will not be processed for this actor until this hook succeeds. Errors thrown from this hook will cause the client to disconnect. + +### `canPublish` and `canSubscribe` + +[API Reference](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html) + +Use schema-level hooks to authorize queue publishes and event subscriptions. Both hooks can be async and must return booleans: + +- `queues..canPublish` runs before inbound queue publishes. +- `events..canSubscribe` runs before inbound event subscription requests. + +For actions, enforce authorization directly inside each action handler. + +Use deny-by-default rules for each hook and return `false` unless explicitly allowed. See [Access Control](/docs/actors/access-control) for full guidance. + +### `onDisconnect` + +[API Reference](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html) + +Called when a client disconnects from the actor. Can be async. Receives the connection object as a second parameter. Use this to clean up any connection-specific resources. + +### `onRequest` + +[API Reference](/typedoc/interfaces/rivetkit.mod.RequestContext.html) + +The `onRequest` hook handles HTTP requests sent to your actor at `/actors/{actorName}/http/*` endpoints. Can be async. It receives the request context and a standard `Request` object, and should return a `Response` object. + +See [Request Handler](/docs/actors/request-handler) for more details. + +### `onWebSocket` + +[API Reference](/typedoc/interfaces/rivetkit.mod.WebSocketContext.html) + +The `onWebSocket` hook handles WebSocket connections to your actor. Can be async. It receives the actor context and a `WebSocket` object. Use this to set up WebSocket event listeners and handle real-time communication. + +See [WebSocket Handler](/docs/actors/websocket-handler) for more details. + +### `onBeforeActionResponse` + +[API Reference](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html) + +The `onBeforeActionResponse` hook is called before sending an action response to the client. Can be async. Use this hook to modify or transform the output of an action before it's sent to the client. This is useful for formatting responses, adding metadata, or applying transformations to the output. + +## Sleeping + +Actors automatically sleep after a period of inactivity to free up resources. When a request arrives for a sleeping actor, it wakes up, restores its state, and handles the request. + +### When Actors Sleep + +#### Idle Timeout + +An actor is considered idle and eligible to sleep when **all** of the following are true: + +- No active HTTP requests +- No active connections (unless they are hibernatable WebSockets) +- No active `run` handler (unless it is waiting on a queue) +- No outstanding `c.keepAwake(promise)` promises +- No pending disconnect callbacks +- No async `onWebSocket` event handlers (eg `open`, `message`, `close`) still running + +Once the actor becomes idle, the sleep timer starts. After `sleepTimeout` (default 30 seconds) of continuous inactivity, the actor begins the sleep process. Any activity resets the timer. + +Outbound requests (e.g. `fetch` calls) do not count as activity and will not keep the actor awake. Wrap them with `c.waitUntil()` if they must complete before the actor sleeps. + +#### Upgrades & Eviction + +The platform may force an actor to migrate to a new machine during version upgrades or when a serverless request is about to timeout. The same [shutdown sequence](#shutdown-sequence) runs, then the actor is rescheduled on a new machine and wakes up with its persisted state. + +Use `onSleep`, `waitUntil`, or `keepAwake` to control the length of the grace period before the actor moves to another machine. + +### Manual Lifecycle Controls + +You can also trigger lifecycle transitions from the Rivet Cloud API. These endpoints are useful for operational workflows, debugging, and forcing an actor to move through the same sleep or reschedule path that the platform would normally trigger. + +```bash +curl -X POST \ + "https://cloud-api.rivet.dev/actors/$ACTOR_ID/sleep?namespace=$NAMESPACE" \ + -H "Authorization: Bearer $RIVET_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{}' +``` + +```bash +curl -X POST \ + "https://cloud-api.rivet.dev/actors/$ACTOR_ID/reschedule?namespace=$NAMESPACE" \ + -H "Authorization: Bearer $RIVET_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{}' +``` + +`/sleep` asks the actor to enter the normal sleep shutdown sequence. `/reschedule` asks the platform to allocate the actor again, which is useful after crashes or when you need to force a fresh placement. Both endpoints require the actor ID and namespace. + +### Skip Ready Wait + +The gateway normally holds requests until the actor is ready. The actor is not ready during startup (before `onWake` finishes) or during the sleep grace period (while `onSleep` and `waitUntil` are running). Probes and readiness checks can opt out with `skipReadyWait` to reach the actor's `onRequest` or `onWebSocket` handler in either window. + +See [Skip Ready Wait](/docs/clients/javascript#skip-ready-wait) on the JavaScript client page for usage. + +### Keeping the Actor Awake + +RivetKit gives you two primitives for holding the actor awake across background work. Both take a `Promise` and differ in how they interact with idle sleep and the grace period. + +| Method | Accepts | Blocks idle sleep | Blocks grace finalize | Use case | +| --- | --- | --- | --- | --- | +| `c.keepAwake(promise)` | `Promise` (returns same promise) | Yes | Yes | Critical work that must keep the actor running end to end (for example a turn in a game, an ongoing tool call). | +| `c.waitUntil(promise)` | `Promise` (returns void) | No | Yes | Best-effort finalization work that may complete during the grace window (for example analytics flushes, cleanup writes). | + +`c.keepAwake(promise)` is the preferred primitive for long-running work the actor should not sleep through. It holds a keep-awake counter until the promise settles, which blocks both idle sleep and the grace finalize step. The promise is returned unchanged, so you can `await` it if you need the value. + +`setPreventSleep(enabled)` is deprecated and now a no-op. Wrap the work you want to keep alive with `c.keepAwake(promise)` instead. + +### On Sleep Hook + +The [`onSleep`](#onsleep) hook runs during shutdown for cleanup like clearing intervals or closing connections. It is best-effort and will not run if the actor crashes. + +### Wait Before Sleep + +`c.waitUntil(promise)` registers a background promise that must resolve before the actor finishes sleeping. Use this to flush data or finish in-flight work during shutdown without blocking the main execution flow. + +The actor waits up to `sleepGracePeriod` for graceful sleep work during the [shutdown sequence](#shutdown-sequence). That single budget covers `onSleep`, `waitUntil`, `keepAwake`, async raw WebSocket handlers such as `message` and `close`. By default, this graceful sleep window is 15 seconds total. If the timeout is exceeded, the actor proceeds with sleep anyway. + +### Sleep Timeouts + +| Option | Default | Description | +|--------|---------|-------------| +| `sleepTimeout` | 30 seconds | Time of inactivity before the actor begins sleeping. | +| `sleepGracePeriod` | 15 seconds | Total graceful shutdown window for hooks, `waitUntil`, `keepAwake`, async raw WebSocket handlers, and disconnects. | + +Rivet enforces a hard limit of **30 minutes** for the entire stop process. These can be configured in the [options](#options). + +### WebSocket Hibernation + +WebSocket connections are preserved across sleep cycles by default and transparently migrated to the new actor instance. Client stays connected and sees no interruption. Actor migration is very fast, realtime workloads are not interrupted. + +### Shutdown Sequence + +When an actor sleeps or is destroyed, it enters the graceful shutdown window: + +1. `c.abortSignal` fires and `c.aborted` becomes `true`. New connections and dispatch are rejected. Alarm timeouts are cancelled. On sleep, scheduled events are persisted and will be re-armed when the actor wakes. +2. `onSleep` or `onDestroy` and `onDisconnect` for each closing connection run during the same window. User `waitUntil` promises and async raw WebSocket handlers are drained. Hibernatable WebSocket connections are preserved on sleep and closed on destroy. +3. Once graceful work has completed, state is saved and final cleanup runs. + +The entire window is bounded by `sleepGracePeriod` on both sleep and destroy. Defaults to 15 seconds. If the window is exceeded, the actor proceeds to state save anyway. + +#### Graceful shutdown window + +During steps 1 through 6, the actor is still fully functional. Database access, `broadcast`, `waitUntil`, `queue.send`, and `schedule.after` all work. State mutations are persisted at step 7. Actions invoked by pre-existing connections or lifecycle hooks continue to execute normally. + +New connections and raw WebSocket upgrades are rejected as soon as the shutdown sequence begins. New requests that arrive during shutdown are held until the actor wakes up again. The caller does not need to retry. + +If `schedule.after` is called during shutdown, the event is persisted so it survives the sleep/wake cycle, but no local timeout is scheduled. The event will fire after the actor wakes. + +In-flight actions are **not** waited on during shutdown. If an action must complete before the actor stops, wrap the critical work with `c.waitUntil()`. + +## Options + +The `options` object allows you to configure various timeouts and behaviors for your actor. + +| Option | Default | Description | +|--------|---------|-------------| +| `createVarsTimeout` | 5000ms | Timeout for `createVars` function | +| `createConnStateTimeout` | 5000ms | Timeout for `createConnState` function | +| `onConnectTimeout` | 5000ms | Timeout for `onConnect` hook | +| `sleepGracePeriod` | 15000ms | Total graceful shutdown window for both sleep and destroy | +| `stateSaveInterval` | 1000ms | Interval for persisting state | +| `actionTimeout` | 60000ms | Timeout for action execution | +| `connectionLivenessTimeout` | 2500ms | Timeout for connection liveness check | +| `connectionLivenessInterval` | 5000ms | Interval for connection liveness check | +| `sleepTimeout` | 30000ms | Time before actor sleeps due to inactivity | +| `canHibernateWebSocket` | false | Whether WebSockets can hibernate (experimental) | + +## Advanced + +### Actor Shutdown Abort Signal + +The `c.abortSignal` provides an `AbortSignal` that fires when the actor is stopping, and `c.aborted` is the shorthand boolean for loop checks. Use these to cancel ongoing operations when the actor sleeps or is destroyed. + +The abort signal fires at the very start of the [shutdown sequence](#shutdown-sequence), before `onSleep` or `onDestroy` runs. This means `c.aborted` is already `true` inside those lifecycle hooks. The signal fires early so that the `run` handler can exit promptly, but the actor remains fully functional throughout the graceful shutdown window. + +See [Canceling Long-Running Actions](/docs/actors/actions#canceling-long-running-actions) for manually canceling operations on-demand. + +### Using `ActorContext` Type Externally + +When extracting logic from lifecycle hooks or actions into external functions, you'll often need to define the type of the context parameter. Rivet provides helper types that make it easy to extract and pass these context types to external functions. + +See [Types](/docs/actors/types) for more details on using `ActorContextOf`. + +## Full Example + +_Source doc path: /docs/actors/lifecycle_ diff --git a/.agents/skills/per-tenant-database/reference/actors/limits.md b/.agents/skills/per-tenant-database/reference/actors/limits.md new file mode 100644 index 0000000..d69c889 --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/actors/limits.md @@ -0,0 +1,169 @@ +# Limits + +> Source: `src/content/docs/actors/limits.mdx` +> Canonical URL: https://rivet.dev/docs/actors/limits +> Description: Limits and constraints for Rivet Actors. + +--- +This page documents the limits for Rivet Actors. + +There are two types of limits: + +- **Soft Limit**: Application-level limit, configurable in RivetKit. These cannot exceed the hard limit. +- **Hard Limit**: Infrastructure-level limit that cannot be configured. + +Soft limits are configured in two places. Registry-level WebSocket message-size limits are passed to `setup`: + +Per-actor limits such as queue sizes and lifecycle timeouts are passed to `actor(...)` via `options`: + +## Limits + +### WebSocket + +These limits affect actions that use `.connect()` and [low-level WebSockets](/docs/actors/websocket-handler). + +| Name | Soft Limit | Hard Limit | Description | +|------|------------|------------|-------------| +| Max incoming message size | 64 KiB | 32 MiB | Maximum size of incoming WebSocket messages. Soft limit configurable via `maxIncomingMessageSize`; hard limit configurable in self-hosted engine config via `guard.websocket_max_message_size`. | +| Max outgoing message size | 1 MiB | 32 MiB | Maximum size of outgoing WebSocket messages. Soft limit configurable via `maxOutgoingMessageSize`; hard limit configurable in self-hosted engine config via `guard.websocket_max_message_size`. | +| Max WebSocket frame size | — | 32 MiB | Maximum size of a single WebSocket frame accepted by Rivet's transport edge. Configurable in self-hosted engine config via `guard.websocket_max_frame_size`. This is not configurable by browser clients and is not negotiated during the WebSocket handshake. | +| WebSocket open timeout | — | 15 seconds | Time allowed for WebSocket connection to be established, including `onBeforeConnect` and `createConnState` hooks. Connection is closed if exceeded. | +| Message ack timeout | — | 30 seconds | Time allowed for message acknowledgment before connection is closed. Only relevant in the case of a network issue and does not affect your application. | + +### Hibernating WebSocket + +Hibernating WebSockets allow actors to sleep while keeping client connections alive. All WebSocket limits above also apply to hibernating WebSockets. See [WebSocket Hibernation](/docs/actors/websocket-handler#web-socket-hibernation) for details. + +| Name | Soft Limit | Hard Limit | Description | +|------|------------|------------|-------------| +| Max pending buffer size | — | 128 MiB | Total size of all buffered messages per connection while actor is sleeping. | +| Max pending message count | — | 65,535 | Maximum number of buffered messages per connection while actor is sleeping. | +| Hibernation timeout | — | 90 seconds | Maximum time an actor has to wake up before the client is disconnected. Only relevant if something is wrong with starting actors. | + +### HTTP + +These limits affect actions that do not use `.connect()` and [low-level HTTP requests](/docs/actors/request-handler). + +| Name | Soft Limit | Hard Limit | Description | +|------|------------|------------|-------------| +| Max request body size | — | 20 MiB | Maximum size of HTTP request bodies. | +| Max response body size | — | 20 MiB | Maximum size of HTTP response bodies. | +| Request timeout | 60 seconds | — | Maximum time for an `onRequest` handler to complete. Defaults to `actionTimeout`; configure with `actionTimeout`. | + +### Networking + +| Name | Soft Limit | Hard Limit | Description | +|------|------------|------------|-------------| +| Connection ping timeout | — | 30 seconds | Connection is closed if a ping is not acknowledged within this time. Applies to both HTTP and WebSocket. Only relevant in the case of a network issue and does not affect your application. | + +### Queue + +| Name | Soft Limit | Hard Limit | Description | +|------|------------|------------|-------------| +| Max queue size | 1,000 messages | — | Maximum number of messages in the queue before new messages are rejected. Configurable via `maxQueueSize`. | +| Max queue message size | 64 KiB | 128 KiB (effective) | Maximum size of each individual queue message. Configurable via `maxQueueMessageSize`. Actual payload is slightly lower after queue serialization overhead. | + +### Actor KV Storage + +These limits apply to the low-level KV storage interface powering Rivet Actors. They likely do not affect your application, but are documented for completeness. + +| Name | Soft Limit | Hard Limit | Description | +|------|------------|------------|-------------| +| Max key size | — | 2 KiB | Maximum size of a single key. | +| Max value size | — | 128 KiB | Maximum size of a single value. | +| Max keys per operation | — | 128 | Maximum number of keys in a single batch get/put/delete operation. Does not apply to range operations (`listRange`, `deleteRange`). | +| Max batch put payload size | — | 976 KiB | Maximum total size of all key-value pairs in a single batch put operation. | +| Max storage size per actor | — | 10 GiB | Maximum total KV storage size for a single actor. | +| List default limit | — | 16,384 | Default maximum number of keys returned by a list operation. | + +### Actor SQLite Storage + +These limits apply to the [SQLite database](/docs/actors/state#sqlite-database) available via `this.sql`. SQLite data is stored through the KV layer, so the storage limit is shared with KV storage. + +| Name | Soft Limit | Hard Limit | Description | +|------|------------|------------|-------------| +| Max storage size per actor | — | 10 GiB | Maximum total storage size for a single actor. This limit is shared with KV storage. | +| Max dirty data per commit | — | 1,310,720 bytes | Maximum raw SQLite page data that can be written in a single commit. This is 320 dirty pages at 4 KiB per page. | +| Transaction deadline | 60 seconds | — | Safety backstop for `db.transaction()`. Configure per transaction with `{ timeout: milliseconds }`; there is no product-level maximum. | +| Transaction coordinator queue | — | 128 operations | Maximum admitted SQLite operations waiting behind an active coordinated transaction. Additional operations fail with `sqlite.transaction_queue_full`. | + +SQLite commit deltas are compressed and split into internal chunks before storage, but all chunks for one commit are published atomically. The commit limit is therefore based on raw dirty page bytes before compression, not the compressed delta size. + +Avoid `VACUUM` inside actor databases. It rewrites the database in one transaction and can exceed the dirty-page commit limit even when ordinary incremental writes fit. + +### Actor Runtime Socket + +The Beta [Actor Runtime Socket](/docs/actors/actor-runtime-socket) negotiates its active frame limit during the handshake. + +| Name | Soft Limit | Hard Limit | Description | +|------|------------|------------|-------------| +| Frame payload size | 32 MiB | — | Default maximum request or response payload. Self-hosted native runtimes can configure `RIVET_ACTOR_RUNTIME_SOCKET_MAX_FRAME_BYTES`; oversized responses return `ResponseTooLarge`. | + +### KV Preloading + +When an actor starts, the engine can pre-fetch KV data declared in the actor name metadata and deliver it alongside the start command. This removes round-trips to storage during actor startup. RivetKit emits the preload manifest from its own key layout and exposes per-actor overrides via `options`. Operators can still enforce a global cap in the [engine config](/docs/self-hosting/configuration) with `pegboard.preload_max_total_bytes`. In serverless mode, this data is serialized into the `/api/rivet/start` request body along with actor config and protocol metadata, so the accepted body size must be larger than the preload budget. RivetKit defaults `serverless.maxStartPayloadBytes` to 16 MiB to leave margin for the default 1 MiB preload budget and larger SQLite startup page preloads. + +| Name | Soft Limit | Hard Limit | Description | +|------|------------|------------|-------------| +| Max total preload size | 1 MiB | — | Maximum total size of all preloaded KV data sent with the start command. Configurable via `pegboard.preload_max_total_bytes`. Setting to 0 disables all preloading. | +| Max workflow preload size | 128 KiB | — | Default maximum size of preloaded workflow data for RivetKit actors. Configurable per actor via `options.preloadMaxWorkflowBytes`. Setting to 0 disables workflow preloading for that actor. | +| Max connections preload size | 64 KiB | — | Default maximum size of preloaded connection data for RivetKit actors. Configurable per actor via `options.preloadMaxConnectionsBytes`. Setting to 0 disables connections preloading for that actor. | + +### Actor Input + +See [Actor Input](/docs/actors/input) for details. + +| Name | Soft Limit | Hard Limit | Description | +|------|------------|------------|-------------| +| Max actor input size | — | 4 MiB | Maximum size of the input passed when creating an actor. | +| Max connection params size | — | 4 KiB | Maximum size of connection parameters passed when connecting to an actor. | +| Max actor key component size | — | 128 bytes | Maximum size of each component in an actor key array. | +| Max actor key total size | — | 1,024 bytes | Maximum total size of the serialized actor key string. | +| Max actor name length | — | 64 characters | Maximum length for actor and project identifiers. | + +### Rate Limiting + +| Name | Soft Limit | Hard Limit | Description | +|------|------------|------------|-------------| +| Rate limit | — | 1200 requests/minute | Default rate limit per actor per IP address with a 1 minute time bucket. | +| Max in-flight requests | — | 32 | Default maximum concurrent requests to an actor per IP address. | + +### Timeouts + +| Name | Soft Limit | Hard Limit | Description | +|------|------------|------------|-------------| +| Action timeout | 60 seconds | — | Timeout for RPC actions. Configurable via `actionTimeout`. | +| On request timeout | 60 seconds | — | Timeout for raw `onRequest` handlers. Defaults to `actionTimeout`; configure with `actionTimeout`. | +| On before connect timeout | 5 seconds | — | Timeout for the `onBeforeConnect` hook. Configurable via `onBeforeConnectTimeout`. | +| Create vars timeout | 5 seconds | — | Timeout for `createVars` hook. Configurable via `createVarsTimeout`. | +| Create conn state timeout | 5 seconds | — | Timeout for `createConnState` hook. Configurable via `createConnStateTimeout`. | +| On connect timeout | 5 seconds | — | Timeout for `onConnect` hook. Configurable via `onConnectTimeout`. | +| On migrate timeout | 30 seconds | — | Timeout for `onMigrate` hook. Configurable via `onMigrateTimeout`. | +| Sleep grace period | 15 seconds | — | Total graceful shutdown budget for both sleep and destroy. Covers `onSleep`/`onDestroy`, run handler shutdown, `waitUntil`, `keepAwake`, async raw WebSocket handlers, and connection cleanup. Configurable via `sleepGracePeriod`. | +| Sleep timeout | 30 seconds | — | Time of inactivity before actor hibernates. Configurable via `sleepTimeout`. | +| State save interval | 1 second | — | Interval between automatic state saves. Configurable via `stateSaveInterval`. | +| Connection liveness timeout | 2.5 seconds | — | Time a stateful connection can miss liveness checks before RivetKit treats it as unhealthy. Configurable via `connectionLivenessTimeout`. | +| Connection liveness interval | 5 seconds | — | Interval between stateful connection liveness checks. Configurable via `connectionLivenessInterval`. | + +### Serverless Shutdown + +These timeouts control how actors are shut down when a serverless request reaches its lifespan limit. See [Shutdown Sequence](/docs/general/runtime-modes#shutdown-sequence) for details. + +| Name | Soft Limit | Hard Limit | Description | +|------|------------|------------|-------------| +| Request lifespan | 3600 seconds (60 min) | — | Total lifespan of a serverless request before drain begins. Configurable via `requestLifespan` in [`configurePool`](/docs/general/registry-configuration). | +| Runner config drain grace period | — | 30 minutes | Time a serverless runner reserves for actors to stop gracefully. Configurable via `drainGracePeriod` in [`configurePool`](/docs/general/registry-configuration). | +| Engine serverless drain fallback | — | 10 seconds | Engine-level fallback used when no per-runner config applies. Configurable via [engine config](/docs/self-hosting/configuration) (`pegboard.serverless_drain_grace_period`). | + +### Actor Lifecycle + +| Name | Soft Limit | Hard Limit | Description | +|------|------------|------------|-------------| +| Actor start threshold | — | 30 seconds | Maximum time for an actor to start before it is considered lost and rescheduled. | +| Actor stop threshold | — | 30 minutes | Maximum time for an actor to stop before it is considered lost. | + +## Increasing Limits + +These limits are sane defaults designed to protect your application from exploits and accidental runaway bugs. If you have a use case that requires different limits, [contact us](https://rivet.dev/contact) to discuss your requirements. + +_Source doc path: /docs/actors/limits_ diff --git a/.agents/skills/per-tenant-database/reference/actors/metadata.md b/.agents/skills/per-tenant-database/reference/actors/metadata.md new file mode 100644 index 0000000..d3dcc24 --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/actors/metadata.md @@ -0,0 +1,39 @@ +# Metadata + +> Source: `src/content/docs/actors/metadata.mdx` +> Canonical URL: https://rivet.dev/docs/actors/metadata +> Description: Metadata provides information about the currently running actor. + +--- +## Actor ID + +Get the unique instance ID of the actor: + +## Actor Name + +Get the actor type name: + +This is useful when you need to know which actor type is running, especially if you have generic utility functions that are shared between different actor implementations. + +## Actor Key + +Get the actor key used to identify this actor instance: + +The key is used to route requests to the correct actor instance and can include parameters passed when creating the actor. + +Learn more about using keys for actor addressing and configuration in the [keys documentation](/docs/actors/keys). + +## Region + +Region can be accessed from the context object via `c.region`. + +`c.region` is only supported on Rivet at the moment. + +## Example Usage + +## API Reference + +- [`ActorDefinition`](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html) - Interface for defining metadata +- [`CreateOptions`](/typedoc/interfaces/rivetkit.client_mod.CreateOptions.html) - Options for creating an actor, including `region` and `input` + +_Source doc path: /docs/actors/metadata_ diff --git a/.agents/skills/per-tenant-database/reference/actors/queues.md b/.agents/skills/per-tenant-database/reference/actors/queues.md new file mode 100644 index 0000000..cf75e0b --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/actors/queues.md @@ -0,0 +1,120 @@ +# Queues & Run Loops + +> Source: `src/content/docs/actors/queues.mdx` +> Canonical URL: https://rivet.dev/docs/actors/queues +> Description: Use actor-local durable queues for serial run loops and request/response workflows. + +--- +## What are queues? + +- **Realtime**: messages are delivered to a live actor as soon as possible. +- **Durable**: messages are persisted and survive actor sleep/restart. +- **Request/response**: clients can wait for a queue completion response. +- **Scalable**: queues absorb large bursts and handle heavy backpressure safely. +- **Local per actor**: each actor instance has its own queue storage (scoped by actor key/id). + +Queues are commonly referred to as "mailboxes" in other actor frameworks. + +## What are queues good for? + +- Great for any task that changes actor state. +- Helps avoid race conditions by handling work in order. +- Makes complex behavior easier to organize. + +## Basic queue + +This is the default pattern. Define queue names in `queues`, process them in `run`, and publish from the client with `handle.send(...)`. + +## Completable messages + +Use this when you want explicit completion/ack semantics but do not need to return data. + +- `message.complete()` resolves a sender waiting on `wait: true` (or `enqueueAndWait`). It does not change durability: messages are removed from queue storage when they are received, not when they are completed. +- If processing fails before `message.complete()`, the message is not redelivered, and any waiting sender times out instead of receiving a completion. +- `status: "timedOut"` means sender timeout elapsed before `message.complete(...)`. + +## Request/reply pattern + +Use this when the sender needs data back from queued work. + +## Queue messages from within an actor + +Queueing is useful from inside actor logic too, not just from clients. + +- Use actions as entrypoints, then enqueue into the run loop to keep mutations serialized. +- You can also call `c.queue.send(...)` from other parts of `run` when needed. +- `c.queue.send(...)` confirms durable enqueue. It does not wait for processing to finish. + +## Defining queue schemas + +You can define queue types with `queue()` or with schema objects. Schema objects support [Standard Schema](https://standardschema.dev/) validators, including [Zod](https://zod.dev/). + +## Pull messages with `next` and `nextBatch` + +Use `next` when you want to wait for one queue message. +Use `nextBatch` when you want to wait for multiple queue messages. + +- Waits until messages are available unless timeout is hit. +- Omit `timeout` to wait indefinitely. + +## Poll messages + +Use `tryNext` when you need one non-blocking read. +Use `tryNextBatch` for non-blocking batch reads. + +- Returns immediately and never waits. + +## Abort signals + +Use `signal` when your receive loop needs external cancellation semantics in addition to actor shutdown behavior. + +## Multiple queues + +Multiple queues let you separate message flows by purpose. By default, receive calls race across all queues when `names` is not specified. In this pattern, prompt messages run through a streaming loop while stop messages act as control signals on a separate receive path. + +Use `iter({ names: ["prompt"] })` as the main stream and `next({ names: ["stop"] })` as a stop signal. + +## Sleeping behavior + +If an actor has a `run` handler, it does not sleep while that handler is actively doing work. It only can sleep when the run loop is blocked waiting for queue entries (for example inside `iter(...)` or `next(...)`). + +This means you can run normal code in `run` without worrying about sleep interrupting it mid-call. + +## Debugging + +- `GET /inspector/queue?limit=50` returns queue size and pending message metadata. +- `GET /inspector/summary` includes `queueSize` for quick queue health checks. +- `POST /queue/:name` with `wait: true` is useful to verify completable/request-response behavior. +- In non-dev mode, inspector endpoints require authorization. + +## Recommendations + +- Actions are for getting data, queue entries are for mutating data. +- Implement connection auth in `onBeforeConnect`. See [Authentication](/docs/actors/authentication). +- Route most state changes through one queue loop so ordering stays predictable. +- If you need more complex multi-step run loops, consider using workflows. +- Use `c.aborted` and `c.abortSignal` for actor shutdown. Use your own `AbortController` for earlier loop cancellation. +- Add `timeout` when callers need bounded wait behavior. +- Use `wait: true` only when the caller actually needs a response. + +## Pitfalls + +### Avoid `wait: true` between actors + +`wait: true` blocks the sender's run loop until the receiver finishes. Between actors, this adds unnecessary overhead and risks deadlocks, especially if the target actor needs to communicate back. If an actor sends a `wait: true` message to *itself*, it is a guaranteed deadlock because the run loop is already busy processing the current message. + +Reserve `wait: true` for external callers (HTTP handlers, CLI tools, client apps). For actor-to-actor communication, send a queue message to the other actor without `wait: true`, then have that actor send a queue message back when the work is done. + +## Tips + +### Message TTL + +Every queue message includes a `createdAt` timestamp. Use this to skip or discard stale messages in your run loop: + +### Delayed delivery + +Use [`c.schedule`](/docs/actors/schedule) to enqueue messages at a future time instead of processing them immediately: + +See [Schedule](/docs/actors/schedule) for the full scheduling API. + +_Source doc path: /docs/actors/queues_ diff --git a/.agents/skills/per-tenant-database/reference/actors/quickstart/backend.md b/.agents/skills/per-tenant-database/reference/actors/quickstart/backend.md new file mode 100644 index 0000000..86cbdf2 --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/actors/quickstart/backend.md @@ -0,0 +1,64 @@ +# Node.js & Bun Quickstart + +> Source: `src/content/docs/actors/quickstart/backend.mdx` +> Canonical URL: https://rivet.dev/docs/actors/quickstart/backend +> Description: Get started with Rivet Actors in Node.js and Bun + +--- +Prefer to start from a complete project? See the runnable [`hello-world`](https://github.com/rivet-dev/rivet/tree/main/examples/hello-world) example. + +## Steps + +### Add Rivet Skill to Coding Agent (Optional) + +If you're using an AI coding assistant (like Claude Code, Cursor, Windsurf, etc.), add Rivet skills for enhanced development assistance: + +```sh +npx skills add rivet-dev/skills +``` + +### Install Rivet + +```sh +npm install rivetkit +``` + +### Create Actors and Start Server + +Create a file with your actors, set up the registry, and start the server: + +### Run Server + +```sh Node.js +npx tsx --watch index.ts +``` + +```sh Bun +bun --watch index.ts +``` + +```sh Deno +deno run --allow-net --allow-read --allow-env --watch index.ts +``` + +Your server is now running on `http://localhost:6420`. Clients connect directly to the Rivet Engine on this port. + +Visit [http://localhost:6420](http://localhost:6420) in your browser (or point your AI agent at it) to open the Rivet developer tools and inspect your actors live. + +### Connect To The Rivet Actor + +This code can run either in your frontend or within your backend: + +### TypeScript + +See the [JavaScript client documentation](/docs/clients/javascript) for more information. + +### React + +See the [React documentation](/docs/clients/react) for more information. + +### Deploy + +## Configuration Options + +_Source doc path: /docs/actors/quickstart/backend_ diff --git a/.agents/skills/per-tenant-database/reference/actors/quickstart/cloudflare.md b/.agents/skills/per-tenant-database/reference/actors/quickstart/cloudflare.md new file mode 100644 index 0000000..e39cbe4 --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/actors/quickstart/cloudflare.md @@ -0,0 +1,165 @@ +# Cloudflare Workers Quickstart + +> Source: `src/content/docs/actors/quickstart/cloudflare.mdx` +> Canonical URL: https://rivet.dev/docs/actors/quickstart/cloudflare +> Description: Set up a Rivet project locally targeting Cloudflare Workers. + +--- +Set up a Rivet project locally that runs on Cloudflare Workers. The `@rivetkit/cloudflare-workers` package wires the WebAssembly runtime for you. + +Prefer to start from a complete project? See the runnable [`hello-world-cloudflare-workers`](https://github.com/rivet-dev/rivet/tree/main/examples/hello-world-cloudflare-workers) example (with [Hono](https://github.com/rivet-dev/rivet/tree/main/examples/hello-world-cloudflare-workers-hono) and [raw router](https://github.com/rivet-dev/rivet/tree/main/examples/hello-world-cloudflare-workers-raw) variants). + +## Steps + +### Install Packages + +```sh +npm install rivetkit @rivetkit/cloudflare-workers +npm install --save-dev wrangler +``` + +### Configure Wrangler + +```toml wrangler.toml +name = "rivetkit-cloudflare" +main = "src/index.ts" +compatibility_date = "2025-04-01" +compatibility_flags = ["nodejs_compat"] +``` + +The `nodejs_compat` flag is required so the runtime can read its connection config from `process.env`. + +### Create the Worker + +`createHandler` serves the Rivet manager API on `/api/rivet`. Pick how you want to handle your own routes: + +- **Default**: Rivet handles everything. +- **Hono**: Mount a [Hono](https://hono.dev) app for your routes (`npm install hono`). +- **Raw**: Provide a `fetch` and route requests yourself. + +```ts Default @nocheck +import { actor } from "rivetkit"; +import { createHandler } from "@rivetkit/cloudflare-workers"; + +const counter = actor({ + state: { count: 0 }, + actions: { + increment: (c, amount = 1) => { + c.state.count += amount; + return c.state.count; + }, + }, +}); + +export default createHandler({ use: { counter } }); +``` + +```ts Hono @nocheck +import { actor } from "rivetkit"; +import { createClient } from "rivetkit/client"; +import { createHandler, setup } from "@rivetkit/cloudflare-workers"; +import { Hono } from "hono"; + +const counter = actor({ + state: { count: 0 }, + actions: { + increment: (c, amount = 1) => { + c.state.count += amount; + return c.state.count; + }, + }, +}); + +// `setup` returns a typed registry, so the client below is fully typed. +const registry = setup({ use: { counter } }); + +const app = new Hono(); + +app.get("/", (c) => c.text("Hello from Hono + Rivet Actors!")); + +app.post("/increment/:name", async (c) => { + // `createClient` reads RIVET_ENDPOINT from the environment (passed by `rivet + // dev`, or set as a Worker secret in production). + const client = createClient(); + const count = await client.counter.getOrCreate(c.req.param("name")).increment(1); + return c.json({ count }); +}); + +// Rivet keeps `/api/rivet`; Hono handles everything else. +export default createHandler(registry, { fetch: app.fetch }); +``` + +```ts Raw @nocheck +import { actor } from "rivetkit"; +import { createClient } from "rivetkit/client"; +import { createHandler, setup } from "@rivetkit/cloudflare-workers"; + +const counter = actor({ + state: { count: 0 }, + actions: { + increment: (c, amount = 1) => { + c.state.count += amount; + return c.state.count; + }, + }, +}); + +// `setup` returns a typed registry, so the client below is fully typed. +const registry = setup({ use: { counter } }); + +// Rivet keeps `/api/rivet`; your `fetch` handles everything else. +export default createHandler(registry, { + fetch: async (request: Request) => { + const url = new URL(request.url); + + if (url.pathname === "/") { + return new Response("Hello from a raw Rivet Worker router!"); + } + + const increment = url.pathname.match(/^\/increment\/(.+)$/); + if (request.method === "POST" && increment) { + // `createClient` reads RIVET_ENDPOINT from the environment (passed by + // `rivet dev`, or set as a Worker secret in production). + const client = createClient(); + const count = await client.counter.getOrCreate(increment[1]).increment(1); + return Response.json({ count }); + } + + return new Response("Not found", { status: 404 }); + }, +}); +``` + +### Run Locally + +Start Rivet. The CLI runs the local engine and spawns `wrangler dev` for you: + +```sh +npx @rivetkit/cli dev --provider cloudflare +``` + +Visit [http://localhost:6420](http://localhost:6420) in your browser (or point your AI agent at it) to open the Rivet developer tools and inspect your actors live. + +### Connect To The Rivet Actor + +This code can run either in your frontend or within your backend. It connects directly to the local engine on `http://localhost:6420`: + +### TypeScript + +See the [JavaScript client documentation](/docs/clients/javascript) for more information. + +### React + +See the [React documentation](/docs/clients/react) for more information. + +### Deploy + +Ready to ship? See [Deploying to Cloudflare Workers](/docs/deploy/cloudflare). + +## Related + +- [Quickstart](/docs/actors/quickstart) +- [Deploying to Cloudflare Workers](/docs/deploy/cloudflare) +- [SQLite](/docs/actors/sqlite) + +_Source doc path: /docs/actors/quickstart/cloudflare_ diff --git a/.agents/skills/per-tenant-database/reference/actors/quickstart/effect.md b/.agents/skills/per-tenant-database/reference/actors/quickstart/effect.md new file mode 100644 index 0000000..fc252e9 --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/actors/quickstart/effect.md @@ -0,0 +1,239 @@ +# Effect.ts Quickstart (Beta) + +> Source: `src/content/docs/actors/quickstart/effect.mdx` +> Canonical URL: https://rivet.dev/docs/actors/quickstart/effect +> Description: Build a Rivet Actor with the Effect SDK + +--- +Effect support is in beta. The `@rivetkit/effect` API may change between releases. See the [`hello-world-effect`](https://github.com/rivet-dev/rivet/tree/main/examples/hello-world-effect) and [`chat-room-effect`](https://github.com/rivet-dev/rivet/tree/main/examples/chat-room-effect) examples for complete runnable projects. + +## Steps + +### Install Rivet + +Add `rivetkit`, the Effect SDK, and its Effect peers: + +```sh +npm install rivetkit @rivetkit/effect effect @effect/platform-node +``` + +### Define Your Actor + +Split each actor into a public contract and a server-only implementation so the contract can be imported from client code without leaking server details. + +The contract declares the actor and its actions. Actions are standalone values with explicit [`effect/Schema`](https://effect.website/docs/schema/introduction/) payloads and successes, validated end to end: + +```ts src/actors/counter/api.ts @nocheck +import { Action, Actor } from "@rivetkit/effect"; +import { Schema } from "effect"; + +export const Increment = Action.make("Increment", { + payload: { amount: Schema.Number }, + success: Schema.Number, +}); + +export const GetCount = Action.make("GetCount", { + success: Schema.Number, +}); + +export const Counter = Actor.make("Counter", { + actions: [Increment, GetCount], +}); +``` + +The implementation registers the actor with `.toLayer`. The wake function runs once when the actor awakes and returns the action handlers. Persisted state is accessed through a `SubscriptionRef`-like `State` API: + +```ts src/actors/counter/live.ts @nocheck +import { Actor, State } from "@rivetkit/effect"; +import { Effect, Schema } from "effect"; +import { Counter } from "./api.ts"; + +export const CounterLive = Counter.toLayer( + Effect.fnUntraced(function* ({ rawRivetkitContext, state }) { + return Counter.of({ + Increment: Effect.fnUntraced(function* ({ payload }) { + const next = yield* State.updateAndGet(state, (current) => ({ + count: current.count + payload.amount, + })).pipe(Effect.orDie); + + // Broadcast the new value to every connected client. + rawRivetkitContext.broadcast("newCount", next.count); + + return next.count; + }), + GetCount: () => + State.get(state).pipe( + Effect.map((current) => current.count), + Effect.orDie, + ), + }); + }), + { + state: { + schema: Schema.Struct({ count: Schema.Number }), + initialValue: () => ({ count: 0 }), + }, + name: "Counter", + icon: "calculator", + }, +); +``` + +### Serve The Registry + +Compose the actor layers and serve them with `Registry.serve`. `Registry.layer()` reads engine config from the environment, and the actor layer is provided a `Client` so actors can call other actors: + +```ts src/main.ts @nocheck +import { NodeRuntime } from "@effect/platform-node"; +import { Client, Registry } from "@rivetkit/effect"; +import { Layer } from "effect"; +import { CounterLive } from "./actors/counter/live.ts"; + +const endpoint = process.env.RIVET_ENDPOINT ?? "http://127.0.0.1:6420"; + +const ActorsLayer = CounterLive.pipe(Layer.provide(Client.layer({ endpoint }))); + +const MainLayer = Registry.serve(ActorsLayer).pipe(Layer.provide(Registry.layer())); + +// Keeps the layer alive. Tears down on SIGINT/SIGTERM. +Layer.launch(MainLayer).pipe(NodeRuntime.runMain); +``` + +### Run The Server + +Set `RIVET_RUN_ENGINE=1` to spawn a local Rivet Engine alongside the server. The engine binary is downloaded and cached the first time you run, so there is nothing else to install: + +```sh +RIVET_RUN_ENGINE=1 npx tsx --watch src/main.ts +``` + +Your server now connects to the Rivet Engine on `http://localhost:6420`. Clients connect directly to the engine on this port. + +Visit [http://localhost:6420](http://localhost:6420) in your browser (or point your AI agent at it) to open the Rivet developer tools and inspect your actors live. + +To point at a remote engine instead, set `RIVET_ENDPOINT=https://...` and omit `RIVET_RUN_ENGINE`. + +### Connect To The Rivet Actor + +This code can run either in your frontend or within your backend: + +### Effect + +The Effect client imports the same actor contract from your registry. `Counter.client` yields a typed accessor backed by the client layer: + +```ts src/client.ts @nocheck +import { NodeRuntime } from "@effect/platform-node"; +import { Client } from "@rivetkit/effect"; +import { Effect } from "effect"; +import { Counter } from "./actors/counter/api.ts"; + +const program = Effect.gen(function* () { + const counter = (yield* Counter.client).getOrCreate("my-counter"); + + const count = yield* counter.Increment({ amount: 3 }); + yield* Effect.log(`New count: ${count}`); + + const total = yield* counter.GetCount(); + yield* Effect.log(`Total: ${total}`); +}); + +const ClientLayer = Client.layer({ endpoint: "http://localhost:6420" }); + +program.pipe(Effect.provide(ClientLayer), NodeRuntime.runMain); +``` + +With the server still running, start the client in another terminal: + +```sh +npx tsx src/client.ts +``` + +See the [`chat-room-effect`](https://github.com/rivet-dev/rivet/tree/main/examples/chat-room-effect) example for a larger project with typed errors and actor-to-actor calls. + +### TypeScript + +A plain RivetKit client can call your Effect actor by name through the same engine. Actor and action names are resolved at runtime, so the client is untyped here: + +```ts client.ts @nocheck +import { createClient } from "rivetkit/client"; + +const client = createClient("http://localhost:6420"); + +const counter = client.Counter.getOrCreate(["my-counter"]); + +const count = await counter.Increment({ amount: 3 }); +console.log("New count:", count); +``` + +See the [JavaScript client documentation](/docs/clients/javascript) for more information. + +### Deploy + +## Feature Support + +The Effect SDK wraps the most common actor features with typed, schema-validated APIs. Everything else is still fully usable through the raw RivetKit context (see [Raw Escape Hatch](#raw-escape-hatch) below), so no feature is off limits, it just isn't typed yet. + +| Feature | Effect-native API | Access | +| --- | --- | --- | +| Actor contract & actions | `Actor.make`, `Action.make` | Typed | +| Persisted state | `State.get` / `set` / `update` / `updateAndGet` / `changes` | Typed | +| Typed client | `Actor.client`, `Client.layer` | Typed | +| Typed errors | `RivetError` | Typed | +| Logging | `RivetLogger` | Typed | +| Sleep request | `Actor.Sleep` | Typed | +| Actor address (`actorId` / `name` / `key`) | `Actor.CurrentAddress` | Typed | +| Registry serve / test / web handler | `Registry` | Typed | +| [Events & broadcast](/docs/actors/events) | Not yet wrapped | `rawRivetkitContext.broadcast(...)` | +| [Schedule](/docs/actors/schedule) | Not yet wrapped | `rawRivetkitContext.schedule.*` | +| [Embedded SQLite](/docs/actors/sqlite) | Not yet wrapped | `rawRivetkitContext.db.execute(...)` | +| [Destroy](/docs/actors/lifecycle) | Not yet wrapped | `rawRivetkitContext.destroy()` | +| Queues, connections, vars, alarms | Not yet wrapped | `rawRivetkitContext.*` | +| Lifecycle hooks (`onSleep` / `onDestroy`) | Not yet wrapped | `rawRivetkitContext.*` | +| Raw HTTP / WebSocket handlers | Not yet wrapped | `rawRivetkitContext.*` | + +### Raw Escape Hatch + +Every wake function receives `rawRivetkitContext`, the underlying RivetKit [actor context](/docs/actors). Reach for it to use any feature that does not have a typed wrapper yet. The typed `state` argument and the raw context point at the same actor, so you can mix both: + +```ts src/actors/counter/live.ts @nocheck +export const CounterLive = Counter.toLayer( + Effect.fnUntraced(function* ({ rawRivetkitContext, state }) { + return Counter.of({ + Increment: Effect.fnUntraced(function* ({ payload }) { + // Typed state wrapper + const next = yield* State.updateAndGet(state, (current) => ({ + count: current.count + payload.amount, + })).pipe(Effect.orDie); + + // Untyped features run through the raw context + rawRivetkitContext.broadcast("newCount", next.count); + rawRivetkitContext.schedule.after(1_000, "tick", {}); + + return next.count; + }), + }); + }), + { + state: { + schema: Schema.Struct({ count: Schema.Number }), + initialValue: () => ({ count: 0 }), + }, + name: "Counter", + }, +); +``` + +Calls through `rawRivetkitContext` are not validated by `effect/Schema` and their payloads are typed as they are in the base RivetKit API. + +## Next Steps + + +- [Actions](/docs/actors/actions) — Define the RPC surface clients call on your actor. + + +- [State](/docs/actors/state) — Persist and load actor state across sleeps and restarts. + + +- [Events](/docs/actors/events) — Broadcast realtime updates to connected clients. + +_Source doc path: /docs/actors/quickstart/effect_ diff --git a/.agents/skills/per-tenant-database/reference/actors/quickstart/next-js.md b/.agents/skills/per-tenant-database/reference/actors/quickstart/next-js.md new file mode 100644 index 0000000..f621606 --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/actors/quickstart/next-js.md @@ -0,0 +1,117 @@ +# Next.js Quickstart + +> Source: `src/content/docs/actors/quickstart/next-js.mdx` +> Canonical URL: https://rivet.dev/docs/actors/quickstart/next-js +> Description: Get started with Rivet Actors in Next.js + +--- +### Add Rivet Skill to Coding Agent (Optional) + +If you're using an AI coding assistant (like Claude Code, Cursor, Windsurf, etc.), add Rivet skills for enhanced development assistance: + +```sh +npx skills add rivet-dev/skills +``` + +### Create a Next.js App + +```sh +npx create-next-app@latest my-app +cd my-app +``` + +### Install RivetKit + +### Create an Actor + +Create a file at `src/rivet/registry.ts` with a simple counter actor: + +### Setup Rivet API route + +Create a file at `src/app/api/rivet/[...all]/route.ts` to setup the API routes: + +```ts src/app/api/rivet/[...all]/route.ts @nocheck +import { toNextHandler } from "@rivetkit/next-js"; +import { registry } from "@/rivet/registry"; + +export const maxDuration = 300; + +export const { GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS } = toNextHandler(registry); +``` + +### Use the Actor in a component + +Create a Counter component and add it to your page: + +```tsx src/components/Counter.tsx @nocheck +"use client"; + +import { createRivetKit } from "@rivetkit/next-js/client"; +import type { registry } from "@/rivet/registry"; +import { useState } from "react"; + +export const { useActor } = createRivetKit({ + endpoint: + process.env.NEXT_PUBLIC_RIVET_ENDPOINT ?? "http://localhost:3000/api/rivet", + namespace: process.env.NEXT_PUBLIC_RIVET_NAMESPACE, + token: process.env.NEXT_PUBLIC_RIVET_TOKEN, +}); + +export function Counter() { + const [count, setCount] = useState(0); + + // Get or create a counter actor for the key "my-counter" + const counter = useActor({ + name: "counter", + key: ["my-counter"] + }); + + // Listen to realtime events + counter.useEvent("newCount", (x: number) => setCount(x)); + + const increment = async () => { + // Call actions + await counter.connection?.increment(1); + }; + + return ( +
+

Count: {count}

+ +
+ ); +} +``` + +```tsx src/app/page.tsx @nocheck +import { Counter } from "@/components/Counter"; + +export default function Home() { + return ( +
+

My App

+ +
+ ); +} +``` + +For information about the Next.js client API, see the [React Client API Reference](/docs/clients/react). + +### Run Locally + +Start the Next.js dev server. Rivet auto-starts a local engine alongside it: + +```sh +npm run dev +``` + +Open `http://localhost:3000` in your browser to use the app. + +Visit [http://localhost:6420](http://localhost:6420) in your browser (or point your AI agent at it) to open the Rivet developer tools and inspect your actors live. + +### Deploy to Vercel + +See the [Vercel deployment guide](/docs/deploy/vercel) for detailed instructions on deploying your Rivet app to Vercel. + +_Source doc path: /docs/actors/quickstart/next-js_ diff --git a/.agents/skills/per-tenant-database/reference/actors/quickstart/react.md b/.agents/skills/per-tenant-database/reference/actors/quickstart/react.md new file mode 100644 index 0000000..8722fed --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/actors/quickstart/react.md @@ -0,0 +1,82 @@ +# React Quickstart + +> Source: `src/content/docs/actors/quickstart/react.mdx` +> Canonical URL: https://rivet.dev/docs/actors/quickstart/react +> Description: Build realtime React applications with Rivet Actors + +--- +## Steps + +### Add Rivet Skill to Coding Agent (Optional) + +If you're using an AI coding assistant (like Claude Code, Cursor, Windsurf, etc.), add Rivet skills for enhanced development assistance: + +```sh +npx skills add rivet-dev/skills +``` + +### Install Dependencies + +```sh +npm install rivetkit @rivetkit/react +``` + +### Create Backend Actor and Start Server + +Create your actor registry on the backend and start the server: + +### Create React Frontend + +Set up your React application: + +For detailed information about the React client API, see the [React Client API Reference](/docs/clients/react). + +### Setup Vite Configuration + +Configure Vite for development: + +```ts vite.config.ts @nocheck +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' + +export default defineConfig({ + plugins: [react()], + server: { + port: 5173, + }, +}) +``` + +### Run Your Application + +Start both the backend and frontend: + +**Terminal 1**: Start the backend + +```sh Node.js +npx tsx --watch backend/index.ts +``` + +```sh Bun +bun --watch backend/index.ts +``` + +```sh Deno +deno run --allow-net --allow-read --allow-env --watch backend/index.ts +``` + +**Terminal 2**: Start the frontend + +```sh Frontend +npx vite +``` + +Open `http://localhost:5173` in your browser. Try opening multiple tabs to see realtime sync in action. + +Visit [http://localhost:6420](http://localhost:6420) in your browser (or point your AI agent at it) to open the Rivet developer tools and inspect your actors live. + +### Deploy + +## Configuration Options + +_Source doc path: /docs/actors/quickstart/react_ diff --git a/.agents/skills/per-tenant-database/reference/actors/quickstart/rust.md b/.agents/skills/per-tenant-database/reference/actors/quickstart/rust.md new file mode 100644 index 0000000..1e55471 --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/actors/quickstart/rust.md @@ -0,0 +1,249 @@ +# Rust Quickstart (Beta) + +> Source: `src/content/docs/actors/quickstart/rust.mdx` +> Canonical URL: https://rivet.dev/docs/actors/quickstart/rust +> Description: Build a Rivet Actor in Rust + +--- +Rust support is in beta. The supported public Rust API is `rivetkit` and `rivetkit-client`; lower-level crates are internal implementation details and do not carry a stability guarantee. See the full API reference on [docs.rs/rivetkit](https://docs.rs/rivetkit), or the runnable [`hello-world-rust`](https://github.com/rivet-dev/rivet/tree/main/examples/hello-world-rust) example. + +## Steps + +### Add Rivet + +Add the `rivetkit` crate and its companions: + +```sh +cargo add rivetkit anyhow async-trait +cargo add serde --features derive +cargo add tokio --features full +``` + +### Define Your Actor + +Put the actor in `src/lib.rs` so both your server and your client can share the same types. An actor is a type that implements `Actor`, plus one `Handles` implementation for each action. Persisted state lives in `type State`; ephemeral runtime state is just fields on your actor struct. + +```rust src/lib.rs +use std::{future::Future, pin::Pin, sync::Arc}; + +use async_trait::async_trait; +use rivetkit::prelude::*; +use serde::{Deserialize, Serialize}; + +type BoxFuture = Pin> + Send>>; + +pub struct Counter; + +#[derive(Default, Serialize, Deserialize)] +pub struct CounterState { + pub count: i64, +} + +#[derive(Serialize, Deserialize)] +pub struct Increment { + pub amount: i64, +} + +impl Action for Increment { + type Output = i64; + + const NAME: &'static str = "increment"; +} + +#[derive(Serialize, Deserialize)] +pub struct NewCount { + pub count: i64, +} + +impl Event for NewCount { + const NAME: &'static str = "newCount"; +} + +#[async_trait] +impl Actor for Counter { + type State = CounterState; + type Input = (); + type Actions = (Increment,); + type Events = (NewCount,); + type Queue = (); + type ConnParams = (); + type ConnState = (); + type Action = action::Raw; + + async fn create_state(_ctx: &Ctx, _input: Self::Input) -> Result { + Ok(CounterState::default()) + } + + async fn create(_ctx: &Ctx) -> Result { + Ok(Self) + } +} + +impl Handles for Counter { + type Future = BoxFuture; + + fn handle(self: Arc, ctx: Ctx, action: Increment) -> Self::Future { + Box::pin(async move { + let count = { + let mut state = ctx.state_mut(); + state.count += action.amount; + state.count + }; + ctx.emit(NewCount { count })?; + Ok(count) + }) + } +} + +pub fn registry() -> Registry { + let mut registry = Registry::new(); + registry.register_actor::("counter"); + registry +} +``` + +### Serve The Registry + +Your `src/main.rs` just starts the registry from the library: + +```rust src/main.rs +#[tokio::main] +async fn main() -> anyhow::Result<()> { + counter::registry().start().await +} +``` + +Replace `counter` with your crate name (the package `name` in `Cargo.toml`, with dashes as underscores). + +### Run The Server + +The Rust runtime connects to the Rivet Engine. Setting `RIVETKIT_ENGINE_AUTO_DOWNLOAD=1` lets the runtime download and cache a matching engine binary the first time you run, so there is nothing else to install: + +```sh +RIVETKIT_ENGINE_AUTO_DOWNLOAD=1 cargo run +``` + +Your server now connects to the Rivet Engine on `http://localhost:6420`. Clients connect directly to the engine on this port. + +Visit [http://localhost:6420](http://localhost:6420) in your browser (or point your AI agent at it) to open the Rivet developer tools and inspect your actors live. + +Already have an engine binary? Set `RIVET_ENGINE_BINARY_PATH=/path/to/rivet-engine` to point at it instead. If you are working inside the [Rivet monorepo](https://github.com/rivet-dev/rivet), a local `cargo build -p rivet-engine` is discovered automatically from `target/debug`. + +### Connect To The Rivet Actor + +This code can run either in your frontend or within your backend: + +### Rust + +Add a `src/bin/client.rs` that imports the same actor types from your library. There is no need to redefine the actor on the client. + +```rust src/bin/client.rs +use counter::{Counter, Increment, NewCount}; +use rivetkit::{ + client::{Client, ClientConfig}, + prelude::*, + TypedClientExt, +}; + +#[tokio::main] +async fn main() -> Result<()> { + let client = Client::new(ClientConfig::new("http://localhost:6420").namespace("default")); + + let counter = client.get_or_create_typed_default::("counter", ["my-counter"])?; + let count = counter.send(Increment { amount: 3 }).await?; + println!("New count: {count}"); + + let connection = counter.connect(); + connection + .on::(|event| println!("Count changed: {}", event.count)) + .await; + connection.send(Increment { amount: 1 }).await?; + + Ok(()) +} +``` + +With the server still running, start the client in another terminal: + +```sh +cargo run --bin client +``` + +See the [`hello-world-rust`](https://github.com/rivet-dev/rivet/tree/main/examples/hello-world-rust) example for a complete runnable counter. + +### TypeScript + +A TypeScript client can call your Rust actor by name through the same engine. Actor and action names are resolved at runtime, so the client is untyped here: + +```ts client.ts @nocheck +import { createClient } from "rivetkit/client"; + +const client = createClient("http://localhost:6420"); + +const counter = client.counter.getOrCreate(["my-counter"]); + +const counterConnection = counter.connect(); +counterConnection.on("newCount", (event) => { + console.log("Event count:", event.count); +}); + +const count = await counterConnection.increment(3); +console.log("New count:", count); + +await counterConnection.increment(1); +``` + +See the [JavaScript client documentation](/docs/clients/javascript) for more information. + +### React + +```tsx Counter.tsx @nocheck +import { createRivetKit } from "@rivetkit/react"; +import { useState } from "react"; + +const { useActor } = createRivetKit("http://localhost:6420"); + +function Counter() { + const [count, setCount] = useState(0); + + const counter = useActor({ + name: "counter", + key: ["my-counter"], + }); + + const increment = async () => { + await counter.connection?.increment(1); + }; + + counter.useEvent("newCount", (event) => { + setCount(event.count); + }); + + return ( +
+

Count: {count}

+ +
+ ); +} +``` + +See the [React documentation](/docs/clients/react) for more information. + +### Deploy + +## Next Steps + + +- [API Reference](https://docs.rs/rivetkit) — Full `rivetkit` crate documentation on docs.rs. + + +- [Actions](/docs/actors/actions) — Define the RPC surface clients call on your actor. + + +- [State](/docs/actors/state) — Persist and load actor state across sleeps and restarts. + + +- [Events](/docs/actors/events) — Broadcast realtime updates to connected clients. + +_Source doc path: /docs/actors/quickstart/rust_ diff --git a/.agents/skills/per-tenant-database/reference/actors/quickstart/supabase.md b/.agents/skills/per-tenant-database/reference/actors/quickstart/supabase.md new file mode 100644 index 0000000..20b59d0 --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/actors/quickstart/supabase.md @@ -0,0 +1,109 @@ +# Supabase Functions Quickstart + +> Source: `src/content/docs/actors/quickstart/supabase.mdx` +> Canonical URL: https://rivet.dev/docs/actors/quickstart/supabase +> Description: Set up a Rivet project locally targeting Supabase Edge Functions. + +--- +Set up a Rivet project locally that runs on Supabase Edge Functions. The `@rivetkit/supabase` package wires the WebAssembly runtime for you. + +Prefer to start from a complete project? See the runnable [`hello-world-supabase-functions`](https://github.com/rivet-dev/rivet/tree/main/examples/hello-world-supabase-functions) example. + +## Steps + +### Prerequisites + +- [Node.js](https://nodejs.org/) +- [Supabase CLI](https://supabase.com/docs/guides/cli) +- Docker, for Supabase's local Edge Runtime + +The CLI runs the local Rivet engine as a bundled native binary, so Docker is only needed for Supabase itself. A Supabase project is only needed to deploy. + +### Create the Function + +```sh +npx supabase functions new rivet +``` + +Add the packages used by the function: + +```sh +npm install rivetkit @rivetkit/supabase +``` + +### Configure the Function + +Call `serve` from `@rivetkit/supabase`. It loads the WebAssembly runtime and serves the Rivet handler. + +```ts supabase/functions/rivet/index.ts @nocheck +import { actor } from "rivetkit"; +import { serve, setup } from "@rivetkit/supabase"; + +const counter = actor({ + state: { count: 0 }, + actions: { + increment: (c, amount = 1) => { + c.state.count += amount; + return c.state.count; + }, + }, +}); + +// `setup` returns a typed registry, so a client can type itself with +// `typeof registry`. +export const registry = setup({ use: { counter } }); + +await serve(registry); +``` + +Add a `deno.json` next to the function so the deploy bundles only the WebAssembly runtime. It points `rivetkit` at the pre-bundled `@rivetkit/supabase`, keeping the deploy small. Without it, the deploy pulls Rivet's native engine and 413s. + +```json supabase/functions/rivet/deno.json +{ + "imports": { + "rivetkit": "npm:@rivetkit/supabase", + "@rivetkit/supabase": "npm:@rivetkit/supabase" + } +} +``` + +Your function code keeps importing from `rivetkit` as usual. The import map only changes how Deno resolves it at bundle time. + +### Run Locally + +Start Rivet. The CLI runs the local engine, spawns `supabase functions serve` for you, and populates the connection values: + +```sh +npx @rivetkit/cli dev --provider supabase +``` + +Visit [http://localhost:6420](http://localhost:6420) in your browser (or point your AI agent at it) to open the Rivet developer tools and inspect your actors live. + +### Call the Actor + +Connect to your actor from a client. This connects directly to the local engine on `http://localhost:6420`: + +```ts client.ts @nocheck +import { createClient } from "rivetkit/client"; +import type { registry } from "./supabase/functions/rivet/index"; + +const client = createClient("http://localhost:6420"); + +const counter = client.counter.getOrCreate(["my-counter"]); +const count = await counter.increment(3); +console.log("New count:", count); +``` + +See the [JavaScript client documentation](/docs/clients/javascript) for more information. + +### Deploy + +Ready to ship? See [Deploying to Supabase Functions](/docs/deploy/supabase). + +## Related + +- [Quickstart](/docs/actors/quickstart) +- [Deploying to Supabase Functions](/docs/deploy/supabase) +- [SQLite](/docs/actors/sqlite) + +_Source doc path: /docs/actors/quickstart/supabase_ diff --git a/.agents/skills/per-tenant-database/reference/actors/request-handler.md b/.agents/skills/per-tenant-database/reference/actors/request-handler.md new file mode 100644 index 0000000..0536b9c --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/actors/request-handler.md @@ -0,0 +1,79 @@ +# Low-Level HTTP Request Handler + +> Source: `src/content/docs/actors/request-handler.mdx` +> Canonical URL: https://rivet.dev/docs/actors/request-handler +> Description: Actors can handle HTTP requests through the `onRequest` handler. + +--- +For most use cases, [actions](/docs/actors/actions) provide high-level API powered by HTTP that's easier to work with than low-level HTTP. However, low-level handlers are required when implementing custom use cases or integrating external libraries that need direct access to the underlying HTTP `Request`/`Response` objects or WebSocket connections. + +## Handling HTTP Requests + +The `onRequest` handler processes HTTP requests sent to your actor. It receives the actor context and a standard `Request` object and returns a `Response` object. + +### Raw HTTP + +### Hono + +See also the [raw fetch handler example](https://github.com/rivet-dev/rivet/tree/main/examples/raw-fetch-handler). + +## Sending Requests To Actors + +### Via RivetKit Client + +Use the `.fetch()` method on an actor handle to send HTTP requests to the actor's `onRequest` handler. This can be executed from either your frontend or backend. + +### Via getGatewayUrl + +Use `.getGatewayUrl()` to get the raw gateway URL for the actor. This is useful when you need to use the URL with external tools or custom HTTP clients. + +### Via HTTP API + +This handler can be accessed with raw HTTP using `https://api.rivet.dev/gateway/{actorId}/request/{...path}`. + +For example, to call `POST /increment` on the counter actor above: + +```bash +curl -X POST "https://api.rivet.dev/gateway/{actorId}/request/increment" \ + -H "x-rivet-token: {token}" +``` + +The request is routed to the actor's `onRequest` handler where: + +- `request.method` is `"POST"` +- `request.url` ends with `/increment` (the path after `/request/`) +- Headers, body, and other request properties are passed through unchanged + +See the [HTTP API reference](/docs/actors/http-api) for more information on HTTP routing and authentication. + +### Via Proxying Requests + +You can proxy HTTP requests from your own server to actor handlers using the RivetKit client. This is useful when you need to add custom authentication, rate limiting, or request transformation before forwarding to actors. + +## Connection & Lifecycle Hooks + +`onRequest` will trigger the `onBeforeConnect`, `onConnect`, and `onDisconnect` hooks. Read more about [lifecycle hooks](/docs/actors/lifecycle). + +Requests in flight will be listed in `c.conns`. Read more about [connections](/docs/actors/connections). + +## WinterTC Compliance + +The `onRequest` handler is WinterTC compliant and will work with existing libraries using the standard `Request` and `Response` types. + +## Limitations + +- Does not support streaming responses & server-sent events at the moment. See the [tracking issue](https://github.com/rivet-dev/rivet/issues/3529). +- `OPTIONS` requests currently are handled by Rivet and are not passed to `onRequest` + +## Advanced + +### Skip Ready Wait + +Requests are normally held at the gateway until the actor is ready. Pass `skipReadyWait: true` on `handle.fetch()` to deliver immediately, including while the actor is still starting or in the [sleep grace period](/docs/actors/lifecycle#shutdown-sequence). See [Skip Ready Wait](/docs/clients/javascript#skip-ready-wait) for details. + +## API Reference + +- [`RequestContext`](/typedoc/interfaces/rivetkit.mod.RequestContext.html) - Context for HTTP request handlers +- [`ActorDefinition`](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html) - Interface for defining request handlers + +_Source doc path: /docs/actors/request-handler_ diff --git a/.agents/skills/per-tenant-database/reference/actors/scaling.md b/.agents/skills/per-tenant-database/reference/actors/scaling.md new file mode 100644 index 0000000..b597115 --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/actors/scaling.md @@ -0,0 +1,10 @@ +# Scaling & Concurrency + +> Source: `src/content/docs/actors/scaling.mdx` +> Canonical URL: https://rivet.dev/docs/actors/scaling +> Description: This page has moved to [design patterns](/docs/actors/design-patterns). + +--- + + +_Source doc path: /docs/actors/scaling_ diff --git a/.agents/skills/per-tenant-database/reference/actors/schedule.md b/.agents/skills/per-tenant-database/reference/actors/schedule.md new file mode 100644 index 0000000..fc9c631 --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/actors/schedule.md @@ -0,0 +1,190 @@ +# Schedule & Cron + +> Source: `src/content/docs/actors/schedule.mdx` +> Canonical URL: https://rivet.dev/docs/actors/schedule +> Description: Run durable one-shot and recurring actor actions on a schedule. + +--- +Rivet Actor schedules invoke actions on the same actor and survive sleep, restarts, upgrades, and crashes. Use one-shot schedules for delayed work, cron jobs for calendar-based work, and fixed intervals for frequent jobs. + +## Quick start + +```ts After +const reminders = actor({ + onCreate: async (c) => { + await c.schedule.after(30_000, "sendReminder", "reminder-123"); + }, + actions: { + sendReminder: (_c, reminderId: string) => { + console.log("Sending reminder", reminderId); + }, + }, +}); +``` + +```ts Cron +const reports = actor({ + onCreate: async (c) => { + await c.cron.set({ + name: "daily-report", + expression: "0 9 * * *", + action: "runReport", + args: ["sales"], // Optional. + timezone: "America/Los_Angeles", // Optional; defaults to UTC. + maxHistory: 100, // Optional; defaults to 100. Set to 0 to disable. + }); + }, + actions: { + runReport: (_c, report: string) => { + console.log("Running report", report); + }, + }, +}); +``` + +```ts Every +const cache = actor({ + onCreate: async (c) => { + await c.cron.every({ + name: "refresh-cache", + interval: 60_000, // Minimum 5 seconds. + action: "refreshCache", + args: ["products"], // Optional. + maxHistory: 100, // Optional; defaults to 100. Set to 0 to disable. + }); + }, + actions: { + refreshCache: (_c, cache: string) => { + console.log("Refreshing cache", cache); + }, + }, +}); +``` + +## Sleep Between Runs + +[Actors do not need to stay awake](/docs/actors/lifecycle#sleeping) while waiting for a scheduled action. Once an actor becomes idle, it can sleep normally. Sleeping does not pause or remove its schedules. Rivet wakes the actor when the next action is due, whether that is seconds or arbitrarily far in the future. + +Recurring Cron and fixed-interval schedules continue until they are updated or deleted. One-shot schedules remain pending until their target time. + +## One-shot schedules + +### Run after a delay + +Runs once after the given delay in milliseconds and returns the generated schedule ID. + +```ts +const id = await c.schedule.after(5_000, "sendReminder", reminderId); +``` + +### Run at a specific time + +Runs once at a Unix timestamp in milliseconds. + +```ts +const id = await c.schedule.at(Date.parse("2026-08-01T09:00:00Z"), "openSale", saleId); +``` + +### Inspect and cancel schedules + +```ts +const schedule = await c.schedule.get(id); +const pending = await c.schedule.list(); +const cancelled = await c.schedule.cancel(id); +``` + +## Recurring jobs + +### Run on a cron schedule + +Cron jobs use standard five-field expressions. Names are unique per actor, and reusing a name updates the existing job. The timezone defaults to UTC. + +```ts +await c.cron.set({ + name: "daily-report", + expression: "0 9 * * *", + action: "runReport", + args: ["sales"], // Optional. + timezone: "America/Los_Angeles", // Optional; defaults to UTC. + maxHistory: 100, // Optional; defaults to 100. Set to 0 to disable. +}); +``` + +### Run at a fixed interval + +Fixed-interval jobs keep their cadence anchored to scheduled deadlines, so action runtime does not introduce drift. Names are unique per actor, and reusing a name updates the existing job. The minimum interval is 5 seconds. + +```ts +await c.cron.every({ + name: "presence-sweep", + interval: 15_000, // Minimum 5 seconds. + action: "sweepPresence", + args: [], // Optional. + maxHistory: 25, // Optional; defaults to 100. Set to 0 to disable. +}); +``` + +### Update and delete jobs + +Submitting a recurring job with an existing name updates it. Changing its cadence calculates a new next run, while changing only its action, arguments, or history settings preserves the existing cadence. + +```ts +const job = await c.cron.get("daily-report"); +const jobs = await c.cron.list(); +const deleted = await c.cron.delete("daily-report"); +``` + +### View run history + +```ts +const recent = await c.cron.history("daily-report", { + limit: 20, // Optional. +}); +``` + +Each entry's `result` is `running`, `ok`, `error`, or `skipped`. Jobs retain 100 entries by default. Set maximum history to zero to disable and clear history, or choose up to 1,000 entries. An actor retains at most 10,000 history entries across all jobs. + +## Run information + +When a schedule invokes an action, it appends a ScheduledFireInfo object after the configured action arguments. + +```ts +import type { ScheduledFireInfo } from "rivetkit"; + +const reports = actor({ + onCreate: async (c) => { + await c.cron.set({ + name: "daily-report", + expression: "0 9 * * *", + action: "runReport", + args: ["sales"], // Optional. + }); + }, + actions: { + runReport: ( + _c, + report: string, + fire: ScheduledFireInfo, + ) => { + console.log( + report, + fire.kind, + fire.id, + fire.name, + fire.scheduledAt, + fire.firedAt, + ); + }, + }, +}); +``` + +## Execution behavior + +- **Durability**: Schedules survive actor sleep, restarts, upgrades, and crashes. See [Sleep Between Runs](#sleep-between-runs). +- **Failures**: Failed runs are not retried immediately. A failed recurring run continues at the next normal cadence; a failed one-shot is complete after its attempted invocation. Cron failures are available in [run history](#view-run-history), so you can build a custom retry mechanism. +- **Idempotency**: Scheduled actions should be idempotent so manually retrying a failed or interrupted run is safe. +- **Overlaps**: Overlapping recurring runs are skipped. +- **Missing actions**: Recurring jobs are deleted when their action no longer exists. + +_Source doc path: /docs/actors/schedule_ diff --git a/.agents/skills/per-tenant-database/reference/actors/sharing-and-joining-state.md b/.agents/skills/per-tenant-database/reference/actors/sharing-and-joining-state.md new file mode 100644 index 0000000..1fe0683 --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/actors/sharing-and-joining-state.md @@ -0,0 +1,10 @@ +# Sharing and Joining State + +> Source: `src/content/docs/actors/sharing-and-joining-state.mdx` +> Canonical URL: https://rivet.dev/docs/actors/sharing-and-joining-state +> Description: This page has moved to [design patterns](/docs/actors/design-patterns). + +--- + + +_Source doc path: /docs/actors/sharing-and-joining-state_ diff --git a/.agents/skills/per-tenant-database/reference/actors/sqlite-drizzle.md b/.agents/skills/per-tenant-database/reference/actors/sqlite-drizzle.md new file mode 100644 index 0000000..edc157a --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/actors/sqlite-drizzle.md @@ -0,0 +1,248 @@ +# SQLite + Drizzle + +> Source: `src/content/docs/actors/sqlite-drizzle.mdx` +> Canonical URL: https://rivet.dev/docs/actors/sqlite-drizzle +> Description: Use Drizzle ORM with embedded SQLite in Rivet Actors. + +--- +Use Drizzle when you want typed schema, typed queries, and generated migrations on top of actor-local SQLite. + +For a high-level overview of where to store actor data, see [State & Storage](/docs/actors/state). + +## What is Drizzle good for? + +- **Typed schema**: define tables in TypeScript and get typed query results. +- **Typed query builder**: write SQL-like queries with autocompletion. +- **Migration workflow**: generate SQL migration files from schema changes. +- **Raw SQL escape hatch**: use `c.db.execute(...)` for direct SQLite when needed. + +## Project structure + +Use one folder per actor database: + +```txt +src/ + actors/ + todo-list/ + index.ts + schema.ts + drizzle.config.ts + drizzle/ + 0000_init.sql + migrations.js + migrations.d.ts + meta/ + _journal.json +``` + +- `index.ts` is the actor implementation. +- `drizzle/` holds the SQL migrations (`*.sql`) and `meta/_journal.json` generated by `drizzle-kit`. +- `migrations.js` is a small RivetKit glue file you maintain by hand. It imports the journal and each `*.sql` file and exports a `{ journal, migrations }` object keyed by migration (for example `m0000`). Add a new entry here whenever `db:generate` produces a new migration. +- Commit the generated migration files and `migrations.js` to source control. + +## Basic setup + +```json package.json +{ + "scripts": { + "db:generate": "find src/actors -name drizzle.config.ts -exec drizzle-kit generate --config {} \\;" + }, + "dependencies": { + "rivetkit": "*", + "drizzle-orm": "^0.44.2" + }, + "devDependencies": { + "drizzle-kit": "^0.31.2" + } +} +``` + +```ts vite.config.ts @nocheck +import { defineConfig, type Plugin } from "vite"; +import { readFileSync } from "node:fs"; + +function sqlRawPlugin(): Plugin { + return { + name: "sql-raw", + transform(_code, id) { + if (id.endsWith(".sql")) { + const content = readFileSync(id, "utf-8"); + return { code: `export default ${JSON.stringify(content)};` }; + } + }, + }; +} + +export default defineConfig({ + plugins: [sqlRawPlugin()], +}); +``` + +```ts drizzle.config.ts @nocheck +import { defineConfig } from "rivetkit/db/drizzle"; + +export default defineConfig({ + schema: "./src/actors/todo-list/schema.ts", + out: "./src/actors/todo-list/drizzle", +}); +``` + +```sql 0000_init.sql +CREATE TABLE `todos` ( + `id` integer PRIMARY KEY AUTOINCREMENT NOT NULL, + `title` text NOT NULL, + `created_at` integer NOT NULL +); +``` + +```json _journal.json +{ + "version": "7", + "dialect": "sqlite", + "entries": [ + { + "idx": 0, + "version": "7", + "when": 1735689600000, + "tag": "0000_init", + "breakpoints": true + } + ] +} +``` + +```ts migrations.js @nocheck +import journal from "./meta/_journal.json"; +import m0000 from "./0000_init.sql"; + +export default { + journal, + migrations: { + m0000, + }, +}; +``` + +```ts index.ts @nocheck +import { actor } from "rivetkit"; +import { db } from "rivetkit/db/drizzle"; +import migrations from "./drizzle/migrations.js"; +import { schema, todos } from "./schema.ts"; + +export const todoList = actor({ + db: db({ schema, migrations }), + actions: { + addTodo: async (c, title: string) => { + const rows = await c.db + .insert(todos) + .values({ title, createdAt: Date.now() }) + .returning(); + return rows[0]; + }, + getTodos: async (c) => { + return await c.db.select().from(todos).orderBy(todos.id); + }, + getTodoCount: async (c) => { + const rows = (await c.db.execute( + "SELECT COUNT(*) AS count FROM todos", + )) as { count: number }[]; + return rows[0]?.count ?? 0; + }, + }, +}); +``` + +```ts index.ts @nocheck +import { setup } from "rivetkit"; +import { todoList } from "./todo-list/index.ts"; + +export const registry = setup({ use: { todoList } }); +registry.start(); +``` + +```ts client.ts @nocheck +import { createClient } from "rivetkit/client"; +import type { registry } from "./index"; + +const client = createClient("http://localhost:6420"); +const todoList = client.todoList.getOrCreate(["main"]); + +await todoList.addTodo("Write Drizzle docs"); + +const todos = await todoList.getTodos(); +const count = await todoList.getTodoCount(); + +console.log(todos, count); +``` + +## Queries + +### Query builder + +Use Drizzle's typed query APIs for most reads and writes. + +```ts @nocheck +import { eq } from "drizzle-orm"; + +await c.db.insert(todos).values({ title, createdAt: Date.now() }); + +const rows = await c.db + .select() + .from(todos) + .where(eq(todos.title, title)); +``` + +### Raw SQL + +`rivetkit/db/drizzle` also exposes raw SQLite access through `c.db.execute(...)`. + +```ts @nocheck +await c.db.execute( + "CREATE INDEX IF NOT EXISTS idx_todos_created_at ON todos(created_at)", +); +``` + +## Queues + +Use queues for ordered mutations and keep actions read-only. Import `queue` alongside `actor` from `rivetkit`. + +```ts @nocheck +import { actor, queue } from "rivetkit"; + +// ... + +queues: { + addTodo: queue<{ title: string }>(), +}, +run: async (c) => { + for await (const message of c.queue.iter()) { + if (message.name === "addTodo") { + await c.db.insert(todos).values({ + title: message.body.title, + createdAt: Date.now(), + }); + } + } +}, +actions: { + getTodos: async (c) => await c.db.select().from(todos), +}, +``` + +## Recommendations + +- Prefer Drizzle query APIs for app code and use raw SQL for advanced SQLite features. +- Keep one `drizzle.config.ts` per actor folder. +- Re-run `db:generate` after schema changes and commit generated migration files. +- Use queues for writes and actions for reads. +- Keep related writes in one action or queue message to reduce interleaved query risk. + +## Read more + +- [Drizzle SQLite quickstart](https://orm.drizzle.team/docs/get-started-sqlite) +- [Drizzle `drizzle-kit generate`](https://orm.drizzle.team/docs/drizzle-kit-generate) +- [Drizzle + Cloudflare D1](https://orm.drizzle.team/docs/deploy-cloudflare-d1) +- [Drizzle + Cloudflare Durable Objects](https://orm.drizzle.team/docs/deploy-cloudflare-do) +- [Cloudflare Durable Objects SQLite storage](https://developers.cloudflare.com/durable-objects/api/sqlite-storage-api/) + +_Source doc path: /docs/actors/sqlite-drizzle_ diff --git a/.agents/skills/per-tenant-database/reference/actors/sqlite.md b/.agents/skills/per-tenant-database/reference/actors/sqlite.md new file mode 100644 index 0000000..cc42bf3 --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/actors/sqlite.md @@ -0,0 +1,127 @@ +# SQLite + +> Source: `src/content/docs/actors/sqlite.mdx` +> Canonical URL: https://rivet.dev/docs/actors/sqlite +> Description: Use embedded SQLite in Rivet Actors with raw SQL queries. + +--- +For a high-level overview of where to store actor data, including when to use `c.state` versus SQLite, see [State & Storage](/docs/actors/state). + +## What is SQLite? + +- **Database per actor**: each actor instance has its own SQLite database, scoped to that actor. +- **High performance**: Rivet Actors keep compute and storage together, so queries avoid network round trips to an external database. +- **Larger-than-memory storage**: SQLite stores data on disk, so you can work with datasets that do not fit in actor memory. +- **Embedded relational database**: use tables, indexes, and SQL queries directly inside actor logic. + +### SQLite features + +- **Indexes**: speed up lookups on frequently queried fields. +- **Search and filtering**: use `WHERE`, `LIKE`, and `ORDER BY` instead of manual in-memory loops. +- **Relationships**: use multiple tables and `JOIN` queries for connected data. +- **Constraints**: use primary keys, unique constraints, and foreign keys for data integrity. +- **Transactions**: apply multiple writes atomically when changes must stay consistent. + +## Raw SQL vs ORM (Drizzle) + +Rivet supports both raw SQL and [Drizzle](https://orm.drizzle.team/) for actor-local SQLite. + +Use **raw SQL** when you want direct query control and minimal abstraction. + +```ts @nocheck +await c.db.execute("INSERT INTO todos (title) VALUES (?)", title); +const rows = await c.db.execute("SELECT id, title FROM todos ORDER BY id DESC"); +``` + +Use **Drizzle** when you want typed schema and typed query APIs. + +```ts @nocheck +await c.vars.drizzle.insert(todos).values({ title }); +const rows = await c.vars.drizzle.select().from(todos).orderBy(desc(todos.id)); +``` + +You can mix both in the same actor. + +For Drizzle setup, see [SQLite + Drizzle](/docs/actors/sqlite-drizzle). + +## Basic setup + +Define `db: db({ onMigrate })` on your actor, create your schema in `onMigrate`, and execute SQL with `c.db.execute(...)`. + +RivetKit wraps `onMigrate` in a SQLite savepoint, so migration steps are atomic. If `onMigrate` throws, all SQL run by that hook is rolled back before the actor starts. + +## Queries + +`c.db.execute(...)` returns an array of row objects for `SELECT` queries. + +```ts @nocheck +const rows = await c.db.execute( + "SELECT id, title FROM todos WHERE title LIKE ?", + `%${query}%`, +); +``` + +### Parameterized queries + +Use `?` placeholders for dynamic values and pass parameters in order after the SQL string. + +```ts @nocheck +await c.db.execute("INSERT INTO todos (title) VALUES (?)", title); +``` + +You can also use named SQLite bindings by passing a single properties object. + +```ts @nocheck +const rows = await c.db.execute( + "SELECT id, title FROM todos WHERE title = :title", + { title: "Write SQLite docs" }, +); +``` + +### Transactions + +Use transactions when multiple writes must succeed or fail together. + +```ts @nocheck +await c.db.transaction(async (tx) => { + await tx.execute("INSERT INTO todos (title) VALUES (?)", title); + await tx.execute( + "INSERT INTO comments (todo_id, body) VALUES (last_insert_rowid(), ?)", + body, + ); +}); +``` + +RivetKit commits when the callback resolves and rolls back when it throws. Other transactions and ordinary actor SQL queue in FIFO order until the callback finishes. Transactions have a 60-second safety timeout by default; increase it for legitimately long work with `{ timeout: 120_000 }`. + +Always use the callback's `tx` value inside the transaction. Starting another transaction or using the outer `c.db` from the callback waits behind the active transaction and eventually reaches the safety timeout; the resulting error points to this possible deadlock. + +Manual `BEGIN`/`COMMIT` calls remain supported for compatibility, but cannot protect against interleaving callers. RivetKit logs a warning recommending `db.transaction()`. Set `warnOnManualTransactions: false` in `db(...)` to disable the warning; the warning itself mentions this flag. + +## Queues + +It's recommended to use queues for mutations and actions for read-only queries. This is the same code structure as the basic setup, but mutation writes are routed through queues. + +## Debugging + +- `GET /inspector/summary` includes `isDatabaseEnabled` so you can confirm SQLite is configured. +- `GET /inspector/database/schema` returns the tables and views discovered in the actor's SQLite database. +- `GET /inspector/database/rows?table=...&limit=100&offset=0` returns paged rows for a specific table or view. +- `POST /inspector/database/execute` lets you run ad-hoc SQL for debugging and data fixes with positional `args` or named `properties`. +- Keep a small read-only action for quick query verification while debugging. +- In non-dev mode, inspector endpoints require authorization. + +## Recommendations + +- Keep schema creation and migration steps in `onMigrate`; RivetKit runs them atomically inside a SQLite savepoint. +- Use `?` placeholders for dynamic values. +- Prefer queue-driven writes for ordered or background work. +- Use transactions for related multi-step mutations when atomicity matters. + +## Read more + +- [SQLite + Drizzle in Rivet Actors](/docs/actors/sqlite-drizzle) +- [SQLite docs](https://sqlite.org/docs.html) +- [SQLite SQL language reference](https://sqlite.org/lang.html) + +_Source doc path: /docs/actors/sqlite_ diff --git a/.agents/skills/per-tenant-database/reference/actors/state.md b/.agents/skills/per-tenant-database/reference/actors/state.md new file mode 100644 index 0000000..559b49c --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/actors/state.md @@ -0,0 +1,270 @@ +# In-Memory State + +> Source: `src/content/docs/actors/state.mdx` +> Canonical URL: https://rivet.dev/docs/actors/state +> Description: Actors store state in memory for instant reads and writes. State can be persisted automatically or kept ephemeral. + +--- +## Types of State + +There are three ways to store data in an actor, depending on what it looks like and whether it needs to survive restarts. + +### Durable + +Simple, serializable data on `c.state` that is automatically persisted and restored across restarts. The default starting point. + +### Ephemeral + +Live objects on `c.vars` like database connections, API clients, and event emitters, or data loaded from an external source. Never persisted. + +```typescript @nocheck External database +import { actor } from "rivetkit"; +import { Pool } from "pg"; + +// One shared pool for the whole process, created once and reused by every actor +const pool = new Pool({ connectionString: process.env.DATABASE_URL }); + +const userActor = actor({ + state: { profile: null as Record | null }, + + // Load this actor's row from the shared pool on each start + createVars: async (c) => { + const { rows } = await pool.query("SELECT * FROM users WHERE id = $1", [c.key[0]]); + return { profile: rows[0] }; + }, + + actions: { + updateEmail: async (c, email: string) => { + await pool.query("UPDATE users SET email = $1 WHERE id = $2", [email, c.key[0]]); + } + } +}); +``` + +### SQLite + +Rivet also provides an embedded SQLite database (`c.db`) for when your data needs to be queried, requires safe schema migrations, or grows too large to hold in memory. See [SQLite](/docs/actors/sqlite). + +```typescript @nocheck Basic +import { actor } from "rivetkit"; +import { db } from "rivetkit/db"; + +const todoList = actor({ + db: db({ + onMigrate: async (db) => { + await db.execute(` + CREATE TABLE IF NOT EXISTS todos ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + title TEXT NOT NULL + ); + `); + }, + }), + + actions: { + add: async (c, title: string) => { + await c.db.execute("INSERT INTO todos (title) VALUES (?)", title); + }, + + list: async (c) => { + return (await c.db.execute( + "SELECT id, title FROM todos ORDER BY id DESC", + )) as { id: number; title: string }[]; + }, + }, +}); +``` + +```typescript @nocheck Load into memory +import { actor } from "rivetkit"; +import { db } from "rivetkit/db"; + +const counter = actor({ + db: db({ + onMigrate: async (db) => { + await db.execute(` + CREATE TABLE IF NOT EXISTS counter ( + id INTEGER PRIMARY KEY CHECK (id = 1), + count INTEGER NOT NULL + ); + `); + await db.execute("INSERT OR IGNORE INTO counter (id, count) VALUES (1, 0)"); + }, + }), + + // Load the count from SQLite into memory on every start + createVars: async (c) => { + const rows = (await c.db.execute( + "SELECT count FROM counter WHERE id = 1", + )) as { count: number }[]; + return { count: rows[0].count }; + }, + + actions: { + get: (c) => c.vars.count, + + increment: async (c) => { + // Update the in-memory value and write it back to SQLite + c.vars.count += 1; + await c.db.execute("UPDATE counter SET count = ? WHERE id = 1", c.vars.count); + return c.vars.count; + }, + }, +}); +``` + +## State Isolation + +Each actor's state is fully isolated. Other actors and clients can't touch it directly; all reads and writes go through the actor's own [Actions](/docs/actors/actions). To share state across actors, see [sharing and joining state](/docs/actors/sharing-and-joining-state). + +## Durable State + +`c.state` lives in memory and is persisted automatically, so reads and writes have no added latency while the data still survives sleeps, restarts, upgrades, and crashes. Use it for small, simple values like counters, flags, and small maps. + +`createState` runs once when the actor is first created. On later starts, state is loaded from storage instead of recreated. See [Lifecycle](/docs/actors/lifecycle). + +### When state saves + +Mutating `c.state` schedules a save automatically. Rapid mutations are batched into a single write on a throttle (`stateSaveInterval`, default 1 second). Reads never trigger a save, saves aren't tied to action or handler boundaries, and state is also flushed when the actor sleeps or shuts down. + +To force a save mid-action, call `c.saveState()`: + +- `c.saveState({ immediate: true })` writes immediately and resolves once the write completes. +- `c.saveState()` schedules a throttled save and returns right away, without waiting for the write. + +Force an immediate save before a risky side effect so a crash can't lose progress: + +### Supported types + +State must be serializable. + +- `null`, `undefined`, `boolean`, `string`, `number`, `BigInt` +- `Date`, `RegExp`, `Error` +- `ArrayBuffer` and typed arrays (`Uint8Array`, `Int8Array`, `Float32Array`, etc.) +- `Map`, `Set`, `Array` +- Plain objects + +When data grows large or needs querying, store it in [Embedded SQLite](#embedded-sqlite) instead. + +## Ephemeral State + +`c.vars` holds data that exists only while the actor runs and is never saved. Use it for live objects that can't be serialized (connections, clients, emitters) or for data loaded from an external source. Most actors use both: `state` for durable data, `vars` for live objects. + +`createVars` runs on every actor start, unlike `createState` which runs once. That makes it the place to open connections and load data each time the actor wakes. + +### Runtime objects + +Build non-serializable objects in `createVars` and use them from actions: + +### Loading from external sources + +Create the connection pool once at module scope and share it across all actors, then use `createVars` (which can be `async`) to load this actor's data from it on each start: + +```typescript @nocheck +import { actor } from "rivetkit"; +import { Pool } from "pg"; + +// One shared pool for the whole process, not one per actor +const pool = new Pool({ connectionString: process.env.DATABASE_URL }); + +const profile = actor({ + state: { cachedName: "" }, + + createVars: async (c) => { + const { rows } = await pool.query("SELECT * FROM users WHERE id = $1", [c.key[0]]); + return { user: rows[0] }; + }, + + actions: { + updateEmail: async (c, email: string) => { + await pool.query("UPDATE users SET email = $1 WHERE id = $2", [email, c.key[0]]); + } + } +}); +``` + +When the actor owns its data, prefer [durable state](#durable-state) or [SQLite](#embedded-sqlite), which need no external infrastructure. + +### Cleanup + +`vars` is dropped when the actor stops, but per-actor resources like timers, subscriptions, and dedicated connections aren't cleaned up for you. Release them in `onSleep` and `onDestroy`. A shared pool stays open for the whole process, so don't close it per actor. + +```typescript @nocheck +const poller = actor({ + state: { ticks: 0 }, + + // Per-actor timer started on each wake + createVars: (c) => ({ timer: setInterval(() => c.state.ticks++, 5000) }), + + // Clear it before the actor sleeps or is destroyed + onSleep: (c) => clearInterval(c.vars.timer), + onDestroy: (c) => clearInterval(c.vars.timer), + + actions: { /* ... */ } +}); +``` + +## Embedded SQLite + +`c.db` is a SQLite database scoped to each actor and stored on disk. Use it for queryable, relational, or larger-than-memory data. Because compute and storage live together, queries run locally with no network round trips. + +A common pattern is to treat SQLite as the source of truth and keep a working copy in `c.vars`: load rows in `createVars`, serve reads from memory, and write changes back to `c.db`. + +```typescript @nocheck +import { actor } from "rivetkit"; +import { db } from "rivetkit/db"; + +const leaderboard = actor({ + db: db({ + onMigrate: async (db) => { + await db.execute(` + CREATE TABLE IF NOT EXISTS scores ( + player TEXT PRIMARY KEY, + score INTEGER NOT NULL + ); + `); + }, + }), + + // Load the table into memory once per start + createVars: async (c) => { + const rows = (await c.db.execute("SELECT player, score FROM scores")) as { + player: string; + score: number; + }[]; + return { scores: new Map(rows.map((r) => [r.player, r.score])) }; + }, + + actions: { + top: (c) => [...c.vars.scores].sort((a, b) => b[1] - a[1]).slice(0, 10), + + record: async (c, player: string, score: number) => { + c.vars.scores.set(player, score); + // Write through to SQLite + await c.db.execute( + "INSERT INTO scores (player, score) VALUES (?, ?) ON CONFLICT(player) DO UPDATE SET score = ?", + player, score, score, + ); + }, + }, +}); +``` + +For the full query API, schema migrations, transactions, and the Drizzle ORM, see: + +- [SQLite](/docs/actors/sqlite): raw SQL against the embedded per-actor database. +- [SQLite + Drizzle](/docs/actors/sqlite-drizzle): typed schema and query APIs. + +## Debugging + +- `GET /inspector/state` returns the actor's current state and `isStateEnabled`. +- `PATCH /inspector/state` lets you set state directly while debugging. +- In non-dev mode, inspector endpoints require authorization. + +## API Reference + +- [`CreateContext`](/typedoc/types/rivetkit.mod.CreateContext.html) - Context available during actor state creation +- [`ActorContext`](/typedoc/interfaces/rivetkit.mod.ActorContext.html) - Context available throughout actor lifecycle +- [`ActorDefinition`](/typedoc/interfaces/rivetkit.mod.ActorDefinition.html) - Interface for defining actors with state + +_Source doc path: /docs/actors/state_ diff --git a/.agents/skills/per-tenant-database/reference/actors/statuses.md b/.agents/skills/per-tenant-database/reference/actors/statuses.md new file mode 100644 index 0000000..cccf52a --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/actors/statuses.md @@ -0,0 +1,36 @@ +# Actor Statuses + +> Source: `src/content/docs/actors/statuses.mdx` +> Canonical URL: https://rivet.dev/docs/actors/statuses +> Description: Understand the lifecycle statuses of Rivet Actors, what they mean, how they appear in the API, and how to troubleshoot common issues. + +--- +## Statuses + +These are the statuses you can see in the dashboard for each actor. + +| Status | Description | +|---|---| +| **Starting** | The actor has been created but has not yet become connectable. | +| **Running** | The actor is live and accepting connections. | +| **Destroyed** | The actor has been gracefully destroyed. | +| **Crashed** | The actor failed to start or encountered a fatal error. See [Troubleshooting](/docs/actors/troubleshooting#actor-status-is-crashed) for common failure reasons. | +| **Sleeping** | The actor has been put to sleep from inactivity. It will be woken up automatically when a new request arrives. | +| **Pending** | The actor is waiting to be allocated to a runner. This happens when no runner is available to handle the actor. See [Troubleshooting](/docs/actors/troubleshooting#actor-status-is-pending) for common causes. | +| **Crash Loop Backoff** | The actor failed to allocate and is waiting to retry with a backoff. This typically means repeated allocation failures. The backoff prevents overloading your infrastructure in the case of a widespread misconfiguration in your backend. See [Troubleshooting](/docs/actors/troubleshooting#actor-status-is-crashed) for common failure reasons. | + +## API Representation + +The actor object returned by the full engine API (used by the dashboard) includes the following timestamp fields used to derive status: + +| Field | Description | +|---|---| +| `createTs` | When the actor was first created. Always present. | +| `connectableTs` | When the actor became connectable. Null if not yet running. | +| `destroyTs` | When the actor was destroyed. | +| `sleepTs` | When the actor entered a sleeping state. | +| `pendingAllocationTs` | When the actor started waiting for an allocation. | +| `rescheduleTs` | When the actor will retry allocation after a failure. | +| `error` | Error details if the actor failed. | + +_Source doc path: /docs/actors/statuses_ diff --git a/.agents/skills/per-tenant-database/reference/actors/testing.md b/.agents/skills/per-tenant-database/reference/actors/testing.md new file mode 100644 index 0000000..1a30fff --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/actors/testing.md @@ -0,0 +1,51 @@ +# Testing + +> Source: `src/content/docs/actors/testing.mdx` +> Canonical URL: https://rivet.dev/docs/actors/testing +> Description: Rivet provides a straightforward testing framework to build reliable and maintainable applications. This guide covers how to write effective tests for your actor-based services. + +--- +## Setup + +To set up testing with Rivet: + +```bash +# Install Vitest +npm install -D vitest + +# Run tests +npm test +``` + +## Basic Testing Setup + +Rivet includes a test helper called `setupTest` that starts your registry in test mode and returns a client connected to it. This allows for fast, isolated tests without external dependencies. + +## Testing Actor State + +State persists within each test, allowing you to verify that your actor correctly maintains state between operations. + +## Testing Events + +For actors that emit events, you can verify events are correctly triggered by subscribing to them: + +## Testing Schedules + +Rivet's schedule functionality can be tested by scheduling work and waiting for it to run: + +Use a short schedule and `expect.poll` the action's observable result. Vitest's fake date and fake JavaScript timers do not advance RivetKit's scheduler, which runs outside the test's JavaScript timer queue. + +## Best Practices + +1. **Isolate tests**: Each test should run independently, avoiding shared state. +2. **Test edge cases**: Verify how your actor handles invalid inputs, concurrent operations, and error conditions. +3. **Test scheduled operations**: Poll observable state or output with a bounded timeout instead of sleeping for an exact duration. +4. **Use realistic data**: Test with data that resembles production scenarios. + +`setupTest` starts the registry and disposes the returned client when the test finishes, so you can focus on writing effective tests for your business logic. + +## API Reference + +- [`setupTest`](/typedoc/functions/rivetkit.test_mod.setupTest.html) - Test setup helper function + +_Source doc path: /docs/actors/testing_ diff --git a/.agents/skills/per-tenant-database/reference/actors/troubleshooting.md b/.agents/skills/per-tenant-database/reference/actors/troubleshooting.md new file mode 100644 index 0000000..65b234d --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/actors/troubleshooting.md @@ -0,0 +1,154 @@ +# Troubleshooting + +> Source: `src/content/docs/actors/troubleshooting.mdx` +> Canonical URL: https://rivet.dev/docs/actors/troubleshooting +> Description: Common issues with Rivet Actors and how to resolve them. + +--- +## Common Steps + +Before diving into specific errors, try these general troubleshooting steps: + +- Check your server logs for `level=ERROR` or `level=WARN` messages. +- Check if any of your backend processes have crashed or restarted unexpectedly. +- If you need more diagnostics, set `RIVET_LOG_LEVEL=DEBUG` for verbose logging. See [Logging](/docs/general/logging) for more options. + +## Reporting Issues + +If you're stuck, reach out on [Discord](https://rivet.dev/discord) or file an issue on [GitHub](https://github.com/rivet-dev/rivet/issues). + +When reporting, please include: + +- **Symptoms** + - Whether this is happening in local dev, deployed, or both + - The error you're seeing (screenshot or error message) + - Relevant source code related to the issue +- **What you've tried to solve it** +- **Environment** + - RivetKit version + - Runtime (Node, Bun, etc.) including version + - If applicable, provider in use (e.g. Vercel, Railway, Cloudflare) + - If applicable, HTTP router in use (e.g. Hono, Express, Elysia) + +## Actor status is crashed + +See [Actor Statuses](/docs/actors/statuses) for more about this status. + +The dashboard will show the specific failure reason. Common errors include: + +### `crashed` + +The actor's `run` handler threw an unhandled exception or exited unexpectedly. Check your actor logs for the error message and stack trace. + +### `no_capacity` + +No server was available to run your actor. The cause depends on your [runtime mode](/docs/general/runtime-modes): + +**Serverless**: + +- Your provider configuration does not have the region enabled that the actor is trying to run in. This is uncommon and usually only happens if an actor was created and then the provider config was updated to remove the region. +- There is an issue connecting to your backend. If the engine is hitting `/api/rivet/start` and failing, check your backend logs for errors. + +**Runners**: + +- You don't have enough runners online. Check your runner list in the dashboard to verify runners are visible and connected. +- Your runners are full. Each runner has a limited number of actor slots (configured via `RIVET_TOTAL_SLOTS`, default: 100,000). Check the dashboard to see if your runners have available capacity and scale up if needed. + +### `runner_no_response` + +The server running your actor did not respond in time. This can happen if your server is overloaded or experienced a network issue. Try restarting your server or checking its health. + +### `runner_connection_lost` + +The server running your actor lost its connection to Rivet. This is usually caused by a network interruption or your server restarting. + +### `runner_draining_timeout` + +Your server is shutting down and the actor did not finish in time. Consider handling graceful shutdown in your actor or increasing your shutdown timeout. + +### `concurrent_actor_limit_reached` + +The actor could not be allocated because the concurrent actor limit was reached. Reduce the number of concurrently running actors or increase your limit. + +### `no_envoys` + +No server was available to run your actor. This is equivalent to `no_capacity` on the current allocation path. See the `no_capacity` section above for the causes and fixes for your [runtime mode](/docs/general/runtime-modes). + +### `envoy_no_response` + +The server running your actor did not respond in time. This can happen if your server is overloaded or experienced a network issue. Try restarting your server or checking its health. + +### `envoy_connection_lost` + +The server running your actor lost its connection to Rivet. This is usually caused by a network interruption or your server restarting. + +### `serverless_http_error` + +Your serverless endpoint returned an HTTP error. Common causes: + +- Your backend is returning an error before the actor can start. Check your server logs. +- Your endpoint is behind authentication or a firewall that is blocking Rivet's requests. +- Your serverless function crashed during startup. Check your platform's function logs (e.g. Vercel, Cloudflare). + +### `serverless_connection_error` + +Rivet was unable to connect to your serverless endpoint. Check that: + +- Your backend is deployed and the endpoint URL is correct. +- Your server is publicly reachable from the internet. +- There are no DNS or firewall issues blocking the connection. + +### `serverless_stream_ended_early` + +The connection to your serverless endpoint was terminated before the actor finished. This usually means your serverless function hit its execution time limit. Ensure that your Rivet provider's request lifespan is configured to match the max duration of your serverless platform. + +### `serverless_invalid_sse_payload` + +Rivet received an unexpected response from your serverless endpoint. This typically means something is intercepting or modifying the request before it reaches your RivetKit handler. Check that: + +- Your server routes requests to `registry.start()`, `registry.serve()`, or `registry.handler()` correctly. +- No middleware is modifying the request or response body. + +### `internal_error` + +An unexpected error occurred within Rivet. If this persists, please [contact support](https://rivet.dev/docs). + +## Actors crashing immediately on startup + +If your actors are being created and then immediately destroyed or crashing, there is likely an error being thrown in your `createState` or `onCreate` lifecycle hooks. These hooks run during actor initialization before the actor is marked as ready. + +Check your server logs for the error message and stack trace. Common causes include: + +- An exception thrown in `createState` (e.g. invalid input, failed validation, or a runtime error when computing initial state) +- An exception thrown in `onCreate` (e.g. a failing external API call, missing configuration, or invalid setup logic) + +Fix the error in the relevant lifecycle hook and redeploy. + +## Actors not upgrading to new code + +If your actors are still running old code after deploying a new version, your [versioning](/docs/actors/versions) is likely not configured correctly. + +Without versioning, Rivet has no way to distinguish old deployments from new ones. The behavior depends on your [runtime mode](/docs/general/runtime-modes): + +- **Serverless**: Old requests may still be open from the previous deployment, so actors continue running on the old version's connection until those requests close. +- **Runners**: The old runner container is still running and will continue accepting new actors. New actors may be scheduled on the old runner instead of the new one. + +To fix this, configure a version number in your [registry configuration](/docs/general/registry-configuration). When a new version is deployed, Rivet will allocate new actors to the latest version and optionally drain old actors to migrate them. + +## Actor status is pending + +See [Actor Statuses](/docs/actors/statuses) for more about this status. + +An actor stays in "pending" status when Rivet is waiting for a server to accept it. The cause depends on your [runtime mode](/docs/general/runtime-modes): + +**Serverless**: + +- A region may have been removed from your provider configuration while an existing actor still lives in that region. The actor has no available server to start on because no provider serves that region anymore. Re-add the region to your provider config or destroy the affected actors. +- Check your backend logs for errors on the `/api/rivet/start` endpoint. + +**Runners**: + +- You don't have enough runners online. Check the dashboard to verify your runners are connected. +- Your runners may be at capacity. Check the dashboard to see if runners have available actor slots and scale up if needed. + +_Source doc path: /docs/actors/troubleshooting_ diff --git a/.agents/skills/per-tenant-database/reference/actors/types.md b/.agents/skills/per-tenant-database/reference/actors/types.md new file mode 100644 index 0000000..3e8f9b0 --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/actors/types.md @@ -0,0 +1,46 @@ +# Types + +> Source: `src/content/docs/actors/types.mdx` +> Canonical URL: https://rivet.dev/docs/actors/types +> Description: TypeScript types for working with Rivet Actors. This page covers context types used in lifecycle hooks and actions, as well as helper types for extracting types from actor definitions. + +--- +## Context Types + +Context types define what properties and methods are available in different parts of the actor lifecycle. + +### Extracting Context Types + +When writing helper functions that work with actor contexts, use context extractor types like `CreateContextOf` or `ActionContextOf` to extract the appropriate context type from your actor definition. + +### All Context Types + +Each lifecycle hook and handler has a corresponding `*ContextOf` type, exported from `"rivetkit"`. Pass `typeof myActor` as the type parameter. + +| Hook / Handler | Context Type | +|---|---| +| `createState` | `CreateContextOf` | +| `onCreate` | `CreateContextOf` | +| `createVars` | `CreateVarsContextOf` | +| `createConnState` | `CreateConnStateContextOf` | +| `onBeforeConnect` | `BeforeConnectContextOf` | +| `onConnect` | `ConnectContextOf` | +| `onDisconnect` | `DisconnectContextOf` | +| `onDestroy` | `DestroyContextOf` | +| `onMigrate` | `MigrateContextOf` | +| `onWake` | `WakeContextOf` | +| `onSleep` | `SleepContextOf` | +| `onStateChange` | `StateChangeContextOf` | +| `onBeforeActionResponse` | `BeforeActionResponseContextOf` | +| `actions.*` | `ActionContextOf` | +| `run` | `RunContextOf` | +| `workflow` orchestration helpers (root, `loop`, `try`, `race`, `join` branches) | `WorkflowContextOf` | +| `workflow` `step` / `tryStep` run + rollback helpers | `WorkflowStepContextOf` | +| `onRequest` | `RequestContextOf` | +| `onWebSocket` | `WebSocketContextOf` | + +`ActorContextOf`, `ConnContextOf`, and `ConnInitContextOf` are general-purpose base context types useful for helper functions that don't correspond to a specific hook. + +Workflow context extractors are exported from both `"rivetkit"` and `"rivetkit/workflow"`. + +_Source doc path: /docs/actors/types_ diff --git a/.agents/skills/per-tenant-database/reference/actors/versions.md b/.agents/skills/per-tenant-database/reference/actors/versions.md new file mode 100644 index 0000000..1b45129 --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/actors/versions.md @@ -0,0 +1,241 @@ +# Versions & Upgrades + +> Source: `src/content/docs/actors/versions.mdx` +> Canonical URL: https://rivet.dev/docs/actors/versions +> Description: When you deploy new code, Rivet ensures actors are upgraded seamlessly without downtime. + +--- +## How Versions Work + +Each runner has a **version number**. When you deploy new code with a new version, Rivet handles the transition automatically: + +- **New actors go to the newest version**: When allocating actors, Rivet always prefers runners with the highest version number +- **Multiple versions can coexist**: Old actors continue running on old versions while new actors are created on the new version +- **Drain old actors**: When enabled, a runner connecting with a newer version number will gracefully stop old actors to be rescheduled to the new version + +Versions are not configured by default. See [Registry Configuration](/docs/general/registry-configuration) to learn how to configure the runner version. + +`RIVET_ENVOY_VERSION` is only needed when self-hosting or using a custom runner. Rivet Compute handles versioning automatically. + +### Example Scenario + +### Drain Enabled + +When a new version is deployed, existing actors are gracefully stopped on the old runner and rescheduled onto the new version. + +```mermaid +sequenceDiagram + participant R1 as Runner v1 + participant R2 as Runner v2 + + Note over R1: Currently running + Note over R2: Deployed + R2->>R1: Drain old actors + R1->>R2: Reschedule actors + Note over R1: Shut down when all actors stopped +``` + +### Drain Disabled + +When a new version is deployed, both versions coexist. New actors are created on the new version while existing actors continue running on the old version until. + +```mermaid +sequenceDiagram + participant R1 as Runner v1 + participant R2 as Runner v2 + + Note over R1: Currently running + Note over R2: Deployed + Note over R1: Actor 1 sleeps from inactivity + Note over R2: Actor 1 wakes up when prompted +``` + +## Configuration + +### Setting the Version + +Configure the runner version using an environment variable or programmatically: + +```bash {{"title": "Environment Variable"}} +RIVET_ENVOY_VERSION=2 +``` + +The version **must** be set at build time, not at runtime. Do not use `Date.now()` or similar runtime values in your registry setup code. This would assign a different version every time the server starts, causing actors to be drained and rescheduled on every restart instead of only on new deployments. + +### Example Configurations + +We recommend injecting a build-time value that increments with every deployment. Here are concrete examples for common setups: + +### Dockerfile + +Generate the version at build time and bake it into the image as an environment variable: + +```bash @nocheck +docker build --build-arg RIVET_ENVOY_VERSION=$(date +%s) . +``` + +```dockerfile @nocheck +FROM node:20-slim +ARG RIVET_ENVOY_VERSION +ENV RIVET_ENVOY_VERSION=$RIVET_ENVOY_VERSION +WORKDIR /app +COPY . . +RUN npm install && npm run build +CMD ["node", "dist/server.js"] +``` + +All containers from this image will share the same version. + +### Next.js + +Set the version in `next.config.ts`. Next.js evaluates this file once at build time and inlines the value into the bundle: + +```typescript @nocheck +import type { NextConfig } from "next"; + +const nextConfig: NextConfig = { + env: { + RIVET_ENVOY_VERSION: String(Math.floor(Date.now() / 1000)), + }, +}; + +export default nextConfig; +``` + +### Vite + +Use `define` in your Vite config. This is evaluated once at build time and inlined into the bundle: + +```typescript @nocheck +import { defineConfig } from "vite"; + +export default defineConfig({ + define: { + "process.env.RIVET_ENVOY_VERSION": JSON.stringify( + String(Math.floor(Date.now() / 1000)) + ), + }, +}); +``` + +### CI/CD + +Set the version from your CI pipeline: + +```yaml @nocheck +# GitHub Actions +env: + RIVET_ENVOY_VERSION: ${{ github.run_number }} +``` + +```bash @nocheck +# Railway / Render / generic CI +export RIVET_ENVOY_VERSION=$(date +%s) +``` + +```bash @nocheck +# Git commit count +export RIVET_ENVOY_VERSION=$(git rev-list --count HEAD) +``` + +### Build Script + +Generate a version file during your build step and import it: + +```json @nocheck +{ + "scripts": { + "build": "echo \"export const BUILD_VERSION = $(date +%s);\" > src/build-version.ts && tsc" + } +} +``` + +```typescript @nocheck +import { actor, setup } from "rivetkit"; +import { BUILD_VERSION } from "./build-version"; + +const myActor = actor({ state: {}, actions: {} }); + +const registry = setup({ + use: { myActor }, + envoy: { + version: BUILD_VERSION, + }, +}); +``` + +### Drain on Version Upgrade + +The `drainOnVersionUpgrade` option controls whether old actors are stopped when a new version is deployed. This is configured in the Rivet dashboard under your runner configuration. See [Pool Configuration](/docs/general/pool-configuration) for the full set of pool options, including how to rate-limit actor eviction during the drain. + +| Value | Behavior | +|-------|----------| +| `false` | Old actors continue running. New actors go to new version. Versions coexist. | +| `true` (default) | Old actors receive stop signal and have 30m to finish gracefully. | + +## Upgrading Actor State + +When you deploy a new version, existing actors may need to handle schema changes in their persisted data. + +### SQLite (recommended for complex schemas) + +**Drizzle (recommended)** + +Use [Drizzle](/docs/actors/sqlite-drizzle) for typed schemas with generated migrations. Drizzle generates versioned `.sql` migration files from your TypeScript schema and applies them in order automatically. This is the recommended approach when your schema evolves frequently. + +**Raw SQL** + +For actors using [raw SQLite](/docs/actors/sqlite), migrations run automatically via the `onMigrate` hook on every actor start. RivetKit wraps the hook in a SQLite savepoint, so the migration is fully atomic. Use SQLite's `user_version` pragma to track which migrations have run: + +### In-memory state (`c.state`) + +If you use `c.state` for persistence, you are responsible for handling schema changes yourself. If you add, remove, or rename fields between versions, your code must handle the old shape gracefully. + +**Manual defaults in `onWake`** + +Apply defaults for missing fields: + +**Zod schema coercion** + +Use [Zod](https://zod.dev/) to parse persisted state on wake. Zod's `.default()` fills in missing fields automatically, so old actor state is coerced to the current schema: + +For anything beyond simple defaults, consider moving to [SQLite](/docs/actors/sqlite) where you get proper migration tooling. + +## Advanced + +### How Version Upgrade Detection Works + +When `drainOnVersionUpgrade` is enabled, Rivet uses two mechanisms to detect version changes: + +- **New runner connection**: When a runner connects with a newer version number, the engine immediately drains all older runners with the same name. This is the primary mechanism for [runner mode](/docs/general/runtime-modes) deployments. +- **Metadata polling** (serverless only): In [serverless mode](/docs/general/runtime-modes), runners periodically poll the engine to check for newer versions and self-drain if one is found. This ensures old runners drain even if no new requests trigger a runner connection. + +### SIGTERM Handling + +When a runner process receives SIGTERM, it gracefully stops all actors before exiting: + +- Each actor's `onSleep` hook is called, giving it time to save state +- Actors are rescheduled to other available runners +- The runner waits up to **30 minutes** for all actors to finish stopping +- If the process is force-killed before actors finish (e.g. SIGKILL), actors are rescheduled with a crash backoff penalty instead of a clean handoff + +Actors have a maximum of 30 minutes to clean up during shutdown. Ensure your platform's drain grace period is at most 30 minutes. + +### Shutdown Timeouts + +Several timeouts control how long each part of the shutdown process can take: + +| Timeout | Default | Description | Configuration | +|---------|---------|-------------|---------------| +| `actor_stop_threshold` | 30m | Engine-side limit on how long each actor has to stop before being marked lost | [Engine config](/docs/self-hosting/configuration) (`pegboard.actor_stop_threshold`) | +| `sleepGracePeriod` | 15s | Total graceful sleep budget for `onSleep`, `waitUntil`, `keepAwake`, and async raw WebSocket handlers | [Actor options](/docs/actors/lifecycle#options) | +| `runner_lost_threshold` | 15s | Fallback detection if the runner dies without graceful shutdown | [Engine config](/docs/self-hosting/configuration) (`pegboard.runner_lost_threshold`) | + +Rivet has a max shutdown grace period of 30 minutes that cannot be configured. + +## Related + +- [Runtime Modes](/docs/general/runtime-modes): Serverless vs runner deployment modes +- [Lifecycle](/docs/actors/lifecycle): Actor lifecycle hooks including `onSleep` + +_Source doc path: /docs/actors/versions_ diff --git a/.agents/skills/per-tenant-database/reference/actors/websocket-handler.md b/.agents/skills/per-tenant-database/reference/actors/websocket-handler.md new file mode 100644 index 0000000..f94499d --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/actors/websocket-handler.md @@ -0,0 +1,201 @@ +# Low-Level WebSocket Handler + +> Source: `src/content/docs/actors/websocket-handler.mdx` +> Canonical URL: https://rivet.dev/docs/actors/websocket-handler +> Description: Actors can handle WebSocket connections through the `onWebSocket` handler. + +--- +For most use cases, [actions](/docs/actors/actions) and [events](/docs/actors/events) provide high-level connection handling powered by WebSockets that's easier to work with than low-level WebSockets. However, low-level handlers are required when implementing custom use cases. + +## Handling WebSocket Connections + +The `onWebSocket` handler manages low-level WebSocket connections. It receives the actor context and a `WebSocket` object. + +See also the [raw WebSocket handler example](https://github.com/rivet-dev/rivet/tree/main/examples/raw-websocket-handler). + +## Connecting To Actors + +### Via RivetKit Client + +Use the `.webSocket()` method on an actor handle to open a WebSocket connection to the actor's `onWebSocket` handler. This can be executed from either your frontend or backend. + +```typescript index.ts @hide @nocheck +import { actor, setup } from "rivetkit"; + +export const chat = actor({ + state: { messages: [] as string[] }, + onWebSocket: (c, websocket) => { + websocket.addEventListener("message", (event) => { + c.state.messages.push(event.data as string); + }); + }, + actions: {} +}); + +export const registry = setup({ use: { chat } }); +registry.start(); +``` + +```typescript client.ts @nocheck +import { createClient } from "rivetkit/client"; +import type { registry } from "./index"; + +const client = createClient("http://localhost:6420"); + +const actor = client.chat.getOrCreate(["my-chat"]); + +// Open WebSocket connection +const ws = await actor.webSocket("/"); + +// Listen for messages +ws.addEventListener("message", (event) => { + const message = JSON.parse(event.data as string); + console.log("Received:", message); +}); + +// Send messages +ws.send(JSON.stringify({ type: "chat", text: "Hello!" })); +``` + +The `.webSocket()` method returns a standard WebSocket. + +### Via getGatewayUrl + +Use `.getGatewayUrl()` to get the raw gateway URL for the actor. This is useful when you need to use the URL with external tools or custom WebSocket clients. + +```typescript index.ts @hide @nocheck +import { actor, setup } from "rivetkit"; + +export const chat = actor({ + state: { messages: [] as string[] }, + onWebSocket: (c, websocket) => { + websocket.addEventListener("message", (event) => { + c.state.messages.push(event.data as string); + }); + }, + actions: {} +}); + +export const registry = setup({ use: { chat } }); +registry.start(); +``` + +```typescript client.ts @nocheck +import { createClient } from "rivetkit/client"; +import type { registry } from "./index"; + +const client = createClient("http://localhost:6420"); + +const actor = client.chat.getOrCreate(["my-chat"]); + +// Get the raw gateway URL +const gatewayUrl = await actor.getGatewayUrl(); +// gatewayUrl = "https://...rivet.dev/..." + +// Convert to WebSocket URL and connect +const wsUrl = gatewayUrl.replace("http://", "ws://").replace("https://", "wss://"); +const ws = new WebSocket(`${wsUrl}/websocket/`); + +ws.addEventListener("message", (event) => { + const message = JSON.parse(event.data as string); + console.log("Received:", message); +}); + +ws.addEventListener("open", () => { + ws.send(JSON.stringify({ type: "chat", text: "Hello!" })); +}); +``` + +### Via HTTP API + +This handler can be accessed with raw WebSockets using `wss://api.rivet.dev/gateway/{actorId}@{token}/websocket/{...path}`. + +For example, to connect to the chat actor above: + +```typescript +// Replace with your actor ID and token +const actorId = "your-actor-id"; +const token = "your-token"; + +const ws = new WebSocket( + `wss://api.rivet.dev/gateway/${actorId}@${token}/websocket/` +); + +ws.addEventListener("message", (event) => { + const message = JSON.parse(event.data as string); + console.log("Received:", message); +}); + +ws.addEventListener("open", () => { + ws.send(JSON.stringify({ type: "chat", text: "Hello!" })); +}); +``` + +```bash +wscat -c "wss://api.rivet.dev/gateway/{actorId}@{token}/websocket/" +``` + +The path after `/websocket/` is passed to your `onWebSocket` handler and can be used to route to different functionality within your actor. For example, to connect with a custom path `/admin`: + +```typescript +// Replace with your actor ID and token +const actorId = "your-actor-id"; +const token = "your-token"; + +const ws = new WebSocket( + `wss://api.rivet.dev/gateway/${actorId}@${token}/websocket/admin` +); +``` + +```bash +wscat -c "wss://api.rivet.dev/gateway/{actorId}@{token}/websocket/admin" +``` + +See the [HTTP API reference](/docs/actors/http-api) for more information on WebSocket routing and authentication. + +### Via Proxying Connections + +You can proxy WebSocket connections from your own server to actor handlers using the RivetKit client. This is useful when you need to add custom authentication or connection management before forwarding to actors. + +See also the [raw WebSocket handler with proxy example](https://github.com/rivet-dev/rivet/tree/main/examples/raw-websocket-handler-proxy). + +## Connection & Lifecycle Hooks + +`onWebSocket` will trigger the `onBeforeConnect`, `onConnect`, and `onDisconnect` hooks. Read more about [lifecycle hooks](/docs/actors/lifecycle). + +Open WebSockets will be listed in `c.conns`. `conn.send` and `c.broadcast` have no effect on low-level WebSocket connections. Read more about [connections](/docs/actors/connections). + +## WinterTC Compliance + +The `onWebSocket` handler uses standard WebSocket APIs and will work with existing libraries expecting WinterTC-compliant WebSocket objects. + +## Advanced + +## WebSocket Hibernation + +WebSocket hibernation allows actors to go to sleep while keeping WebSocket connections alive. Actors automatically wake up when a message is received or the connection closes. + +Enable hibernation by setting `canHibernateWebSocket: true`. You can also pass a function `(request) => boolean` for conditional control. + +Since `open` only fires once when the client first connects, use `c.conn.state` to store per-connection data that persists across sleep cycles. See [connections](/docs/actors/connections) for more details. + +### Accessing the Request + +The underlying HTTP request is available via `c.request`. This is useful for accessing the path or query parameters. + +### Skip Ready Wait + +Connections are normally held at the gateway until the actor is ready. Pass `skipReadyWait: true` on `handle.webSocket()` to connect immediately, including while the actor is still starting or in the [sleep grace period](/docs/actors/lifecycle#shutdown-sequence). See [Skip Ready Wait](/docs/clients/javascript#skip-ready-wait) for details. + +### Async Handlers + +The `onWebSocket` handler can be async, allowing you to perform async code before setting up event listeners: + +## API Reference + +- [`WebSocketContext`](/typedoc/interfaces/rivetkit.mod.WebSocketContext.html) - Context for WebSocket handlers +- [`UniversalWebSocket`](/typedoc/interfaces/rivetkit.mod.UniversalWebSocket.html) - Universal WebSocket interface +- [`handleRawWebSocketHandler`](/typedoc/functions/rivetkit.mod.handleRawWebSocketHandler.html) - Function to handle raw WebSocket +- [`UpgradeWebSocketArgs`](/typedoc/interfaces/rivetkit.mod.UpgradeWebSocketArgs.html) - Arguments for WebSocket upgrade + +_Source doc path: /docs/actors/websocket-handler_ diff --git a/.agents/skills/per-tenant-database/reference/actors/workflows.md b/.agents/skills/per-tenant-database/reference/actors/workflows.md new file mode 100644 index 0000000..9c59975 --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/actors/workflows.md @@ -0,0 +1,228 @@ +# Workflows + +> Source: `src/content/docs/actors/workflows.mdx` +> Canonical URL: https://rivet.dev/docs/actors/workflows +> Description: Build durable, replayable run loops in Rivet Actors with steps, queue waits, timers, and rollback. + +--- +Use workflows for durable, multi-step execution with replay safety. + +## What are workflows? + +A workflow is a durable, replayable run handler for a Rivet Actor. + +- Survives restarts: workflow progress is saved automatically. +- Re-runs safely: replay follows the same recorded steps. +- Event-driven: workflows can pause for queue messages, then continue. + +## Getting started + +### Simple workflow + +Use this when you need a short multi-step sequence. + +### Loops + +This is the recommended workflow shape for most actor workloads. + +- Use a queue wait inside the loop to receive the next unit of work. +- Keep actor state changes in a single workflow loop. +- This gives you one durable workflow that manages all actor progress. + +### Setup & teardown + +Use this when the workflow should initialize resources, process queued commands, then clean up. + +## Features + +### Queue + +Use this for fire-and-forget commands where the client does not need a reply. + +Use the `Loops` example above as the baseline pattern. + +### Request/response (using queue) + +Use this when the caller needs a response from queued processing. + +### Timers + +Use queue messages as the trigger source, then sleep durably inside the workflow. + +### Join + +Use `join` when several independent tasks can run in parallel. + +### Race + +Use `race` when you need first-winner behavior. + +### Timeouts + +Use step timeouts and retries for slow or flaky dependencies. + +Step timeouts are critical by default and fail immediately. Set `retryOnTimeout: true` if a timeout should retry like any other error using `maxRetries`. + +Workflows use roll-forward semantics everywhere. When a step throws, any `state` or `vars` mutations it made before failing are never rolled back, whether the step retries or the failure is caught by `tryStep` or `try`. The next attempt observes whatever the failed attempt already wrote, so write steps idempotently: check before you increment, or move the mutation after the fallible work. + +### Handling terminal failures as data + +Use `tryStep` when a step failure should produce data instead of failing the whole workflow. + +Use `try` when you want to recover from terminal `step`, `join`, or `race` failures inside a named block. + +```ts +async function runPaymentFlow(ctx: any) { + return await ctx.try("payment-flow", async (blockCtx: any) => { + const auth = await blockCtx.step("authorize", async (blockCtx) => + authorizeOrder("order-123"), + ); + const capture = await blockCtx.step("capture", async (blockCtx) => + captureOrder("order-123"), + ); + return { auth, capture }; + }); +} + +async function authorizeOrder(orderId: string): Promise { + return `auth-${orderId}`; +} + +async function captureOrder(orderId: string): Promise { + return `capture-${orderId}`; +} +``` + +- `tryStep` and `try` only catch terminal failures. Retry backoff, sleeps, queue waits, eviction, and history divergence still rethrow. +- Catching a failure does not undo it. `state` and `vars` mutations made before the failure remain visible after `tryStep` or `try` returns, so use explicit compensating steps when a caught failure needs cleanup. +- `RollbackError` is not caught by default. Pass `catch: ["rollback"]` when you want rollback failures returned as data. + +### Error hooks + +Use `onError` when you want a best-effort notification for workflow failures. + +- Step failures include the attempt number, retry counts, whether the step will retry, and the next retry delay. +- Workflow failures also include terminal errors outside steps, such as rollback failures or code/history mismatches. +- The hook is observational. It is not part of workflow replay, so use it for logging, metrics, or updating non-critical actor state. +- This is also a good place to forward workflow failures to Sentry or another error reporting pipeline. + +### Rollback + +Use rollback checkpoints before steps that have compensating actions. + +## Patterns + +### Store workflow progress in state + broadcast + +Store progress in `state` so replay and recovery always restore it. Broadcast state changes so clients can render progress in realtime. + +### Cron (queue-driven) + +Rivet scheduling triggers actions. For cron-like workflows, use a small scheduled action as a bridge that enqueues work, then process that work in the workflow loop. + +These are common workflow shapes used in production systems. + +### Queue-driven worker + +Use this when external systems enqueue work and the actor should process each item durably. + +### Setup & teardown + +Use this when you need one-time initialization before a long-lived loop, plus cleanup when the actor stops sleeping or is destroyed. + +### Human approval gate + +Use this when an operation must pause for a user or system decision before continuing. + +### Fan-out / fan-in (join) + +Use this when independent work items can run in parallel and you need a single merged result. + +### Batch drainer + +Use this when throughput matters and handling one message at a time is too expensive. + +### Coordinator -> worker RPC + +Use this when one actor orchestrates work by calling actions on other actors. + +### Request/response over queue (async RPC) + +Use this when you want decoupled actor-to-actor communication with durable waits and explicit completion. + +### Scatter-gather across actors + +Use this when multiple actors can process independent parts of a request in parallel, then return a merged response. + +### Timeout + fallback actor + +Use this when a primary actor call might be slow or unavailable and you need a deterministic fallback path. + +### Cross-actor saga (compensating actions) + +Use this when a workflow spans multiple actors and each side effect may need compensation. + +### Signal-driven control loop + +Use this when workflow progress should be triggered by commands/events instead of fixed polling intervals. + +### Poll + backoff loop + +Use this when an external dependency has variable availability and retries should slow down after failures. + +### Child worker orchestration + +Use this when one workflow coordinates many child workers (actors or worker workflows) and manages their lifecycle. + +### Bounded drain + concurrency cap + +Use this when inbound work can spike and you need predictable per-iteration limits. + +### Versioned workflow evolution + +Use this when workflow structure changes across deployments and old histories must still replay. + +### Version gates with `getVersion` + +Use `ctx.getVersion(name, latest)` to branch behavior when you change a workflow's logic while old instances are still in flight. It returns the version this instance is pinned to at that point: + +- A fresh instance resolves to `latest`. +- An instance that already executed past this point under older code resolves to `1` (the implicit floor). + +The resolved version is recorded in history, so replays are deterministic and each instance stays on the branch it started on. Inside a loop, every iteration resolves independently, so in-flight iterations finish on the old branch while new iterations pick up `latest`. + +`latest` must be an integer `>= 1`, and the gate name must be unique within its scope like any other entry. Once every old instance has drained, retire the gate by replacing the call with `ctx.removed(name, "version_check")`. + +### Checkpoint-friendly loop design + +Use this when you need reliable replay and resume semantics across crashes and restarts. + +## Migrations + +- Keep workflow entry names stable once deployed. +- If an old entry was removed or renamed, call `ctx.removed(name, originalType)`. +- To change behavior at a point while old instances are still running, gate it with `ctx.getVersion(name, latest)` (see [Version gates](#version-gates-with-getversion)). +- This keeps replay compatible across deployments. + +## Step-only access to actor APIs + +`state`, `vars`, `db`, `client()`, and connection/event APIs are only valid inside `ctx.step(...)` callbacks. + +Use non-step workflow code for orchestration only: queue waits, sleeps, loops, joins, races, and rollback boundaries. Keep actor-local side effects in steps. + +## Debugging + +- `GET /inspector/workflow-history` returns workflow history status for an actor. +- Response includes `isWorkflowEnabled` and `history`. +- In non-dev mode, inspector endpoints require authorization. + +## Recommendations + +- Prefer queue-driven loops for long-lived workflows. +- Structure long-lived workflows with setup and teardown around the main loop. +- Keep actor state changes and side effects inside steps. +- Store workflow progress in `state` and broadcast updates as progress changes. +- Use timeouts and rollback for external side effects. +- Write step bodies idempotently. `state` and `vars` mutations from a failed attempt are never rolled back, whether the step retries or `tryStep`/`try` catches the failure. + +_Source doc path: /docs/actors/workflows_ diff --git a/.agents/skills/per-tenant-database/reference/cli.md b/.agents/skills/per-tenant-database/reference/cli.md new file mode 100644 index 0000000..34880ea --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/cli.md @@ -0,0 +1,103 @@ +# CLI + +> Source: `src/content/docs/cli.mdx` +> Canonical URL: https://rivet.dev/docs/cli +> Description: Reference for the optional rivet CLI: deploy to Rivet Compute and run local dev for serverless platforms. + +--- +The `rivet` CLI (`@rivetkit/cli`) is optional. You only need it for: + +| Use case | Command | +| --- | --- | +| Deploy to Rivet Compute | `rivet deploy` | +| Local dev for serverless platforms (Cloudflare, Supabase) | `rivet dev --provider ` | + +Run it with your package runner: + +```bash +npx @rivetkit/cli +``` + +| Command | Description | +| --- | --- | +| `rivet dev` | Run a local engine and your handler's dev server. | +| `rivet deploy` | Build and deploy the project to Rivet Cloud. | +| `rivet engine` | Run the bundled `rivet-engine` binary directly. | +| `rivet setup-ci` | Install the GitHub Actions deploy workflow. | + +## `rivet dev` + +Starts a local engine, spawns your dev server, and registers the serverless runner pointing at it. The engine keeps running across restarts; Ctrl-C stops only the dev server. + +```bash +rivet dev [--provider ] [--port N] [--fn-name NAME] [--url URL] [-- ...] +``` + +`--provider` selects how the dev server is launched. Anything after `--` is appended to the preset command (or is the command to run when no provider is set). + +| `--provider` | Spawns | Port | +| --- | --- | --- | +| _(omitted)_ | your `-- ` (needs `--port`) | from `--port` | +| `serverless` | your `-- ` (gets `PORT`) | auto | +| `cloudflare` | `wrangler dev` | `8787` | +| `supabase` | `supabase functions serve` | `54321` | +| `none` | nothing (engine only) | — | + +For `cloudflare`, the CLI also passes the engine endpoint as `--var RIVET_ENDPOINT:...`, so the Worker connects back with no `wrangler.toml` config. + +| Flag | Description | +| --- | --- | +| `--port` | Handler port. Required without a provider unless `--url` is set. | +| `--fn-name` | Supabase function name (default `rivet`). | +| `--url` | Explicit handler URL, overriding port and path. | +| `--engine-binary` | Path to a `rivet-engine` binary. | + +## `rivet deploy` + +Builds and pushes your project's Docker image and upserts the managed pool, printing the dashboard URL. See [Deploying to Rivet Compute](/docs/deploy/rivet-compute). + +```bash +rivet deploy --token cloud_api_xxxxx +``` + +The token is saved to `~/.rivet/credentials` (also read from `RIVET_CLOUD_TOKEN`), so later deploys can omit it. + +| Flag | Default | Description | +| --- | --- | --- | +| `--token` | env / credentials | Rivet Cloud API token. | +| `--namespace` | `production` | Cloud namespace. | +| `--project` / `--org` | from token | Override project/org. | +| `--dockerfile` | `Dockerfile` | Dockerfile to build. | +| `--build-context` | `.` | Docker build context. | +| `--env KEY=VAL` | — | Environment override, repeatable. | +| `--image` | project slug | Image repository name. | +| `--tag` | git short SHA | Image tag. | + +## `rivet engine` + +Runs the bundled `rivet-engine` binary directly, against the same local database and ports as `rivet dev`. Arguments are forwarded verbatim. + +```bash +rivet engine nuke # wipe local engine state +rivet engine wf list # inspect workflows +``` + +## `rivet setup-ci` + +Installs `.github/workflows/rivet-deploy.yml`, which deploys to Rivet Cloud on push and pull request. Add `--force` to overwrite. Then set the token secret: + +```bash +gh secret set RIVET_CLOUD_TOKEN +``` + +## Engine binary resolution + +`rivet dev` and `rivet engine` resolve the `rivet-engine` binary from, in order: `--engine-binary`, `RIVET_ENGINE_BINARY_PATH`, a binary bundled next to the CLI, a local `target/{debug,release}` build, then an auto-downloaded release. Set `RIVETKIT_ENGINE_AUTO_DOWNLOAD=0` to require a local binary. + +## Related + +- [Deploying to Rivet Compute](/docs/deploy/rivet-compute) +- [Cloudflare Workers Quickstart](/docs/actors/quickstart/cloudflare) +- [Supabase Functions Quickstart](/docs/actors/quickstart/supabase) + +_Source doc path: /docs/cli_ diff --git a/.agents/skills/per-tenant-database/reference/clients/javascript.md b/.agents/skills/per-tenant-database/reference/clients/javascript.md new file mode 100644 index 0000000..689d79b --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/clients/javascript.md @@ -0,0 +1,120 @@ +# Node.js & Bun + +> Source: `src/content/docs/clients/javascript.mdx` +> Canonical URL: https://rivet.dev/docs/clients/javascript +> Description: Connect JavaScript apps to Rivet Actors. + +--- +## Getting Started + +See the [backend quickstart guide](/docs/actors/quickstart/backend) for getting started. + +## Minimal Client + +## Stateless vs Stateful + +## Getting Actors + +## Connection Parameters + +Use `params` for static connection parameters. Use `getParams` when the value can change between connection attempts, such as refreshing a JWT before each `.connect()` or reconnect. + +## Subscribing to Events + +## Connection Lifecycle + +## Low-Level HTTP & WebSocket + +For actors that implement `onRequest` or `onWebSocket`, call them directly: + +```ts @nocheck +import { createClient } from "rivetkit/client"; + +const client = createClient(); +const handle = client.chatRoom.getOrCreate(["general"]); + +const response = await handle.fetch("history"); +const history = await response.json(); + +const ws = await handle.webSocket("stream"); +ws.addEventListener("message", (event) => { + console.log("message:", event.data); +}); +ws.send("hello"); +``` + +## Calling from Backend + +## Error Handling + +## Concepts + +### Keys + +Keys uniquely identify actor instances. Use compound keys (arrays) for hierarchical addressing: + +Don't build keys with string interpolation like `"org:${userId}"` when `userId` contains user data. Use arrays instead to prevent key injection attacks. + +### Environment Variables + +`createClient()` automatically reads: + +- `RIVET_ENDPOINT` (endpoint) +- `RIVET_NAMESPACE` +- `RIVET_TOKEN` +- `RIVET_RUNNER` + +Defaults to `http://localhost:6420` when unset. RivetKit runs on port 6420 by default. + +### Endpoint Format + +Endpoints support URL auth syntax: + +``` +https://namespace:token@api.rivet.dev +``` + +You can also pass the endpoint without auth and provide `RIVET_NAMESPACE` and `RIVET_TOKEN` separately. For serverless deployments, use your app's `/api/rivet` URL. See [Endpoints](/docs/general/endpoints#url-auth-syntax) for details. + +## Advanced + +### Skip Ready Wait + +Requests are normally held at the gateway until the actor is ready to accept traffic. An actor is not ready while it's still starting (before `onWake` finishes) or while it's in the [sleep grace period](/docs/actors/lifecycle#shutdown-sequence) (running `onSleep`, `waitUntil`, and pending disconnects). + +Pass `skipReadyWait: true` on the [low-level HTTP and WebSocket APIs](#low-level-http--websocket) to deliver immediately and reach the actor's `onRequest` / `onWebSocket` handler in either window: + +```ts @nocheck +import { createClient } from "rivetkit/client"; + +const client = createClient(); +const handle = client.chatRoom.getOrCreate(["general"]); + +const response = await handle.fetch("/healthz", { + skipReadyWait: true, +}); + +const ws = await handle.webSocket("probe", undefined, { + skipReadyWait: true, +}); +``` + +Requests can still return transient lifecycle or gateway errors. Retry once the actor is available again. + +- `actor.stopping`: the actor has fully stopped, i.e. the sleep grace period has ended but it has not yet restarted. +- `guard.actor_stopped_while_waiting`: the request reached the actor tunnel, but the actor stopped before the gateway received a response. +- `guard.tunnel_request_aborted`: the actor tunnel aborted the request before a response started. +- `guard.tunnel_message_timeout`: the gateway dropped the in-flight tunnel request after its tunnel message timeout. +- `guard.tunnel_response_closed`: the actor tunnel closed before sending a response. +- `guard.gateway_response_start_timeout`: the gateway timed out waiting for the actor response to start. + +## API Reference + +**Package:** [rivetkit](https://www.npmjs.com/package/rivetkit) + +See the [RivetKit client overview](/docs/clients). + +- [`createClient`](/typedoc/functions/rivetkit.client_mod.createClient.html) - Create a client +- [`Client`](/typedoc/types/rivetkit.mod.Client.html) - Client type + +_Source doc path: /docs/clients/javascript_ diff --git a/.agents/skills/per-tenant-database/reference/clients/react.md b/.agents/skills/per-tenant-database/reference/clients/react.md new file mode 100644 index 0000000..9b976e2 --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/clients/react.md @@ -0,0 +1,88 @@ +# React + +> Source: `src/content/docs/clients/react.mdx` +> Canonical URL: https://rivet.dev/docs/clients/react +> Description: Connect React apps to Rivet Actors. + +--- +## Getting Started + +See the [React quickstart guide](/docs/actors/quickstart/react) for getting started. + +## Install + +## Minimal Client + +## Stateless vs Stateful + +## Getting Actors + +## Connection Parameters + +## Subscribing to Events + +## Connection Lifecycle + +## Low-Level HTTP & WebSocket + +Use the JavaScript client for raw HTTP and WebSocket access: + +```tsx @nocheck +import { createClient } from "rivetkit/client"; + +const client = createClient(); +const handle = client.chatRoom.getOrCreate(["general"]); + +const response = await handle.fetch("history"); +const history = await response.json(); + +const ws = await handle.webSocket("stream"); +ws.addEventListener("message", (event) => { + console.log("message:", event.data); +}); +ws.send("hello"); +``` + +## Calling from Backend + +Use the JavaScript client on your backend (Node.js/Bun). See the [JavaScript client docs](/docs/clients/javascript). + +## Error Handling + +## Concepts + +### Keys + +Keys uniquely identify actor instances. Use compound keys (arrays) for hierarchical addressing: + +Don't build keys with string interpolation like `"org:${userId}"` when `userId` contains user data. Use arrays instead to prevent key injection attacks. + +### Environment Variables + +`createRivetKit()` (and the underlying `createClient()` instance) automatically read: + +- `RIVET_ENDPOINT` +- `RIVET_NAMESPACE` +- `RIVET_TOKEN` +- `RIVET_RUNNER` + +Defaults to `http://localhost:6420` when unset. RivetKit runs on port 6420 by default. + +### Endpoint Format + +Endpoints support URL auth syntax: + +``` +https://namespace:token@api.rivet.dev +``` + +You can also pass the endpoint without auth and provide `RIVET_NAMESPACE` and `RIVET_TOKEN` separately. For serverless deployments, use your app's `/api/rivet` URL. See [Endpoints](/docs/general/endpoints#url-auth-syntax) for details. + +## API Reference + +**Package:** [@rivetkit/react](https://www.npmjs.com/package/@rivetkit/react) + +- [`createRivetKit`](/docs/clients/react) - Create hooks for React +- [`useActor`](/docs/clients/react) - Hook for actor instances + +_Source doc path: /docs/clients/react_ diff --git a/.agents/skills/per-tenant-database/reference/clients/rust.md b/.agents/skills/per-tenant-database/reference/clients/rust.md new file mode 100644 index 0000000..32bebfc --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/clients/rust.md @@ -0,0 +1,367 @@ +# Rust (Beta) + +> Source: `src/content/docs/clients/rust.mdx` +> Canonical URL: https://rivet.dev/docs/clients/rust +> Description: Connect Rust apps to Rivet Actors. + +--- +Rust support is in beta. The supported public Rust API is `rivetkit` and `rivetkit::client`; lower-level crates are internal implementation details and do not carry a stability guarantee. See the full API reference on [docs.rs/rivetkit](https://docs.rs/rivetkit), or the runnable [`hello-world-rust`](https://github.com/rivet-dev/rivet/tree/main/examples/hello-world-rust) example. + +## Getting Started + +See the [Rust quickstart guide](/docs/actors/quickstart/rust) for getting started. + +## Install + +Add the `rivetkit` crate and its companions: + +```sh +cargo add rivetkit anyhow async-trait +cargo add serde --features derive +cargo add tokio --features full +``` + +The Rust client is strongly typed. It shares the same action and event types as your actor, so define your actor in `src/lib.rs` and import those types from both your server and your client. There is no need to redefine the actor on the client. See [Define Your Actor](/docs/actors/quickstart/rust#define-your-actor) in the quickstart for the actor definition this page builds on. + +## Minimal Client + +```rust @nocheck +use counter::{Counter, Increment}; +use rivetkit::{ + client::{Client, ClientConfig}, + prelude::*, + TypedClientExt, +}; + +#[tokio::main] +async fn main() -> Result<()> { + let client = Client::new(ClientConfig::new("http://localhost:6420").namespace("default")); + + let counter = client.get_or_create_typed_default::("counter", ["my-counter"])?; + let count = counter.send(Increment { amount: 1 }).await?; + println!("New count: {count}"); + + Ok(()) +} +``` + +`counter` here is your crate name (the package `name` in `Cargo.toml`, with dashes as underscores). `Counter` and `Increment` are the types you defined alongside your actor. + +## Stateless vs Stateful + +```rust @nocheck +use counter::{Counter, Increment, NewCount}; +use rivetkit::{ + client::{Client, ClientConfig}, + prelude::*, + TypedClientExt, +}; + +#[tokio::main] +async fn main() -> Result<()> { + let client = Client::new(ClientConfig::new("http://localhost:6420").namespace("default")); + let counter = client.get_or_create_typed_default::("counter", ["my-counter"])?; + + // Stateless: each call is independent + counter.send(Increment { amount: 1 }).await?; + + // Stateful: keep a connection open for realtime events + let connection = counter.connect(); + connection + .on::(|event| println!("count: {}", event.count)) + .await; + connection.send(Increment { amount: 1 }).await?; + + connection.disconnect().await; + Ok(()) +} +``` + +A stateless call on the handle opens a short-lived request per action. A connection keeps a WebSocket open so you can receive events and reuse it across calls. + +## Getting Actors + +```rust @nocheck +use counter::Counter; +use rivetkit::{ + client::{Client, ClientConfig, GetOrCreateOptions}, + prelude::*, + TypedClientExt, +}; +use serde_json::json; + +#[tokio::main] +async fn main() -> Result<()> { + let client = Client::new(ClientConfig::new("http://localhost:6420").namespace("default")); + + // Get or create an actor + let room = client.get_or_create_typed_default::("counter", ["room-42"])?; + + // Get an existing actor handle (fails when used if the actor does not exist) + let existing = client.get_typed_default::("counter", ["room-42"])?; + + // Create a new actor with input + let created = client.get_or_create_typed::( + "counter", + ["game-1"], + GetOrCreateOptions { + create_with_input: Some(json!({ "mode": "ranked" })), + ..Default::default() + }, + )?; + + // Get an actor handle by ID + let by_id = client.get_for_id("counter", "actor-id", Default::default())?; + + // Resolve the actor ID + let resolved_id = room.inner().resolve().await?; + println!("Resolved ID: {resolved_id}"); + + Ok(()) +} +``` + +`get_typed_default` / `get_or_create_typed_default` use default options. The non-default variants (`get_typed` / `get_or_create_typed`) take `GetOptions` / `GetOrCreateOptions` for connection parameters, input, and region. + +## Connection Parameters + +Pass connection parameters through the handle options. They are delivered to the actor's `create_conn_state` callback: + +```rust @nocheck +use counter::Counter; +use rivetkit::{ + client::{Client, ClientConfig, GetOrCreateOptions}, + prelude::*, + TypedClientExt, +}; +use serde_json::json; + +#[tokio::main] +async fn main() -> Result<()> { + let client = Client::new(ClientConfig::new("http://localhost:6420").namespace("default")); + + let chat = client.get_or_create_typed::( + "counter", + ["general"], + GetOrCreateOptions { + params: Some(json!({ "authToken": "jwt-token-here" })), + ..Default::default() + }, + )?; + + let connection = chat.connect(); + connection.disconnect().await; + Ok(()) +} +``` + +## Subscribing to Events + +`on` registers a typed callback for an event and returns once the subscription is registered: + +```rust @nocheck +use counter::{Counter, NewCount}; +use rivetkit::{ + client::{Client, ClientConfig}, + prelude::*, + TypedClientExt, +}; + +#[tokio::main] +async fn main() -> Result<()> { + let client = Client::new(ClientConfig::new("http://localhost:6420").namespace("default")); + let connection = client + .get_or_create_typed_default::("counter", ["general"])? + .connect(); + + connection + .on::(|event| println!("count changed: {}", event.count)) + .await; + + Ok(()) +} +``` + +Event callbacks are synchronous and run for every matching event. The actor's emitted event type (here `NewCount`) is decoded into the typed value for you. + +## Connection Lifecycle + +The lower-level connection exposes lifecycle callbacks and the current status. Reach it with `connection.inner()`: + +```rust @nocheck +use counter::Counter; +use rivetkit::{ + client::{Client, ClientConfig}, + prelude::*, + TypedClientExt, +}; + +#[tokio::main] +async fn main() -> Result<()> { + let client = Client::new(ClientConfig::new("http://localhost:6420").namespace("default")); + let connection = client + .get_or_create_typed_default::("counter", ["general"])? + .connect(); + let inner = connection.inner().clone(); + + inner.on_open(|| println!("connected")).await; + inner.on_close(|| println!("disconnected")).await; + inner.on_error(|message| eprintln!("error: {message}")).await; + inner + .on_status_change(|status| println!("status: {status:?}")) + .await; + + println!("current status: {:?}", inner.conn_status()); + + connection.disconnect().await; + Ok(()) +} +``` + +`ConnectionStatus` is one of `Idle`, `Connecting`, `Connected`, or `Disconnected`. Connections reconnect automatically with backoff until you call `disconnect`. + +## Low-Level HTTP & WebSocket + +For actors that implement `on_request` or `on_websocket`, call them directly on the untyped handle (`handle.inner()`). `fetch` returns a `reqwest::Response`, and `web_socket` returns a `tokio_tungstenite` stream. This example also needs a few extra crates: + +```sh +cargo add futures-util tokio-tungstenite +cargo add reqwest --features json +``` + +```rust @nocheck +use counter::Counter; +use futures_util::{SinkExt, StreamExt}; +use reqwest::{header::HeaderMap, Method}; +use rivetkit::{ + client::{Client, ClientConfig}, + prelude::*, + TypedClientExt, +}; +use tokio_tungstenite::tungstenite::Message; + +#[tokio::main] +async fn main() -> Result<()> { + let client = Client::new(ClientConfig::new("http://localhost:6420").namespace("default")); + let handle = client.get_or_create_typed_default::("counter", ["general"])?; + + // Raw HTTP request + let response = handle + .inner() + .fetch("history", Method::GET, HeaderMap::new(), None) + .await?; + let history: Vec = response.json().await?; + println!("history: {history:?}"); + + // Raw WebSocket connection + let mut ws = handle.inner().web_socket("stream", None).await?; + ws.send(Message::text("hello")).await?; + if let Some(message) = ws.next().await { + println!("received: {:?}", message?); + } + + Ok(()) +} +``` + +## Calling from Backend + +The client is a normal Tokio type, so you can hold it in your backend (Axum, Actix, etc.) and call actors from request handlers. The client is `Clone` and cheap to share: + +```rust @nocheck +use counter::{Counter, Increment}; +use rivetkit::{ + client::{Client, ClientConfig}, + prelude::*, + TypedClientExt, +}; + +async fn increment(client: Client) -> Result { + let counter = client.get_or_create_typed_default::("counter", ["server-counter"])?; + let count = counter.send(Increment { amount: 1 }).await?; + Ok(count) +} +``` + +## Error Handling + +Action and connection calls return `anyhow::Result`. Actor-side errors surface as an `anyhow::Error` carrying the error group, code, message, and metadata: + +```rust @nocheck +use counter::{Counter, Increment}; +use rivetkit::{ + client::{Client, ClientConfig}, + prelude::*, + TypedClientExt, +}; + +#[tokio::main] +async fn main() -> Result<()> { + let client = Client::new(ClientConfig::new("http://localhost:6420").namespace("default")); + let counter = client.get_or_create_typed_default::("counter", ["my-counter"])?; + + match counter.send(Increment { amount: 1 }).await { + Ok(count) => println!("count: {count}"), + Err(error) => eprintln!("action failed: {error:#}"), + } + + Ok(()) +} +``` + +## Concepts + +### Keys + +Keys uniquely identify actor instances. Use compound keys (arrays) for hierarchical addressing: + +```rust @nocheck +use counter::Counter; +use rivetkit::{ + client::{Client, ClientConfig}, + prelude::*, + TypedClientExt, +}; + +#[tokio::main] +async fn main() -> Result<()> { + let client = Client::new(ClientConfig::new("http://localhost:6420").namespace("default")); + + // Compound key: [org, room] + let room = client.get_or_create_typed_default::("counter", ["org-acme", "general"])?; + let actor_id = room.inner().resolve().await?; + println!("Actor ID: {actor_id}"); + + Ok(()) +} +``` + +Keys accept arrays of `&str` or `String` (`["org-acme", "general"]`). Don't build keys with string interpolation like `format!("org:{user_id}")` when `user_id` contains user data. Use arrays instead to prevent key injection attacks. + +### Configuration + +`ClientConfig::new(endpoint)` is a builder. The endpoint is always required; there is no default. Common options: + +```rust @nocheck +use rivetkit::client::ClientConfig; + +let config = ClientConfig::new("http://localhost:6420") + .namespace("default") + .token("pk_...") + .pool_name("my-pool") + .header("x-custom", "value"); +``` + +- `namespace` - target namespace (defaults to the engine's configured namespace). +- `token` - authentication token for the engine. +- `pool_name` - runner pool to target. +- `header` / `headers` - extra HTTP headers sent with each request. +- `max_input_size` - cap on encoded action input size. + +For serverless deployments, set the endpoint to your app's `/api/rivet` URL. See [Endpoints](/docs/general/endpoints) for details. + +## API Reference + +See the full client API documentation on [docs.rs/rivetkit-client](https://docs.rs/rivetkit-client/latest/rivetkit_client/). + +_Source doc path: /docs/clients/rust_ diff --git a/.agents/skills/per-tenant-database/reference/clients/swift.md b/.agents/skills/per-tenant-database/reference/clients/swift.md new file mode 100644 index 0000000..c760645 --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/clients/swift.md @@ -0,0 +1,454 @@ +# Swift + +> Source: `src/content/docs/clients/swift.mdx` +> Canonical URL: https://rivet.dev/docs/clients/swift +> Description: Connect Swift apps to Rivet Actors. + +--- +## Install + +Add the Swift package dependency and import `RivetKitClient`: + +```swift +// Package.swift +dependencies: [ + .package(url: "https://github.com/rivet-dev/rivetkit-swift", from: "2.0.0") +] + +targets: [ + .target( + name: "MyApp", + dependencies: [ + .product(name: "RivetKitClient", package: "rivetkit-swift") + ] + ) +] +``` + +## Minimal Client + +### Endpoint URL + +```swift +import RivetKitClient + +let config = try ClientConfig( + endpoint: "https://my-namespace:pk_...@api.rivet.dev" +) +let client = RivetKitClient(config: config) + +let handle = client.getOrCreate("counter", ["my-counter"]) +let count: Int = try await handle.action("increment", 1, as: Int.self) +``` + +### Explicit Fields + +```swift +import RivetKitClient + +let config = try ClientConfig( + endpoint: "https://api.rivet.dev", + namespace: "my-namespace", + token: "pk_..." +) +let client = RivetKitClient(config: config) + +let handle = client.getOrCreate("counter", ["my-counter"]) +let count: Int = try await handle.action("increment", 1, as: Int.self) +``` + +## Stateless vs Stateful + +```swift +import RivetKitClient + +let config = try ClientConfig(endpoint: "http://localhost:6420") +let client = RivetKitClient(config: config) + +let handle = client.getOrCreate("counter", ["my-counter"]) + +// Stateless: each call is independent +let current: Int = try await handle.action("getCount", as: Int.self) +print("Current count: \(current)") + +// Stateful: keep a connection open for realtime events +let conn = handle.connect() + +// Subscribe to events using AsyncStream +let eventTask = Task { + for await count in await conn.events("count", as: Int.self) { + print("Event: \(count)") + } +} + +_ = try await conn.action("increment", 1, as: Int.self) + +eventTask.cancel() +await conn.dispose() +await client.dispose() +``` + +## Getting Actors + +```swift +import RivetKitClient + +struct GameInput: Encodable { + let mode: String +} + +let config = try ClientConfig(endpoint: "http://localhost:6420") +let client = RivetKitClient(config: config) + +// Get or create an actor +let room = client.getOrCreate("chatRoom", ["room-42"]) + +// Get an existing actor (fails if not found) +let existing = client.get("chatRoom", ["room-42"]) + +// Create a new actor with input +let created = try await client.create( + "game", + ["game-1"], + options: CreateOptions(input: GameInput(mode: "ranked")) +) + +// Get actor by ID +let byId = client.getForId("chatRoom", "actor-id") + +// Resolve actor ID +let resolvedId = try await room.resolve() +print("Resolved ID: \(resolvedId)") + +await client.dispose() +``` + +Actions support positional overloads for 0–5 args: + +```swift +import RivetKitClient + +let config = try ClientConfig(endpoint: "http://localhost:6420") +let client = RivetKitClient(config: config) +let handle = client.getOrCreate("counter", ["my-counter"]) + +let count: Int = try await handle.action("getCount") +let updated: String = try await handle.action("rename", "new-name") +let ok: Bool = try await handle.action("setScore", "user-1", 42) + +print("Count: \(count), Updated: \(updated), OK: \(ok)") +await client.dispose() +``` + +If you need more than 5 arguments, use the raw JSON fallback: + +```swift +import RivetKitClient + +let config = try ClientConfig(endpoint: "http://localhost:6420") +let client = RivetKitClient(config: config) +let handle = client.getOrCreate("counter", ["my-counter"]) + +let args: [JSONValue] = [ + .string("user-1"), + .number(.int(42)), + .string("extra"), + .string("more"), + .string("args"), + .string("here") +] +let ok: Bool = try await handle.action("setScore", args: args, as: Bool.self) +print("OK: \(ok)") + +await client.dispose() +``` + +## Connection Parameters + +```swift +import RivetKitClient + +struct ConnParams: Encodable { + let authToken: String +} + +let config = try ClientConfig(endpoint: "http://localhost:6420") +let client = RivetKitClient(config: config) + +let chat = client.getOrCreate( + "chatRoom", + ["general"], + options: GetOrCreateOptions(params: ConnParams(authToken: "jwt-token-here")) +) + +let conn = chat.connect() + +// Use the connection... +for await status in await conn.statusChanges() { + print("Status: \(status.rawValue)") + if status == .connected { + break + } +} + +await conn.dispose() +await client.dispose() +``` + +## Subscribing to Events + +```swift +import RivetKitClient + +let config = try ClientConfig(endpoint: "http://localhost:6420") +let client = RivetKitClient(config: config) +let conn = client.getOrCreate("chatRoom", ["general"]).connect() + +// Subscribe to events using AsyncStream +let messageTask = Task { + for await (from, body) in await conn.events("message", as: (String, String).self) { + print("\(from): \(body)") + } +} + +// For one-time events, break after receiving +let gameOverTask = Task { + for await _ in await conn.events("gameOver", as: Void.self) { + print("done") + break + } +} + +// Let it run for a bit +try await Task.sleep(for: .seconds(5)) + +// Cancel when done +messageTask.cancel() +gameOverTask.cancel() +await conn.dispose() +await client.dispose() +``` + +Event streams support 0–5 typed arguments. If you need raw values or more than 5 arguments, use `JSONValue`: + +```swift +import RivetKitClient + +let config = try ClientConfig(endpoint: "http://localhost:6420") +let client = RivetKitClient(config: config) +let conn = client.getOrCreate("chatRoom", ["general"]).connect() + +let rawTask = Task { + for await args in await conn.events("message") { + print(args) + } +} + +try await Task.sleep(for: .seconds(5)) +rawTask.cancel() +await conn.dispose() +await client.dispose() +``` + +## Connection Lifecycle + +```swift +import RivetKitClient + +let config = try ClientConfig(endpoint: "http://localhost:6420") +let client = RivetKitClient(config: config) +let conn = client.getOrCreate("chatRoom", ["general"]).connect() + +// Monitor status changes (immediately yields current status) +let statusTask = Task { + for await status in await conn.statusChanges() { + print("status: \(status.rawValue)") + } +} + +// Monitor errors +let errorTask = Task { + for await error in await conn.errors() { + print("error: \(error.group).\(error.code)") + } +} + +// Monitor open/close events +let openTask = Task { + for await _ in await conn.opens() { + print("connected") + } +} + +let closeTask = Task { + for await _ in await conn.closes() { + print("disconnected") + } +} + +// Check current status +let current = await conn.currentStatus +print("Current status: \(current.rawValue)") + +// Let it run for a bit +try await Task.sleep(for: .seconds(5)) + +// Cleanup +statusTask.cancel() +errorTask.cancel() +openTask.cancel() +closeTask.cancel() +await conn.dispose() +await client.dispose() +``` + +## Low-Level HTTP & WebSocket + +For actors that implement `onRequest` or `onWebSocket`, you can call them directly: + +```swift +import RivetKitClient + +let config = try ClientConfig(endpoint: "http://localhost:6420") +let client = RivetKitClient(config: config) +let handle = client.getOrCreate("chatRoom", ["general"]) + +// Raw HTTP request +let response = try await handle.fetch("history") +let history: [String] = try response.json([String].self) +print("History: \(history)") + +// Raw WebSocket connection +let websocket = try await handle.websocket(path: "stream") +try await websocket.send(text: "hello") +let message = try await websocket.receive() +print("Received: \(message)") + +await client.dispose() +``` + +## Calling from Backend + +Use the same client in server-side Swift (Vapor, Hummingbird, etc.): + +```swift +import RivetKitClient + +let config = try ClientConfig(endpoint: "http://localhost:6420") +let client = RivetKitClient(config: config) + +let handle = client.getOrCreate("counter", ["server-counter"]) +let count: Int = try await handle.action("increment", 1, as: Int.self) +print("Count: \(count)") + +await client.dispose() +``` + +## Error Handling + +```swift +import RivetKitClient + +let config = try ClientConfig(endpoint: "http://localhost:6420") +let client = RivetKitClient(config: config) + +do { + _ = try await client.getOrCreate("user", ["user-123"]) + .action("updateUsername", "ab", as: String.self) +} catch let error as ActorError { + print("Error code: \(error.code)") + print("Metadata: \(String(describing: error.metadata))") +} + +await client.dispose() +``` + +If you need an untyped response, you can decode to `JSONValue`: + +```swift +import RivetKitClient + +let config = try ClientConfig(endpoint: "http://localhost:6420") +let client = RivetKitClient(config: config) +let handle = client.getOrCreate("data", ["raw"]) + +let value: JSONValue = try await handle.action("getRawPayload") +print("Raw value: \(value)") + +await client.dispose() +``` + +## Concepts + +### Keys + +Keys uniquely identify actor instances. Use compound keys (arrays) for hierarchical addressing: + +```swift +import RivetKitClient + +let config = try ClientConfig(endpoint: "http://localhost:6420") +let client = RivetKitClient(config: config) + +// Use compound keys for hierarchical addressing +let room = client.getOrCreate("chatRoom", ["org-acme", "general"]) +let actorId = try await room.resolve() +print("Actor ID: \(actorId)") + +await client.dispose() +``` + +Don't build keys with string interpolation like `"org:\(userId)"` when `userId` contains user data. Use arrays instead to prevent key injection attacks. + +### Environment Variables + +`ClientConfig` reads optional values from environment variables: + +- `RIVET_NAMESPACE` - Namespace (can also be in endpoint URL) +- `RIVET_TOKEN` - Authentication token (can also be in endpoint URL) +- `RIVET_RUNNER` - Runner name (defaults to `"default"`) + +The `endpoint` parameter is always required. There is no default endpoint. + +### Endpoint Format + +Endpoints support URL auth syntax: + +``` +https://namespace:token@api.rivet.dev +``` + +You can also pass the endpoint without auth and provide `RIVET_NAMESPACE` and `RIVET_TOKEN` separately. For serverless deployments, set the endpoint to your app's `/api/rivet` URL. See [Endpoints](/docs/general/endpoints#url-auth-syntax) for details. + +## API Reference + +### Client +- `RivetKitClient(config:)` - Create a client with a config +- `ClientConfig` - Configure endpoint, namespace, and token +- `client.get()` / `getOrCreate()` / `getForId()` / `create()` - Get actor handles +- `client.dispose()` - Dispose the client and all connections + +### ActorHandle +- `handle.action(name, args..., as:)` - Stateless action call +- `handle.connect()` - Create a stateful connection +- `handle.resolve()` - Get the actor ID +- `handle.getGatewayUrl()` - Get the raw gateway URL +- `handle.fetch(path, request:)` - Raw HTTP request +- `handle.websocket(path:)` - Raw WebSocket connection + +### ActorConnection +- `conn.action(name, args..., as:)` - Action call over WebSocket +- `conn.events(name, as:)` - AsyncStream of typed events +- `conn.statusChanges()` - AsyncStream of status changes +- `conn.errors()` - AsyncStream of connection errors +- `conn.opens()` - AsyncStream that yields on connection open +- `conn.closes()` - AsyncStream that yields on connection close +- `conn.currentStatus` - Current connection status +- `conn.dispose()` - Close the connection + +### Types +- `ActorConnStatus` - Connection status enum (`.idle`, `.connecting`, `.connected`, `.disconnected`, `.disposed`) +- `ActorError` - Typed actor errors with `group`, `code`, `message`, `metadata` +- `JSONValue` - Raw JSON value for untyped responses + +_Source doc path: /docs/clients/swift_ diff --git a/.agents/skills/per-tenant-database/reference/clients/swiftui.md b/.agents/skills/per-tenant-database/reference/clients/swiftui.md new file mode 100644 index 0000000..4ae65d8 --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/clients/swiftui.md @@ -0,0 +1,359 @@ +# SwiftUI + +> Source: `src/content/docs/clients/swiftui.mdx` +> Canonical URL: https://rivet.dev/docs/clients/swiftui +> Description: Build SwiftUI apps with Rivet Actors. + +--- +## Install + +Add the Swift package dependency and import `RivetKitSwiftUI`: + +```swift +// Package.swift +dependencies: [ + .package(url: "https://github.com/rivet-dev/rivetkit-swift", from: "2.0.0") +] + +targets: [ + .target( + name: "MyApp", + dependencies: [ + .product(name: "RivetKitSwiftUI", package: "rivetkit-swift") + ] + ) +] +``` + +`RivetKitSwiftUI` re-exports `RivetKitClient` and `SwiftUI`, so a single import covers both. + +## Minimal Client + +```swift HelloWorldApp.swift +import RivetKitSwiftUI +import SwiftUI + +@main +struct HelloWorldApp: App { + var body: some Scene { + WindowGroup { + ContentView() + .rivetKit(endpoint: "https://my-namespace:pk_...@api.rivet.dev") + } + } +} +``` + +```swift ContentView.swift +import RivetKitSwiftUI +import SwiftUI + +struct ContentView: View { + @Actor("counter", key: ["my-counter"]) private var counter + @State private var count = 0 + + var body: some View { + VStack(spacing: 16) { + Text("\(count)") + .font(.system(size: 64, weight: .bold, design: .rounded)) + + Button("Increment") { + counter.send("increment", 1) + } + .disabled(!counter.isConnected) + } + .task { + count = (try? await counter.action("getCount")) ?? 0 + } + .onActorEvent(counter, "newCount") { (newCount: Int) in + count = newCount + } + } +} +``` + +## Actor Options + +The `@Actor` property wrapper always uses get-or-create semantics and accepts: + +- `name` (required) +- `key` as `String` or `[String]` (required) +- `params` (optional connection parameters) +- `createWithInput` (optional creation input) +- `createInRegion` (optional creation hint) +- `enabled` (toggle connection lifecycle) + +```swift +import RivetKitSwiftUI +import SwiftUI + +struct ConnParams: Encodable { + let authToken: String +} + +struct ChatView: View { + @Actor( + "chatRoom", + key: ["general"], + params: ConnParams(authToken: "jwt-token"), + enabled: true + ) private var chat + + var body: some View { + Text("Chat: \(chat.connStatus.rawValue)") + } +} +``` + +## Actions + +```swift +import RivetKitSwiftUI +import SwiftUI + +struct CounterView: View { + @Actor("counter", key: ["my-counter"]) private var counter + @State private var count = 0 + @State private var name = "" + + var body: some View { + VStack { + Text("Count: \(count)") + Text("Name: \(name)") + + Button("Fetch") { + Task { + count = try await counter.action("getCount") + name = try await counter.action("rename", "new-name") + } + } + + Button("Increment") { + counter.send("increment", 1) + } + } + } +} +``` + +## Subscribing to Events + +```swift +import RivetKitSwiftUI +import SwiftUI + +struct GameView: View { + @Actor("game", key: ["game-1"]) private var game + @State private var count = 0 + @State private var isGameOver = false + + var body: some View { + VStack { + Text("Count: \(count)") + if isGameOver { + Text("Game Over!") + } + } + .onActorEvent(game, "newCount") { (newCount: Int) in + count = newCount + } + .onActorEvent(game, "gameOver") { + isGameOver = true + } + } +} +``` + +## Async Event Streams + +```swift +import RivetKitSwiftUI +import SwiftUI + +struct ChatView: View { + @Actor("chatRoom", key: ["general"]) private var chat + @State private var messages: [String] = [] + + var body: some View { + List(messages, id: \.self) { message in + Text(message) + } + .task { + for await message in chat.events("message", as: String.self) { + messages.append(message) + } + } + } +} +``` + +## Connection Status + +```swift +import RivetKitSwiftUI +import SwiftUI + +struct StatusView: View { + @Actor("counter", key: ["my-counter"]) private var counter + @State private var count = 0 + + var body: some View { + VStack { + Text("Status: \(counter.connStatus.rawValue)") + + if counter.connStatus == .connected { + Text("Connected!") + .foregroundStyle(.green) + } + + Button("Fetch via Handle") { + Task { + if let handle = counter.handle { + count = try await handle.action("getCount", as: Int.self) + } + } + } + .disabled(!counter.isConnected) + } + } +} +``` + +## Error Handling + +```swift +import RivetKitSwiftUI +import SwiftUI + +struct UserView: View { + @Actor("user", key: ["user-123"]) private var user + @State private var errorMessage: String? + @State private var username = "" + + var body: some View { + VStack { + TextField("Username", text: $username) + + Button("Update Username") { + Task { + do { + let _: String = try await user.action("updateUsername", username) + } catch let error as ActorError { + errorMessage = "\(error.code): \(String(describing: error.metadata))" + } + } + } + + if let errorMessage { + Text(errorMessage) + .foregroundStyle(.red) + } + } + .onActorError(user) { error in + errorMessage = "\(error.group).\(error.code): \(error.message)" + } + } +} +``` + +## Concepts + +### Keys + +Keys uniquely identify actor instances. Use compound keys (arrays) for hierarchical addressing: + +```swift +import RivetKitSwiftUI +import SwiftUI + +struct OrgChatView: View { + @Actor("chatRoom", key: ["org-acme", "general"]) private var room + + var body: some View { + Text("Room: \(room.connStatus.rawValue)") + } +} +``` + +Don't build keys with string interpolation like `"org:\(userId)"` when `userId` contains user data. Use arrays instead to prevent key injection attacks. + +### Environment Configuration + +Call `.rivetKit(endpoint:)` or `.rivetKit(client:)` once at the root of your view tree: + +```swift +// With endpoint string (recommended for most apps) +@main +struct MyApp: App { + var body: some Scene { + WindowGroup { + ContentView() + .rivetKit(endpoint: "https://my-namespace:pk_...@api.rivet.dev") + } + } +} + +// With custom client (for advanced configuration) +@main +struct MyApp: App { + private let client = RivetKitClient( + config: try! ClientConfig(endpoint: "https://api.rivet.dev", token: "pk_...") + ) + + var body: some Scene { + WindowGroup { + ContentView() + .rivetKit(client: client) + } + } +} +``` + +When using `.rivetKit(endpoint:)`, the client is created once and cached per endpoint. When using `.rivetKit(client:)`, store the client as a property on `App` (not inside `body`) since SwiftUI can call `body` multiple times. + +### Environment Variables + +`ClientConfig` reads optional values from environment variables: + +- `RIVET_NAMESPACE` - Namespace (can also be in endpoint URL) +- `RIVET_TOKEN` - Authentication token (can also be in endpoint URL) +- `RIVET_RUNNER` - Runner name (defaults to `"default"`) + +The endpoint is always required. There is no default endpoint. + +### Endpoint Format + +Endpoints support URL auth syntax: + +``` +https://namespace:token@api.rivet.dev +``` + +You can also pass the endpoint without auth and provide `RIVET_NAMESPACE` and `RIVET_TOKEN` separately. For serverless deployments, set the endpoint to your app's `/api/rivet` URL. See [Endpoints](/docs/general/endpoints#url-auth-syntax) for details. + +## API Reference + +### Property Wrapper +- `@Actor(name, key:, params:, createWithInput:, createInRegion:, enabled:)` - SwiftUI property wrapper for actor connections + +### View Modifiers +- `.rivetKit(endpoint:)` - Configure client with an endpoint URL (creates cached client) +- `.rivetKit(client:)` - Configure client with a custom instance +- `.onActorEvent(actor, event) { ... }` - Subscribe to actor events (supports 0–5 typed args) +- `.onActorError(actor) { error in ... }` - Handle actor errors + +### ActorObservable +- `actor.action(name, args..., as:)` - Async action call +- `actor.send(name, args...)` - Fire-and-forget action +- `actor.events(name, as:)` - AsyncStream of typed events +- `actor.connStatus` - Current connection status +- `actor.isConnected` - Whether connected +- `actor.handle` - Underlying `ActorHandle` (optional) +- `actor.connection` - Underlying `ActorConnection` (optional) +- `actor.error` - Most recent error (optional) + +### Types +- `ActorConnStatus` - Connection status enum (`.idle`, `.connecting`, `.connected`, `.disconnected`, `.disposed`) +- `ActorError` - Typed actor errors with `group`, `code`, `message`, `metadata` + +_Source doc path: /docs/clients/swiftui_ diff --git a/.agents/skills/per-tenant-database/reference/cookbook/ai-agent.md b/.agents/skills/per-tenant-database/reference/cookbook/ai-agent.md new file mode 100644 index 0000000..1adb285 --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/cookbook/ai-agent.md @@ -0,0 +1,127 @@ +# AI Agent + +> Source: `src/content/cookbook/ai-agent.mdx` +> Canonical URL: https://rivet.dev/cookbook/ai-agent +> Description: Build an AI agent backend with persistent memory: one Rivet Actor per conversation, queued message handling, and streaming LLM responses as realtime events. + +--- +Patterns for building AI agent backends with RivetKit, where each conversation is one Rivet Actor that owns its memory, its message queue, and its streaming output. + +## Starter Code + +Start with one of the working examples on [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/ai-agent) and adapt it. The sections below describe the flagship `ai-agent` example unless a variant is called out explicitly. + +| Variant | Starter Code | Use When | +| --- | --- | --- | +| Queue-driven AI SDK agent | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/ai-agent) | You want a streaming chat agent where each conversation keeps its own persistent memory and processes one message at a time. | +| Sandbox coding agent | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/sandbox-coding-agent) | The agent should run a coding agent (Codex by default) inside an isolated [sandbox](/docs/actors/sandbox) via Docker, Daytona, or E2B. | +| Durable streams agent (experimental) | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/experimental-durable-streams-ai-agent) | You want replayable, restart-safe prompt and response delivery through durable streams instead of actor state and events. | + +## Conversation Memory + +Use one actor per conversation, keyed by a conversation or agent id (see [Actor Keys](/docs/actors/keys)). The agent actor's persistent [state](/docs/actors/state) is the conversation memory: in the `ai-agent` example, `messages` and `status` live in JSON actor state and survive sleep and restarts with no external database. Every model call rebuilds the prompt from `c.state.messages` plus a system prompt, so memory and inference input are the same data. + +| Variant | Where Memory Lives | Persisted State Fields | +| --- | --- | --- | +| `ai-agent` | JSON actor state | `messages`, `status` | +| `sandbox-coding-agent` | JSON actor state plus the sandbox ACP session | `messages`, `status`, `sessionId` | +| `experimental-durable-streams-ai-agent` | Durable streams; the actor stores only its conversation id and a read cursor | `conversationId`, `promptStreamOffset` | + +## Message Handling + +In the `ai-agent` example, the client pushes user input onto the agent's `message` [queue](/docs/actors/queues) with `agent.connection.send("message", { text, sender })`. This is a queue push, not an action call. The actor's `run` hook (see [Lifecycle](/docs/actors/lifecycle)) consumes the queue serially with `for await (const queued of c.queue.iter())`. + +Serial queue consumption is the per-conversation concurrency guarantee: at most one in-flight model call per actor, with no extra locking. The `status` field (`thinking` while a model call is in flight) is UI signal only; the run loop is the actual lock. The loop also checks `c.aborted` inside the token stream so shutdown exits gracefully. + +| Variant | Message Ingress | Serialization Guarantee | +| --- | --- | --- | +| `ai-agent` | `message` queue pushed via `connection.send` | `run` hook pops one queued message at a time with `c.queue.iter()`. | +| `sandbox-coding-agent` | `sendMessage` [action](/docs/actors/actions), no queue | Each call awaits the sandbox round trip before broadcasting the result. | +| `experimental-durable-streams-ai-agent` | Durable prompt stream long-polled from `onWake` | `promptStreamOffset` is persisted per chunk, so restarts resume without reprocessing prompts. | + +## Streaming Responses + +The `ai-agent` actor broadcasts a `response` [event](/docs/actors/events) for every model text delta. The payload carries `messageId`, the per-token `delta`, the cumulative `content`, and a `done` flag (plus `error` on failure), so clients can either append deltas or idempotently replace the message by `messageId` using `content`. The example frontend replaces by `messageId`, which tolerates dropped events. The terminal broadcast has an empty `delta`, the full `content`, and `done: true`. + +Because the assistant message object lives in `c.state.messages` and is mutated in place during streaming, partial content persists if the actor restarts mid-stream. The example broadcasts once per AI SDK delta with no throttling; batching or throttling deltas is a recommended extension for high-traffic deployments, not something the example implements. + +Variant differences: `sandbox-coding-agent` sends a single `response` broadcast with `done: true` after the sandbox finishes (no incremental streaming), and `experimental-durable-streams-ai-agent` appends per-token chunks to a durable response stream, then broadcasts `responseComplete` or `responseError`. + +## Architecture + +| Topic | Summary | +| --- | --- | +| Topology | `agentManager["primary"]` singleton directory plus one `agent[agentId]` actor per conversation. | +| Ingress | Client pushes `AgentQueueMessage` payloads onto the agent's `message` queue with `connection.send`. | +| Streaming | One `response` broadcast per model delta, terminal broadcast with `done: true`. | +| Memory | Full transcript and status in JSON actor state; no external database. | + +The manager creates `AgentInfo` records and warms each agent through [actor-to-actor communication](/docs/actors/communicating-between-actors): `createAgent` calls `c.client()`, then `client.agent.getOrCreate([info.id])` and awaits `getStatus()` so the conversation actor exists before the client connects. The sandbox variant extends this topology with a `codingSandbox` actor that shares the agent's key (`codingSandbox.getOrCreate([c.key[0]])`), so the agent-to-sandbox mapping is implicit in the key space. + +**Actors** + +- **Key**: `agentManager["primary"]` +- **Responsibility**: Directory actor. Creates `AgentInfo` records, lists agents, and warms each agent actor via `c.client()`. +- **Actions** + - `createAgent` + - `listAgents` +- **Queues** + - None +- **State** + - JSON + - `agents` + +- **Key**: `agent[agentId]` +- **Responsibility**: One actor per conversation. Holds the full message history and status, consumes queued user messages in its `run` loop, calls the model via the AI SDK, and broadcasts streaming deltas. +- **Actions** + - `getHistory` + - `getStatus` +- **Queues** + - `message` +- **Events** + - `messageAdded` + - `status` + - `response` +- **State** + - JSON + - `messages` + - `status` + +**Lifecycle** + +```mermaid +sequenceDiagram + participant C as Client + participant AM as agentManager + participant A as agent + participant LLM as Model API + + C->>AM: createAgent(name) + AM->>A: getOrCreate([info.id]) + getStatus() + AM-->>C: AgentInfo + C->>A: connection.send("message", {text, sender}) + Note over A: run loop pops queue via c.queue.iter() + A-->>C: messageAdded (user message) + A-->>C: messageAdded (assistant placeholder) + A-->>C: status (thinking) + A->>LLM: streamText(system prompt + history) + loop each text delta + LLM-->>A: delta + A-->>C: response {messageId, delta, content, done: false} + end + A-->>C: response {delta: "", content, done: true} + A-->>C: status (idle) +``` + +## Security Checklist + +The examples ship without auth so they stay minimal. Apply this baseline before exposing an agent backend. + +- **API keys stay server-side**: `OPENAI_API_KEY` (or `ANTHROPIC_API_KEY`) is read by the AI SDK inside the actor process. The key never reaches the browser; clients only talk to the actor over RivetKit. The sandbox variant forwards keys into the sandbox env, never to the client. +- **Add authentication**: The examples have no auth, so anyone who reaches the server can create agents, list them, and message any agent whose key they can guess. Add `onBeforeConnect` or `createConnState` checks with scoped tokens as a recommended extension. See [Authentication](/docs/actors/authentication). +- **Validate and rate-limit queue payloads**: The example only skips bodies without a string `text`. Enforce payload size limits, schema validation, and per-connection rate limits as a recommended extension. +- **Derive sender identity server-side**: The example trusts the client-supplied `sender` field verbatim. Bind sender identity to the authenticated connection instead. +- **Cap or trim message history**: The example sends the full transcript on every model call with no cap. Trim or summarize old messages as a recommended extension so prompts and state stay bounded. +- **Set cost ceilings per conversation**: Add per-agent token budgets and quotas as a recommended extension. The sandbox variant runs real compute, so also enforce per-user sandbox quotas and restrict sandbox network egress. + +_Source doc path: /cookbook/ai-agent_ diff --git a/.agents/skills/per-tenant-database/reference/cookbook/chat-room.md b/.agents/skills/per-tenant-database/reference/cookbook/chat-room.md new file mode 100644 index 0000000..cb8dbe4 --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/cookbook/chat-room.md @@ -0,0 +1,114 @@ +# Chat Room + +> Source: `src/content/cookbook/chat-room.mdx` +> Canonical URL: https://rivet.dev/cookbook/chat-room +> Description: Build a realtime chat room backend with Rivet Actors: one actor per room, SQLite-backed message history, and WebSocket broadcast to every connected client. + +--- +Patterns for building a chat room backend with RivetKit: room-scoped actors, persistent message history, and realtime delivery over WebSocket connections. + +## Starter Code + +Start with the working example on [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/chat-room) and adapt it. The backend is a single `chatRoom` actor; the frontend is a React app using `@rivetkit/react` (see the [React quickstart](/docs/actors/quickstart/react)). + +| Topic | Summary | +| --- | --- | +| Room model | One `chatRoom` actor per room key. The frontend defaults the key to `general`; typing a different room name connects to a different actor. | +| History | SQLite `messages` table created in `db({ onMigrate })`, read back with `ORDER BY id ASC`. | +| Delivery | `sendMessage` inserts the row, then broadcasts a typed `newMessage` event to every connected client. | +| Identity | None in the example. `sender` is a plain action argument; production should bind identity to the connection. | + +## Room-Per-Actor Model + +Each room is one Rivet Actor instance, addressed by [key](/docs/actors/keys). The client calls `useActor({ name: "chatRoom", key: [roomId] })`, which gets-or-creates the actor for that room. This gives you: + +- **Isolation**: each room's history and connections are fully scoped to its key. Switching the room input re-keys the hook and connects to a different actor with separate history. +- **A single serialized writer**: all `sendMessage` calls for one room run through one actor, so message ordering is consistent without locks. The SQLite `AUTOINCREMENT` id is the canonical order, which is why `getHistory` sorts by `id` rather than by timestamp. +- **Natural scaling**: rooms spread across the cluster independently. A hot room does not slow down other rooms. + +## Message History Storage + +This example stores history in the actor's SQLite database, not in JSON state. Pick based on history size and query needs: + +| Approach | Use When | Implementation Guidance | +| --- | --- | --- | +| [SQLite](/docs/actors/sqlite) (what this example uses) | Large or long-lived history that needs ordering, caps, pagination, or search | Create the `messages` table in `db({ onMigrate })`, insert with parameterized queries (`c.db.execute("INSERT ... VALUES (?, ?, ?)", ...)`), and read with `ORDER BY id ASC`. History survives actor sleep and scales past what you want in memory. | +| [JSON state](/docs/actors/state) | Small recent history, for example the last 50 to 100 messages | Push onto a `messages` array in actor state and trim to a cap on every send. Simplest option, but the whole history lives in memory and there is no query layer, so it only fits bounded recent-history use cases. | + +## Broadcast Delivery + +New messages reach connected clients through a typed [event](/docs/actors/events): + +- The actor declares `events: { newMessage: event() }`, where `Message` is `{ sender, text, timestamp }`. +- The `sendMessage` [action](/docs/actors/actions) builds the message with a server-side `Date.now()` timestamp, inserts it into the `messages` table, then calls `c.broadcast("newMessage", message)` and returns the message to the caller. +- Each client subscribes with `useEvent("newMessage", ...)` and appends to its local list. The sender renders its own message through the same broadcast path as everyone else, so all clients stay on one code path. +- History load is connection-gated: once the connection is ready, the client calls `getHistory()` once to render the backlog, then relies on events for everything after. + +Use `c.broadcast(...)` for room-wide messages. For private or per-recipient payloads (such as DMs inside a room), send on the individual connection instead, which is a recommended extension beyond this example. + +## Typing Indicators And Presence (Extension) + +The example does not implement typing indicators, presence, or join/leave handling of any kind. There is no `createConnState`, `onConnect`, or `onDisconnect` in the code. If you need them, add them as ephemeral [connection](/docs/actors/connections) behavior: + +- **Keep it ephemeral**: store the username and typing flag in per-connection state, never in SQLite or persisted actor state. Presence is derived from live connections and should disappear with them. +- **Broadcast on change only**: emit a typing event when a user starts or stops typing, and a presence event from `onConnect` / `onDisconnect`, rather than polling or ticking. +- **Expire on the client**: clear a typing indicator after a short client-side timeout so a dropped connection never leaves a stuck "is typing" row. + +## Per-User Inbox (Extension) + +For offline delivery, DMs, unread counts, or notification fanout, add a `userInbox[userId]` actor per user. This is an extension beyond the example: + +- The room actor forwards each message to the inbox actor of every member via [actor-to-actor calls](/docs/actors/communicating-between-actors), so users who are not connected to the room still accumulate messages. +- The inbox actor owns per-user unread state and serves it when the user comes online, independent of which rooms they are in. +- DMs become a degenerate room: either a `chatRoom` keyed by the sorted pair of user ids, or direct inbox-to-inbox delivery if you do not need shared history semantics. + +## Actors + +- **Key**: `chatRoom[roomId]` +- **Responsibility**: Owns one chat room. Persists the room's message history in its SQLite database and broadcasts each new message to every connected client. +- **Actions** + - `sendMessage` + - `getHistory` +- **Queues** + - None +- **Events** + - `newMessage` +- **State** + - SQLite + - `messages` table: `id` (autoincrement primary key), `sender`, `text`, `timestamp` + +## Lifecycle + +```mermaid +sequenceDiagram + participant A as Client A + participant B as Client B + participant R as chatRoom + + A->>R: connect with key [roomId] + Note over R: every start runs onMigrate (CREATE TABLE IF NOT EXISTS messages) + A->>R: getHistory() + R-->>A: Message[] ordered by id + B->>R: connect with key [roomId] + B->>R: getHistory() + R-->>B: Message[] ordered by id + A->>R: sendMessage(sender, text) + Note over R: INSERT row with server timestamp + R-->>A: newMessage (broadcast) + R-->>B: newMessage (broadcast) + A->>R: disconnect + Note over R: history stays in SQLite for the next connection +``` + +## Security Checklist + +The example is intentionally minimal and skips all of the following. Add them before production: + +- **Auth before join**: any client can join any room by knowing its name, and `sender` is arbitrary client input on every call. Validate a token during [connection auth](/docs/actors/authentication), bind identity to [connection state](/docs/actors/connections), and check room membership before serving history. Never trust a sender name passed as an action argument. +- **Message length clamps**: the example accepts empty messages and has no length limit. Trim server-side, reject empty text, and clamp to a maximum length. +- **Per-connection rate limiting**: rate limit `sendMessage` per connection to stop spam and broadcast amplification. +- **Server-side timestamps and ids**: the example already does this correctly. `timestamp` comes from `Date.now()` inside the action and `id` from SQLite `AUTOINCREMENT`. Keep it that way; never accept client-supplied timestamps or ids. +- **History caps**: `getHistory` returns every row with no limit. Add a `LIMIT` plus pagination, and prune or archive old rows so a long-lived room cannot grow unbounded. +- **Parameterized queries**: the example already inserts with `?` placeholders. Keep all user-supplied text out of SQL string interpolation. + +_Source doc path: /cookbook/chat-room_ diff --git a/.agents/skills/per-tenant-database/reference/cookbook/collaborative-text-editor.md b/.agents/skills/per-tenant-database/reference/cookbook/collaborative-text-editor.md new file mode 100644 index 0000000..cacbc1f --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/cookbook/collaborative-text-editor.md @@ -0,0 +1,157 @@ +# Collaborative Text Editor + +> Source: `src/content/cookbook/collaborative-text-editor.mdx` +> Canonical URL: https://rivet.dev/cookbook/collaborative-text-editor +> Description: Build a collaborative text editor backend with Yjs CRDTs and Rivet Actors: per-document actors relay sync and awareness updates and persist snapshots. + +--- +Patterns for building a Yjs server on RivetKit: CRDT document sync, presence and cursors, and snapshot persistence, with one Rivet Actor per document acting as a relay. + +## Starter Code + +Start with the working example on [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/collaborative-document) and adapt it to your editor. It ships a React frontend with a plain textarea, remote cursor overlays, and a workspace document index. + +| Use Case | Starter Code | Common Examples | +| --- | --- | --- | +| Shared document editing | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/collaborative-document) | Notion-style docs, shared notes, pair-writing tools, form co-editing | + +## CRDT vs OT + +Two families of algorithms solve concurrent text editing. The choice decides what your server has to do. + +| Dimension | CRDT (Yjs) | Operational Transformation | +| --- | --- | --- | +| Conflict resolution model | Commutative merges. Updates apply in any order on any peer and converge to the same result. | Server transforms each operation against every concurrent operation. Correctness depends on a central sequencer. | +| Offline support | Strong. Clients keep editing locally and merge buffered updates on reconnect. | Weak. Long-lived divergence makes transformation chains complex and fragile. | +| Server role | Relay plus persistence. The server applies opaque updates and rebroadcasts them. It never needs to understand document semantics. | Authoritative transformer. The server must implement transformation logic for every operation type. | +| Library maturity | Yjs is mature and widely deployed, with bindings for ProseMirror, CodeMirror, Monaco, and others. | Production-grade implementations are mostly proprietary (Google Docs) or aging (ShareDB). | + +The example uses Yjs because CRDTs let the server stay a relay-style Rivet Actor. The actor applies each incoming update to a server-side `Y.Doc` so it can persist the merged state and serve late joiners, but it never transforms operations or arbitrates conflicts. Ordering does not matter because Yjs merges are commutative. + +## Document Actor Model + +| Topic | Summary | +| --- | --- | +| Topology | One `document[workspaceId, documentId]` actor per document plus one `documentList[workspaceId]` coordinator per workspace. | +| Sync model | Each client holds a local `Y.Doc`. The document actor relays incremental Yjs updates as broadcast [events](/docs/actors/events) and keeps a server-side merged copy in vars. | +| Persistence | Full merged Yjs snapshot overwritten in one binary [actor KV](/docs/actors/kv) key (`yjs:doc`) on every sync update. Document metadata lives in JSON [state](/docs/actors/state). | +| Queues | None. The example is purely [actions](/docs/actors/actions) plus broadcast events. | +| Presence | Yjs Awareness relayed through the same `applyUpdate` action. Per-connection `connState` tracks asserted awareness clientIds for disconnect cleanup. | + +The two-actor split follows the coordinator pattern from [Design Patterns](/docs/actors/design-patterns): the coordinator owns discovery and creation, and each document actor owns one document's realtime state. Multi-part [keys](/docs/actors/keys) scope both actors to a workspace. + +**Actors** + +- **Key**: `document[workspaceId, documentId]` +- **Responsibility**: Applies incoming sync and awareness updates to a server-side `Y.Doc` and `Awareness`, persists the merged Yjs snapshot to actor KV, and broadcasts updates to all connected collaborators. +- **Actions** + - `getContent` + - `applyUpdate` + - `getAwareness` +- **Queues** + - None +- **State** + - JSON metadata only: `title`, `createdAt`, `updatedAt` + - Binary KV key `yjs:doc` holding the full merged Yjs snapshot + - Ephemeral vars: the live `Y.Doc` and `Awareness`, created in `createVars` and rehydrated from KV on actor start + - Per-connection `connState`: `clientIds` of awareness clients asserted by that connection + +- **Key**: `documentList[workspaceId]` +- **Responsibility**: Coordinator for one workspace. Creates document actors through the actor-to-actor client and maintains the index of document summaries. +- **Actions** + - `createDocument` + - `listDocuments` + - `deleteDocument` +- **Queues** + - None +- **State** + - JSON + - `documents` array of `DocumentSummary` entries (`id`, `title`, `createdAt`, `updatedAt`) + +The coordinator's `createDocument` generates a UUID, then explicitly creates the document actor with `c.client()` and passes `{ title, createdAt }` as creation [input](/docs/actors/input), which the document actor's `createState` consumes. See [Communicating Between Actors](/docs/actors/communicating-between-actors) for the actor-to-actor client. + +## Update Relay + +A single `applyUpdate(update, kind, clientId?)` action handles both update kinds. Updates cross the action boundary as `number[]` byte arrays and are converted back to `Uint8Array` on each side. + +| Kind | Server Applies To | Persists | Broadcasts | +| --- | --- | --- | --- | +| `"sync"` | `c.vars.doc` via `Y.applyUpdate` with origin `"client"` | Full merged snapshot to KV key `yjs:doc`, then bumps `updatedAt` | `sync` event carrying the incremental update | +| `"awareness"` | `c.vars.awareness` via `applyAwarenessUpdate` with origin `"client"` | Nothing. Presence is ephemeral. | `awareness` event carrying the update | + +Note the asymmetry on the sync branch: the broadcast carries only the small incremental update, while the KV write stores the full merged document re-encoded with `Y.encodeStateAsUpdate`. + +Yjs origin tags are the echo guards that keep the relay loop-free: + +| Origin Tag | Set Where | Effect | +| --- | --- | --- | +| `"local"` | Client edits inside `doc.transact(..., "local")` | The client's update listener fires and sends `applyUpdate` to the actor. | +| `"client"` | Server applying an incoming update to its `Y.Doc` or `Awareness` | Marks the change as client-originated on the server copy. | +| `"remote"` | Client applying broadcast events or initial sync data | Update listeners early-return on `"remote"`, so a client never re-sends its own echo. | + +On connect or reconnect, the client calls `getContent` and `getAwareness`, then applies both results to its local `Y.Doc` and `Awareness` with origin `"remote"`. After that, every change flows through `applyUpdate` and the broadcast events. + +## Awareness And Presence + +Presence (user names, colors, cursor positions) rides on the Yjs Awareness protocol instead of actor state: + +- Clients set presence with `awareness.setLocalStateField` for the `user` and `cursor` fields. The awareness update listener encodes the change and sends `applyUpdate(update, "awareness", awareness.clientID)`. +- The actor records each asserted `clientId` in that connection's `connState.clientIds`, applies the update to the server-side `Awareness`, and broadcasts the `awareness` event to all peers. See [Connections](/docs/actors/connections) for per-connection state. +- `onDisconnect` reads the connection's `clientIds`, calls `removeAwarenessStates` on the server-side `Awareness`, and broadcasts the encoded removal so every remaining client drops the departed user's cursor. See [Lifecycle](/docs/actors/lifecycle) for the hook. + +Because the actor tracks which awareness clientIds belong to which connection, presence cleanup is automatic on disconnect with no client cooperation required. + +## Persistence And Compaction + +The example persists with a full-snapshot overwrite: on every `"sync"` update, the actor re-encodes the entire merged document with `Y.encodeStateAsUpdate` and overwrites the single binary KV key `yjs:doc`. There is no append-only update log and no separate compaction job. Compaction is implicit because `Y.encodeStateAsUpdate` emits one compact merged representation of the document, so Yjs merge semantics keep the stored blob compact on their own. + +| Property | Full-Snapshot Overwrite (the example) | +| --- | --- | +| Write cost | One full-document KV write per sync update, so every keystroke rewrites the whole blob. | +| Read cost | One binary KV read in `createVars` rehydrates the document on actor start. | +| Crash safety | The last completed `applyUpdate` is durable. No log replay needed. | +| Sweet spot | Small to medium documents where simplicity beats write amplification. | + +**Recommended extension (not in the example)**: for large documents or very high edit rates, switch to appending incremental updates to a KV update log and writing a merged snapshot only periodically (for example every N updates). Boot becomes snapshot plus log replay, and the snapshot write becomes the explicit compaction step that truncates the log. Adopt this only when full-snapshot writes become the measured bottleneck or the blob approaches KV value size limits. + +## Lifecycle + +```mermaid +sequenceDiagram + participant A as Client A + participant B as Client B + participant DL as documentList + participant D as document + + A->>DL: listDocuments() + A->>DL: createDocument(title) + DL->>D: create([workspaceId, documentId], input) + DL-->>A: DocumentSummary + A->>D: connect + B->>D: connect + Note over D: createVars rehydrates Y.Doc from KV "yjs:doc" + A->>D: getContent() + getAwareness() + D-->>A: encoded doc + awareness state + Note over A: local edit with origin "local" + A->>D: applyUpdate(update, "sync") + Note over D: apply with origin "client", overwrite KV snapshot + D-->>B: sync event (incremental update) + Note over B: apply with origin "remote", no echo + A->>D: applyUpdate(update, "awareness", clientId) + D-->>B: awareness event + B-->>D: disconnect + Note over D: onDisconnect removes B's awareness clientIds + D-->>A: awareness event (removal) +``` + +## Security Checklist + +The example ships with no authentication or authorization. Harden it with this baseline before production. None of these are implemented in the example. + +- **Authenticate before connect**: Anyone who knows or guesses a workspace ID can connect, and because `useActor` implicitly getOrCreates, connecting with a nonexistent workspace ID silently creates a blank `documentList` coordinator. Add connection auth so unauthenticated clients never reach an actor. See [Authentication](/docs/actors/authentication). +- **Per-document access control**: Validate that the authenticated user is allowed to access the specific `[workspaceId, documentId]` key, not just any document. +- **Cap and rate limit `applyUpdate`**: Update payloads are unvalidated `number[]` arrays with no size limit, and the example client sends one action per keystroke and per cursor move with zero throttling. Enforce payload size caps and per-connection rate limits on the server, and debounce on the client. +- **Do not trust client-asserted awareness clientIds**: The `clientId` argument to `applyUpdate` is client-supplied and trusted as-is. Derive or verify presence identity from connection-scoped server state instead. +- **Destroy actors and KV on delete**: `deleteDocument` only filters the entry out of the coordinator's index. The document actor and its KV snapshot are orphaned. On delete, also destroy the document actor and its storage, with a permission check on who may delete. + +_Source doc path: /cookbook/collaborative-text-editor_ diff --git a/.agents/skills/per-tenant-database/reference/cookbook/cron-jobs.md b/.agents/skills/per-tenant-database/reference/cookbook/cron-jobs.md new file mode 100644 index 0000000..21c7657 --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/cookbook/cron-jobs.md @@ -0,0 +1,69 @@ +# Cron Jobs and Scheduled Tasks + +> Source: `src/content/cookbook/cron-jobs.mdx` +> Canonical URL: https://rivet.dev/cookbook/cron-jobs +> Description: Patterns for durable one-shot, calendar, and fixed-interval work on Rivet Actors. + +--- +Rivet Actor schedules are durable actor-local timers. They survive actor sleep, restarts, upgrades, deploys, and crashes without a separate cron service. + +## Choose a schedule type + +| API | Use it for | +| --- | --- | +| `c.schedule.after(delayMs, action, ...args)` | One-time work after a relative delay. | +| `c.schedule.at(timestamp, action, ...args)` | One-time work at an exact Unix timestamp in milliseconds. | +| `c.cron.set({ ... })` | Named calendar recurrence in an IANA timezone. | +| `c.cron.every({ ... })` | Named fixed intervals of at least 5 seconds. | + +All callbacks are ordinary actions on the same actor. Keep the action name fixed in your code rather than accepting an arbitrary action name from a client. + +See [Schedule & Cron](/docs/actors/schedule) for the full API, history, cancellation, failure behavior, and limits. + +## Calendar job + +Use `cron.set` instead of manually re-arming a one-shot action: + +Install fixed background jobs in `onCreate` so setup runs once per actor. The job name remains an upsert key, so a later `cron.set` call updates the existing job rather than creating a duplicate. `cron.set` also handles timezone and daylight-saving transitions. + +## Fixed-interval job + +Use `cron.every` for frequent work such as presence sweeps or cache refreshes: + +```ts +await c.cron.every({ + name: "presence-sweep", + interval: 15_000, // Minimum 5 seconds. + action: "sweepPresence", + maxHistory: 25, +}); +``` + +Intervals remain anchored to scheduled deadlines rather than drifting by the action's runtime. If a previous run is still active, the overlapping occurrence is skipped. + +## Cancellation and updates + +Keep the ID returned by a one-shot schedule when it may need cancellation: + +```ts +const id = await c.schedule.after(60_000, "expireSession", sessionId); +await c.schedule.cancel(id); +``` + +Recurring jobs are managed by name: + +```ts +await c.cron.delete("presence-sweep"); +``` + +Calling `cron.set` or `cron.every` again with the same name replaces its configuration. + +## Failure and idempotency + +Keep scheduled actions idempotent when duplicate work would be harmful. See [Execution behavior](/docs/actors/schedule#execution-behavior) for retry behavior and workflow guidance. + +## Topology + +Use a singleton actor key for one global job, such as `jobs["daily-report"]`. Use an actor per user or resource for isolated reminders, trials, billing periods, or other per-entity schedules. + +_Source doc path: /cookbook/cron-jobs_ diff --git a/.agents/skills/per-tenant-database/reference/cookbook/live-cursors.md b/.agents/skills/per-tenant-database/reference/cookbook/live-cursors.md new file mode 100644 index 0000000..71c94ab --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/cookbook/live-cursors.md @@ -0,0 +1,152 @@ +# Live Cursors and Presence + +> Source: `src/content/cookbook/live-cursors.mdx` +> Canonical URL: https://rivet.dev/cookbook/live-cursors +> Description: Live cursors and multiplayer presence with Rivet Actors: per-connection cursor state, realtime updates over events or raw WebSockets, and throttling. + +--- +Patterns for building live cursors, multiplayer presence, and realtime cursor sharing with RivetKit. One room actor fans cursor positions out to every connected client, keyed per room with [actor keys](/docs/actors/keys). + +## Starter Code + +Start with one of the two working variants on GitHub. Both implement the same collaborative cursor canvas with persistent text labels; they differ only in transport. + +| Variant | Starter Code | Transport | Presence Storage | +| --- | --- | --- | --- | +| `cursors` | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/cursors) | Typed [actions](/docs/actors/actions) and [events](/docs/actors/events) over the RivetKit connection | `connState` per connection | +| `cursors-raw-websocket` | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/cursors-raw-websocket) | Raw [`onWebSocket` handler](/docs/actors/websocket-handler) with a custom JSON message protocol | Socket map in `createVars` | + +Use `cursors` by default: typed actions, typed events, and automatic connection tracking cover most apps with less code. Use `cursors-raw-websocket` when you need full control of the wire format, for example a custom JSON or binary protocol, or clients that do not use the RivetKit client library. + +## Connection State vs Persistent State + +Presence is ephemeral by definition. A cursor position is only meaningful while its connection is alive, so it belongs in per-connection storage, not in persistent actor state. Persistent state is reserved for data that must survive disconnects and actor restarts. + +| Data | Where It Lives | Why | +| --- | --- | --- | +| Cursor position | `connState` (`cursors`) or the `createVars` socket map (`cursors-raw-websocket`) | Scoped to one connection and discarded with it. Stale presence cannot accumulate in storage. | +| Text labels (`textLabels`) | Persistent actor `state` in both variants | Canvas content must survive disconnects and actor restarts. | + +In the `cursors` variant, `updateCursor` writes `c.conn.state.cursor` and `getRoomState` rebuilds the presence snapshot by iterating `c.conns.values()`, so the cursor map is always derived from live connections rather than stored. See [Connections](/docs/actors/connections) for `connState` and [State](/docs/actors/state) for persistence semantics. + +## Presence Lifecycle + +- **Join**: The `cursors-raw-websocket` variant pushes an `init` message with the current `{ cursors, textLabels }` snapshot as soon as a socket connects. The `cursors` variant has no explicit join broadcast; the client calls the `getRoomState` action once after connecting to seed its local maps, and peers first see a new user on that user's first `cursorMoved` broadcast. +- **Move**: Every `updateCursor` call writes the connection's presence entry, then broadcasts `cursorMoved` to all connections, including the sender. +- **Leave**: The `cursors` variant handles leave in `onDisconnect`, broadcasting `cursorRemoved` with the connection's last cursor. The raw variant does the same from the socket `close` listener, then deletes the session from the `vars.websockets` map. Clients delete that user from their local cursor map, so stale cursors disappear the moment a tab closes. + +See [Lifecycle](/docs/actors/lifecycle) for `onDisconnect` and `createVars`. + +## Update Throttling + +Neither example throttles. Both frontends send a cursor update on every raw `mousemove` event with no debounce or interval cap. That is fine for a demo, but a fast mouse on a high-refresh display can emit hundreds of events per second per user. The patterns below are recommended production hardening on top of the starter code, not something the examples implement. + +| Layer | Pattern | Guidance | +| --- | --- | --- | +| Client (smoothness) | Throttle to 20-30Hz | Sample the latest pointer position every 33-50ms and send only that. Drop intermediate moves, but always flush the final position so cursors settle at the true location. Interpolate between received positions on the rendering side. | +| Server (enforcement) | Per-connection rate limit | Track the last accepted update timestamp per connection and drop or coalesce updates arriving faster than your cap. Client throttles are cooperative; the actor is the enforcement boundary. | + +## Actors + +- **Key**: `cursorRoom[roomId]` (the frontend defaults `roomId` to `"general"`) +- **Responsibility**: Holds per-connection cursor presence in `connState`, persists shared text labels in actor state, and broadcasts cursor and text updates to all connections. +- **Actions** + - `updateCursor` + - `updateText` + - `removeText` + - `getRoomState` +- **Events** + - `cursorMoved` + - `cursorRemoved` + - `textUpdated` + - `textRemoved` +- **Queues** + - None +- **State** + - JSON + - `textLabels` (persistent) + - `connState.cursor` per connection (ephemeral) + +- **Key**: `cursorRoom[roomId]` (resolved via `client.cursorRoom.getOrCreate(roomId)`) +- **Responsibility**: Exposes a raw WebSocket endpoint, tracks live sockets and their cursors in a `createVars` map keyed by a `sessionId` query parameter, persists text labels, and manually fans JSON frames out to every socket. +- **Actions** + - `getOrCreate` (stub returning `{ status: "ok" }`; the frontend resolves the actor ID with the client handle's `getOrCreate(roomId).resolve()`, which creates the actor without dispatching this action) + - `getRoomState` +- **Queues** + - None +- **State** + - JSON + - `textLabels` (persistent) + - `vars.websockets` map of `sessionId` to socket and cursor (in-memory, lost on restart) + +The raw variant defines no RivetKit events. Its message names are `type` fields on raw JSON frames: + +| Direction | Message `type` | Payload | +| --- | --- | --- | +| Client to server | `updateCursor` | `{ userId, x, y }` | +| Client to server | `updateText` | `{ id, userId, text, x, y }` | +| Client to server | `removeText` | `{ id }` | +| Server to client | `init` | `{ cursors, textLabels }` snapshot on connect | +| Server to client | `cursorMoved`, `textUpdated`, `textRemoved`, `cursorRemoved` | The corresponding cursor, label, or ID payload | + +## Lifecycle + +### cursors (Actions + Events) + +```mermaid +sequenceDiagram + participant A as Client A + participant R as cursorRoom + participant B as Other Clients + + A->>R: connect via useActor (cursorRoom[roomId]) + A->>R: getRoomState() + R-->>A: {cursors, textLabels} + loop every mouse move + A->>R: updateCursor(userId, x, y) + Note over R: write c.conn.state.cursor + R-->>B: cursorMoved (broadcast) + end + A->>R: updateText(id, userId, text, x, y) + Note over R: upsert persistent state.textLabels + R-->>B: textUpdated (broadcast) + Note over A: tab closes + Note over R: onDisconnect reads conn.state.cursor + R-->>B: cursorRemoved (broadcast) +``` + +### cursors-raw-websocket + +```mermaid +sequenceDiagram + participant A as Client A + participant R as cursorRoom + participant B as Other Clients + + A->>R: getOrCreate(roomId).resolve() + R-->>A: actorId + A->>R: open WebSocket /gateway/{actorId}/websocket?sessionId=... + Note over R: close 1008 if sessionId is missing + Note over R: store socket in vars.websockets + R-->>A: init {cursors, textLabels} + loop every mouse move + A->>R: {type: "updateCursor"} frame + Note over R: update session cursor in vars + R-->>B: cursorMoved frame + end + Note over A: socket closes + R-->>B: cursorRemoved frame + Note over R: delete session from vars.websockets +``` + +## Security Checklist + +Both examples ship without authentication so the presence pattern stays readable. Everything below is recommended hardening for production, not behavior the examples implement. + +- **Identity**: Bind presence identity to the connection (`c.conn.id` in the actions variant, a server-generated session ID in the raw variant). Never trust a client-supplied `userId`; in the examples it is a random client-generated string, so any client can impersonate or remove any cursor. +- **Authorization**: Authorize label mutations by owner. In the examples, `updateText` accepts arbitrary `id` and `userId` arguments and `removeText` accepts an arbitrary `id`, so any client can edit or delete any label. +- **Input validation**: Clamp `x` and `y` to canvas bounds, cap text label length, and cap the total `textLabels` count so persistent state cannot grow unbounded. +- **Rate limiting**: Enforce a per-connection cap on `updateCursor` (for example 30Hz) and on label writes, as described in [Update Throttling](#update-throttling). +- **Protocol strictness (raw variant)**: Validate message shape before use and close the socket on malformed JSON instead of logging and continuing. Reject duplicate `sessionId` values rather than silently overwriting another session's socket entry. + +_Source doc path: /cookbook/live-cursors_ diff --git a/.agents/skills/per-tenant-database/reference/cookbook/multiplayer-game.md b/.agents/skills/per-tenant-database/reference/cookbook/multiplayer-game.md new file mode 100644 index 0000000..425b113 --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/cookbook/multiplayer-game.md @@ -0,0 +1,713 @@ +# Multiplayer Game + +> Source: `src/content/cookbook/multiplayer-game.mdx` +> Canonical URL: https://rivet.dev/cookbook/multiplayer-game +> Description: Pragmatic patterns for building multiplayer games: matchmaking, tick loops, realtime state, interest management, and validation. + +--- +Patterns for building multiplayer games with RivetKit, intended as a practical checklist you can adapt per genre. + +## Starter Code + +Start with one of the working examples on [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/multiplayer-game-patterns/src/actors/) and adapt it to your game. Do not start from scratch for matchmaking and lifecycle flows. + +| Game Classification | Starter Code | Common Examples | +| --- | --- | --- | +| Battle Royale | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/multiplayer-game-patterns/src/actors/battle-royale/) | Fortnite, Apex Legends, PUBG, Warzone | +| Arena | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/multiplayer-game-patterns/src/actors/arena/) | Call of Duty TDM/FFA, Halo Slayer, Counter-Strike casual, VALORANT unrated, Overwatch Quick Play, Rocket League | +| IO Style | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/multiplayer-game-patterns/src/actors/io-style/) | Agar.io, Slither.io, surviv.io | +| Open World | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/multiplayer-game-patterns/src/actors/open-world/) | Minecraft survival servers, Rust-like worlds, MMO zone/chunk worlds | +| Party | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/multiplayer-game-patterns/src/actors/party/) | Fall Guys private lobbies, custom game rooms, social party sessions | +| Physics 2D | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/multiplayer-game-patterns/src/actors/physics-2d/) | Top-down physics brawlers, 2D arena games, platform fighters | +| Physics 3D | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/multiplayer-game-patterns/src/actors/physics-3d/) | Physics sandbox sessions, 3D arena games, movement playgrounds | +| Ranked | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/multiplayer-game-patterns/src/actors/ranked/) | Chess ladders, competitive card games, duel arena ranked queues | +| Turn-Based | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/multiplayer-game-patterns/src/actors/turn-based/) | Chess correspondence, Words With Friends, async board games | +| Idle | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/multiplayer-game-patterns/src/actors/idle/) | Cookie Clicker, Idle Miner Tycoon, Adventure Capitalist | + +## Server Simulation + +### Game Loop And Tick Rates + +| Pattern | Use When | Implementation Guidance | +| --- | --- | --- | +| Fixed realtime loop | Battle Royale, Arena, IO Style, Open World, Ranked | Run in `run` with `sleep(tickMs)` and exit on `c.aborted`. | +| Action-driven updates | Party, Turn-Based | Mutate and broadcast only on actions/events rather than scheduled ticks. | +| Coarse offline progression | Any mode with idle progression | Use `c.schedule.after(...)` with coarse windows (for example 5 to 15 minutes) and apply catch-up from elapsed wall clock time. | + +### Physics + +Start with custom kinematic logic for simple games. Switch to a full physics engine when you need joints, stacked bodies, high collision density, or complex shapes (rotated polygons, capsules, convex hulls, triangle meshes). + +Pick one engine per simulation. Keep frontend-only libs out of backend simulation paths and treat server state as authoritative. + +| Dimension | Primary Engine | Fallback Engines | Example Code | +| --- | --- | --- | --- | +| 2D | `@dimforge/rapier2d` | `planck-js`, `matter-js` | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/multiplayer-game-patterns/src/actors/physics-2d/) | +| 3D | `@dimforge/rapier3d` | `cannon-es`, `ammo.js` | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/multiplayer-game-patterns/src/actors/physics-3d/) | + +### Spatial Indexing + +For non-physics spatial queries, use a dedicated index instead of naive `O(n^2)` checks: + +| Index Type | Recommendation | +| --- | --- | +| AABB index | For AOI, visibility, and non-collider entities, use `rbush` for dynamic sets or `flatbush` for static-ish sets. | +| Point index | For nearest-neighbor or within-radius queries, use `d3-quadtree`. | + +## Networking & State Sync + +### Netcode + +| Model | When To Use | Implementation | +| --- | --- | --- | +| Hybrid (client movement, server combat) | Shooters, action sports, ranked duels | Client owns movement and sends capped-rate position updates. Server validates for anti-cheat. Combat (projectiles, hits, damage) is fully server-authoritative. | +| Server-authoritative with interpolation | IO Style, persistent worlds | Client sends input commands. Server simulates on fixed ticks and publishes authoritative snapshots. Client interpolates between snapshots. | +| Server-authoritative (basic logic) | Turn-based, event-driven | Server validates and applies discrete actions (turns, phase transitions, votes). Client displays confirmed state. | + +### Realtime Data Model + +- **Snapshots and diffs**: Publish state as events. Send a full snapshot on join/resync, then per-tick diffs for regular updates. +- **Batch per tick**: Keep events small and typed. Batch high-frequency updates per tick. +- **Avoid UI framework state for game updates**: Use `requestAnimationFrame` or a Canvas/Three.js loop for simulation, not React state. Reserve UI framework state for menus, HUD, and forms. +- **Broadcast vs per-connection**: Use `c.broadcast(...)` for shared updates and `conn.send(...)` for private/per-player data. + +### Shared Simulation Logic + +Shared simulation logic runs on both the client and the server. For example, an `applyInput(state, input, dt)` function that integrates velocity and clamps to world bounds can run on the client for prediction and on the server for validation. + +- **Hybrid modes**: Client runs shared movement as primary authority, server runs it for anti-cheat validation. +- **Server-authoritative modes**: Client uses shared logic for interpolation and prediction only. +- **Keep it pure**: Movement integration, input transforms, collision helpers, and constants only. +- **Put shared code in `src/shared/`**: Keep deterministic helpers in `src/shared/sim/*` with no side effects. + +### Interest Management + +Control what each client receives to reduce bandwidth and prevent information leaks. + +#### Per-Player Replication Filters + +- **Filter by relevance**: Send each client only state relevant to that player (proximity, line-of-sight, team, or game phase). +- **Shooters and action games**: Limit replication by proximity and optional field-of-view checks. +- **Server-side only**: Clients should never receive data they should not see. + +#### Sharded Worlds + +- **Partition large worlds**: Use chunk actors keyed by `worldId:chunkX:chunkY`. +- **Subscribe to nearby chunks**: Clients connect only to nearby partitions (for example a 3x3 chunk window). +- **Use sparingly**: Only when the world is large and state-heavy (sandbox builders, MMOs), not as a default for small matches. + +## Backend Infrastructure + +### Persistence + +- **In-memory state**: Best for realtime game state that changes every tick (player positions, inputs, match phase, scores). +- **SQLite (`rivetkit/db`)**: Better for large or table-like state that needs queries, indexes, or long-term persistence (tiles, inventory, matchmaking pools). Serialize DB work through a queue since multiple actions can hit the same actor concurrently. + +### Matchmaking Patterns + +Common building blocks used across the architecture patterns below. + +#### Actor Topology + +| Primitive | Use When | Typical Ownership | +| --- | --- | --- | +| `matchmaker["main"]` + `match[matchId]` | Session-based multiplayer (battle royale, arena, ranked, party, turn-based) | Matchmaker owns discovery/assignment. Match owns lifecycle and gameplay state. | +| `chunk[worldId,chunkX,chunkY]` | Large continuous worlds that need sharding | Each chunk owns local players, chunk state, and local simulation. | +| `world[playerId]` | Per-player progression loops (idle/solo world state) | Per-player resources, buildings, timers, and progression. | +| `player[username]` | Canonical profile/rating reused across matches | Durable player stats (for example rating and win/loss). | +| `leaderboard["main"]` | Shared rankings across many matches/players | Global ordered score rows and top lists. | + +#### Queueing Strategy + +- Multiple players can hit the matchmaker at the same time, so actions like find/create, queue/unqueue, and close need to be serialized through actor queues to avoid races. +- Match-local actions (gameplay, scoring) do not need queueing unless they write back to the matchmaker. + +## Security And Anti-Cheat + +Start with this baseline, then harden further for competitive or high-risk environments. + +### Baseline Checklist + +- **Identity**: Use `c.conn.id` as the authoritative transport identity. Treat `playerId`/`username` in params as untrusted input and bind through server-issued assignment/join tickets. +- **Authorization**: Validate the caller is allowed to mutate the target entity (room membership, turn ownership, host-only actions). +- **Input validation**: Clamp sizes/lengths, validate enums, and validate usernames (length, allowed chars, avoid unbounded Unicode). +- **Rate limiting**: Per-connection rate limits for spammy actions (chat, join/leave, fire, movement updates). +- **State integrity**: Server recomputes derived state (scores, win conditions, placements). Never allow client-authoritative changes to inventory/currency/leaderboard totals. + +### Movement Validation + +For any mode with client-authoritative movement (hybrid flows), clients may send position/rotation updates for smoothness, but the server must: + +- Enforce max delta per update (speed cap) based on elapsed time. +- Reject or clamp teleports. +- Enforce world bounds (and basic collision if applicable). +- Rate limit update frequency (for example 20Hz max). + +## Architecture Patterns + +Each game type below starts with a quick summary table, then details actors and lifecycle. + +### Battle Royale + +| Topic | Summary | +| --- | --- | +| Matchmaking | Immediate routing to the fullest non-started lobby (oldest tie-break); players wait in lobby until capacity, then the match starts. | +| Netcode | Hybrid. Client owns movement, camera, and local prediction. Server owns zone state, projectiles, hit resolution, eliminations, loot, and final placement. | +| Tick Rate | 10 ticks/sec (`100ms`) with a fixed loop for zone progression and lifecycle checks. | +| Physics | Client owns movement with server anti-cheat validation; projectiles, hits, and damage are server-authoritative. Use `@dimforge/rapier3d` for 3D or `@dimforge/rapier2d` for top-down 2D. | + +**Actors** + +- **Key**: `matchmaker["main"]` +- **Responsibility**: Finds or creates lobbies, tracks pending reservations, and maintains occupancy. +- **Actions** + - `findMatch` + - `pendingPlayerConnected` + - `updateMatch` + - `closeMatch` +- **Queues** + - `findMatch` + - `pendingPlayerConnected` + - `updateMatch` + - `closeMatch` +- **State** + - SQLite + - `matches` + - `pending_players` + - `player_count` includes connected and pending players + +- **Key**: `match[matchId]` +- **Responsibility**: Runs lobby/live/finished phases, owns player state, zone progression, and eliminations. +- **Actions** + - `connect` + - Movement and combat actions +- **Queues** + - None +- **State** + - JSON + - `phase` + - `players` + - `zone` + - `eliminations` + - `snapshot data` + +**Lifecycle** + +```mermaid +sequenceDiagram + participant C as Client + participant MM as matchmaker + participant M as match + + C->>MM: findMatch() + alt no open lobby + MM->>M: create(matchId) + end + MM-->>C: {matchId, playerId} + C->>M: connect(playerId) + M->>MM: pendingPlayerConnected(matchId, playerId) + MM-->>M: accepted + Note over M: lobby countdown -> live + M-->>C: snapshot + shoot events + M->>MM: closeMatch(matchId) +``` + +### Arena + +| Topic | Summary | +| --- | --- | +| Matchmaking | Mode-based fixed-capacity queues (`duo`, `squad`, `ffa`) that build only full matches and pre-assign teams (except FFA). | +| Netcode | Hybrid. Client owns movement plus prediction and smoothing. Server owns team or FFA assignment, projectiles, hit resolution, phase transitions, and scoring. | +| Tick Rate | 20 ticks/sec (`50ms`) with a tighter loop for live team and FFA snapshots. | +| Physics | Medium to high intensity; client movement with server validation and server-authoritative combat/entities. | + +**Actors** + +- **Key**: `matchmaker["main"]` +- **Responsibility**: Runs mode queues, builds full matches, assigns teams, and publishes assignments. +- **Actions** + - `queueForMatch` + - `unqueueForMatch` + - `matchCompleted` +- **Queues** + - `queueForMatch` + - `unqueueForMatch` + - `matchCompleted` +- **State** + - SQLite + - `player_pool` + - `matches` + - `assignments` keyed by connection and player + +- **Key**: `match[matchId]` +- **Responsibility**: Runs match phases and in-match player/team state for score and win conditions. +- **Actions** + - `connect` + - Gameplay actions +- **Queues** + - None +- **State** + - JSON + - `phase` + - `players` + - `team assignments` + - `score and win state` + +**Lifecycle** + +```mermaid +sequenceDiagram + participant C as Client + participant MM as matchmaker + participant M as match + + C->>MM: queueForMatch(mode) + Note over MM: enqueue in player_pool + Note over MM: fill when capacity reached + MM->>M: create(matchId, assignments) + Note over MM: persist assignments + MM-->>C: assignmentReady + C->>M: connect(playerId) + Note over M: waiting -> live when all players connect + M->>MM: matchCompleted(matchId) +``` + +### IO Style + +| Topic | Summary | +| --- | --- | +| Matchmaking | Open-lobby routing to the fullest room below capacity; room counts are heartbeated and new lobbies are auto-created when needed. | +| Netcode | Server-authoritative with interpolation. Client sends input intents and interpolates. Server owns movement, bounds, room membership, and canonical snapshots. | +| Tick Rate | 10 ticks/sec (`100ms`) with lightweight periodic room snapshots. | +| Physics | Low to medium intensity; server-authoritative kinematic movement, escalating to a physics engine only when collisions get complex. | + +**Actors** + +- **Key**: `matchmaker["main"]` +- **Responsibility**: Routes players into the fullest open lobby and tracks reservations and occupancy. +- **Actions** + - `findLobby` + - `pendingPlayerConnected` + - `updateMatch` + - `closeMatch` +- **Queues** + - `findLobby` + - `pendingPlayerConnected` + - `updateMatch` + - `closeMatch` +- **State** + - SQLite + - `matches` + - `pending_players` + - Occupancy includes pending reservations + +- **Key**: `match[matchId]` +- **Responsibility**: Runs per-match movement simulation and broadcasts snapshots. +- **Actions** + - `connect` + - `setInput` +- **Queues** + - None +- **State** + - JSON + - `players` + - `inputs` + - `movement state` + - `snapshot cache` + +**Lifecycle** + +```mermaid +sequenceDiagram + participant C as Client + participant MM as matchmaker + participant M as match + + C->>MM: findLobby() + alt no open lobby + MM->>M: create(matchId) + end + MM-->>C: {matchId, playerId} + C->>M: connect(playerId) + M->>MM: pendingPlayerConnected(matchId, playerId) + MM-->>M: accepted + Note over M: fixed tick simulation + M-->>C: snapshot events + M->>MM: closeMatch(matchId) +``` + +### Open World + +| Topic | Summary | +| --- | --- | +| Matchmaking | Client-driven chunk routing from world coordinates, with nearby chunk windows preloaded via adjacent chunk connections. | +| Netcode | Hybrid for sandbox (client movement with validation) or server-authoritative for MMO-like flows. Server owns chunk routing, persistence, and canonical world state. | +| Tick Rate | 10 ticks/sec per chunk actor (`100ms`), so load scales with active chunks. | +| Physics | Medium to high at scale; chunk-local simulation can be server-authoritative (MMO-like) or client movement with server validation (sandbox-like). | + +**Actors** + +- **Key**: `chunk[worldId,chunkX,chunkY]` +- **Responsibility**: Owns chunk-local players, blocks, movement tick, and chunk membership. +- **Actions** + - `connect` + - `enterChunk` + - `addPlayer` + - `setInput` + - `leaveChunk` + - `removePlayer` +- **Queues** + - None +- **State** + - JSON + - `connections` + - `players` + - `blocks` scoped to one chunk key + +**Lifecycle** + +```mermaid +sequenceDiagram + participant C as Client + participant CH as chunk + + Note over C: resolve chunk keys from world position + loop each visible chunk + C->>CH: connect(worldId, chunkX, chunkY, playerId) + Note over CH: store connection metadata + end + C->>CH: enterChunk/addPlayer + loop movement updates + C->>CH: setInput(...) + CH-->>C: snapshot + end + C->>CH: leaveChunk/removePlayer or disconnect + Note over CH: remove membership and metadata +``` + +### Party + +| Topic | Summary | +| --- | --- | +| Matchmaking | Host-created private party flow using party codes and explicit joins. | +| Netcode | Server-authoritative (basic logic). Server owns membership, host permissions, and phase transitions. | +| Tick Rate | No continuous tick; updates are event-driven (`join`, `start`, `finish`). | +| Physics | Low intensity for lobby-first flows; usually no dedicated physics or indexing unless you add realtime mini-games. | + +**Actors** + +- **Key**: `matchmaker["main"]` +- **Responsibility**: Handles party create/join flow, validates join tickets, and tracks party size. +- **Actions** + - `createParty` + - `joinParty` + - `verifyJoin` + - `updatePartySize` + - `closeParty` +- **Queues** + - `createParty` + - `joinParty` + - `verifyJoin` + - `updatePartySize` + - `closeParty` +- **State** + - SQLite + - `parties` + - `join_tickets` for party lookup and join validation + +- **Key**: `match[matchId]` +- **Responsibility**: Owns party members, host role, ready flags, and phase transitions. +- **Actions** + - `connect` + - `startGame` + - `finishGame` +- **Queues** + - None +- **State** + - JSON + - `members` + - `host` + - `ready state` + - `phase` + - `party events` + +**Lifecycle** + +### Host Flow + +```mermaid +sequenceDiagram + participant H as Host Client + participant MM as matchmaker + participant M as match + + H->>MM: createParty() + MM-->>H: {matchId, partyCode, playerId, joinToken} + H->>M: connect(playerId, joinToken) + M->>MM: verifyJoin(...) + MM-->>M: allowed + M->>MM: updatePartySize(playerCount) + H->>M: startGame() / finishGame() + M->>MM: closeParty(matchId) +``` + +### Joiner Flow + +```mermaid +sequenceDiagram + participant J as Joiner Client + participant MM as matchmaker + participant M as match + + J->>MM: joinParty(partyCode) + MM-->>J: {matchId, playerId, joinToken} + J->>M: connect(playerId, joinToken) + M->>MM: verifyJoin(...) + MM-->>M: allowed / denied + M->>MM: updatePartySize(playerCount) +``` + +### Ranked + +| Topic | Summary | +| --- | --- | +| Matchmaking | ELO-based queue pairing with a widening search window as wait time increases. | +| Netcode | Hybrid. Client owns movement with local prediction and interpolation. Server owns projectiles, hit resolution, match results, and rating updates. | +| Tick Rate | 20 ticks/sec (`50ms`) with fixed live ticks for deterministic pacing and broadcast cadence. | +| Physics | Medium to high intensity; client movement with server validation and server-authoritative combat/hit resolution. | + +**Actors** + +- **Key**: `matchmaker["main"]` +- **Responsibility**: Runs rating-based queueing, pairing, assignment persistence, and completion fanout. +- **Actions** + - `queueForMatch` + - `unqueueForMatch` + - `matchCompleted` +- **Queues** + - `queueForMatch` + - `unqueueForMatch` + - `matchCompleted` +- **State** + - SQLite + - `player_pool` + - `matches` + - `assignments` with rating window and connection scoping + +- **Key**: `match[matchId]` +- **Responsibility**: Runs ranked match phase, score, and winner reporting. +- **Actions** + - `connect` + - Gameplay actions +- **Queues** + - None +- **State** + - JSON + - `phase` + - `players` + - `score` + - `winner` + - `completion payload` + +- **Key**: `player[username]` +- **Responsibility**: Stores canonical player MMR and win/loss profile. +- **Actions** + - `initialize` + - `getRating` + - `applyMatchResult` +- **Queues** + - None +- **State** + - JSON + - `rating` + - `wins` + - `losses` + - `match counters` + +- **Key**: `leaderboard["main"]` +- **Responsibility**: Stores and serves top-ranked players. +- **Actions** + - `updatePlayer` +- **Queues** + - None +- **State** + - SQLite + - Leaderboard score rows + - Top-list ordering + +**Lifecycle** + +```mermaid +sequenceDiagram + participant C as Client + participant MM as matchmaker + participant P as player + participant M as match + participant LB as leaderboard + + C->>MM: queueForMatch(username) + MM->>P: initialize/getRating + P-->>MM: rating + Note over MM: store queue row + retry pairing + MM->>M: create(matchId, assigned players) + MM-->>C: assignmentReady + C->>M: connect(username) + M->>MM: matchCompleted(...) + MM->>P: applyMatchResult(...) + MM->>LB: updatePlayer(...) + Note over MM: remove matches + assignments rows +``` + +### Turn-Based + +| Topic | Summary | +| --- | --- | +| Matchmaking | Async private-invite and public-queue pairing in the same pattern. | +| Netcode | Server-authoritative (basic logic). Client can draft moves before submit. Server owns turn ownership, committed move log, turn order, and completion state. | +| Tick Rate | No continuous tick; move submission and turn transitions drive updates. | +| Physics | Very low intensity; no realtime physics loop, just discrete rules validation. Indexing is optional and mostly for board or query convenience at scale. | + +**Actors** + +- **Key**: `matchmaker["main"]` +- **Responsibility**: Handles private invite and public queue pairing for async matches. +- **Actions** + - `createGame` + - `joinByCode` + - `queueForMatch` + - `unqueueForMatch` + - `closeMatch` +- **Queues** + - `createGame` + - `joinByCode` + - `queueForMatch` + - `unqueueForMatch` + - `closeMatch` +- **State** + - SQLite + - `matches` + - `player_pool` + - `assignments` for invite and queue mapping + +- **Key**: `match[matchId]` +- **Responsibility**: Owns board state, turn order, move validation, and final result. +- **Actions** + - `connect` + - `makeMove` +- **Queues** + - None +- **State** + - JSON + - `board` + - `turns` + - `players` + - `connection presence` + - `result` + +**Lifecycle** + +### Public Queue + +```mermaid +sequenceDiagram + participant A as Client A + participant B as Client B + participant MM as matchmaker + participant M as match + + A->>MM: queueForMatch() + B->>MM: queueForMatch() + Note over MM: pair first two queued players + MM->>M: create(matchId) + seed X/O players + MM-->>A: assignment/match info + MM-->>B: assignment/match info + A->>M: connect(playerId) + B->>M: connect(playerId) + A->>M: makeMove() + B->>M: makeMove() + opt all players disconnected for timeout + Note over M: destroy after idle timeout + end + M->>MM: closeMatch(matchId) +``` + +### Private Invite + +```mermaid +sequenceDiagram + participant A as Client A + participant B as Client B + participant MM as matchmaker + participant M as match + + A->>MM: createGame() + MM-->>A: {matchId, playerId, inviteCode} + B->>MM: joinByCode(inviteCode) + MM->>M: create(matchId) + seed X/O players + MM-->>A: assignment/match info + MM-->>B: assignment/match info + A->>M: connect(playerId) + B->>M: connect(playerId) + A->>M: makeMove() + B->>M: makeMove() + M->>MM: closeMatch(matchId) +``` + +### Idle + +| Topic | Summary | +| --- | --- | +| Matchmaking | No matchmaker; each player uses a direct per-player actor and a shared leaderboard actor. | +| Netcode | Server-authoritative (basic logic). Client owns UI and build intent. Server owns resources, production rates, building validation, and leaderboard totals. | +| Tick Rate | No continuous tick; use `c.schedule.after(...)` for coarse intervals and compute offline catch-up from elapsed wall time. | +| Physics | None for standard idle loops; transitions are discrete (`build`, `collect`, `upgrade`) and do not need spatial indexing. | + +**Actors** + +- **Key**: `world[playerId]` +- **Responsibility**: Owns one player's progression, buildings, production scheduling, and state updates. +- **Actions** + - `initialize` + - `build` + - `collectProduction` +- **Queues** + - None +- **State** + - JSON + - Per-player buildings + - `resources` + - `timers` + - `progression state` + +- **Key**: `leaderboard["main"]` +- **Responsibility**: Stores global scores and serves leaderboard updates. +- **Actions** + - `updateScore` +- **Queues** + - `updateScore` +- **State** + - SQLite + - `scores` table keyed by player + - Current leaderboard totals + +**Lifecycle** + +```mermaid +sequenceDiagram + participant C as Client + participant W as world + participant LB as leaderboard + + C->>W: getOrCreate(playerId) + initialize() + Note over W: seed state + schedule collection + W-->>C: stateUpdate + loop gameplay loop + C->>W: build() / collectProduction() + W->>LB: updateScore(...) + Note over LB: upsert scores + LB-->>C: leaderboardUpdate + W-->>C: stateUpdate + end +``` + +_Source doc path: /cookbook/multiplayer-game_ diff --git a/.agents/skills/per-tenant-database/reference/cookbook/per-tenant-database.md b/.agents/skills/per-tenant-database/reference/cookbook/per-tenant-database.md new file mode 100644 index 0000000..e3c5616 --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/cookbook/per-tenant-database.md @@ -0,0 +1,119 @@ +# Database per Tenant + +> Source: `src/content/cookbook/per-tenant-database.mdx` +> Canonical URL: https://rivet.dev/cookbook/per-tenant-database +> Description: 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. + +--- +Patterns 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. + +## Starter Code + +Start with the working example on [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/per-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. + +| Topic | Summary | +| --- | --- | +| Isolation | One `companyDatabase` actor per tenant, keyed by company name. Switching tenants swaps the entire dataset. | +| State | JSON actor state holding `employees` and `projects` arrays plus timestamps. No SQLite, no queues, no scheduling. | +| Realtime | Every write action mutates state, then broadcasts a typed event (`employeeAdded`, `projectAdded`) to all connected clients of that tenant. | +| Auth | None. The sign-in screen is cosmetic. Production guidance is in the [security checklist](#security-checklist). | + +## The Isolation Model + +The 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: + +- **One actor per tenant**: `companyDatabase[tenantId]` addresses exactly one actor instance. Two tenants can never share an actor. +- **One dataset per tenant**: All reads and writes go through that actor's [state](/docs/actors/state), 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. +- **No key injection**: Keys are arrays, not interpolated strings. `key: [tenantId]` cannot be escaped the way `"tenant:" + tenantId` string concatenation can. See [Keys](/docs/actors/keys). + +The example's test ([tests/per-tenant-database.test.ts](https://github.com/rivet-dev/rivet/tree/main/examples/per-tenant-database/tests/per-tenant-database.test.ts)) proves the isolation: data written to `companyDatabase["Alpha Co"]` never appears in `companyDatabase["Beta Co"]`. + +## Choosing a State Backend + +The example uses plain JSON actor state. The same key-equals-tenant model works with any actor state backend. + +| Backend | Use When | Docs | Working Code | +| --- | --- | --- | --- | +| JSON actor state | Small datasets, simple reads, whole dataset fits comfortably in memory. What the example uses. | [State](/docs/actors/state) | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/per-tenant-database) | +| Actor SQLite (`rivetkit/db`) | Tables, indexes, SQL queries, larger-than-memory data, per-tenant relational schema. | [SQLite](/docs/actors/sqlite) | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/kitchen-sink/src/actors/state/sqlite-raw.ts) | +| SQLite + Drizzle | Typed schema, query builder, and generated migration files on top of actor SQLite. | [SQLite + Drizzle](/docs/actors/sqlite-drizzle) | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/kitchen-sink/src/actors/state/sqlite-drizzle/) | + +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. + +## Migrations + +The per-tenant example has no migrations because JSON state has no schema. When you adopt SQLite, migrations run per tenant database: + +- **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](/docs/actors/sqlite). +- **Drizzle**: `drizzle-kit` generates migration files from your typed schema, and `db({ schema, migrations })` applies them when the actor wakes. See [SQLite + Drizzle](/docs/actors/sqlite-drizzle). + +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. + +## Tenant Id Must Come From Auth + +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): + +- Derive the tenant id from a verified credential, such as a JWT claim, never from user input. +- Validate the credential against `c.key` in `onBeforeConnect` (pass/fail) or `createConnState` (store the verified user on connection state). See [Authentication](/docs/actors/authentication) and [Connections](/docs/actors/connections). +- Add per-action permission checks on top of connection-level auth. See [Access Control](/docs/actors/access-control). + +## Actors + +- **Key**: `companyDatabase[companyName]` (single-element array key; `c.key[0]` is the company name) +- **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. +- **Actions** + - `addEmployee` + - `listEmployees` + - `addProject` + - `listProjects` + - `getStats` +- **Queues** + - None +- **Events** + - `employeeAdded` + - `projectAdded` +- **State** + - JSON + - `company_name` + - `employees` + - `projects` + - `created_at` + - `updated_at` + +Every 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](/docs/actors/actions) and [Events](/docs/actors/events). + +## Lifecycle + +```mermaid +sequenceDiagram + participant A as Tenant A client + participant DA as companyDatabase A + participant B as Tenant B client + participant DB as companyDatabase B + + Note over A: authenticate and derive tenant id + A->>DA: connect with key [tenantA] + Note over DA: createState seeds company_name, employees, projects + A->>DA: listEmployees() + listProjects() + getStats() + A->>DA: addEmployee(name, role) + DA-->>A: employeeAdded event + B->>DB: connect with key [tenantB] + Note over DB: separate actor, separate dataset + B->>DB: listEmployees() + DB-->>B: tenant B data only +``` + +In 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. + +## Security Checklist + +The example ships with none of these. Apply all of them before production. + +- **Tenant identity**: Derive the tenant id from a verified JWT claim, never from a client-supplied string. +- **Connection validation**: In `onBeforeConnect` or `createConnState`, verify the credential's tenant claim matches `c.key` and reject mismatches. +- **Per-action authorization**: Check the caller's role before mutating actions (`addEmployee`, `addProject`), not just at connect time. See [Access Control](/docs/actors/access-control). +- **Input validation**: Clamp name and role lengths and validate enums. The example only trims input and substitutes fallback defaults. +- **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. +- **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](/docs/actors/sqlite) when the dataset outgrows memory. + +_Source doc path: /cookbook/per-tenant-database_ diff --git a/.agents/skills/per-tenant-database/reference/cookbook/vpc-air-gapped.md b/.agents/skills/per-tenant-database/reference/cookbook/vpc-air-gapped.md new file mode 100644 index 0000000..f7d87a3 --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/cookbook/vpc-air-gapped.md @@ -0,0 +1,136 @@ +# Deploying Rivet in a VPC or Air-Gapped Network + +> Source: `src/content/cookbook/vpc-air-gapped.mdx` +> Canonical URL: https://rivet.dev/cookbook/vpc-air-gapped +> Description: Run Rivet entirely inside your own perimeter: single-binary or Docker Compose install, file system storage with no database infrastructure, and no outbound telemetry by default. + +--- +Patterns for running self-hosted Rivet inside a private network: a VPC without internet egress, an on-premises rack, or a fully air-gapped environment. The engine is one service, the recommended single-node storage backend is the local file system, and the engine makes no outbound connections by default. Self-hosting is the only Rivet deployment model that supports air-gapped networks; see the [Self-Hosting Overview](/docs/self-hosting) for the full comparison with BYOC. + +## What Runs Inside the Perimeter + +A self-hosted deployment has three components, all of which live inside your network: + +| Component | Role | Inside the perimeter | +| --- | --- | --- | +| Your backend | Your application server, including the runner that executes actor code | Yes | +| Rivet Engine | Orchestration service that manages actor lifecycle, routes messages, and serves the dashboard and APIs | Yes | +| Storage | Persistence for actor state. Local file system for single-node, PostgreSQL or FoundationDB for multi-node | Yes | + +There is no license server, no Rivet Cloud account, and no callback to `rivet.dev`. Clients inside the perimeter reach actors through the engine's gateway over your private network. See [Architecture](/docs/self-hosting#architecture). + +## Single-Binary Install + +The engine compiles to a single `rivet-engine` binary. Build it from source outside the perimeter, then copy the binary across the boundary: + +```bash +git clone https://github.com/rivet-dev/rivet.git +cd rivet +cargo build --release -p rivet-engine +# Copy target/release/rivet-engine into the perimeter. +``` + +Prebuilt binaries are coming soon; see [Installing Rivet Engine](/docs/self-hosting/install) for current options. + +Run it with the file system backend, which stores everything on local disk and is the production-ready choice for single-node deployments. The [File System](/docs/self-hosting/filesystem) docs list air-gapped environments as a primary use case because it needs no database infrastructure: + +```bash +RIVET__database__file_system__path="/var/lib/rivet/data" ./rivet-engine +``` + +Configuration can also come from files. The engine discovers config at `/etc/rivet/config.json` on Linux (JSON, JSON5, JSONC, YAML, and YML are all supported), and `--config` overrides the path. Environment variables use the `RIVET__` prefix with `__` as the separator. See [Configuration](/docs/self-hosting/configuration). + +The engine serves its own dashboard on port `6420`, so inspection and namespace management work with nothing but a browser inside the perimeter. + +## Docker Compose Deployment + +For Docker hosts without registry access, move the engine image across the boundary the standard way: + +```bash +# Outside the perimeter. +docker pull rivetdev/engine:latest +docker save rivetdev/engine:latest -o rivet-engine.tar +# Inside the perimeter. +docker load -i rivet-engine.tar +``` + +Then run the engine and your app together in one Compose file: + +```yaml +services: + rivet-engine: + image: rivetdev/engine:latest + ports: + - "6420:6420" + volumes: + - rivet-data:/data + environment: + RIVET__FILE_SYSTEM__PATH: "/data" + restart: unless-stopped + + my-app: + build: . + environment: + RIVET_ENDPOINT: "http://default:admin@rivet-engine:6420" + depends_on: + - rivet-engine + restart: unless-stopped + +volumes: + rivet-data: +``` + +`RIVET_ENDPOINT` uses the format `http://namespace:token@host:port` and tells your app to connect to the engine as a runner instead of running standalone. After both services start, register your runner with the engine through the dashboard or its API. The full walkthrough, including PostgreSQL setup for multi-node deployments, is in [Docker Compose](/docs/self-hosting/docker-compose). + +## No Outbound Telemetry + +The engine exports traces and metrics only when you opt in with OpenTelemetry. Export is disabled unless `RIVET_OTEL_ENABLED=1` is set, and the export target defaults to a local collector at `http://localhost:4317`. With no configuration, nothing crosses the perimeter. + +When you want observability, keep it inside the network: + +- Set `RIVET_OTEL_ENABLED=1` and point `RIVET_OTEL_GRPC_ENDPOINT` at a collector you run inside the perimeter. +- Adjust `RIVET_OTEL_SAMPLER_RATIO` to control trace sampling. +- Use the engine's health endpoint for liveness and readiness probes. + +See the [Production Checklist](/docs/self-hosting/production-checklist) for monitoring guidance. + +## Embedding Rivet in a Customer's Environment + +If you ship software that runs inside your customers' VPCs, the same setup turns Rivet into an internal component of your product rather than a service your customers must reach over the internet: + +- **Ship the engine next to your app.** Add `rivetdev/engine` to the Compose file or chart you already deliver. Your app finds it over the private network via `RIVET_ENDPOINT`, so one artifact deploys the whole stack. +- **One namespace per install.** The endpoint URL carries the namespace and token (`http://namespace:token@host:port`), so a single image works across customer deployments. See [Endpoints](/docs/general/endpoints). +- **Generate a strong admin token per install.** Replace the default token and keep it server-side. Never include the admin token in `RIVET_PUBLIC_ENDPOINT` or anywhere clients can read it. +- **Public endpoint only when needed.** `RIVET_PUBLIC_ENDPOINT` with a public (`pk_`) token is only required when browser clients connect to actors in [serverless runtime mode](/docs/general/runtime-modes). Backend-only deployments can skip it entirely. +- **TLS at the customer's edge.** Terminate TLS with the customer's reverse proxy or load balancer in front of the engine. + +## Scaling Past One Node + +| Backend | Use when | Status | +| --- | --- | --- | +| [File System](/docs/self-hosting/filesystem) (RocksDB-based) | Single-node deployments, including air-gapped installs | Production-ready, single node only | +| [PostgreSQL](/docs/self-hosting/postgres) | Multi-node deployments | Recommended for multi-node today, but experimental | +| FoundationDB | Largest production deployments | [Enterprise](/sales) | + +For multi-node deployments, run two or more engine nodes behind a load balancer and add NATS for pub/sub, which replaces the default PostgreSQL `LISTEN`/`NOTIFY` path at high throughput. Neither is needed for a single-node file system install. See the [Production Checklist](/docs/self-hosting/production-checklist). + +## Perimeter Checklist + +- **Admin token**: Generate a strong, random token for engine authentication and verify it is not exposed to clients. +- **TLS termination**: Encrypt connections to the engine via a reverse proxy or load balancer. +- **No public exposure**: Keep port `6420` reachable only from inside the perimeter unless clients outside it genuinely need access. +- **Health checks**: Configure liveness and readiness probes against the engine health endpoint. +- **Telemetry**: Leave OpenTelemetry export off, or point it at a collector inside the network. +- **Backups**: With the file system backend, back up the data directory. With PostgreSQL, configure automated backups and failover. + +## Full Configuration + +- [Self-Hosting Overview](/docs/self-hosting) for architecture and the self-host vs BYOC comparison +- [Installing Rivet Engine](/docs/self-hosting/install) for Docker, binary, and source installs +- [Docker Container](/docs/self-hosting/docker-container) and [Docker Compose](/docs/self-hosting/docker-compose) for container deployments +- [Kubernetes](/docs/self-hosting/kubernetes) for cluster deployments +- [Configuration](/docs/self-hosting/configuration) for every option and the full JSON schema +- [Endpoints](/docs/general/endpoints) for connecting your backend and clients +- [Production Checklist](/docs/self-hosting/production-checklist) before going live + +_Source doc path: /cookbook/vpc-air-gapped_ diff --git a/.agents/skills/per-tenant-database/reference/deploy/aws-ecs.md b/.agents/skills/per-tenant-database/reference/deploy/aws-ecs.md new file mode 100644 index 0000000..ab381ce --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/deploy/aws-ecs.md @@ -0,0 +1,13 @@ +# Deploying to AWS ECS + +> Source: `src/content/docs/deploy/aws-ecs.mdx` +> Canonical URL: https://rivet.dev/docs/deploy/aws-ecs +> Description: Run your backend on Amazon ECS with Fargate. + +--- +See the [AWS ECS template](https://github.com/rivet-dev/template-aws-ecs) for deployment instructions: + +- [Deploy with Terraform](https://github.com/rivet-dev/template-aws-ecs#option-a-deploy-with-terraform) +- [Deploy with AWS CLI](https://github.com/rivet-dev/template-aws-ecs#option-b-deploy-with-aws-cli) + +_Source doc path: /docs/deploy/aws-ecs_ diff --git a/.agents/skills/per-tenant-database/reference/deploy/aws-lambda.md b/.agents/skills/per-tenant-database/reference/deploy/aws-lambda.md new file mode 100644 index 0000000..f1327cc --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/deploy/aws-lambda.md @@ -0,0 +1,10 @@ +# Deploy To Amazon Web Services Lambda + +> Source: `src/content/docs/deploy/aws-lambda.mdx` +> Canonical URL: https://rivet.dev/docs/deploy/aws-lambda +> Description: _AWS Lambda is coming soon_ + +--- + + +_Source doc path: /docs/deploy/aws-lambda_ diff --git a/.agents/skills/per-tenant-database/reference/deploy/cloudflare.md b/.agents/skills/per-tenant-database/reference/deploy/cloudflare.md new file mode 100644 index 0000000..970078e --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/deploy/cloudflare.md @@ -0,0 +1,53 @@ +# Deploying to Cloudflare Workers + +> Source: `src/content/docs/deploy/cloudflare.mdx` +> Canonical URL: https://rivet.dev/docs/deploy/cloudflare +> Description: Deploy an existing Rivet project to Cloudflare Workers. + +--- +This guide covers deploying an existing Rivet project to Cloudflare Workers. + +## Prerequisites + +- [Cloudflare account](https://dash.cloudflare.com/) +- [`wrangler`](https://developers.cloudflare.com/workers/wrangler/) configured for your account +- A Rivet namespace from the [Rivet Dashboard](https://dashboard.rivet.dev/) or a self-hosted Rivet Engine + +## Steps + +### Set up your project + +Follow the [Cloudflare Workers Quickstart](/docs/actors/quickstart/cloudflare) to set up your project locally. + +### Configure Wrangler + +Set your Rivet connection values as Worker variables. Find `RIVET_ENDPOINT` and `RIVET_PUBLIC_ENDPOINT` in the [Rivet Dashboard](https://dashboard.rivet.dev/) under **Settings → Namespace → Advanced → Backend Configuration** and copy them in. + +```toml wrangler.toml +name = "rivetkit-cloudflare" +main = "src/index.ts" +compatibility_date = "2025-04-01" +compatibility_flags = ["nodejs_compat"] + +[vars] +RIVET_ENDPOINT = "https://your-namespace:sk_...@api.rivet.dev" +RIVET_PUBLIC_ENDPOINT = "https://your-namespace@api.rivet.dev" +``` + +### Deploy + +```sh +npx wrangler deploy +``` + +### Register the Serverless Runner URL + +After deploy, set the Worker URL with the `/api/rivet` path as the serverless runner URL in Rivet. + +## Related + +- [Cloudflare Workers Quickstart](/docs/actors/quickstart/cloudflare) +- [Deploying to Supabase Functions](/docs/deploy/supabase) +- [SQLite](/docs/actors/sqlite) + +_Source doc path: /docs/deploy/cloudflare_ diff --git a/.agents/skills/per-tenant-database/reference/deploy/container-runner.md b/.agents/skills/per-tenant-database/reference/deploy/container-runner.md new file mode 100644 index 0000000..7bb0c5a --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/deploy/container-runner.md @@ -0,0 +1,95 @@ +# Container Runner + +> Source: `src/content/docs/deploy/container-runner.mdx` +> Canonical URL: https://rivet.dev/docs/deploy/container-runner +> Description: Run any containerized server as a Rivet Actor. + +--- +The container runner (`rivet-container-runner`) is an adapter for running arbitrary containers as Rivet Actors. Use it for non-RivetKit workloads such as Unity or Godot dedicated game servers and batch jobs like FFmpeg transcoding. + +## Steps + +### Prerequisites + +- A containerized server (a Unity or Godot dedicated server, a plain Node process, or any HTTP/WebSocket server) +- Access to the [Rivet Cloud](https://dashboard.rivet.dev/) or a [self-hosted Rivet Engine](/docs/general/self-hosting) +- Docker running locally + +### Install in Your Container + +Download the static binary from Rivet's release artifacts in your Dockerfile and set it as the entrypoint, passing your server's launch command after `--`: + +```dockerfile @nocheck +FROM debian:bookworm-slim +RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates curl && rm -rf /var/lib/apt/lists/* + +# Install the Rivet container runner. +RUN curl -fsSL https://releases.rivet.dev/rivet/latest/container-runner/rivet-container-runner-x86_64-unknown-linux-musl \ + -o /usr/local/bin/rivet-container-runner \ + && chmod +x /usr/local/bin/rivet-container-runner + +# Your server binary and assets. +COPY build/ /game/ +WORKDIR /game + +ENTRYPOINT ["rivet-container-runner", "--", "./GameServer", "-batchmode", "-nographics", "-logFile", "-"] +``` + +Artifacts are published for `x86_64-unknown-linux-musl` and `aarch64-unknown-linux-musl`. The binaries are fully static, so they run in any Linux base image, including `scratch`. Pin a version by replacing `latest` with a release version, for example `https://releases.rivet.dev/rivet/2.3.3/container-runner/rivet-container-runner-x86_64-unknown-linux-musl`. + +### Deploy + +Deploy the image to [Rivet Compute](/docs/deploy/rivet-compute) with the CLI. For game servers, configure the pool with one actor per instance: + +```bash +npx @rivetkit/cli deploy \ + --token "$RIVET_CLOUD_TOKEN" \ + --instance-request-concurrency 1 \ + --dockerfile Dockerfile +``` + +### Create Actors and Connect Clients + +Create actors against the pool's runner (`default`) and connect clients through the gateway URL shown in the dashboard. WebSocket clients connect at the bare gateway URL with the `rivet` WebSocket subprotocol. + +## How It Works + +1. The engine cold-starts your container and calls `POST /api/rivet/start` on the port it injects as `RIVET_PORT`. +2. The runner spawns your server as a child process with `PORT` set to the child port, waits for the port to open, and reports the actor as running. +3. Gateway traffic for the actor arrives over Rivet's tunnel and is proxied to `127.0.0.1:`. WebSocket clients connect at the bare gateway URL with the `rivet` WebSocket subprotocol. Raw HTTP reaches the child under the `/request/*` prefix on the actor surface (the prefix is stripped before proxying); other paths are reserved for the runtime's own endpoints. +4. Child stdout and stderr are re-emitted with an `[actorId=... key=...]` prefix so actor logs are attributed in the dashboard. +5. When an actor stops, the runner sends its child `SIGTERM`, escalates to `SIGKILL` after a grace period, and exits the process once no actors remain. + +## Configuration + +All flags can also be set through environment variables: + +| Flag | Environment variable | Default | Description | +| --- | --- | --- | --- | +| `--port` | `RIVET_PORT` / `PORT` | `8080` | Serverless front-door HTTP port. Rivet Compute injects `RIVET_PORT` automatically. | +| `--child-port` | `CHILD_PORT` | `7770` | First local child port; each actor's child gets the next free port at or above this, exported to the child as `PORT`. | +| `--actor-name` | `RIVET_ACTOR_NAME` | `game` | Actor name this runner serves. | +| `--runner-version` | `RIVET_RUNNER_VERSION` | `1` | Version reported to the engine, used to drain old runners on deploy. | +| `--base-path` | `RIVET_SERVERLESS_BASE_PATH` | `/api/rivet` | Base path the engine calls for serverless start. | +| `--stop-grace-secs` | `RIVET_STOP_GRACE_SECS` | `25` | `SIGTERM` to `SIGKILL` grace period when stopping the child. Capped to a few seconds when the platform itself is reclaiming the instance, so shutdown fits inside the platform's own kill window. | +| `--readiness-timeout-secs` | `RIVET_READINESS_TIMEOUT_SECS` | `30` | How long to wait for the child's port to open before failing the start. | + +### Per-Actor Input + +The actor's `input` payload can override the launch spec per actor. All fields are optional and fall back to the entrypoint command. RivetKit clients pass this object directly; when creating actors through the raw engine API, encode it as CBOR before base64-encoding the `input` field: + +```json +{ + "command": ["./GameServer", "-batchmode"], + "args": ["-extra-flag"], + "env": { "MATCH_MODE": "ranked" } +} +``` + +`command` replaces the entrypoint command template, `args` are appended to it, and `env` adds environment variables for the child. + +## Source and Examples + +The runner and a full end-to-end example, including a Unity FishNet demo project and a local test harness, live in the Rivet repository under [`container-runner/`](https://github.com/rivet-dev/rivet/tree/main/container-runner). + +_Source doc path: /docs/deploy/container-runner_ diff --git a/.agents/skills/per-tenant-database/reference/deploy/freestyle.md b/.agents/skills/per-tenant-database/reference/deploy/freestyle.md new file mode 100644 index 0000000..eb6ed57 --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/deploy/freestyle.md @@ -0,0 +1,136 @@ +# Deploying to Freestyle + +> Source: `src/content/docs/deploy/freestyle.mdx` +> Canonical URL: https://rivet.dev/docs/deploy/freestyle +> Description: Deploy RivetKit app to Freestyle.sh, a cloud platform for running AI-generated code with built-in security and scalability. + +--- +Freestyle provides built-in security for running untrusted AI-generated code, making it ideal for AI agent applications. Using Rivet, it is easy to deploy your vibe-coded or user-provided RivetKit backends straight to Freestyle. + +- [Freestyle + Rivet](https://github.com/rivet-dev/rivet/tree/main/examples/freestyle) — Complete example of deploying RivetKit app to Freestyle.sh. + +## Setup + +### Install packages + +Install RivetKit and Hono and create your registry: + +```bash +npm install rivetkit hono +``` + +### Configure serverless driver + +Update your server code to run the registry serverless with Deno. + +```typescript index.ts @hide +import { actor, setup } from "rivetkit"; + +export const counter = actor({ + state: { count: 0 }, + actions: { + increment: (c, x: number) => { + c.state.count += x; + return c.state.count; + }, + }, +}); + +export const registry = setup({ + use: { counter }, +}); +registry.start(); +``` + +```typescript server.ts @nocheck +import { registry } from "./index"; + +// Freestyle uses Deno under the hood for web deployments +// @ts-ignore Deno is a Freestyle runtime global +Deno.serve((request: Request) => registry.handler(request)); +``` + +The `Deno.serve` API is provided by Freestyle's runtime environment. The `@ts-ignore` comment suppresses TypeScript errors for this platform-specific API. + +### Deploy to Freestyle + +Deploy your application to Freestyle with the correct configuration. Create a deployment script or add this to your existing deployment process: + +```typescript @nocheck +const FREESTYLE_DOMAIN = "my-domain.style.dev"; // Change to your desired Freestyle domain + +declare const freestyle: any; +declare const buildDir: string; + +const res = await freestyle.deployWeb(buildDir, { + envVars: { + FREESTYLE_ENDPOINT: `https://${FREESTYLE_DOMAIN}`, + RIVET_RUNNER_KIND: "serverless", + // For self-hosted instances: + // RIVET_ENDPOINT: "http://127.0.0.1:6420", + RIVET_ENDPOINT: "api.rivet.dev", + }, + timeout: 60 * 5, // Increases max request lifetime on the runner + entrypoint: "server.ts", // File which starts serverless runner + domains: [FREESTYLE_DOMAIN], + build: false, +}); +``` + +Details on `buildDir` and other settings are available on [Freestyle docs](https://docs.freestyle.sh/web/web). + +Run this deployment script to push your application to Freestyle. + +**Deployment Configuration:** + +- `timeout: 60 * 5` - Set timeout to 5 minutes for actor operations - it's important to keep this high +- `entrypoint: "server.ts"` - Entry point file with your serverless setup +- `domains` - Your Freestyle domain(s) +- `build: false` - Disable build if you're pre-building your assets + +### Configure runner + +Update the runner configuration on the Rivet side to connect with your Freestyle deployment. Create a configuration script and run it after your Freestyle deployment is live: + +```typescript @nocheck +import { RivetClient } from "@rivetkit/engine-api-full"; + +const rivet = new RivetClient({ + environment: "https://api.rivet.dev", + token: process.env.RIVET_API_TOKEN, +}); + +const FREESTYLE_DOMAIN = "my-domain.style.dev"; // Change to your desired Freestyle domain +const RIVET_NAMESPACE = "my-rivet-namespace"; // Change to your Rivet namespace + +await rivet.runnerConfigsUpsert("freestyle-runner", { + namespace: RIVET_NAMESPACE, + datacenters: { + default: { + serverless: { + url: `https://${FREESTYLE_DOMAIN}/start`, + runnersMargin: 1, + minRunners: 1, + maxRunners: 1, + slotsPerRunner: 1, + // Must be shorter than Freestyle request `timeout` config + requestLifespan: 60 * 5 - 5, + }, + }, + }, +}); +``` + +Execute this configuration script to register your Freestyle deployment with Rivet. + +**Runner Configuration:** + +- `url` - Freestyle deployment URL with `/start` endpoint +- `runnersMargin` - Buffer of runners to maintain +- `minRunners/maxRunners` - Scaling limits +- `slotsPerRunner` - Concurrent actors per runner +- `requestLifespan` - Request timeout (slightly less than Freestyle timeout) + +Once executed, Rivet will be connected to your Freestyle serverless instance. + +_Source doc path: /docs/deploy/freestyle_ diff --git a/.agents/skills/per-tenant-database/reference/deploy/gcp-cloud-run.md b/.agents/skills/per-tenant-database/reference/deploy/gcp-cloud-run.md new file mode 100644 index 0000000..f140950 --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/deploy/gcp-cloud-run.md @@ -0,0 +1,74 @@ +# Deploying to Google Cloud Run + +> Source: `src/content/docs/deploy/gcp-cloud-run.mdx` +> Canonical URL: https://rivet.dev/docs/deploy/gcp-cloud-run +> Description: Deploy your RivetKit app to Google Cloud Run. + +--- +## Steps + +### Prerequisites + +- Google Cloud project with Cloud Run and Artifact Registry enabled +- `gcloud` CLI authenticated (`gcloud auth login`) and project set (`gcloud config set project YOUR_PROJECT`) +- Artifact Registry repository or Container Registry enabled +- Your RivetKit app + - If you don't have one, see the [Quickstart](/docs/actors/quickstart) page or our [Examples](https://github.com/rivet-dev/rivet/tree/main/examples) +- Access to the [Rivet Cloud](https://dashboard.rivet.dev/) or a [self-hosted Rivet Engine](/docs/general/self-hosting) + +### Package Your App + +Create a `Dockerfile` in your project root: + +```dockerfile +FROM node:20-alpine +WORKDIR /app +COPY package*.json ./ +RUN npm ci --omit=dev +COPY . . +ENV PORT=8080 +CMD ["node", "server.js"] +``` + +### Build and Push the Image + +Use Cloud Build to build and push the image. Replace the region and repository with your own. + +```bash +gcloud builds submit --tag us-central1-docker.pkg.dev/YOUR_PROJECT/rivetkit-app/rivetkit-app:latest +``` + +### Set Environment Variables + +After creating your project on the Rivet dashboard, select Google Cloud Run as your provider. You'll be provided `RIVET_ENDPOINT` and `RIVET_PUBLIC_ENDPOINT` environment variables to use when deploying. + +### Deploy to Cloud Run + +Deploy the service to Cloud Run, passing the Rivet environment variables. Adjust the region and image as needed. + +```bash +gcloud run deploy rivetkit-app \ + --image us-central1-docker.pkg.dev/YOUR_PROJECT/rivetkit-app/rivetkit-app:latest \ + --region us-central1 \ + --allow-unauthenticated \ + --min-instances 1 \ + --set-env-vars RIVET_ENDPOINT=,RIVET_PUBLIC_ENDPOINT= +``` + +### Connect to Rivet + +1. After deployment, note the service URL (e.g. `https://rivetkit-app-xxxxx-uc.a.run.app`) +2. On the Rivet dashboard, paste your URL with the `/api/rivet` path into the connect form (e.g. `https://rivetkit-app-xxxxx-uc.a.run.app/api/rivet`) +3. Click "Done" + +### Verify + +Confirm the service is running: + +```bash +gcloud run services describe rivetkit-app --region us-central1 --format 'value(status.conditions[?type="Ready"].status)' +``` + +Your app should appear as connected on the Rivet dashboard once the service reports ready. + +_Source doc path: /docs/deploy/gcp-cloud-run_ diff --git a/.agents/skills/per-tenant-database/reference/deploy/hetzner.md b/.agents/skills/per-tenant-database/reference/deploy/hetzner.md new file mode 100644 index 0000000..5b61024 --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/deploy/hetzner.md @@ -0,0 +1,10 @@ +# Deploying to Hetzner + +> Source: `src/content/docs/deploy/hetzner.mdx` +> Canonical URL: https://rivet.dev/docs/deploy/hetzner +> Description: Please see the VM & Bare Metal guide. + +--- + + +_Source doc path: /docs/deploy/hetzner_ diff --git a/.agents/skills/per-tenant-database/reference/deploy/kubernetes.md b/.agents/skills/per-tenant-database/reference/deploy/kubernetes.md new file mode 100644 index 0000000..43496a9 --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/deploy/kubernetes.md @@ -0,0 +1,121 @@ +# Deploying to Kubernetes + +> Source: `src/content/docs/deploy/kubernetes.mdx` +> Canonical URL: https://rivet.dev/docs/deploy/kubernetes +> Description: Deploy your RivetKit app to any Kubernetes cluster. + +--- +## Steps + +### Prerequisites + +- A Kubernetes cluster with `kubectl` access (EKS, GKE, k3s, etc.) +- Container registry credentials (Docker Hub, GHCR, GCR, etc.) +- Your RivetKit app + - If you don't have one, see the [Quickstart](/docs/actors/quickstart) page or our [Examples](https://github.com/rivet-dev/rivet/tree/main/examples) +- Access to the [Rivet Cloud](https://dashboard.rivet.dev/) or a [self-hosted Rivet Engine](/docs/general/self-hosting) + +### Package Your App + +Create a `Dockerfile` in your project root: + +```dockerfile +FROM node:20-alpine +WORKDIR /app +COPY package*.json ./ +RUN npm ci --omit=dev +COPY . . +ENV PORT=8080 +CMD ["node", "server.js"] +``` + +### Build and Push the Image + +```bash +docker build -t registry.example.com/your-team/rivetkit-app:latest . +docker push registry.example.com/your-team/rivetkit-app:latest +``` + +Replace `registry.example.com/your-team` with your registry path. Auth with `docker login` first if needed. + +### Set Environment Variables + +After creating your project on the Rivet dashboard, select Kubernetes as your provider. You'll be provided `RIVET_ENDPOINT` and `RIVET_PUBLIC_ENDPOINT` environment variables. + +Create a `rivetkit-secrets.yaml` for your environment variables: + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: rivetkit-secrets +type: Opaque +stringData: + RIVET_ENDPOINT: + RIVET_PUBLIC_ENDPOINT: +``` + +### Deploy to Kubernetes + +Create a `deployment.yaml`: + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: rivetkit-app +spec: + replicas: 1 + selector: + matchLabels: + app: rivetkit-app + template: + metadata: + labels: + app: rivetkit-app + spec: + # Allow enough time for actors to gracefully stop on SIGTERM. + # The runner waits up to 30m for actors to finish. + # Add buffer for runner shutdown overhead after actors stop. + # See: /docs/actors/versions#graceful-shutdown-sigterm + terminationGracePeriodSeconds: 2100 + containers: + - name: rivetkit-app + image: registry.example.com/your-team/rivetkit-app:latest + envFrom: + - secretRef: + name: rivetkit-secrets +``` + +Apply both manifests: + +```bash +kubectl apply -f rivetkit-secrets.yaml +kubectl apply -f deployment.yaml +``` + +### Connect to Rivet + +1. Add a `Service` and Ingress to expose your app externally (e.g. `my-app.example.com`) +2. On the Rivet dashboard, paste your domain with the `/api/rivet` path into the connect form (e.g. `https://my-app.example.com/api/rivet`) +3. Click "Done" + +Rivet envoys connect to your app over long-lived WebSockets, and your app's clients (browsers, SDKs) do the same. Default Ingress and cloud load balancer idle timeouts (typically 30 to 60 seconds) drop these connections and cause reconnect storms. + +Raise the idle / read / send timeout on every Ingress and load balancer in front of your app to at least 1 hour (`3600` seconds). Examples: + +- **NGINX Ingress**: `nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"` and `nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"` +- **AWS Load Balancer Controller (ALB)**: `alb.ingress.kubernetes.io/load-balancer-attributes: idle_timeout.timeout_seconds=3600` +- **GCE Ingress (GKE)**: set `timeoutSec: 3600` on the `BackendConfig` referenced by the Service. + +### Verify + +Check that the pod is running: + +```bash +kubectl get pods -l app=rivetkit-app +``` + +Your app should appear as connected on the Rivet dashboard once the pod is ready. + +_Source doc path: /docs/deploy/kubernetes_ diff --git a/.agents/skills/per-tenant-database/reference/deploy/railway.md b/.agents/skills/per-tenant-database/reference/deploy/railway.md new file mode 100644 index 0000000..640ac37 --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/deploy/railway.md @@ -0,0 +1,45 @@ +# Deploying to Railway + +> Source: `src/content/docs/deploy/railway.mdx` +> Canonical URL: https://rivet.dev/docs/deploy/railway +> Description: Deploy your RivetKit app to Railway. + +--- +## Steps + +### Prerequisites + +- [Railway account](https://railway.app) +- Your RivetKit app + - If you don't have one, see the [Quickstart](/docs/actors/quickstart) page or our [Examples](https://github.com/rivet-dev/rivet/tree/main/examples) +- Access to the [Rivet Cloud](https://dashboard.rivet.dev/) or a [self-hosted Rivet Engine](/docs/general/self-hosting) + +### Deploy to Railway + +1. Connect your GitHub account to Railway +2. Select your repository containing your RivetKit app +3. Railway will automatically detect and deploy your app + +See [Railway's deployment docs](https://docs.railway.com/quick-start) for more details. + +### Set Environment Variables + +After creating your project on the Rivet dashboard, select Railway as your provider. You'll be provided `RIVET_ENDPOINT` and `RIVET_PUBLIC_ENDPOINT` environment variables to add to your Railway service. + +See [Railway's environment variables docs](https://docs.railway.com/guides/variables#service-variables) for more details. + +### Connect to Rivet + +1. In your Railway project, go to **Settings > Networking** +2. Click **Create Custom Domain** then **Create Domain** to generate a Railway domain (e.g. `my-app.railway.app`) +3. On the Rivet dashboard, paste your domain with the `/api/rivet` path into the connect form (e.g. `https://my-app.railway.app/api/rivet`) +4. Click "Done" + +Rivet envoys connect to your app over long-lived WebSockets, and your app's clients (browsers, SDKs) do the same. Railway's HTTP proxy supports WebSockets, but if you front your app with your own reverse proxy (NGINX, Caddy, etc.) inside the Railway service, raise its idle / read timeout to at least 1 hour (`3600` seconds). Default proxy timeouts (typically 30 to 60 seconds) drop these connections and cause reconnect storms. + +### Configure Sleeping & Multi-Region (Optional) + +- [Enable App Sleeping](https://docs.railway.com/reference/app-sleeping) to reduce costs when idle +- [Configure Multi-Region](https://docs.railway.com/reference/deployment-regions) to deploy closer to your users + +_Source doc path: /docs/deploy/railway_ diff --git a/.agents/skills/per-tenant-database/reference/deploy/rivet-compute.md b/.agents/skills/per-tenant-database/reference/deploy/rivet-compute.md new file mode 100644 index 0000000..69507aa --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/deploy/rivet-compute.md @@ -0,0 +1,144 @@ +# Deploying to Rivet Compute + +> Source: `src/content/docs/deploy/rivet-compute.mdx` +> Canonical URL: https://rivet.dev/docs/deploy/rivet-compute +> Description: Run your backend on Rivet Compute. + +--- +Using an AI coding agent? Open **Connect** on the [Rivet dashboard](https://dashboard.rivet.dev), select **Rivet Cloud**, and paste the one-shot prompt into your agent and have it connect with Rivet Compute for you. + +## Steps + +### Prerequisites + +- Your RivetKit app + - If you don't have one, see the [Quickstart](/docs/actors/quickstart) page or our [Examples](https://github.com/rivet-dev/rivet/tree/main/examples) +- A [Rivet Cloud](https://dashboard.rivet.dev) account and project +- Docker running locally + +### Create a Dockerfile + +Add a `Dockerfile` to your project root that builds and runs your RivetKit server: + +```dockerfile @nocheck +FROM node:24-alpine +WORKDIR /app +COPY package*.json ./ +RUN npm ci --omit=dev +COPY . . +CMD ["node", "src/server.js"] +``` + +### Get Your Cloud Token + +1. Open the [Rivet dashboard](https://dashboard.rivet.dev) and navigate to your project +2. Click **Connect** and select **Rivet Cloud** +3. Copy the **`RIVET_CLOUD_TOKEN`** value shown + +### Deploy + +Run the deploy command from your project root. The token is saved to `~/.rivet/credentials`, so later deploys can omit it. + +```bash +npx @rivetkit/cli deploy --token cloud_api_xxxxx +``` + +The CLI resolves your project from the token, builds and pushes your Docker image to Rivet's built-in registry, upserts the managed pool, and prints the deployment URL on stdout when the pool is ready. + +### Optionally Add CI + +After local deploys work, install the GitHub Actions workflow that deploys on every push and pull request: + +```bash +npx @rivetkit/cli setup-ci +``` + +This writes `.github/workflows/rivet-deploy.yml`. Add your token as a repository secret to enable it: + +```bash +gh secret set RIVET_CLOUD_TOKEN +``` + +The workflow creates production and pull-request namespaces, posts preview links, and cleans up PR namespaces when pull requests close. See the [CLI reference](/docs/cli) for all commands. + +### Monitor Deployment + +The dashboard shows live status as Rivet Compute provisions your backend: + +| Status | Description | +| --- | --- | +| Initializing | Starting the runtime environment | +| Deploying | Pulling and launching your container | +| Binding | Connecting the runner to the network | +| Ready | Deployment complete | + +Once the status reaches **Ready**, your backend is live and actors are available for connections. + +If you are an agent monitoring the deployment via API rather than the dashboard, poll the managed-pool endpoint on the Cloud API. + +The `RIVET_CLOUD_TOKEN` secret is a `cloud_api_*` management token scoped to the Cloud API at `cloud-api.rivet.dev`. Use it for `Authorization: Bearer ...` against the Cloud API. Do not confuse it with a `pk_*` publishable key, which is scoped to the Rivet Engine API at `api.rivet.dev` and will 401 against this endpoint. + +Substitute `$CLOUD_API_URL` (typically `https://cloud-api.rivet.dev`), `$PROJECT`, `$ORG`, `$CLOUD_NAMESPACE`, and `$CLOUD_TOKEN`. + +Poll every 5 seconds until `status` is `ready`. Stop and investigate if `status` is `error`. + +```bash +curl -s "$CLOUD_API_URL/projects/$PROJECT/namespaces/$CLOUD_NAMESPACE/managed-pools/default?org=$ORG" -H "Authorization: Bearer $CLOUD_TOKEN" +``` + +## Checking Logs + +Use the CLI to read your deployment's logs. By default `rivet logs` prints the last 100 lines from the `production` namespace, oldest to newest, then exits. + +```bash +npx @rivetkit/cli logs +``` + +The CLI resolves your token the same way `deploy` does (the `--token` flag, then the `RIVET_CLOUD_TOKEN` environment variable, then `~/.rivet/credentials`). + +### Follow logs live + +Pass `--follow` (`-f`) to stream new logs as they arrive instead of fetching history: + +```bash +npx @rivetkit/cli logs --follow +``` + +Each formatted line is printed as ` [] `: + +``` +2026-06-16T18:26:51.160Z [INFO] eu-central-1 server listening on port 3000 +2026-06-17T11:24:20.425Z [ERROR] us-east-1 failed to connect to upstream +``` + +A few examples: + +```bash +# Last 200 lines from a specific namespace +npx @rivetkit/cli logs --namespace production -n 200 + +# Live tail, only lines containing "error" +npx @rivetkit/cli logs --follow --contains error + +# JSON output piped to jq +npx @rivetkit/cli logs -n 50 --json | jq . +``` + +## Troubleshooting + +If the status stays in **Initializing** for more than a few minutes, verify that: + +- The `RIVET_CLOUD_TOKEN` secret is correctly set in your GitHub repository +- The GitHub Actions workflow completed without errors — check the run logs + +If the status shows **Error**, check that your container starts successfully and does not exit immediately (you can check this with container logs). Common causes: + +- The server file is not calling `registry.start()` +- A runtime crash on startup — test the image locally with `docker run` +- The server is not listening on the `RIVET_PORT` environment variable (RivetKit reads `RIVET_PORT`, defaulting to `3000`) + +## Pricing + +You are billed for the compute resources your deployment uses while it is running. + +_Source doc path: /docs/deploy/rivet-compute_ diff --git a/.agents/skills/per-tenant-database/reference/deploy/supabase.md b/.agents/skills/per-tenant-database/reference/deploy/supabase.md new file mode 100644 index 0000000..fa98436 --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/deploy/supabase.md @@ -0,0 +1,50 @@ +# Deploying to Supabase Functions + +> Source: `src/content/docs/deploy/supabase.mdx` +> Canonical URL: https://rivet.dev/docs/deploy/supabase +> Description: Deploy an existing Rivet project to Supabase Edge Functions. + +--- +This guide covers deploying an existing Rivet project to Supabase Edge Functions. + +## Prerequisites + +- [Supabase project](https://supabase.com/) +- [Supabase CLI](https://supabase.com/docs/guides/cli) configured for your project +- A Rivet namespace from the [Rivet Dashboard](https://dashboard.rivet.dev/) or a self-hosted Rivet Engine + +## Steps + +### Set up your project + +Follow the [Supabase Functions Quickstart](/docs/actors/quickstart/supabase) to set up your project locally. + +### Set Secrets + +Set your Rivet connection values as Supabase secrets. Find `RIVET_ENDPOINT` and `RIVET_PUBLIC_ENDPOINT` in the [Rivet Dashboard](https://dashboard.rivet.dev/) under **Settings → Namespace → Advanced → Backend Configuration** and copy them in. + +```sh +npx supabase secrets set \ + RIVET_ENDPOINT=https://your-namespace:sk_...@api.rivet.dev \ + RIVET_PUBLIC_ENDPOINT=https://your-namespace@api.rivet.dev +``` + +### Deploy + +Make sure the function has the `deno.json` import map from the [quickstart](/docs/actors/quickstart/supabase) that points `rivetkit` at `@rivetkit/supabase`. It keeps the deploy to the WebAssembly runtime; without it the deploy pulls Rivet's native engine and fails with a `413` (request too large). + +```sh +npx supabase functions deploy rivet +``` + +### Register the Serverless Runner URL + +After deploy, set the function URL with the `/api/rivet` path as the serverless runner URL in Rivet. For a function named `rivet`, this is usually `https://your-project.functions.supabase.co/functions/v1/rivet/api/rivet`. + +## Related + +- [Supabase Functions Quickstart](/docs/actors/quickstart/supabase) +- [Deploying to Cloudflare Workers](/docs/deploy/cloudflare) +- [SQLite](/docs/actors/sqlite) + +_Source doc path: /docs/deploy/supabase_ diff --git a/.agents/skills/per-tenant-database/reference/deploy/vercel.md b/.agents/skills/per-tenant-database/reference/deploy/vercel.md new file mode 100644 index 0000000..e8b5ba3 --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/deploy/vercel.md @@ -0,0 +1,138 @@ +# Deploying to Vercel + +> Source: `src/content/docs/deploy/vercel.mdx` +> Canonical URL: https://rivet.dev/docs/deploy/vercel +> Description: Deploy your Next.js Rivet app to Vercel. + +--- +This guide assumes a Next.js app. + +## Prerequisites + +- [Vercel account](https://vercel.com/) +- A Next.js Rivet app +- Access to the [Rivet Cloud](https://dashboard.rivet.dev/) or a [self-hosted Rivet Engine](/docs/general/self-hosting) + +## Steps + +### Set up your project + +Follow the [Next.js Quickstart](/docs/actors/quickstart/next-js) to set up your project. + +### Verify Your Project Structure + +Your Next.js project should have the following structure: + +- `src/app/api/rivet/[...all]/route.ts`: RivetKit route handler +- `src/rivet/registry.ts`: Actor definitions and registry + +The route handler sets `maxDuration` to extend the serverless function timeout so long-lived actor requests are not cut short: + +```ts src/app/api/rivet/[...all]/route.ts @nocheck +import { toNextHandler } from "@rivetkit/next-js"; +import { registry } from "@/rivet/registry"; + +export const maxDuration = 300; + +export const { GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS } = toNextHandler(registry); +``` + +### Set Environment Variables + +Set `RIVET_ENDPOINT` and `RIVET_PUBLIC_ENDPOINT` in your Vercel project settings using the URL auth format: + +``` +RIVET_ENDPOINT=https://my-namespace:sk_****@api.rivet.dev +RIVET_PUBLIC_ENDPOINT=https://my-namespace:pk_****@api.rivet.dev +``` + +`RIVET_ENDPOINT` uses the secret token for server-side access. `RIVET_PUBLIC_ENDPOINT` uses the publishable token and tells the metadata endpoint what connection info to provide to clients. + +### Deploy to Vercel + +1. Connect your GitHub repository to Vercel +2. Vercel will deploy your app + +### Configure Preview Deployments (Recommended) + +Add a GitHub action to automatically create isolated Rivet namespaces for each PR: + +1. Add these secrets to your GitHub repository: + - `RIVET_CLOUD_TOKEN`: Get from [Rivet Dashboard](https://dashboard.rivet.dev) → Settings → Advanced → Cloud API Tokens + - `VERCEL_TOKEN`: Get from [Vercel Account Settings](https://vercel.com/account/tokens) + +2. Create `.github/workflows/rivet-preview.yml`: + +```yaml +name: Rivet Preview + +on: + pull_request: + types: [opened, synchronize, reopened] + push: + branches: [main] + +concurrency: + group: rivet-preview-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + rivet-preview: + runs-on: ubuntu-latest + permissions: + pull-requests: write + steps: + - uses: rivet-dev/preview-namespace-action@v1 + with: + platform: vercel + rivet-token: ${{ secrets.RIVET_CLOUD_TOKEN }} + vercel-token: ${{ secrets.VERCEL_TOKEN }} +``` + +## Troubleshooting + +``` +Error: ENOENT: no such file or directory, mkdir '.../rivetkit/.../state' +``` + +**Cause:** The `RIVET_ENDPOINT` environment variable is not configured. RivetKit falls back to the file system driver, which fails in Vercel's read-only serverless environment. + +**Solution:** Ensure `RIVET_ENDPOINT` is set with your Rivet endpoint using the URL auth format: + +``` +RIVET_ENDPOINT=https://my-namespace:sk_****@api.rivet.dev +``` + +If using the [preview-namespace-action](https://github.com/rivet-dev/preview-namespace-action), this is configured automatically. + +The `/api/rivet/metadata` endpoint returns data but `clientEndpoint`, `clientNamespace`, and `clientToken` are missing. + +**Cause:** The `RIVET_PUBLIC_ENDPOINT` environment variable is not configured. This tells the metadata endpoint what connection info to provide to clients. + +**Solution:** Set `RIVET_PUBLIC_ENDPOINT` with the publishable token (for client access): + +``` +RIVET_PUBLIC_ENDPOINT=https://my-namespace:pk_****@api.rivet.dev +``` + +If using the [preview-namespace-action](https://github.com/rivet-dev/preview-namespace-action), this is configured automatically. + +Rivet fails to connect to your Vercel deployment with a 401 error mentioning "Authentication Required". + +**Cause:** [Vercel Deployment Protection](https://vercel.com/docs/security/deployment-protection) is blocking requests from Rivet. + +**Solution:** + +1. Create a bypass secret in your Vercel project settings +2. In Rivet, go to **Settings > Providers** +3. Click the three dots on your provider and select **Edit** +4. Click **Add Header** and add `x-vercel-protection-bypass` with your bypass secret + +If using the [preview-namespace-action](https://github.com/rivet-dev/preview-namespace-action), this is configured automatically. + +## Related + +- [Next.js Quickstart](/docs/actors/quickstart/next-js) +- [Self-Hosting](/docs/general/self-hosting) + +_Source doc path: /docs/deploy/vercel_ diff --git a/.agents/skills/per-tenant-database/reference/deploy/vm-and-bare-metal.md b/.agents/skills/per-tenant-database/reference/deploy/vm-and-bare-metal.md new file mode 100644 index 0000000..6a123f9 --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/deploy/vm-and-bare-metal.md @@ -0,0 +1,96 @@ +# Deploying to VMs & Bare Metal + +> Source: `src/content/docs/deploy/vm-and-bare-metal.mdx` +> Canonical URL: https://rivet.dev/docs/deploy/vm-and-bare-metal +> Description: Deploy your RivetKit app to any Linux VM or bare metal host. + +--- +## Steps + +### Prerequisites + +- A Linux VM or bare metal server with SSH access +- Your RivetKit app + - If you don't have one, see the [Quickstart](/docs/actors/quickstart) page or our [Examples](https://github.com/rivet-dev/rivet/tree/main/examples) +- Access to the [Rivet Cloud](https://dashboard.rivet.dev/) or a [self-hosted Rivet Engine](/docs/general/self-hosting) + +### Upload Your App + +- Build your RivetKit app locally +- Copy the build output to your server (example): + +```bash +scp -r ./dist user@server:/opt/rivetkit-app +``` + +Place the files somewhere readable by the service user, such as `/opt/rivetkit-app`. + +### Set Environment Variables + +After creating your project on the Rivet dashboard, select VM & Bare Metal as your provider. You'll be provided `RIVET_ENDPOINT` and `RIVET_PUBLIC_ENDPOINT` environment variables to use in the next step. + +### Create the systemd Service + +Create `/etc/systemd/system/rivetkit-app.service`: + +```ini +[Unit] +Description=RivetKit App +After=network.target + +[Service] +Type=simple +WorkingDirectory=/opt/rivetkit-app +ExecStart=/usr/bin/node server.js +Restart=on-failure +Environment=RIVET_ENDPOINT= +Environment=RIVET_PUBLIC_ENDPOINT= + +[Install] +WantedBy=multi-user.target +``` + +Replace the environment values with those from the Rivet dashboard and adjust paths to match your deployment. + +### Start the Service + +Reload systemd units and start the service: + +```bash +sudo systemctl daemon-reload +sudo systemctl enable --now rivetkit-app.service +``` + +### Connect to Rivet + +1. Ensure your server is accessible via a public URL (e.g. `https://my-app.example.com`) +2. On the Rivet dashboard, paste your URL with the `/api/rivet` path into the connect form (e.g. `https://my-app.example.com/api/rivet`) +3. Click "Done" + +If you front your app with a reverse proxy or load balancer (NGINX, HAProxy, Caddy, AWS ALB/NLB, etc.), raise its idle / read timeout to at least 1 hour (`3600` seconds). Rivet envoys connect to your app over long-lived WebSockets, and clients (browsers, SDKs) do the same. Default proxy timeouts (typically 30 to 60 seconds) drop these connections and cause reconnect storms. + +Examples: + +- **NGINX**: `proxy_read_timeout 3600s;` and `proxy_send_timeout 3600s;` in the relevant `location` block. +- **HAProxy**: `timeout client 1h` and `timeout server 1h` in the matching frontend / backend. +- **AWS ALB / NLB**: set the load balancer attribute `idle_timeout.timeout_seconds = 3600`. + +## Operating + +### Restart + +Restart the service after deploying new builds or environment changes: + +```bash +sudo systemctl restart rivetkit-app.service +``` + +### Logs + +Follow realtime logs when debugging: + +```bash +sudo journalctl -u rivetkit-app.service -f +``` + +_Source doc path: /docs/deploy/vm-and-bare-metal_ diff --git a/.agents/skills/per-tenant-database/reference/general/actor-configuration.md b/.agents/skills/per-tenant-database/reference/general/actor-configuration.md new file mode 100644 index 0000000..72bc6fd --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/general/actor-configuration.md @@ -0,0 +1,19 @@ +# Actor Configuration + +> Source: `src/content/docs/general/actor-configuration.mdx` +> Canonical URL: https://rivet.dev/docs/general/actor-configuration +> Description: This page documents the configuration options available when defining a RivetKit actor. The actor configuration is passed to the `actor()` function. + +--- +## Basic Example + +## Configuration Reference + +## Related + +- [Registry Configuration](/docs/general/registry-configuration): Configure the RivetKit registry +- [State](/docs/actors/state): Managing actor state +- [Actions](/docs/actors/actions): Defining actor actions +- [Lifecycle](/docs/actors/lifecycle): Actor lifecycle hooks + +_Source doc path: /docs/general/actor-configuration_ diff --git a/.agents/skills/per-tenant-database/reference/general/architecture.md b/.agents/skills/per-tenant-database/reference/general/architecture.md new file mode 100644 index 0000000..3da2912 --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/general/architecture.md @@ -0,0 +1,452 @@ +# Architecture + +> Source: `src/content/docs/general/architecture.mdx` +> Canonical URL: https://rivet.dev/docs/general/architecture +> Description: - rivetkit is the typescript library used for both local development & to connect your application to rivet - a rivetkit instance is called a "runner." you can run multiple runners to scale rivetkit horizontally. read omre about runners below. + +--- +## 3 ways of running + +### rivetkit + +- rivetkit is the typescript library used for both local development & to connect your application to rivet +- a rivetkit instance is called a "runner." you can run multiple runners to scale rivetkit horizontally. read more about runners below. + +#### local development + +- in local development, rivetkit provides a full actor environment for single-node deployments + +#### drivers + +- rivetkit supports multiple drivers. currently supports: file system (default in local dev), memory, rivet engine (used for rivet cloud & self-hosting), cloudflare durable objects (does not rely on rivet engine) +- drivers are very flexible to enable you to write your actors once and plug in to any system that fits your architecture adequately +- see the driver interface + - actordriver https://github.com/rivet-dev/rivet/blob/eeb01fc4d9ca0e06f2e740d267bd53280ca7330e/rivetkit-typescript/packages/rivetkit/src/actor/driver.ts + - managerdriver https://github.com/rivet-dev/rivet/blob/eeb01fc4d9ca0e06f2e740d267bd53280ca7330e/rivetkit-typescript/packages/rivetkit/src/manager/driver.ts + +### rivet cloud + +- provides multi-region and highest performance out of the box +- accessible at dashboard.rivet.dev and the api is avialble at api.rivet.dev + +### rivet self-hosted + +- available as a standalone rust binary or a docker contianer +- can be configured to persist to postgres or rocksdb +- can scale horiziontally across multipe nodes and can scale across multiple regions +- see [self-hosting docs](/docs/self-hosting/) + +## actors + +- Actors for long-lived processes with durable state, realtime, and hibernate when not in use. read more about actors at a high level at (link to actors/index) + +### actor-per-entity + +- actors are designed to have an actor-per-entity +- you can think about actors a bit like objects in object-oriented programming where each is responsible for their own state and expose methods (ie actions in our case) +- examples include + - actor per user + - actor per user session + - actor per document + - actor per game room + - actor per tenant + - actor per rate limit topic + +### architecting for scale + +- actors scale by: + - having isolated state to each actor that combines compute and storage for in-memory reads and writes + - communication is standardized based on actions & events + - scale horizontally +- read more about scaling at [Design Patterns](/docs/actors/design-patterns) + +### horizontal scaling + +- actors can run across multiple rivetkit runners. this is orchestrated by rivetkit itself. + +### lifecycle + +actors have create, destroy, wake, and sleep lifecycle hooks that you can implement to modify behavior. see the lifecycle docs for reference on actor lifecycel hook sequences + +### actor sleeping + +- actors sleep when not in use +- an actor is considered not in use when there are no active network connections to the actor (or the network connections are hibernatable websockets, see below) and there are no actions in flight +- actors have a sleep timeout (configured in `options.sleepTimeout`) that decides how long to keep the actor in memory with no recent activity +- sleep can be held off for the lifetime of a promise with `c.keepAwake(promise)` +- see the [sleeping docs](/docs/actors/lifecycle#sleeping) for full details + +### wake events + +- actors can wake to any of the follwoing events: + - network requests + - websocket messages + - alarms (see scheduling docs) + +### live actor migration + +- live actor migrations lets your application ugprade, crash, or hot reload cahnges without interruption to your user or application (including websockets) +- this is powered by hibernating websockets for live websocket migraiton & our fault tolerance mechanism (read more below) + +### coldstart performance + +- actors have negligible coldstart performance. the code to run the actor is already started (ie the runner), so creating/starting an actor is incredibly cheap. +- creating new actors with a key requires some overhead to communicate with other regions in order to reserve the actor's key (see below). actors can be created without keys with near-0 latency. + +### multi-region, globally unique actor keys + +- actors can optionally have a globally unique "key" +- when creating an actor with a key +- this system is highly optimized to reduce wan round trips using per-key Paxos with a custom database called Epoxy (https://github.com/rivet-dev/rivet/tree/main/engine/packages/epoxy) +- limitation: when creating an actor with a given key, that key will always be pinned to that region even if the actor is destroyed. creating a new actor with the same key will always live in the same region. +- see the actors keys document + +### input + +- actors have input data that can be passed to them when constructed +- this is similar to apssing data to a constructor in an object + +### generic parameters + +actor definitions include the following generic parameters that you'll see frequently in the code: + +- state +- conn state +- conn params +- ephemeral variables +- input data +- (experimental) database connector + +### persistence + +- state automatically flushes to storage intelligently +- to force a state flush and wait for it to finish, call (TODO: look this up in state document) +- read more about state persistence in the state document (link to document) +- state is stored in the same place as where the actor lives. loading an actor in to memory has comparable performance to network attached storage, and once in memory, has performance of any standard in-memory read/write like a variable. + +### scheduling & alarms + +- actors have a scheduling api to be able to wake up at any time in the indefinite future +- think of this like setTimeout but without a max timeout +- rivet is responsible for waking the actor when this timeout wakes + +### ephemeral variables + +- actors have the ability to create ephemrla variables for things that you do not want to be persisted with the actor's state +- this is useful for non-serializable data like a utility class like a pubsubs erver or something (TODO extra info) +- link to ephemeral variables docs + +### actions + +- for stateless clients, actions are sent as http requests via `POST /gateway/{actor id}/actions/{action name}` +- for stateful clients, actions are sent as websocket messages + +### events & subscriptions + +- events are sent as websocket messages + +### error handling + +- this is different than fault tolerance: + - error handling is a user error + - fault tolerance is something goes wrong that your applciation was not built to handle (ie hard crash, oom, network error) +- rivet provdies a special UserError class to throw custom errors that will be returned to the client +- all other errors are returned as a generic "internal error" +- this is becuase leaking error deatils is a common security hole, so we default to expose-nothing errors + +### logging + +- rivet uses pino for logging +- we expose a scoped child logger for each actor at `c.log` that automatically logs the actor id + key +- this allows you to search lgos easily by actor id without having to log the actor id frequently +- logs can be configured via the `RIVET_LOG_LEVEL` env var + +### fault tolerance + +- actors are fault tolerant, meaning that the host machine can crash and the actors will proceed to operate as if nothing happened +- runners maintain a socket with rivet engine. when this socket closes or takes to long to ping, actors will reschedule +- hibernating websockets (enabled by default) will live-migrate to the new actor as if nothing happened + +### crash policy + +- there are 3 crash policies: sleep, restart, and destroyed + - sleep (default, usually the option you want): + - when to use: actors that need high-performance in-memory logic. + - when not to use: you need this actor running at all times no matter what, even if idle + - examples: (list commone xamples) + - destroy: + - when to use: actors that need to run once until completion. on crash, do not try to reschedule. + - when not to use: if you want your actor to have fault tolerance and be able to run transaprenlty to the underlying runner + - examples: batch jobs, image conversions, ephemeral jobs, (TODO come up with better eaxmples) + - restart: + - when to use: actors that should be running at all times + - when not to use: if you don't absolutely need something running at all times, since this consumes needless compute resources. considure using the scheduling api instead. + - examples: maintain outbound sockets, daemons, always-running jobs, (TODO come up with better examples) + +the behavior for each is described below: + +| Event | Restart | Sleep | Destroy | +|------------------------------|--------------|--------------|--------------| +| Graceful exit (StopCode::Ok) | Destroy | Destroy | Destroy | +| Crash (non-Ok exit) | Reschedule | Sleep | Destroy | +| Lost (runner disappeared) | Reschedule | Sleep | Destroy | +| Lost + force_reschedule | Reschedule | Reschedule | Reschedule | +| GoingAway (runner draining) | Reschedule | Sleep | Destroy | +| No capacity (allocation) | Queue (wait) | Sleep | Queue (wait) | +| No capacity + serverless | Queue (wait) | Queue (wait) | Queue (wait) | +| Wake signal (while sleeping) | Reschedule | Reschedule | Reschedule | + +### inspector + +- actors provide an inspector api to implement the: + - repl + - state read/write + - network inspector + - event log +- this is impelmented over a websocket over bare + +### http api + +- see the http api document on actors + +### multi-region + +- actors can be scheduled across multiple regions +- each actor has an actor id which embeds which region it lives in +- networking is automatically routed to the region that an actor lives in +- limitation: actors curretnly cannot migrate across regions + +### backpressure + +#### no runner capacity + +- this is how actors with different crash policies behave when when there's backpressure: + - sleep = sleeps (sheds load by not rescheduling) + - restart = queues + - destroy = queues +- see the above matrix for more details on actor crash policy on how it handles no capacity. + +- the actor queue is built to withstand high amounts of backpressure on rivet, so queueing actors is fine here +- a large queue means it'll take more time for your application to process the queue to catch up with demand when it comes online. + +#### per-actor cpu & networking exhaustion + +- actors are isolated, so they each have their own individual bottleneck. you can think of this like a process thread where each thread can only do so much. +- there is no durable message queue/"mailbox" for actors. if the actor cannot respond in time, then the request is dropped. +- if an actor exhauses its cpu or networking, then the runner +- returns service unavailble (503) if the actor fails to respond to a request in time +- there is no hard cap on the networking or cpu usage for each actor at the moment +- if your actor is resource intensive, it's common to use a separate mailbox actor to act as a queue + +## runners + +### regular vs serverless runners + +there are 2 types of runners: + +- regular: these are standard nodejs processes connected to rivet that rivet can orchestrate actors to and send network requests to at any time +- serverless: rivet works with serverless platforms. when an actor is created, it has a request-per-actor model where it opens a long-running request on the serverless platofrm to run a given actor. + +### runner pool + +- runners are pooled together by sharing a common name (ie "default") +- when an actor is created, it chooses the pool by selecting the runner name to run on +- rivet will automatically load balance actors across these runners + +### runner key + +- not relevnat for serverless runners +- each runner has a unique key that it provides when connecting. keys are unique to the instace the runner is running on and should be the same if the runner is restarted. +- this can be the: machine's ip, k8s pod name, etc +- if there is an existing runner connected with a given key, the runner will disconnect the old runner and replace it +- rivet is designed to handle network partitions by waiting for runners to miss a ping, indicating it's no longer alive. however, often times runners restart immediately after a hard crash and reconnect. in this case, the runner will reconnect on restart and terminate the old runner in order to prevent further actors from scheduling to the crashed runner. + +### capacity + +- not relevnat for serverless runners +- each runner can be assigned a capacity of how many actors it can run +- rivet will schedule with spread (not binpacking) in order to spread load over actors + +#### usefulness of capacity = 1 + +- setting a capacity of 1 is helpful for situations where you have cpu-intensive apps that should not run with any other actors +- examples include game servers, ffmpeg jobs, etc + +### versions & upgrading code + +- each runner has a version index +- actors are always scheduled to the highest verison index (see runner priority below) +- this means that when a new runner is deployed: + 1. runners with higher index come online + 2. actors schedule to the highest index, stop scheduling to the older index + 3. old index runners start draining and migrating actors to new index + 4. all old runners are now shut down +- websocekts are live migrated to the new version when upgrading using hibernating websockets to users see no hiccup in their applications +- this is important because actors should never downgrade their runner. they should always move to a newer version of code in order to prevent corruption. + +### runner scheduling prioroty + +- actors are scheduled to runners sorted by priority of (version DESC, remaining capacity ASC) + +### multi-region + +TODO + +### shutdown sequence + +- runner shutdown is important to ensure that actors do not get unexpectedly terminated when either: + - upgrading your applciation and taking down old pods + - scaling down your runners horizontally (ie from an hpa) + - pressing ctrl-c when in development +- on shutdown: + 1. tell rivet the runner is stopping + 2. rivet tells all the actors on this runner to migrate + 3. runner waits for all actors to finish migrating + 4. runner exits process + +### reconnection + +- runners can handle temporary network partitions +- they'll automatically reconnect and replay missed commands/events between rivet and the runner +- this happens transparenlty to the user +- if disconnected for too long (indicating a network partition), the runner will shut itself down and exit + +### autoscaling + +- not relevant to serverless +- runners currently autoscale on cpu. more intelligent scaling is coming soon. +- tune your runner total slots capacity accordingly +- it's up to you to configure your hpa/etc to work like this. see the Connect guides (link to index page) for reference on hwo to configure this. + +### serverless timeouts + +- serverless runners take in to account the maximum run duration of the serverless platform +- the runners will mgirate actors to a new request before the request times out +- this is completely transparent to you and the user because of the fault tolerance and websocket migraiton characteristics +- it's common for actors to go sleep before hitting the serverless timeout + +## networking + +### web standards + +- everything in rivet is built on webstandards by default +- nothing in rivet requires you to use our sdk, our sdks are meant to be a convenience. it's built to be as easy to use raw http/websocket endpoints from scratch. +- actions, events, etc are all built on simple, well-documented http/websocket under the hood (link to openapi & asyncapi docs). +- you can use low-level request handlers (lnk to dock) and low-level weboscket handlers (link to doc) to handle low-level primtivies yourself + +### encoding + +- rivetkit's action/events api supports communicating via [VBARE](link to github repo, see the blog post for the link), CBOR, or JSON + - VBARE: high-perofrmance & compact, optimal use case + - CBOR: descent encoding/decoding perf + portable libraries, good for implemnting high-ish performance on other platforms + - JSON: good for fast implementations & debugging (easy to read) + +### tunneling + +- when a runner connects it opens a tunnel to rivet to allow incoming traffic + - this is simila to systems like tailscale, ngrok, or other vpns + - we do this for security & configuraiton simplicity since it means that you don't have to manage exposing your rivetkit applications' networkig to rivet. instead, anything that can open a socket to rivet can accept inbound traffic to actors. + +### gateway + +- incoming traffic to actors come to the Rivet gateway and are routed to the appropriate runner +- the rivet gateway automatically handles: + - multi-region routing to route traffic to the correct reigon for an actor + - automatically waking the actor if needed + - sending traffic over the runner + +### hibernating websockets + +- hibernating web sockets are a core component of live actor migration & fault tolerance. it allows us to maintain an open websocket while the actor crashes, upgrades, or moves to another runner. +TODO: copy the rest of this from low-level webosckets document and rephrase + +### actor health endpoin + +- actors provide a simple, utility health endpoint at `/health` that lets you check if your actor is reachable (e.g. `curl https://api.rivet.dev/gateway/{actor id}/health`) + +## multi-region + +### networking + +- actors may live in different regions than inbound requests +- Rivet uses the Epoxy (link again) system to handle global routing to route traffic to the correct region with high performance +- this is completely transparent to you. your app sends traffic to https://api.rivet.dev/gateway/* and it automatically routes to the correct actor in the appropriate region + +### globally unique actor keys + +- actor keys are globally unique to be able to benefit from multi-region capabilities without any extra work +- see more about globally uniuqe actor keys above +- see the actor keys document + +### regional endpoints + +- each reigon has a regional endpoint +- this endpoint is used specifically for connecting runners (for example https://api-us-east-1.rivet.dev), opt to use api.rivet.dev for all other traffic +- runners are required to connect to the regional endpoints + - this is because runners are sensitive to latency to the rivet regional datacenter + - we add datacenters regularly so each runner needs to be pinned to a single datacenter in order to ensure your availble datacneter list doesn't change sporadically without your consent + +### persistence + +- data is always persisted in the same region that is written +- this is important for minimal coldstarts & data locality laws + +## namespaces + +- rivet provides namespaces to run multiple actor systems in isolation +- this makes it really easy to have prod/staging environments or completely different applications running on the same rivet instance +- when you connect to rivet, you can specify which namespace you're connecting to +- self-hotsed rivet defaults to namespace "default" +- rivet cloud provdies isolated tokens for each namespace + +## manager api + +- rivet provides a standard rest api for managing actors + - useful endoints include: + - get /actors + - delete /actors/{} + - get /actors/names -> get all actor types available + +## comparison to prior art for actors + +### runtime + +- there are very few serious actor implementation targeted at the javascirpt eocsystem. rivet is arguably the most serious open-source actor implementation for typescript out there. + +### library vs orchestrator + +- some actor systems opt to be purely a library while rivet opts to have an orchestrator (i.e. the single rust binary) +- this lets us to a lot of things other actor systems can't: + - separating orchestration, persistence, and proxy lets us isoalte the core to be incredibly reliable while the fast-changing applications that ocnnect to rivet can be more error-prone safely. with a library, the blast radius of your application also affects the entire actor system. + - support for serverless platforms to benefit from cost, multi-region, blitz scaling, and relibaiblity benefits + - optimize fault tolerance since we can make more assumtions about application state when the rivet core does not crash and your app does + +### scheduling + +actors is a loose term, but there are generally 2 types of schedulign in practice: + +- ephemeral actors + - examples: erlang/otp, akka, swift + - provides no persistence or sleeping mechanism by defualt + - relies on supervisors for managing persistence +- [virtual actors](https://www.microsoft.com/en-us/research/project/orleans-virtual-actors/) + - an extension of the actor pattern that provides actors that can hibernate ("sleep") when not in use + - examples: orleans, dapr, durable objects + +rivet has similarities with both to provide more flexibility: + +- crash policies provdie for 3 types of actors: + - sleep -> most similar to virutal actors + - restart -> most similar to ephemeral actors but with a supervisor to auto-restart, however still has a durable queue ot handle backpressure + - crash -> most similar to traditional actors but with no supervisor to restart, however still has a durable queue to handle backpressure + +### communication + +- many actor frameworks use inbox patterns (think: queue-per-actor) to handle sending messages between actors +- there is no callback mechanims, instead you need to send messages back to the actual actor +- rivet opts to behave like web standards instead of using the message pattern + - actors can impelment the inbox pattern optionally + - but we provide lower-level networking to be able to be compatible with more techniologies + - rivet assumes the same serial concurerntly that other actors do (by the nature of javascript being single-threaded) but we allow you to run promises in parallel or handl eyour own concurrency control (which some other actor frameworks might require a spawning new actor to do) + +_Source doc path: /docs/general/architecture_ diff --git a/.agents/skills/per-tenant-database/reference/general/cors.md b/.agents/skills/per-tenant-database/reference/general/cors.md new file mode 100644 index 0000000..330fe38 --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/general/cors.md @@ -0,0 +1,18 @@ +# Cross-Origin Resource Sharing + +> Source: `src/content/docs/general/cors.mdx` +> Canonical URL: https://rivet.dev/docs/general/cors +> Description: Cross-Origin Resource Sharing (CORS) controls which origins (domains) can access your actors. When actors are exposed to the public internet, proper origin validation is critical to prevent security breaches and denial of service attacks. + +--- +Unlike stateless HTTP APIs that use CORS headers, Rivet Actors are stateful and support persistent WebSocket connections. Since WebSockets don't natively support CORS, we validate origins manually in the `onBeforeConnect` hook before connections may open. + +## Implementing Origin Restrictions + +To implement origin restrictions on Rivet Actors, use the `onBeforeConnect` hook to verify the request. + +To catch the error on the client, use the following code: + + See tracking issue for [configuring CORS per-actor on the gateway](https://github.com/rivet-dev/rivet/issues/3539) that will remove the need to implement origin restrictions in `onBforeRequest`. + +_Source doc path: /docs/general/cors_ diff --git a/.agents/skills/per-tenant-database/reference/general/docs-for-llms.md b/.agents/skills/per-tenant-database/reference/general/docs-for-llms.md new file mode 100644 index 0000000..db0c216 --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/general/docs-for-llms.md @@ -0,0 +1,51 @@ +# Documentation for LLMs & AI + +> Source: `src/content/docs/general/docs-for-llms.mdx` +> Canonical URL: https://rivet.dev/docs/general/docs-for-llms +> Description: Rivet provides optimized documentation formats specifically designed for Large Language Models (LLMs) and AI integration tools. + +--- +## Skills (Recommended) + +For AI coding assistants like Claude Code, Cursor, or Windsurf, install Rivet skills for the best development experience: + +```sh +npx skills add rivet-dev/skills +``` + +Skills provide your AI assistant with Rivet-specific knowledge, best practices, and code patterns directly in your project context. + +## Available Formats + +### `llms.txt` (Condensed) +A condensed version of the documentation perfect for quick reference and context-aware AI assistance. + +**Access:** /llms.txt + +This format includes: +- Key concepts and features +- Essential getting started information +- Summaries of main functionality +- Optimized for token efficiency + +### `llms-full.txt` (Complete) +The complete documentation in a single file, ideal for comprehensive AI assistance and in-depth analysis. + +**Access:** /llms-full.txt + +This format includes: +- Complete documentation content +- All examples and detailed explanations +- Full API references and guides +- Suitable for complex queries and comprehensive understanding + +## Access Pages As Markdown + +Each documentation page is also available as clean markdown by appending `.md` to any documentation URL path. + +For example: + +- Original URL: `https://rivet.dev/docs/actors` +- Markdown URL: `https://rivet.dev/docs/actors.md` + +_Source doc path: /docs/general/docs-for-llms_ diff --git a/.agents/skills/per-tenant-database/reference/general/edge.md b/.agents/skills/per-tenant-database/reference/general/edge.md new file mode 100644 index 0000000..814dde2 --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/general/edge.md @@ -0,0 +1,24 @@ +# Edge Networking + +> Source: `src/content/docs/general/edge.mdx` +> Canonical URL: https://rivet.dev/docs/general/edge +> Description: Actors automatically run near your users on your provider's global network. + +--- +At the moment, edge networking is only supported on Rivet Cloud & Cloudflare Workers. More self-hosted platforms are on the roadmap. + +## Region selection + +### Automatic region selection + +By default, actors will choose the nearest region based on the client's location. + +Under the hood, Rivet and Cloudflare use [Anycast routing](https://en.wikipedia.org/wiki/Anycast) to automatically find the best location for the client to connect to without relying on a slow manual pinging process. + +### Manual region selection + +The region an actor is created in can be overridden using region options: + +See [Create Manage Actors](/docs/actors/communicating-between-actors) for more information. + +_Source doc path: /docs/general/edge_ diff --git a/.agents/skills/per-tenant-database/reference/general/endpoints.md b/.agents/skills/per-tenant-database/reference/general/endpoints.md new file mode 100644 index 0000000..5c376e1 --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/general/endpoints.md @@ -0,0 +1,98 @@ +# Endpoints + +> Source: `src/content/docs/general/endpoints.mdx` +> Canonical URL: https://rivet.dev/docs/general/endpoints +> Description: Configure how your backend connects to Rivet and how clients reach your actors. + +--- +## Local Development + +No configuration is needed for local development. RivetKit runs entirely on your local machine without any extra configuration to run Rivet Actors. + +## Production Deployment + +When deploying to production, you need to configure endpoints so your backend can communicate with Rivet Engine and clients can reach your actors. + +Diagram showing Client connecting to Rivet, which connects to Your Backend + +### Private Endpoint + +The private endpoint tells your backend where to find the Rivet Engine. + +### Environment Variable + +```bash +RIVET_ENDPOINT=https://my-namespace:sk_xxxxx@api.rivet.dev +``` + +### Config + +### Public Endpoint + +The public endpoint tells clients where to connect to reach your actors. + +This endpoint and token will be exposed to the internet. Use a public token (`pk_`), not your secret token (`sk_`). + +The public endpoint is only required if using the [serverless runtime mode](/docs/general/runtime-modes#runners) and if you have a frontend using RivetKit. + +### Environment Variable + +```bash +RIVET_PUBLIC_ENDPOINT=https://my-namespace:pk_xxxxx@api.rivet.dev +``` + +### Config + +## Advanced + +### URL Auth Syntax + +Endpoint URLs support embedding namespace and token directly in the URL: + +``` +https://namespace:token@host/path +``` + +This is the recommended approach for simplicity. Alternatively, you can use separate environment variables: + +```bash +RIVET_ENDPOINT=https://api.rivet.dev +RIVET_NAMESPACE=my-namespace +RIVET_TOKEN=sk_xxxxx +``` + +### Security + +In serverless mode, the private endpoint is used to validate that requests to `GET /api/rivet/start` are coming from your trusted Rivet endpoint. If the private endpoint is not configured, anyone can run a self-hosted instance of Rivet and connect to your backend from any endpoint. + +### How Clients Connect + +This flow applies to [serverless runtime mode](/docs/general/runtime-modes#serverless). For [runner runtime mode](/docs/general/runtime-modes#runners) or [clients configured to connect directly to Rivet](/docs/clients/javascript), clients connect directly to Rivet and this metadata flow is not needed. + +When a client connects to your serverless application, it follows this flow: + +1. Client makes a request to `https://my-app.example.com/api/rivet/metadata` +2. Your app returns the public endpoint configuration: + ```json + { + "clientEndpoint": "https://api.rivet.dev", + "clientNamespace": "my-namespace", + "clientToken": "pk_xxxxx" + } + ``` +3. Client caches these values and uses them for subsequent requests +4. Client connects to `https://api.rivet.dev/gateway/{actor}`, which routes requests to your actors + +This indirection exists because Rivet acts as a gateway between clients and your actors. This is because Rivet handles routing, load balancing, and actor lifecycle management of actors. + +## Reference + +| Environment Variable | Config Option | Description | +|---------------------|---------------|-------------| +| `RIVET_ENDPOINT` | `endpoint` | Rivet Engine URL for your backend | +| `RIVET_NAMESPACE` | `namespace` | Namespace for actor isolation | +| `RIVET_TOKEN` | `token` | Authentication token for engine connection | +| `RIVET_PUBLIC_ENDPOINT` | `serverless.publicEndpoint` | Client-facing endpoint | +| `RIVET_PUBLIC_TOKEN` | `serverless.publicToken` | Client-facing token | + +_Source doc path: /docs/general/endpoints_ diff --git a/.agents/skills/per-tenant-database/reference/general/environment-variables.md b/.agents/skills/per-tenant-database/reference/general/environment-variables.md new file mode 100644 index 0000000..2a86e98 --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/general/environment-variables.md @@ -0,0 +1,87 @@ +# Environment Variables + +> Source: `src/content/docs/general/environment-variables.mdx` +> Canonical URL: https://rivet.dev/docs/general/environment-variables +> Description: This page documents all environment variables that configure RivetKit behavior. + +--- +## Connection + +| Environment Variable | Description | +|---------------------|-------------| +| `RIVET_ENDPOINT` | Endpoint URL to connect to Rivet Engine. Supports [URL auth syntax](/docs/general/endpoints#url-auth-syntax). | +| `RIVET_TOKEN` | Authentication token for Rivet Engine | +| `RIVET_NAMESPACE` | Namespace to use (default: "default") | + +## Public Endpoint + +These variables configure how clients connect to your actors. + +| Environment Variable | Description | +|---------------------|-------------| +| `RIVET_PUBLIC_ENDPOINT` | Public endpoint for client connections. Supports [URL auth syntax](/docs/general/endpoints#url-auth-syntax). | +| `RIVET_PUBLIC_TOKEN` | Public token for client authentication | + +## Runner Configuration + +| Environment Variable | Description | +|---------------------|-------------| +| `RIVET_RUNNER` | Runner name (default: "default") | +| `RIVET_RUNNER_VERSION` | Version number for the runner. See [Versions & Upgrades](/docs/actors/versions). | +| `RIVET_RUNNER_KIND` | Type of runner | +| `RIVET_TOTAL_SLOTS` | Total actor slots available (default: 100000) | + +## Engine + +| Environment Variable | Description | +|---------------------|-------------| +| `RIVET_RUN_ENGINE` | Set to `1` to spawn the engine process | +| `RIVET_RUN_ENGINE_HOST` | Host to bind the spawned local engine process to. Defaults to `127.0.0.1`. | +| `RIVET_RUN_ENGINE_PORT` | Port to bind the spawned local engine process to. Defaults to `6420`. | +| `RIVET_RUN_ENGINE_VERSION` | Version of engine to download | + +## Inspector + +| Environment Variable | Description | +|---------------------|-------------| +| `RIVET_INSPECTOR_DISABLE` | Set to `1` to disable the inspector | + +## Metrics + +| Environment Variable | Description | +|---------------------|-------------| +| `_RIVET_METRICS_TOKEN` | Bearer token that gates the per-actor Prometheus `/metrics` endpoint. When unset, `/metrics` is disabled. | + +## Experimental + +| Environment Variable | Description | +|---------------------|-------------| +| `RIVET_EXPERIMENTAL_OTEL` | Set to `1` to enable experimental OTel tracing in Rivet Actors | + +## Storage + +| Environment Variable | Description | +|---------------------|-------------| +| `RIVETKIT_RUNTIME` | Runtime binding to use for RivetKit core: `auto`, `native`, or `wasm`. Defaults to `auto`. | +| `RIVETKIT_STORAGE_PATH` | Overrides the default file-system storage path used by RivetKit when using the default driver. | + +## Lifecycle + +| Environment Variable | Description | +|---------------------|-------------| +| `RIVETKIT_RUNTIME_MODE` | Controls how `registry.start()` runs. Accepted values are `envoy` and `serverless`; any other explicit value errors. Defaults to `envoy`: opens a long-lived WebSocket to the engine (Mode A). Set to `serverless` to bind an HTTP listener via `registry.listen()` (Mode B). | +| `RIVETKIT_PUBLIC_DIR` | Directory of static assets to serve alongside the framework routes when calling `registry.listen()`. Used as a fallback when `opts.publicDir` is not passed. On auto-listen via `registry.start()`, defaults to `/public` when this env var is unset. | +| `RIVET_PORT` | Port the listener binds when calling `registry.listen()` without an explicit `opts.port`. Must be an integer between 1 and 65535. Defaults to `3000`. | + +## Logging + +| Environment Variable | Description | +|---------------------|-------------| +| `RIVET_LOG_LEVEL` | Log level: `trace`, `debug`, `info`, `warn`, `error`, `fatal`, `silent` | +| `RIVET_LOG_TARGET` | Set to `1` to include log target | +| `RIVET_LOG_TIMESTAMP` | Set to `1` to include timestamps | +| `RIVET_LOG_MESSAGE` | Set to `1` to include message formatting | +| `RIVET_LOG_ERROR_STACK` | Set to `1` to include error stack traces | +| `RIVET_LOG_HEADERS` | Set to `1` to log request headers | + +_Source doc path: /docs/general/environment-variables_ diff --git a/.agents/skills/per-tenant-database/reference/general/http-server.md b/.agents/skills/per-tenant-database/reference/general/http-server.md new file mode 100644 index 0000000..c3a386c --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/general/http-server.md @@ -0,0 +1,96 @@ +# HTTP Server + +> Source: `src/content/docs/general/http-server.mdx` +> Canonical URL: https://rivet.dev/docs/general/http-server +> Description: Different ways to run your RivetKit HTTP server. + +--- +## Methods of Running Your Server + +### registry.start() + +The simplest way to run your server. Starts a local RivetKit server, serves static files from a `public` directory, and starts the actor runner: + +Run with `npx tsx --watch index.ts` (Node.js), `bun --watch index.ts` (Bun), or `deno run --allow-net --allow-read --allow-env --watch index.ts` (Deno). Clients connect to the Rivet Engine on `http://localhost:6420`. + +### With Fetch Handlers + +A [fetch handler](https://wintercg.org/) is a function that takes a `Request` and returns a `Response`. This is the standard pattern used by Cloudflare Workers, Deno Deploy, Bun, and other modern runtimes. + +Use `registry.serve()` to get a fetch handler: + +To integrate with a router like [Hono](https://hono.dev/) or [Elysia](https://elysiajs.com/), use `registry.handler()`: + +### Hono + +### Elysia + +Then run your server: + +```bash Node.js +npx tsx --watch server.ts +``` + +```bash Bun +bun --watch server.ts +``` + +```bash Deno +deno run --allow-net --allow-read --allow-env --watch server.ts +``` + +### Explicit HTTP Server + +If you need to explicitly start the HTTP server instead of using the fetch handler pattern: + +### Node.js (Hono) + +Using Hono with `@hono/node-server`: + +### Node.js (Adapter) + +Using `@whatwg-node/server` to adapt the fetch handler to Node's HTTP server: + +```ts server.ts @nocheck +import { actor, setup } from "rivetkit"; +import { createServer } from "node:http"; +import { createServerAdapter } from "@whatwg-node/server"; + +const myActor = actor({ state: {}, actions: {} }); +const registry = setup({ use: { myActor } }); + +const handler = createServerAdapter(registry.serve().fetch); +const server = createServer(handler); +server.listen(3000); +``` + +### Bun + +Using Bun's native server: + +```ts server.ts @nocheck +import { actor, setup } from "rivetkit"; + +const myActor = actor({ state: {}, actions: {} }); +const registry = setup({ use: { myActor } }); + +Bun.serve({ + port: 3000, + fetch: (request: Request) => registry.handler(request), +}); +``` + +### Deno + +Using Deno's native server: + +```ts server.ts @nocheck +import { actor, setup } from "rivetkit"; + +const myActor = actor({ state: {}, actions: {} }); +const registry = setup({ use: { myActor } }); + +Deno.serve({ port: 3000 }, (request: Request) => registry.handler(request)); +``` + +_Source doc path: /docs/general/http-server_ diff --git a/.agents/skills/per-tenant-database/reference/general/logging.md b/.agents/skills/per-tenant-database/reference/general/logging.md new file mode 100644 index 0000000..f7ce9eb --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/general/logging.md @@ -0,0 +1,74 @@ +# Logging + +> Source: `src/content/docs/general/logging.mdx` +> Canonical URL: https://rivet.dev/docs/general/logging +> Description: Actors provide a built-in way to log complex data to the console. + +--- +Using the context's log object (`c.log`) allows you to log complex data using structured logging. + +Using the actor logging API is completely optional. + +## Log levels + +There are 7 log levels: + +| Level | Call | Description | +| ------ | ------------------------------- | ---------------------------------------------------------------- | +| Fatal | `c.log.fatal(message, ...args);` | Critical errors that prevent core functionality | +| Error | `c.log.error(message, ...args);` | Errors that affect functionality but allow continued operation | +| Warn | `c.log.warn(message, ...args);` | Potentially harmful situations that should be addressed | +| Info | `c.log.info(message, ...args);` | General information about significant events & state changes | +| Debug | `c.log.debug(message, ...args);` | Detailed debugging information, usually used in development | +| Trace | `c.log.trace(message, ...args);` | Very detailed debugging information, usually for tracing flow | +| Silent | N/A | Disables all logging output | + +## Structured logging + +The built-in logging API (using `c.log`) provides structured logging to let you log key-value +pairs instead of raw strings. Structured logs are readable by both machines & +humans to make them easier to parse & search. + +When using `c.log`, the actor's name, key, and actor ID are automatically included in every log output. This makes it easy to filter and trace logs by specific actors in production environments. + +### Examples + +The logging system is built on [Pino](https://getpino.io/#/docs/api?id=logger), a high-performance structured logger for Node.js. + +## Configuration + +### Environment Variables + +You can configure logging behavior using environment variables: + +| Variable | Description | Values | Default | +| -------- | ----------- | ------ | ------- | +| `RIVET_LOG_LEVEL` | Sets the minimum log level to display | `trace`, `debug`, `info`, `warn`, `error`, `fatal`, `silent` | `warn` | +| `RIVET_LOG_TARGET` | Include the module name that logged the message | `1` to enable, `0` to disable | `0` | +| `RIVET_LOG_TIMESTAMP` | Include timestamp in log output | `1` to enable, `0` to disable | `0` | +| `RIVET_LOG_MESSAGE` | Enable detailed message logging for debugging | `1` to enable, `0` to disable | `0` | +| `RIVET_LOG_ERROR_STACK` | Include stack traces in error output | `1` to enable, `0` to disable | `0` | +| `RIVET_LOG_HEADERS` | Log HTTP headers in requests | `1` to enable, `0` to disable | `0` | + +Example: +```bash +RIVET_LOG_LEVEL=debug RIVET_LOG_TARGET=1 RIVET_LOG_TIMESTAMP=1 node server.js +``` + +### Log Level + +You can configure the log level programmatically when setting up your registry: + +### Custom Pino Logger + +You can also provide a custom Pino base logger for more advanced logging configurations: + +If using a custom base logger, you must manually configure your own log level in the Pino logger. + +For more advanced Pino configuration options, see the [Pino API documentation](https://getpino.io/#/docs/api?id=export). + +### Disable Welcome Message + +You can disable the default RivetKit welcome message with: + +_Source doc path: /docs/general/logging_ diff --git a/.agents/skills/per-tenant-database/reference/general/pool-configuration.md b/.agents/skills/per-tenant-database/reference/general/pool-configuration.md new file mode 100644 index 0000000..21b00f1 --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/general/pool-configuration.md @@ -0,0 +1,119 @@ +# Pool Configuration + +> Source: `src/content/docs/general/pool-configuration.mdx` +> Canonical URL: https://rivet.dev/docs/general/pool-configuration +> Description: Reference for runner pool configuration, including drain behavior, actor eviction rate limiting, and serverless-specific options. + +--- +A **runner pool** is the set of runners Rivet manages for a given runner name within a namespace. The pool configuration controls how runners are scaled, drained on version upgrades, and how quickly actors are evicted from drained runners. + +There are two pool kinds: + +- **`normal`** — runners connect to the engine themselves (for example a long-running process started by you, Docker, or Kubernetes). The engine does not start or stop runners. +- **`serverless`** — the engine calls an HTTP endpoint to wake runners on demand. Used by serverless platforms (Vercel, Cloudflare Workers, Freestyle, etc.). See [Runtime Modes](/docs/general/runtime-modes). + +## Setting the Configuration + +Configure a pool from the [Rivet dashboard](https://dashboard.rivet.dev) under your namespace's runner settings. The dashboard is the recommended way to manage pool configuration. + +You can also set pool configuration directly through the API or the TypeScript SDK: + +```typescript SDK @nocheck +import { RivetClient } from "@rivetkit/engine-api-full"; + +const rivet = new RivetClient({ + environment: "https://api.rivet.dev", + token: process.env.RIVET_TOKEN!, +}); + +await rivet.runnerConfigsUpsert("default", { + namespace: "default", + datacenters: { + default: { + serverless: { + url: "https://my-app.example.com/api/rivet", + requestLifespan: 60 * 15, + actorEvictionDelay: 5, + actorEvictionPeriod: 60, + actorEvictionRate: 1, + }, + }, + }, +}); +``` + +```bash curl +curl -X PUT "https://api.rivet.dev/runner-configs/default?namespace=default" \ + -H "Authorization: Bearer $RIVET_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "datacenters": { + "default": { + "serverless": { + "url": "https://my-app.example.com/api/rivet", + "request_lifespan": 900, + "actor_eviction_delay": 5, + "actor_eviction_period": 60, + "actor_eviction_rate": 1 + } + } + } + }' +``` + +The HTTP API uses `snake_case`. The TypeScript SDK uses `camelCase`. The field names below use `camelCase`. + +## Common Options + +These options apply to both `normal` and `serverless` pools. + +| Option | Type | Default | Description | +|---|---|---|---| +| `drainOnVersionUpgrade` | `bool` | `true` | When a new runner version is deployed, stop old runners and migrate their actors. See [Versions & Upgrades](/docs/actors/versions#drain-on-version-upgrade). | +| `actorEvictionDelay` | `u32` (seconds) | `0` | Delay before actor eviction begins after a drain is triggered. Gives clients time to receive the drain signal before migrations start. | +| `actorEvictionPeriod` | `u32` (seconds) | `0` | Window over which evictions are batched. Used together with `actorEvictionRate` to smooth eviction load on the rest of the pool. | +| `actorEvictionRate` | `f32` (actors/sec) | `1.0` | Maximum number of actors evicted per second once eviction is underway. Set higher for fast cutovers, lower to spread reschedule load. | +| `metadata` | `object` | — | Arbitrary JSON metadata attached to the pool. Useful for tagging and dashboards. | + +### Tuning eviction rate limiting + +Eviction kicks in whenever a runner is drained — most commonly during a version upgrade with `drainOnVersionUpgrade: true`, but also when a runner disconnects ungracefully or is replaced. + +A typical tuning starts from: + +- `actorEvictionDelay: 5` — five-second head start so clients see the drain notification before migrations begin. +- `actorEvictionPeriod: 60` — batch over a one-minute window. +- `actorEvictionRate: 1` — evict one actor per second. + +Increase `actorEvictionRate` for small pools where a full cutover finishes in seconds. Decrease it for large pools to avoid bursts of reschedule traffic. + +## Serverless-Only Options + +These options only apply when `kind: "serverless"`. + +| Option | Type | Default | Description | +|---|---|---|---| +| `url` | `string` | — | HTTP endpoint the engine calls to wake a runner. | +| `headers` | `map` | `{}` | Additional headers sent with the wake request (for example an auth token). | +| `requestLifespan` | `u32` (seconds) | — | Total lifespan of a serverless request before drain begins. Must be shorter than your platform's request timeout. | +| `maxConcurrentActors` | `u64` | `1000` | Soft cap on concurrent actors hosted across the pool. | +| `drainGracePeriod` | `u32` (seconds) | `1800` (30 min) | Time a serverless runner reserves at the end of its lifespan for actors to stop gracefully. | +| `metadataPollInterval` | `u64` (ms) | engine default | How often each runner re-fetches pool metadata to detect new versions. | + +### Deprecated options + +The following options still parse for backwards compatibility but should not be used in new configurations: + +- `slotsPerRunner` +- `minRunners` +- `maxRunners` +- `runnersMargin` + +## Related + +- [Runtime Modes](/docs/general/runtime-modes) — runner vs serverless behavior. +- [Versions & Upgrades](/docs/actors/versions) — how `drainOnVersionUpgrade` interacts with version detection. +- [Limits](/docs/actors/limits) — request lifespan and drain grace period limits. +- [Debugging](/docs/actors/debugging) — inspect a pool configuration via the API. + +_Source doc path: /docs/general/pool-configuration_ diff --git a/.agents/skills/per-tenant-database/reference/general/production-checklist.md b/.agents/skills/per-tenant-database/reference/general/production-checklist.md new file mode 100644 index 0000000..1ebe257 --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/general/production-checklist.md @@ -0,0 +1,98 @@ +# Production Checklist + +> Source: `src/content/docs/general/production-checklist.mdx` +> Canonical URL: https://rivet.dev/docs/general/production-checklist +> Description: Checklist for deploying Rivet Actors to production. + +--- +We recommend passing this page to your coding agent to verify your configuration before deploying. + +## Environment + +- **Set `NODE_ENV=production`.** Ensures optimized performance and disables development-only behavior. +- **Ensure log level is not set to debug.** Leave `RIVET_LOG_LEVEL` at its default or explicitly set it to `warn` to avoid excessive logging. See [Logging](/docs/general/logging). +- **Do not set `RIVET_EXPOSE_ERRORS=1` in production.** This exposes internal error details to clients. It is automatically enabled when `NODE_ENV=development`. See [Errors](/docs/actors/errors). + +## Runtime Mode + +- **Configure a runner version.** Required for graceful upgrades and draining of old actors. Applies to both serverless and runner modes. See [Versions & Upgrades](/docs/actors/versions). + +### Serverless + +- **Check platform timeouts.** Rivet handles migration between invocations automatically, but shorter timeouts increase migration frequency. See [Timeouts](/docs/general/runtime-modes#timeouts). +- **Verify `/api/rivet/start` body size limits.** Serverless actor starts carry actor config and preloaded KV or SQLite startup data in the request body. Keep `serverless.maxStartPayloadBytes` and your platform or proxy body limit at **16 MiB or higher**, or lower the preload budget if your platform cannot accept that size. See [Limits](/docs/actors/limits#kv-preloading). +- **Configure max runners.** Go to Settings > Providers > Edit Provider > Max Runners to set the limit. The default is 100,000 runners. This is effectively your max actor count. +- **Verify your platform rate limit accommodates your actor create and wake frequency.** Actor start requests are sent from Rivet's servers, so they all originate from the same IP. Per-IP rate limits will throttle the engine well before they would throttle real end-user traffic. Size your platform's rate limit to your peak actor create and wake rate, not your end-user request rate. +- **Set the per-instance max concurrent actor limit.** Each serverless instance hosts one actor per in-flight `/api/rivet/start` request, so your platform's per-instance concurrency (e.g. GCP Cloud Run `--concurrency`, AWS Lambda reserved concurrency, Vercel `maxDuration` + concurrency) directly caps actors per instance. Pick a value based on per-actor memory and CPU; the platform autoscales out additional instances once existing ones hit the cap. +- **Tune `requestLifespan` to your platform's hard request timeout.** `requestLifespan` (default `3600`, 60 minutes) is the total lifespan of each serverless request before actors migrate to a fresh instance. Set it just below your platform's hard timeout (e.g. `295` for Vercel Hobby, `3595` for Vercel Pro, `840` for Cloud Run's 15-min cap). Configure via [`configurePool`](/docs/general/registry-configuration). See [Timeouts](/docs/general/runtime-modes#timeouts). +- **Tune `drainGracePeriod` to cover graceful actor shutdown.** Time reserved at the end of `requestLifespan` for actors to stop gracefully before the request is forcibly closed. Default is 30 minutes from the engine; lower it for short-lived stateless actors, raise it if your actors do non-trivial cleanup or final SQLite writes. Configure via [`configurePool`](/docs/general/registry-configuration). See [Limits](/docs/actors/limits). + +### Runner + +- **Set a graceful shutdown period of at least 35 minutes.** Runners need up to 30 minutes to drain actors during upgrades, plus buffer for shutdown overhead. In Kubernetes, set `terminationGracePeriodSeconds: 2100` on the pod spec. + +## Actors + +### Design Patterns + +- **Do not use god actors.** Avoid putting all logic into a single actor type. See [Design Patterns](/docs/actors/design-patterns). +- **Do not use actor-per-request patterns.** Avoid creating a new actor for each request. See [Design Patterns](/docs/actors/design-patterns). + +### Lifecycle + +- **Do not rely on `onSleep` for critical cleanup.** `onSleep` is not called during crashes or forced terminations. See [Lifecycle](/docs/actors/lifecycle). + +### State + +- **Verify `c.state` does not grow unbounded.** Avoid using arrays or objects that grow over time in state. Use [SQLite](/docs/actors/sqlite) for unbounded or append-heavy data instead. +- **Verify actor data does not exceed 10 GB.** Contact [enterprise support](https://rivet.dev/sales) if you need more storage. +- **Use input parameters and `createState` for actor initialization.** See [Input Parameters](/docs/actors/input). + +### Events + +- **Use `conn.send()` instead of `c.broadcast()` for private events.** `c.broadcast()` sends to all connected clients. Use `conn.send()` to send events to a specific connection. See [Realtime](/docs/actors/events). + +### Actions + +- **Review action timeout configuration.** The default `actionTimeout` is 60 seconds. Increase it if you have long-running actions like API calls or file processing. See [Actor Configuration](/docs/general/actor-configuration). +- **Review message size limits.** The default `maxIncomingMessageSize` is 64 KiB and `maxOutgoingMessageSize` is 1 MiB. Increase if your actors send or receive large JSON payloads. See [Registry Configuration](/docs/general/registry-configuration). + +### Queues + +- **Review queue limits.** The default `maxQueueSize` is 1,000 messages and `maxQueueMessageSize` is 64 KiB. Increase if you expect burst traffic or large queue payloads. See [Actor Configuration](/docs/general/actor-configuration). +- **Ensure queue handlers are idempotent.** If processing fails before `message.complete()`, the message will be retried. See [Queues](/docs/actors/queues). + +### Workflows + +- **Verify workflows do not generate infinite steps.** Use `ctx.loop` to avoid creating unbounded step histories. See [Workflows](/docs/actors/workflows). + +## Security + +### Authentication + +- **Validate connections in `createConnState` or `onBeforeConnect`.** Do not trust client input without validation. See [Authentication](/docs/actors/authentication). + +### CORS + +- **Configure CORS for production.** Restrict allowed origins instead of allowing all. See [CORS](/docs/general/cors). + +### Tokens (Rivet Cloud) + +- **Use `pk_*` tokens for `RIVET_PUBLIC_ENDPOINT`.** Public tokens are safe to expose to clients. +- **Use `sk_*` tokens for `RIVET_ENDPOINT`.** Secret tokens should only be used server-side. +- **Do not leak your secret token.** Never expose `sk_*` tokens in client-side code, public repositories, or browser environments. See [Endpoints](/docs/general/endpoints). +- **Verify you're connecting to the correct region.** Use the nearest datacenter endpoint (e.g. `api-us-west-1.rivet.dev`) for lowest latency. + +### Access Control + +Access control is only needed if you want granular permissions for different clients. For most use cases, basic authentication in `onBeforeConnect` or `createConnState` is sufficient. + +- **Use deny-by-default rules.** Reject unknown roles in `onBeforeConnect`, action handlers, `canPublish`, and `canSubscribe`. See [Access Control](/docs/actors/access-control). +- **Authorize actions explicitly.** Check the caller's role in each action handler and throw `forbidden` for unauthorized access. +- **Gate event subscriptions and queue publishes.** Use `canSubscribe` and `canPublish` hooks to restrict which clients can subscribe to events or publish to queues. + +## Clients + +- **Dispose connections and/or client when not in use (JavaScript client).** Call `conn.dispose()` or `client.dispose()` when no longer needed to free resources. React and SwiftUI clients handle this automatically. See [Connection Lifecycle](/docs/clients/javascript#connection-lifecycle). + +_Source doc path: /docs/general/production-checklist_ diff --git a/.agents/skills/per-tenant-database/reference/general/registry-configuration.md b/.agents/skills/per-tenant-database/reference/general/registry-configuration.md new file mode 100644 index 0000000..3574c4a --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/general/registry-configuration.md @@ -0,0 +1,32 @@ +# Registry Configuration + +> Source: `src/content/docs/general/registry-configuration.mdx` +> Canonical URL: https://rivet.dev/docs/general/registry-configuration +> Description: This page documents the configuration options available when setting up a RivetKit registry. The registry configuration is passed to the `setup()` function. + +--- +## Example Configurations + +### Basic Setup + +### Connecting to Rivet Engine + +## Starting Your App + +After configuring your registry, start it: + +See [Runtime Modes](/docs/general/runtime-modes) for details on when to use each mode. + +## Environment Variables + +Many configuration options can be set via environment variables. See [Environment Variables](/docs/general/environment-variables) for a complete reference. + +## Configuration Reference + +## Related + +- [Actor Configuration](/docs/general/actor-configuration): Configure individual actors +- [HTTP Server Setup](/docs/general/http-server): Set up HTTP routing and middleware +- [Architecture](/docs/general/architecture): Understand how RivetKit works + +_Source doc path: /docs/general/registry-configuration_ diff --git a/.agents/skills/per-tenant-database/reference/general/runtime-modes.md b/.agents/skills/per-tenant-database/reference/general/runtime-modes.md new file mode 100644 index 0000000..43ba986 --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/general/runtime-modes.md @@ -0,0 +1,99 @@ +# Runtime Modes + +> Source: `src/content/docs/general/runtime-modes.mdx` +> Canonical URL: https://rivet.dev/docs/general/runtime-modes +> Description: RivetKit supports two runtime modes for running your actors: + +--- +- **Serverless**: Default mode. Responds to HTTP requests and scales automatically. +- **Runners**: Background processes without HTTP endpoints. Only needed for advanced scenarios. + +## Serverless + +Serverless is the default and recommended mode. Rivet sends HTTP requests to your backend to run actor logic, allowing your infrastructure to scale automatically. + +### Benefits + +- **Platform support**: Works with serverless platforms (Vercel, Cloudflare Workers, etc.) +- **Scale to zero**: No cost when idle +- **Edge deployments**: Easier to deploy to edge locations +- **Preview deployments**: Integrates with preview deployments on platforms like Vercel and Railway +- **Efficient autoscaling**: Request-based autoscaling can be faster and more efficient than CPU-based autoscaling depending on the platform + +### Example + +See [Server Setup](/docs/general/http-server/) for more configuration options. + +### Architecture + +When a client creates an actor, it sends a request to the Rivet Engine. The engine then calls `GET /api/rivet/start` on your serverless backend to run the actor. + +Serverless architecture diagram + +### Advanced + +#### Endpoints + +Rivet exposes the following endpoints: + +- `GET /api/rivet/metadata`: Validates configuration +- `GET /api/rivet/start`: Runs an actor + +You should never call these endpoints yourself, this is included purely for comprehension of how Rivet works under the hood. + +#### Timeouts + +Serverless platforms like Vercel have function timeouts. Rivet handles this automatically by migrating actors between function invocations, preserving state through `ctx.state`. Write your code as if it runs forever, Rivet handles the rest. + +Read more about [how we handle timeouts](/blog/2025-10-20-how-we-built-websocket-servers-for-vercel-functions/#timeouts-and-failover). + +#### Shutdown Sequence + +Each serverless request has a configurable lifespan (`requestLifespan`, default: 60 minutes). Set this to match your platform's function timeout (e.g. `requestLifespan: 3600` for Vercel Pro). + +When the request nears its lifespan, the engine reserves a grace period (`serverless_drain_grace_period`, default: 10 seconds) at the end to gracefully stop actors. For example, with a 3600-second lifespan, actors begin stopping at 3590 seconds. After the full lifespan elapses, the connection is forcibly closed and any remaining actors are rescheduled. + +See [Limits](/docs/actors/limits#serverless-shutdown) for configuration details. + +## Runners + +Runners run actors as long-running background processes without exposing an HTTP endpoint. + +### When to Use Runners + +- **No HTTP server**: Your app does not or cannot expose an HTTP server +- **No load balancer**: You don't have a load balancer to distribute HTTP requests across your servers +- **Custom scaling**: You have custom scaling requirements + +### Example + +The runner runs in the background, ready to run actors. + +### Architecture + +On startup, your backend calls `registry.startEnvoy()` which opens a persistent connection to the Rivet Engine. When a client creates an actor, the engine sends a command through this connection to start the actor on your backend. + +Runners architecture diagram + +### Configuration + +#### Runner Pool + +Use `RIVET_RUNNER` to assign runners to a pool. This lets you control which runners handle specific actors. + +```bash +RIVET_RUNNER=gpu-workers +``` + +See [Pool Configuration](/docs/general/pool-configuration) for how pools are scaled, drained on version upgrades, and rate-limited during actor eviction. + +## Comparison + +| Mode | Method | Use Case | +|------|--------|----------| +| Auto | `registry.start()` | Simplest setup. Starts server, serves static files, and runs actors. | +| Serverless | `registry.serve()` | Fetch handler for serverless platforms | +| Serverless | `registry.handler()` | Integrating with existing routers (Hono, Elysia, etc.) | +| Runner | `registry.startEnvoy()` | Long-running processes without HTTP endpoints | + +_Source doc path: /docs/general/runtime-modes_ diff --git a/.agents/skills/per-tenant-database/reference/general/wasm-vs-native-sdk.md b/.agents/skills/per-tenant-database/reference/general/wasm-vs-native-sdk.md new file mode 100644 index 0000000..18cdd13 --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/general/wasm-vs-native-sdk.md @@ -0,0 +1,79 @@ +# WASM vs Native SDK + +> Source: `src/content/docs/general/wasm-vs-native-sdk.mdx` +> Canonical URL: https://rivet.dev/docs/general/wasm-vs-native-sdk +> Description: RivetKit runs your actors on a native or a WebAssembly runtime depending on your platform. + +--- +RivetKit ships two runtimes for executing actor logic. Most projects use the +native runtime automatically. Edge and serverless platforms that cannot run +native binaries use the WebAssembly runtime. + +- **Native**: Default. Runs on Node.js and Bun via native bindings. Best performance. +- **WebAssembly (wasm)**: Runs anywhere a `WebAssembly.Module` can be instantiated, including Cloudflare Workers, Supabase Edge Functions, and Deno Deploy. + +## Native runtime + +The native runtime is the default and requires no configuration. It loads +platform-specific native bindings for the best performance and full feature +support on Node.js and Bun. + +## WebAssembly runtime + +Edge platforms run on isolates (V8, Deno) that cannot load native binaries, so +RivetKit provides a WebAssembly build of its core runtime in the +`@rivetkit/rivetkit-wasm` package. You select it with `runtime: "wasm"` and pass +the wasm bindings and binary through the `wasm` option. + +### Recommended: use a platform package + +For supported platforms, use the dedicated package instead of wiring the wasm +runtime by hand. They install the wasm runtime, load the binary, and (on +Cloudflare) provide the outbound WebSocket the engine tunnel needs. + +- **Cloudflare Workers**: [`@rivetkit/cloudflare-workers`](/docs/actors/quickstart/cloudflare) +- **Supabase Edge Functions**: [`@rivetkit/supabase`](/docs/actors/quickstart/supabase) + +```typescript @nocheck +// Cloudflare Workers +import { actor } from "rivetkit"; +import { createHandler } from "@rivetkit/cloudflare-workers"; + +const counter = actor({ state: { count: 0 }, actions: {} }); + +export default createHandler({ use: { counter } }); +``` + +### Manual configuration + +For platforms without a dedicated package, configure the wasm runtime directly. +Pass the bindings from `@rivetkit/rivetkit-wasm` and the wasm binary as +`initInput`. `initInput` accepts a `Uint8Array`, `URL`, `Response`, or a +`WebAssembly.Module`. + +```typescript @nocheck +import { actor, setup } from "rivetkit"; +import * as wasmBindings from "@rivetkit/rivetkit-wasm"; +import wasmModule from "@rivetkit/rivetkit-wasm/rivetkit_wasm_bg.wasm"; + +const counter = actor({ state: { count: 0 }, actions: {} }); + +const registry = setup({ + runtime: "wasm", + wasm: { bindings: wasmBindings, initInput: wasmModule }, + use: { counter }, +}); +``` + +On platforms without an outbound `new WebSocket(url)` constructor (such as +Cloudflare Workers), you must also install a fetch-based `globalThis.WebSocket` +shim so the actor can open its tunnel to the engine. The platform packages handle +this for you, which is why they are recommended. + +## Related + +- [Runtime Modes](/docs/general/runtime-modes) +- [Cloudflare Workers Quickstart](/docs/actors/quickstart/cloudflare) +- [Supabase Functions Quickstart](/docs/actors/quickstart/supabase) + +_Source doc path: /docs/general/wasm-vs-native-sdk_ diff --git a/.agents/skills/per-tenant-database/reference/self-hosting/configuration.md b/.agents/skills/per-tenant-database/reference/self-hosting/configuration.md new file mode 100644 index 0000000..a71d16e --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/self-hosting/configuration.md @@ -0,0 +1,74 @@ +# Configuration + +> Source: `src/content/docs/self-hosting/configuration.mdx` +> Canonical URL: https://rivet.dev/docs/self-hosting/configuration +> Description: Rivet Engine can be configured through environment variables or configuration files. + +--- +The full JSON Schema for the configuration is available at [/docs/engine-config-schema.json](/docs/engine-config-schema.json). + +## Configuration Sources + +Rivet supports JSON, JSON5, JSONC, YAML, YML, and environment variable configurations. + +**Environment Variables** + +Use the `RIVET__` prefix with `__` as separator to configure properties in the config. For example: set the `RIVET__database__postgres__url` environment variable for `database.postgres.url`. + +**Configuration Paths** + +Configuration files are automatically discovered in platform-specific directories: + +- Linux: `/etc/rivet/config.json` +- macOS: `/Library/Application Support/rivet/config.json` +- Windows: `C:\ProgramData\rivet\config.json` + +**Multiple Files** + +Multiple configuration files in the same directory are loaded and merged together. For example: `/etc/rivet/config.json` and `/etc/rivet/database.json` will be merged together. + +**Override Configuration Path** + +You can override the default configuration path using the `--config` flag: + +```bash +# Load from a specific file +rivet-engine --config /path/to/config.json + +# Load from a directory +rivet-engine --config /etc/rivet + +# Load multiple paths (merged in order) +rivet-engine --config /etc/rivet/base.json --config /etc/rivet/override.json +``` + +## Configuration Reference + +### Pegboard Envoy Load Balancing + +`pegboard.envoy_load_balancer` supports the `hash` strategy for hash-ring-based envoy selection: + +```json +{ + "pegboard": { + "envoy_load_balancer": { + "hash": { + "virtual_nodes": 8, + "samples": 2, + "max_scan": 16, + "use_snapshot_read": true + } + } + } +} +``` + +Use `samples: 1` for a uniform random pick that skips slot reads. Use `samples >= 2` for power-of-K choices over envoy slot counts. Treat `virtual_nodes` as an operational invariant once envoys have registered. + +## Related + +- RivetKit actor runtime persistence lives in SQLite. Existing actor KV data is imported into SQLite the first time an actor wakes on the migrated runtime, then the original KV data is left frozen for downgrade safety. +- [PostgreSQL](/docs/self-hosting/postgres): Configure the experimental PostgreSQL backend +- [File System](/docs/self-hosting/filesystem): Configure file system storage for development + +_Source doc path: /docs/self-hosting/configuration_ diff --git a/.agents/skills/per-tenant-database/reference/self-hosting/docker-compose.md b/.agents/skills/per-tenant-database/reference/self-hosting/docker-compose.md new file mode 100644 index 0000000..8f2cea1 --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/self-hosting/docker-compose.md @@ -0,0 +1,188 @@ +# Docker Compose + +> Source: `src/content/docs/self-hosting/docker-compose.mdx` +> Canonical URL: https://rivet.dev/docs/self-hosting/docker-compose +> Description: Deploy Rivet Engine with docker-compose for multi-container setups. + +--- +## Quick Start + +### Create docker-compose.yaml + +Create a `docker-compose.yaml` in your project root: + +```yaml +services: + rivet-engine: + image: rivetdev/engine:latest + ports: + - "6420:6420" + volumes: + - rivet-data:/data + environment: + RIVET__FILE_SYSTEM__PATH: "/data" + restart: unless-stopped + +volumes: + rivet-data: +``` + +### Start the engine + +```bash +docker-compose up -d +``` + +## Connecting Your Project + +Once the engine is running, add your app as a service in the same Compose file. + +### Create your server + +Follow the [quickstart](/docs/actors/quickstart/backend) to create a working server with actors. + +### Create a Dockerfile + +Create a `Dockerfile` in your project root: + +```dockerfile @nocheck +FROM node:22-slim +WORKDIR /app +COPY package.json pnpm-lock.yaml ./ +RUN corepack enable && pnpm install --frozen-lockfile +COPY . . +RUN pnpm build +CMD ["node", "dist/index.js"] +``` + +### Add your app to docker-compose.yaml + +Update your `docker-compose.yaml` to include your app alongside the engine: + +```yaml +services: + rivet-engine: + image: rivetdev/engine:latest + ports: + - "6420:6420" + volumes: + - rivet-data:/data + environment: + RIVET__FILE_SYSTEM__PATH: "/data" + restart: unless-stopped + + my-app: + build: . + environment: + RIVET_ENDPOINT: "http://default:admin@rivet-engine:6420" + depends_on: + - rivet-engine + restart: unless-stopped + +volumes: + rivet-data: +``` + +`RIVET_ENDPOINT` tells your app to connect to the engine as a runner instead of running standalone. The URL uses the format `http://namespace:token@host:port`. Inside the Docker network, your app reaches the engine at `rivet-engine:6420`. See [Endpoints](/docs/general/endpoints) for all options. + +### Start the services + +```bash +docker-compose up -d +``` + +### Register your runner with the engine + +### Dashboard + +1. Open the Rivet Engine dashboard at `http://localhost:6420`. +2. Enter your admin token when prompted. +3. In the namespace sidebar, click **Settings**. +4. Click **Add Provider**, then choose **Custom**. +5. Click **Next** (these settings can be changed later). +6. Click **Next** (you can safely skip the env ar step for Docker Compose). +5. Go to **Confirm Connection**, enter your app endpoint (`http://my-app:6420/api/rivet`), then click **Add**. + +### CLI (curl) + +Register your runner programmatically via the engine API: + +```bash @nocheck +curl -X PUT "http://localhost:6420/runner-configs/default?namespace=default" \ + -H "Content-Type: application/json" \ + -d '{ + "datacenters": { + "default": { + "normal": {} + } + } + }' +``` + +## Configuration + +### Config File + +Mount a JSON configuration file in your `docker-compose.yaml`: + +```yaml +services: + rivet-engine: + image: rivetdev/engine:latest + ports: + - "6420:6420" + volumes: + - ./rivet-config.json:/etc/rivet/config.json:ro + - rivet-data:/data + restart: unless-stopped + +volumes: + rivet-data: +``` + +Create `rivet-config.json` in the same directory as your `docker-compose.yaml`. See the [Configuration](/docs/self-hosting/configuration) docs for all available options and the full [JSON Schema](/docs/engine-config-schema.json). + +```json +{ + "postgres": { + "url": "postgresql://rivet:password@postgres:5432/rivet" + } +} +``` + +### Postgres Setup + +PostgreSQL is the recommended backend for multi-node self-hosted deployments today, but it remains experimental. For a production-ready single-node Rivet deployment, use the file system backend (RocksDB-based). Enterprise teams can contact [enterprise support](https://rivet.dev/sales) about FoundationDB for the most scalable production-ready deployment. + +```yaml +services: + postgres: + image: postgres:15 + environment: + POSTGRES_DB: rivet + POSTGRES_USER: rivet + POSTGRES_PASSWORD: rivet_password + volumes: + - postgres-data:/var/lib/postgresql/data + restart: unless-stopped + + rivet-engine: + image: rivetdev/engine:latest + ports: + - "6420:6420" + environment: + RIVET__POSTGRES__URL: postgresql://rivet:rivet_password@postgres:5432/rivet + depends_on: + - postgres + restart: unless-stopped + +volumes: + postgres-data: +``` + +## Next Steps + +- Review the [Production Checklist](/docs/self-hosting/production-checklist) before going live +- See [Configuration](/docs/self-hosting/configuration) for all options + +_Source doc path: /docs/self-hosting/docker-compose_ diff --git a/.agents/skills/per-tenant-database/reference/self-hosting/docker-container.md b/.agents/skills/per-tenant-database/reference/self-hosting/docker-container.md new file mode 100644 index 0000000..e6983ae --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/self-hosting/docker-container.md @@ -0,0 +1,150 @@ +# Docker Container + +> Source: `src/content/docs/self-hosting/docker-container.mdx` +> Canonical URL: https://rivet.dev/docs/self-hosting/docker-container +> Description: Run Rivet Engine in a single Docker container. + +--- +## Quick Start + +### Start the engine + +```bash +docker run -d \ + --name rivet-engine \ + -p 6420:6420 \ + -v rivet-data:/data \ + -e RIVET__FILE_SYSTEM__PATH="/data" \ + rivetdev/engine:latest +``` + +### Verify the engine is running + +```bash +curl http://localhost:6420/health +``` + +## Connecting Your Project + +Once the engine is running, connect your app to it. + +### Create your server + +Follow the [quickstart](/docs/actors/quickstart/backend) to create a working server with actors. + +### Start your app with RIVET_ENDPOINT + +Set `RIVET_ENDPOINT` to tell your app to connect to the engine as a runner instead of running standalone: + +```bash @nocheck +RIVET_ENDPOINT="http://default:admin@host.docker.internal:6420" npm start +``` + +If running your app outside of Docker, use `localhost` instead of `host.docker.internal`: + +```bash @nocheck +RIVET_ENDPOINT="http://default:admin@localhost:6420" npm start +``` + +See [Endpoints](/docs/general/endpoints) for all options. + +### Register your runner with the engine + +### Dashboard + +1. Open the Rivet Engine dashboard at `http://localhost:6420`. +2. Enter your admin token when prompted. +3. In the namespace sidebar, click **Settings**. +4. Click **Add Provider**, then choose **Custom**. +5. Click **Next**. +6. Go to **Confirm Connection**, enter your app endpoint, then click **Add**. + +### CLI (curl) + +Register your runner programmatically via the engine API: + +```bash @nocheck +curl -X PUT "http://localhost:6420/runner-configs/default?namespace=default" \ + -H "Content-Type: application/json" \ + -d '{ + "datacenters": { + "default": { + "normal": {} + } + } + }' +``` + +## Configuration + +### Environment Variables + +Configure Rivet using environment variables: + +```bash +docker run -d \ + --name rivet-engine \ + -p 6420:6420 \ + -v rivet-data:/data \ + -e RIVET__FILE_SYSTEM__PATH="/data" \ + -e RIVET__POSTGRES__URL="postgresql://postgres:password@localhost:5432/db" \ + rivetdev/engine:latest +``` + +### Config File + +Mount a JSON configuration file: + +```bash +docker run -d \ + --name rivet-engine \ + -p 6420:6420 \ + -v rivet-data:/data \ + -v $(pwd)/rivet-config.json:/etc/rivet/config.json:ro \ + rivetdev/engine:latest +``` + +Create `rivet-config.json` in your working directory. See the [Configuration](/docs/self-hosting/configuration) docs for all available options and the full [JSON Schema](/docs/engine-config-schema.json). + +```json +{ + "postgres": { + "url": "postgresql://postgres:password@localhost:5432/db" + } +} +``` + +### Postgres Setup + +PostgreSQL is the recommended backend for multi-node self-hosted deployments today, but it remains experimental. For a production-ready single-node Rivet deployment, use the file system backend (RocksDB-based). Enterprise teams can contact [enterprise support](https://rivet.dev/sales) about FoundationDB for the most scalable production-ready deployment. + +```bash +# Create network +docker network create rivet-net + +# Run PostgreSQL +docker run -d \ + --name postgres \ + --network rivet-net \ + -e POSTGRES_DB=rivet \ + -e POSTGRES_USER=rivet \ + -e POSTGRES_PASSWORD=rivet_password \ + -v postgres-data:/var/lib/postgresql/data \ + postgres:15 + +# Run Rivet Engine +docker run -d \ + --name rivet-engine \ + --network rivet-net \ + -p 6420:6420 \ + -e RIVET__POSTGRES__URL="postgresql://rivet:rivet_password@postgres:5432/rivet" \ + rivetdev/engine +``` + +## Next Steps + +- Review the [Production Checklist](/docs/self-hosting/production-checklist) before going live +- Use [Docker Compose](/docs/self-hosting/docker-compose) for multi-container setups +- See [Configuration](/docs/self-hosting/configuration) for all options + +_Source doc path: /docs/self-hosting/docker-container_ diff --git a/.agents/skills/per-tenant-database/reference/self-hosting/filesystem.md b/.agents/skills/per-tenant-database/reference/self-hosting/filesystem.md new file mode 100644 index 0000000..30f7bdb --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/self-hosting/filesystem.md @@ -0,0 +1,47 @@ +# File System + +> Source: `src/content/docs/self-hosting/filesystem.mdx` +> Canonical URL: https://rivet.dev/docs/self-hosting/filesystem +> Description: The file system backend stores all data on the local disk. This is suitable for single-node deployments, development, and testing. + +--- +For a production-ready single-node Rivet deployment, use the file system backend (RocksDB-based); for multi-node deployments, PostgreSQL is the recommended backend today but remains experimental as we evaluate the best fit for scalability and performance, and Enterprise teams can contact [enterprise support](https://rivet.dev/sales) about FoundationDB. + +## Configuration + +```json Configuration-file +{ + "database": { + "file_system": { + "path": "/var/lib/rivet/data" + } + } +} +``` + +```bash Environment-variables +RIVET__database__file_system__path="/var/lib/rivet/data" +``` + +## Default Paths + +If no path is specified, Rivet uses platform-specific default locations: + +- Linux: `~/.local/share/rivet-engine/db` +- macOS: `~/Library/Application Support/rivet-engine/db` +- Windows: `%APPDATA%\rivet-engine\db` + +When running in a container or as a service, the path defaults to `./data/db` relative to the working directory. + +## When to Use File System + +The file system backend is ideal for: + +- Local development +- Single-node deployments +- Testing and prototyping +- Air-gapped environments without database infrastructure + +If you need a production-ready Rivet deployment today, use this backend for smaller single-node setups; for multi-node deployments, PostgreSQL is the recommended backend today though still experimental, and Enterprise teams can contact [enterprise support](https://rivet.dev/sales) about FoundationDB for the most scalable production-ready deployment. + +_Source doc path: /docs/self-hosting/filesystem_ diff --git a/.agents/skills/per-tenant-database/reference/self-hosting/foundationdb.md b/.agents/skills/per-tenant-database/reference/self-hosting/foundationdb.md new file mode 100644 index 0000000..b8331bb --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/self-hosting/foundationdb.md @@ -0,0 +1,35 @@ +# FoundationDB (Enterprise) + +> Source: `src/content/docs/self-hosting/foundationdb.mdx` +> Canonical URL: https://rivet.dev/docs/self-hosting/foundationdb +> Description: FoundationDB is the recommended storage backend for scalable production Rivet deployments. + +--- +FoundationDB requires an enterprise license. Contact [enterprise support](https://rivet.dev/sales) for setup guidance and access. + +## Overview + +FoundationDB is the recommended storage backend for scalable, production-ready Rivet deployments. It is a distributed, ordered key-value store originally built by Apple. + +FoundationDB powers some of the largest infrastructure in the world: + +- **Apple**: iCloud and other Apple services +- **Snowflake**: Cloud data platform metadata layer +- **Datadog**: Observability and monitoring platform +- **Tigris Data**: Globally distributed object storage + +Its strict serializability guarantees, fault tolerance, and ability to scale linearly across nodes make it the ideal backend for Rivet's actor state and orchestration layer. + +## Why FoundationDB + +| | RocksDB (File System) | PostgreSQL | FoundationDB | +|---|---|---|---| +| **Scalability** | Single node | Primary/replica failover | Linear horizontal scaling | +| **Fault tolerance** | None | Primary/replica failover | Automatic recovery with no data loss | +| **Production readiness** | Development and small deployments | Experimental for multi-node | Battle-tested at global scale | + +## Getting Started + +FoundationDB configuration and cluster setup are handled as part of enterprise onboarding. Contact [enterprise support](https://rivet.dev/sales) to get started. + +_Source doc path: /docs/self-hosting/foundationdb_ diff --git a/.agents/skills/per-tenant-database/reference/self-hosting/install.md b/.agents/skills/per-tenant-database/reference/self-hosting/install.md new file mode 100644 index 0000000..344be8e --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/self-hosting/install.md @@ -0,0 +1,32 @@ +# Installing Rivet Engine + +> Source: `src/content/docs/self-hosting/install.mdx` +> Canonical URL: https://rivet.dev/docs/self-hosting/install +> Description: Install Rivet Engine using Docker, binaries, or a source build. + +--- +For more options: + +- [Docker Container](/docs/self-hosting/docker-container) for persistent storage, configuration, and production setups +- [Docker Compose](/docs/self-hosting/docker-compose) for multi-container deployments with PostgreSQL + +## Docker + +```bash +docker run -p 6420:6420 rivetdev/engine +``` + +## Prebuilt Binaries + + Prebuilt binaries coming soon + +## Build From Source + +```bash +git clone https://github.com/rivet-dev/rivet.git +cd rivet +cargo build --release -p rivet-engine +./target/release/rivet-engine +``` + +_Source doc path: /docs/self-hosting/install_ diff --git a/.agents/skills/per-tenant-database/reference/self-hosting/kubernetes.md b/.agents/skills/per-tenant-database/reference/self-hosting/kubernetes.md new file mode 100644 index 0000000..a2c619c --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/self-hosting/kubernetes.md @@ -0,0 +1,236 @@ +# Kubernetes + +> Source: `src/content/docs/self-hosting/kubernetes.mdx` +> Canonical URL: https://rivet.dev/docs/self-hosting/kubernetes +> Description: Deploy production-ready Rivet Engine to Kubernetes with PostgreSQL storage. + +--- +PostgreSQL is the recommended backend for multi-node self-hosted deployments today, but it remains experimental. For a production-ready single-node Rivet deployment, use the file system backend (RocksDB-based). Enterprise teams can contact [enterprise support](https://rivet.dev/sales) about FoundationDB for the most scalable production-ready deployment. + +## Prerequisites + +- Kubernetes cluster +- `kubectl` configured +- [Metrics server](https://github.com/kubernetes-sigs/metrics-server) (required for HPA) — included by default in most distributions (k3d, GKE, EKS, AKS) + +## Deploy Rivet Engine + + +### Download Manifests + + Download the `self-host/k8s/engine` directory from the Rivet repository: + + ```bash + npx giget@latest gh:rivet-dev/rivet/self-host/k8s/engine rivet-k8s + cd rivet-k8s + ``` + + + +### Configure Engine + + In `02-engine-configmap.yaml`, set `public_url` to your engine's external URL. + + + +### Configure PostgreSQL + + In `11-postgres-secret.yaml`, update the PostgreSQL password. See [Using a Managed PostgreSQL Service](#using-a-managed-postgresql-service) for external databases. + + + +### Configure Admin Token + + Generate a secure admin token and save it somewhere safe: + + ```bash + openssl rand -hex 32 + ``` + + Create the namespace and store the token as a Kubernetes secret: + + ```bash + kubectl create namespace rivet-engine + kubectl -n rivet-engine create secret generic rivet-secrets --from-literal=admin-token=YOUR_TOKEN_HERE + ``` + + + +### Deploy + + ```bash + # Apply all manifests + kubectl apply -f . + + # Wait for all pods to be ready + kubectl -n rivet-engine wait --for=condition=ready pod -l app=nats --timeout=300s + kubectl -n rivet-engine wait --for=condition=ready pod -l app=postgres --timeout=300s + kubectl -n rivet-engine wait --for=condition=ready pod -l app=rivet-engine --timeout=300s + + # Verify all pods are running + kubectl -n rivet-engine get pods + ``` + + + +### Access the Engine + + Visit `/ui` on your `public_url` to access the dashboard. + + +## Connecting Your Project + +### Create your server + +Follow the [quickstart](/docs/actors/quickstart/backend) to create a working server with actors. + +### Create Kubernetes manifests + +Create these manifest files for your app: + +```yaml deployment.yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: rivetkit-app + namespace: your-namespace +spec: + replicas: 1 + selector: + matchLabels: + app: rivetkit-app + template: + metadata: + labels: + app: rivetkit-app + spec: + # Allow enough time for actors to gracefully stop on SIGTERM. + # The runner waits up to 30m for actors to finish. + # Add buffer for runner shutdown overhead after actors stop. + # See: /docs/actors/versions#graceful-shutdown-sigterm + terminationGracePeriodSeconds: 2100 + containers: + - name: rivetkit-app + image: registry.example.com/your-team/rivetkit-app:latest + envFrom: + - secretRef: + name: rivetkit-secrets +``` + +```yaml service.yaml +apiVersion: v1 +kind: Service +metadata: + name: rivetkit-app + namespace: your-namespace +spec: + selector: + app: rivetkit-app + ports: + - name: http + port: 8080 + targetPort: 8080 +``` + +### Configure the endpoint + +Create `rivetkit-secrets.yaml` with `RIVET_ENDPOINT` pointing to the engine. This tells your app to connect as a runner instead of running standalone. See [Endpoints](/docs/general/endpoints) for all options. + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: rivetkit-secrets + namespace: your-namespace +type: Opaque +stringData: + RIVET_ENDPOINT: http://my-app:your-admin-token@your-engine.example.com +``` + +### Deploy your app + +```bash +kubectl apply -f rivetkit-secrets.yaml +kubectl apply -f deployment.yaml +kubectl apply -f service.yaml +``` + +### Register your runner with the engine + +### Dashboard + +1. Open the Rivet Engine dashboard in your browser. +2. Enter your admin token when prompted. +3. In the namespace sidebar, click **Settings**. +4. Click **Add Provider**, then choose **Custom**. +5. Click **Next**. +6. Go to **Confirm Connection**, enter your app endpoint (e.g. `http://rivetkit-app.your-namespace:8080/api/rivet`), then click **Add**. + +### CLI (curl) + +Register your runner programmatically via the engine API: + +```bash @nocheck +curl -X PUT "https://your-engine.example.com/runner-configs/default?namespace=default" \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer your-admin-token" \ + -d '{ + "datacenters": { + "default": { + "normal": {} + } + } + }' +``` + +## Advanced + +### Using a Managed PostgreSQL Service + +If you prefer to use a managed PostgreSQL service (e.g. Amazon RDS, Cloud SQL, Azure Database) instead of the bundled Postgres deployment: + +- Update the `postgres.url` connection string in `02-engine-configmap.yaml` to point to your managed instance +- Delete the bundled PostgreSQL manifests: + - `10-postgres-configmap.yaml` + - `11-postgres-secret.yaml` + - `12-postgres-statefulset.yaml` + - `13-postgres-service.yaml` + +### Applying Configuration Updates + +When making subsequent changes to `02-engine-configmap.yaml`, restart the engine pods to pick up the new configuration: + +```bash +kubectl apply -f 02-engine-configmap.yaml +kubectl -n rivet-engine rollout restart deployment/rivet-engine +``` + +### Ingress and Load Balancer Timeouts + +Rivet uses long-lived WebSocket connections for both client traffic (browsers, SDKs) and envoy traffic (actor hosts connecting back to the engine). Default Ingress and cloud load balancer idle timeouts (typically 30 to 60 seconds) drop these connections and cause reconnect storms. + +Raise the idle / read / send timeout on every Ingress and load balancer in front of the engine to at least 1 hour (`3600` seconds). Examples: + +- **NGINX Ingress** (annotations on the Ingress): + + ```yaml + nginx.ingress.kubernetes.io/proxy-read-timeout: "3600" + nginx.ingress.kubernetes.io/proxy-send-timeout: "3600" + ``` + +- **AWS Load Balancer Controller (ALB)**: + + ```yaml + alb.ingress.kubernetes.io/load-balancer-attributes: idle_timeout.timeout_seconds=3600 + ``` + +- **GCE Ingress (GKE)**: set `timeoutSec: 3600` on the `BackendConfig` referenced by the Service. + +The same guidance applies to the load balancer fronting your RivetKit app, since envoys connect to it over WebSocket. + +## Next Steps + +- Review the [Production Checklist](/docs/self-hosting/production-checklist) before going live +- See [Configuration](/docs/self-hosting/configuration) for all engine config options + +_Source doc path: /docs/self-hosting/kubernetes_ diff --git a/.agents/skills/per-tenant-database/reference/self-hosting/multi-region.md b/.agents/skills/per-tenant-database/reference/self-hosting/multi-region.md new file mode 100644 index 0000000..2ecca85 --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/self-hosting/multi-region.md @@ -0,0 +1,10 @@ +# Multi-Region + +> Source: `src/content/docs/self-hosting/multi-region.mdx` +> Canonical URL: https://rivet.dev/docs/self-hosting/multi-region +> Description: Rivet Engine supports scaling transparently across multiple regions. + +--- +Documentation coming soon + +_Source doc path: /docs/self-hosting/multi-region_ diff --git a/.agents/skills/per-tenant-database/reference/self-hosting/postgres.md b/.agents/skills/per-tenant-database/reference/self-hosting/postgres.md new file mode 100644 index 0000000..e935246 --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/self-hosting/postgres.md @@ -0,0 +1,192 @@ +# PostgreSQL + +> Source: `src/content/docs/self-hosting/postgres.mdx` +> Canonical URL: https://rivet.dev/docs/self-hosting/postgres +> Description: Configure PostgreSQL for self-hosted Rivet deployments. + +--- +PostgreSQL is the recommended backend for multi-node self-hosted deployments today, but it remains experimental. For a production-ready single-node Rivet deployment, use the file system backend (RocksDB-based). Enterprise teams can contact [enterprise support](https://rivet.dev/sales) about FoundationDB for the most scalable production-ready deployment. + +## Basic Configuration + +```json Configuration-file +{ + "database": { + "postgres": { + "url": "postgresql://user:password@host:5432/database" + } + } +} +``` + +```bash Environment-variables +RIVET__database__postgres__url="postgresql://user:password@host:5432/database" +``` + +## Managed Postgres Compatibility + +Some hosted PostgreSQL platforms require additional configuration due to platform-specific restrictions. + +### PlanetScale + +Use direct connection (not connection pooler). + +```json Configuration-file +{ + "database": { + "postgres": { + "url": "postgresql://pscale_api_.:@.pg.psdb.cloud:5432/postgres?sslmode=require", + "unstable_disable_lock_customization": true + } + } +} +``` + +```bash Environment-variables +RIVET__database__postgres__url="postgresql://pscale_api_.:@.pg.psdb.cloud:5432/postgres?sslmode=require" +RIVET__database__postgres__unstable_disable_lock_customization=true +``` + +### Supabase + +Use direct connection on port `5432` (not connection pooler). + +#### Without SSL + +```json Configuration-file +{ + "database": { + "postgres": { + "url": "postgresql://postgres:@db..supabase.co:5432/postgres?sslmode=disable", + "unstable_disable_lock_customization": true + } + } +} +``` + +```bash Environment-variables +RIVET__database__postgres__url="postgresql://postgres:@db..supabase.co:5432/postgres?sslmode=disable" +RIVET__database__postgres__unstable_disable_lock_customization=true +``` + +#### With SSL + +Download the root certificate from your Supabase dashboard and specify its path. See [Supabase SSL Enforcement](https://supabase.com/docs/guides/platform/ssl-enforcement) for details. + +```json Configuration-file +{ + "database": { + "postgres": { + "url": "postgresql://postgres:@db..supabase.co:5432/postgres?sslmode=require", + "unstable_disable_lock_customization": true, + "ssl": { + "root_cert_path": "/path/to/supabase-ca.crt" + } + } + } +} +``` + +```bash Environment-variables +RIVET__database__postgres__url="postgresql://postgres:@db..supabase.co:5432/postgres?sslmode=require" +RIVET__database__postgres__unstable_disable_lock_customization=true +RIVET__database__postgres__ssl__root_cert_path="/path/to/supabase-ca.crt" +``` + +## SSL/TLS Support + +To enable SSL for Postgres, add `sslmode=require` to your PostgreSQL connection URL: + +```json Configuration-file +{ + "database": { + "postgres": { + "url": "postgresql://user:password@host.example.com:5432/database?sslmode=require" + } + } +} +``` + +```bash Environment-variables +RIVET__database__postgres__url="postgresql://user:password@host.example.com:5432/database?sslmode=require" +``` + +The `sslmode` parameter controls TLS usage: + +- `disable`: Do not use TLS +- `prefer`: Use TLS if available, otherwise connect without TLS (default) +- `require`: Require TLS connection (fails if TLS is not available) + +To verify the server certificate against a CA or verify the hostname, use custom SSL certificates (see below). + +### Custom SSL Certificates + +For databases using custom certificate authorities (e.g., Supabase) or requiring client certificate authentication, you can specify certificate paths in the configuration: + +```json Configuration-file +{ + "database": { + "postgres": { + "url": "postgresql://user:password@host:5432/database?sslmode=require", + "ssl": { + "root_cert_path": "/path/to/root-ca.crt", + "client_cert_path": "/path/to/client.crt", + "client_key_path": "/path/to/client.key" + } + } + } +} +``` + +```bash Environment-variables +RIVET__database__postgres__url="postgresql://user:password@host:5432/database?sslmode=require" +RIVET__database__postgres__ssl__root_cert_path="/path/to/root-ca.crt" +RIVET__database__postgres__ssl__client_cert_path="/path/to/client.crt" +RIVET__database__postgres__ssl__client_key_path="/path/to/client.key" +``` + +| Parameter | Description | PostgreSQL Equivalent | +|-----------|-------------|----------------------| +| `root_cert_path` | Path to the root certificate file for verifying the server's certificate | `sslrootcert` | +| `client_cert_path` | Path to the client certificate file for client certificate authentication | `sslcert` | +| `client_key_path` | Path to the client private key file for client certificate authentication | `sslkey` | + +All SSL paths are optional. If not specified, Rivet uses the default system root certificates from Mozilla's root certificate store. + +## Do Not Use Connection Poolers + +Rivet requires direct PostgreSQL connections for session-level features and does not support connection poolers. + +Do not use: + +- PgBouncer +- Supavisor +- AWS RDS Proxy + +## Troubleshooting + +### Permission Denied Errors + +If you see errors like: + +``` +ERROR: permission denied to set parameter "deadlock_timeout" +ERROR: current transaction is aborted, commands ignored until end of transaction block +``` + +Add `unstable_disable_lock_customization: true` to your configuration: + +```json +{ + "database": { + "postgres": { + "url": "postgresql://...", + "unstable_disable_lock_customization": true + } + } +} +``` + +This disables Rivet's attempt to set `lock_timeout = 0` and `deadlock_timeout = 10ms`. Since `lock_timeout` defaults to `0` in PostgreSQL, skipping these settings is safe. Deadlock detection will use the default `1s` timeout instead of `10ms`. + +_Source doc path: /docs/self-hosting/postgres_ diff --git a/.agents/skills/per-tenant-database/reference/self-hosting/production-checklist.md b/.agents/skills/per-tenant-database/reference/self-hosting/production-checklist.md new file mode 100644 index 0000000..3cefa5a --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/self-hosting/production-checklist.md @@ -0,0 +1,53 @@ +# Production Checklist + +> Source: `src/content/docs/self-hosting/production-checklist.mdx` +> Canonical URL: https://rivet.dev/docs/self-hosting/production-checklist +> Description: Checklist for deploying a self-hosted Rivet Engine to production. + +--- +We recommend passing this page to your coding agent to verify your configuration before deploying. + +PostgreSQL is the recommended backend for multi-node self-hosted deployments today, but it remains experimental. For a production-ready single-node Rivet deployment, use the file system backend (RocksDB-based). Enterprise teams can contact [enterprise support](https://rivet.dev/sales) about FoundationDB for the most scalable production-ready deployment. + +Also review the [general production checklist](/docs/general/production-checklist). + +## Security + +- **Validate that you have an admin token configured.** Generate a strong, random token for engine authentication. See [Configuration](/docs/self-hosting/configuration). +- **Verify your admin token is not exposed publicly.** Do not include the admin token in `RIVET_PUBLIC_ENDPOINT` or anywhere accessible to clients. See [Endpoints](/docs/general/endpoints#public-endpoint). +- **Configure TLS termination.** Ensure connections to the engine are encrypted via a reverse proxy or load balancer. + +## Resources + +- **Set container resource limits.** Recommended at least 1 CPU and 2 GB of RAM per Rivet Engine instance. +- **Configure health checks.** Set up liveness and readiness probes on port `6421` at `/health`. Recommended timeout of 5 seconds. + +## Scaling + +- **Configure autoscaling for the Rivet Engine.** Set target CPU utilization to 70% and memory to 80% to ensure headroom for traffic spikes. In Kubernetes, this is configured via a Horizontal Pod Autoscaler (HPA). +- **Use 2+ engine nodes for redundancy.** Running a single engine node is a single point of failure. Deploy at least two engine instances behind a load balancer. +- **RocksDB only supports a single node.** Do not run multiple RocksDB nodes. For a production-ready single-node Rivet deployment, use the file system backend (RocksDB-based). For multi-node deployments, PostgreSQL is the recommended backend today, though it remains experimental as we evaluate the best fit for scalability and performance. +- **Validate the rate limit on your serverless actor host.** Actor start requests are sent from your engine instances, so they all originate from a small set of IPs. Per-IP rate limits on the actor host will throttle the engine before they would throttle end-user traffic. Size the limit to your peak actor create and wake rate, and configure platform max concurrency (e.g. on GCP Cloud Run) to match your expected concurrent actor count. + +## PostgreSQL + +- **PostgreSQL is recommended for multi-node deployments, but remains experimental.** Validate the deployment carefully before rollout. +- **Configure automated backups.** Set up regular backups for your PostgreSQL database to prevent data loss. +- **Configure failover.** Set up a standby replica with automatic failover to ensure high availability. +- **Use FoundationDB for the most scalable production-ready deployments.** FoundationDB provides the best performance, scalability, and uptime for Rivet. Contact [enterprise support](https://rivet.dev/sales) for FoundationDB guidance. + +## NATS + +- **Use NATS for pub/sub (recommended).** By default, Rivet uses PostgreSQL `LISTEN`/`NOTIFY` for pub/sub which has limited throughput. NATS significantly improves performance for high-traffic deployments. This is not needed if using RocksDB. See [Configuration](/docs/self-hosting/configuration). +- **Deploy 2+ NATS replicas.** Run at least two NATS replicas for high availability. + +## Monitoring + +- **Configure OpenTelemetry.** The Rivet Engine supports exporting traces and metrics via OpenTelemetry. Set `RIVET_OTEL_ENABLED=1` and `RIVET_OTEL_GRPC_ENDPOINT` to your collector endpoint (defaults to `http://localhost:4317`). Adjust `RIVET_OTEL_SAMPLER_RATIO` to control trace sampling (defaults to `0.001`). See [Configuration](/docs/self-hosting/configuration). +- **Set up alerts for critical metrics.** Monitor engine CPU, memory, request latency, and error rates. Configure alerts to notify your team before issues become outages. + +## Enterprise + +- **Contact [enterprise support](https://rivet.dev/sales) for production-ready deployments.** We can help with architecture review, scaling guidance, and FoundationDB support. + +_Source doc path: /docs/self-hosting/production-checklist_ diff --git a/.agents/skills/per-tenant-database/reference/self-hosting/railway.md b/.agents/skills/per-tenant-database/reference/self-hosting/railway.md new file mode 100644 index 0000000..c4bb922 --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/self-hosting/railway.md @@ -0,0 +1,99 @@ +# Railway Deployment + +> Source: `src/content/docs/self-hosting/railway.mdx` +> Canonical URL: https://rivet.dev/docs/self-hosting/railway +> Description: Railway provides a simple platform for deploying Rivet Engine with automatic scaling and managed infrastructure. + +--- +## Video Tutorial + +PostgreSQL is the recommended backend for multi-node self-hosted deployments today, but it remains experimental. For a production-ready single-node Rivet deployment, use the file system backend (RocksDB-based). Enterprise teams can contact [enterprise support](https://rivet.dev/sales) about FoundationDB for the most scalable production-ready deployment. + +## Quick Deploy + +Choose the template that best fits your needs: + +| **Rivet Template** | **Rivet Starter** | +|-------------------|-------------------| +| [![Deploy on Railway](https://railway.com/button.svg)](https://railway.com/deploy/rivet?referralCode=RC7bza&utm_medium=integration&utm_source=template&utm_campaign=generic) | [![Deploy Rivet Starter](https://railway.com/button.svg)](https://railway.com/deploy/rivet-starter) | +| **Blank template** to start fresh | **Complete example** with chat app | +| - Rivet Engine | - Pre-configured Rivet Engine | +| - PostgreSQL database | - Example chat application with Actors | +| - Basic configuration | - PostgreSQL database | +| - Manual setup required | - Rivet Inspector for debugging | +| | - Ready to run immediately | + +You can also use the [Rivet Railway template](https://github.com/rivet-dev/template-railway) as a starting point for your application. + +After deploying either template, you can find the `RIVET__AUTH__ADMIN_TOKEN` under the **Variables** tab in the Railway dashboard. This token is required to access the Rivet Inspector. + +## Manual Deployment + +### Prerequisites + +1. [Railway account](https://railway.app) +2. [Railway CLI](https://docs.railway.app/develop/cli) (optional) + +### Step 1: Create New Project + +```bash +# Using Railway CLI +railway init + +# Or create via dashboard +# https://railway.app/new +``` + +### Step 2: Add Services + +#### Deploy PostgreSQL Database + +1. Click "New Service" → "Database" → "PostgreSQL" +2. Railway automatically provisions and configures PostgreSQL +3. Note the connection string from the service variables + +#### Deploy Rivet Engine + +1. Click "New Service" → "Docker Image" +2. Set image: `rivetdev/engine:latest` +3. Configure environment variables: + - `RIVET__POSTGRES__URL=${{Postgres.DATABASE_URL}}` +4. Configure graceful shutdown (see [Graceful Shutdown](#graceful-shutdown) below) + +### Step 3: Deploy Your Application + +Follow the [Railway Quick Start guide](https://docs.railway.com/quick-start) to deploy your repository: + +1. Connect your GitHub account to Railway +2. Select your repository containing your Rivet application +3. Railway will automatically detect and deploy your application +4. Configure environment variables for your application: + - `RIVET_ENDPOINT=${{Rivet.RAILWAY_PRIVATE_DOMAIN}}` - Points to the Rivet Engine service's private domain + +## WebSocket Timeouts + +Rivet uses long-lived WebSocket connections for both client traffic (browsers, SDKs) and envoy traffic (actor hosts connecting back to the engine). Railway's HTTP proxy supports WebSockets, but you must make sure no app-side timeout cuts them off. + +If you front the engine with your own reverse proxy (NGINX, Caddy, etc.) inside the Railway service, raise its idle / read timeout to at least 1 hour (`3600` seconds). The same guidance applies to the Railway service hosting your RivetKit app, since envoys connect to it over WebSocket. + +## Graceful Shutdown + +By default, Railway kills the old deploy 0 seconds after sending `SIGTERM` (see [Railway's docs](https://docs.railway.com/deployments/reference#singleton-deploys)), so in-flight requests are dropped and state flushes can be interrupted on every deploy. Rivet ships with a `SIGTERM` handler that drains cleanly, but it only gets to run if Railway is configured to give it time. + +Configure the following under **Settings → Deploy** on your service: + +- **Draining seconds** — the grace window between `SIGTERM` and `SIGKILL`. This is how long Rivet has to finish in-flight work and flush state. See [Railway's deployment teardown docs](https://docs.railway.com/deployments/deployment-teardown). + +Set draining seconds in the dashboard, via `drainingSeconds` in config-as-code, or via the `RAILWAY_DEPLOYMENT_DRAINING_SECONDS` service variable. A reasonable value is **60 seconds**. + +If your start command is a wrapper process (for example `npm start`, `yarn start`, `pnpm start`, or a shell script), the wrapper becomes PID 1 and swallows the signal — your app never drains and Railway force-kills it at the end of the window. + +- Invoke your binary directly as the start command (for example `node dist/index.js`, not `npm start`). +- Or run `dumb-init` / `tini` as PID 1 in your Dockerfile so signals forward to your process. + +## Next Steps + +- Review the [Production Checklist](/docs/self-hosting/production-checklist) before going live +- See [Configuration](/docs/self-hosting/configuration) for all options + +_Source doc path: /docs/self-hosting/railway_ diff --git a/.agents/skills/per-tenant-database/reference/self-hosting/render.md b/.agents/skills/per-tenant-database/reference/self-hosting/render.md new file mode 100644 index 0000000..0bc6d6c --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/self-hosting/render.md @@ -0,0 +1,131 @@ +# Render Deployment + +> Source: `src/content/docs/self-hosting/render.mdx` +> Canonical URL: https://rivet.dev/docs/self-hosting/render +> Description: Deploy Rivet Engine to Render with managed PostgreSQL and automatic HTTPS, using the experimental PostgreSQL backend. + +--- +PostgreSQL is the recommended backend for multi-node self-hosted deployments today, but it remains experimental. For a production-ready single-node Rivet deployment, use the file system backend (RocksDB-based). Enterprise teams can contact [enterprise support](https://rivet.dev/sales) about FoundationDB for the most scalable production-ready deployment. + +## Prerequisites + +1. A [Render account](https://render.com) +2. A GitHub repository + +## Deploy Rivet Engine + + +### Create the Blueprint Files + + Add these three files to the root of your GitHub repository: + + **render.yaml** + + ```yaml + databases: + - name: rivet-db + plan: basic-256mb + databaseName: rivet + user: rivet + + services: + - type: web + name: rivet-engine + runtime: docker + dockerfilePath: ./Dockerfile.render + plan: starter + healthCheckPath: /health + envVars: + - key: DATABASE_URL + fromDatabase: + name: rivet-db + property: connectionString + - key: RIVET__AUTH__ADMIN_TOKEN + generateValue: true + ``` + + **Dockerfile.render** + + ```dockerfile + FROM rivetdev/engine:latest + + COPY entrypoint.render.sh /entrypoint.render.sh + RUN chmod +x /entrypoint.render.sh + + ENTRYPOINT ["/entrypoint.render.sh"] + ``` + + **entrypoint.render.sh** + + ```bash + #!/bin/sh + + if [ -n "$DATABASE_URL" ]; then + export RIVET__POSTGRES__URL="${DATABASE_URL}?sslmode=disable" + fi + + exec /usr/bin/rivet-engine start + ``` + + Commit and push these files to your repository. + + + +### Deploy to Render + + 1. Go to the [Render Dashboard](https://dashboard.render.com/) + 2. Click **Blueprints** in the left sidebar + 3. Click **New Blueprint Instance** + 4. Connect your GitHub account if you haven't already + 5. Select the repository containing the files from the previous step + 6. Click **Apply** + + Render will automatically create the PostgreSQL database and deploy the Rivet Engine. + + + +### Get Your Admin Token + + 1. Once deployed, go to your **rivet-engine** service in the Render Dashboard + 2. Click the **Environment** tab + 3. Find `RIVET__AUTH__ADMIN_TOKEN` and click the eye icon to reveal the value + 4. Copy this token — you'll need it to access the dashboard + + + +### Access the Rivet Dashboard + + Open your service URL in a browser: + + ``` + https://rivet-engine-xxxx.onrender.com/ui/ + ``` + + Replace `rivet-engine-xxxx` with your actual service name from the Render Dashboard. + + Enter the admin token from the previous step to log in. + + +## WebSocket Timeouts + +Rivet uses long-lived WebSocket connections for both client traffic (browsers, SDKs) and envoy traffic (actor hosts connecting back to the engine). Render's HTTP proxy supports WebSockets, but its default idle timeout will close connections that have no traffic for too long. + +If you front the engine with your own reverse proxy (NGINX, Caddy, etc.) on Render, raise its idle / read timeout to at least 1 hour (`3600` seconds). The same guidance applies to the Render service hosting your RivetKit app, since envoys connect to it over WebSocket. + +## Connecting Your Application + +To connect a RivetKit application to your self-hosted engine, set these environment variables in your app: + +```bash +RIVET_ENDPOINT=https://:@rivet-engine-xxxx.onrender.com +RIVET_PUBLIC_ENDPOINT=https://@rivet-engine-xxxx.onrender.com +``` + +See the [Connect guide](/docs/deploy/custom) for more details on connecting your application. + +## Next Steps + +- Review the [Production Checklist](/docs/self-hosting/production-checklist) before going live +- See [Configuration](/docs/self-hosting/configuration) for all options + +_Source doc path: /docs/self-hosting/render_ diff --git a/.agents/skills/per-tenant-database/reference/self-hosting/tls.md b/.agents/skills/per-tenant-database/reference/self-hosting/tls.md new file mode 100644 index 0000000..63573d2 --- /dev/null +++ b/.agents/skills/per-tenant-database/reference/self-hosting/tls.md @@ -0,0 +1,22 @@ +# TLS & Certificates + +> Source: `src/content/docs/self-hosting/tls.mdx` +> Canonical URL: https://rivet.dev/docs/self-hosting/tls +> Description: How Rivet validates TLS root certificates. + +--- +Public CAs (Let's Encrypt, AWS ACM, and so on) work out of the box. You only need to read this page if you're running behind a corporate or private CA. + +Rivet reads the operating system trust store (`/etc/ssl/certs` on Linux, Keychain on macOS, Schannel on Windows) for all outbound HTTPS. Operator-installed corporate CAs live there and are honored automatically. + +To trust a private CA inside the official `rivetdev/engine` image, extend it the standard Debian way: + +```dockerfile +FROM rivetdev/engine:latest +COPY my-corp-ca.crt /usr/local/share/ca-certificates/ +RUN update-ca-certificates +``` + +Rebuild and redeploy. The same approach works for any machine running a Rivet client: install the CA into the OS trust store and Rivet picks it up. + +_Source doc path: /docs/self-hosting/tls_ diff --git a/apps/web/design/zopu-audit-expanded.png b/apps/web/design/zopu-audit-expanded.png new file mode 100644 index 0000000000000000000000000000000000000000..581768a8c7baa521a3e3a5cda91378c99650a51f GIT binary patch literal 176350 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

Ykb=%XLwM8=f1i~!eWf`y8@ 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%0AlKijn-XWanhk^UDFI>BVmT4d*6M>OIc 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�Gwm%_aYe19?=e3Ms4Pb`!R613Z`ujWqODScV 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 literal 0 HcmV?d00001 diff --git a/apps/web/design/zopu-chat.fig b/apps/web/design/zopu-chat.fig new file mode 100644 index 0000000000000000000000000000000000000000..1ca3d8cac894e2c3cd54eb6cbf46b19880fdbbdc GIT binary patch literal 48954 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

@P#dcBcg0g%4TvZ=ZCo9!>4-oE$f^@9S=%JD6E< z{b07>WT+@VRd?Bx28nj*>tGn$;r2T#&5BKA=A#|RqROj_oYV)k-0zMg?_G~f%^Apt~+tWeQ~}CJ|D_9hEyCWo6Z77-1SB)30QrSx8T2B zG(X>p9e7#Gmf*guf#A3qru;QwuEF^@8AvyN74*32u-OG7frl(>u5cXo>vt61RtB1n zGVBs)acDR}q{wJ(Xdl5k%N%k3TpjOyIZmK%h=He|_$Vmb8Lbu?hfb8))XoM8L}_ze zpswjuUl^T$ZK{um&^IyovNX`GIw)Hpp&r&K6PJGDa?ovq>w}e-OSO)vZeUGQJ5yA#X!| zND{h^fc58;N(ty9?}pKS_+@OTgGJ2;UhLz#Mf>WYGT>bW;Cv4?I`M7XWZFXpbz;PB z?%3#-x2^nG$evkMQOdXV9atL8xvjm`bRBeU@B&+{Yy( zFKvM6_%`rO{z|ZF$eR*1_enS2h-3+WhK7kQB$wyWk0MoGN1$OPsyC81SBoZ+wT`h( zfLaQS>RQ9Mc|C$6HT7i_%09U-xcChkj7LUjF#`NJi`2qjXLBJ>3@xEJwd~X2ygU@w z;?cX`tdsEzRJ+Vfh? zZt+`41Lbj-07k{|jo5R}#i}(NQUI+>2>%jCqee7E&$06>skl6V`KV8a7sHqOTw14J zXbJq(HscYJgp*Y3dnGR8Pu$Xw=**=x>-?y#fGOntz1*X^-4In~(ddgS_QNz7L?^V! z7kL2LF|hqqpD4v=(?Z~Prk9@Kj)FnK*$~jB?Bk!r29Xo1c;XGK4|ZK5cP4ue-;odV z`#PU-T$W>f%g-{RR@}p>tOk0<9MFW6h%atE#ike{Y}R@9jAEI5c3c>6VmQt4cpqH!2v}==<9Ce_M(>7rju@?kshb$A%D8Ta+y4Va z-dxr*+8P(c&iEq+6_p(IR!Z`~ZkHfbfzteEz@Y_uDlv;!GS4-ndVHvK3)F){eCSmf>|L>0@4_an!Exs6C)8gsv4 zjCh4$VziM*GuVtGGzHADe+|bg#7-(PHlW^bs%kU;aIpmKfUrn6;562)*gf%72^vnZ zEQ5jTr%IiV(J|C`h*_VL+D}jDhono9>y2flgBTAB^`UTQEiY<4{^`tgF$7^3&BS1} z{23d&?E7^X>yj18-SSo3?_yBQ4ebc!%Ntl^dsssi?nbazMf)(4W!sdH6RR4D|q z;rMUWj$nPM3YmX~a}}-Ggv~;4pBRHKLYR8AA*N(Sf{%+MS7u73wRDF>Vj25oqWZWc_FHvPp!J-TN zXWZ%p6Hvlm9Zt(HMWCX_Gizfy4RB%)%;vXT&*HaFq`6ZiqX}#-Q-MTF zcWOS}R>!pR(pO2UQ|YrML$Ihzbz6=(Q(yRz!29GxF*f8>;&(rQ&lY+}Kz8kT#&?ci zF#zOdfn()(ijyhGdX?yJr_M-r=FTD`=$ZjE#cW}FK-DEJ_XD99S)qT>J`BM`M%pla zJCdkNLLH{OlT11$iW(pB?`K<3t0R)2VrjgNIkC^GE2U~ZnYA{((PFx!J+UYH)O7~8t?j%MHe4UEzVc$$YPh}P!ED38rd`^ z*5Q^NGbOF-UfbYLsZu*s>O*k5Lk7dey|%Qmp*eyu@PQ*QsOd1Ye;_4GDfMi#z zC#lO&(4quOdm!gyNNG{J)iUKL49Pea{Fw7%kwKG2=gNZSL7AHrTKY6q!AI%|-As9T z!TGU7?^>!Qc1Iq{u^icR5TbBRaqAh^d=oc)sgAi_01iT82_`&T$HjsIY5NKM>Ys05 zz^ea-93a5-!wp7(j{5Enu%>WF^7VgqjZPwSTU&HwjPi$%jc|H$*S?J3r~X{Px*%Ji zysNQa^Hw^REg=pV8K-$-PKvsL6Ek(Q`$72ym8z%4XL+&q6Y<~zWMU?M2%#83h6BvK zC@d~`mVvv~#!%uw{Nv|7kQ>eZdaSWU5D!2*nxNR=OLJNK!kFfWV7LlFn%1^14qqbi zSgnUzniFd$nGfhSc^8yKUq!pUvCDRPtcHHtPH)7UNO2;e;MV=n5AOXz=+?9zyfaXg z`oY8DVE)ll>b^FxmQ+4C*SEw>ZI*V`Q3{!1M`3+=|5ze@VZuR)=Vz~H2f~=3*!u?p z+W+u*=|c_IM+2eto(QiBd0YQ(bX@NtfNNt!4$S2F2zVQ7d-FxKHxjVXaPDC2L$FxN zJSoCuj2yLIi_5#D%b*2KUVRj3Zj^TLl35J3jWyc}rqu)(+P#i#$%@>zMQq zWC;>Ix}deMO+bC@(PvaVjb{tL09%a~RtS7#Q!~ z34N!q3#J`0gP(O-?Y_3#uV85F=VE}@ZiHXrekW4w0QcI1>x7YvgpdI4<}wH7N;9HrgdzNl!Bo5{$c*aaIE zg7K$0<1qcAv3pU=7btP#>hnDae&chnZ>0>8U_|wmE?@3aS`ld+h!zdInp`l-*k8EZ zpqLU25SHCSzPpD?n(4nRL8=X(>NV_D(!w{c*wb0!5enUh&u>*BZYa}o>pGW`YK@aT z)~4bC1m=xfXjF6fU<)NxG_iWd2EpI!=8uzZ??1<}UOZ|Ryq(jf_Vy9<$+^YNQZjpg zp`W659mQZrGrmk+ICek&)@lnODtGF8wWcynTt-S6vAx94?jxD zxE3Md)%q8L*J@lO_*|Bd`un1SyudxbC2)~*jvmjNfEsb24`1*myrFTEOYkKybcD@~ z+EWM{RwSQkJW%G~>}}9Hy*Mb_oq{5qogsK6kZgVx)WjT zQyNbEouV~RKWa0lO$VwKhJ)-JB(Y6VP5YLS;-N=tcQ>%{_5D60g7XPNOIC;wr5SsY zd$fueoSHwJ#}iW)6-sv>kFP+_;b+*UC)yclYG1DHys~H8#OW&YgCz`L8UBr&`TpW% zS6rj)NnR!f(Ge&l)YV+KB`5KR3P~U`Vq7ub)jpf;PnxBxp+1jIjm%W4HpMh&QwlId z=cWqd;1Cm`tgsM6qR_kCh>r-wgnm*WbV$lI{9teROdVSdo-KbRbnv>6J8Y@9F4P*-)TnzIGiv8)Fbb5>2GvaVYl>|l&J4gIM7 z4d2$a-HIQ-pkJ%1`ed`ia6BCXwqsK@%TQaPK3b$7BBthZs@86X|K`BUN<`1x%%-oH zs<*~Ejc~!F%f64$1&?yQG93t6B#owEZY2++Ml5qNclmDmBu*+FL&ci*b< zC1QUtia=_y7)^BXcW$Hpoy;oSx~2*z9hQA6>hE00JZD(@_a+o;v?Sk}m&d$uz@ z4`sJ6V+OmWaUM_c768?)qo%NmX0zD&^}nei)~$AX4f~k#b?jVi5?Bo{+CF?aJ{WT0 zJ@rZRvmr3sV=Y1*0=f8Gp%GWhfb960<6VH2>XtZnH9L8U{d-XJ9!_5NEn`AM9s5vA zH3O$fIYIjK(+lMV=0>N63f=&x@ph1D5H#IJ&EHL@5MC%HihVJaspP(x-$@9Z``!xz z`9(tw2@%F!`RZSPHpaV9xaTb?BWXJHhw7+7w}N&A^y82=N3~6BeB^DLDei1I)Qx$& zRQ=E-w3w|b9#4TQ%jr0-(INNPdT(El-p#icw~Z|f)2JH*aK5(2u#go$fInEFdKNjp z+dzM}JUpANR~L#4%3ds^oL94YodCI8;-vGlyqrm0+;j(H9k;xLrE)Rm1Vbv?StgTO z7gJK{YJYe}9Zp-9`ZU}Rfe6yHk%s)Jpb%4Fybxyjuf;jiK;40$CE*u}+1a%Mywf%f zTiAm~gyQI2iHu(-mOkxLX^;fnZ4rY_&Yo|Vs~#-DHejJaF9Pg7l?xfb*zgE@LLai& z3SO9s7v-&4>v+WcRTL`?=Xf$3g42htWfRPpU7Ue!rAHV1tA0~f7}dI~Vn*6b;Ip%x ztx1xzLQY77S%;Gk^3hu(_o9VX%%CONW6^_R?@zF-+I7;FZV9KA?3yfY4a5%~H_Y=! zz$2glfybQH8#y~Xp}dPbqt2Np+6CEWxXM2Iv-P*basd>A=#y5nfaE-dI`-|5VgTg* zs#~c33n?!~jU6yX3{o!5=K_dQ>YIWVIk%GkG`R?=6`kK22kRB~t>gk=ns|MUxNE)f zG5X3)TD_oRtF3TI*J>B8RCXM=RH@AiB$Ztq(;|fU%c~j0u&eOi6guwYl#C#)0CMZK zlB8=bYvg-m+@*g96p9`1A`igZGA^ny7oYXUbGC5+HWQz{ZUhkc{i@;SJ(_yfkoX?~ z?&GBzrzX3z&R=$<2nNhI+6Dh0)4o=iPZ@T=8MA{&-RA)1K$)`V6~$|g%TOEV6{WBv zD-OzzhjE5~`s@r8v&Mz(!DHF?fSWEnY9Ud(yRzm`dW~clEG&c~yGVMVO4$LypvHX(yWFB5LSm!iwqwEu@jbR$pa@WVHa(&rQizJ`Y6vhR2;DD=yDJ4F}Eh{5u zIXP_xe^T&?z61)Dw_}MST46l_-=V_t_32!QeiCrU+>{)C3pIyz2qERWQEe=r+%nZU2$oiGO23OV#B>;n>UY`_Y;A4$lztTzX0KjSKO*N?Tj!Np08A3sNmRN@91O;rvo$Fh;D z3X?4GhO8s`fKx?Ywk$-59Y_|VKZUOtRA+ii?*c-;-v^FG(!y4!XEO&528ZZm^A1&; z671Jafosd?E*y;QJt?yM=XRZrqowYbjnr)wpm30C0Lwy!3=Z6mv9ySFrzxgecKsg4 zR<yv?TX2`WZO)Ij$fYviI~A#o{5YfWw@1&PMkFjYMqJL{O})%;vZL23xOyX%0vFoU1r@&j4qR}6y5JRLu^r>6Q*WHnCfUM2}_vfiC9>znX(|$ z5JOn`e*xW!UIn~_hrfHYZ_cL1e=gm(qR|0xFU=3ORUcpsqx1ua*M!#SfQ;D-ZH>J? zOYc9AXT+yIgJ3({W3!GCNw-e%q~3|^RDy_-{I(Fr2;+QPHcHQeq!q!&#yN2dMpk=@ zub+NK#bqO(5__07y(~eqVqCpj*x}Gc!6jM^dKmruv$Zo3wGz=yHQ#=iD&WP8RW8X8t~T(BvGO_6HME_-OM zaj;xlPNM9MNuGBpTfn#gB%am;@5bj_6Nk^jRr)FI^-&jtrp zYbu+G$I^~GHr8DN2(KT+?wfeNs75GD6;kksLUV`*ppR*OOy9esMITPR9j*-_M;b&e zOCO>4a1o{o!zmvIRdZV}Cs!KBy}&V2YLR5f+v*vQ_KYgUlWQTQ#(mh392!d(6au%o|&(|QFRGa`Aibgvtel9ZN%%$v}9m5a_s)i8R>rACpUE zgl7`kRikZL!e+{a=i2RqW$m$-07^JCjbV#;QRzPKP1ljPZ>zK~sT4%H-BxY?wrV@0 zJF6h^uH}V(2+y`a`=p1TW8v~f4?WjH^#vY$-cr6^&Le0S0;C`$PO~fla?aPGl<-@r z*kKoh@z5>|BcNTB3XXPhDjwRy($UKvo{AFo2+2t^7=(wfza$AS_Q){O*`v0_GJ8xq z*xBP4M;Af9Fzc2P)x1!xEKBIn$Il%>=PdpA1WPgq6WCgcIY5jY^f@ zKtgvZf+vlHGJEp&P-ag#yj-gy!jJ|MdMb9UB=pqUzURKvCACnn2Jxw9*YZsaM!h`m;{!e? z0cm`|=OU^wf*dYkbGG+P0QkI8o|}Xl!sqi2^87MF^)ZC-vORuZAbn%Bb_6b8PDd^O zkyKeY2nKc24_rS;lz_C*#Zit@fl z*)v~I@C9XxQXPl|Kc_m```qM^a<%!{R8rOrswY`Exh#2-KHS8Bm3M7G$@#>rk(1!9@uDfL9|u5L}o{Xp8>*=(jSb5=B{FuODR(t*XJy*(JE<# zw4sy|^Ci%Ysv7pRkU0A%7{3UNJJhg|+|<`IiR9+BO6EF*Yf80(0u}Lgqe$w@Ae`0R z&E`^dNj}@K`Yty8S3IT7i?!2rZE&-hio{vefVIM%FUt9!{#e_`?5o>8=#GOXcFA~o zew$rXg6iu)wXOWjA5*JxD1E$l3BhJWBp{K(wpm*xFZ-5%Sx}ySnK(;)8>5@92fEa0 z9At36ff~V=c^$FHmq1U{wajk z%Ae?n?1)ev*q_p2b?k*{o`8i}mhOL7_tuYgaeb!-$Z)X;Q?YN77+SBuUK7R?h9+mn zdI+iJiN&-O>5drnqj)8fwU%Fn|C3k&J_IH&S@b>67gv0v8+m=?BlYls1)NZIjH%9F&kI?3=DlO zGkY`c6b@TC8Cs)KulUv+!g{A<`%?pi{o$)@jgRIaDwq4zaiI?^tpxZHMB-0%%ia#M zn~ngQ<^^fAUXu(0-NQDlppT<9BKMzDkBlZZ{po%j`7`byUhdp=4N;_;|ufg>59J-&TwAhlHUy;Wo zwB_!Y7dv;J4I$iPDd=?p?$%mS5Tn<7B+j@~zwPkOkzU*s^dem;{JBZxSHSdo;nMCAQ$1z`t)jz_rReFwo<|J{5VEZTtIqj=4}{D zG23WS4$^w&d-&l3p*hw1E4ul~`L($=`;h#&2k|@5V}#Vhx*Np?6@d zCF{=u@4{LO7NCXRomy`ecn=mFC1=)0C4UrRBFwTqBmK65HQu07n#m(DD<_78kQFs% zd7-u_I?o2~bCr3dk(kv&9tI4=&8?q?lBgRD&)4rqpu? zveJ0fyJd>`g{6E$?#^2*i|9oFa$6tsaoAH3dVK0ud@VtC2}1$%zVo;xm(9}@gMc$v zFUpWodqjKbKA-Ye^69HmpC-DIB zu`BFCLPavpn7@IT(fWdYff0wrbwm8XstC0JFLX*q-L(+~&?<78i5we7#@1hqQGtOt z$Yw#n!SOe}iCI^-oEmS2FBXlD-Hx`cIJcy-&f$nVUK+JyZyv`av{xf@2f z$87<;G;OwCUF7)?sU%qZw5S59LQuUtT|FdGspZxwk!6#Rgg^v;M1m6I>Y03T4zee? z#3K)Svw^!l7?)T{$L1nsqj=L|2)EI-)9&C9f?%&X~ zwQ&-Vb=-CowYW`muePo(prU0x+HB4S{2uWDx0luV@b(BkOrfwIg~@3<4Vj}Itc)IU zy2(nZ)j{yQwSqeoA3g`NH-zNE7w$gS(BMrUo6P2t3{E|We((Ve7HJUI^A((4>UCFxrpmu zEH|0djx^^4I5A7UY_cKXU5!I!k;qRStTOJo5HN_GPRgZ_Z%_if7n&K~Y9QmbZ$4i? zXumD!l~iB{39Wu0KM&rj&hKibaS_5|McAxqsO7QNFcFxIeHId5QBgSngqnIMhXX*$ zhiKmHlY`*C3cWXuaaa=zR&!o(&WH8^&bVNiRtL_yV3}qI&b(lmb_dSBV3~#oIhUd3 z!I;a?^kBRQ(ZaHmD@;gY*%=*7CYO6G*B4baA>w?*K^ez94i>SAgUm-=o8?+)&I z+tcLs!Qqz#Wz-yg%@lEW1V2EtI$cdLK%cj)Q-5` zgEiJD^{y~OFiEiCNhaM8G4m61oN_au*N%ytZ%42>`14M-7BGaHdbY)bc;7$<{- znt{#f*RZl(C{P>f*S6nrX@MLy^uom*7nJ5rb2N@_!Ej3T$=?EX4Iaq_e{g2djDO-H zH9&+>NFIRVU1tVI!cP;WSgq0xf))9&?60=Lah|&*(j(D)SMCE-D;JQ~J2b&w9^u^p z^*X)pfjDocLElGn(;&HicWAB|Gg;i$yg?q>1`FrQc$fbGW5FBN5-1pQ_#X!sDT7sD zz;-xfu>I9zHa6-+APh-52Kr;PO%Y4oW%S&kIAGzb5}G=-&Ny}{BO%!UgdfPN{q)Fa z;93a1M#@y0YAM+igOSl{u@)OGhTLq2(E>q&+aAi$f3YTR!E@>4>dw%yJ zUk)L#i`;bG81KLj?$Hu!X|;PG+nhMz=R9U5Wa9Dd5vtA@iYkeel8Qt{N07jw9Rkg5 z{_HT6-5&F5OiUo=5D)?&+GqN<`O6MOaH zm&Eu)=tutwYmN{ZsnU zm8XnGOhf$K&IW9u%Nq-wlTZ>cKCQ%nBGb-~^%$5k+8J=u7=mX@(tB9*L+wq&lsV*<|j%axA(q-BPa@d{`Rp)q@Y;R$m=19}%bxbr=)nq4>o= z!E2P%cX7;Il0f7EIo%$axHHLJA=i(YM|qGQk*^&ykCq4!@OY5$v5!v>rOkit%9748|>5zFa(y?5`h$(!aJi-0avYXmKf+lycbi`eG1OnjT z0*LK|k&#pAaG`n_*B=WLSZj!(EXa7;M?-VZ&R|ZsJz7MkB-9zx5{(Yu^+ud3b3SHw zoV7RCSPc&?o|6>`KBohDfM30|qbA41C{AwY7Lz0^I9?yHRPB;sXNSE|j5P(20lOum zsxd2ghnlC{i`CkZ<>^J9j+WCZ5nE(#9Ewlb64+$b$-}JrLfX zE{KEmiZgE+gAy+NH1<1?ggC865GZ^+Ra*#N!95{PW=gB<{UI&@ve#;XBNu0d+^UQ8 zHzr|OtlRGRju!?wkA@INuGK~C+2w2#ovTiI?(LR0IKcoLk84*)Zy?%>d2B=JK@#dl zkP1i4?S?gklaZE~d+BlJwh+elizn=exN#so?OKa?3}NdK5N(Gp=XU7wSh}Y7`xrs+ ze@na45V@`^{a$tTtLko9`@VYLE!*7OxS$gg33 z3}FZ+3?YOtgfJKrOb8|nVF+OuLNLad!2}b`U_vlq2!ka|R)z zhzmvxDWU@pV#LQnr-5nq(4R@NDm9Qkx^fwBtd*!GDZwEqI$*pN&op=oLB?`V1l^r$ zA`#F&U$({3O^oaLa*qJ@GU1_;aLQ1LE`ne7-V%Oya1nXME*q=Rmf3HYOyOYzaew&& z`caSi_u6BlSjEAwA?yW+J~9~IiG|EpIbBZdm?hgn`fiBj2`tp-S8%XEbP9+k1!d%0 z2N|V(_5%!lZ-hSSDoa=CX1_}C=AToMuvFJ7BTKu?o8GN}tlUl6mdFaE{_oZeb;t;2 zu`&qA=G`wKW8q;0d2P|oByt?Q=HWM}OFtNj(r9vnJyeHg+>5f^fv_BsSowt7(D|Vl zHGh@6QnrF@uWht|Af_rY9K+5!(9f(D;9EG}aaQ$xl}9#f0ne>3`)P$!0(dw40tDf; zF*VLH1;(B*`lL=!c1~$dSPGZwPX4i@#-diC;h zP`iG`H#Jt9Cs8l0)J#e#KewIKKD)tGgyKI+Y@y3O`gUuco`?At3EHzGXBhh zd?Ml%d#*X*)K&X%_q08OaYp!3^$SNso2w+8VjRFkbOA~rvdlSy9rdjZCT~*mis9wyMN~5}J&_vsDG>%d!5DgPf zrVKbCRNuOl=%8WF;j|jy^%y+dB7pM}YRCW`@N5YGGY{Pz44I-fhuy;M0gi`(QGYS12H^ND=A=;-#GX=N^ib{!8n?5giGK<0)&I;#NUJ~d26?l-GQC0$7 zAh^YHL31#3N6?Y%8#7va?hI|tX0)O$Ch%!UKyHroXg^YojqEI0 z+x0S}J&y@z2#O6^dOt;(NY;&6wi?RPr`ydvbx@L*s@&wj8=ZK7QB>vTgchmzHB<_s z=n7-{Hx-<1k6(C>o^xp6ZD+8}oTQhnPQ&R-oh`R4SO}4PP3&~Od9l@C}CLL#C!E>!oy1S7az7YVILg=+ z2jo@8UM$l3+I}V1wnJ}K<#osNDgAC$TrX#hp3TJDRe3`~!EEfxB8vmR^2y>x&C;_U zS;cs~!!=Sl{nbh6B65N8URB(Z(Vyij;F0%RQJpQmM4kk)7`9h-_^0`a879IDnM66u6!Z|0$Co$8?MoXZhMhxfs z)n+6U@{(*<#wL-lk}>dCBMp5k0aP7IC6Rd(q;V4FHhsolF(CQ*Fp*jxQ^~4Z2B`c( zDIOtM<h}Vvj=XLApK-=qfjQY32CIucMa0Rp;Jzc}M&aMMxtS z|AYQCa&=sN1~pYUa)$aaS)ta&pRmBGX^>;uumYxI0BKHJZA?m^NXXQ3^q$W~Cn7}_mgJuueHkUk_1kT zn6%@B56RWyb7Ny_?JySxsyLra=8 zlTcchi`QXi!6``@1)?843cG=GCF%h-&|c6dYRUHo%3E}=0Fp%^hYo%8`*v}wd}o|n zUtlTi2{H3wv$!Mgc@^)!$0~~6>#4IUT5Lqr1a$pQ1h(}N=c1j~-1dUvo6v*M%Lkm@ z*{c&b2P3@_uZh#A$IiS;P5DSVn_FcW;;I`=P8J~egqw;_MxJRMZF&vb9ekvsE5tm= z(pNje_(qk|84Ol=VXJIGts)QxH2oCZ*2znAg@UB5Sn@1ws9$CuPQ$eaJbi@U=QKh< zV2n+fb1;2r46U{ZtAn_D!o+?0v`^(mt0vrZF{SuHgIJ@1Q3SE_c?oHBR%KvP&16yL ztwF71Ew$p~QpIm!B_z9tc*R?#JF@_`KuK`S@h!zsm~?0lDMMD%WvMM%^~hylKG%RB z=;>5XsD)okWcfr#76}mNbaVp(=SlU9hR3O3copr&Ln7?^^q^Fj+~ABL`Kr{xq?I|# zwo{*!p)Mf7Fh>zRB#l4WotPd65stoOgnq7mv5x+|z{yM!GUOk=9zYrVA`Fo=44F3= z;BMnHi-0H1(t9)9+tlmVve{S`w!k7axzIsoAaj6rlABv=i9}q@OwfzhzG7Pd&u_iIl(*|g>g8g_6S8qTpJnhpTT2+Wd6%8y#)s}pxLa}8?2 z7{)!mUVg7din@ePBzg9201s$3wR%^@#+O$Y00+Pln&kR%3v6C7BD#OaPq2Y;0#xz8 z^cz2I6)Z@ziW*fj>8(zQ@4F2SDoF+z~;IZ=9VttRiz$7qwVeUYe3g=u=uSm!WlpX|kuO zzKv~7OzvEINl5qwLK4;`Xk={y5{(s^M2LCfzD2{F1i}yXwKCl0cd%WFE&`&P#f6j~ zT-SwoPkC2x`q!Z^{s+fbCpoR^5m}&p_@r?;t*&Q z!qZu%>Np(bREa#`X6)3Ci(_h*tX5xGtpb=t_x+F@;Z5cZ8`LUgun@;U?u|~)sH{eB zyP%o|n%EV?7R0TIDqcjPkO0+04`FtNr4`u9G)dmSY2yp7VXW#^q*4p5wV_NFwD-#^8W!%`AjCUFc!{%KG13T2}_KU z5eQ7jzh(?eNNR+=vS^Cv+xr3fb}b^NtI*)k8zo3eywc0fin6-GIV7*fo3xL|*O)`x zTbMR(BQi<5R)u>Rnb*iDVVu{ztGIrpZN5qw3yX_^iTee5!Nc=ciFk<_d?3(w%KfBkJVxQYWOv7&1ay#KLjk9a&@{?6%r;eODa;9W z=m-LqlRzXhpqwsEmO{^NSxt1i#Y@?wPj%+QmG1NG0z^b4I}+y!r9JXJ}ML%A*z-&~XJBiH8)IvY|}oQ*jL&ZZucoX!1Mnzr;|Y1-OL#j`C?sBqKgGbHZp zn%VwM7YX0BiP2`V`Y-aOv?ChpP{Xd2C%1i$> z21+)|!!l%)7V(`R5}nn>zSILFx57NDiBg$T3o#`6m!%Rb6D8QMiNZGpHr55nT7@Al zY#$1Edau=AAmZy_OaW!H7>N-s5_mzoB@a>vqI6Vud#c?mnfapRH~F0Y8@Q z`Re~W`3TU*i(eo0-nNeJ-#k67EuRrN_8aw&w&%-q3G>yik zUS{4#N)#GG4TPuyO)dod7A5Wf1Yg1w6Uj`(lkec&YX@r)g4#aR5x@oyg5W%h0D!Gp zcKrwL+`hv{01uaWlyB+~Nf^ADyrO&wu^SLic+TG>_b(yYDr(|fF z$Uww-#NSCX2BB~7(vjSg9jjLI^$Gh~nb4j!!bV_V5i(xLC7ZtJ3_@#W$I!T8s7PG~ zpGBxpj0~(5Qs@K;$2953_Sh7l;^?}uV;!(Zzz0@4acI#PFNc%T(+5X{hAh5ton9v& zfCFceBBxHc7pFNkB-gyZadxf?|77kP4QS`MJt2iWVk*ieWga_iy*MhsjwUkKZk zZK8LzptrT|_{v=m>d7zJ_pi144LQ!F8Z~trf_ANZ&Q!*UT6~5&MOtLc-^PjBSA5}i z0sGqeSH6xqMQSx1W0X$~-q{lj*HJ)WurQ~anX_}U<5b^Y5c3$5x*cz@rT2)K*n*f> z842lBEmxu!TqewRj&j68&8K>gDLSqWPSInvnxd;}Yl@iv_7t5{hom$LX zZQbM?yxjQf8fagP&u=~RY?xx|M$8D|piDjDHVc8umSz6kiWBBChr=|PDYlO@ZO6E% z0>u!&3+}$lQpRrl8taJ2!PTA_^~T!^)eZjbqb%7k*rx*&GcseZU7S(bcpYnXJ;ox- zx#3y-xp91wPv6uT9c49dHize11bubug3vUYgu2}b2=6fL)}7en;@@41of&33cx06I zyc-H9{JUpS8t7hr%poS+$B(<84$b)g!00${JUBYeo_PoxYF6T5slp@A$-hVEyJJiG z!H+G@EiZ`Y^5fkZe$NxOm40#>;(*`vloO0RJ-Q5c5*F48R3oFTzj-9$EVJaDH;&)h zhLGgXLkx(w^dx*=w5-DkGvkyjSdk$H&=w9#Ze>hw;w~egwZmtsZq}>?fCCKma|Y;8VR{OYyCS zPYx$92}I33d%(;AP0k~$$)NvEo~b;IivpXs6^23EJ6zv6D}C!0vb8c=wCnyNE;WD2`5V{xV;v{}Bsyddq20T_YwsNe$|>N<|V0*#Ty#AuB+8nna+* zfp6ikEWMzsRTgnJO*!5#TrGp$9PW$>1Gzm`W{)+5v8E>?Zba`2+49m{@Ep`wIZSc) zLW>Whv>or-2u87c~&i~7!b(qvJsw$anBmEvDM5G!cYq@nLH?{AhJ2}|G%cS z;CY{|_$FF0kTkJQ5%4n*GHdrGR(_~4LDPX1T)Bt9H6%*00EZk1;PEEOP>yxxxm6jQ zn-xD%?f8mo`(j5pms=g)Z!hX-RyByh(#eDeS!VPS_$mBF>Si%4maD!cnchgM^$Uz&KjbW)IZhO%F}D;=(}Gjhs1{uKucUKTdEm!K`mw|k z9Q|t*7WF!dG5oK3G8(jhZ|#-)C92I{r^yX*{DC{l9*xA98cH!O zNBFZbl^r51)%+D2Jy;3df+tow{~D};BwP7L@hM&n2sP|7#rt^b43C?|2W^F=Li>q9 zE2F6R5F^MuBXIl$BK>ml+cx8uF)0RR{aqU>D+SPh+%DQ6kic>XP-hzirL4#&?Q%FY zX*dJ}FxyYpmasvrL240{X}z~yvzDsaDqv0Vsj&j;WDbr)Y-C^v7Y$3fGzIfF)>udv z1P?gcmB2C$2`JN(46T=t)$2}QwmIO~knVicg7v{>o38Z?O&1`1s)Q%fRh@4bNRX zg`CjLUb1dlS|PqxBHHkleRodGtfhu=-ZO#*WCR6k*H;`v{R;=1n~#Ez@V!~TC-Z@g zF>o5T{?MUNZN=VCJGaU@m)yz8^&x&Wd;#KD52*)1tdDy=(}wGg(=V_ob;`TjWk&Ba zqaq*{(#nQ&><$fJ&nQD!U@7oU3EN`cDg83}OutAEg1HMaL}j*TG|^ccc93%RS~Fst z%|6zQe2^*NzXwDN7A+u3)ip-Q_3Ly4wM0KGh%YkyWelAy1vJS+kgq-sdb~+^8##87 zJT>H?LX(;PfF@PG0w6sws?i;R`rq90%q>N}(ajrEVO1n5Ikq}fjWZMe|ym2XWm z>&I(e|7}epPf8k)^7e$**Eu1j&U$aP_yUIudC(I)ckww7AyW2hSb!yu?6G~+79^~K zlM?ZBf+LSvPzr|g-<-AflAsB48do~vAKPgmScxtY477=CpP_o#8B5Jx zCykXB|9A9PrlW~)H~LACzcG0}<%o^oD9LB%@kqHNTBn-on#zxGh1jtqLWFV#Q1WBr z(03{QXOb8*4;t3neJ+g*PmU-vP=Fbvo6sjxc`qf2h$#k?ZoF>jaVbf0pRee;Y}7F_ z~;6 z<_Rn)Ah05u!hpd+>e+gM9M0sbT6LTq1S{ViSHJ4|^NA5V#VNz7ODS6&&j(&geb$DG zYR+XGX5-JtT^w&>vq>K%JxpSJMyp|$0#q@VdmCVHU=Ew_Eqk|_l|s0A*8IhKwV zu_M1fL>MPeGD~(?)&>IM>Y|cY6-$_7vb z4~6s&f=9)74yWm%f+Nsyz^777}sPU8=n>rh?t(djcBXn&NK{ru*b_$ zbyK{xFh{n4_#Wcx^_H15;;k+57T70~;YWBlqQOW?JvOkvpfoSfGNZ& zm<7=(5_foFc-u}-kv`eQ1_2#dxV`)E+#I9PL0<^Xu&9kR=N`Y&S-|#NlJ;5>LVan% zK2Mk%jUTVK_S>z+BQM|_hwroW17mzcWWz-gwDCwvzU(^aiZBd7g{{aoBwf$8S38is zg>wx$B$j!jB|$CC2D`~F0To~9SlZ3A;yBTPOL6SkTS8*m3%BYJhLK`AjKtesoFn~P zN*4hOkM~S@38yr_EcwjQv9(s`GIVB^o*Vd?KZ~6bdvm#CuOpB8jEsp;>E7&*?@vEU#C$4Zc)q5O(O6T_e!vai}y;nhKq-IHE{8e=!P5u zqLI?oK%(~M7(HI$D1zlQoSb8b-92vc%AKO)-EV&1!j>6Jo{^ zV;;spbPdb?Kln`m04TLD{a`A$b#DURQAB4BO%M=^)uaQVH^7QZa?;qvllf#^n{7;L zhPNNqB{)dog@u=ZT0+!La~IUJJ!!;*h224gSB1U_d4+7TM?i4x5|iXwr>Xw_|G)kI zZByG1JP-E_D8&xopi;zOfLmUMS8v{i+W!^H9ZRub?=Ig zv|f`iz}}8CORq%~?WO7Q!V8qejF$qWBD^o38r~HjX}le07G9bjFJdV`Dk65pN19lg z9xuBqKGJqqe58fFW+q4i9%DXgBM<~k=zJ7P2M73&`KS$018MH7<*)!5osaquz}0x= zqf>vdNCAD+hQpN{&V2O6X@!O}-wjnj0Qx9Y8I==oKKkV603XgrqiTJosPW>XIQSG$ zK@C1?+cUnP!bfj5h>8T&5aos%d~`|A6I04ZYt(1}02iQ-@~GvhdSJ{)Ti{s|Way)| z8I;f%-WdQ5iI3W37@$Vxqf}%@0s(z=DuyBKS-s@D4f$4xqeN5~J*q_&#a z4y`jOt?hDC>1vXHq7e;$Vd~X(nCTAO#|0CWQiH6Uob@OJ67W@Df8pnlqCo*_ghw zg;DcGX3`cKoVi_@)aF%IabN8GlfJ+J49n1@xGfDL z@G>cmFI}dWNuRucxm}qQ*QE;)qe+{zxSZIeKe8;~LCT~!E}$R=DJ~HNfNViX#+ueq?vRE13#A1%q%!MW99-ko{Z)d4L~?E z05a;}Oe{Bilgdy)su4C5fEt4{Db$rEDuyPdR+6wrlO{!w_Ana)x1%#@kSbIJIZJw@ z;>4Ag6zTvb>b#^+1{~4BS<<8_L=bS6G)jt4>?O4oFvta6Nox=gs1L5BQ=?BBcz`8+in78EXGw8DX+y-Yq&S|8fWwv4=EPSumJ|m7cvy@j zjcW42l&+*Wsygt}mGp^;*9d1xYhX|ZmvpL&2DqeANh&DeEGZNcEl`{#eHy@lJ!eUy zjx6v4F6oq!Dp3qeT8r37x{^{NO?1GO^hNGS1jCXp4aw~omO^k)087dgWX6(VNo`AB z-`Rk;ph8#DA3vZR(3O4lqQQlMlccv93%D6c3MIpe z10zYFVmN7YlGeB#5rj!Ph2zmk(x#~uYCSMXZ7+suaF6zo^0Nl_sO^OraF0g$YQQ~e z)AB=Q>OG3%sfm*Jrfzueh#Tk8r70)hrJ&|K%EN;~=h3Lz*~4r$2}(GR?ofJS=sX%_ z!`Q=Ym9lUi#o>^{#c0%#vy0Ir0|b3XZ3YO^QLF8c^qQdh`*`*Dab;z3@RhX%$Mpp# zCaVh}v1((GEt_#-}FJB#m$=Vhcnk6^4KwHO_x0=qGC!G>ELz#trF4ON&Iagb7*ZC&3IFL{iYk4T(id zi?mCakVt+KH)s&~qKzApYH5*;w6uDH{$8CnZhB~zFzJCAG^mF~ORInXpE&pPUq1mn+Uh_Xx(uhr8y*#okyFJ1Tef<6RL|*s0x7J(WMH2 zH)=VOjziE-3i?SaCJT1~R#q4sSXT!)E-Wd-88m9O*UoddOOixM85%TdlzH)Ds*D)b zOZd%$*|G&}i;Nkx)~Hq^z~A%wc}~BNgRU^RB)BkHVy=`7C6sW)h*2Xm5d1twe@O(9 zDJ3EY&Xi|ig~}c5OqG8UIZ*aVdQ;{(GAv=x;-j3?^HKuIU?|%N0Y$lHsY#he37N7- z!C9K|)sh1|RZ6cS(v(|HKa^aW#3(1g(o)_ua7rwCk|?PlA*3WA3QP&*30b-1f+@-( zV^GQ;e9V+N8vQ9}Ofi)&!~{wwFJooW8bf6jW|WXj80te3L1YSf7FLM-lgI%w&ygWH zr{{%iBLoy=8YN^%GrmfC6_JMI(j*3X)4(CAAR&Z=@`Q{mG6sdr(dZBPLQH^6T4RW0 z!cd>ySy-W7o+HCv8zG?Rneo*gmnJcKRFDwTD>4S9_d-md$AqCiygWySJTtz^qk@DG z-U~4SUY;XEF)Bz15zBLAxXW{781^a$7~un#)P@0uewK8`6dveUQYz(7(pgekKpECq zQYr%SD8VItF_KwN1Xh|l13RBV9Qw2TM-~VToOS7m-H!D!*-SwY6uk;uB0)9Jne8vYgMA}Y(Z*3 zoF%0J_@&^=aPEr{zjQP&yBheL1(iuH;z=BCidEi+9la%hLV;e~t0|zu=BxzKO z9KN&Z5Mj?r+LVLGb&@m&4lyQ-Bwf0Jwdf>mXkl}b^r=P_C?iRkXjnOMlE%<+_ML4; z4vLeM%HXj(8AD1CCMndd0K@m_5|tCT!R9*~3?4#wk3voPQF@O)y?Eirdo+q_>|r)3 zH;dk*GF+r-;5|z9#mmENQDA;>kH%=xr3&}x6A;~ZHmCx=;2ym_CGp`NjdJQy!#zsX zRDh`SXytPCFdLH#=TR6wH?`R+=Q?uPO2K{KaZMAo+P1P;!O~ zsaox|AB38tS`BwKSXp7Pa9mwqzOclE$=ZOcOG=Up2_%>x00Fo%BlYjb|Ghzj2Jzao zal_Nn(&8;y!i4vq*QSjdu_a5Gh_z|sX4j^To3KCKN%SyVQ=kOSqb`^@D0Cims{KuA zer#K^AnH6Cg|8lFvkg$sqb-!KU~)0KR3~t9G3u0r&Bf@G3TbPcd`F?~k3}lJqfa6g z-#OKslHT;c*IS|hZVZ+JU%0*~&>{+LiP{h2zpMFh0thI;gB?3~c<|!I)bQU?T`BPU zhQHX=-|KP#))pL?tP8pns4ebwK#G(p=f7iO`gwA+w8X3i4HA>1rL~izr4@D?8afZN zRXJlWMw?=gdYCN<3Z9EmCNx(Mv(XsN@92^nuZP)cfTSKqZMDA%kkrF$SJM5vs#H@- zdbM4tgEnqfu%!$&G^E+D-qVZz1KEdDFC=`4# znzW==z`vuk_BR#qu~w@|PqJMJYS19DrP0aHqa%`E%*oFK^Xk%arlwl$wHp5M_+zr4 zH{<7x`FU9xV1+qAsn+vQswJH=)oQPap-0AKebL&WTGAUPn0RDn2mE(QWsROtOG;B~ zJiKOBdo50_hHD_4|L)Z3uYu51W4aBf^a=#(&IdlEphT8u1*r?VAh4sT}*4QOo^du)u(I z$;$FDSzlZT4Ec`Z#$W9AuQ6E-D7C2HVOy$cph1{25k#P+3<3yPDFgg>RO8RXAPiOp zO14__04u9XQZ4DU)@pxkh?E-u5d3AkZASP=H&Is-c%Zn5om{*q;rCPs(KIt^6R{N7e zsJZwL0u#L)W|Y84g3IcHb)`h>cl=4G^tDqu#C06Ue-LUM1gQTYFx71T;>yYZj7!%P zrCL4xYf0*pPHVNnT*V>UCcMq$-WGyuGl1VihR{x+afnO=vuHcqrqwgtt;Y!@tS#HCn|-2xifZ@V1!lRV>5ruJPg#x!ez&?WW+gWy;cYm=+fU&4 z6WHtTC(d^QzuS<^a_u7L+tKh1g_iq>NLN%Mms=6;Rm1_m3+XPRAiVttcszflEa2B( zw!jc7+PTQ)-fnWQ_`%=y-YVL^ul!=|ZwG?eYR$df<=z@a(BB{YeEi(ozI#vj-Ae16 zziUG-m|KW@wd(VCqv1uXU~XN#1ymbP^e-NyxEHrVio0uZ*Wwg{ThRn7QYcoSxKp6G z7AaCFgy3GRKq*cjxU>y{QV8CEzVDs?dGGx;=kA`_*_pd@vvcR(&)wPKi=wmaT;@t` z2GaDqG9pw?+6BjSnZ6xwf#?oz(@i@B%l6h1qM-WPn+9gKg@1RYXouF#j_iCC5UEj6 z!~XU7cqLThD{#!Ib#4E*MjM|W2-YpoI>H*}T~`RV4DoWt3I`3Mc#HhkZNQioW{2@- zl}cITT-Uvk(~waT1Z|<%BMo?ylKTR-EsX&X8;*Q0CfLTWq0vGIFx{08z91U8xfU^l zPU|PU2h$NAj0FQqiA(P`beWRc%4NC}w+m-cONQ9XNr4cyIseCU*#H0?1HT9eQBITMS~pby9>@ai!UG`Z*S6o$XksWx zqx9&uH~`;|8q^7G>x3p*+;upubM!QF<~PI*p=4p+&SWLr0Hj)1=|D1O!I{H7|LQjf%T&`liwM;d znhD$hnnnsxr{x`c{^%L1dcD!&5TQA?`|sO;1)fR>KT`Z*!!6u^aQ3WDTdV3E{X!%; z;8b28%{MuSA|6ZjJ6(@DcOs6&pV;TCKb3x5dqHbDfFRkXA&yQd9l?_QeSn;(`8FG+b?M zauNG`?`N4hg_tnYXh<9Y;n9y|zp%i@ESPME0!Hxf{(eJ%Ff`u~inSM{K4m%j2;@Ea zoqR6&dIWjJg#{n5!WK?39P3B0vCCIhsK!(Um1TTpQk{>YQCroO(!XU22{Q2u+7WLR z4ac4si*k7>r+HMVGYzja#<9&0Ooe}BjHZ(;Ud&35y~>$9^T zt^A(7cg3UjrmrPN4)#!3XL>A=I>R{iRAbbtlRTuKbZfL;OjZejCi(qC)N9VlfeaH4 z8hjtx;(oKoiMx<_on%N|QDus3Wea+<#kycTKZc?Aq~k2OzS?9hd&)5de-cKLR5j!> zMv{7{T!>|NWYjDMJJk+8P-V(R{KrEZQ~x-o?geL#x8}|7sAv9qpK|_eZ;8&ZBLGPq z`9*QQW3`~J-^^tLy*3v)`5SnrfGP1h#ieCubxUuA*l`N)*(V;e9zOcu5dHL)Q%q^9 z;UrbgvA+b#^q7Jy7;3Q;{9Ha_Wx}KjwzD&-Bc{fRGh`~#rxF_vY>Qcl}b zrKed!|5it?a-2$A6R1k_*k zIM1YKRv9V6^NU^5CA~GX3?)SsrpIu;E$T2jzhhwG<{b^itgUS@uY$FtBli>^SI}^t zo0*uzFH3VnWZ*Z)z2k1;V|^1=$t0KJDk(;}jv|gm&&^7uIsJBuk+47yiqn5pj@=f3IWsk>& zS3Wl;64+)f{_a%tHcWzuZ`?0^EvW0=YDoq4HtYTN-=oRUz(hh^#OepLTDkNY2jJlQ zR$pJ%xK^aQAFC}8Yhu`2SFILCzkjB>FzQa-l0=PdlVTy(jB?LnhL@Z>=A6iSNpk7f zSL_PEx^u;I?E2E#7km6?a*LP@N65;qYAY>aVhHd{Z#s{}*NWc3HEMTgr-DCA{pW84 z>ASc<7yGqNN)V7KEA!q-jc)4*PZ_9LeH(1p{~UcDsD%1O>`i=v-6VjQLXz>f#-h@V zUexpNAcY#m*`UxdntgDP{yY|UBDS*H^|c5tr5o-tR@+3G3fE!|FBcVa3h%bNM=qre zzHU9QsjiH*H3#PWf7Gj|kr`F>$YMvq6Q`bdCTt--_sM1@l#RAWtK5wZ<1Li0`m#2D z<^Yh*RGLb8%co26Dhw8DmFM#Wh<0&kR)*`t=`Jcsb#Mn3 zTtlm>XiYPr#waNF6g!|aGIv!*?HS~IGE>+$F9yu9VhL65S6XfM!CvIa4 z9V5R|)7#v*w+6gluxJ~|x(rdB(V4Gi(GZB%vBr5lvg#vm+B z-*nYuqnQ5%pfsPmr>dKUzXTDP`m@qFwXvm8kux5CXO^TQq5GAELCjL+RQoEWxzaFB zhCVazVg?5b;iE{MLHm=n+KhO4Y>Yv8($GMR-3erIi5pyFOUBrRI5M~U)l+!_O8(06 zxRlr&;8FUJFKrB;sD;*%470L25|4KyY}sb)Ps2iVqA=BsSIf$a z5lI@p#x&;QMj1ATV`sduTb}td6mxGMl}!umviMwjL94ZNag5wOibg`GA?{TnL=w5% z83u)^URpSs-Q+aSz6UjW%756a1?qH+J ze(21K)qZI`5#ueuD@hr(Gi7D+#GH*&R*z9*)R@sm$C=qkonRo#1Sht0AWPFgHYXZ` zXPZQ9saVc`#Ga1ttdb!hSm>;J?z~#iRHYz5DjdbxF3FIeClP;zot-B!Ss%w|V?v}A zje%dlU}v35OM5wmYGVK{G(64tf|&(S+kW=pxlCUcamygm5`ST zvSGkVYZ)Q?FMYlf7|1wRC6C_5B40q*UPhGQXeIh(X7gnn9wH!PYgp9~cuvGC$wlK* zlb@NFm+4Y~s<-HCoAa0utCm$i)AR7nhz#581t z@=(r7m;tDNyeo3M^v!lA!*;|Dc*j{gt!y&c55~m1A9k|Tf?eq z>{h}hn~#Z(>Wgu6)Ub%APH#~_^f8grd_NZlF;K1F2jxvXH=>NBe4b~%u@iXsQZ2+htd5H)3Y&F7@!0Z&Yn27-6Q4*%?xc@?{ZUwj<(`4>jj{ap&Gq3ke zojoZ=soT7;`SnHQG73Q2U#5k?;Cw3`rF@W>hygK0yUrj_TpcI91bf+h0VWnJo=g=t z-S8-+20Oeg9}^obFkcuKB`tHpHl2LW8?dL6v1;x%&BG@mS}IarU@!#u#+l44lFbZo zWlh{pw~8I34@jV=dOTjKSwoJ$nJ+&hP?N@-9qMdQpo5bvY^s@8r1MHj;$vC9Lzgc* zR+|R3WU)-`ASRSNW!AMbj0qnqhBOyCROhskgEMh6{hW> zi4k}6U-pl=Z6`tbS_q@+bF z>7YZJ*gRDPWn5U;Z@WwbkzhuCSQ6KrEtzl=3_3a>L@;Q>TL6Js>SOiOtxTGYB|qDO zhIBhwBk7QPAD0MxKHC3251fzKLD~NkIcYXlQHy8RF7A7s9_3-*8?>hOmue91+`n#d zw8zcYybY!eZIFRv!@)|7 zeQWjSy#&F;uKd#EUB6Z*bFsgI8qVLi@q>5<;SO{D?FOgalc5cJo0n(Z9penQIKh;O zh@J5OB&p>nbs!23v5(r|`m;_y0L?-)c8tya9O(UA<~sHCm(&u{;@8{X-v&d7N0Q(! zC3-_B3(vT%O}-SUsxNIq1zd8=^4IRqdwj$RATW%g@0;j0V^9u_YSiynZ; z9(v(KM-SM4^d1ZL4ac8F7lnn*S~T@yN8mFecyP}dx55&(y>0_h98nPQ-9o|*P{Vv* zOsRSZ)BJTu=G3>5tXg#uG{x>Hi$5%SJfJ}aCjuYaPG2RYaJImkJD)%9JlX4kRwZOq z8V2vyh9-$@NiT<MDkElKi2YC9b2LG{q_v?sOZ=hEi6H2KvMdtUc6 zQi@`ii6p}3=@^tYoc}Gt_0$7r7LEVqy?Wn;MABDduJif2t zwz~8hdiiwYmI!{Eqz$dC?UFkFjpWH|umy(k-ynai6ymd&h8=%`Ez1?`j}2QkRE0qp z5Ra6nYzMBvJHhhr?3og>r6>-W#>w@=$RfyL`&eHT!B7Zp0Qqr!SYu|RjXY6bBhf)x zO!v=C)_Lg|7>EF}xN7`T9$7&;Xw#Ux@R)<{TbPfzh^1%t7SrnFPCkN6WvU_+(ykKW z7l*j%wEZ6Mp!wI*GZY|fA?iZ#YWwP=zOVQoO0!csJIw3jG5FDGFGs#$YCzaT@2d$2 z==Ve;CYe&dt~+Kak8@?{zj@$0S?tn-^y^OL$mh=@`?Jn62*-hY`E*W@0`5E^&Py67 zDXcD*sI$eT1}M%UQ}Qz?KRv=Ye}86jf6aYZ3OMgW8VjYl2M72 z6*fL398iN9FyyXtiLlHo^p2M3bTMg-?NE?L^)ycdF$Vi7RE$(ZW4VU?|nrlercin zZh{t2;RGT%5WA=6-%5(@KLATZ*d?XB4XDQK_1VfFZluc&vEL7y|-#&%eR9yBJ3>n5oTXl<)NR zV|P9`=EFzx1_2O3(RRka(uMX5H(>@s$)OJx=fA{vsP^E0fQ)E0Xx42 zb)|jr2c*V5^C^@JL0%YkixsXUjT^v!YN@0GC#OaSE!tlqkZf1&DJ8Bng29g?tV~3b zpW_BxWRD6{-PD!xMfi~mw#5k7Tzlz6Y!bUQ*#rUlJS80t7q!K^B>L5%fkE5I&(-IYDR!H1G7pOWCf zlfM^QSlhM3woPL@u#+EFNT$drv6G)Hbhkr!QRl`pOuvm6_I537+7;G zG|A_o<4Vil3Vtq4)}IvrkQQBj8pfN){{R?T*Sk_ELD9l&UTy{B9(KPwmC8G7#1&kA z@b6f@ir)&p)eB)tIki}0-bx?bz_JEQI6yn_4+~?rZ{H$(q%Hj4vi0<;R)OTb!nb161Y&XeWt z6o#+(`cBbB=Rm^9euieQJbs$6=3aF7n;eV<0tJ3#@Z~P$3tCJR@@b%!+h6G_& zUr6bdQ$au0J(wo2B*{BO#$#EeZw@I%6G`XC1*Ps3_W34&u6PEu z=7g`~ZwWqL@nM;9`K5k&u$$BHMlS4YRCkNKif2YsT@eh?l&Dkai93ep_jlr5Qc{!>S-S}5j3R( zbk?S+Q5!vj)gRfC1C~LIop4BOw4S5EFgY)Y+aLA{zU~!|;{0L* zB0T{u2SI#!#`R;Z8qX1&q1|Q7EqqL!4bf8(XPS#53d{70=xPNy`64LOwKbhVx zkmw+SdfI&9ULndp@bUEJpa9v8PcKyJzA-f@wLf(LRo$O>rJ)UjtK6AO#3GcolEY%5 z`1dzaV&J=4oq{l1wHAxQWHXBxm%5> zV$|H}QiosPn=NQI0!a^pQ(=&GmYUR|Yc^{zUF3RHH(8f-`af+EY055XtVpEhKrn8k z8e^a2mChHNV?yp?gpYP6_y@&wI}<4)@_}neK)O7Pi2(Dpns6WGE-1wSloh^CumMUt zT@u3T&pzTC`{9O_tabNAUdD%VY#C-d71%d5*J^v?Ray+yxq35#TKiW_dwfYffSK`r zrOC9(<+>q+9o${Ti6%FMt<|(a0Z~vRJot?1tNlB$!nQzL?(NUuhQ?j_;6B2PDckS< z;(pyuT4PM{5kx*mKKou-&{G z#Eo5gS#)mubx9 z&`djd6@ir;a(XvmuP4`Ccy8Pg1G7&EY{)I^zaV+362M+7L9v8|#tvV491&M*I-tI0vx2VQAcXW8k;Y z&$0byA!l)6O#DiUNG&DWL3l>Qvd}1uboAP@x@Xfg1u9Xt$7&A!+zDNC_Dnw#NEe4S z61d4%YMuym__qsHeKI&+cLZMk03sSw2ViC?$vCKz-NK0j%EQ%Ed`#z%ML`~HTI z3K?czShH-_$CutPJXDmIuEFTZ7NXatlX5;MG{VXM;*gFuHu|>qx9Wgf%^B;=85`%_ zv*9k^2^>M&3r28oHS#E&62w5cLW3k(pVr0ysm#K2{JoGY;+;rIM`&L=hiBLIPn-yG z;At><_!>u1yLFMNf-_2G>e~TI?3r4r8|;xrS35B&;%y*GBNL2vcwd-k17`Dwu~yhV ziXb0?a6M=oG9_SC+}!HnwMAgJ!Z7lZ;;MaDqOcLJvnBl@?DlPcfx~2ZgFmobE*>S( zKZ+FudfVvjRn;q%dlodd$n+Jt#WN}$&`8)7>S-D9E4xB+2=kOsnoHT*5a`2N8LKhfDuTwNz8E#>rviU>3f zfo!tr1N4uU?WZY+$fuqL0b-%7bJWR3gbREmN`2s>sHbN4V8Pilua4Fa;$3Lq1{Ygi z@4?oxXUtyNzG8>N2}3OO1WWTQ075?L%Ktfx>HC%Eqqu~Qo*YH*2ybf;vyQ?=iS{TLpIhW~s2&G^?Q^Gn8DIo;Y;k?SWHCk=J%+syo zLx`|g@n?svM`6_h-un?d`aaygDEX^`;<46gTL;*Dlkdp14Upmo9-a=08?VFOla z_ZvU~-5<-nRi2VM-ZUH%SpNt1(e8rMM3S@3`|!jr+Z{`N$hGmAUkt15Zih|0A^5$< z1uw+E^DK@>F`Z(9cnUau%r|=q!@Oy!ECi*OB!{D-$};0u5>;_y(8nOytCh(&7`u~c z3`KE#S$x;M_-HL)3|eyTgJTUA`vR9g+En3(bx>S^9j~OEt_-n$OI4lw3?Y}e0NIPH z&kIQ-3g@^0L(PlS#j^#ncHgd&SPfWZNi;2j6>H$6Ts$(UNS)}ffDom^!C41s*Izd%XC zyVZXJtHK&7Tlh~V_C2GwgtDR_0r{?Hcx}C;!Ur+1B*pv)c~?&84~T2oe#Dt&m~;5< zmEg(CFhCb_@3)U@4`~Gt8W*%m#DH<^Ng6Y@F^}OITPV2Hw|ntn?!tG{_9Z`vB?_Fq z4_jQL5Bm|-omFmqgrGz0g^=Z% zNfAV17nIcYBL?<16zrpQrGRs)&;z|Knn-mI|A%?Af-n`rVz`zt1ATl|jBEX|c;eeQ z?{deGi6D&B+}5l6HOhh0ay@oLRj1bx(CzCFGm%hQ?!6?O^Wk@gS=Tg3XP$TPO_E-8 zUz*Ud+_zWK+f8pnIHOq9t9x-GShr~w?^w8JY4$PpgH&g7;)7uTS1{=TytjQ~OZw3l z;Q?C}p(RG|KoehVxs_(2C=^ zM%G;zfBq=diI0FgEJ^GRx!0r3lVA81m1Kb%Fg(!wW*5aRpn96DaOafoK)c?PWF6s` zE<@tRFFpv@cZX>@1}Mg!AbI;6Xr^;Vnpes0=Ry_P;p=Hp&Bj#sws9vGK+Kp^B_#H! zE$(K6&*BtBzdI^5=r?66xb*8r9?TP{h7{smo>?{oUPSx>@#27; zuaPMTq~M)rKnlvg2O0go`z9L8h_o32QRgJaC9CiGFP{MmrVL#8Es4goJ_kVZURot;c4s??1ZS`N@H*T3 zxcZIvnwxE7K&?0JxuD4qQKD@m7j*qAgbikMt3+E#VfUS2yU~f_MGt$PhCEE5-WPIz zqp>AS2D8L{uuz8cuVEC*;(##TP%ssV8|K>HQ;W&N-x%;y*@2lQ|DOx&9B z{TQ|1q?(NVl&Yo$SC7Kp8}${V)9!?`H_Af6Put+=U@vje@Gw~gIVq~ev*UaJA1 zKfBY`t2)?AO>~o0B+_8tWs>!uCdrQY)tsNet?ac#T4qYtJBZg_$K2$c;V z3^cWyzfXgC*-YlDWAvI#B?OsW2!B%49CZBin`@7ONtf+@IEZpuBVsN6l!+iil@4`h z@UZjb?ttik4;YOgzcct+lR4#s+aL58PC@`doH7aI^NZxW*1ox%OD^(^19$3 zL~`yr!@*B)MzObo%Wax=D1NZ@>=aw_ZPqEoH`)ZnK_5{ZFNyJW{?-wKK5erZ+YKlG z57m*o*~UVw&A2GJcs>z+@*HVed7c2oXfQC8h7jT-Fate>O3Wfz{a4pK_m&*5JPWHf z-}pj}bfKb0%zM>WpX!WU$*b^W-d{id#AA=q9<7r#15H*6qn5VEc-K&bCWCb|C zKgD!0N00e$#|y6fTV0`K7}Q$F3iG?Su>VAqC=vt1A{91)L5a8RMqB!$>Fr_zAoy54 zwcPoI3G0oTDIbCCYcDk)p#u&#xrTI`BI2*Q-)&E2#X|i6-J%*ndo+f|+MQyRLC|@-ZXzwLH3t#iGt2ogP@}Q6%-vFJ zMmgq_;(e$RNhj%G?;??G*vtElg!gbp+;cKPOSzzj*!6bJc$9RfrI$L|!Z8Hoas|f1 z?D?TZ4j9#jcxBkgf(SkJ5n4a{?Q_`t9?%LcLePp_T5LA-e;97%aW`<1mTDK&vl7P%cllMKhD2OQRL9x& zsA&PFbO2vol6?>Q(U=qJC|XZ%sTU#htcSH&cmMzn0Dw(^nJNaLkTOvEhEo#W!OR$( z$E?-*ShreQpSoMBza5htp+R;2llYx5c!&h1wMsF z-P{x0jhF3xGC%4Cx_SC`T3Z%aem046ymrYbp4NYRQ)aOFdxyqaTfgN1r%0E5qGj`G z-}%*DNKJTal@`?zqn06h=KAHI%ua_KwSV78iLPI+q6=009-?vDS%R*}HAoc8(<5K7 ze9J>AwrqQ1jNq?2KOuUL4umuVH$H)B4jvxb@-|J;&q zhqdU9{g#-TsmNUXm7=;x5O^w@&SX78ldTb3%%TiaF}B6?=4B&z;wa6KPsldd9aKEF zj(u3#N2>m@Ty}NW*_w&Y=dx^&J8UUy-}`U*4<0~uQPPOazemZb_%ZR)Bn6!kw4aTN zIWu04G?>y-KjW10AYtVcDZ$12wh7PFxSS~FmdNMjc)MLq(SNW>&o_Y8V>HR5liP^p zM-Z_-FI6{ksU=mbaTrkhHMuRVBw&0Y;)&{CKUGD#zmBQ zQ_-B|A32n^Dk-z*M^JJ`3-);5CLa;i)0|m7MZDLvSN3p7f^#{H+$?-a1L~>_LQ4|h z43@8cfp0lT`53_KqzNBbUWcTueC@eWy%)_gH}&NVTBC32!j;6qPPIwwnGuecsfL(L zoqu~;BvuY=^4TgtLs zQbX%Vpd-c)1{E`kB&t>~SR$cUC~O^!o)Dg7UoH5z@`0yXH}jYO;UB5^26&69i64D{$aa8IQK^lM0r-iTl>)Io0MnJmJIP5%R zj!XR5WTMT)!_W=t`Nt*)!~Wtr8u_9tlveFb)SnBU6KFqOs023Rtc6!EYbpye_iBag zo=*ZBXQC4Gc?^l43yiTJtoz4e%i|L{I4WdCiI#sVX<18 zW(Ax#??Y0`>B-7vd?vP03jw^tz@Q&H2u#$9zjYYF9sL`x}mFDjFDbM&5zT8a7 zDdT*qGsfOEkWNqiOgBj)mNaZ8^g@_II_CPKS3&sT9p%EBqm`==K78R>F-zcLaLyP2HxZ^|Ycp608_`QT{L5C_5 zm-y^F7Y53>Myc-ij<0P+g+#ONf5tnc0@Y_FzZHM6b@QPyRry6~S;R-$X_gd6_$Sw! zfRrF%C~N&{qF2+L_YnlMWON)^YuBSk*lcyVy8BJ4-wW5g4^->#G=71`a?#~#OPYa& zBwx&oLWg-|30F%+Z^BbbvcVODb<86-?s$9BQ7LwvA6I2N5j7auORgl&gr4ysaGyE| zgbJf?ha;?6RbIasgOTAomc&;wvbSENVHc(f!31WfSjr*{vTMg zN0Xw1&%H@%L?K>}(@N~bGur9UudBb(r-;0VqyVw|&v#E=bI=WBK}ekw@p{v}J#QU^ zEu(6p65^y^tXM|extu#nnKtZPX;Xqv_|ORzr0@5g34=ukIz(EAIA2fPbf@kt#Z_l< z@AOQHB!_$sH#RPDYEsBZHCJm3k^XLUuX|+-T2p!cg7gg@m}_b%q9C` z)b)iIHcuj#fHEsq!89#Nr!xMtXA%igUvgsvJ&E7G+D!^B3kZo(ge=^xC{oY&-G0ZR zs+$ZAe=>M;4144IfqvpfRVMghDgS!wB=P*kHbu+dzl%+(Bt3;A@|zYxCP!ebh2<<` z8OOAptkmHRW~`-2IjbCl(oW!)mbvk2KNH>eBtfsR1%wiLDNVN=KDx>L)Vb)q$C!l7 zN5QYGIaZz)_YEgCeHjW$s92d6%CyW@CI1Q<4wo#rDNz1Ia<41LU~3dzqIvL^5EIThTnS+q3+ahIt z__KRU&K(jvY|*7VJLgszI*1%&?xRj+d%DI)ACQfkyYT6WSMF7!{SB)8Esiyryw(f1 zMm{KKW^U0LSu@vHvZrT_E&${G?yo+7F8_A0&v@33mN-g<<9a~qqYc)&ADNRV9Hs>v zxSeY8%$}6Ke4F6?YS)jwe?WguoJ=+mE3D#!Ma`&j=2O0}LPuRe)x1^y?q>0c>%7Zl zeg8<&L2{K|vBp*A?Tx*!XfSev5?2rxTkb``dZ zwdE%gOL;hF#5YR&+_FD@{$jKSM zOxV=MVtu%d9+vOEIWS*Ot~j(HWJ-SE_1fGl+%V>SUt9FF>vAiXtG|D4^Et)TeRWe^ zjgS@4m1uyzaro|5G0*hdU>2E=t5%s|r#b~;!0S?3n+paK{7?c|QY0Tj4ib7-LQTYPooqhf#r( z#vhd0MG{J?pyqyfHs=+UVpxE`S0BG=Hi>XUc3Kk^;yy{=M~9)ycd(y(52B4B5JQ(pxB@#FOAD0|Q9GJ`_Fl6@GAKdX<=_sZ+-ma8$67zu*rU z{IpiOIcOSg#vM&7P1$?olK5Wz> zS#pYUrOH8E+JUSz1pA^%P3qC-v$q60$&(+twe{6Yc}o3_nhUX-4UvQ$WVgpjJ5~mk zM|)^_9lxx~r_T)ADk@*Ll1Uue{&msep@Gc8;|&^1Kc`{wdr9n|iBt35#%C!X&By`&`95eJAPDNDoM)XCqS6@$q%`U47BzQq-A8t15P zxsQjyBdVwdXF7mB$~|%0hJbarz4vV_ON!1u#lnwD!RiI>{l7YXK2IiJmwJ_0z05XP zf3N!-TO#>8gJ#m!=h~Mqkf1LsU3~9e3^YIHLFutMDNig$y*kRe z|4K+^6nMnk1ufnz8vnQ%BW;LlTTEKa;vhXDmBWKDq+vqMu_h1gvcgp))LV=GIoG42 z2R^^nJ)|2qc*JWtE#6_r{icu?)I}U`A-Ol9a0p% zVQ*mzIw^V|k4ao_M&QPV@s!QhYkRMlQxmzsX%zxF3Gqg^?1i2mvGXh;I|kW zMPk#1Iuh@O!=;@)^dd|%H+?=Ui!=Sf8FW51$#O~$#2*0RwrKSp#i$fYkL*#(Z&?yi z>_lQ?g?>^h`e6CQEOn}vg=fP6L6IVg<7<4H-7FF^s8(%(4*?&{C7AB zgE&2RPYq>?_Hnp=M=`#F-- zQ0+GzA!I%~DC}!QjhJOokS(tN+*z~!l*=f7bHS%^R?u;-KK3wg+7-9=UCo8fgGi*t z(GR%-Pg=muC8E4YS-Q7eMd{ zT#t40*o^g5+G1H6XPazc1^-im2v)Y29u*z*RkV1wYvxt#Z+F%{zvL zwHMhb1I4zY?HN@^rk0Z*6(2kS`a!OjpX>_j596u2cr!ft2NBJfINB#ndvj@s%WX?j z)%kd8(PRGmOB24>xz{Y+(CXX1ILXu2PYDKY`&r{uTl2wDdL063I^rF9p6yXf@$JBH@;jO zPu}0TC6SVKpV+wlLB@;aR_JK)em2Ir@Mx0$t|;@ZU#*$RnduG_(Kd1#8A9$P?4j=TIQ|3P`?>@qf9Fh>eLVV(qYM~>WA}AOH-PfXyJDq*o7=7lNi6UWv*V7C z-@S1Am^RBfOZ#SEKh-tJyl73je5r3xmeAf`i{K5D!jFQqn{cF`d-szqx7hRAvt(a#Dpi;B1?vNSG+PZEIU&b%L+ zhQ#4Tkm!mg*Dc%|;|X~87L!P-puW|J)6K;xl@>0z>#YgdkN^;$Ba`CA0P%BWQ+grE zFU2?|yS#*c_3_7$E^U?N^@+#k4W88CeIL)!`}f`7>WaFJp)6yMY7-;d1aSdnMut=C zeEMJ9pVymzm0O(g_NV^w7z8MbC1CGhWO2=Ert?XO)Q4JCqB z>#@-q>EYlK0si;K4uCM;0eu0;{VD}S1}7&bH4jmTFwfL1_%0R!bk1%QsezCbzPb6;1mkt^8Q!y707boO_3 z40Lr-3B+WGiHJ!FibxBJ%9@GF%1KJfiHitHiHKSQ-x%p(VH4r}p9Zd1`FRKQ0s#Nf z|9=+47El%a-`?2~9E4ff#?9m9|19@E1@qt4|7}&#g_xTDA2I#UqW`mr|GVISMM?Vp ZuLc|GJ^D|M7@!shP>(sj#$*Bj{~tAD8=(LI literal 0 HcmV?d00001 diff --git a/apps/web/design/zopu-core-product-experience-final.png b/apps/web/design/zopu-core-product-experience-final.png new file mode 100644 index 0000000000000000000000000000000000000000..ef5910ec76d28fa20d00bde60db8b27750f6440f GIT binary patch literal 244151 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

-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

`!OD>F$LU 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 PZ?f_)RiyJ@7<>O4AJ^a` literal 0 HcmV?d00001 diff --git a/apps/web/design/zopu-core-product-experience-polish-2.png b/apps/web/design/zopu-core-product-experience-polish-2.png new file mode 100644 index 0000000000000000000000000000000000000000..da9b6fcd1ce4910238c7a9cef936a795c4ca04f5 GIT binary patch literal 226936 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�uQuYYpWKhV;>%AT>SaO6o!fxpy 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

E}k}@8^$- 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 z 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%0Dr3=`g;%lH{G6>}e)d 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 Snm#r*=SJJ_iJ literal 0 HcmV?d00001 diff --git a/apps/web/design/zopu-design-foundations.png b/apps/web/design/zopu-design-foundations.png new file mode 100644 index 0000000000000000000000000000000000000000..bca74f8059233415a39ca9c22a5ca9f7a6de37a0 GIT binary patch literal 183063 zcmeEtWmuEp|L+V85G+bs0cns{TBJsZbc5234DThV$=wc&dKN)C=DZ| zV+_tSe|i4rygV<@b6w(|#eIM8Z^!5P{NlMXIVl|}2m~Srt0-uLK-Y3Wpi7h2t^n_p z7FfCg|6TCZR+a~q4lt|&H-ClH7Q1Xg&e`{v6!);B0+!>J z{GdU7`yyLhO1F?_V9)~F^B-*1Db}{X_5-ArTKXE7Gj{7Bu1j&h13gMF19bfV>;J}q zVChVUwF@Okyo!WACL4C5w#HHTo~;?q0KTWecnJhg5dtg1-oXN6suVDmU@~^7W5h)4 zI_S`Pfe%z5zN3F9wg!|n(`5oyn7J72$Le+g6kHYmN1oCN5i4Sdc>;SLzjqg$p%8ru z0-cVmOHt?Ge2BLu}Jp5_VfP=iFccJ$v7*8s=X+=*x)0cnH*e?Z#$KS;GokQ;1R z&Q(jW^xY8wP?ml%dn3!LmX&&7#d|OZu)hlFj{YU!w=7&;CgjBZ;Vi`c;|EuO{asAJ z5Y_rgjqK|n`#?ol2Fy#qLc~cK_*T5!Oc5)vHT?39J|pqx>@hVlya1^M)x0bYsqj{I zU-te;BXK#HCT5*(R83oaY%?7j(U=KmDs7er_p-Zq;)K{3ISb?bGqzkksY<9Ty|+t~ zs(Sb&2eI;xM!%M3z8Xo~iVRCmSXHTEj>WE%f!1%+Nm1n^N1YIjPcfw6 z=A~YC5Sv~Up4a>pqOrpmr{N=4_z_?LS4abro_IvA1q2p2m7MB*xBC;ebtX#Cg$!|| zPpKihCWZ!9twzVm)k}DKCTH-W!VnVxPJw-_w}g!+%lb6;?m;$qN+KpQqfN^VLRJNP zt-|f4U=)jqA3z7TiD4DGLl*lK=nXvan zTEZKsuKJRJw5ff`y6^Z@MqXwQNqyCz59?ub`J6&-lMgSzKd~2Zh=OlndAW9{o6AEg zsRZ3#3#U~@8yV15%CN;CRa^p#5k*6?llTlEp-OJXqss&oL)~R!?>e#RBNix*SJ9bT36qQRtc>w`T{F(* zS(!(vpVkf^rW)Lg7&1@;*SX?JH`o~$m>P?bvMjb{osPJ~+QkvK4L}7$A|$F}dzuaX zrY*!w$ExmM0NsIyz`vFtFR@|mBR)Tr%vvJqQPs1V2LR6{?z<7yBYl0um#lxYAbGiX zn$JB|yPPM&g|)B}{){c5c4}{U#9a_Au4c@A6oEVU_qF1u4){nG${in z`<>}6AZ8V!Ny$k}IPFkt_b5TqzPvYmtKuf^-<_WNB|qiM0qP)q=PxOLrBl(l#0LV!n2J*87&@W7bX+ci4y3Fi z1pSBYq=0W7*#BkX-ve|rCDnjHay#vO3gx2*9y-<=L#KeorM6IjL+PzX*(75W0L%W) z6#ZY!&jpkpq(Y9O%&EmU2C}Ulee|+N=s7qPK8XEP5-G;ZIUhTQ)3deBk0{*VdtA}1wjQAtTviv>S> z_hdm&k17=@iJq}B=2svHgp@GHkaSZ#xImNugykQaA5#O|0YZoeNCassB*?OU8{K4e z_zWM|-zcbkT%y7T`}vFeO`|!|J{HRcz4YDOwGw{Tk-X7O0l!aNXzae5=mmf%k$XG( zcZq&14vhP|mfHS3tw3yyKoUDUyT-wyHVinRs_IerRgnF~baw2Qbrw3JQu@>U>v#WJ zD8&nH0%Q>(x&wecxo|l|!mIaJS?D6U87M@-uSaa`@C(~*sI*(4tlK;68*UL?>OL&{ zM0eIm?OXggh8SsLYErKO97|LI5dCI0SfDaD=Xy88%BAg)1&Fu=#MYK}SGu%QS%JLBS$I&oS?;uWBg+%Yb}&D2STEyQ9xQT-9hbz9v}0VHMpZ z#O?VtmSD$1+ax)$4f>u@nsaM$VToa{5i&b+gx_+*2!@4+?=SmrA&~vppq*`!yP!a? zFt|w^qXIFMg8oHbY>gw4?)doUzGPf%V_WBP6@txb$+$}`zvfX@yT|5wr+)-t5no90-6fVE|XObX4=}AXRv^#oDq|nqDQw0mInJzARdG>6dV33IIPEu z{L1^ZJGY;eq9N50=5cKEkq3Odyyl6Y0^cfhe;ch%*o#}Ddp-W`)!NY4Xr(AAGq+}G zWOqk*clY2PT4u`TxZWG*HCXGuhRG`q4D4pt5R(DHvoj5yrs%A8;uuw20Br|_{yR|q zgTEzJT_!4*Ml)CoRqv~&i8-3ljdm?juydm~l6@CCn7B3>!S?>J z-Yhml`RI+hVe9oW`;)WD>BS2yngi*CtP28UEEZ1;&hQqCiwF#|x#9U#waXAkrn*iV zbaLgx-RD1LTqf5sn?1#)`uKwlcCci%+gO~YO!g-Ba7dBHV%i!{{&GvpnU${DCI1Yy zYXZg`BW@15O_A%7&AZqqF@ZZ({DfoO#V-7f)-lh!O#0t9D9&7rpem(;b)r|5qkG^c zdh_oqOTdd<^$l+zVG%r-YY3sV$t#O+ertVVysfk{DQQ!w*3>JC(t;y$OIAD zTQ>+*)kl?;m4~~2UP` z!Qi@Sv*yklNs^0i`H<~j?De$eF?q8#OGBju`U~V!3sconywq#gQBb z%|$MGfuSXok1%6llgbx=|KZqo`6|%_dGh4RrHDxWcs3pCRlCNSIO}y${rtQLCIN0z zY-V~ombEo`TS&qfLv18Y&$-1)XBTuc%&)&y$u=utw0G?|=Y{<{c$X`Mo^2Ew@?KEF zMBAdM=e*Gd)@k3Ofc zaOIQGksl#sd*6@n$gL*)+6;cPxWaX$D4~9-h{q;kkWAprWVhKkYZmMNb!TVCYcPbS z2o86A?z`|ibnooy`B0diZ~ex>6w+Z{$;@*^FH8w?ynZ40;jU^+j#(l>e4xT_ZDO1v z^}=5bAde;L0vYJe`-TxIV#K8Ncb}0D+v&1ZN!>KhFc6$Kb_HAk;5Tb(C-oOBfp02d1wEWh*h4#$k;2YOQo>ae<}~Od)KBEq?d&5 z5j2FZs;zN5I1l5F3tXnE5vF(J=Fw-N*33zR+d?~Pj&AT+Cf|q4Cxo#P2y+^2Hq*Cu z-_Q2mgc{qBg?0+OHFD66GBRK=Un6a_v{3vPIsS91DcaXK(A4_a15j2lZd6?_T<{j%*5W(cKdblN#8>L6@E4_6u<@P4@ z$uzArY!;Tcj zyy~#Zh%5gpa+-cIHA5NmwwofSFr@VrAf*v*F0Mh=Ab(xrF)PkigQO60ELrrf4O&(m zQF?%D7-odb6wYftei}|AfT0m?F2J0f6Mo-3S&daL@?0J`!7@UM&RecJwY(!Su68yO z-ns{3GcB{e`r_)}%u1KVEe{05&0!;ng%4hz%C!Jdzpz{V1100?F^;o|OxHc@VVFC_ z-;85Z%w1OLiKpbR%$RT&+wZk9k5PBxK}7X1a7EX|S)XUJ-Aftr@CXpspY2j%%@Gn; zD5<}fG6?fg5gL8di9$UaPUy6KbIt`+86h@WQ^L1y*i5SI*}>i^EmfHYg{$GdZBQ$2 zTnFq`$wojRlda*Om{yM%V{8s(F!ZbJ^tWN9& zr6Z2)qeR!56{Kn_-kS7Twi@E|SxJRO@vFBlIfZ?$z?NP*cYtA|Dkt2QBYd}Rct>#i zrJ7(-RkcR};GXVoiSe#4yx`Hb#GID}5Ej}sM;nWnJ>Ov+8I$K`Q=&(W!V)H0{_k=Q z9)o>h){%#zR-yvjo*zUHD#DE3ltgAQ+u^NBMusa2u(I5q@RCF|m|#89+s7v|Gq+A= zR#b?Nhl?TM9^@`rWtN^ZOe$vnwzQ(;^+^h;bXjT7wiAQ{KsFVRIaOAygG++n=gu zbNoA$@K~*I3^x*dVNuZmH&*5ZJG#JBQhcb1pqYx^TGwzp=`rRC*o zbCz4drmoJ&o4=~Rzkdy9s~0PJ zaN9~f1-*=8*ndN9GNIXv+KZURRx2I2y?y=07;wh-X>Xhl8Dk%$;7Y`IeD*gYM%==} z!p{wKb?dWu9o>pdZ;OV4wK*w`nXi4+yngwa*~OM$IDSwpDCWkF-kMGNFmTl-9m+J8fAIN2* zYs%nrIWLc3a*sqB3OG-bPO6+%{Ozb3Wp7bvl6`2+G%gNt;5QyaL4uyy((lde{P_tjv=Uv8<=;d8+Zdu0lq0;%^ zGMR1`&9k$Ln4dd}OCve2kJ?{wR^7ivsu4f3D|&Df9(=>#DY7$FHmHdy;AKNSWUJJF z6X8G&Hc{@gpG8J`QucM(XG zwSiUtudrpC(Sh)d!7l!oJ{E8&xTjR;i}G%f*TUyOHe8(^tYqISwyKu@m2&RAMyXB6 z1*`t%2E7L*0fR1(sh(6#t1+q&`I}=Rwr$CxROr^6cs3OKH~akfHxDHNeSSLQ$V9Af{wAUAMo#B8 z5p^$=T0JT&qdk`Ox^Vre3}v_Sdi0J?3Y*NH;XjK$6`Gp6FSVzEOBW%G3P$N7tVtLR}Blqpgl?H~)Ene10 znAZnE2^D?m$j&cu^SkNBl%!^+(LUv=yNymOb<6JO1F$~)Hl3kU4Xu@gZqu1NJs37- ztT~AAT@ zrrS&>?BB48ijQB`_busrwV8^)2z~`da90Zt?o6fRN>C-i|)`<%u2`@O|q0D|S zU?Mg<)*2~NzDZ&=r%N^Uxagqa3s?6Bbu?uWxY9;_8jHuJ9?&xL#yyOhK(S&tHOd)s zY?D+}P@n=E&Qqh2-rp16dDA0n80d@LPZe>8CGr~O3_X|D<1?=MIj<>I_@vTKeB1mI z2tx&Ap~O1upM69YhcI@Hc&|pdFnf2`DJs81QL#O9`2>n%+pvo~15%aU2tizAr0F96 zQ7;|AE{QyHU|=6#_SsNU^ckIfWTUnaY^34`43-*p!RPk1c;4=PJhh5MpW#+3Uu8uH zbz*!;Uw8MKTM48H?mV#~{r1H+zarIJxo(VUvzxxJ``bD$4oUy&?ye&VIzCBk&oqyb zFZ|uky+>;Uv}rnofwvPA6L|lK4&CO7OfH?zh=R9|23rY%=MW%Rdfugaxt^oPW24j? zuXj5NKbD3=Hp5T*-2^#8<);MFN<6?Atg49dv4x`og zIWJdAUOuYj8rw?aawv!Zcu^@^JsR*UA*veF9-qyUAnJd=XE`&d^i}Ieho!z04TfLNse*@*j_ zcMu!3KoXt zpwBc%Wr6~XSIKDhw?5z8J+2Rx&)!;l;{2YHl2dlf?Tlk0j@{G1HOlP^atF*Et1BZM zb@#><*`94Y^}W?c_cZ$U?Y838$;4SdISKtC;=?r*FeMoHpEf`A5s z2+%2zKw^W=3Eszx2E(;Ydd2z@^L@g_yv1%(px`!Ew|3%CX;@(e`z zaWbH(;YlEtQD|cgf_V{=n)Mb%P-8& z&%@{l11gO+$nSnRLDwm%&dV-r4*-3W_N*Iahld?X(J;Yuc0{@U(8BgKGby!8so((& z#y*j;-eMko_Oj$2ckY0`v4i#O!Ex)&vs=1ZP1}?ocnnE0$VVS}$=)pR>tCbZ61ecGU!g>|y zR;gD^tnpEN*RQ(4`mS~F#g6FTgVO8m6~3%J!unM^4f~J1WP%FZt$V74eh+(sQN=}! zG+)gAa~Cxs#BeP{dGRP<>|X95?8L+&G?tp%k(uj8s0_lMmNdi`sOjURI?ggpv!sL$qf;U*R(ZqEw>(Ge$8{~TEmyY1f~WS8H6 zZ8KsZzuZbF7U1ROou>B0Cl(Rn<9QQB_-E>S#NK{Sd_2{x<>{PVStP(i`teGqlZmNm zLy=*9RrR*h9g5H~aDBkoc2EG*jVrT=SF`WmzO+wTeEXss2Q!ZzI!o9=SWbRy%~X|W zx?j#A|LWCmeehU^k@xF;lH=oJ64<6sG~Ea?eeGz5ii!%#xOGHB6>`qH;fMPw$LW;~ zCo8VP?oU5K=5GTjxEX9Stdd)ol2n>y_3h-Gp08jJ4NR%cs2%GYQ$c6;2^JfhT~UFN zKuY@^y>^+Z*9fQKOJs{=@U54Ctm&2ky(?d- zFcN=YxPa&&CJ4JgyNm+ZGS#4HnK(A7zhnFYk0dry zJAREleSMV8%OJU3)qR0vfJ`}{OHBZJ%PhWKCi_HFTLAx)+#h6G!0Gu$nqR^IHkAK| z(m36}GXig47$cG5e_sbTC;_a(e=e5$oC+8Ia|@D_fAI2$XSs`=`g@5uTtfF zVOecU9_!{-xHtZ3gB2JAht-dbsW>iX;8Rx`Rc&5@L%i*2{J+rku&(lhXC$gN9ypk? zcIrrzYMrP8Js9Yy&kO`un(Dt9@RLCbb^vLo#ImO(dmF(hJa7kyYO{QlVPI|ANN#-; zEGphB3m%cYs9YDm6bg1UQ8TSWadEG56^8nsirCi0dIj_RvcFf^kO>!a4WfvcXl`=F zuaz&T()~Ip)A-^YS|{VgZP|qoO9Ru4S%eVGzSxw~=JmD5@;H2&Y9LWyM`3Ya%r<0@Ll* ztgEI53ioRW0@I2Ptg9Dm)Jtd8rbD)>VNqrGn=Hkx)D#DQut1!7NWoW7L5=*j?d!_f zQ?hmLQJx!DP*#kZ&5rFN7;#Nv+jdoNu_Z<0Fts%FYSO(sd zN5y&-O)a~^_`|p|Xy;D^B38?)lP&FTI52u{zAB>_W0w6si3iQk zn%a8`GX>TCbOyZJ2znF(^^Nd0P0*$gp2pT_h9;YCWaEpe>XPQR4O?BF8G~SPd7Fvi zyI9=2WN&23)<8X_(R$0+ zuFSO)L2dAVHfoMW$(d+W3{72bsTZ(^r1YIj&k( zmB!e7;h{6$Aid}q&NWJRcxJj8#zS3OJu+cDpx55V|Gzj!y{9L|_T2)lAuBRJkQ-<^t*GFe~#UJ;|$qpM0BIupgB-llwjy)pRpX zsT)*@O{NFYW1kqWRIjcj8jOoCd6u4Rl86b^o-PS_^p7xMaG%RKJUD=BOn*@`MaEWvgNX1)?iA--y$< z<6Ui&t)`vo!u!f=?mOPL!;==Xle<^8@Iq)XSL^4IRf!vPKlD+5nD7Ci@MWxjRVF+W zl@t6ejeKtRF#-9xl9wqOMc$(edD;v&zTbLLJWj!+X6tKRPEM+2nBI~t!u~fY^?=t@ zY4K=jwI{NWvc8r(jM=!2k~zL(`i|-*?c(~&Ht7TyRd|_Y-w=3WwJtJ^DlKCqg%sOq_49nvQ^*<7|!2+ls zC8e7OvI6xkNu!VA)E|U>NmW-$;x~f$mJGr~Po|UX#uZ6EvL1^}7 zy=ZjFun`OoZLeIYGs*d~%pXCcA|bVOas9c0+|SwmYZ!S<;pPvoKyr5x-L8K5pSLVf zP_wO*)n!}xI8& z($ntosP94faIQ@;?%BE&A)?~GElqw^&*M7Dq*Hav|BIuffs>V$tx6&1^vb#=LdH|M zExotHexn83yckr0Og~kwA>?i5X zD6ifPnBRJ7eVKZ{{yYPHZcK8NmkSUcF)PvJ#8z_rfjJPwXRvsKfde}m-W$Tzk)}Ef3$2=9Z659YXh%= z$nH~e0>m?fOu)dze5tzbJC%5eM~zIZqzunA!Z$*4mOIKF@a+R_6&=|6Wzrf#PqNh= z|1V3U9;Krl^}9!w;ymgZsJ!9#?vt+GH8k}<`u}XFgcC?>0t?|ro5zkcLyrxPzO_?9 z^p=Zl8unpP9)#wVFhQj{`M5}T+mPdq#;bLk(nq|{Gk3A*V>A}u$i76dC!Lx;)=PWS zxh7pxT5V-&fy0&$_$+q@)HKW`pZ=iIMfi~LP|c@r%3Ljx91}w=m(?0UxNYC1GA`@C z^uS$3U93(gkC1*9f%Jhe7lDEsKM8gWXB%ONwa))c@(=8lQSx!t0~v$E5~v@c28Tbl z5A>ToYDmdOvp1Ofbwb7dBTtY>sE?~poU{?%cBXTQ>8Ae^TOj+_ja>^`1+XfhHUO^h zFVvZ~H1Im+T@*|N-M><4kPGDCFBFS{TG)WMEr~dnzcrT%M_~r=T5fG_RO-LB0jXRg z1qze@Trs!)JNYA4Q2!mJfCc{FmK^|q?EfI(UN;2@6l~4K!=qzll#(;3l0NA#Wo~0_ z&B^+H?>Y$N<2KTh;CG0H>@9u~Kpx}!PXc#sU1l1L6crV_Dagm278Vu?fD!51a9yPY zWV7V$rK=8Y4y?nd!wbO2DqKJX?6-x5h;GbBBVWy5aha(Cr-`{}v)oegvHxz)AueuU zR_~>?%x_jjoW>u`YySLnn{_zfWy(4fz>UAK>o9hTW4sJv@usy>udpi6c(9X*)NgNp zrIQy$A-=7Q8+iiDA zh32ovh0rawSZxY|oh=E03okd{E`U{W<=KIt4dxy=k zd6#ZV6m)m|v%seTs?%U@Z33CC)vo?yh9Y;dcwj&d%xNr+{f_?pmRMbgb6W46-{zE@ z8cTi+0!3DR1;$=tPKeEhc?#)+m01`gY4GLa5e@OxO<62!bmtHoA+9Dyy|U>@emTQ4 zs@m=A>pSB*RSrY?WlaaEQFKCZ@QLO>s`dYE??;Bay2mC#<3&0_K4GpW`(^usq0DQ+ zmneRwH=jchgk!=JWT^$p2LZ_Pq!u?IeQ0VHu-)z*!2r62&d8{VsRuA_{LSmy8i6>3 zb?8NwmH{cxs*K}h*@X1;^gm7ACMt;he2F4V9pD32)4^o6@>A6Lan1`*Yy}YvWeY8eOy?P}Z@T z=e{0gHpsuAX2wq^#lUXO0xg@l{v!B;;kTD>OB?1q!d?VR>po@}T}(QYXAU@urI1|z zW*rKIb{4Q>s;|-okctkYgwOE(OMy9cyGsw@&FkyV`>R7=cLtG81{x_sipyU-qYkFr zntOcO+uKtmA*C}x=g`H)MZHz4wc$eJ8rSsEQnO;nEYtwNFzBx{b3acL)7V``mjyIJ z@jig2_nvaFf=NZ5!ph-h?PFY1i-d&_xpDq+0cw7lSIY$Sbv1X#b3R-pO@=ujpg}TEy2ze$BwZ*wwtyfR0YsWg<+O%Wf^^C7Ba@ z8Nm-9+4NWwRyR>Z(ro{9qRz6o;y5@YZ)CNUAnR9txSzNZP5_x^-3zD*Y~*ix{iU{- z4v-bebm~=5FfB<43DC1oASELmE`{Ph95lY!w$wu7N;~`Y&E&(U+p4 z&Zn;)+!?gj6*mn^fBtw|VR2!BO_CvN=AlMv!9=AkyW|Z}|H>{}c8tEPxwPCaJpte& z=(~B;J;3XK`hAFB?Eg~)bcwHT)YR4*-hHV4GsP~W@I8e@K~Fsi8MQrNX$kFI1pJ}F z)=Z-)&ywtoXsI0)-$rw=c5@(MxKM`!x<6uYJ@PgPH0=7rD%t@+?R8UJk`tb+c1dIM z-y5_Jy)bM7V1m!~I%OXM#DM>NMV%L5U@+hV5a_^z7;SFlYNS=KP1l7_a=a0X|9qDN za4rformtK0an)|KrPVG|`bLRqX~i!e86MXIUMc|{DY%0taP#u2+eZMZZcBs;&fjHH z^PvU7UIJ7&0ti8Xih?A&OkX8mUtc0ccS=m_v;kh4R+EF|D+X1o#H0cddOI*c_C!-{^lhnxh!bZ4uh~3ef99J9I`uFEG#2J`Mnod_)C5 z*;{1=_jGj?XjlCRf?Wc|ywM~Oz*Ll!Y{avE^IU!|y?@wDR2Xw}=wfp>#Ra)Lji;C( z7$eZT8}B7LRk?^w05r)Y?YFX$8_EH1uBZ^wNPhG*l*z9E@Yhuyi#us-xGpYWCCniZ9JSA+F9(~U&)Ow(nu4N*!%g69o*=* zkD;*$$Oa6Th!yhN?H9j$M-l`&8XV@*iDSG7Td$pu8hf&wQS6GX2mC)<%RQ9#h!cKr^Lgzwv96Pv^!84+?evP|vcR;cpzlowE4e{D) zJk(E-wpr+k1rSXsn{~@fR5UcWQq(34y$oOs871|VnS9|H&;z5!?GAbg{CefzlW^Af zY#2j#`^UN|SbyiMLrpCBN@fGe=|og&uz~)$c<{&j&q9m7aTa)@rORff&JGg{L>D(| z_R&GoU+1G_o4|8wdeZ&}e*srbQ2aLaV(Aq*eWF{6_{d6i&(5ad<{2fZlhm!4J^jpo04{8)zlRdDpRd7EY#_2?==?gf#>Av139Y)}xnCFLA_19Y|utb-Ifau29gGn7E3mOCPda5YFn8}hv(P++@Lg%acp!b+X=K7;x?8!o$L*(QcYO85T8Zv8UV>CQ^Dqa6X zYWwK2%lNmam3C-VM~2Taj&*x>Uo(nWqaMo#H61cFp7az0F4Lysnf$wk^E3bYKRMwr zMj-7PS0mcWc(Jv$bpeoiY)ZI&G$5MiQS+G|Oyo3q(hVIKTaG8TxMR+!`AvToY-q|X zQj*g?l4FWC;7JsiS`_oU%J4c^>5G^);BtY;f+4!ZckkZy?3c3>Xl6gyg9kw@8jjQY0ba0x^-Hl%hYoc z5)wcEHA;j@4gaR0GNY(MCu2|plkd@e04E5>0LbPNezSTF-JbwcfwpUgf5};n)+;WnAg!J^i?}ZpoN2uplJ z*075n3m0g}`td^^(1&#Bz!M@wJS@ERWG_w=@Z-C%dz z+5eM9*iIFulqcu>R1H(WueY_Gs<6o_m*Tt6 zA3qHO^!@AX=|K&huW{=sb_q~)=mJ97C<3bBIYyR%zc*F`_{U!Z)-o) z5t;gU8`;C~Yuk^CB9zC?-G5(njHvI%b(SH|rB$y9|fRvMXgS}`n@ujsT_OqL>J%HpHlqVJ>K6|W?tLVvg=yzB?cUy?iOW6 z@A!{57=cG%cmdUTF3`LOV_RLaI5!4FQZ&8gIUubZRJSDq)<*ekh}f`z&_@a>DJ85Y#k1)&yjV37VnvE%I;-FDm)Mq z5J2;r)DJ5?xQ##L$Sg6H_35)W(bcuCahr8DKp5Mosj9960n;UOrqQKz)(+*hX20B* zV#ON!$*fTVU22BvP|DT9e?QtzfU4tq z={=mC@^%`Vv=?!dB`n;X%J(kr*%&P`cNr_Qa2qQza;b5dvdtYVAOrzL!!#7|vO(jK z^y0=&h;-SY3@G3haMS4EXB5@A(Nbjgi>^}RIt(>6^<=svWb$s5EZ`gF`5(_3J8c{< zXPDGE1A0Gd?4*3UPML-~oDf5pOaL~GKl;$=44eaazQ>uk9T^$fMo$8d3nyg-;O?qJ znI*n2oj?y3;+|KH8spphQp9L)Og1z~5m^FRAAc2klb%jI3h*XFfXf*+c5*Q{&mdCA zDD)-<+uRxG^ZQ3gbyd}Hsd?k%((>{Y40gwVsLXYG1Fuae4>%S=0srW)U0$!S>0OT$ zH+AzQ$ly;s(Gvhq#t>#s;;z&700+|lj9Vp4jZn6_1$nBaqsuHzcRZaC$jqR#6f>kp z9s_w6L zO)<&q7qq*=poAT*u%uw*ijbmth9Jy5(upXv_Vfsa=AavuEquHNYp2BmuY)7HMI?r z<#Yq9>E0xPN-`S$Z$Q#o>07KnL6k5Cqb<4lZkGoD=16q`dDsXw71hLuuP;$Rfca16 zUb$HTFa!GM?N#STv{mU6RW-n^wBt_}`bedXgN-R3JgxAFI>5%H)6+RK%L1;Xj$RUP z0^>`dRdRsuPun>4-CbWBDRvQ|LpC?hM{#$SmX!3BS~Sn_8}A{A{N8FH_#7V$qUD_{Aho=hEIOr?HZ9pud5V5I|E?QjW{9yhJmII@TOJ;YJZY0J6xZGm7y5 zwT~k%ejadO^p|xuz0`OhL;y=c_uQg&5A>2|?*c&M^&TpobpS%Nj>%J7UlCTCcy@AF znI`U15kf+8kmlA@4s>V4WMyZW#C~zOqu&D6)|3XlxPDX9uhZQU66HTI6F7ya297v$xa^w)lrO0BIDR z)1KP{P_VRW#V%%(&5}8H&^|XzOFla*QYrWIH4@NImF@vGRNVUf`?kk(-=5J2T*&(N z?WfH7bTjvbS2Vf*!U9;F1D%CQ&3p}_p1RCdM`9}${qbl1TN8l8uO&0S1R#d1&flzY zrQyOj^h-@0%4$z5dSo(!HlLt?yqcRPo5&9%D3lR<00BL%4gF$iPy+d+8zNs>l zVS1Km<4egl&vQ90h{=9ldRA_o}*Q4`wEX0tqlm7em)`bTe z5fAA6+e&Yjx0ME)l^>=Slsm0s$4%Bh^BsJ&++q||E$0a%4I7uc^_23K*2&q~t*uV* z?H6r3ff>sgZz_c=w>HxyoemoB*{mlIhQ+>Z!epCCDr!;YS%n@DZ|62QOQ&nypK%#4 zaO^)rWM$cfy>A8YKhdI8+&(-s=;@G!^Xb&b&n+*1oo;Yvov^BQm{b5hT%AD_*E=uv z1SNd>bS)1&uS8r7)+v(Y@#p&mb}1=SF0%@SicB@_G89BQ;g3OCyal&`c9J)4Qt@+S zq~6Dy^hNw8)n0{n=;@0^3M>S=Y1K76xI-zJUI2Mtyy+89_!@qH^Y;X`iOGIoM>PFc z&aj*+ry(Ulp`Qk+;*|geTJ_!=J1tvy0MR;}>42(s0p5zYKtYBIc11aIbc+ms=azA# zNb^Ixl$o=R9wetOs2?7lE=AIZm2Fw&1vNL(mFe@D6yS7=jTJwC`t)K6o%XfL0jv5s zJbb9uypf*f&O^2+dPybXVKJ*N808-d@u$8)y8ISR@Djr&Q?v8vmiJV^o=ak4VOm0; zIm;}X^||#5vS{zE@Q~~*>M_X%mY6pBiMHchoSjRw(UZnR;=^b}buM1K7!P1>3vsn5 z6wl1e%$k#LiZ(lyo10rZ7HcM`8e@$?s@b)7$ywZ!@hX_x+A3>nYvUBtNaIwxasnu( zZ~_p!czkykz(P^*4h+2G-SEt%wvG;NzEn}2h#Pwe>k|?2#Rg^EO-)T}62Jy@pFdnW zIn*gNtt+7v=E)&a*ch~x2k_L;XjE(Li7&?dIt7J(=4>V@m2RndUC!kzS2*iDmQ=)S zbB`3_gTHEyEWcigxTk7r`lZ5ujNdx~f4DKEG6U|1%B!nKc;F9Q^13ubMr`IgBXx3e za)vjE$1JU@FOtnXv>m0{F^@Bab6I{wvBwSC`VJ_behL zgaqVyeiCjgrg6c3xK=Pfzb-}E=^+4~z4!~r8yRGMA|7hYjy?(6WT0MJ90x2FWUCl{ z=kDG2<>i7Cm39VO%iHsvUmm;56Pf}ER}_?#gnTh<-YJL;TW({+Z=Rh#-YJ=x%v0;t z7ys-msyU0hb+omy^>?Od%YOX$(a_kq;&qGs{+-7_{%X?=V~^+5x(GaOra(|sPhRjz z-**6*T|a}*RP%71Zs>Nod2=5RBp7pxi|wf+#UnPAwgW8O+^UTqe&7Wge0M(rc-2?0 z%%eVi`lJhOo|!<|MW3LjYMKe9i@liwBg>bvUS!MbmKc8KtF8tSS@=B|Bmr4X%@1s{ z)|8zyW@%f7=kB@}moK9&f`bf7F>fLno~SkwM*GuvP3k`Y(*PmJj1-_^qNN6)mX;P? z^QsqwzzIwqppBRk-(3Yf8990TBnBp!JG;qhH}J)}zlcn3Ccrl?atMoM)I>{yK79D_ zdVO3AVEOUnq7OlWM!-DyXq#}fZMD#g_hF{uZemoU?n~>OKVsJi2naBI6vULLmXt5w z9`>cGhU4+$WT54C@#k>I6%J+P3@KmvI*&z={Y(zKtLxZxGXE532C#1sugZpWFd*D> z0)#YpeB97yYcfwTQCUez``e5Aj-y2+!5d5ch9iZoLti!HB3i$H50>!eHA2}UPfo9p zkO;cXn5t`d4n1ypd>GibwLVd)45eU1e^JWQc0WB5^o|%9cuq@C|BPK;C({>AiZz4q;2K;w9(RS^(IrK(gm3Olwue&Qc6B|)$H&iDJ>Ybqls58Vg#Yi`18PWy zNz0aCqEBjlDclwDzo!tWxte8(fYDz4%;#i<(#_3-gM0A`VK$i&csh$-?;Da@KIa;k z`L?Hg)6qL9f%oc{yu7^cI5;?|1=F|g)7{0#e`aEmC4K@ZCi_&K%bxhWhrK=f*w~n< zV0ZW_t!4&#Wu=;0xk*T~?M0CR8eN3{Pa#COK@vEhrp{cN3K?0`p=usjuy`S-AV^aQ z-WYYo+3oa;fzLR}Zk5kcq2sig$}I@)({I)>G4V}KPOhAG@}t`VUyz!f{y~ipThX&M z#GC%*ca!kR!|ewQ3;|@Gt;tMomv)kJDk^EsXXHj=G-`!u%?11}gl`fP6Hnd3RM4-; zCt+h_Uo}1Uyi)B+^8I43sQ$@4G0DB7FS6r~%gf6OmG-7oGV!hN{czCG?liTx`)g!m zn69o#A6!gRKde=8*TwjhLricY!ZXsB%>P_Nv5So<_Np zp|Y~ldGY`>Xs_*s74LyjXX3Nf@BNeHrJ*q~e%aid-2ox_vYwjp2#vL%fbeXzLLXHw zD5x`5z{h7wFrLrrZ7t%I+Rft1l`9hm`)MwFxC`xu{xLBGToV)9+uL7Wm8`0KRq^9@ z*_~NfzDYYF&%?`W7LWQStF>`S7<@dVqa*Ut08phOdMSP~V*VBx4H{cMhpQqj8fn(# z-XoTi?Nb77(9C}2KHZBi#(C4((_gMc(5c?NfB(*tI>8zj+}syrDLwG|Z=SB2JA z%oC%WBc*yS`1I!V)aeCv1-xxk20oMN1!Obr(HpVPQ&3wGQuce9y%_I&B!1r@I z&FXz5Z8#$*C;w$jWni-J81L)6=XlolNWx^I9IY#^?19_i`ngm4TWByDD>awbT&o1z zpP5_V@7qZ`BAxLt?x|F$&LRN3_e;Esi{}=C4;7Ov`9&8Y?6!=`B_wX28=ckib*`w; zMKnjB(xx2=&T8g%#f>U_RiXX*GVf^-F7znbJgktBi575s5o&q3IoU8H^ijvsQr1B9 zpG-L(ofpM4-ZwDkAc9nTA_&ipM>|8SlIVa(``Xj6C`$zH;d-29nU0~_Qc?H!1O2H5 z<9o>5;eD&g<468~O+@%+$Q6JZBrdO9YfTao^1&q2Yu;Zu=h3}NMMZ^-X6D~tLQdGw zNP$Pau`!qz&tn%VwzR=u!C^kw*wYiN_(;NLd**3%bv2xcb<`t|!`$0&z>|&Kg;92- zN!BOJlB6Q&yc!j0O|??Cr)u2(`R5-Uvsm?cZf$Y@WBA* zKR;s=nt5@-@4Wp6P6+t^F3GbpOn;_$p&oz>T21v*3NS4h8PUHbW z_%Q2wV740E*6|5;+3b9-+98f}d1b}6r-Qz!G<#=y<+3$ONQiWEb5*|r@Brx2TSZN8 zYsxoRc^nWJsC2Dai$GOzQH51(-D_FJ)VnO`;b>NcG4c&f#|cc6QDap|mYrIq;Z`t8?Hhl(O1 z7}T&WEiJ{-iQ1tCvvH`A+QFuLypJFH`r_P0tIksvA`?eD5xYq%^wIKo&GI)gZ>7-O z^7c7@TUN#ec`SE4zh7b^Y2Cs44}CH+{!rELi$@JUcvn>LP|GJ8 zK-P24YI67V^i;)9dv(JDb8~uWMcE5Tp;X1G6hnGQMay6RejTfK{CR(h`fk$Q!4h?@ z&54SJk9XEg$1CqczWmF2gk_YIshbHH4{x~yKD&)m4mc8!4Lm;C{hg^8*1Xd}SCha) zoqd)~%wwmNDM!Mjd2W-UqM<>YDiN6CHvD~|J>t{z9QLUE_GL9Ew$oP+kRB-nI|fVa zPq;^jf_jgXk-mkIsgi7H|@`xQ+)kTL7Ih8scmBE`JSifrPjw#JQs!;C%7qHKFOepFOc zw6{uL8$f4~cMjs{ z{X=u@=^f`PX*W~FgBSN#F1ho)>MDE8p63+UpSSI&?2iYLf6Cfrne$KYLy0PAt53A%YEK- z&%e&V`T*S;>N_hT9!S~%FpcxcE+v3i$1_Lt9Mv^sSVTn9IB^+YfYou;1}Fp$*=%db zBfnD1vLgNck%DlB=eK(JoCHrNpm^wPZ;!QfbX>8rHZXWrwmFFtu{B*5{4!6A)nicx za^z@k-K+9Rrvnvs#xL@;L^fV=_yub7LWxT}{>`y7?hFrh(Wp~Z>%)fv54Sn<+RRL9 zTU%S1I6?165*T6hQM8Z5gJhh}SMFmIcS4qq&uwy7Q87fL=5>TroyuYvseo#MdyMO1 z5M=V3Q#I1*QW0uJr?;e9A1V5J z%Li`EP;iyIpC}b>TxeBC^LTX`4#slC^LWH!Q%bQIEGrytP5V?;xy9-|3j}-r^efmGPPZ;V;{%=?oHbC+6@~U*)>`bY{+uLf7Xl!blbvR=Brd~R8vR5XJv%fVQ zrB<}WqBHX(ARyr8ojZmHtIuU(*wNkqC9YJcLVE*)Sac{?t-|h%b8YyWers4VC=|CK1-?6%*!ptqi?);;+8T;|kHFSfg3N52K1`-@Om zrp)i@(*wx!7Q(%+HGIU7R2FL6IXuizY`BrDRr68o6CN@x4G!k2yBJI8h+?875P_Wj zGJ&_L_n}wT*-0SG z1W>&(Jo?QN33v}hSJ}-b@F7W=o9mBqju!H|a-ms4r+89PWL!{Om;x7lezGJAKK^EX zZ?Xut&Gua!DmfMAKAgLE>k}udVg#0$;V8J+TnfUJpdUv&^P-4KZay~4)q5W?h~(l* zEz!`?4R%sKbS7{KY3_Ih?94X7w)_mf-w)_C=#op1hs!CRU{_G{+{y{E+u|wVA$kVm$dTQv6A4GY1NkmpU&7X_;_n)0R zD}yPqwL$|&eB?(ScT1B0Pm+Xuqr3iCiMjls+2CV7^oNbcDj-yYCz3{W|6J&^ z|KE$vi;Ii%VSgY|HFBhZ0`h_O#ks{);eAF%m-R<~Zh+c};?K2{-6bLE0__bOe6FCM zL@+599$wVH9}Dk;!?Zq9Lge9r=)@4a8S)lPakM}`lOhofu4?Yn!#~SVD=jA{S8P2V z8QSzoadW9~f3?rsO-n0=WQdw|rANqgEX~%)C&V`_^^w>uywhb**TdsL)>H|P&}b#| zlfv|H+UKDy!5%2E>qc=23E~OOfrN#wgJ0PX&azKkrlCYVacbISFF7f3<2pUqh|8+> z80TE*HWYStE*C#Z6sV$Ac_0LiJ!!pYvdS0}&g?-S!rcr?PAKx~2L{-hTsOvwjmCau zCoz8iC3Wo7+|?EM+OQ+vX^RwP2x4gJjRQ6w;XnHm5<&+i*en~@$HmgRuJHaYL49xU zXW3Hq{49lsMIun9KYXtcCVCHEJe>g6U2HjdYj1aP#A>v?KfS{@$S>om_v!KRv4NxW z-0ferA`-Zl2uzo~#cTCVqiB3p-^eDnGFO*8j<%+aAFBiI@w!NxF@E0xY$Lc zp`p<`JlxXNC7W8?Ac?xF`YJgm?jWqG#<<5yF5&gD0m83QDw3h?XVa%_=r1<%`RmpN zQ=!h_jz0~)dj0mF4VTZdnf)1W&n*^4CHt5Ud9}hf@B9e@b5%uF8?OKxY6nk{rBp)q zVra->%tE@R^1#N18?7EFrWiIiTr4asLQ6bGjW^&CQKqJXRZCt|LK;9U;QF3qyewz! z!-o&H%e_xueA8(A-4RvSYRJMrF{xJNcn3z1EoIkvrEJJ2^6L(FC**3^1p(k{N5u_xzs1*&ErI80gFFiWE=-t0&~|tq3+o5e z=MaXP5JQYF@?JH~Hu$(8P8|v@wQAkl;D527dK}NZtxR&VnkxTMcUddD=~CY)8TI@eZIevB1f1QXV6t;lv+5=Wh8$;5@W#>)Z&dJR*T=i!D(sem zD$mnu1o+CVsVTUlp)Vhxp#lT_XRTvnu|Alj+sm^e4x>O)wuZ1#L!bEEalz$LD$OlC z=J*yMtWtw9JxXEi&X1;P;bPnT=3876bRr|_@R*zTf~fJBH4M7f)&9Bh4~3c6Qa4HD z`CeJGQ{P1++EDIL{L@9q_#pLEKnOm5jE(9O23U81G6n`foMykK^~^iqrr6upH{@+* z>M!o~jCRFwsn)pOZwVr$q@$y2s%!}+87LR02=F>9wIm=l7w++X$KE}AhCw{ib9%7$ zYnV|?vD|$HoBriR2Q1=E0M%AK&>rfHY-6e9XeGpQn6tkAMe{Kj|0%%FeycEtD?sQY zy5V~H&iPO(YIAclN_6ys-uZh!m5bka4wFc35)Fsi?0mIEDP0OeNeJ0|rhZRkS2U@B z8>{04H!$_$J9CtR9Xq#>AaG1v-2C!_j51Li(v$3`{>=yX?^4rg!CwmY)GL(G|_g6>fy)nNEWfBZzDi4qO!$e5V zw?IMo*lANcp4&6Pb?8NB5|_OGS#h?q=|uiR07hXx(V)s#ows3O`k@|T08YCNF(A!$ z=BanN!y45mT!t@DhW|qm(u$&EVDKK)uFai9>ZKOChq-DRmClNqrLG5bOj_k%1gE?% zw9w%^K7Ie8hJu9!MMy|Ui^`d1YiUUuHn>1Yx91maShnIfvqD>U8KF9-9xzn5pD7f5PjzvVG(w zY_ok^>thYF4sWHns5KZIG@N^nM#zT8s9qV4)!<`JfeT`hPnq)5R4)Z?qc@HfVm$hI zXMSST4xMBd)#2MW=fm}w%8N+GQb@?3>uVoUuk)0{n8~FzNF@qNix=P&(!XHHG!C8y zw<=lci95-FPR6P7$WKJ^o>6ynH=y&6*@xawArjDTxh5_gg7S%FKUgRW^6FQwUIA^x zlJ6zN>#$z0hKGmSnbgA^fkb=~zaswF{A_399-excWoou8+s8X(Ke7ihvz5~QE9Dx* z1G(KUuENBzbm`&m3>Rl)0Ir5{Sqml#VWvP?aL#JrTj?2Bx`8jGOFmiY@G|OPbHE3{ zg1ukUNo21VW&%pwA7=VD&E!Oc@CkYmH`}$QA@Wbv7oC;>SEOsKM72)$ zQO=7z-JR*ne3RLC_lzH*-MW=x(x*qv?d&`0cEoCZaTO4VtP6A`76&dAxp?XurMBzG zXt1tNrv*K*yX3-pM~j$7__&dX$2tiyjZ&Jg_?(O|>=O_&vpQZ%nvS#QJg-X4n)*^D zW+ppHm%97XBnLz{ytXENAhxMwzlt#Ic&Vd5uztBTG4KRg>*C3i&TeNkRwYD+A8 zuR?c!(EQgz=gPopA=tP=s<^D#@b?A?$%AY~@qA8^bCgmFY0t16Cc&|Tjv_^?##N)- z-p_%E(`L19V1b?W$IuY&8_Kp^X z+t@B|V>xb2H6%Akn3xR&T*@U#mCA06IUp&Ci}y8^>)jjU8g-rtatXYKs=21Nr~9Vj zf%rdg`S^e~Kp82veT2uXIoBVNgUnJ^*n-&2r``$OxO2h4inf#93UhuWN|=?>ZkCv- zWG0r}=g4{ah>Ggt#kCt(zhq{%v_H?Wv31O_Z^I5}K4Nn)8%%rh_G$nr+YOYnu)|+p zpQ}%513R*Pbd;%^D!y8%Yet^@pBA9zDcWbgm>-PKOP^1wotp8l)FfS-9r2>>Bh9vYZ`E^K#f3%OqJ~tnlS(tm5p8nj( zhXR?&gpmImDS&q)Tw>Os@TxrzAmw?dXC!tEIpV~`ME^t-09Vb$&4BDJ{QR&NfX{;+ zA$9!;ld>|e{c>wt*rDGT0P*v)vmxJy1(@^fNiI40_+AxTNFuka!xOQ=#iUUw17&Vs zfB#&b%ieNt!6TG{f`aA7OZm3?`X>%V02LE939X~%7Z=V!3Pf1MRV@#N3a91x*Xc5o z(a`*Smc*u`s;=%JIGf7lHo6W8;3TlN_i0H0riC zq$pS_1ZqrB-D0;rM+lS>ffEf_uF_P!KXOG7c7v zmRD9PRlGKYKX^R%X;8Z5MrInD zjn8TG3)qNCo}!565I{PCjqyh-tE>IBBB+j=BgstTK=s}s;vk1og#g#wtU{}%-FmXB zz~ba}pwHyl;U=>7WMO4}d)%Y5J@Z2Y+K~jpKHZ)629m;Eb8cAK(3O z>pr=5t$R2d7c~?<#6q6LbH+kfO-Ayuc(fonPT+q zRrY2}Z|_~kZyltJsr>ra!dxs*0e1*9fd8Pis=k%aj0m%fqL@;p% z;Gw^Arr*51c6!aG{l}G#<9|}>jeC3p0vgwA{t1CEft-7HeUz=RiTXTE?8y_954Ujp zX$JcHQ&e~6XhDzojgP}zWl2rv0nh7214ueMqdDkB^koCb%d7`#J@_E3I82>ApY%pO zB6#e~B65Q|@e1W}(`{foq8=jMZd(F1=F#S)w38d8b%94vGTILSC(CrKdWqq>9r&%_ZQTH@QCae#P{z<=uK~t)=nQ*OXHGs>AqO2F~CGoR#yJm zACk4r3a9|u9a&Gu58xRIf8|IAC)TKzTJW6QzV`%N;zWhaV}4En0Rcb`!Ph;)v1F5* z9d_2;seACU4)eD6KW(gApfVTknKdWvdlM^7eg|45K=)ab^Wp z!P(7Cxx{QN(g~k;0n^IOnFX>Isc?q3ev(WMyU$*!WT}Q^WzhhURPMMzh|JzBVjlDF z?9I1@QO657MasraGaaQKSsg}jS+k!)6|ym1k-Oui{rm}x@fJ@gzZdGdU${zn^5E$C zNDjqXopPlN0TM?5T?GsHMhFGaz7(EZkRfhk!%}s)KHGzagD>!!g8s#K6>YM>LPP!2 z00NMea41$07vfeN2N^6d+*DNTL)l*&h%cE{sn^ShySxEoWEkyyqHKnbjl( z(28qTR_DMOUB7j21|*OFH1$Z~5)pN53AiCuF}dHqWvl0r&Qr@QWG4BueDSJ7=fizi z>)^6SCCB;4=OFPdQN2R;U;wqY+L07j95vVY`Z|02eX)l$G|vQ5y_09hwQ0|&Rb79! zwD@ps2~c=y1A&B0K7gs<7rMyu6acMEmO-T7$pjP=jgW5Z>0e&5oWbICCUSh@vYrBe zeGQPvXnw<6=~7WtQ0^Q%yLm;9(5d9MZj6&IbcacJY!(s_*==12*oQt_a9P?;7Kz=$ zB>Oor5l`lM{BV?W$YJd@s2Kqt$kx(p?Qi#o!Wil$ETTB0w$kU>Duj|!D-nsFRueX< z2H8sJM1t@1n*4Qjbm|ePnCxc=-%&-NHGcr{X#=i7T=t@FUkT7O0PnFgiHWn+iZWcY zBY#zbAHt!!knDO@!otx7o_%iiT9=`Viu@IdlAR~$Eg0DCkxS4d-0=-oqN%7G_+oMg*Kwd4a*e#r|fVn`OI8;sw zC7JE-_TQL^K8GZ{I$m?!XNT*_VxJ;G|6yQ~o2{5Ceut3D>L+fMB{8r4XUNNP$|9Jp z-}+SfG#|*QOQ33NYunpXWNNw#wMUh;=GW#`Xi+jTVc>*mfTHGQ9n=$&xCxg%{H1nF z7$A9;s5!keQsl;kg4A%dj}d;lb$_H}?^4=p@ayxqshYEyrL_*IhFuSqe}I?D(l?FK zZF$&uG!v!$)xhy4GC(1XrSav{zqQFrIP?U(7We?l>+byg0P)ywAoDVH$+~WKVfoqR z#kB_)!$F|dMnBPKfs{|9+~%#MmmI|ssu2ncC#TUuR^R?b4IV1Sddt8-m?oc7f1#Ki zu^f+kMyib3v?pT6ZLa0>4{uvLyN0HwnC=AL*Yh8VKnMU?i>`@DD+J+kwWPgbp-IIJ->#9_dR36IV_RGPP26_we9ZoBJ7dZ1DBk*^zRgGb_KqR@#VHSca@E z;MpLIQptItshLPEPwIJ+ehVgorHKc|nhFjjc`>yJc`+{nNP{d302$qBJqr;}0J{5i z!^VxoM5mjWnDV80fPoho9ndj+%l*|MGMG3020Bg@%B(aGWVoS_3Lp}CtHx%W?=m0` zP~2#_t^k{{j4{A9tD{=Exw)eicGR%(4Gj(6*~%HP*Kz9?bl)Rco_6^OQ<9=4VnE)c_=WMqP;wP*s2y zq_%PjSdg|DHfkdyvGUg^mUH7D_V8#FzMufzJ2wIJN@bJuJ>$M}+iO$L%ca{riu-Ukz{HxP$+sE0#kI30Ak-;#vB{(NaK&pMhWv?7sCqP-6uiJ2Aky-VbPY{Xn#LEzJ zF&5S~v4d5P-k>DQbcp+)N>DD-LfkWUL&_z)V!1M(e_G5{E#UuSknNF-9b*n_YGsa$ zcUA|D=bP_zCbCJS%AM@u1`u=jlL~n1pB9_mOTVJCWlGjS5j| zMX1E?kdcuQa#_YM>Lc9Ruy5UGk^y*W`D8pVoIx%0#vMYFUj$s9{9|LUeifB2?xst( z9X>C!5;#X7vXj2lq(|Hbpf?$W#p077Gb=dW@){}EeROAaxWRGH=t8YfW&W%$(^}j0 z;55Mxr+Hw0Si3Ebi&=Jhjr!rEM~{L?`7mx3n-2Qh>bE>L`?Y*8ENZIF&`gR%S~>uv zo;?$xAq5%FVeG)l5aMN}(;9$J%rq~mxm)B$?Dm>vEn?Y|d$ww5jI~IsHuzxo-Csgf zmjrn0aqi^>;EGQ!I&C}w>9YFbf z)A>|ozOq6W;R;PDx{f@d`8309)9&?_jS_&ArStZ>o)J7-H*$VMarNWBm#g`s6|7PQikmd zk_@lPS&A9}`V8H{p?a)4*BHDh(aZ?8tR2SeG4V%g8zpUo6bHg{b8OB)0;-pe%q=d8 z4_i$><|BS6N_Yn%9Vi)%fj9D%`7Ge(cmtg{`d|JA`7Z4{|B+AR-c8NT{T0d*k&J3T zD;?I_fW1c+)X!3pkOO!`BwZDPhGZ2Xw?^_ze;hu1an3{OJ@~BN?R5DH+WAPh+xG?& z*h$LH&fLE{5He4mJV9oZ$1=N%T^$Z(MELlGo}m};3eYriob*G;8p0PvfJDqpj{Ch<9{kTG#^}zzyDpRPnCH(T=bH`~ zv*<0f%8z9RsKb)hQfZ%-Q`TDgr)0hP`o!%EsRBlC`+7OtFcB8NOZ_})s>MXZKh zxGDSzdGO(vZ~x!@$D{)^IZ|W((#QuCB@Z^nN{kyaWRt433CfW7D~lu)-Q(_wv0nLz zGWnJ~h6HjfcWTnBac?Q%Q=2lE6&|C0fTx_~h=sb7y;0ft0#uM9k+|e5pid;I&4Y>^ ziQK*7FjpvUl?Q>mKjdcy$DZ&e-@HD5SiHjLIe#|o3iJjy!7r{LNj&&&-!1-mu^due$PoKN&;d5E>O9YXospGLgQbW zLgR&^Re!UdrAQwet|MCayac*b!!P_(Oft(J{e^8b3rZ$CpAY9uR6B$r2^J`y4oX2T zobS{_3WSp1+F(wa(Ti2+Zjc)rFWQ~Bo+;Dn|@kseN`8N0$}jGalNQR}|qy@R~L>70;M@dEu}rdzlS(WA+* zsKRgGT^}uR!+azW*pB21kv2u-pQ3W*`xWUo!50TLuod!D)Z5H9vd|yVKk$|!1-YKz zf&W2D(}hKZxA*qu0klMcOg2QD?zsU-aJYFBlb?6sqLZ>KJSw12)PDO`ReeZz?)?rD zjjZ)J?M+jB1k@MWlZ&g@mxs$RG0FH*W-btJP%8Lby?zU2d#(w%u2|%bMhiO7LDCaI z#uaJQW#J4uj>5A9Ru5hP-?>e#gJI~NO0L4wN23d|YaBy)n$qK(-$skv@Z=MZuh@X> z(lc8n#KC;%^=aLu36L9*=sl;NU{I?>(n5+yID~{!BOOur1cqm=MNCMGACF}>^0(Y5+U%2xGNb{6H%(H%P!3Q>at5$xxms1YbH*ImDnH(RpR8o_{P_=**Z}cCOw#Kd zu|1!vatcN4Um%=R=u~&MqzVn%gpYGxj<8$4g9tT{(c@f|H{0O6j+D_l4P-o*_P@{k z15z23?cGXgvD>#@4_BLnq$5+5vYip99I}KQX^QhyNKUJ_SK-z|N5}nPb7VAB&Lm!* zDxs&APe`#CO}YY+?i$cVbN*`_GP1I2byg%GEcwyb7IW)9{lz6d{R=onjy#MXO$x<}8lTgT4S&i0JVb!j_Ne;n6prS7{ z`r)j)Drd+wOd@chwWMipQ22u>A9k6fJ z=hY#^0b>nRQ>1&5nSCnYpr6q_i6~k#Y)A!syu8$Cjt|8hKT-u9JbIb{8q28aD0Ce`weq{xGfpWCS#gPLy#4tnhd8 z(}s`x>j!*ZT?ri=Wkls#PWof}1W6o!gU6g(CXEzsy<8}q%efr(nbOt<1fHSkQcy}{DQZ>hGJ^t`vW@d?Pa+^OPsMJ35*@d9a8SH=Jwi$hpu3e{6>NJ; zUHW}G-71Wo@Q1Ugt&jn~{!URBWo+g_Vu10eDFl z&vQk@Er1W7AJaS3K!uBgk6&MlK((oHjS#4EqF$w+RT4z0a@t6Fb+9VASq4>Fyn}6D zG$ZAM2XBC`>CIQ1H^8i@{%{?{0`KcQzhUL|=qHJ?7)Ha5_d6ep9o zPYv?<00KU$Ho@YO5=e-z&Nm0>?k&nXPgOJk%WpbRc^|Ix>P%mQPiHKTH)wOi*o@^C zyW?6$SX0r^Z}u*mw;WnpOqbqqI#Ne!7oos17}=hMyRxpkI2PE^Z=Ly3UQuB?)bs&k zV@#<^j^wLKcIY}Q9<9>*DRBGAn(D;F#M}G(27~Qhru|u1Wdi){tBxxZep&)Dsc6TG zZ-9Z^sLxQd`}!iMIZN>ohmnC=p#=}<)00i-n*lv7+kx6);9&qPhqNtaSDYk&7BC)j8F|^|< zQgQU(W{R58`s?A=Q6OO}>wz0$ik?zt-cpjs6f(&APd;5K`!V0gaChnX$f#jWXbYQ} zUz7Ng)Dpvb(6yCQf(e+D|H%{c+M)!l{FviXXy|6{=_GU?ib_ZTQSvFJF`fQKgw$=| z7r`&Sb5Hnim%JhV#%i_-U7(lzs(}{&e-{In71S*mUuz>HIeB?`mzP^F zzu$vb)nZNd$GyiUArZ5;-Wd z=S~ID#1wsfokgG>{cD&4%G{iupT9o^`P`kyte@aTI}XbCexvGWxsHj8i@S$Xgdai6 z5)J{uPTLt2DOc)+0lv++i25T~A`iRAto1H`d$^DR)(vSYb8%m2ZiR$kH8!PNpSr(+ zgcC?%3Ib&IeOwuc;wfXE3b3f2zH(A)>`K!`TqQGY5*|0npS@ zXakqB!uv6}z^IGx;~k>NMaP~bp_b`sZNyYxUx=W`se34bo0qpA2D(s`!K|geB4c?V zHU@}`VBJIBO|6)Q#}-^BtUk{PZTY1#UjhLieH2`kFS}y!mH*Wq0oP96U}|oTgNO4e zsPvF}g&B%%hF8VUQac+OuBof50|_V8CfJ_>(p=#09(5tx-djM;7sF}!>f`2;2Ova7 z3#}n=-1k$@koBZ<6+`fK1zrJ5WhoO`|3Fzb*ZuXUIx}oH?ht=YM!q7%Ed9P%fkL** z@Shstf0jRFR>p2=(RlAQ!-X3baNE0mh?SKU#cF5UgR|{z8&Lg9gXoo3CF^q~|KrD@ zP^s*mpV&cU3yO(}aUBN{1MpuGiud^W`2k^Kf!050ySrioTy>#wj~r;>t{`co*$6>Ui05|+1<|Q3cxw2(ER}3*Xf2UUECQke=yEa8Z?LknH$r3I z^0LYP{yva^EPz?A+9~GiJO%B3YTwlOcpTIXNCFpG-WKS$m<(jx0d7?GV2KKOf`$(4 zHfXphqT{Kkpa!88Il=+S+ML*J3c2{=B(kyWlHtCuH^w-C(#U$5M+)7wQ22)66A*+5d3mm{ zubWI&^N{d4{xU(P@v5r$Z{NN}cD%wWpaA)-R&MhUda4&;*DNM0BLK#Lay_dc3G7tY z*jNHy4?UuQpcKNe2V7t%jWU17Fky{UUK}?{C|b8*d{)oBLj!|kV;Pgu*n_peBaQ}3v27% zFIV6w$vN>mZBG0EYXRsa1;i2FsF>g%BvoK}UHL(l(-KU^=6-DBZ~_M(pUnsz=udLU zRIXgRVFQvyv!UFWg$vMRWC39VVoG#;mN(F-YEDrDewEc}Qv(c4I!Vyu7h7#LGnhcO zawZfcs%2IrhliK?TeS^enJ$Hbgm`4Yu77-Bz{0xj6hm*k8Ij6v8Cx__^*SxWEm#B5 z{ZHBr~|r#Qu6a|MwkWWc};vgg^kP3wJci z859QIIk^MV8+Klu9Rf)kEtgl9XgUAMm;Y@$exUfv`zg@2{P*#!)Xv_%ajbc) zxJl-Pq>PL~a{vLm^;A4`U=)>#_+a$q>xwAbN`Vp!Dq}$2CNqS?{qNK3Sz6wCqNDQw zxqJN1EXp5f#Qg95=Sf!V2%R zT;gM%$w+5tWceMHz*k|ja}|!4O7`wmWEWG3SD67C9HeCRBU0G+odNmrwkR$#_&qNw zYrq;#9X!i!pIGDVXaxruflk)h6?eX5oW9!6*FZdlHDX7xu7F~Z1|_dN-002t?Gbe~ zA+u-K6huDIK(pZAN9e}xZ@YAzAHoyWSkj$=L~6KBpcSE@q}*A}$;&YR6cp4vI5@bj z3WwGY)V%^AqzhhgqNk@PCLsYFw@XD1WDt5VnZjZu|7~9FsXvVH`~*JKFj`-HQ)BRY zJt$#8UtV>{g^x~f(Y+xY4jnm0wivhTVaVlZnKfBn%}@xi*s&$wv}$5WCo7@jU!Qjo z4j~AtwwV>LzS*i({~6g8*OpX=B60(hoRt;pjqr!tMXv|x-o2BGJAlbd5!rVsPfr~L z3D`b<9sHVoS4_09G4rLD?rBL)MoU<#W0;wYzVqAI7__qq{T!KlQ0o18gcuK>*ex9! zF3EXL?bt;&ov-E9sn)vl!00{FprD|YF~s>fx8r*BimkBlRoJd)p(z1Io%8zhfH3R>{1(=ddj7ot8mL}Bm_st_kLZflT!NKJhNM?O=d;&lKK zR}l2f`vmP+v&tJ7q^+nrJ3B`%(vp+Q^VhZTsJ^{=o!enGTn+2@ybZJ{QU#Dn05^cY zw~}dO3H)KkTGge++oQphJRWEn0i1dsP~hsJ8g@B*EgnHhu|iIh7^N=nhJ`Zqv23iTsW zQ#Q~8ILs9g!#o@>wbTa*1RS7`5C~^yW}ZaQ!LJa26yz8N8*;nsQoziaMWru zX4x7m`2@iR8NHyV>flKQVt+s$CfS|AVg3w={_FE&TO?}-eZ9lOO-&z=$RCLKpNWwl z%`Y$eLT{{q>ps$!+)sW-VUnZ*tqPfJyy-xO?e2mk1_^H?{J3rBX=gl779zkdU3sAy$K^x{<=;Zh z(R3z^k2{W{`2h`;S&-u>(4s;pA`^@)Q}e zU+dF#)4AUu?o}TQsY4Ry-PkCKoG1V+B(gcYGn&;XCzIW3A{tZ#$XE*mGd`={d(+#o zu$IUu2#ucG(8vVi2Y@DCuC0K%ls@3EVA=sc5S(wpTf>md*6m7d*A+^rfqrh!HV_E9 zJ7;WlOd^f6%E?;rc;*@;JKjmt-d?toIxH*}BP<|BZ2(aZ9B3{`K=dJb$Pk1Op$Y?e zNLzrBEhR)LciGDr)kiwTa-r@Y5E*XO-)64f9%+te@#Ke2lGy{UsWbp-R>m&*-EiN; zG7mlGBL&Xo&~kjF#*K?Z+r7`eT4VHb!^i;Iia{BX+!ADaan5t~+O?vCSE8a9gKd=2 zXo`y;URy8RX|ou4cls&qPZ)WIVZ=@d(ZhK;-DRL(Nm5FRl9H0Z%?-5(^P{M!XrcEH zo$@7j2R2SzCrnHxt!m$>s9_I1wP~kn;SaX}?HxNi1S*IW5|%3SDoB@L9B6xf7i8|9 zh|?I7s_|&++LMeGC+{Cmph!0v{yyokX9vA0usInm&@ulj*R+=G3?_E65mU3UuxJWG z`zjkk3sS-&RX8q2h^=!3Oqb>Qqzp2-B1fQBKVp#h|Jd#!ABTh$C1WlY5`yRiA-%?% z16vSaZ0DON&&0tyhA|;XUF1VxnjMSnPiKAs0RjP|PEA(=Hh$N#via;wm&x$h*O$zR z_<;n$56O44T&o0nN`C#rImLhd$^68{zO9E~k*-}1_&wx17VKFd`r;vFRp=Z);;?Ac9+8UT-*c1A3=9$GH5*6P5a^a{&TT) zc@Ye&IsYOIr0=jItuc{0oacMG0l6tsSpJ)|FngT`%2v(e2g3J`?nS`L`B@0 z7g&8fm1_0kql3yr|9`hTA~rTOn>^eLc`vcC?*e5P%bwoF!m5D8e|&trH9e^cuCT@b zc@8N}&<~y7_=pCuQ%Gd2w_&C>tNpUV`o;!K*;zao)-JO*BPS2F;~#2stFM1+<7S=+ z^9>EwQ~}+l1mZ5ED>j5rKGA-BzoM!Nne_A4jmI!wZr#q1j}!NM6B^E>8EYtAXABf+ zI68Wo)crln_b|g;v@b<$XXn6hD1ZRk5^;!$ffPeQfxOuOQRx6s{%5~1>aghO*32aL z&~L+Z@|f?x!f`2yF??aY1&X1~M%Sn*@S5>+UPYr6Q3))A?ITCD{Sm!CQnWG%SqF%#hjp$D5LpJEf&`v0#=V zXJ@c6+0cnshd5L(4T7HXhA%(7w?;GDZ(pjfAg5D6zgq<4+^=_jih*~?-u-!-z{(J& z6>>O&gg98piZV}H%WODXq}KgJW@B@6^Ssg=Psnn*`!{`5_VXN0WHJb9zKjhwQPJ0Q z|HVB1WY9-RN=r61kMT;+xi~wsnD$c?>BAX?65AL0VT{`F$;o#u@=HpdE5e*AUw#(v zAKMD_UlVR)W`a5gnxXbXnq3tD3_vG1wV{9a8;0vdGO7zGXu~|IDbKTns!}L>26A2O ziu5t#ppK>FKW6*(HxpiWqPb0h3YUJa6GKbposbq3uqSKVobA z1vF4Ygh+x(8_2GyB7BIu^B_1tHow}=iwld0uo4DlK-FJn2Uy+!giDiJSVWkSQn*0) z#|?-swCO{079NAYiwj^*-kF)krNPi{iz!e4Z(h!ytY5)!@ow)6BihGhnBVt^H&)lyP}$bxFeEut52SsLqi#dFhG3v_wR^wp1+Qzb>N2%%LJ3SWQFxK z5b*rF+~6NEOE}~mRSRB6h_&Ehxeym~z`ntw6{o$g56hg$JU$_zh}{Y@efWjYFW~oJ zrbKiJ39r2`OcIRqR|bvD2k@*_PFtJ@(Be%#G<3<}K#Y&?3pELpvz`6Kf4%dk{ zRv%x)M;{s)9W_>W$HF@KpLC2r`QIFVp=WMT0c=WKNeLTrakI)&*kQmqx`KXpC&V2e zI}gl*{yEyStg!GW1hA2j5r!_szx$pj2u1~|xRHwnAVrp_^yItvrskw@ie2=OJ?gO9 z`PN&4Ok!H(yZw|fd`c8_Qt8S#E=G{>A@L9XTexXDKi6hPm|J=|(Vs04)}6%H z!p!G{jqCUhW?C9bb|T9I(EcC`U#K7kO3pymwm!mY_ABeklfMZwvXeeT5b0PTz@h;5 zX|p-OGgfAeSr+xQw#bAMuu|z=WXmD4)b#%Ljf9i4l!BT%EH>7^FZ%GAZEh|)kSqG; z=AR-Wo<}Z0itLmrj7;C#`T+EUMr&S}#yATQNTi&^LJS@rUP}y{G16jCV8*d=>&(-8 z(4JsAV6?wJiUS#ebmmFt_TNi-8e<@nCcrwYs{x=i3dKfP(B!I!%W#YYT$H5oP<}zz}hI5YDKM z3&ki5B%d1jYsGrqFxaq|G8epv3&@z^!J}kh!4O6zrvqIEjpj`_A;eqz*~&7B>>(9N zJ;@TmiTkSqsPG}unFqg+Mcd?L0^kisZT(GfHXkxDpuYiRBd(;xVl)(f&EOLuh^ryO zz)AD<_fHY^!ww)|YqmHDZw@5F!NqL`43qXnEc>DK&fI`Hr78}u*&ve+IQ87)pdpK@P$VM1 z%6>%{Tv;~M$VhLro8~gM#0ZvIxv=jd{TjC>=q^?W9|1$c8|2ZegRDfv2R#{Pd(a7E zx%||u!W9eNoIKVU>8-+5YDJ8WOn6{P!`TEJ0AI+F;bopia`elBv&O>ikLZ7o8}hz7 z|5|?=7I;VJvu|8#MXn|3%2*)`><;bsE>8ED7nwDVRbR|IkHIktg@-PyJOmWg0<*zu zO|1CnBO?Z$YsmBS_Yrc|hfMnQs4|%=vR&3zU_5YwYOO^~`oP@~i~dRnqT1u#4u;?} zhE8?B6hNOWp{3TW!=huo^#r5tIXQXHdt==xY_NCK)Mas?l!4HcdL#T!21`9svIb-|}o%*363{)QtSd{-BAuZy6+ zg)C~9x_lDg3ca&~FZ+bPwGQ;?uxuZ2eKH-(Rf}x;RSjNN9=l5&LL5cg%3kCGgDVNR zXD20O+7;clV$A|Ot_dZ?Q=63sFzA9I!QYpk3ZzrSe4baSsWTb;GF_x0|DfpkS?vIm z^Yrxeps5g>^v7OCG76hn%7{rc?M+L`LvUoY+ZTj2P{Jo3b-(a_QP$FOCn^WMQKcmoZc zSR^EtE9`&Y4XAr!Fqwq*-<2rf{~P7S+y#v}VLnN|Jt4W?6+r*t?;zNr|JDl?L6i;^6$wcZ2}x;-k`fS*RuB-S5s?<@mKH?mZji3KAD#LB_pWta>-%Qb zID_x|Jij<+pB=)&OjP^B#82yLOJ4MW03#s&a7d=Oc;A&7EI)kh+mL3X5rMv(r9lN>=hELbw}Qu&dy+NbC&V;%6k_UI-OJnx8bP1@b}d7`3X=tjV=jo zsZskUU1-AKmq-2xN2+qNqcF57m7tApdidtvHNX(HpHnvuGN5H{{CJalW{~|$qU!YO zYGa+A!2CpG5U4wMj8X=cnCtw}=(n({3Yhg#04*#vJRziIVj^EdWeBvo%+I6%#b3H! z)^2L9mzdu4sO0h^IY=9A?eV2v3K9_Y&(9s@O+|}^LlSm~FQKd#Zpz>@YVm}@o?l={ zDL(S|Y|9Tfb1-x83gsy*p<{=wWE!}F>#MVMLNmv{#?mft*CH^6s@OQ@x^Fs^pbIfANfi%{AbMitM2IPcJ)=HSp2AZh{@FF zD+gx!9)Np%_j8F2$(_^_z}u3eAc-LvZW=#o(VuZh2)CepzzcK}-(j3te-sCbMDA z{P>46S90zQGxRTA4KW361tv5Prl-k|2zoBF^S;)+K(W5QvY#-&9JHHMjJ|aZhbve0 z;Hb%=##{-q+DK7(Gczv23d?DjF56<_{)o1|VO1cFQ*mYGqx!pV;q1V3|GS&@FGn&G z6n24Zs&eCkmTvq`)ULO)2j- ziVHUjBs&P>i2fhWZWRf8>CVp6|58^E6RKJ{+uZGc z{#`}HDI5LY>d|df3Wo*n;E?Yr9>aD|P8uh20(JUQLPiC*&~4D77NZVUx^SWTmB@)l zXySk)Uj(5*z;fg?UOyT`KHw`{lu;y@2q=s@Gvjbq05NHPVj~>Pve}kU6NTtoylkru z?kL0Vd|jMAGBB@IbM&~tA*t`~2R=GxQFf@u==^>q|NZlWE#aMxL=k?kDWT$dz=y-< zEOxA|%;!z=5(3axL94eKog&qtLR63HO1-017bf5|^jr-TDd-}XX6w@7s_X)+6O-Y# zg_TV}8k`q8S2{m2RxmNWJabIzR$+7{89Rw7W|x&)o9P7vf`N~x^yN$%bi6fikvOrw zPRF=U;fe35OB{DrMN1CS?52^!?ScR^3i||T;a>*0mRBv? zD*F0(yTxQnKr$8UX3de0!@tK{eON>Jh%v{?uTYUZ+Xu@{2A3RGIeFh-%P}mN5Vi$Z zx_UHRFzsHy6gCzC`P2z-;UNyjp_6=#EPYee2}8vdzxM)9Rd2j6N;7wC9-oj-o$L2cL`p(=h_8%HWopF13f zaUK=I$+Urq=-Kh>+%@t*x$}6>5yEX*`eV3}Bp*0A-B$bg?rIhn(Yz8iz)Z^9AAuCz z4bldC!Pr*KUmaO5*^hmVSKden?9qtY3iiWr-~qK9z1=7sNbtkzjSSPr0E@t~~ zK-Zd0<-WybWm{5u2O}X2IFWeklkMxn<3FsskO&n^d+-rwv?x6jlRIv@EmPg3MelC) zN75d}28PX27A1{50J3T=hous#Sb%m83*(vSfVQL{iwUjBmGxEQBLn6mO;?yz67DjK zG2MP!K2Bkiz_d@{YZE&?ed))vonPL;W(}49mBv^S;_^8q>HX@t0*Q;$?~}j~a7Ro& zo~U^WgV^1$`UZz6fCL96*Mpv|=@ipYxYU7$Dl5TA(ikg8hxt8t|0H{@xxF!{P|UO4 z4WLB8VdUVoh!533ZyUUp8l-mzk*-LJ`l#<-fA=)LziMU=pxb}w`kjcG6%2*zFW+O>AW2dp%wr~gyqH!F)zW&)_*aW(XO{VM$ zk0PCxD-zHtj)GtuoYr3{{4LK$0XnJJuWy~!9xQS|{~cR~sX$RpP0g4{S!oHh>W_VV zC`brr=&|CR`L{p1xp_FHkHzg@ZfSwc!GP2?PAL4bAc+RCkEE?&oqCVo+e|7k#c<0?_}VE?=Z ze{gFcxejhz{4Q|~e^Gjz+w}S*s*-}uu358#%JE8cA}ha@0YwQ+t!#>|3%hXna@+kn zUIBq4Kxc4T)I1lS5lZeF{*MdLS26pA=E-UL(`H;Oo`p|IV+@60_D#~suIgf#Z*LFw z9WhzjQm|1=5!#TP>q~hnuLEpsy|lg`B{GNky!{!9rhv6hXG&Ku_nVnswoZzUK7gKY z+doW9JL|L}hL`#I`Qb5ZxuUpC@oTq>YjV1rl+@R%svl#oJUqw%*Tt6Aw?-42Wmd=} zPG|#ffdqh>a^eWt)prVyXDFG!O$4aiNSPudzrHme^%*jfV`uNbe&8RAt0-mIl}5(lsnoXAd;QEP?1@pQ`}1t6og4cX+#*t)uAll` z=Sq1gx{dTGydu9A7CsJ4T+iGMX*Vwl*}dpa~xN88z(pULDZIRbnl52>6-rO zUI$TeK@e$Rtihq`9Bboo===B8WWAM44M#f2>fsn&YY~}w{}*OUnFiNK+HkVCJ4KfcGAW&wUO&+{tb^H|lvLAMU?)C>1$9|y zdp>gg+v=BJ{BNhUd1c!gKX`K{5U3`JjG@!N25 z>(`Zn3$lB(RrL3(BGJ_sQ2uf{6^h+!c^7;qauXakIc{*86mA|VXd-!p_CXiXEX2cV zGZ$e%&z5az6GY?qeerxR7WAyrn(gV1?2@+?{=vffdz^Ps!J*N6>_-mOqgKI>3}?>< z;vnME9pRCC7XA;<@9!5idY|4{UR#ifJR9(7yq)-C1#?>-qt1#QwLVGv#Dt1{z+YZe zyRyk?*Ikg`#fq0^NsfzHxCN;I3W*wFFThXP_0ThOPI4-JIUgi$XnTMhuyG z44Ph~>(@>V>uWFB|2jwXD$Dc3H9+l1h8lCIgsfJpU1=POA%HM|w?(Aqks72B`geae zu^1_?6J0BYF#>!i%{zq8!KPoGej@+ePfr|l?{;94!?~<~=HoN+%q{=k(Y`t9TqEqk zw6tcVAC9kEiUM?UJtyZ4*rd1zLwy{ZJ-Gd1wIjVwRDBp1>gvDQ6qO|`m-=8jSa9QJ za3*e~%W`tH&lPY!zPN8>ge6+#ccZTI4?N`>CSr1>HmY?WemsLw%_(B9Yi;`RX+vDe zj0>g8Nj5g`*RLCL*W~o`SB%|bl>LvF-sO3Z&sqG$&tEwuHMRMW@V^HH#pVAGGDe!{ zM!OkID#9+d>xCu;2Pl*70LQZDzYi%C5F=Y3uj}Gj1<2jR!b_t+33&1}>JS307pzU^ zFmZP)Z~PbJvU~=R>k3iO01hlDY|5RpPymd!D(%d;V2z?CWL;dGrHXK)&*IuRI)C#O zfz1s2HV(2@-25#F>}WzK#bWc{59*hSvw`vr_yQ!r4e|HVasN(DX2gjfO8mOKCm`fK zs|_%xUH?t9+|C{T?{E7QAVc_rTNz7BEC2hSxRpOmN&Wk02wcIL6c2iXw&+TAL%f^Y zT~+FRzQ@JuD*k+eBSPlOJdmnDAL4NPd9HY0j5MLH0y|hbj((=v)XXdjCDy}(ESC$P zY>U4d&jr_(;=1I|5;Lh>BWfs1ItqbxS2hTAcnl=QU8H7^l#Vl15w^`Qd*>rs%BrC* z9Y;GO14(XO(~|s3E+tK}=y!bE)85{4SP}-2f1Zuszt5(p4Q%F$>LZPaaoCNU?~{cf ztr=Cx+L}T8$0M*;DgIg1o9gv}Xb(C48#^`c{jwF*`Ls7ieQg<5B-r`pBRYqX47#~% zuC?<^TS+iZ=HNM}L~Th-QG(A}GLxvQtMeE)JUeF-mgOiM0^2*&{uy0Ci-%qGXfK`N7K5)1nFFhR@b zlvP1MCZzpiYf{qFwd*E+0v#!0$vnh*t4HH&UM`-Mknjcp#e}fmah(w_dSqIXX0B ztdHr%_s<60D8z4raQmZ-;E*V;l!TB|3u1;4N?dq*sLy?=KP5?=I=Yox(~XO^W7hy2 zc#F{>raI7vE)(CK8SVH0t7==OHM?Vp2c|eu>#Ivkzx_EaChPq_hw?qB2&Ez=B~^Eb zIU#z#W5<`=C9=Cel1|{|mbR2`H=&`q?ef6JNri^ zH1^#Vbc6r!bj7QBD7yAP!i)0t^OJ!zM|Lfg*U$w8qI8fW#d&CsFJHOxMl37ss?EB< zdzBBBn3pk99u$vM&wC}PyX(r8D^2gNdrka=4&JF-45-%mwl1!hyV;7&1DHXft-&E( zW0v!vcl)OGlIgj5ts<8>;?iOAXHevNy;b*Vl_?N33?z4Fswf)Z>mOk7jKiO{HHfJu zOdx&do}D&*cBf|ER#clm{91!Qalhiz-Bo~bBIqoxJ$AgVY zEF$r~)YsoDk8plY{}JcP2YDvpRdNdYNr)-45*( ziOe(vhubn2WOfTZc=E(~*Cx15okyj%X%pS_+}t)z&5d;n+wly^e6015>T^{8eV@zo zpkj4p>=_RhF?#>MNsM6LJgR-KgxG%}pAPQn?pattOwG+|m$4U^Emz+pg|yg-Lid8aYe;WhPVyZ zx`?o_`^!J?@_^Ce?z7SP>6Ae!&$?RkJe)H;kF0T8jXcYWnm3>anpGUwp@LpbkKcwBY##!VhS~JlKaCyEb9Zu;G-l5 z3m-<6{NpD1b7w6_o1^jOF%d!SFNO3Hao|H!vuC_9BZn6nXE+VEa>jdhMEq>4RC#wk zM@9Wk;a7H!8%Gu=a_ELjrDbHccJ=z65_mYInLYpZ?OVzGK=%Xm98SN^jWi`nLm2$9 zw}vlXE$6K6#V0fZvE}}6zl~Eq!6*9j>-69eQDc^>Al7ss_aGn@;LuSOX5KR?I>{kY-M!zMHhh`yoW%H?T^h^^orezMs0?Z^;4%y+O2Ex8NeguUf`Nm=xz2g41WaK0nMY}RUW zdG{m40HJOf7S$vnkQGcdS#GBg5V=;$*%YTq5ul?(_&ty!v{A$PSWpmLr6JBpg*ap3 zKo)gRFsLUNY$KbJmvo$esYO0&?vF?ymng(Qg=QqTU}HUWPjY_cTc$si zEzXNDK^s4GnfoX$o0}%#72cmLy7Bf=j$QVe^vdsv60mfarlNgPGBXEnT|M}3k+|i{ z$h8l|X(c>UQ@oD)r>ujLvho$>P0!E8RKDCnh(VoyE-RX>+)pU*CBQ+Ng^}mtWwD)Kgt;)Zfr)jHMZ>hnc+>{!3$E z-tgdLY5X&p77<|^^}`J!V{TF1!B>P~H8H(p6L9$D7bcN>DuNo}Fk6H6^Z>W|^H{k- zB=9~?Mh6~iu(xixpC_?fsFfgl#VDj=<}<5m2EiXCR)|Udx?okW$K`=65GoVx7D$J{ zQTY4Ir*Y6+@52s>z9W7(Fx)((BAzDqM$CDrKHBxq zRMhu=pr0Q~fz8v?4=8^7;~bZgdLGkAkTYyzon2kc`^ss2?AK9W?6aly9C~$3TtY*? zbG_5xJ9gZ_T>~(o6K-dOBvUzsj%x?7s(5>e-lv_Zyr!jPV#e09xlzBmI^{;Roe%YK z3ZW|&)w|g=ytID0c!5QsXXkxn_-7o2Z%}eadL8nc+Gcvs9%5O2f&)n@SzS0^BinPQ zKfSK)DyE;q%{kVwy*H%F&l7RuWCw{dR-{tXpVd{Zp3)g0v#oUh{uOhh>(3o@m-RhP<9G%nPI43#u znUlh`4BM3;VWWvEN_lgUV{<3VdZ3`?sGdPYpv6cNQ>o+pkw^zFdeyRc@b^M9@6)}r zFv!1=^62DKQsPuYM8KC=R@Iv4ioeDioYC)TV_ufKp)M~%&HEgR)BF`jgHHpmj+o>w z9l}8zeg87Y>_DwYdFoyB2TU4!+3kJx5Mll-&lLPYfIvfsos{vm#};Vht59l?mqbKanBt?Xan*| z2NpUPl;3DcAP~6-oHT>h2};zYOG|O%gvA2v*a)9U^mpincRNK0lW9I*0UuK_*;shj z=N$8)u1#l6e#u)L2I~1=f=2)Gm3}AN+2zU&va3X8X9Vi*# z+b%S~7|~I8cRK~QV_8-5c@*>)`G`c>Wg45>eRxfiY1}_2cS3e;|9)dmgPDU;L2Nbk zLP<+#{IOqoWQI?9>r`waa0-7iZVNGP4tpNJ zOqh3(Z#M*e(!6za^U$|$D6#Tzlrs33Q{z*VVK%oz*vhl`+13VQsu&gc&$VZaB3NWF zch$_WV`gv=bHgR8X)QcG*=bn+-fjHEN=?0u=ujpDS+WKf(e@K4Klq+XsY(*Cq$tOy zbI-MKUtJ>mmUc}V1|Bx z1FY^(#y{`fv&(R>Lqw0x4({V zlG-V!^;L2(0?_^t!UOby}SETDNP>cDTzzzH6b^&z});CPF zX;B=%HAvU%!<6o2pN`bFZN%KXyss{g~{4IV8Es` z#BnRrzym=3ZLjBA*q8hQf`kh*vE!$|ZUdyWh*7UWTU1v3-nZ-0?5TI_Ti?dU?x&=b z=!U$7!*s9L(cI#;9}@IShdKHcO zCAC>rMFclihbCJ`-M-A>U^^!jH0np!*)ut4xP0?>vkHg}!OJ2VF}w0se(N^8b$`h? zAsPS0kLV3isYEqbC35FF;!p*ab)&#o&1D??Xm1pn0g-mKV-MTR`VX&FFx=l5)>j|> z?v9!A-UA2x?|62-*b5waZnQZDcM{#I$81$?cy4D-=1oz!hP}^Ia2Cu^e|s-r5Y6Tr z!^{cOoV|UGsoHHh*6bG^(=jOvx|MnzRf;$3CokJN)cAo4^W-g>tDhebtuu{NN64K{ z3>c>y=Z&tuSBPM)6D45~w4kd?Q+=|@w;^Wp&MEd5`oCNestq6nbbnKVxB%k ztbDmY1BJuN9O-Kb$ZlFvPpvJ_zqzV;Me5*!HrANM=&x{K7@9|a00pU?%)RI}(#&}5 zwhP#o4H+w<3m*-7FFm6cD%EgWzv6YTd2u~wa>9 z_IJ?A?|fQFgmNz0PWO=7ZXt$2@}KMyUHnYXJf0hE>BfK=7pr!iLw<8mpw))hOGprb z7Ke0$8sR}b{D;XMq3fg{wO4my(EMSRE*YDo`ao~J_V;uXQJA6k1gK~@4BY@cfc5ru zoTAu=hN2&v0~GbGkDik5!OOfzt*@Ux8_2G){XBT5$ldHhp_}m8VXRJcqYuQV*v-KN zH31+7v`MNY_G_!aKMLF0j-U)yt_Dt!j$KY#nPcm9l%U?&n2YMq4mj1uYZQopkD=c; z)g9~jzu__0udnTeg%P;kup7AVZ!CPCbxP{2YYoCJuh>z*56QyU+KGuX0$BD}-~TS?2_r*zz0nUJ5*%e|u%>i%Z(wdRjt0 zOs7rsNav&{t}jl#c3hp7B>smH>qeVci~}j*#(&*z|?VmxhXtM=D3gcP;WD`vf1}fRCwQ$(pz#24;vW$a>gf8_h&5}EKnRY^ z1}k7%b-SA50))neoV6^Z(%!1wCeg4+7fItO9YvarA>?Cs&9J@!5Iqd}t!W>Uf44D|G7 zedYU5%HX%lT3cJ2P;&`BdHNKCWFmbKP?o0V>*0%D&~sp^i-Cc+$<5n)z?H`FHMjt_ ziNpZJEHp8Z6*OGJ@*mF&+=H8W_b5N8Bx(U2o0%7yd<8i9=6S97YZjf8&7s|(2O+S| zYIXGh!N9kjJ1P?{vgX@U6DAzmVKeg$w(sd;ZbPH>z@O@u!ZRDg%|S~b*G97kAkz2Y#1z7acrvogDM^=Z9D;VUxh^>Hq3Qc|{5b z3~62_jjv{zQv@_N5*L2?M!I#UqT+20lCpvv3x$J}s-BGZ#yAh1XL*#ja7Z>GHRyW| z=UJZ8Uv?^Aj#AoD-3Pz;H58sV6kae}E-biai|$j{ySIKtK)>$JU`>a$zEy{s^+YZM zTovgY92|&mYAMf-lndW3Qaz-i$ANVZ=i#u?2z9^?GHlg^#|o`)$~OgAy}@xo!lk$~ z4ep`aw}XB!l|BZ95Yn1w_=`ELjW797hikUul-5T9b2C+I8ziYZ?H>&iZl+&qbcE0q z2yK)N1eBgnpI&@Ekw!SnJX=(Ux5vvL)|2R0UT&@0O->Gr{FC(b+aFD(@bMjApSpOm zM$jtbo_^)k@n*xT@Fwj8tvjQ$hnoNVqXN;EmS&}lPFx(4iPvGNVqU|A zguGk%3l8T_VXu3IoIQ(Zjd~Wt`m`9m;+HS8;q?nqSy2;@$&*&bA>F-jm>7WQ z4vCLvLMY&!nU_pxU0pAdQkWr-iCT2NXk%J?m0DDeXX~52w7oifX9vo}oEP6HoZLmZ z?CVG86mB8VH)x0Hp=)sP&Fle2!g6IgF&lpS4Bj+x zCIybr-ads&8io_nnG?bs+}zRD$q^)oVIUF-V66KD7CYd(ksmqh2_gx5#B3I~KG?1& zo#Z1tgW?zh&RiSF>~xa1JpSSRwIjT?JQUO&XfwRQb}al6|M;x&jk+W?p(AItw%Ua) zaE^8cKXY~=Xu-EKjB89z2$}sTV^FMy-;?daNJXvMMz`4j3wr;r0B2F85GkOL|GxrT zB4jR8&?p996yPsy4DWvr~xsVfo(8n>RyLSJYDL zy}0;NR${9uZXCJra~0)^(eT?SY;6siKhf?F2HRDdvbt0d6T%k8^#_wSZsh3k5@f)x z*P;hx9sKvhYgapD%|c5nN&uqs(l)PPeuMo!)FQjVX=}WBM|2`pN2WKRaY=^`-%t(?;d|C1t((d{>p1O zs3>BTDxxJRh8tqIgG9Kz8e9}9d&B5V#(Qe7E zwA`g%q_-{ov9irKSu;Pw#YMa3M|dpixwu?1XkWKm@37ss_{8{UHkawQeuF>vb3L5W1X)TPXS4FlNZ3VQ(^we{T+A+? z{M6|(cFBe5hO|yB7|&^CHZ+anah!H73Dt6v_C;T5X&XQ0asA}_yI_fH17A*OQP{Tl zh>D6_PMBQgD=d0V{E_5Gc^DP4*=GrLFTElJ-LzxE8 z;&+LOvO0YmPLFmYMlZ7irnLCEmsue)G3&2BDZ$wDfkByY4gT}biT~s3@Np(X_^k_0 zUWj81ya}Nm_DVc}2$+|3=D+@aPaLKGVt)e=|9%#67qUHj z=s~@~uA@A0BAlm;jGP?AT~ZQj>xHdGiMKMM;9UR(7!iyKv&)A_ZxUVmeE=RZJ1@x_ zG`!s@A|e8Up#sF=pgliB?hXl|pMwW8ZfX}=iRa%w8L473G=#y@^UnjYuX?-pucPtb z=i#d-6@xgUfcth2ZydIn?LYO;KP24T>GH)5%Q{X-a`@8Lw&afsLB$92VY(klA3jud z6+ZR9&&|W5vpChgGce|@{J#%&F@lL(Xw(1eDT}wnC5EpGo7`%@g)+$#a)lXMju&6` zoJIGILdEm%Lphl*$Myfo45h94m|=;-M%wJ^K~%(un+lKO$EP$J|0w^HNDL)DIjwoQ z+y8z7v4$+(#Krk2EiH)(AUp*2S&h7LKB!zuB;&^auC;jUjlzpqf2kSi(oHSVWi-zu z#I;V0Jw7Wku!y1UN<|n>-SGQ~vGvpXDW9 zN?4%M12Ons=YyG{zLg+MuWA3c%!>J>VwCd2X-|q&80=RPi@zF$NZ-IftB?s(gk4NK zfEjf`QZlyn&u3fw_0h0(k4C2*ZYSvlYA23+eU}k5uAJ+Ds>lF#pvPL0lCccPa&&koZ^J-0Xa(Nb(yL=2rJRBYFE#NIlXK>s9JWEirK}i#T#IzU� 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%0DX3jd${s;MuCd>)6fCE=Y24bX0S2 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 pnBSO;`7vu$!whGst&?l^er{{SSgM$iBN literal 0 HcmV?d00001 diff --git a/apps/web/design/zopu-design-mobile.png b/apps/web/design/zopu-design-mobile.png new file mode 100644 index 0000000000000000000000000000000000000000..0b95a4235f9f5d24d1b49f4568f5dec972fa6734 GIT binary patch literal 41429 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

@P#dcBcg0g%4TvZ=ZCo9!>4-oE$f^@9S=%JD6E< z{b07>WT+@VRd?Bx28nj*>tGn$;r2T#&5BKA=A#|RqROj_oYV)k-0zMg?_G~f%^Apt~+tWeQ~}CJ|D_9hEyCWo6Z77-1SB)30QrSx8T2B zG(X>p9e7#Gmf*guf#A3qru;QwuEF^@8AvyN74*32u-OG7frl(>u5cXo>vt61RtB1n zGVBs)acDR}q{wJ(Xdl5k%N%k3TpjOyIZmK%h=He|_$Vmb8Lbu?hfb8))XoM8L}_ze zpswjuUl^T$ZK{um&^IyovNX`GIw)Hpp&r&K6PJGDa?ovq>w}e-OSO)vZeUGQJ5yA#X!| zND{h^fc58;N(ty9?}pKS_+@OTgGJ2;UhLz#Mf>WYGT>bW;Cv4?I`M7XWZFXpbz;PB z?%3#-x2^nG$evkMQOdXV9atL8xvjm`bRBeU@B&+{Yy( zFKvM6_%`rO{z|ZF$eR*1_enS2h-3+WhK7kQB$wyWk0MoGN1$OPsyC81SBoZ+wT`h( zfLaQS>RQ9Mc|C$6HT7i_%09U-xcChkj7LUjF#`NJi`2qjXLBJ>3@xEJwd~X2ygU@w z;?cX`tdsEzRJ+Vfh? zZt+`41Lbj-07k{|jo5R}#i}(NQUI+>2>%jCqee7E&$06>skl6V`KV8a7sHqOTw14J zXbJq(HscYJgp*Y3dnGR8Pu$Xw=**=x>-?y#fGOntz1*X^-4In~(ddgS_QNz7L?^V! z7kL2LF|hqqpD4v=(?Z~Prk9@Kj)FnK*$~jB?Bk!r29Xo1c;XGK4|ZK5cP4ue-;odV z`#PU-T$W>f%g-{RR@}p>tOk0<9MFW6h%atE#ike{Y}R@9jAEI5c3c>6VmQt4cpqH!2v}==<9Ce_M(>7rju@?kshb$A%D8Ta+y4Va z-dxr*+8P(c&iEq+6_p(IR!Z`~ZkHfbfzteEz@Y_uDlv;!GS4-ndVHvK3)F){eCSmf>|L>0@4_an!Exs6C)8gsv4 zjCh4$VziM*GuVtGGzHADe+|bg#7-(PHlW^bs%kU;aIpmKfUrn6;562)*gf%72^vnZ zEQ5jTr%IiV(J|C`h*_VL+D}jDhono9>y2flgBTAB^`UTQEiY<4{^`tgF$7^3&BS1} z{23d&?E7^X>yj18-SSo3?_yBQ4ebc!%Ntl^dsssi?nbazMf)(4W!sdH6RR4D|q z;rMUWj$nPM3YmX~a}}-Ggv~;4pBRHKLYR8AA*N(Sf{%+MS7u73wRDF>Vj25oqWZWc_FHvPp!J-TN zXWZ%p6Hvlm9Zt(HMWCX_Gizfy4RB%)%;vXT&*HaFq`6ZiqX}#-Q-MTF zcWOS}R>!pR(pO2UQ|YrML$Ihzbz6=(Q(yRz!29GxF*f8>;&(rQ&lY+}Kz8kT#&?ci zF#zOdfn()(ijyhGdX?yJr_M-r=FTD`=$ZjE#cW}FK-DEJ_XD99S)qT>J`BM`M%pla zJCdkNLLH{OlT11$iW(pB?`K<3t0R)2VrjgNIkC^GE2U~ZnYA{((PFx!J+UYH)O7~8t?j%MHe4UEzVc$$YPh}P!ED38rd`^ z*5Q^NGbOF-UfbYLsZu*s>O*k5Lk7dey|%Qmp*eyu@PQ*QsOd1Ye;_4GDfMi#z zC#lO&(4quOdm!gyNNG{J)iUKL49Pea{Fw7%kwKG2=gNZSL7AHrTKY6q!AI%|-As9T z!TGU7?^>!Qc1Iq{u^icR5TbBRaqAh^d=oc)sgAi_01iT82_`&T$HjsIY5NKM>Ys05 zz^ea-93a5-!wp7(j{5Enu%>WF^7VgqjZPwSTU&HwjPi$%jc|H$*S?J3r~X{Px*%Ji zysNQa^Hw^REg=pV8K-$-PKvsL6Ek(Q`$72ym8z%4XL+&q6Y<~zWMU?M2%#83h6BvK zC@d~`mVvv~#!%uw{Nv|7kQ>eZdaSWU5D!2*nxNR=OLJNK!kFfWV7LlFn%1^14qqbi zSgnUzniFd$nGfhSc^8yKUq!pUvCDRPtcHHtPH)7UNO2;e;MV=n5AOXz=+?9zyfaXg z`oY8DVE)ll>b^FxmQ+4C*SEw>ZI*V`Q3{!1M`3+=|5ze@VZuR)=Vz~H2f~=3*!u?p z+W+u*=|c_IM+2eto(QiBd0YQ(bX@NtfNNt!4$S2F2zVQ7d-FxKHxjVXaPDC2L$FxN zJSoCuj2yLIi_5#D%b*2KUVRj3Zj^TLl35J3jWyc}rqu)(+P#i#$%@>zMQq zWC;>Ix}deMO+bC@(PvaVjb{tL09%a~RtS7#Q!~ z34N!q3#J`0gP(O-?Y_3#uV85F=VE}@ZiHXrekW4w0QcI1>x7YvgpdI4<}wH7N;9HrgdzNl!Bo5{$c*aaIE zg7K$0<1qcAv3pU=7btP#>hnDae&chnZ>0>8U_|wmE?@3aS`ld+h!zdInp`l-*k8EZ zpqLU25SHCSzPpD?n(4nRL8=X(>NV_D(!w{c*wb0!5enUh&u>*BZYa}o>pGW`YK@aT z)~4bC1m=xfXjF6fU<)NxG_iWd2EpI!=8uzZ??1<}UOZ|Ryq(jf_Vy9<$+^YNQZjpg zp`W659mQZrGrmk+ICek&)@lnODtGF8wWcynTt-S6vAx94?jxD zxE3Md)%q8L*J@lO_*|Bd`un1SyudxbC2)~*jvmjNfEsb24`1*myrFTEOYkKybcD@~ z+EWM{RwSQkJW%G~>}}9Hy*Mb_oq{5qogsK6kZgVx)WjT zQyNbEouV~RKWa0lO$VwKhJ)-JB(Y6VP5YLS;-N=tcQ>%{_5D60g7XPNOIC;wr5SsY zd$fueoSHwJ#}iW)6-sv>kFP+_;b+*UC)yclYG1DHys~H8#OW&YgCz`L8UBr&`TpW% zS6rj)NnR!f(Ge&l)YV+KB`5KR3P~U`Vq7ub)jpf;PnxBxp+1jIjm%W4HpMh&QwlId z=cWqd;1Cm`tgsM6qR_kCh>r-wgnm*WbV$lI{9teROdVSdo-KbRbnv>6J8Y@9F4P*-)TnzIGiv8)Fbb5>2GvaVYl>|l&J4gIM7 z4d2$a-HIQ-pkJ%1`ed`ia6BCXwqsK@%TQaPK3b$7BBthZs@86X|K`BUN<`1x%%-oH zs<*~Ejc~!F%f64$1&?yQG93t6B#owEZY2++Ml5qNclmDmBu*+FL&ci*b< zC1QUtia=_y7)^BXcW$Hpoy;oSx~2*z9hQA6>hE00JZD(@_a+o;v?Sk}m&d$uz@ z4`sJ6V+OmWaUM_c768?)qo%NmX0zD&^}nei)~$AX4f~k#b?jVi5?Bo{+CF?aJ{WT0 zJ@rZRvmr3sV=Y1*0=f8Gp%GWhfb960<6VH2>XtZnH9L8U{d-XJ9!_5NEn`AM9s5vA zH3O$fIYIjK(+lMV=0>N63f=&x@ph1D5H#IJ&EHL@5MC%HihVJaspP(x-$@9Z``!xz z`9(tw2@%F!`RZSPHpaV9xaTb?BWXJHhw7+7w}N&A^y82=N3~6BeB^DLDei1I)Qx$& zRQ=E-w3w|b9#4TQ%jr0-(INNPdT(El-p#icw~Z|f)2JH*aK5(2u#go$fInEFdKNjp z+dzM}JUpANR~L#4%3ds^oL94YodCI8;-vGlyqrm0+;j(H9k;xLrE)Rm1Vbv?StgTO z7gJK{YJYe}9Zp-9`ZU}Rfe6yHk%s)Jpb%4Fybxyjuf;jiK;40$CE*u}+1a%Mywf%f zTiAm~gyQI2iHu(-mOkxLX^;fnZ4rY_&Yo|Vs~#-DHejJaF9Pg7l?xfb*zgE@LLai& z3SO9s7v-&4>v+WcRTL`?=Xf$3g42htWfRPpU7Ue!rAHV1tA0~f7}dI~Vn*6b;Ip%x ztx1xzLQY77S%;Gk^3hu(_o9VX%%CONW6^_R?@zF-+I7;FZV9KA?3yfY4a5%~H_Y=! zz$2glfybQH8#y~Xp}dPbqt2Np+6CEWxXM2Iv-P*basd>A=#y5nfaE-dI`-|5VgTg* zs#~c33n?!~jU6yX3{o!5=K_dQ>YIWVIk%GkG`R?=6`kK22kRB~t>gk=ns|MUxNE)f zG5X3)TD_oRtF3TI*J>B8RCXM=RH@AiB$Ztq(;|fU%c~j0u&eOi6guwYl#C#)0CMZK zlB8=bYvg-m+@*g96p9`1A`igZGA^ny7oYXUbGC5+HWQz{ZUhkc{i@;SJ(_yfkoX?~ z?&GBzrzX3z&R=$<2nNhI+6Dh0)4o=iPZ@T=8MA{&-RA)1K$)`V6~$|g%TOEV6{WBv zD-OzzhjE5~`s@r8v&Mz(!DHF?fSWEnY9Ud(yRzm`dW~clEG&c~yGVMVO4$LypvHX(yWFB5LSm!iwqwEu@jbR$pa@WVHa(&rQizJ`Y6vhR2;DD=yDJ4F}Eh{5u zIXP_xe^T&?z61)Dw_}MST46l_-=V_t_32!QeiCrU+>{)C3pIyz2qERWQEe=r+%nZU2$oiGO23OV#B>;n>UY`_Y;A4$lztTzX0KjSKO*N?Tj!Np08A3sNmRN@91O;rvo$Fh;D z3X?4GhO8s`fKx?Ywk$-59Y_|VKZUOtRA+ii?*c-;-v^FG(!y4!XEO&528ZZm^A1&; z671Jafosd?E*y;QJt?yM=XRZrqowYbjnr)wpm30C0Lwy!3=Z6mv9ySFrzxgecKsg4 zR<yv?TX2`WZO)Ij$fYviI~A#o{5YfWw@1&PMkFjYMqJL{O})%;vZL23xOyX%0vFoU1r@&j4qR}6y5JRLu^r>6Q*WHnCfUM2}_vfiC9>znX(|$ z5JOn`e*xW!UIn~_hrfHYZ_cL1e=gm(qR|0xFU=3ORUcpsqx1ua*M!#SfQ;D-ZH>J? zOYc9AXT+yIgJ3({W3!GCNw-e%q~3|^RDy_-{I(Fr2;+QPHcHQeq!q!&#yN2dMpk=@ zub+NK#bqO(5__07y(~eqVqCpj*x}Gc!6jM^dKmruv$Zo3wGz=yHQ#=iD&WP8RW8X8t~T(BvGO_6HME_-OM zaj;xlPNM9MNuGBpTfn#gB%am;@5bj_6Nk^jRr)FI^-&jtrp zYbu+G$I^~GHr8DN2(KT+?wfeNs75GD6;kksLUV`*ppR*OOy9esMITPR9j*-_M;b&e zOCO>4a1o{o!zmvIRdZV}Cs!KBy}&V2YLR5f+v*vQ_KYgUlWQTQ#(mh392!d(6au%o|&(|QFRGa`Aibgvtel9ZN%%$v}9m5a_s)i8R>rACpUE zgl7`kRikZL!e+{a=i2RqW$m$-07^JCjbV#;QRzPKP1ljPZ>zK~sT4%H-BxY?wrV@0 zJF6h^uH}V(2+y`a`=p1TW8v~f4?WjH^#vY$-cr6^&Le0S0;C`$PO~fla?aPGl<-@r z*kKoh@z5>|BcNTB3XXPhDjwRy($UKvo{AFo2+2t^7=(wfza$AS_Q){O*`v0_GJ8xq z*xBP4M;Af9Fzc2P)x1!xEKBIn$Il%>=PdpA1WPgq6WCgcIY5jY^f@ zKtgvZf+vlHGJEp&P-ag#yj-gy!jJ|MdMb9UB=pqUzURKvCACnn2Jxw9*YZsaM!h`m;{!e? z0cm`|=OU^wf*dYkbGG+P0QkI8o|}Xl!sqi2^87MF^)ZC-vORuZAbn%Bb_6b8PDd^O zkyKeY2nKc24_rS;lz_C*#Zit@fl z*)v~I@C9XxQXPl|Kc_m```qM^a<%!{R8rOrswY`Exh#2-KHS8Bm3M7G$@#>rk(1!9@uDfL9|u5L}o{Xp8>*=(jSb5=B{FuODR(t*XJy*(JE<# zw4sy|^Ci%Ysv7pRkU0A%7{3UNJJhg|+|<`IiR9+BO6EF*Yf80(0u}Lgqe$w@Ae`0R z&E`^dNj}@K`Yty8S3IT7i?!2rZE&-hio{vefVIM%FUt9!{#e_`?5o>8=#GOXcFA~o zew$rXg6iu)wXOWjA5*JxD1E$l3BhJWBp{K(wpm*xFZ-5%Sx}ySnK(;)8>5@92fEa0 z9At36ff~V=c^$FHmq1U{wajk z%Ae?n?1)ev*q_p2b?k*{o`8i}mhOL7_tuYgaeb!-$Z)X;Q?YN77+SBuUK7R?h9+mn zdI+iJiN&-O>5drnqj)8fwU%Fn|C3k&J_IH&S@b>67gv0v8+m=?BlYls1)NZIjH%9F&kI?3=DlO zGkY`c6b@TC8Cs)KulUv+!g{A<`%?pi{o$)@jgRIaDwq4zaiI?^tpxZHMB-0%%ia#M zn~ngQ<^^fAUXu(0-NQDlppT<9BKMzDkBlZZ{po%j`7`byUhdp=4N;_;|ufg>59J-&TwAhlHUy;Wo zwB_!Y7dv;J4I$iPDd=?p?$%mS5Tn<7B+j@~zwPkOkzU*s^dem;{JBZxSHSdo;nMCAQ$1z`t)jz_rReFwo<|J{5VEZTtIqj=4}{D zG23WS4$^w&d-&l3p*hw1E4ul~`L($=`;h#&2k|@5V}#Vhx*Np?6@d zCF{=u@4{LO7NCXRomy`ecn=mFC1=)0C4UrRBFwTqBmK65HQu07n#m(DD<_78kQFs% zd7-u_I?o2~bCr3dk(kv&9tI4=&8?q?lBgRD&)4rqpu? zveJ0fyJd>`g{6E$?#^2*i|9oFa$6tsaoAH3dVK0ud@VtC2}1$%zVo;xm(9}@gMc$v zFUpWodqjKbKA-Ye^69HmpC-DIB zu`BFCLPavpn7@IT(fWdYff0wrbwm8XstC0JFLX*q-L(+~&?<78i5we7#@1hqQGtOt z$Yw#n!SOe}iCI^-oEmS2FBXlD-Hx`cIJcy-&f$nVUK+JyZyv`av{xf@2f z$87<;G;OwCUF7)?sU%qZw5S59LQuUtT|FdGspZxwk!6#Rgg^v;M1m6I>Y03T4zee? z#3K)Svw^!l7?)T{$L1nsqj=L|2)EI-)9&C9f?%&X~ zwQ&-Vb=-CowYW`muePo(prU0x+HB4S{2uWDx0luV@b(BkOrfwIg~@3<4Vj}Itc)IU zy2(nZ)j{yQwSqeoA3g`NH-zNE7w$gS(BMrUo6P2t3{E|We((Ve7HJUI^A((4>UCFxrpmu zEH|0djx^^4I5A7UY_cKXU5!I!k;qRStTOJo5HN_GPRgZ_Z%_if7n&K~Y9QmbZ$4i? zXumD!l~iB{39Wu0KM&rj&hKibaS_5|McAxqsO7QNFcFxIeHId5QBgSngqnIMhXX*$ zhiKmHlY`*C3cWXuaaa=zR&!o(&WH8^&bVNiRtL_yV3}qI&b(lmb_dSBV3~#oIhUd3 z!I;a?^kBRQ(ZaHmD@;gY*%=*7CYO6G*B4baA>w?*K^ez94i>SAgUm-=o8?+)&I z+tcLs!Qqz#Wz-yg%@lEW1V2EtI$cdLK%cj)Q-5` zgEiJD^{y~OFiEiCNhaM8G4m61oN_au*N%ytZ%42>`14M-7BGaHdbY)bc;7$<{- znt{#f*RZl(C{P>f*S6nrX@MLy^uom*7nJ5rb2N@_!Ej3T$=?EX4Iaq_e{g2djDO-H zH9&+>NFIRVU1tVI!cP;WSgq0xf))9&?60=Lah|&*(j(D)SMCE-D;JQ~J2b&w9^u^p z^*X)pfjDocLElGn(;&HicWAB|Gg;i$yg?q>1`FrQc$fbGW5FBN5-1pQ_#X!sDT7sD zz;-xfu>I9zHa6-+APh-52Kr;PO%Y4oW%S&kIAGzb5}G=-&Ny}{BO%!UgdfPN{q)Fa z;93a1M#@y0YAM+igOSl{u@)OGhTLq2(E>q&+aAi$f3YTR!E@>4>dw%yJ zUk)L#i`;bG81KLj?$Hu!X|;PG+nhMz=R9U5Wa9Dd5vtA@iYkeel8Qt{N07jw9Rkg5 z{_HT6-5&F5OiUo=5D)?&+GqN<`O6MOaH zm&Eu)=tutwYmN{ZsnU zm8XnGOhf$K&IW9u%Nq-wlTZ>cKCQ%nBGb-~^%$5k+8J=u7=mX@(tB9*L+wq&lsV*<|j%axA(q-BPa@d{`Rp)q@Y;R$m=19}%bxbr=)nq4>o= z!E2P%cX7;Il0f7EIo%$axHHLJA=i(YM|qGQk*^&ykCq4!@OY5$v5!v>rOkit%9748|>5zFa(y?5`h$(!aJi-0avYXmKf+lycbi`eG1OnjT z0*LK|k&#pAaG`n_*B=WLSZj!(EXa7;M?-VZ&R|ZsJz7MkB-9zx5{(Yu^+ud3b3SHw zoV7RCSPc&?o|6>`KBohDfM30|qbA41C{AwY7Lz0^I9?yHRPB;sXNSE|j5P(20lOum zsxd2ghnlC{i`CkZ<>^J9j+WCZ5nE(#9Ewlb64+$b$-}JrLfX zE{KEmiZgE+gAy+NH1<1?ggC865GZ^+Ra*#N!95{PW=gB<{UI&@ve#;XBNu0d+^UQ8 zHzr|OtlRGRju!?wkA@INuGK~C+2w2#ovTiI?(LR0IKcoLk84*)Zy?%>d2B=JK@#dl zkP1i4?S?gklaZE~d+BlJwh+elizn=exN#so?OKa?3}NdK5N(Gp=XU7wSh}Y7`xrs+ ze@na45V@`^{a$tTtLko9`@VYLE!*7OxS$gg33 z3}FZ+3?YOtgfJKrOb8|nVF+OuLNLad!2}b`U_vlq2!ka|R)z zhzmvxDWU@pV#LQnr-5nq(4R@NDm9Qkx^fwBtd*!GDZwEqI$*pN&op=oLB?`V1l^r$ zA`#F&U$({3O^oaLa*qJ@GU1_;aLQ1LE`ne7-V%Oya1nXME*q=Rmf3HYOyOYzaew&& z`caSi_u6BlSjEAwA?yW+J~9~IiG|EpIbBZdm?hgn`fiBj2`tp-S8%XEbP9+k1!d%0 z2N|V(_5%!lZ-hSSDoa=CX1_}C=AToMuvFJ7BTKu?o8GN}tlUl6mdFaE{_oZeb;t;2 zu`&qA=G`wKW8q;0d2P|oByt?Q=HWM}OFtNj(r9vnJyeHg+>5f^fv_BsSowt7(D|Vl zHGh@6QnrF@uWht|Af_rY9K+5!(9f(D;9EG}aaQ$xl}9#f0ne>3`)P$!0(dw40tDf; zF*VLH1;(B*`lL=!c1~$dSPGZwPX4i@#-diC;h zP`iG`H#Jt9Cs8l0)J#e#KewIKKD)tGgyKI+Y@y3O`gUuco`?At3EHzGXBhh zd?Ml%d#*X*)K&X%_q08OaYp!3^$SNso2w+8VjRFkbOA~rvdlSy9rdjZCT~*mis9wyMN~5}J&_vsDG>%d!5DgPf zrVKbCRNuOl=%8WF;j|jy^%y+dB7pM}YRCW`@N5YGGY{Pz44I-fhuy;M0gi`(QGYS12H^ND=A=;-#GX=N^ib{!8n?5giGK<0)&I;#NUJ~d26?l-GQC0$7 zAh^YHL31#3N6?Y%8#7va?hI|tX0)O$Ch%!UKyHroXg^YojqEI0 z+x0S}J&y@z2#O6^dOt;(NY;&6wi?RPr`ydvbx@L*s@&wj8=ZK7QB>vTgchmzHB<_s z=n7-{Hx-<1k6(C>o^xp6ZD+8}oTQhnPQ&R-oh`R4SO}4PP3&~Od9l@C}CLL#C!E>!oy1S7az7YVILg=+ z2jo@8UM$l3+I}V1wnJ}K<#osNDgAC$TrX#hp3TJDRe3`~!EEfxB8vmR^2y>x&C;_U zS;cs~!!=Sl{nbh6B65N8URB(Z(Vyij;F0%RQJpQmM4kk)7`9h-_^0`a879IDnM66u6!Z|0$Co$8?MoXZhMhxfs z)n+6U@{(*<#wL-lk}>dCBMp5k0aP7IC6Rd(q;V4FHhsolF(CQ*Fp*jxQ^~4Z2B`c( zDIOtM<h}Vvj=XLApK-=qfjQY32CIucMa0Rp;Jzc}M&aMMxtS z|AYQCa&=sN1~pYUa)$aaS)ta&pRmBGX^>;uumYxI0BKHJZA?m^NXXQ3^q$W~Cn7}_mgJuueHkUk_1kT zn6%@B56RWyb7Ny_?JySxsyLra=8 zlTcchi`QXi!6``@1)?843cG=GCF%h-&|c6dYRUHo%3E}=0Fp%^hYo%8`*v}wd}o|n zUtlTi2{H3wv$!Mgc@^)!$0~~6>#4IUT5Lqr1a$pQ1h(}N=c1j~-1dUvo6v*M%Lkm@ z*{c&b2P3@_uZh#A$IiS;P5DSVn_FcW;;I`=P8J~egqw;_MxJRMZF&vb9ekvsE5tm= z(pNje_(qk|84Ol=VXJIGts)QxH2oCZ*2znAg@UB5Sn@1ws9$CuPQ$eaJbi@U=QKh< zV2n+fb1;2r46U{ZtAn_D!o+?0v`^(mt0vrZF{SuHgIJ@1Q3SE_c?oHBR%KvP&16yL ztwF71Ew$p~QpIm!B_z9tc*R?#JF@_`KuK`S@h!zsm~?0lDMMD%WvMM%^~hylKG%RB z=;>5XsD)okWcfr#76}mNbaVp(=SlU9hR3O3copr&Ln7?^^q^Fj+~ABL`Kr{xq?I|# zwo{*!p)Mf7Fh>zRB#l4WotPd65stoOgnq7mv5x+|z{yM!GUOk=9zYrVA`Fo=44F3= z;BMnHi-0H1(t9)9+tlmVve{S`w!k7axzIsoAaj6rlABv=i9}q@OwfzhzG7Pd&u_iIl(*|g>g8g_6S8qTpJnhpTT2+Wd6%8y#)s}pxLa}8?2 z7{)!mUVg7din@ePBzg9201s$3wR%^@#+O$Y00+Pln&kR%3v6C7BD#OaPq2Y;0#xz8 z^cz2I6)Z@ziW*fj>8(zQ@4F2SDoF+z~;IZ=9VttRiz$7qwVeUYe3g=u=uSm!WlpX|kuO zzKv~7OzvEINl5qwLK4;`Xk={y5{(s^M2LCfzD2{F1i}yXwKCl0cd%WFE&`&P#f6j~ zT-SwoPkC2x`q!Z^{s+fbCpoR^5m}&p_@r?;t*&Q z!qZu%>Np(bREa#`X6)3Ci(_h*tX5xGtpb=t_x+F@;Z5cZ8`LUgun@;U?u|~)sH{eB zyP%o|n%EV?7R0TIDqcjPkO0+04`FtNr4`u9G)dmSY2yp7VXW#^q*4p5wV_NFwD-#^8W!%`AjCUFc!{%KG13T2}_KU z5eQ7jzh(?eNNR+=vS^Cv+xr3fb}b^NtI*)k8zo3eywc0fin6-GIV7*fo3xL|*O)`x zTbMR(BQi<5R)u>Rnb*iDVVu{ztGIrpZN5qw3yX_^iTee5!Nc=ciFk<_d?3(w%KfBkJVxQYWOv7&1ay#KLjk9a&@{?6%r;eODa;9W z=m-LqlRzXhpqwsEmO{^NSxt1i#Y@?wPj%+QmG1NG0z^b4I}+y!r9JXJ}ML%A*z-&~XJBiH8)IvY|}oQ*jL&ZZucoX!1Mnzr;|Y1-OL#j`C?sBqKgGbHZp zn%VwM7YX0BiP2`V`Y-aOv?ChpP{Xd2C%1i$> z21+)|!!l%)7V(`R5}nn>zSILFx57NDiBg$T3o#`6m!%Rb6D8QMiNZGpHr55nT7@Al zY#$1Edau=AAmZy_OaW!H7>N-s5_mzoB@a>vqI6Vud#c?mnfapRH~F0Y8@Q z`Re~W`3TU*i(eo0-nNeJ-#k67EuRrN_8aw&w&%-q3G>yik zUS{4#N)#GG4TPuyO)dod7A5Wf1Yg1w6Uj`(lkec&YX@r)g4#aR5x@oyg5W%h0D!Gp zcKrwL+`hv{01uaWlyB+~Nf^ADyrO&wu^SLic+TG>_b(yYDr(|fF z$Uww-#NSCX2BB~7(vjSg9jjLI^$Gh~nb4j!!bV_V5i(xLC7ZtJ3_@#W$I!T8s7PG~ zpGBxpj0~(5Qs@K;$2953_Sh7l;^?}uV;!(Zzz0@4acI#PFNc%T(+5X{hAh5ton9v& zfCFceBBxHc7pFNkB-gyZadxf?|77kP4QS`MJt2iWVk*ieWga_iy*MhsjwUkKZk zZK8LzptrT|_{v=m>d7zJ_pi144LQ!F8Z~trf_ANZ&Q!*UT6~5&MOtLc-^PjBSA5}i z0sGqeSH6xqMQSx1W0X$~-q{lj*HJ)WurQ~anX_}U<5b^Y5c3$5x*cz@rT2)K*n*f> z842lBEmxu!TqewRj&j68&8K>gDLSqWPSInvnxd;}Yl@iv_7t5{hom$LX zZQbM?yxjQf8fagP&u=~RY?xx|M$8D|piDjDHVc8umSz6kiWBBChr=|PDYlO@ZO6E% z0>u!&3+}$lQpRrl8taJ2!PTA_^~T!^)eZjbqb%7k*rx*&GcseZU7S(bcpYnXJ;ox- zx#3y-xp91wPv6uT9c49dHize11bubug3vUYgu2}b2=6fL)}7en;@@41of&33cx06I zyc-H9{JUpS8t7hr%poS+$B(<84$b)g!00${JUBYeo_PoxYF6T5slp@A$-hVEyJJiG z!H+G@EiZ`Y^5fkZe$NxOm40#>;(*`vloO0RJ-Q5c5*F48R3oFTzj-9$EVJaDH;&)h zhLGgXLkx(w^dx*=w5-DkGvkyjSdk$H&=w9#Ze>hw;w~egwZmtsZq}>?fCCKma|Y;8VR{OYyCS zPYx$92}I33d%(;AP0k~$$)NvEo~b;IivpXs6^23EJ6zv6D}C!0vb8c=wCnyNE;WD2`5V{xV;v{}Bsyddq20T_YwsNe$|>N<|V0*#Ty#AuB+8nna+* zfp6ikEWMzsRTgnJO*!5#TrGp$9PW$>1Gzm`W{)+5v8E>?Zba`2+49m{@Ep`wIZSc) zLW>Whv>or-2u87c~&i~7!b(qvJsw$anBmEvDM5G!cYq@nLH?{AhJ2}|G%cS z;CY{|_$FF0kTkJQ5%4n*GHdrGR(_~4LDPX1T)Bt9H6%*00EZk1;PEEOP>yxxxm6jQ zn-xD%?f8mo`(j5pms=g)Z!hX-RyByh(#eDeS!VPS_$mBF>Si%4maD!cnchgM^$Uz&KjbW)IZhO%F}D;=(}Gjhs1{uKucUKTdEm!K`mw|k z9Q|t*7WF!dG5oK3G8(jhZ|#-)C92I{r^yX*{DC{l9*xA98cH!O zNBFZbl^r51)%+D2Jy;3df+tow{~D};BwP7L@hM&n2sP|7#rt^b43C?|2W^F=Li>q9 zE2F6R5F^MuBXIl$BK>ml+cx8uF)0RR{aqU>D+SPh+%DQ6kic>XP-hzirL4#&?Q%FY zX*dJ}FxyYpmasvrL240{X}z~yvzDsaDqv0Vsj&j;WDbr)Y-C^v7Y$3fGzIfF)>udv z1P?gcmB2C$2`JN(46T=t)$2}QwmIO~knVicg7v{>o38Z?O&1`1s)Q%fRh@4bNRX zg`CjLUb1dlS|PqxBHHkleRodGtfhu=-ZO#*WCR6k*H;`v{R;=1n~#Ez@V!~TC-Z@g zF>o5T{?MUNZN=VCJGaU@m)yz8^&x&Wd;#KD52*)1tdDy=(}wGg(=V_ob;`TjWk&Ba zqaq*{(#nQ&><$fJ&nQD!U@7oU3EN`cDg83}OutAEg1HMaL}j*TG|^ccc93%RS~Fst z%|6zQe2^*NzXwDN7A+u3)ip-Q_3Ly4wM0KGh%YkyWelAy1vJS+kgq-sdb~+^8##87 zJT>H?LX(;PfF@PG0w6sws?i;R`rq90%q>N}(ajrEVO1n5Ikq}fjWZMe|ym2XWm z>&I(e|7}epPf8k)^7e$**Eu1j&U$aP_yUIudC(I)ckww7AyW2hSb!yu?6G~+79^~K zlM?ZBf+LSvPzr|g-<-AflAsB48do~vAKPgmScxtY477=CpP_o#8B5Jx zCykXB|9A9PrlW~)H~LACzcG0}<%o^oD9LB%@kqHNTBn-on#zxGh1jtqLWFV#Q1WBr z(03{QXOb8*4;t3neJ+g*PmU-vP=Fbvo6sjxc`qf2h$#k?ZoF>jaVbf0pRee;Y}7F_ z~;6 z<_Rn)Ah05u!hpd+>e+gM9M0sbT6LTq1S{ViSHJ4|^NA5V#VNz7ODS6&&j(&geb$DG zYR+XGX5-JtT^w&>vq>K%JxpSJMyp|$0#q@VdmCVHU=Ew_Eqk|_l|s0A*8IhKwV zu_M1fL>MPeGD~(?)&>IM>Y|cY6-$_7vb z4~6s&f=9)74yWm%f+Nsyz^777}sPU8=n>rh?t(djcBXn&NK{ru*b_$ zbyK{xFh{n4_#Wcx^_H15;;k+57T70~;YWBlqQOW?JvOkvpfoSfGNZ& zm<7=(5_foFc-u}-kv`eQ1_2#dxV`)E+#I9PL0<^Xu&9kR=N`Y&S-|#NlJ;5>LVan% zK2Mk%jUTVK_S>z+BQM|_hwroW17mzcWWz-gwDCwvzU(^aiZBd7g{{aoBwf$8S38is zg>wx$B$j!jB|$CC2D`~F0To~9SlZ3A;yBTPOL6SkTS8*m3%BYJhLK`AjKtesoFn~P zN*4hOkM~S@38yr_EcwjQv9(s`GIVB^o*Vd?KZ~6bdvm#CuOpB8jEsp;>E7&*?@vEU#C$4Zc)q5O(O6T_e!vai}y;nhKq-IHE{8e=!P5u zqLI?oK%(~M7(HI$D1zlQoSb8b-92vc%AKO)-EV&1!j>6Jo{^ zV;;spbPdb?KW<9~04TLD{h;>J7yu-+1inH?>={ZB5U1&Ml~Gs)p@@F$>BRN|>(3j) zD3x;E7GWS{A?_pg^T1U~DfjMY_D)r8_BVMXz-Io5BC1R>^G$?msJ`P%l0{Z`(VVVn zr#%^u8GRaL{QbP64+#DJC@z5f{i!G(bNqc0M}^+1MIQM4{SbkUXZYNGstle`DnQ+- z{r>4-nRnsi2vkxK*%SNNp4?`ZMdXbf`VrvwIi$>jBxEx?&G|*PDGo+0(8p#!m z4XIXx1`aT_Gi21gZN-pY?m5c?W}ZsJCqwm-cdPtU}CsFGJJ-t!d~upDh(f;!AKhX2U+#DNyF!@Qq}ju^^v_^?r#PoY2G$z__%O= zWNro{XoMh2t0D1VjVJ8_WhdZldAm z_=g&jnjAlnr4Od~`$v@G(8-WMRMOrKB(??o{pAst2>!mC5eO=EkREr7CfMQb4~hQN zxcg&VQts{rdi!cqrZl-zDwP`Yp_hC1a?fBnXw<*~hK9Dbs#v+URuwDVQixkpY%I-P zzi{r^h1ifGa!-ba43T@{?pfTmU)!MGYvV;@sXOgNfG?f5$4N822zA^QG`fw@t97ig z=^JRQ^q3PNeLNzly$1jEmI^t2HN{I$rI4gSl^;LEoX7hdu_aACjxwBrWavuBlPAQXsq(_h+y7QA(y98NXkxtfO$wR znf;XQ!E!8+!ehSL@Ln4)nCV4`Z=kWlTPozR6Cfb=Q?}_hivOO-- zi_rB`wx{(|w#Vr#Oc<|3UH?^)3e)wUb>%zPU*vKk!u2CtY~kT>{cFU!JJ(;Pf`}m3 zKdjPnqvQI;QUD3$`peduxc;-G>iW5`WW4J~yK2#%D3;**bC^H@!u7AgIC`sEO?kO~ zyb@AsTvtU&x&9noB$!^;PsE}sBD#KLizz~ao_}ps;mGrsjrc&x^UsO6qZHzTJDxv> zl$tEhj~ss4^ZaQb`use95&8kip65r4V$SjWWy?99e`w-|pXblfV(bOaztzGB=lQot z(5)coJpYhM>G_jQg#0*uE)9d5i433Pzp;dc&+!kNM8{7Ia&-L0B(tOAA0nYp;`q;w zh`bzsnh~2pf1ga%GU)G@6&aiSCEC#B?+=Hf?mlva3-0aDJ#lZp>_fU@$cMkHMSQE5b$;P7Z4E8U0Y*w-Q8VVV{6ye z*qq+PG)08P4`iuG42$b1@%MjZfItnF_xDFQzx@5-5m}(Wf5ga&1b^R6Wh&t>pFZyX zh%s&M{&1}6?%#kw>Fux0dAjq7&LOFWA!j-sS8S~+)+KOtUpBxvxND;Oo5fvM*_h&< z1@N;b&u!4mI@V}qFfV>`mlQ8~s@aw`0Ex;jNb!R40Exn8wD#~6cRaU26N7p2gQuEp z*aazGUNc&I@7xB>cdFU;n$g;Gr-~s3Pi%vA!cz&)8 zSx?V@)jn&-2eYRi@PF`G-cH-l|J?(l~xGr8jJjzbr!-th7OI)ubkc zj{gj4&Z!=`Y(&RD)~ZJCl?}&VY*lrIqrZ;~A?i-G2m&I1KS|{Bc=Gp&Lv9rtf8W@u zulLuw?jO|K@A`v!%brM~hP;F85>0W>t~d|n`F z__(fN>BAY<&!v__Rz=eD;`xV6?A%q4-X3b6 zzpGV^jwd|7afN%Us#$`bzpN;V=jr%$$SLs5OYU&|NF8-o{X0;9e{G1;T{WGWYVw!0 zN|}ay={4eV*YC^auH~9@N!BHto7wxyI5xTGq7;%c3uvtH2vfYU269Op!B-m&BXx)S z2)+2p;2$Oe2#DEUH~6b<5Nml6Vh~P*a8U|Lc!VilZviVTzaA zN9c9G+6LVbrg*hqZG-Js+u#rsXimXJ*l__@0~-E%=nIxCjTAY?p@PM~j3H>NZ|Z># zIs$+qf-rh}j(|vm^ZJsm{yq@CDenG{AWS2?xXZ`J0?Vc*E@E+2v2w|n!N-r>F_*h` zA!BuSU$DNhNMPC^$Utl)22N~2C+PH`psX2S<3&%&Y_t(HJvs?Okj5f`Nh2|ELJtbc zc}ixJlOP0TBnD38DVYsMV&DWuV&KH;@csGq?gYT zL3aS8CT@%_M3>JI!8S%0VjH6i(dkkkUwqthVAX7spI-7r=GiyyD4sK?^Rq2a_8i~mtU%D6s@j1dcmD?( zK6k1`badSPBSbIGhFFN>?T>iBI7NTnzFX0sw|41p$U)>hggIB_&F!wacBNQVtVAM< zRmECdv8q_N#*{B$IGecR7p!j=FVfWAHNah$ao2M0dx@N#+ue{lyCrHQqFk(L{QVGp z?A-k#0yN$IB1SK``yDb`a?smf5}~>6*g?a~%LdohiWdGnA;;f88$`z4Kbz9z?$ztA z*x8O9czDaVw-sI;IzT;XpaC;arlzO_SixeOSfQe5^7lIw1&Sl3(48ugpu1lrt)g@H ze?ZB}*(iDyjbL0xJ$l^V1w4ekt=8VF1FX)Q1tjWCn{Dp`9>QE5V0GNI+2-m1tJCTL ztK)PfE_D7rQi>RNsz^py@b~AAc7*Zw%Zy%p;O`q%q$~cuSkV|Mcb|($ZqJa4Fn8bh z1?ugeA=lCjL!P8Z&l1U8^DXbLiMNE~DjJO|oJ>toIl%yfIZwE{)ZDifms08erbxr* zCFB9q?L$_%Dbnz9`;b*_AF>KX7Joko3rbUdxT{5U_o2K%rMS00gbjpJDqDbkfqj8# zZTQ{c&j#?bx}KCy$;dS}Jx4 ztsOhuUn>uoFA7G|xO!E6u9XK&SFftC)vM|oVZ-155q)x=aQENP2oU&|R_)kP3^Rrq z!?059?f(8aBKQ(VnDQ2_vAt)ec)3502)adUZ0?UEg6)qZf=*8ovEwa_zwcIK@I?xL zABPbL{yuUHm%C$a?tan8g}ZO2SLp30@&4TTTzG%(JllIKMIIhj)(I#4K>-CoPq0w@ zx$7#dEHw9bb$_Go>h8LWnBy-9Rcs&?40pdu^Lbl^h8AXZRj82rOMwAcUMpS2S2<_S zs}&db7IJs3bQN8#xH#8JSFyFy2992wwFp)g20+FN8+SjgRk~9h>Z)@0*%cM|cr^NB=!?leo2h8$@Sj+s|?H)MHN7e`e^ zMWV?)Q-z>18do?OG8$Jn$&zz_i+yBoTGjU+U?h!O>?3mzFp{P{z(^XWAE{gJuN9~} z)gl<0+GSH_KTZ7pZ598z>H-7O7`Ti`28l=}dE;&ZFyWR3I@6RLjfqARg3g?_eboxvnmnWk-I;{ zV&_hEsE;0RA6eYtt@_SKOV`}lHFs9^BBgsv^keDLkSnQPw8Q``t&x&DeyDr8;o3|*frUEYQ zz!jNpU=L0&ph6Q3h{I2VaiEMwAgTh*MSwo~fIfxv-&d3NO8`q(Ir`EA6zQS$=rncS z+S9Ec82y9=H(G!mcyy9Bg}OO)r0rfRO=p4&Jx7K^FBhQGLYiLPhL=V6z>`MnXhBEk z$l^z%x$ERxlpd>qOM_jJX&QTQI)(~Ov|8G>Sfj^og$~U6lZq zo;v!{A&T_SALukS)YhJM@&lupSa72=^uVKy+7xP{sUz(#m2QDfGYj$qlTqk_ClgK` zWj-dj$Q&#{m+9bT$riMrlcDZeO$A)Jfjzhkh{G=<5LH1wpiiOfmjG5Cph%CLxAyWA z7Tn|{Z3?s9OO@xyaAYA(Z|;F7P0o?UPfor?Ww0x<97BaBd4&UIvI+tE_dFtpZ zf1uOMPJUo=h8}n_(bQ3Hfld>n&;t+VV}c7z2QLeTy5k1+;4lJF6=1&vuyEem!%5l{ z;yE%LxCfpzIQbTZW2n#ss}P_Mo;v!%PJUo8(bN$}p$DFq4qleGfjzj_F9EDKNt?pA z2c9%<3>BKZQ%7Gf(bUmP2QQ23mjIUA15X-v>gda*gO{c4fhSE%2QQ104qg`4(=1p- z$hqEM86d^M^&a^LB9805@ydreuJ>io0z5ETz3)PRfTt6z-s|w=aK!5U&`Q&ow2ujl z)q7%O^eD=0j%DWO)=WS>V4S&>JwJ)hwiPK?hz$fy=P0M{1B`6Vo?U5 zm+HY_fbSn`u?wB?{c3si3PrvjN16q_?@y*Uh1d5N)7Q@)-;YdP!qxYawIM`PCkTDM zKNMYcKongNr=`2Qy9ES9k8Y%qln-LAp~wq>(NGX#wf(qlBaG`1bo_Z})ch z&CYupZ|3)YGf(N)71i!zyw<;Ff2)zGa}LQO>tUFmXtH9`rHCo9 zIW?T0t8PQEer`nVs#9)&CUBm%A5$lV`0s*P^HD&bd$doWu@oDc!+pR@e>pn{Q(Asw zc~?J2JPI=xB9Oj@EAp|RlWsnaH5^tWUBO40QU6`Qt>jw@&nakxU@7;AV$!OX*!S0cFpiDGh|fJaN$pslpQrioj~`Q6(qw;r1`9L9-FL_lTlsYLuwH zn}a)Iw8NZXtmPXa>Y*D7`%TZChgO?ul@7#r(TJ}S^fw>cQH|<<&7!WcxEiWo=w;kj9`(a?Oz+BhFC||6!aeFkI9c;RY;3H3fr6*i<(bf?)u!6>gssLaNIzI zP3EVXgw4&P`)KA}%)p@UqG)>4*91kit=01sl?O^Rw+aCmMFsgAqr=w)@sNI*H_v$G zw4bG(sYt09ra-5g`v0CrD~KUdJ(Mr-L#4plNPZQ^KCFB9lV59JFW`k6j1;(3zdXE4 zrv58W*OPqoZR|3Y&-Lwyd3jja)Fqx@MNZ$@(OtX{3LRJ*=U66j=bSru%3S<20->d* zpmTVh8gHR5a{s8nYan{_OCKU@ZX(C8m=rJwletx6gb3rqoV!{J`{fd#Tosjy4HyUvfL0fASq=IRCktoWyq@8n5Xz z5zdtRQ_H@-W@;K!zM9m&Tb*wIe$l$SCe|dLH`ib7;?G{L$?96u*jJNWH!pXdIad`} z%dB`uCls2Jl#^3zB^x?SK~nMEw*LSM^_;@0ZM1zY-HSW$hNiA@5ADyAAg-rOI+arGd#S`k&)5*366F zKm(@XYdiUXvZBEMKk%}LB>a1Vxq02LMV%%W{Cm6~qSVhvUwt^$auBISB5)4q+tRt7 z6#O%yGD`nInPW#3$6w`I6RSV)guO*@=?#6f!s z0Zu4tuVnq4{$McMpd~kkMc>3Ot;>D_aD2P8P29Ai=1xZlY20}q!9Fz94anLC*6k{u zo^|GI?G;){vn=lm6yDjC*$j_i7{{Q{9nPY?Z?z6CX0o&s44^(f=9aw|(bq`RZbxwP zP^%?8$UOo0iahS?N<>X{J#%Fc5wn52KCz*u7Mugf){*$2_8Agb^G@c5?k3kTuZ#AE z0FKhFuSD97wBSB8d{D<7LdcNYBabP|toS>KJaL^kon(0gARs*I5$G9mYcW{jLxg7glS1?g+Rs;dO7I=WdB zIXBsxV(jg0;}Uki%+U$6PdP!UozDC9bFcc==JdvrMVkLiyZtkqblb!5#7(f zAcU?vwI5V=vVC3g;^n5hdQK|OFJ~zAheDq=f7j*$@9O!!i^%nHM%xUY#M&y2w_|2V zdvlH2oKHdpSn5rmE{PN$lZMJgb3z481Jpga;QGamqqgcUR4ZbY?$@UrW4g7=8M+FO z#m=3dFL6D`Y5INC>L-}I{ncok^Ucf>nVFdj9x6rhwj;uh46My$N4gqk+l9_EG}xQM zI!xEQ_YBXDa}+Kz#A5Un)H)=XKH8bFMrA1P=XfjbL#O|g=bGa2S2Jm@MPew6w!J1L zZHVG9rM<}d=&kWe-0p+f((y=4gBww7{%FN&|F=lNoI%vVg$B6v2zVsV@lzBnZFll{mQu0D%v=@~y-75!c7NL@~AF+E&( zyIa1_LY+AK3cqL$@I9ewq1JjBw_eQrsRKef!|7OXXkI=pN@ z$Ms1Q)5)lGl^=AX2=F{((jUHkZ9{`WW9w5?Sx7(6T8&d;I?XA*S*oEtKFeHcAqSgr zp8KTAI78dDHAjO)RC3Uaq=ZA+te}@1d~pX*0^F;zP6~ z{~5EdONYF+&_hs@^xqh)>hkS+<6oEpc`Us}CUo-6sYqRcSn0fy1Pauy_Q-S^uzjWR z_LEj#ZOJH#OsHq1`uF8NM_N^l=IksKky%A5R~GKdM2%u<#Wb9paMX7lJbY@-WfpSQ zM?V~Eh?W1%)4ZiaiK3)iK%s@p_tFD(RkjCoi;prqV$zR@=u^y=s#Fmv*@WnlGojN2 zXkxJjpfXxDF(Q9zLi0(xLamN7Rg5ruhZLRpwQM5KWe81FjcO|$Q1ptJ+o&lI-KXj6 zC;aW9^ySmdCP1U6_)3ciqCHut>i93iK-Yj{Jo5x6`O?)DHiLNEp}GVd`dZ6B0U9I*&J26)#jek2R|9Kb%n` zDEuoo_ef*-houJQ%&fX}^ySjM4Mp!f`MtC}VI^!BrMM6f5QjuyhkXQ}{x*^ zHnN&nHV=b@IT;NbACooz-MxX8NHnrQK7x%&@bDo}C2R;XWi#AfeOF#)1;jgkWnf19 zV$>Uq$wuto+seR=g+>?kSodeqgba(C=On07>ZQt`HQpWMj!RNP>(-tVesv<(o=asE zZHLE5U$e^M$EoAy(uNiA@Dq>Rs-}o6x`%A$%0@B(&Rufl)Cr*M&@C6B` zCw=uwRufHAlNGA=t)g3=74fYxf*%(=d_)P`z**%XW8Tkr&7{~;u*5a#qJscmeUUbh zqd}R>Cc;WbKCTb`|74J%HVh5oO#?hI_jrl)!c`Edwa)fyUasTzz8Klf6_C>WU~3O= zJn~w>lM_?Ge2=1actr_ftC29_WNN0jp!GP8C{77(pzqS9Z+kjZINuZ3CzGrHyb#0T zu1~32i(oJd@6tE#Eebu>93U-vh~%R+^qklHC+Q7WB)2Ivef9_^uSEQt@(&-^1~s5-c$eWrNvVGPnxaj&@;&<_8IYCrcq{tGmZZ(%i!(6u$V)0g668G>DlV5cc?M!}dih~AqKtEwK zO6*>SY=;r?-Ve6lB7Cxck>%#AnsGwx4*d;vTejg}WRv~F=Ht!d9c=@}cbgR6nFZ$J z{5UCE$M4Fw_2UP-(K?<{X21kn3R{9cPraMjcFT6Q;X2=lPOWH%?ST^HJHN5&P~ykL z)r~i>%F$8HWRk6TPm6KN0y1ggEMQ&*YfVc0SMsFXRD%HS=auZEqUm3Qh~0!%Xv(hS zIpqunK5}KU`i>_*egX*JhET<;{ml$JA2lX7pU(z{6|Yyw)yau;cqKa7eB@#>s{cvk zy|Yh#{fgzYz7DK4Dhj)RVQq!ikMA(SQBV6GuugG4x~Ho6(J7A3i=f>*P~s#xlI#C* z;AiFViJ?b7BzYMnf<^3S9Hye^>8rS;BZMXD9I$&uP_}%o2Z7n4`^HMTPaEvi8;5nvc~d~O98mKs%0f*KWD@t% z!StCbPMTxU{1zqYT@~mEMP0iJyd#vsS9?4ql*;89LqJ!Nnv3(`XNIq>_k#P>H)wknWBKv$t3XmUYR zLl{X%YBQ>wD48muFce6n7&LaA20&5qCAl4V8lnwWtU(3-p@8fCSDqlVg zl$WrHfryXBO!VanWqA?*uyyAXhRoUR6L&OY+WQB#?`D2-W+(Sumf+nRKAG~GZk`ux zsa(M#xzq6xZ{H^`rS)i<#_ZIoijW?jZ&2ni9zzMh3M+Ff0xfS;N<|``vOcH2yolMH zT&o-3%QN-oM+h&X^V@ky+FbuFw7qj9=D=pL)PiBb-|nM5&XVOBohL_uCSP4s*f_PWagBwVyo9 zwhsg4(@zML>hqA=4XAzUiapQ3^)JYNeQr6B5t;HVmI4vpNGZnI^ZVx4`3L-gW=9v= zvlrMiy_Wi3m!BLYHG=uCt8CCay5xXB2}p_e=CQyYq%{)ikiZZnxo>q~*lEpPVBNCd z87Do$mXMH+LH8K^@F+cReuikOLu80QoV+-$_xyzJX6WyNc8?*tykGSPdyQHID{p@e?G4NO&ubFL z4AkZTG}5$Z(O6gJ=i2$h6?L+@*RRaK$+_XXaC;L0EoQaLs4c(Q>EYXt54u+n#FNTSIH*dm}=Rn$srEG$OIJ_iZu z#|mRIW%NnSe@K&g+|m5jBTrU(7^IT>3|^3}I>{e94fnb{eg^iblc45n00SR1qjx~i zFh@1eUDMQsLDVGo1yZjXBgbm3gMZ5O-k$wTniSD2|W{H|3Y^F zV4lxVz1OtYsc*1ktu85Yqo8^cVTDb1@$X3QBoHroptT(`lQ;=IwJNdnvCryqCjd!k znW;n9HiX6JX^2gXCIO5YGHax-f_rr5Z6h5VIRtJAGjbWywYW^|Q<;&KDI&~z#0fPc z!Uvz@8nizSkbi*cMSk^Pg|lJ-M5O1+sWqjy9@|Hs*AY}3a|)#+OaMoeC|knYm}L

Ga$HOWlt?$E|-*Q?F956>&~c6p1SL6Wvubm}jZ6C+kf zKSpD&HJ%W6STB~%?s$Wjr7V6j&0YlT>D{!u&lVurqp+~qHY z^=}Ug4%961sZK1LLrUjg76T^w-PbVPNfF=zR0NVW#OXkX<=?;;-&yce4%DR&)<|tF z=zu8sRabqjd<}BllV8i0gzvrX4A!eb&R`kxiY%#lG(L#q^=__O2VT~9yn6|ItF6m9 zR^2B3?R zctOu+;PKnueu+vIJZ$a3PhVJ5)=0tNQwPO@o^N)8dXS!P`w{0O1)bl@x2M)BO?|2q zCsW=Vbd$^Y6pHDs043C<819p6|gXjMv!kU}MWV5Pg#T;|X! zhPEF^uPtRgs#(P>(u7hJodAfHnxFhMo6yksM~nD6U|j<&v2s`l1U2wX8))x9DPj!@ z+aImul^vk?o-&^VMcl!MVqzG62e<4H5)h9ok94ZKEM%d365^vS>EtCWYnjk_jH=-m z>wBnze0)|@=c{yjs#nXCCaB$n7GGV3Hrc(_Bz-(7ICRpDL^%CrTj9{nh;i=&66O-H zUig3eU+Is;o}Qa&A*)rmRdRMp=Y|3rQgJI-%qRTtEHx@H*EqQ2I#T)+y1IJ-5Z{CZ zR5JNLWZ`~1+76HIJYaI%m%yw%!P|~76j0(oTydMvUhNSCad@%ciZw)AcO zcJqG!y|$e%ELw0RL)^G76ETdWjbAIjU zc>wF9ABIZg)7tF3kss$fKR*oZa&3xZs0+@O3zFf6VPxm;&D)4pe9Pb6?aOH|Isz)6w^ts&%sgVlPeULwQgSPMsJb}sA6ZgLPwyY)N*SyX^tMI! z&S;^TodV_C+S|}fGOohGH3qCOe5bqPXFY8K!j7mqVfUWm9Ww%`r7T;H@k*DKR?E)f+lu7l&0( z3j8HOGHL14u#cuT@p#w0D1%newozo7aul>djoKXwBxLh9&MpGfuuN>mO%t2=Gcs>* z6~I)WSY>v0;U{A(j2-SSLqT%^_T?dh_1`#nRBP#?Ng%%C^AXD?~XZoh&B?5Z(c%8-A2vTXkBN>}TYOrHpf-CEJv;GysKBr^ zZg0qVN6eBlz%(MGh%8w3bV4Fu<=}^g_agCr+xRN+E$`9fkN)Sn?d|n>BwhhD3=7Y* zRkNBlaS9aFhGqi*Vb(YOI`&E=0*&_BprcQoegs^qavWogr_M$D zj9ISPcS&%4ReJ$TR@JY3`qGU1Zu9QZRz*JS?$KIVLjD34NkyBdpJ*ruyiL0Romb9& zB}du^=HAS=$>;~lX5WKSlmKk!$bV1%j}AP>lwRvWR5j(=enDP1)@;5!24vi)zh~2+ zb+L1dH4WwrR6nmF(R0-g4cIJ>7dq5$4HF+v7L{(x^>4b>9Phe!(L$ z^l0`+bn5aIYY%TQ5L3}$`cz1k4zFqcx!8mTX^#=Tb>Hw0bJq`Sh>7)%PlNI&)BDx> z_}#Sal&@UX5C84*cHY(}9d~gUYr)irz@{uf{jrdev1c0kv2X=s8 zdUlF|3v=}WujQqfP5zvCmn>VO?`<& zZP>dvk9Sm#>s#={FY8mmG2^=l8*V#r(@RwSpCqTrTY~0xkHiz!&~^oyZ*d{fX83(5 z>UPxg9Gz5^Kye)M-J-COCCiYiEO?(^$Q!|xOh(|^IFS~>R4P=QfZ(GttJ?$HN8-;1*p~E^3nCRb=RF&Y?oOYi#9~K%6mt<}zzO{+fZ1^*) zg;gR-v53P|;e)S4fH6yEpW25>RhL7ZW2G8NZp9lC61k;-D^lg*Kz6`$bt^LCArCs_jhg@3D4Wx~n0Hea(<1r;(=t0_6+oYnHmS z^=fSo-{~hudaozCYlcy|ftIkv!ApY7mey(=YK(*)e|g{q zZ;imC?MWaCU9VOS_;vR<$nqBR3C_=Ahl8l*?z%JCoj*9w6bio>LKg+B?Jzbsgl-!Ww$%;P1BdAEhMQf4XX7U+TbG z$_+0!S2O%hyrO96*JqX|dP z5;P~WLnSC7=skk=zfFLT9N7*+5*RkiEvdQ7&w6?A#qu}p`?yZv@8M5D`**;4Y5$O0 zvKuwBo0+4Nn#OC_bCoq?un~jYi1uX>WUDO}AmORelp8*|s|sgx5&Lbl1>?E*ECo?S|8pFbh$6Bkl{FwAGW!M< zVDvi+!TtT_Ti@2Wm7%;FlL32)a!PX9{=}HNUXYC@Y8sX%VLy3VK)-cIjDHL@?Hzvv z-G|VDNRz1aJ{|9U3r*B8KF;fZ@3cdA%t8lXs*~B*##;Mh!1e*KuKH0pkvb=X6w;v^ zZ^hGl{pmu`ckjNa&|v!Fi9s?=aOa-jjOOhSSnhNB?%$Q6m`r;<4X{?pBrsq{himZ@ z;g7ayoXoKGBWGt|zOeb4-=w)@(+Pk7j-K;7^m#@;>0I-fvUle(QhgXV0Lo!fER8$` zw^i*I41ltJkTraj!sj;)w+S%nkj_Ov>^-4jB<#4EO?ynf$J#*b@`SlRCyh}X!3wB6 zXG6|k%_;;yyO{p6TV<9x&h0U}wX-(Et>o6Bgsr*M+XyDYZ~bt2Iyw~CiNEeie6HRjmJr!hbNP;l!=U;`Sp%?>l6n%g*r_IlCfweFz0m*o ztzCT&4u18U<>u_B{TMGur6C*o#MheTc$B=WK*XeX9mqXr>NVSvPS zQ}SapL=*1t;T^xBXsnb#*$2{+BW~i&iS^y0a7xh(xmMt%;3fi{LtH5pYhgD#JL#_jEM5l8J z;wNeG4-&@5Tz<0ABjpUROC7ofN^_csuen1370y z+!sj%fU}8mK$CK*fahOH_+exYGq}y+99Uxb zB#-At({MMGHI3A}T}B#aSnD%#S@HhE-8F1HMcm>V7O(8opHTYw0+y;Ms(SY*zfc|8 zV{U*3k?{j9509*EP$MpG7iVSsf^wwwux&>RNaqapS0%$FGHu38*B96v>($!JO-S4) zJ50pRSFaLk{k^>=$-a%5Qak>p67qWAI@`V{l*%hrK~FJfh`8}wjG|HBs%q9Bfe~62?MVuWrn0G=*;BcqUMV_8A>3o9Ncrs96G`f~#iyyy zy@GpOoTG${=h=z1R1;oMC+)Os_0XT{J6P|q`Sr5Vii|NlZ>sGvy>UVsJg_v1m9^NS z$8g6sL4xX(B-wYPIWnslii0-P|B@N?1~k+~+R$YTztNFRtJx{<56SPZU?{p+6$b!K zWU6lSR~z{UuRo>}p#AQ@G$bC4P}PTWjGMRUC8Cs(l`!eMB&*7qPp`O@xaSWwvoK^OrXq75ZF zmiA*1)OeH=zn6FX3$mFiTos}(a3DnWeEv#;n@PFUL@y9@E^X;jy12WKhmqx$q1y~HK=1rZ4ZbNHDl);=aHHKEC#AzGGD zBpU#l*Td3TkEIP#XL;RXu?M&@3SWEar|;ujzO?8WN2)W*pXMHys1%y1_*a#yno`0@ zdx*vkqO`|IcOTyoxOf<$_G>6Z$IuTvC`k7w9Bb7bm$-C%GpJFFK>-Cu5qx?V3)O7W zm+>>E-V17?x8wcOC_U03IRclX6UDH2C5QhPu^rpq4_78Poq@mdQbt-!L>)0;JVXfZ z#Pd)%JhuO?99%k6dT9bXy(LK1Vb1Z*j^qn{jHdhB*BuClx~7rlluShMY=^Ymz>+ZvB!|e}oSz zfy{d0(rUx~H%SOtKu}XfJxO4NF$AGeo?lp1}qeh?530 z_*u7kz^2-CdYClp2wQ;xFSW2*agS-k%M9h?xLU;w!L{SVKcqY#u7T)@qZZTtT9Zsa zx04j35mV;u*v|2qokphx&H)IDEfNK#9EL|63M2rUQzIY9rY}v?%{7lI7?O&ra+&hz z;O~uLu<1oZV3|FEY#=cnRl++ITfhrhK+ncxw15`<$V|37^ok|3zaONoqh%}M0!mK= zm-_cn83qs7;5YQ^blvpwM~xO8tD6>)&WgTSFrW&W4f%#+jIitgCvTpym=fVEl4 ztMEGAI{oRVDrA=JN&sP=kpj$GuIY~x1!>2{{X$?PWWU1u$?{&A+$@iY#JFE_n^|C& z?2i*u<-TZ>0tTV}6Xq^NKvpb~#J1P_N>G{fFzX;XfhWsL0?|^7%Wq>UkRebD8bMMj>uO2?2k38D__4R2wMGYJK+_y)N6BQejmt& z{(aT+b|s=a-SD?B4OYdOHE#Z7NtEsPAC4#82drz6AtwncapiU0Ua~Vc;p{3p^!qe5dO(> zno=ifTZmqOeYH(CgGh2# z&bnHVkhGM)3(h;q(53ib8wCAL%uJ(4zE^-<7L~m&R4X}`l5oPS#5~67D0IZ0IGvdm zm-RuTeVTDpcR0qt?k=QI{$(6TnySdCfE+j;r%wZ6SA4|Un$^4bbI5t8wu7?oU=Wu> zj)d}-8Vkeo0kc3lhd(I=&!jckV^%80u-Vu!{bx~V9D6R0lOVg>KTiR5ptefiEG#0| zLli#(Rs1`M86vWPt=^2u!VG6}(gaz`IN}Mk9J0RTF`Fl^T>GsCD8Ely`*ejlD$tJF zA!3Be9g>am@{<>!!`FV}_(^B(yB1F4xRXBJ{B&J9$Kmr?=iseu1%$Yf zKJERSmK`~R@M}EE;^f_ilS{tStkdI?w7kYcr!Gx-d+~Lfx)!yUu@%}a>yvMS&Gd4L zP9Z6N=MT@|YiPXei|IN|EgU~~TH4jN#JGO=RT1|p$mjZS`ApOK3^%vM8?~t%ZtH@< z<=I0?DubJIOHnX*)U?vGw;PY<@EEW0x9~oQUo>TxF3SBdAsSWMxC%Py85^K_jO|-4r z${41~mV}ugVy(ZHz^O zO7v<^bcub&KjPcUMSVHG`Y#lCf(P8SWSH`!rNy!X{2^JNn`W9Bl(I2|eIFM`Go%_; z%Ks?g5~_$XuwBoK?TTd>zhMGahpxkI_@>UY^^`yYE77}5gJm-7YgYz=S6q@M)u29@8Gmf>VpXn zZdSZ(^xRvaTbs-%l;(5<+o}JWg38Uv+XS z{y~)W;GyQSPWBTJ`7q(S{43x?cj%|`eF#0w`n^*+OQYUhC;;(R8VTSjStE3XOQXp8 zpjz^a6k=&U#NwB>ekGiLzbo~Tj9I-o4cF;`t)#3Vt4dM@Tlg<>0$dS`hVTh?@c|_0wGU!&Nvv58u{3Zbj=?bkf z((#XLzi{8X><603f^vzqo1(Y<`a|;d=Pit*b)aPha9vVXdA#zW2J`~3n6LJq6n@Vg z`T1gh=3E8qFNRNggRvvp!-IR_Zi)@GMzD<;cwOCNBw84d(MKPtpCZG@2!irj)#411t%WU7m!deApu__^6D=6JTUNbi} zCj3G}vIcr0VvS!zykvO0eg`cI#qa?oM^bHE}yUe*4qAIiJA1 z>%pY3>K;B2WxGa@{^!O+Jk$XcKyRb|5w8sf*+(Y$L2i5n^p^nr`1=j z#|%5v((P_XdA+@06-%Vt8r@%P>_f|EFp?p5K(OZE0T`Zjhx9c; zBcmT_3i{HM4Sk0ERAQLUIPxTD5qSzg@l?Vi_A9n6`a@GOyQ~p4cxvyZp5w~}LiaVD z2#e6j!O2x|_~f9!v&1swpd8|r?45&O4gEN=&%9M=wB$YLyg{Wjya?(<+;%!|d^eTW z-QSZ@$x>)PU1T7!nf4U?Q+%eTZT7@R={{=Yd@pS7E9yx7)VjJzCpx?Ah-T7J^H;rR zh-oCyiL)*&=s}9N%Y0?m4alz@;D*T(i_QxX{{6Y&XHdRTw0ouf<;Bt zf##{-gwWkw(Okg8eRCVUiZ3TK+%nTYS6_tkS0sP)lu8f$8Y8*gd=ztqFGVL8oj_(q zT;9WP?+q*?)~2R1WfkzZT}c7>;k`3PE0&C4Ct5yxE?UvweB!Vl?_qpaSW_9)Da(xY z>vTiKBM~Sp&W(+f4I!9+I@gR73OoM?9fkm$pl4U>CN)~oKDu}wY?0Nxkt>>Wjw;c; z*q{36Q%H$Wa#fxR*O^UJ;fHPJnE=zu|M7>vnEC91?+7yzO!`a0O}jhr;wnFJK036p zYl|d+1itu2Xr($%R}28~g>xn3A8s`WE~}Vj*C&?*{q@O-Oh1JhorZj}`6nYNsuqPv&$j_UjJI+xZI13m$kD{ZhOT8S_;0 z_oAT``Ysxs0^H$!K~kGt;8tb>_s>Wn-A0TFll>c1B1R!`bx=Mvu~*RcS(+cGBd`$@ z{9!2p8&fk0HV8D^MJ6rx?WgY;*32Y;+79eOTjFFOdsQATOlz}jG zv+n;Pj!zLJrTF#u0VK*)=DmCFNO(1{ zTM{T)Tj4wp>Aoz1y(gG&fKD z^kR-`pHDr_v+JFY*OJ;U6)nC(8^G^oFt`UF!>I~zu8z8sv2kCZy>pcLbU%==#0V|DhhD8MVh>Qo~hQHL)H>`XPX)yN1~*R(t(ayy#X6mFvevG znEkXVk+vQ^LeNp1PWWh9GGXxJ@@7<$0uYM0onQDED;v^019xx(iAckCw2rl!pyoJD z(w~Lyr%R zSzaG=1Rhmc-o!R~7|Yzm)@e0GESQUknHW4bWGPfVyJCLTc}}XCY&+igYix2kugO}R z$8|%a?}4;Xz+XsKj$RGUVjiYhG*S?+4PuU|OJ6XLC{vm6gewpksKQl97;I`E;te$1 zRN*#kaa~e3EHzeHA^Q|8$jZ>Hf;cD5Wb2^qG+H4}5dx9t>EJhFV4XnVw#X9hAX{WaZ5V z(S7f?yQJgg4H6UK7Dv1kl%oYxrOS|muT+>~ztr|_D+RU#KRSI92v%HQU>r_L|LF@$ z4&D=A@xaCg^X2|I3C@@HSd4^DkbF5IZa{T}#DSR2=r5mC=X$?!_I#87;qu5=ey!W0 zm&faW_RR8E%xJycx{rVLo6L6e1(;lB__+Bydm8~pf--iS1j`Ab9o?E&_qOr(dXLS* z#^HrJHbc_Iy}Xr}MVtMFZXmpHc%i;Nz^dC>IMW?qBiy(-mexmS!dgDK*747`oj%Q? z+@>VV28|Q{Sc@_|1A;kdqPVSLZE`Js{>zO(FiA@eZfCq))!5HQj$dLVy1o2z+qNvO zY0FmcTw3BQHxb<4_6)i5t#LtG;#-LYB7&oOXNz)gzU&9i zvfy~BwM0Nr<`=hIka^E`=hldH{jgQEE8K%-29ioM29~e&%;AZZC5RecS}8-gGO2|k zK;W#Ucx?b5BQcfAe~4yp+ULB4+B)^M8vIHCN{2H?7`ee(PjgH>Q6x(JA%g|1F~AJSinE zoz)w)uf=(7!PCmsQ8OPsVvOGr((;9&<=iHRCK`N-c6ccg?n8<7?X9fD5xJ#_$omxD zc;*Revoa3#Sn-aMH_e2_x%()Nth^fktUXGNESPwnmx(g&r5RbBk&0DGltVim#6u&OMZt?qzOW*-{Igl!E)Ap?Yk&cw7o%ySUd4sMTyt>#Lay@wuw@EFO(f)7cyfTTL4p)}-rP<$vG8Adhe zi}%f7$jLJohuBiP*yW%T4+CU@O|MO7;RRN-l%7y6RIlm^oL(hDz4SvJ%>_aF852?yZ$Yg=e zH0xmKwRgr_a`+S zj!e{{PC7PR!8T{|h$s;xJ^d0$TB(Yb&Mc#Y!z`V+gyURdd{uwWbslNuyHg-aS#<2@ZE;v9=CW zHqzC4I#TiWu*ez7aZV^rb#fc;o%_&`BdNE%50<3lo8sT^z-^s# zv{y-oY;3B{$w(SL54+KJmQkF5ZfP>p(`jcukYD7uWnO5ydfCV%9KtAE%~wEH$Bfy2 zg$UuDtjpg1tE48I+vEtAjp;^~MWopbqpm(%K>k&fWak7Efr=P$p$Z@nGqkV>%S14( zA2&-p_?r)z5TLeHDQ~p<;ZiVAVvFdb?~*cvoe~XCLjaZiU6+;}?FNmcZx_q)=?6j) zk#^w!8gLGO8r2B-ueg%A<#}Ue8Gx)#NU;b;MWdbbRR+;H&|!MQhiGVQkbthZ z(_%s!l)H@PS_=eIg%E1Nhnq033T^CVEyjH4Zc@1#eCEyBGay(1C&tngsqw)R0zU)d zkN$3=`oL{OeHjcuMBglK2+qVMkgI$AZDdia&r|B79fFTrMHbC*iyQ^gv0qIf^!u1=uu)J0( zx~G032sTjtT^ypT{<9{pRsS%>(LNH1vHFea6sx}s3(yOmU&j>*WIX@y1RWCC{Aea5 zXk!%!LdC`HfXiDbV~w5`Rg@{6;E0J81sm)E(e4KiVo6W%DOT@IbQpi6U+Q zdVaGIV?8`eeKC0+zJTKy1R*wmn!txF0eJqXs4*Tyq+s*sfNE+U^PhD{%b@v51tP~B z25qp|DNxDc4G0LbxQS1X#c!s;c>;@>-u_r@qXWm^)rH6L8%wNk{6i8PKhlm4e}C=| z0RDc_$DP05#pUl0sciWBQAI@l{&0vdj=PWi4~6c&SSlmR+b1iLt_`^w3^1fvGV4~i z=8}#sPsrN5tCPbs;%4NoE-niXiJRPtT$gXm8^x+Rm;`qX!Vs43K*gc$AT`k$D1Ffu zXrQCtI1$njK&{$=4`Z4EsXMK}2rL=_syJFpi9q_w6_l=$b<|Q+$Z5>gr=b|v=p|l+ z8V`n&HZo&{9`evuS8#N1cssw~4MCi?bUci$<7{bbSkeXx*G|T*JL~xN_ogvTlO&)m~Y%5$~D&5G7BGKG72{r@(8(P=D^&Njhcm- z5TO@?FocCI&_D+>Aa#eYTtQ(d#x;1zR~x=@g9x+mAqJ0-OJbvDAqHUx>t#Ud?uBAp z^WL~Y^p22Az90-?xloL2+!1m~7m9IB3&ps`={V3tC}Z_s-S{BxsXuv!=R;I~(Tom$ z>PIFq2=dfFRyKhkrG7CoD4A0K5D9@1sQ$AtF{u7BGhCp0Oqx>v4KQ@9-qOYO6R{|d z+KT(se@26f2hWWHI>SZ5Wh@b6k2G<*E35t_+?G{;nv&3}f5^ivnr6n$jvVzHk#dgu zhejGOK=l)gQ>*^7E*48x6@<|fE1vqvN;Ez7m#LWg^VC141%gefADM!~5~%*OETt5# zzlef}#ZkW+m81S43u@5pCxT=CVh4TD{79<{2zmY+B_wb*KaQ#u(EP~+xIpt0skr2T z<}aHfAd2Ttu6PTZf9uH^n}1km<@u2zf>dzK-_<0g!v-|Jad=}8H2?63bj-g-Bm>P) zM#bTnznBFe=S~$W$&cq>c35)!=^qG29RH9G5jVpEi~nqcp9~g1S(66C;xE%c1dqjE zWFn)&;!j&j@q@)*6v2kYUWCOR1p50)E@b|GF-R7Ff6Iud_4m=#y`;NE9hA8HVt0rf zDD?J+Luo{Bf6PkMoj)eXP1M;>6G$6!G!oo33GVt1Z*E<3SJ%X=!&~CH30d#lmP=A_ zrL9f3b$RDzXQb|}i^K*?OYR#90L`t6;AaorY1>Xgoh<<7o?aoXsZ}Vp=@UGJX%Sq| z(tJLM(s-b*2fg^w7vMTNj@%mUrp$|0qqV2aY-s8+gW7ZgdBQZ8HCoyLEm7JE>v{ko zezXRwqn#(#XaO>K(ak`6nz=z!%b{u0Lav1AA563~8;~fCc63cfKXk{y`XAM>-3y{G}W^T~TLau~mHXu=%jLJ`r;b%?WDzleMG~4nA z7g~AAJv#+;@(CWo@SqnzSdG>m=CVc$Yp^<)xj_@N0g1vf{H(zxnr(Q=9iQMK>{X++ z_iC^@FB_1kw?wn;J;6hmtHJ8HC7Nxn2CLI*usTlXp(CkI)BYxXjZ@RYf1-4{bV%s5>!9&2<6G@7n>nKAy)rT zMw9HQpDYGl8rP3ps;iO$rGBFeAR&}I+MfDztc8f`H?laRanygd0{KI!KWPkGq10bS zBkrl+EQXt>{vp>2ET!H8WA!Jm2zct}n#%IjpC$syQ-4{C4`8DD&9XYg5Y<0K!9?WA zr?OG%CmSk2slUwal={aCS2*Th6Tt^Gf3bv}u48^=)dY6f{9PnmHvjPHviVaJ4(MX@ z57$03p5M6r9P=laq#g4U=a$g?TRmbN^CQvl*!)Q(2{!-mgNqE!f5S>t(FHXB7}gQb z!!b5L(#ZnPZ+tp};g~IXXX9ge7!E-j;H2 z>(a>DTqe1e%sD!Pe&xPPjF2SVrD=!8;^vF?qbt-?hA_02v$ZqH9W8*>s+)J{G_zf!<+fDq z1_BQ4V}LDs?ZqE`_Gg72`)l=A7#;m$1u1PrKu)VjL!+@0xQnwjmI z>;?jky!PTx9{X$aixs4@iZnEts!TFNB@t$%Ay0A(I}1l?{J<5sFaql4kx`BY>Rgs3~ZDD7*esqPdiZnE?A6;SV zM_1_dAV5xZK-+)yBaH7q^FdYQ+n?+qf{5LIBo7c19QV)d_&_}FFVg|lW^jL!%?VE7 z{ezIkqsD9*%wwKjE$5wdMkHnEdsXy6u_NLT7cyXdg>=SwNU+C8NjUi z&rHOy>L0p^I;&a{u}b|>CG=Fe&~?mzMdFVbH2)fv2P2O8kr>RGfX$y?S(C8&%SNzS z0GdB3qRi7Ve=)=rGiZJ^DOMkcM@+E!xj?)zkNM9&{19=>Ka4tCdj2yHHV!!EPeK{* zp!tyI_ z$HW8{|1e0NpWQirV+IuvIDWG1_={>96#Dx*j?4u5D@D)WGZXxMWYqinXAY?DJ{Obh z?hn&1cORKpVKu!qui-3v9WOc?{08!Ri&Z_ z$Ny~x7mj~O1L{sS$w`FceX1sJ5MJBKXizTuc|c1pmoE_i~Y zu50J?q9_w`?tV&!0Hr(Ce^+n+tZF#o{#@2GM#^&@SCQonGqcT=lNaJ{`8ZoK-{gjZZqR-!=4sHH^8h{N>xKkxcbLZ}- zE2J%OxcliK1c?5)`)L4D4SwE!D#*`U0AS3DkStoNK#*mVYTS8()47HffmwHlWGPpp zXsH4V($rHTcRe!hcv+)}h{&(rD+qJf?DEd7Mp4oP6xgugu3>obqm5v7bP7Lfw3QSu zdW6=V_OL-yH#OTdl7}#zuF=wi)@bDt+;bkVI$2PPmkhxM%{<0KSk3|@Di7erPk!QO zO}5k8%WloKjAM;fw&9+QV0E&U6ffDs2F;A*AuJDCqlNQ;)xi*K(8O7QMByiX)?l}0 z8@Az&t)zItNFKs)9ZkqCqS!YRPt zkHX{c52Np=CJ;gS`@{C=ezL2Ox6c(_vxXdn4I516@a~pEjN~!t=u9A&cX>HdtTpO3 z*SvE>D_WW$PMUhkj=5_bZYGcb4)jgiwry)B0Tk&*f;sN{#Jd}}i$t52iz;2iCQaSW zy>%Yloy|HMMM!HRNK-e^-F0Bi(umtx8VJ}Y9VHa{R6d}lZQHgrOY;EIfi(N|sA)4Zd$d@aHci6c!8vh z2`FO0PTB(Ta>v1DX7*qspFmiI5fN6mqA;H{Ki&nB^6R86@HI2D=M`Z@^zupb!*$XY z=!!5Ra-FmVwocjtolZ8!fS;rOD={%HTt8VD0&A=OB-jB79QAYAxB|!OH`cJihSfj( z;wymFKSqPqKX$~>r+%)kLU*b&*utoOGZ{|CqB_Q^KL?B>D@64pUl7?({bDZOUPSd5 zTeRtL{YkGLP)Gen7HrI;{^42PQGZ&P1+Jg0Sfh*UFY9_C2&$iqt7zh=pSYER?5Y3E z4zUCbQT;^~BIcf2>%$AxpG?t#4^;m!i)oD2k3^Ayiq$`cHIxO_uXg1sRDanCL2AeR zXC_3DgXZ6|vZKKCDz&|vWs zlZJ?}_}7q5Ai&=vG9>(cGNU@DzmF7rx>HqG81eVniVCUReIk*YB;xMl0F(6gkttH3 zx1U4`Jdq-YeC&+7KI5*LcX`c~jxI0muDQ|)nZvVlqob>PTeF(WJGZwLFAJ|(2e&ep zMe3RN*{OOcJN^e!AE7kL-gc(AJ@%+PgVbJlXBNQQga{R-i zFm!#2h!Np=bZ6R^5ZdhA9n|FSr_uLM4(sGK#S6Nd-2D`U4@W{#Z$IrrDC+G8X@?5RdOHEyzCfE2yGV$kZl$kJa@5$L7S^;Y^yli zva^&3+E)mKZL18jY!WOK?kOr8TSOAe9wG^}qq=K;<<> zxm@N0HW>lUl>dmzyw?Gm>~z(PBx^?|!U35asmyqy(H*sMKbaMFf-pMpx(s*g4}- z&6wPGgB9rm-tNhvf zfpu*LAdo=YpU9DVwm^3NaV5Un@wCKt#CgMbi%`YPLnN}?T7 z{RKXOpmUl}=^rttgPL=~-;uv@&~`=S%>(Bn5N*a-{~LZqj87R`Lfo3Anih?6aq(7& zSU2oUs5wLE=daPwt1_Z4h?M1c8|OUfLmajaA6;F9Koj%PA{RnNLppQ#h5r)3Gl}$> zR{-vAvmJMz-EcRseCGCY++`M2(nDa5oIV(lG;SXv5ZpU!d1~&!f_7j|aHhDsOV}WcG^tLcEeC zQn&I9umMoFkStqD>~^b2@+->2aG^h+byY9JYrl$&Z`d$YXSrpA6yPl&iQ3Z}e?_B; zzQv9;y>e$e<5U|%VF{uZqu`>GCtCsKOMT|5qAE+#qL2qsDQcR)<>1Y7_qG&y-m1by08 zb!Iccxhj&54;gdnw?@vL2B%8~wa$LCHw*SgyX}C$6g~wEM4)$Ao=9nDyg~h*1awsu zm$2cAqY4-_x>g>@GM3F3t+uiSm_t=RNFv8Qe#o%5!!21eA|})>2;oUHeJP)>|GK)I zx79dJ`G+^1TI!5PK^ z1=d-kKjulStpW4-u)3D8<3?)va$W4CMgdDrcFC~&t(=r%<@;dP%^C11hQrxT8OeW% z>o+Fn%zGBVA>ZTpl#tD_$ySLP?%zl0sZdr8J`k(f z%vvF6sX2(oLrPD_(~OEP)h!6vnMMyN{yvpi9p&~LqqwmmBmuU6M=qk3GKY@ecrYl+ z!a{YzSMMK?B^Zxh#~d;EYxMwvURFx;;xI1$t7*C{CT1fRb40srlvR`8+v;FPUIo!= zyVWCtGL!RdFUhPc&2P55SqaIB?!Rr$$A)~L>$n8xV;Yzy8!z3o#`)$TsSq_5f+V{p z2u-$&d9sZXbtsp3+ixyJ+-g0tv~CTcQ!b3L6v?Tuv{H`#Ur(vS|w z=9g?|7Ny}YY~`DpxNBC?3WFfCYVmMlBimUPGOuR++LGqBI*6Iy%YWq~!zaE4vTZ6? zG-aUirAe~Mgz6((b6J-ou6PVa=!vNf%;h&!VZ`l>5V|ZMKZ~v#BhvQv8(sSNFBUCQ zv_2tt@avq`4pDdrPzXX~+%*lZj-f^B2mC9{N>4thz0*E13>GWno5?-CkvN{$Zn{R_ zNQhzipX{%Mybpxvp4!trevb>tzV-5T(m^%4yu?IWN^gmsymWncAFSuX6#jnjjBk&F zE&A%mcsMB!9Xlci)?q*xptqlE74$qsF;4Aay?LE;`CnKdZr!^yfhf^w)Vk8!AgXhER}JN|xSmg!=%e$BZr2wAb;<{MTdZktR351|t6csJ_OSmf z;yY8;V~@+a&D@ar@6LTR1U95!wN@7%>H!&$qo0uJ8OjD@bI?4gE()pq+17(kQfW*C z`5DBxtiHJC(0JsbG=R-WXIY>p;+sVi&w3Y42?;bM7PH`Pq@DRH=F_?{g0zF6?fR8* zIBqas95g44mH4%ov9-4mWWe~7pxcQ~bQ+{o(ws_k(sii`CBt5$87k=-#1ub1#2HDZ z&)dRx(S}p7pi*-_^NQo!FzCHB>x|2}sWQZnPUEtSj%?A7NHNAD@0~@Wl+j7L>*%A@ zdgpt-r?77q9XGIK!yjMr?YbiJ$TE7hoQo#SwO@FmR(3UTWy*+X?hNR&gD@3jS5VcR8}u33u@sZp4tw;p83 zINMZ=LN}dUGL zjm&wj=Te|(#k>Dz7N3{T3ncujq~0Y|BIW@yg!*YwFK6wo%ORH^z|8>}o(wjf{>ISC z;G-@t$YJQ^S8uec;JagGHUySc_2qMRzGeQRt^{Rij5guAbfqo@J*wpS~2YH7zD*wSh+T(1z&QJPr?3q_EX6u6vGxmx9 zlxI)nr97`!mNOW)gA%#MsKspcR;fas;X`-}E(&%o9he#vm5Z;##7Ey0&0(6b&G*7e z$C;f=%Sr(8l>}i!8r@Q`56S?Gpw0a1O3T(6ZNJhuBNpG8W%r0g2pIHCR|5mNdKrog z-}071Apa4B4mLg*CBrcF6`IK!>58mr!Y+2Hw6sDYa;?8j8+Ke4X3?LS;S%1zyoT#r zul=n5Z4Zp(KUwA>eJaM%-|?4E)zd8Naw+bIaIlYDAJW$Y(-}Q|qG;Q|eRx|Dn-$4n z9izBGoa_FVH!OxT;;5U5uAzb-Vq`3?f*Ei;l_#h*?UOxYc`av<-q5mu#=1t~?AzBL zv%4B!K((@s#cz?sBitH~GK*F08P^;W$mA!V*>VQ;_!cQj^a$XswZY5U2IwnJ`LMqJ zZm3YY$b^{}l%&qK9-PzL{sU4yZ$b!5`j)b*N^3*l=IcGC%DNX)yd5~11X5QUKW@TW zdU>qj#@9gu!s{Ks^j{)olw&s{rWX{|os^G)gY4Z&|Q8uh*|V=Q6k5|TZ8i?XRBIP<0QF-#zDZq=8EzJlOf;Z8HGm2xv|J>FDfJ1&I0CfYJtTgYe3mRsVUEimI&n9jp<%N3$aQr4Kc zVoD7~kKC+cqQOH<^%;l)yIiwrthP5KQW?|2rWLQ3M$3>`K0|6OSz1%>%QWxr9@%0; z2{d#zSpo~l_l1k7OoIV5>3*Yq+pW)9DAqt?AXXXujJfWxHU}4_1ClhVPIEe zM1pF>8EWoS;f>8@jU55hk>`jmw%zTZ{EW}5*T)9SC+LL~%s0L@lGSqe+G)(OO}oK5 zK@EpB>9OC$otLBg3NrgtSab?N(&J{{F9_@*lqjK!dejSLN2no}PTUr1qj06a(Z2f# z%1k8k%-(NOlTjhUNf8abGMY5U^BTUa9TD8(U&zm$x}FT{g@%SxaTS|RmkS?)<}W4O z*JNE9ubCMbX`W`aXVrB|16ex&d_+!fY^S{{JP{#KkKJ7rRTK*j3Kl4UaACye%J)={ zwaVI4=uCs;?#EzjyWrI{@B#G7-}o7-BDAnzxtoA`V*_@001v*?KV3&SmAgvCrV1si z*au?{VW|q4o|ufcW*^ev!Z_bVMf4I+vpd%09WUlf4Q+yV1h+{>%||HTRAdXk~! zLit4kl}1FCqjwRsV49RgZxV5u@52$BUZK>d-w;Gl$dF5qzp{2_pDC4|0?mhSIaz*A z(UH9ewr|Xg@c!Y;m`*K~>{84k_Ebzpe=K(RMlMDcyBCW+mX|yRZ*8g>g>ORcAu%6| z1oQa33=9WO-yDy}j|o{V7d2c|E}G1!&G#=GdUv)#f%e)4&^cizWKW5oO%{i`vl%+0 z2ZdzUtDtR#j*sLms^}5@-Ohq^K}=yX+@rp~nyDiE+GJ6BiOO*tCa5YcAbZVSCD9x3 zk~9{77#*U^@sWCYrFp6XTxvmGd_p$1AMe7ChgRiEZJ`~%>s519_b7TTVQJnpHkB5s z&B40KDJp?$?=T!Vk+Z`(AKeU+8JZ;x`$C*axn`Dt{fq)Xk z36#`e(?MlXb{~Ps3|0DW+V^6%1S9lD{zP_Psb1FDL!QMH3z?e=r`{R3_v&J)n;&l> zKP#l{dj-a>)ty-e4Iv z-=G(uuEL&_2R31~&(Wuad|xUuK#s-geWWxER3L6Rqya}BZfJ`d@h+;Iz?qgWWH|+R zUFx8sj6og^!N@3wBPMhs8ywt1pn9yQ^t^ z@H3Jlda43e1CSzzxit+Craj=sNhYMq!WTQvsj1~~0n)R_Fxsul5sCxS=Ee(z=^Jz~ z{nL+PR#W`1GXYhU!C@VV1xN?+e&ESJ^#>Ni!f)}7$9i4iULgK+8OYVGWetA80&QHc zA^y_YAG7xPNEpF6X%ZQa_%4P84k3Xs)p16t^kXcl^uZKysMkc-nHT8Q3?i=?1-o&z zULs4YCCZv8MN%8#s{eW!AGK9%V0@8c$i6s@BMpeC8Z;3FvUNHs>)>#s}qkVH~18eTEa_QV(SU{_^5mA098 zQ5=0`AAJ)SdxAxKStn{=$^T0ws&NCqcHhn2Ah<9 z1=GJocWfX;O`S8~7a$dv{tZB+)}(TN{wSvwve=-Jgq$oK+N`pM6;DAURg(@J&=Nt5 zzy&bm$8-Z+%y3HqOuFR%-XJ}7=04Y>3`r6|Cdq`d9e0{eHyD<|hw9~qLPHNIv^4BM zCVj?;v%uAd_(?+uoJr>d9t^8%<7Y|qAzO-62bd!Jic+=m_edpLnW83V-^27T(H~P^ z`I26T=M2xUIeL7Fjbu-rb9S`9$HQfw8s`$1{|rE+)|Bd%OKsf@D%_)`gTdgOFg&}p ziqkZQL|tfI>b%{)+ek5W!PKhzWW&)TA#he{Vrg)4X~-rr5ll1VXxRIR-3nS~Fn)jD zuE-BId}YjUDyL4xAEBH|#W` z%VaH2nzLPtDk3AwoIIiK=)Z(+a_2UZc_x#^802C(JM5u_OriqWSdd$?BNF>Sb*?r( z3&kzENX{=QOTvy9U68A~Ze)Fe0qiSg>urSj%ge{ssr!$+wW|%5X;jA^QB85129m=r$N|ppQTn^W6qwTt&|26z zl#i(XAQtZoG$)#4T$b z2tIfnLZh~W9r_M(H5m_wztaPes_1B^3asog*X!$bo6l=6u>IJ2zM($i17=Qa`LL0C z)a2ny-W8QT!IC|%f$*|(@TCH#-s%F+!wR1|K7kxcfNZ4j&{s-~1_KgQ^Ib$dyGKAui(oMy#9wa|-c+ zG9>7D*KP6Z3m!OWx@|o7CwKXmt4WO|2Q<^tvW_>#h7 z#jfx6#7rGpaE){GQ z<04C0)rhcMfS0WhGr(Im>5c=>#uOTfVG?+_#_X$8+=}xx>ZA4I9aX>ozSh6Yg+5qdx_lAP`TPZ7MPyFW5^=X>Uah<@Z#h`#sMh9RdviU?@B1}s9LnX4MiAcl6b zS5T!)o?$om(RV54LBC;Y>(3F&R`EGH;|AR!>BR+Z z@)5&9aML$;158ZT6l}g`ZC6zet}f0#JDmm(66>8S-%!5%CU9wWA;e5q6?Wj(aMPIr z8@RUKU?eFru4NMgyIw^;*(yuYl8rn^zgFVk4N`r8prtI2xs??G3%E2iS0HfZ$QgP| z3_2=SA#xD?#!@AZ44Da$gc)bN2K3VoYe=Mn)Uo>o@TUp6C2jwEZgun9F|eyi zMmk6WpZ71_YW%8iDfD+%U|FV2P@RO^e-2N)!x`>jW82xbE*nVAS>cD3<$txUwo(*M z1N)($ZH3BYG~NdmmcAV%M>|1>ykuO{EyDP7J>rHWi9L3jpnRIWb3z8fh};@*rQE7h zBMR~mAD5C(xK}&&8NC?jwUHF`gkhJ^x2Vq)P9J)i&8nR>dRKEs;+p6gQ`DF9`-o~5 zNIIxj2PpEoHxS&Y?1+s3CW?b@J?Lw6cr_ctR)IyY#K7|gCvYYHR}F+q%aOjBP-f*$ zY4}&gA-z6&qadhe;b09=7>`>U50u%FQbXe)ET&!ZqOao_VRl!_EHyU{?nNPxi)g2f zJ(YbEGn848R`uaH*f~=(cyW~33msffVYJS(#9ENNQ)cb4;}0gM>C>{A>b+&kEC_;@ zgD+BEE|RQU2pGW%t|9By2AEOp8ey~P^iQAocP(AwcdEwbk_WAvZ#UfdDrpn@&XSnY<}RHQ643IykTeG?dzZ%up2zuxPTj6xM)I9n(KPF!bCsps6r+5J9kPaHC_7Kpy^GjrchqE5UdVNdb3~x^ zs{&30^j*v*A9Sn&&aOHd_e*I~y5!FE))XkA9dw@&~^wIVJn6p*yImA)6y{|D@1Sv7y=^=TGfa?{3tIt ziu+##XIzC2FKSS_Rp2?De88rV>pKO-uxldBX zIp{Lco8d3!vlQo;RAmM|{YRbXV&<-92h{zQS~Bd4BF_t=-Z15pEE;L0x_gBtvLA=w zJgC04G-(qTt7rpF_ zwQ8(Hx^vwA%&lb9RHv3mH?Mru_c}@h=@^y$oqb1&GXh?^wt?IokY$7Ab zlLqYB2FJ<fpl_Oz%2#+J`xe{BS`qvd((%eUkQO0nvWUOM7YyhmNptcrAOEa z@4+Y)HPx#<86Wl0<$91|!Q18mcU`Cydg*bGYfu?1AnTMdIxj)gE|Ye>$C%l6(8xIW zQzX{f4dBr+W=$zCQa!0<=_fmG5!E|8Zsa|!N>w*WS!4Y2-Jn_!V{fD<9 zh1(BFuhSaqB;2Nb$aHwnL_cNW+8w2eX^n*z9bxwnj5d!}^w!g(sEEKjtWbJG4Q&U>wfT)(+9RTnP4!8nAf47m#k2W1G#}f482tVFgxI#{kA#dm7Fr5FH zTF(f+a@miF&v66A!AuW#4PC9)>mzivys-|xZ~6e+b-slJ89UlUs9t-7{mDg9GS_s_ zr0O!l*02i;w(x}D0obnC@@=JNU?r7jA^RJw+72;G5dG)S zdiG!fKvx5P%z83X*a|aGb{P2|Ub`|yiFL<|F(uT8%o;I}s3htj3M3_P$WUM~FtjIi z;yD|0$5*(d$4X;it=VEgP>i-yKtbHd1k_=2s^>n3Y4&cN8EhgR9>4!UOmyjUf|aoD zTocg8T^D4VEphia?MWgH;(rSoDuLy-ToMMVG6L~`)U%mugwj5HAa+ehuZTt&!qc*b zQ6_WBd#HVOKiNbA56J*$|Cv!v`Qhc{PxW%g?ftBfByb&Wa002yttEyVacaPOoY&`IZVN zGhIoP&vJ3os(dDOO$Af}ekK6Bo%fCMI4i%v(g*ezzPf8dJRWlw=R3?<435oypV}wg#`#D^~IsD}sXUgE(#xNyH1d`%{B_Kmv?f3K13L~@K@t1%@U zhf@W1cE;fr&C1@x;`Or?wAC2i{1ABq;_J$^8(?uG4ONtQiC{vcX=|`W(KtG}%gGch zXo-E(U7rj@seJS*E-7Y*u>$rK<|~SFq&+EHyfzQ{$k+n|9Vp7V?RZhuYOE`6cVNWq zgYIy`i}^csw5gWfvHtn>RTv6`+(*8BL4sBVN#C>r95L2)BtiwLF zw>dSKHnEb;sr0ypFF4dF%A-w7E6NxeD{tyO0KJi{d3YUH;kYD11f4H~-?NuggR~h7d;=o(k39ka7}|rUu@A*6mbf5h zlSirYM^H))@fEvp+UN`}(v}HIwaL&lOGx-4LK$Q zQ(HOk`=#Ac!exv_=YJqItOKI#+kX-F1mMInj)lkAXLV(lk7`~lJ>s$BmCZ+ z2~i;Z=@`~-8nj&n(9Yp0d3T|7Uvn%O+@(W8n6GgP$O?kCRv_!2mjIc=5k_lf3UWeF zGWfh=%~)Vl{EMj~TpqH23U+r=G02Kb?=u_!0SCHMm6HBy`&wq-P!I}!p&E)-Pa9lEEHNQyrWp~8XSLYnbppj!|O+KhTKQpcnG(Q1XjJvGIA zK3XWkn!C_|ka2>pFYv6ef|x(WssF<1Rx6|1YGm#)mOi1dz$|)(q=UEk`HzVyemnG@ zFmp0G(WX>fHy<`U7#Txj=jSge2e)KmZgY}n8l9}f!!*T6)S0;~a75<6C&8<@uQ`vm z>bi`UNbs-oo?scTgoPeqO&^M9lk~@8YtyjhKS^Zuip6OZQAqMTo>S2}TDm3^7{0SG z;0F6jesW8vy*3$bQgb5bj}v7_Q#^Cz25q>=(nm>d>&UyZ%_gB8FIt5BpV|0LeBNr@ zqR+C?owkR#yDF4*Em2;7R|DzVZ*7%&vVM33ga>`f!qIbJX$;c=pw%}po4Uzr*k#C{#3 zp9?}n^zA6z^V1J|cm)^2w(efCkx2=`Ir3y&s&=fE0KMroPumwaqi}0V%a&M$*jY_# zU!MAc@tgxh=^Pq(6`s=&afOVDcL>6j+8lr zF$e{;4sBc7@AF!jxTnwK@Q>lS4maI1Xrr-UI1d!^)vZn{r0c~xH6|Z3_r%KjR)pkg zFy?<%q-O}|jUSh){uwzsm#`j_6Sq#A6d=1bHYc^42Q0@h_DdB8ziQMle=MNkby3=N zcW|rQRbiJ$X3%V3@%?|gE|hj_pT%!z;`zo?F&>;6wG-%%^lHXq-VsUblY+p-i!vXs0zL@PB>WqsL7sb#e z-($W19IRotm?bbR<*D~KNq@m(L2l-KoD>%1YCW_Ls7h~KZSP|lY$c*kh{<54YFciN zJi?dz$`AS1bk*Hdmp}+m`-}a4%h+^jF@knn9?{zNg%y%tvz4~!wVg6*FZ$(%yy#HJ zlA7KAajCer%_)I!1;?C4P3GnY3**tcPTiJ!1#?0zQcXcn%Y{{NY8}#ZuX@HMFqU@sY}XfUoL89?$VB& zz01u1Xe21Li6e8VlqH1Mgyq?IMxYSqAhXdU?l{rlUUO`zbsx}QHMKDS zC3thMn+X^*sA-bN1)7=2_=GX^01@tJ2%yNXVVXc*o>#>EEd^5@4HYPS&5=O)J6q#r zJgYoB2GNQr1|LQ}@SKnxE5QD#3%Q?7v9M4ncpC~rbyhtMAQ7USH(M?%r%Tq8^V!?^ zGd5=CHrD#EQ(sU$lj}EL*326`#76Y>3Cr&fYQtZL}jfy=N@} zd&^IJVhRsR&e=WsS^dYcakb}Y`W(l$h{g#I@;f;Swzd72n*77g)N46iPP5+-=OgT| z^n?z#UXjXk3;)#f{Nd@#wzx3cNa@B$=s^3>7a)p^i^%b_()0KSnZtLpKK!t2<6vZ| zUgP^=syz5PIaJuh^gjh9`*6n>NPv&NmTbh#inK3vH?OHjqO?N%+a8Lv8)nlQY`>^Y zhl&sNd4H8|__mxl#{fNI#Y^5z>%h&*+x4)B9UQzU@G%D#Ou^8%!G}#$FImr#-#_bN z$J8%KPZYS5ey4S9WJF9_4rD8T@u;5VujQ_Zp~A@M&}10QEONPB!&O5PN1r+w$D3iP zlC;Wsg)AQ9fC*y7<=qZ$7VP&P_+}3=?@8t^aqIGbSlHJ||*q*J~RLt$}A8rL)zL7kROac#q6P-+PSL0}4 z`^?|US>0Q&+Xt(Q(qAVw5PT>QbXJ7;)?0O5Ah`yM2bR$ZdyEC|jY;(};o20MQ%G+d zf*2QI1h(4E1N#SQv63p-`CW4-`I0RjHHUeib>|%yer?kETMx#+Q295Ss|abYl$jT; zjDi}ME@~O>Tkle@a7CKxI0vOMhYc$mvVY&$v{=T1G5QyHIliSt09e8@o)Yy3iLR_@ zxAiKvr#6+mdK#9D-b}0j^arV|DcFzC;{_Wr2T7k5bdpM0?Tj`R%J%HF@di#Lvw-3y zQYHo>ByIe8u`=VmWG}!ST0!GF(V024bfN|gH@i3Qvx4$>J!FTb1BLr6#uZdzOB>I5 zizDOlhnqIWk;}99`uUD_s2@0LQ+Ar2j789_(?CTiLphO#_<6|6gT;u-+F@9)5|6W$ za~80!s`l@kyE?mFcYc0h&Rge+@qp()^zUzQam$WVxs0rK#j8?G%PTbnxfz1JMJ;#| zkJI%%&){-cSA5ysf5(IJYNO8;_G$9&n?(m}BPHN0oh>P?tnTIhds6dP9uth_mPt_2 z!fn*NDMOzHq(@#;^rs`@ESeWPmb$t0qJ}r{MP*u+hrg+{FTFvB6bgOM!i&9-dC#fz zr!u6FK((Ww@}Kf!#c&tI?aZW#j8LUp@-;q4_c63=)qD8{~6P`Zjp` zsf^#c9N+xf-aNR?eJp^C(Ywz|>4y7jj#~9~)DzZi|L%V+i~UG`b+2S@#Pu8{8a`JK zj;iRIf268$7{x3ApZz;UDiFxtDAAZS_LcA5ld9KQhjE?pbItcSPe-{j=@P zje?`Sx%QS8mXThH%@iGTTxCr(+qP|OZMJQ1ZnL@BZMJP|!>hJl*4AcjHea=A!>jLo zf6VU=&N*{t>fM=pC`D|ng#1iCQwgmbw82_EL6^ZwY?--h)e={4yoPfX1vs(8$*D|q zh*{;pvJ2C?`qmX=-TAMo4xL-RzqswNT*ERkZho_kl2-xD+~(X6ay_y=vnGdx>K&A& z)HQLOPA%3AP7wsp+VPJ`;+nhb05z~U-Iu@3|*9(4F&bjRrgcl`J(^6e4G;6QI+iO z>dEcgy!lWtWPG`UmQ0q-soK~osFzi`uvXbUY%fL3CIg3L@b#9c<2b3eGRk~55=UEv z?Oz|!#8$$>iaGPLvXrJ32>I^Kh;HEKO}X+MrAVBu7UpYyG1?h& z4!?MsH1RA@OK{^EFHF3MG6uSc(mumd-B@Piu9+MwQSiM37K_i$?QH^0v4RY9eIO}H zgKhWoL^7gpvN6OjnMwfkZUjxvE*50nYWQ%LP3(V`+(-rJN|tG&i=*%!H8pczW&OD( zbh?OmA$JlewO&WZ8VUu|a+y!|D=nzD=L;xcG8ZUEv<8J83#x7ivT-38ryT(m~Ve=k-M!Jtlk3X!Z6?5f*aEw35dk3zac^h@YUa z@?SV&OQZ8G7X=Woz0=v%1G+CN(?v}?q!a~+#pdPxbvAP7YHnMOjy|b9wh<093Wn1S zic|>ZT!#}KlSag@{}R=2)R=<65n~!tJX-#P!?efPhYSE5qHxBBvraJVF?w`aGd8Jb z2G3HCOO~>oLODbsVeGFyu-XAH?`arsSJ{%fU!8$O-_chdlUrRkAW_B&be=ecLoJ>J zTpO;;m}?vsKxVZX#6RAy81G<*)u2_tcJg&EGYHvDW~k?J7WHPAwc);z2rKGxx`?%w z=a`Q4HRop0p$=|fIKEcFj+c3r)9(A&$dbB{#sRf@;4^iC;)e$_O8D8l^n#hLcmV=q z)w>NU$;R7M1wYV`08d0bx0rCYcpBAjn{+mruTF9Z;sov9y8E0iqmStK&W-+@&A~?= zg2w4W1~iBtZbip|B$!);O_F4Ce^Wjn69mz?9f26Di#zh%4i`?aJ6YoUkB<8LHlHvF zlGHFr`mA(yY5@Uq0WF$aqUEALPj9x@nA(4+Q$okp)ss+6T*I-HUM z7s>be{UJFqlD0}Yd7Obi>7!Cn0%{NRfhc}6ZNk{BoIF}440^BcraD_V2nc$H*iFm6R8axtLE$AJ0i_n_&EDESg;U<*kr@%cW|d9;Y1@+IPZB zlNVMZLY8kD=)NYy-g;`zJ-`VZ2RuSwY_>^Nnm8n#Tuahg*4Yc&~`>KtvAt4)2_EcE@ zo0l1dp~Uhj=&0$agiAq%Y7n+5nc%4Zc7LvnF4i0+c`y0ztRCbCvS6q>LgTFA^K6>dCSO@**F;5`5uvceG(E zCf}9@pwFLIiFkIq%c9~AKBS&7V{@t4$8Tv13+Tx|dL22OS<0<{-_qh6+%coE@Iaf3 zVJS=uO;>+djWj68o^X9WEjlRk@)lk@E%Z$4`(>qQX9M42)R&I9Sxd`!kF#7L=JUM_ zyA;lr*G9tHq3m}670!=>crs!Y`CJk2Lm9|u+Dry?*pmdEB^SBPO{+5APeht|QJ?So z6umOJJva+^*%u-GbW|-DW{44QCLb)euw@!?lEY=JMk3;AvDY_s4`-SY1-U07NIXlY zsI;p+_n()_Fp26Vn4iwLPv>r%ZMGwDY?D4{sp_QmJ`nm9FF?(fdMl<>D_c)P57X)K zor3EWo@d7QJ>K4~S+fImnTx`0=TjA`FcjxK6@C=VP}C zW4Q_k!V`I=_FDj3oS`8wc&7Rztp0Vb*|3Eo9z%u(K1)vzO7j|PP@F~Bml7rB zSthKq624>Ai@(PT5oR#X`>EJi<=@?z{`Zs^@3RXosCeaW`tp(0gH=b1P4D8~-+N8X zn-4{SM@6rjtqAC8SVHey)LjhYI|*ss?r&D|>`BEc-{5fe=C)=@EJkLx7#B-W;1w9= zs)3igvz44H6yFRQ+mr%D-+y*K_M8yX`z_53T%3!?X_Ux3hi&oZz2-+w2@XQu1LO|Q zgEk1=z@0&-@W9ieNqEEeF~zLM%se9I}^_! zHUC)kTAktTMEsMLKtuTM7oQK>NJiDQ`jjjz=NVG$sVT@uWrZ5-1h}T}apZei{eGMv z1XOBwJB;n!HvfA{2`X(0D3$AikD7O|hU>^lVak(T$8PL^cEqtSdx^jnFwLl{TBRrI zN!m40^DN8la2lKnVN3dpkj}W;r;)xqfn-ZyBhT}lZ{8%MD(eF2w*|X#%MC(0Vtp$1 z2;G8=JNclXZ^Yz5ngXu3K#s^FIz#eOab!u&d9#*zm|h6)OZbIZ^jz!clDchZT3xVB zF3KtpK716q@tufL!^wPs7OnmzUjcICwU86S>gIlkc+f1JNoE+z<_G>oszuA@|45UD zXjv%z?<3Xn1+chFXMpXvPIs&`GIn>>*#3=ncLYCXLOQ+1?gXBHCYdG1BaKtSDiDnq z^DXF&+(t-Fy1&`UGs`D{g4bA~dtU7?O4Y^f_vflLY2! z-%_I)gqnB4DF$L^W~DN9Ul-!Ae?zEaGNLnVoPI7ls~_R3R9(F zcMxL4MEhT_2R6RWR0Ff97|)v0LEqpB^mc#59ET@5rsumq^bb*^J*~=?#CDD5#rcMo zlep@CdZ}w+{p-=HyG@@rYb27c2#xYptB-F4&AK)o%_vw4YSJl%Hi~D>%*w!+o74wLUzB_XvCFr)XWJ zjt%%wNU?ZAe%bi8OR(lUX1DHBh0}$6y)0I7*SQr6cIFRtenga3dQC9#OsBqc!KfR$ zbVw1;k1xa(j?dX5sL=AwMDuur6OwUawsp7!LM1sdNd3N~iLwibMSV*du>fB1W7Yl9baV@z0v8=+>g@$m-PfRg@E(cX` zMWZo7v0F2yXQ6M+IgbZwXgKJ12<@)cxoL$gc%7zUbDb)s#s`t0a4D1)qP+B46vJ?o9 zv16Y8_sKXeLJ+>ZP4IbwpiFh^b2S`zL_#-YOOP6enqq%X37tc>+YYkDrx+Uisudk_ zKnm_gM3=BYg%kGTR7YD5AGJeduh8>oE<>D^V@Xb5|I3@PEI!j<8e*^n(>g!HRG7EJw+ z=^CqtXVxu1?Zg!xuufyL3U z!5%_VaUD;KdiGE$A=mXCR5^3~&2Ug8GyRmE5{QI3%n}I1|HMeJxVho_livoR^>$y) z#rOxX|3UKia{8`puS${ZE|za+@z1_$inIfcCWqT#W?psu2q&IjtqIV;eLA0lu761~ z8oc72RurFyBv)?LS6X|$ESyc6M+0JE=~Ui#OuNS2>cJ$h$1E)WF2 zg(%l$oPdvDg8u##kGp!E`H8XeMVRGP44q#{>``{0_N7VmW20*(;t|{myFu2sGa_lV zE-i$`?k9}KhA@~!c!AD$E&Rb% z3-awbUDR9t;S@GhfDIKCLK4p}zk|sjkw-MmF$4gDjUy(3U|m9*D@7L#RyiVj-bqY}yx?@df^`fuQt-&q(l0PGIhLB@*pg91+bZKXy z7wX}=fA?RqDI18~X7DP*E+~yrup=(yzpZH;zx)uo+`D_TRf^V+EE1n#K8iaj9^7l( zdq#>Sm=*6FjD?b%Rqw1)Kuu&)ikK@n*_pzUq8#V6n)tqO>xkT;NL=FmW<6fBa!bn& zMM8ZBVok)M+X}LFHyqXWK67-&H;qrc5{x-{OmOVA+A?4?Cag@iy7JlW&MZmylv-c= z{Q0L4(}nd}iJzSgi@cz*1^T4S=bXPt%3NF-b{KCexTAUo(l?Oi=GVL$;US!aGf2Hc zEyIH4w+0p=HIFP=h&!UL+i+{rY3uic51L4jSlb#TleyqOIuoa2mOs@*aA*xNi-}jQ z5tW|5b2XV;q$?dx3UW!8&MZ-M$Eo={7H=kVGdWa`t<`}@mNkZln)Y~3{zB_yfq9mT z6LgY|hfUwH@<}Fcz6VpsT0n#$*~$i`8R7~!(-E;V2{u^VutMvgTJO*uCIVjjo- zr~7O5UV5NyB`btnj%Lddbvhg`wk0nRYXmF1MG6^{hM&76T~yc?0)kp@{%aUA$D0(E zv@cexzis6&Sq9`D2;sBb%%(FcOKqXpHK(9%_@jy~vr2rJ=_i37VtBi+*@_jMJDj$b z5zzf1Xvz~)PmIcbBs<04M~F!m@(3wIix0TwvM?rajmpWo4tjv#Pb|FRRX>?_Uxq8# zs64^ObabIT?>0-tB^UBi_^Ca?jf`YN4~+7M;O=0UwI1o!WG{~)cd!M`Tv3x52OQ3- z_?T@dwh1sn!a5GCi4xhe{!$tl-bCCWz?#Gp>u`u}6Ss zUZb9m@Og=?EOHz!88LF*?Hj9wrWrzzjPRJ>`~HeV*onL?(ZIu-V{R$i=Tcg3!gjI{ z&E|3RT_{1WlYgs{R@$1sU#ZYa`BEszm%CflI!`e|Gu4cvm6pG{XjI2D;{Y7ndl|{d zw%5(vr?wq4NTGE0`IGI_P4!`CqIumn5us|=9BAgQsxp~K$G-e9Je+7zXEJnZK$}zD z$8w7q*thki@@S9W=;zl!>AM=Qjc&4HNWcg!%4ERRm2s3jkcVa2tpeAfJbWo%5~)(< zEBIcnha09gyyke1Opgkop#j0K2-7+}6S9ST=4#6Fl)$+7H*4B{#vMnqYM!LSfaZlz zR$-_^f^bJP_9X%WyVoao22(lb38|`@vMOc;g=8WVYOg#xTGZ|ig4p_i;7Obq>5$jz zl7+V``S7`e*Dew38jzV5^{DPsvMPCT&a1t`)rfeLnc)yj<722d*xqUr@Xp-{GpHxr z;SN^9mdA1vm34ZExeXm!Qh&@CS}KN1wMwj|yo6mhT4pWbdbn!|uRF)QvwNc=yKb45ybO-H*b){1w}>QC-Ou-4408+DOXA# z-hP!@QO?GYLSoOs6S=+di9l&lhaQOY_B1neX6oKsn^6{~2T5HVU)~q7wI}tJdH!B+ z)t{{)%Rmh9Hi$G&q>6hzwzB*qmKO2K&gL-dc0N;vSm>-P;}lS$_2eqiEsv(AmHpcp}`YfLjXuc%^QI ze|djEMki;kD*FrGsa8;kzUEZIH}46c6S^-S z3};>a2qqHH+$8ZIY+P0p2^*n`gIZ=ZK!(u0xSms^w`j z7-o~<_Rx18x&z)y#l?hulk2f^=f<(<+XBsHgcdYaXA-&m#G zhyYcv=k3A~I7eRr*Wyf|^k(-}3Au7ta4{|a6HK&v=Qh=Tw9(myr`qGAG1hQjmVAe6 z%aL>Y@vcjg#)i4J(;rqvH{b4SdP~oIuE4f>1Z;tf%qH@o$?Y#FmH@#+O=ol`f6^L3 zfW<`l!uJ_T(XVl;UGA&oL}K&i4PN$W;Y8=iYk%9*@s)bQ=p+-d^Nn1wG-n0Z7ajd| zNE!ue2CLS*vU~Y0{>exio-HU#qIDp2il9qV=oaygW(jizrg1~worMh=Q$($~Ck2-_ z?Z@UIk68`9o;HNepCVu5(5)bEQ>zgMIY$gjCRS6w-+1})*!dh8nH+?){MAIQ%-)>G z+lww|3w!Sj@f4t)u{)tn;eRd07!e(f;WI5g+GHsWE+lR%kGNE@H9bIo8n!DpR`|Ig z5PRt`SnXHGFj#?YdUGcO#=;qxh0OTbhm-sHPq7S-CjfZq0K2sJ8w>pB+KcV&PjbUI z>X`f}vc`9YLfj_t%Na6lapHW2s$lCSBJ{eK(UO5p-G^jR=dikS>@We79~x~&&N9ih zz_YA@rQ)B)rFG)J;Ix6cl-eN+%DN$Fc*1om+SVy-RE1+|^uTd>O7 zYUYmhJ#sv%XQV>?-Wjk2iYWahGWZ23?d~{Rk^a0HUc~VOi4mzkzjI+lL#2KWu1LYC zp<+%lyYSN)xJ}oc^LqM9bo6ucD1E$o=V#dQBBONJRHb+{Or~-<=w{6T_mcdJ@fN9L zc;dEbDO({KYS8v__q^bB5gR#0yUaZX3u5`&Vc3-g>Iy~N@mi1@*BSPL%mV`FlHCBi zK^~@KY=69z{2znxQbVNgW#yX>8hkMn)T(^2E`w9Pny1zTQe}swc1dUecvMIN19(Wc z2XvRYYEbR@qE!s0_6{(uq+&1f+U&WGuDfL%YJn7sj1Lks%Zo1J!VFX3klo$cYwYmF zOi?p|T}E5XY=T_>XZ&J)_ zYd;QVu`;{~R^{ET`_*-9LV^b|Zy0k0@t%vjQ7?FuK3IwDCf;L!(;pfo#Eb>b!v)qsq5+HSU@oR%-A(Wux-~%&M;`B@qcC0Cu-~<#>iWI{k`c5ELzqC;u%56qzmI^(4 zko5hASl<^{(5ta#sh0E>p#H(>hbw)t5Q1D3f8oHaU(f$z-8wK;cR90xCXPHAo2{My7B*scrLqiQ;mA^OR5$yl$5)d_361cva zS^iO3aK5gk1fD=b)zvJ{C88mhBC&omTraI5czqYMo4KV;wzot29-3jB6oPJb>G|dw zi8)o=`ysb|I=f}|tATg38i>(E6#3>9vA?|4>g?g_s>Wp0%B}=a7yvzXxbcj?_N4TY zkCxZJ!tjuYUWi5uF>AerQI8fAdZ5&Z8%`g)wbuVoX;qmIN1gBCEGZ>;Yq+W#B_9^m zhln{nx9VP(P7!YUsGd)ndfN{c$BSowXpFk4x8eV5sBf-EeIhg2Lx)m%T8*DB@`){H zDvh}u(WMc?4nT_ht~2QVX@`4r9|wZ1?prE_7r|JB;KnIb*$`l1v?kO=_bIZTr~Ghb%l2hNp(y zP6fY|^4}xK-CwCR$Q$ZeySuut&VB__yJ%Omz)oq5C0ISzrOOb*j3ae!CqOX@r-_3Vdt zTm_0KZfnt}duXo$>PXh{53gtdnfsdpwf%0vXGu-~*~hBACz99N35V8q$Y0WHdw(;? znxgoQQErlDM)jVZU7A!oX93tc z32uHkY5d%M)n{QuFn?;+iS7A!53oiE?cRZ+M+tc?Hd+IJwuO^e1^L=4!oOct#Il9_ ziA0p~v7-NO;rqS3q$7x4rhE&UNK~gw7ghcz%5SZDv$(pM96fO!C_WBRwKLx|mb<|R zTE)*6=VrAs$W!iMRVE}r`^#`qPK_J*7qwc>3o?{tWgX?rS>%NotKF*ynCCZ(8+ekZ z`T|k9KX%api*d8=J@i3k<`7SRSy?hx1*}D(1W}v8lb>gFJWpm!eeQTpSJc7ZcvGxk zn;Ed3QkA8xpgnB5c}tNQ=Uh>1!QxvWm6z8mU)iGr0#RfNZIx?Ps=+9I{O}K7 zI2_4SPp18+rt=VD{!mbd`=y{R_%lVMm}p$6)p<6Gq*X0)G>*3Trq;zGZ5y)o2Vw~v zv9|bMX(DZLvUDv}mA;BWIo*)ln9XDL1&CZY`JPvL^bS{}GUh~|1JYI>vzWbkvsNoI zGlYW5+IVwB)Dziwn?RQ+0zQwQ{>yfgaC@`DZW$bPe>0M_#L^ISfBW^>2Nb+WBrsyi zA1Vg6PN*r9_CCXjcwn*W8#CZ!#5=c!H@`0P@bYuIPiks%z5t34H9%`_&Z(X@o%S=! zktAbP2QWp=3s2;=FU~C58_%7#$gs|op*S{n2pCNzD@WXv#BRIEEXfjZ-IL=PSo8Da z{6L7*=Anus?RJgR1eM}32ygu)%d*StSlPD5qQIMkK=P21ekZ~jMc;3^tC2{%iWg_Y4*>vpzg`o#GlTwv6aut?|b#mnA&4eOU@^ z&wRvZLN1_F?1F-Mdk;7VjDnZ;ENNj?__*A6tHlNRl9c2ZkK2*WVmA1bnifeo25VCTFGZyoX-|2di@`VLelLi`&<~TlD?T^SAxmQKue{3Mn zPfMxgLV(jDb00z`G-r?T@f%khabT3;MivfqBNy=9Bxjtbw}?sZJ)l$bEs&{=_a7YG zG>d{J-l0S&SAt#!bAM5(E!pCHd>BBiil?9I09${NeP07xu*a4d{xPfSU1P5t+*Gd$ zg`Wmbh*bE}&qaTSO7#K$O>F21$=!z%_6bR^hm!Isui)v^* znb@^~IoKq+Pln5!uy9o5buXzC-FP`0P=q-zHi2_qdJn9*8eA6V0>)D`p|v5?(z<9W zTYHK|%OO=6bkN!%33LZAx?l^1Smlj3k!RaC@yD(0O{72tpng+ba-0T&wS_-cIj}bBquw_GSB%E{7<~H%;&A z>fhADh1pU$O_sqn842=hYQge^ScS~u6ZCjSMQN{cRg6H*0(q$5LP_k3fJ_e_21Cuw zuyu$q?-}+PW{k+w8xNX+&DzbIl^o-~NQjYUdcg@H?F-c` z$!`s>#!m;+2wT+cUaqktqzdlQeh8E*FVM2P4HgluztIE81_(I0^34)xms zzA;|t&*A3j&{WQR{K8}yz5r8J>b`R^tXqk?kdUCT6pzYNEs>NkzJc^M9Y&SDl0rt8 zJ?cul%-3JS?QjQCk_s8lF~75Bj-&GWB=LlV=Yj+V2itep4M<&)$9&{GFWdO$8(|!g za0=N;5vVm`wjO%nm4(X%+ff>*NxuIk_#g2N@}0uc5?wPu60U2G?hdY<2whLh;qDL1 z0T$e@>L&&4sOTBw{P;|DgkkSs0`MCAcY&IYPp}VZ<)CJBa2Q&Y{)$r0W+vo-U-Ek| zgH@{xnLFm>4?V8Qq5Deu)&5HanG=`a0oM~?_)h4yA3BFin+JUW5JVE&8{Ol`9kj1V zdM4UEC+g@vs}*Y9&T6%+6RmW%)g8=@>HFPVJvKeH>?}~_Z|mq?b|v*Xwf{RehJ&p0dm}AGoLz}dUm{1= z{v0otQS{xLEMxM}P2s-;(nx{&oj2g& z<7wNl-vHc1Q&M+iui}&n5Y7mhi@@icM-tJQLWbDwC^gegBD6;*E^EDfRI<)Q*;!X( zZmA#4vAclk2J_aB1sMU{&n%>qsBS(x*jUD~nd`roeY+gYIx%I$NN4*td?+{hVF=jP zX9*o1p}dZsVq_q2wGK}Tez}**Xcs85$kn8T}k2z_(OBd zw@w)v+LD>Z<5Z6P^KlO9TQ8B+?)&g@f*POC8EzURdkA8>8m^2ZH__$1$!?21tDN0^ z20j}fgrA;kB&5Rn=EtxJ`9DE!i;&6h(V4VyZz|&6c@`_QTUx3i`!}y?>-qwVh1EU} z_oatOssuvT2D0)t$a1YAw0IzN1od~Eo(9=>LV518bE}+S>eSz%;37@XsJVIv2O}Ga zN(Z}N_DMAu_@S2(38j%n^b+O(0wpS45NZLiQw7_hJe45nHcS*U zh(bw(QG@T=l|79523$#5Fz}SBc>1z!7Ct9|HvQBQ+x{Nh2CDq=C`LkH-QQfmV9_s; z;CP*X`*}Ff%wOL3-^Oz-L^l_0|M#kdq4ySa7~M4dlr7^w{q!p`=kislgxvNz-5)9Jy1LJ;tCO;uadIb^8*hya zgc-32;~C%aDo@o>!}Fw%9o^DduY}P>fEtJzDw*nvy1Vj-f}+xJ#pDxW{5^Ac#yc{$ zJ1XPfA7G^!#D-EWe&vxi^PB4$*wCBqje1~c>YkiX!G%K_noWGzZ29av^hB} z5H4aXOi%0=EOlYq;>AcD^#~a25G>bZd`*a_=n=j8=J(vz1oFxG&A3Fo98iYei`N&} zVbs{8r2Ax#;Fw*oWz7tF_MY_wtxt=xZtq_kqWt>=CXFW)wLZSfbKr1K~1n!-(Q97-1U$h9R7G#Xpn1}_`T zg3BX_Sk)HkZw(K2=g;YK!JIci&1?cfU8+P@sJ#q!X17g&)>&% z@UpDs*70J+*4q26=e5oRlm9*~+{AdS9qxvmZ34+$qdZ1I%>Nd8%VeA8e%1} z8⪼^ObqaEE=wN1pan5KM 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 R_MF_Q#v@&oA|=ar{|`p78*2an literal 0 HcmV?d00001 diff --git a/apps/web/design/zopu-core-product-experience.png b/apps/web/design/zopu-core-product-experience.png new file mode 100644 index 0000000000000000000000000000000000000000..466e50537346ced4f577c0ac4988b4b46a732bf2 GIT binary patch literal 128629 zcmd42Wmr{P)HV#FAT1zWf`mwSgCZf_-7PKMpwcZMoeI+3y$KPK?(W$11~#4VkOf(WS1Ox<3>G$F)2na}l2nbJapCW@RQ)cIX zz%N8+6)7=d>#InOzV@Bk+YyN)ogGIr zP(OT}!Vh&46Kseq%FHi$iGnN9v^LL4{l&$_``IzN3^O|-kEg@TwXy02Ara=8o^5ev4!$6N9vmpwGdboS&p?){lmbQ0aH1!kp#R#}qV~+gyRI*-6)sk7=UY4=x}IAz zJ6WXHTjtDv*XVJS`ABvJn?sRx?tmm zbLLkQ<~0=`Vee#+{}oO_`ZOZ^7`Ej9Halpz$_W=8J&M~%T>bsXza!I3JL$6+GIxb8 zbl))t1_k|I`2QZX8VL&8T(~wMdLBdW(y-t;#}v_k#+>?}Jaa5bHu^=zT(3D0!^4lg zUlBk5XXgr^uSSGhXAHHm|UeWl=tD| zI}kj7UZckG>K}1bQiznFJb99zwhU7mk@)vO#45qv*)Ri2!dZgMrd62{s<-f>#pye?4HXjgx0g#E!53?B6F)nY-9lv3Ysi_Dp zG+a>seLUCX71e(d<$ini?=GvU$UOaT+6>+jPtM|{+U%ZRZoZ%2%<_oByy)f6^G*3> zR6mLe=cVoRr@(1DNET1@PWf*iXeHlv7%+ZAWS-xvgecbCV*2bomBC0$5XWB%_~qfy z*-re8=--LZH-DuIG%&@}CDUByfrii+Bt!TMGbt;yd#S?c|GCA9165mWi<_rB1%gm-W*7qX?AybZ-Sz#ruMwBuyDLkHzb-|(8)fDWxPzkxbrVw zT;Oo$fTX4S{AP(!h(H0GKVuPoROgElvDFB$Det@4G4PrfJ`ZRZk6EUx_=vC?l+%9* zTS?`%@woh%M10o8#>AMLGxs{sDpk*EoA$)JfU?$R3d{FK>2YrzMBBLc#izM+iMrbX zvBd%LGxeu~}=G z1O?MP;hU9Sr`CGLEF2shA7)D!jEzz1G}^BI7JpuHxP|D3g@>D*?aeS5)Fd}F@XglP zz0=XrDYQF=!#&;Gn?FyvFZdF&BrzU(P5f2IAmzdMv@=ocyrHg{c(nDf^9l-X%}CAq zco@j>J*t62P=+MmR>5WcOwX$nnfRv^k;X(q@OjR8w>i7-5yX3eA7l1S-S`erEkbh= z*NNXpmMf;zd#_v)dh8C=M>i}UC-jBI#GtgV9Sz@?-nE<`c&~46ChC;y)LILjcRK5R z?0E9CFX`*^3*ViSLZR#a>PQm)*A>8^MS35!hFnuNP>wO#&o5N^qF{28B2Q{JdeJ&M zI)0vdDHXjwYg6`QITFkAa7|G(@$%xI$@An{b#1$lcc(hFgomRYUHkUb#^6Arvth2j zjIJCFF~&qbS2OwG7`>!9`m$(V(|)E~@s6z42R@a(FUZ3~kY9a4`l_#L>)c564u0&9 z^&T0K$A10~rmw^DZh4!?`dy~~*`>z5CDme;t zMcaJI&5$(aoeTy78&j@S3qfGx0zC);(+iyAABrUo2ChtpR-N;LGK(mFf$17GjLk9zX zr*m21Qu^ND6yND|M{09!jW?<;xAGeJ2khi+%Wb;S|OqIi;IhPMGp_Y^Cfrx7YxlCle!Ii z$9<1>{V%(dJ}MJvM3`x=Mq0bR+a7E_SpNYuf`=zj zz;)+vvHkoJs-=Pg)>xi`V4mrNDdSHd4*rg6_WIL+MGxEG%)oL(NG4^7)%&pypZkUL z5<`f{mgASqXF3f5Rc z(e*pHURQk75)4|e+(GG=pYNDBvaE>0c`??3Kto4+d_+%~lsbWDv=$uod1-A8JAHyn zKtSNo+u*Jvt;f{V6kCtr9Aa*N9v1)M!JeeQvaLJzfBuCm!?f_a()2g-Pp15ER6M%J z!oj$m9Ld7oJc`1XYRjjBu$FhHI}_Mi4@&TP37bdq<9(O~upd|?jNa#;O&`0%7RfJe zv!u5~?r(yB78Gn{?>_k4^zdj)Cp>bx^_-?xk?p%0rMiel?%(WF@1eHhs`+xltCj!E;B~Y)*<7JnS1=|9 z<(+KMFOkHir6zM5rN2%^PbF^CzoM>${oZ$TUc=OuyOVIE-Gq(S#NODE7^6oIg3&rZ=p$2m77>3t;Ug ziZ$T=^*4q_XFsQ>wPtH=2rpVCqwZ@}7JZ=${h7jPuDg>*ciHDi)fo>?@4Z!){~hs2 zERM^8i(BDFhJf4pcfVV$mJc}KT8UockIuhZ8f!gKYa2-h0#+0&A1kD4$YpG8nIaOI zXn9$)fYsqG*qDWmb!1igLd(AX5=gEZAF33fl3Flui%5zVmKB{pZhp&S5y^0no*iS(xGS%-wL$q@RISfMTU&?l3v@IgT$ zw&ev=KI?xpdTtojmLoe=7I}B;p2(^zDQU5(YzIB^NM`dB0!C(~zgi$JHWu-XpP$Iu z?`ZSuGdq`^`4r&SVzZK@?(ZB%u2V(iGB{Ge`E%BZAh;3m;maGym5RyaBf~QAI(p)D z*7C`9cS==Dsr^#e>v1r_09a>FXQ&F~Es}uC)~5w~Xvp_>;6%LmX{}DspCtv(o3y-a zjzy`Co})zh8=Hka?doJ+e~Ks)?mYcwH=^LT#>P~pcNYPeq&&lA`huwxKYC`vHn%M^ zsc308vSHj-qk-rwEnibOEk{eVIe}feboxUGJOwTxVb6x9-di6Zq1~zbfhj|LQJiI^ zzTK(vp&&wiar=YCfI6>5zaH}ByeLFi3%Q3K5i$&m=uQ2>g4JvdLr{>t-{mF!WSM?n zie+XuG5O#!*!M@vu~L+#qq$*nnOqDW3F^r7X6VL}&Z6Xl=TH1PUovmXRXka2hCfk! z-v^&E^iQzz?PBxTn9$rhSAw+XP@g3wCjRcp^68&wEf-lAq3}O;Y;xOIvDVhoBElBF zd3NtUcb}l+H)mH5)_L(}jNE8(lj_WahkE_625fG7T6edw>jF)Ph@t=O1lYav z>&7;i>%2EiST0QQibnqzg!un{;2;CXowJ&uA1J(fRTU)rs|&-B`n_A>OvPc&!$u>8hL z-;rN7k!j^X5C;AaLo(VLhI#IdNO?f_?@s)8nFqJ$!#X>~=oC}`%sHH+EbrerI8G$A zYSnzC;$3oi$=Z5BjWBiI8f}^78w}AX-~5s(OvLYGHoVi=lJ0vpqvzih)pB%YgW?O3 zj?WW1-ZI7TI2ZOZ91GDj^p48U@+A25y({==9?m*kdhOYAz3AO?IjI|>ml+q=xy5ek zv9%)AaCNJz}j+w3)FqBZq6P$2Wx|-icF2dzK0Nr zI^uF$Pp(S81(NQ4ck7^{ zs*0G*XZ)zp%4)pakn+=)FOU4Lj-vFZtItlxvZ#_V`KG3c4ho7Rzd<;vVg}#MGc$xoznNY z>g~Bd>(S(`Bu@(U>KfU7ZnkaqY703HnOmP zE;Ubjm#5TfA@5YVbg4l3B?-5+nzgo=*duT}D*7w`2{O&Qf^XY1g}vWP%V032@jD~$ z&t*AI?}MNl>5YmC2Ld$R87t4K>Vxr-bHO<#ij0iRYwj6C7quhP9z7P#Y~%KA7nLm* zjFR|Nottid6BEZ2VDUU)3+|W8w{O#(2OELqGuh|O@sX3mK!~Lk9`$Lv$83Df#AF6M z>4lF$B8#?|j0}hSqAMZDCWatHo$c;R8)5VmmW%U^Eu72xWm^*w6UCa&eFMBledRCT zD=I>-+NN;u@w;6uksrFnOsMl)pdGg?qg@rSQQ2gC~*>Q4khDAkfRCfeu*IMK2 z4{rC*-CmDXsWaO0kQ(}4(#jIBTI?mW*I13^$!%N#=TV|n`6Wj5Dio-j^<+sewkQTT zFZL}L$9*U?gduqN_!7--`yjY}uvauRG`Iu=!B(j)a5sOQdaEu?!~3LQZS6=(UaU{M z4h^6Eu!Z}d2oFN5WLc%d)5v+uXNf#OXYU>jjws~2$+h^@j_K-=-gnk zNB}UQOnoL`ZMhu$NKSWo`PPp?C<8Kqsgvz4{8q1#jd>Q71y~QIuXtAp69*^fE;~r>({T3uCT@2{Cw4F3wf5t6Ar&yS3Xbga|*Y)iwh4CN{&Ew z)4Sysd;LO#9ONl|ck-%^%awOE$+RC-?p3y6AE1s>9`=5V1MXm~DxT zu;{bq+ID|;{nEbK6HZ6rv+Orp?=UoMEIRr_0^N`Nu8%4yhLkt@Tvo@-(0AXJ;HGgo z`` zeN`Wu;e9poX~5jsxX#dhEcwve+v~yymwT9Ej{kkeqNX8!jh5@qr19^5VyVi_N#xm1=mn<`KPU!@i1L-@HkGPhe#Ion9wC|=X^_5 zZAnJxS=RXGhYat7MsWB=UTvTzvW8%+^ijPN!`+>C9>VmB%yuW%Uz6!pv}XhPV*Ced zLi@5UlLzP6*Gzsp*BV5($ZeHLp5@TI&g-laB0oC%0)M zHu?B-QI9O(x`XHpDv-plnoV(u_t>P&aNh+&yi@AulAcNS{6hAl&iQ-yDoRNgV$W>o z%ihpGXSdXrVJ=HZd3v=yVe@G4o<0n0mvhpL~|s0_CgVT`L#xtfPX6L?Yn$z4-_ zq$|ka;`}i&WqN#d$oZmYwcZ&mHMLl8SAR_YgiLfa0B@EI&w9=73T2a6gicRX6GRxe zFan^N{?X3OkNr7Pd~IVy@3zJ|FpO}+g3`pCo!Gj_l<{E!;up<{o`R62p>NW74XZ_q zxp{f9si~4CCb)@9Ly=ahANkVzvjo$2xUE;uueIyU*cmmLdi1s4i;0;SiKg+|nXP=e zY*?_bs&-f1VxbWbNZnS=e}+#!(86DRF$lX5?A@u*3Hwd= zQ@O<@C2=W~g|xh`#u|=xVC%Lwt)SwBW?!FX6;EGj-bWW69$uhaO@L6WS!%Yu zD$%fTpkh8(O`isKuEMzo3!}?usw~5d$5yR~0@&Y;V{<0`s)q41T^o{!CL zjVADv)a)5>8z8Ad-yAYum<7|7z@aek%Jz8s7Ev~d>4#TTYJV1Q$d?-*TwGkF`|s=f z3w>Waoo5=1h1n}BK@|BU+Ul~3wnVE9BWp-XFX8oPU;7Jh;Rpg2qoGV;8X7v2RJtcm zE}5>J5fe8oSF+%!*fH%6+Wl#q5gCH+U(?fZLmR!%SqS>mx+a-*Lt|ok44f#CPPLtG z!A#g42AR&xEaSf>KTlr0{0J2kq6Y7_i2auSWq?b&OZaAm$&8AS0V_Lu zrMP6`-6%aPYs?ckwCV_UDlQ?l=F}KIv^&o)F4gmx!b#BlSiDGG*77*e(RTij7sQh_ z*4xvzb?y9rdV8s;sMwWwXJ(qU*&JAO>uu@EWbnzbEVJ#Sp$@$;IJr_1u%%A$>)VT4 zKj7Sz8u6lzHRsWG&y$us1|~~&;}a6Pw?=Y~JcKstmf|JM%xEb6uh?75_V7m=-a1g_ z(h#G=>(L|%0?^>~)PvVBr-l5bDk9M{>^#LZIXyi`YW9=4a_u%-hD!EIxh#PoZO%Nz z3ZEI3VBdRYV-`Y2O(x6na!AF`+_a4otN0XtUlP=?ut?%t(j|X5K2}Hob$wcShz~LV z04)xCD8HtqMT~7P7gGhy)Hs?NU8An`Ji{aH`Mc3a%x%KQlg2z8o|3L26gxOA>Ss9_ zxeeBe({d8y?!uP_q1k=k3=CnU?RR{9#$kT^D}Vy_>&P^P*8%J32(>?*oq1To*-$nU z08|kjSrwje3!pAt{&%t4W8sj;(^Xb@Ig&S>Wf{#MgF@gN(5$$OR?@zzi@kX4=^Cq- zBO~lLJSKdMiL8wJLw=$GqJCM_^`x2}e8Ai!x3nPXVmUiIrEvL?#o{RUXYvP(K%zdy zCFXZz`GgKZyJ7W2H%BO+%f5+X-9~pu5a8nA^ghf@$7#>ma>#5vbR|$qyuxk3x{c(R z7#PH_hV2dPkZQL9!(YFCA20Yr*6U-t3URaAi|`qVcsJbo0jRp*h zFE)3nJ~xqbT;O4Jy$}`6QWPP2{^6(4BIM`0iJt(Co4@g+w0HkZm&za|l0{hGoUNyW zd|xOvL?M?ckOESqmeCr5)VyB&)-t2xZ@n%sgENY#9!8oWDz2wFMNmc_fuT;Dz_Uth zw_<_9%zKk(ZvEGX*S9DBw+ZdL)dCss#w-RZnIbX??~2!T=^M7<`Wyu^fJz8aU^8m6 z^|g{*b*05+RQy$w}WZvoZsE?OLA!(s98fjKZ4K; zRO>}7GZu&u@;L!iobNaSVL@63IWrwk8QR2XF?@!ea@oZ|y!8qbgzk8l93-o5&Dtl> zM+Xg15uk`sB)s46cgsJzL0S4!gd-A5XGaerIS~)NTK8sa;hzMi8%gufdDPn(PD-c zBGaP)1NBR;tii^^Y{fXu?l5Gd&Wp-<54asT zp-wsxi9Tn&Atk!a!O6*5dMhh6u#W@?eThtT#maajW@fl}cwGfJT@gcb>(c3Sr!DP{ z>w7a@qqGqp?qV1cgekBW26x|@nLQ`QtR~{i78P`}wFDj^M0iQRDML}tmI*f7JWT3=fA`T2k^|=S%7j?vf~bZavC|lnAFgmcuO)jx(BNdL5h0 zX$9KVJY|O;NqB6<)m&T(ldCNi@@K4=`PcfR5gPPZ002bx>RyyM_^pM;OMphawdg7d z$P56uh>14ASiam3Q>gQY(Hj15kxA*C-gM-G8Av^s^)9(h&q1)5WUlQYWDnMsCk=C~ z#+V2>7$>eSHa^q*vXTiMvJ|qb9WweqTmX>m<)I8GNqBh)AfdEM0RUhfU{GwvosaKy zZVA(P9Yl7hBfCAF*@wYSjxUZIdg ziF~nnx8aDvwrs)G?vo*%zY+1>tQ9gHr#_1Y)pOB42>sl3YJ8KdN-_+824cSMTwhX zi>MaN@_r*mLP|>b{y8}o{m973-oSvl`0g9VVSArInzs$9B!Euz`&nw8NS5!>Z&0V37UuEjq!MY9z$6zF4P z?@9fMDJh3C_x4_9!hH1&5WNgeSbxWmc3o7O79?gWB-ulgNXE4D?S<>?XZpVmiFC7J z>eSmwjalR;yzW^k!$#1&xc^NZ%%5@55^~SOCg{x@C5y`=Q+ECYzDePhc1ylTHqO*2 z32G1WfA9Qd8Pv5K4U3PDZkWjnStP{Q*DbaES`0*$E}LFvMp{pm4Gh%J_k$n35efdE zK_`qK(-P!#Mqul+BIZfA5z9K~$@@uoZ4xMX9hZBSW+KoQM z#I(%P*L%H-Q9Wx62h>_>;`Rzy$|EDcy1SK2bx>yOU!R%5Il{#3F%0)swH%QT@ZxN=Z9kf2+51SSa5|lb@?c zxier)fD6pk+tKo{l*vKoEb5&-%Zq{WXV39B{hN#GB3Lf9eEZ`Eb!x`smoxG~YF_$~ zx3IH(zuQQaYrgqYN2b-)qMNnUS&}UcyXu*b&FI-?&zgFimyP_JyByp3wOXxdYj?+K zS<29K>+LabZf%#Ef*Lp?PX+ZBC?0kfAO{juFg$Z3`rq!H%5BBH^-&5W zX*9i7rj@<60d^?Mk1O=Z^!^2SJ7>P)Px))k50fq3RP_MPF$I+grex71iviu?ni(q} z_yi#^>)#+#<)-Il`X}4oFPU}fHX{^8(P|n>bt=;XbCGn_?umrM*(`}_t*4AZF#S^p zA8}{Abf|$n=hAolDQhNwE7p|N@D38huIsC9iAp_{wqI7m|v357KM_jtTo3iabI6a+= z36ixvmN(q2XowpxQ|~vsk0lv%m2id00d@wzS6x+FPL5hYV6$N89DB+4-M)b9dpQ}4 zeZyia^-*T`ELWQ;wzHU}5AMHHz{nBc=y&QFOZobeDd;75JUD>rP38hzN=mK8Dpy^R z>yxfGj0+{iV+}V&Hrct|$LGo~`C83Md2U)ozJ3Boo068+pLzaj;36*lpxKxJG&MRu z&>t_=%yP3#?;Uo4b5`s*{XV^NM4Yd`lYl&A%IHNi}La+O|RXGrV7G_ptK-6P$|lL zgbb46$GBBn$waHNbbAOO1*8M9INgAPse?U>r>D2oMBcvN+MbMqLn_Z`@l^YUQ>+dy zXQ__B=??D;j@RM(Ac0b;eVsA19d4Bhcu`{@CIAT^V5h`s*v;bN5KF9z#&t$U20!-{ zTHouX<1EI>&sgL*G_bZNVM&UhlTVAU=S`xH2_eX@H|t%MsIi)(zzB_Ayl=oLqM`Zv z_3NA4NVz(PIT;WWfCsBQS_1>y{c?Wf^x95;-50Dgy}Wf1sAmMJ#LvIMh_T8eTz0eR z%xS!;<$g%OPKN;Fp~phPZPj_O(ERH4>(!Fzj7JN+oE+6`7y}vEiJx&a;0{~KG3LVT4myp0AzlDVK1~=+) zS<7KKzAoh;?mM%7+Q!6{z z;TBh%H@cn9lAI^);=*;x$uU!DM+&^HeiLk|6n=4+h^3yylNPlzVUxI1#Q1==6|teA zol#t?(rgsmG#ZKYB{5S~8xu70H}(TPd7)=-D2mCY_S5=IN`T<#h=S6}2;NYkZjSk}vFI8ai zKHsde?ZrhyM=tTje0Pi-~Ssc-ZvwU6-i$q)bx%VkFtj7Ws9pYCJ?oSPp6yRYhX6I-?b7V1Rh zgFuHtTvR6{{?%i5OrCWPm4`tbF2lsrVJG?uU(Ol!G`zaMb+A>;WlY=R$3a5?4XJyV zxu^nryl8D%mHdHi5g4tkRKZJ=^&YL?(bn;#!@(?sS}&+l8#UJh8Seo0e6#^DfmWU} zTFLX*Y9|Kuto?>z?C)1L6|N4xOZB}-ula$~a8oLVJH#cQK6nU9 z|FO^U@t4o9-{H!-lKB)pP*-Ay(XVnj2q^0K?aR0T3rON^FS|E*V&y5ibUL2=s~BBZ zAM0*`ShdMDfKtZdy#pX5*_@?E`%W!C$D1OJ~l*rfO%{ao=S$UUN=39;%mku_Us3~D6!iaFjq3{>&I_b5_{xFLgacD15gD=JD%nH6tmh_K7|dddY@82_)) zaKHG;pkxU<+zjzhwEabWqmtTv+3Gugm5VYK=qNgKaV#&GHbrVyd*VFQM=`pq)#4En zkcC1>*l2+iVc_nu?HH95jfyfTcQL}po7HAw`S-o9(ab5fXXB|fHasuc=OR78l@Viy z>)5%c+JHk5&Ut8sA`4A!xs7O6<6rXa>~Kd%V_^D(o#oDUM^BoE*<^6=(?OIK%W@|A zR%t#cNRCJ_bxgrQ-brSH!|qx!o?d=AJ_Z@_86I9A@Vkzl<%>X9DO&DX1_kfM1Tq&= z9NH*40d|A0VIVJH3>AT4}|%Ph0}w5&xdD;`yts-#L@`P76pBbvJg0%jOERnY(KXvGyeOH5ccQ zbTb&=G4BadmJ^%nw#!lR39YRTKFLCf+4Lk03pCr7X1y8blb0N~Te?(aG~JuWBe z8*a<_uMyrqA!E*fBiyTYdC01YuL?1(Q4jgMfN|YvYV~Tuc-J$8$tM}!t6*aJbb>! zdW_%`Ao9RB=a4+%`=5n|VU=%)?_g)mo*NVOp`nq>a}I4kWRnim!pb{YMQ`628zVxn z?!(YonsW8x-u`WjCgryO3y5T|UOQFWZsYE`2?7Wd#b}}k-^`plZ+L%mV9Ee65!5LB zP8f9R?fblu7FS`Xy9lA-;hR85j^03elG%OHsx1_-_m*Ji@VjhxPy^9*Xl|AZ`$d@} zRdxETCn@>cuQUhj^-B7zC(E%tS(^j(C~@Du`RJWiDta3BczSv=-8ds^N09*fnHs`D z%eBa&kM|4L-HCivC`-%b*&CLYi@0|zP`0V%REP$r-OjoS`&_MP0PhmT`|{#4DgnA+ z@aG@SA&8!J217jkao4az!-ZiI{0&5^cdj|f{R%?iv1CzwbZJSsy8;J3)=8`wP}*zW z+O|k^5%{gfGuPR(5sCbgY%UY=Iy+Ks{@%U7>NVZ)+vxIU^@l)4_Z&^GmKK4B@Ux^3 zI7v!3-x&q!E>7Dt1iP4`O=sIz0@+2U-N$Y3TEZv)|I)H?2Ea$Yts5 zcv=Fr0pw=_<}1AXuHFZ&ERl?Q(0i~@W3_Xh<3;)*r1n>$_DvgZ;K+=D>;+=WV}N5~Uu9a&0*m=lKmhp@h-5*cUom5gqJOXT z4eBZc${YZ#OJ7=}1_;m_Kr00iDyS71*UzD%qrc?g!AfGh`P#?d&+Rb?VBRx`VO&x@ zZ$eLzmX-Q`j~=BB?i zyD1OtJ$}8L*a^8myKVmL)<&x3lWaKT7T?pVx(2qUA`w*}i0RN>Jy3dT7 ze(^Fsgu*kfP?hfZOt&1wCLO-mB4WnIZ(nF5JjKS&H@HPcSch&TTp5v(gO6^pF+?E^66K87hnLGS4Xb|_%~>A$gWkVV`yfU{>VSifZs~G<)J{qfH_Eib zd&GJ2PVkB6GYZrQi0L@E*jSOv3cDva_lE#n@#kc%aa5RFAQA(@vo!Y{3#=_ z@XrYdR-GEPYPJ;pNcq#74;r&bxNWLFcKCo8hq#MPi_HPcQN8FHIr+Q3L_4D6OQYX0 zw*j4B9w|6X2s~qCX9%+?YZW=1-93Hu5nk&4wb~N9%b!@Ihy`HfJz&c|%r|U1lDk*z z@`1z&Kyp+76Lj6>&NkL~NhePxC1!%lpk55LCkgD<15Tb=YaO4K)@N9(IeJqw_B~e@ zX5)adEXpb@TWh`DpjX!3mgRNz#EQ6n^C(vW6f}FH_d$*G z$s)FMV?)t-t&|+g_rD`Fdl*pvpLEGIO7QjkAhg?{aIxlc;U>w=M^@Ge4oP0rq9`kl_e>N;!x6RTRLvZkIslp%h; z;(Qoed=puLJfUd5`-$^yiZPefuH~IsynSm6m({-I-&dr&8YC@lhDRu{y!{)#^W8#s zC^`j2(`fJU6J!zW5-p?SNkxespBs=_f0`_xpC?&)^d`4TN) zHmfsge!SK{PxKNM68dtE+|u3o1b%qwYX)1Cu;`Km9KzogyDQED>$H<{7v@`rXV*)1JdiffmAqLQFORLniEj4kSYNp@f%93 zPqenW$_If;Ab{%oaFeU^4}9ja}HSmz!~0e;M1R0Vl%H z=M2@-b?@<;jHXk7S~3}UUV3tHkXE_v8|JFX|9lrxoy=}Z0^^!*`+6hcM*hH{^9u){-J@tXcCzW{%o|C(NX_!5u3T5-SxbQMd?%1c*x(B zDuwSZasbJ{-Tu0-y+il7h~`a>#!t&t7rvF7V&Cxs6!s&6sYO_hF00{DTJYNeWP)0b za9v#ZIZ5tRWhl$Gyk#mf^WX`d_UPOQ!Vb@wYU!DuzPS9BOKeQ!*p0Y2zM_EhcBzP5 zwa4wW$480h_qh{Q1{e-)Va7)SM#Z z$ArldbN8-h!7tQy`V%N#fU>@Fvx0tE+ok}3+;xu=-vZ#FVChAGM(Lc1Ho$-q3^dQb zOAhFOqKQDdC(jWoYC~t&_6VGMP-xc{0iePkB}1w|HtNyP(mPK~OpU(CF@9kS7>WSM z2K6pFQ1ibI7X#HQmL&9~J_*1!1pQ6jZ1NnO0QZQd0>CKXB^GFwzs;UAaB;yPzTUr9 zYB3nXnVQMh+ifr-%NwxIcipX{$yfU+li=}CM6+|3G`d;0V%AY6#ThLk^{@4^2V)Kwq#f#OE(?xgwo!2-a9c-aQaL8J-nQ;1b+_uACe z2`vNv6QqF9KkvbmKoGzMYAJx%(BIEwVq%JP&1~_UzA^>VQ2<+1vhv4{2Xv5}%YO8m z4(*h^mfTWlhwWFgt+b3W*W7E@BtegE=MCZAaV^bv5)VLmG-TtT~nnP7sX!UvD zWNr0M(-N64UEx9esEC{{ZSt`2Xeu6-Xj#_gIN%J?zP;e)j`#Bc zfLZ-`)%ZV{h9^6~q>x?FT58K?FQzH_pV2dsN{_$RQ_Zt;gUw1OTm4{pT!)l-EsWzHBIv3=z!8HN(gX!YU!Z0hrLZ? zy7a^-gnwnaltlcx4AqMa(50%=IDRaQPI%}TeBb@lOYFlk%?um|+1$J0 zthfI~`tzZDBED#o{Wa)O!#iB4_}bC)V79~^zKcq=o_}mC!Aan=7xPP>zx|*}{zcY3 zOV2wNo?|_d)No@bMlc3DS5{N=_%oxY$o;^YQX?95)cjuv#$eczcp+MnLY_HZg zDu`9h_+4ka!Z(-9e&Vjcz6`vc>kscRAwtfxV)R^`DP&841et4J+_vFuBGmv9{*|CZ zlj=W^EmW=LYvg|jT`XSA|BE1;w*=v`5=PfAi)v#c&_4V44j(7>Z$}jqXpp>^g2Iip z@4{Iol}Ci-#B-elQx1$DS~!b;pjU#deA*VvmZ#Z19mKN;ps(_><-&)8X*aOZ_w#`< zxb~QSM|khC^i#||Hx#$#oPT;u=~2t40;YBmUtjU$c_)X4ej5mL#uh}x6-yh4?pk?# zVpjyNB?SG`CRqA)t0^Qb^iO5+wD+_KzCiG>z55QSI;aw0A!A7qf!?@Nf*`J{f8OQu z`hcPif|h1JzVdV|N=;n+Z_vU)JHXJig%-Mi9<&5rQJg8_$2}7uGoH4k-+P=7CW!m` zppE#3wCG_3l|?aEY{BHO^nq^8?|4-J8xSpRfMPsR@ZoV9tuMaVg5N+(DnJ13-Ge!D zfg*6YHRFxdBwYKsV&mb&8lg`L?|l?Yen!HVfj`Bo0rV+m^;+I@@TTxV&Oph_r7kR1 zGgMWrlD*%`;6Md^KSYv2 zLI(ikea+v+7E|2!&)Za#qXBKV=8Invr+ifN*LzD?TUSm2o@lf`PwO);T(39Ja?dHA zD;2o}B0Uzz5$>b7i@X8nQ) z5rOwila+YD&dSQl>$sPmYiEGBT6hWp!CtZ6gre$a9^J;y-r6qd*HS_g?L;&}wEknQ z-eseS5^dFYAtFVYtzH{{F6wM&NvW>z=_;_8(lr%b~>g-C{=%;M0 zpk{YU!o9w`mb10>p;WI@j8U^>n4VB%;^n)LyA3;snHC9$D3YHjS0|1rE0^%f1ETp_ z@0h>enA1w_=4^Qet~LnCg}u~37tmFo=@Wz-Pfy?A3Z?I2XiuLisi}PfVcYY{53zs? zpU0jRosgfOU!+l-M=Uz17KTe3`WE30LH+7)izlmN$4kEV0H!OZ3+eiRU1tq{Nt~B(JP^r z1T_5DE(4Y2hV=%7EWZjR-~?!>m&#sO{z^Mzl(J_`y7DOTGaZS;7Ih~R17 zT{KaoQJMOmUS3z~^$@co6T8_O zWzey!0y@W>Pp6R(?8U6D;}X*D&64^ys?eTX=mtHyDfPWRYorwQS`WQG0(}L2RZP3P zsCXr~-Qi7?tk0JnTt6%gHa#P>)J`-nHH0j_ez{ zsrssYX=kx?7e5A*jIF(jsc#m<`mUB6f3*|U%QYru8=dpbwG$K4)A#&t_GYRR&9yZa z1Zqpg5bq9L2QvgMYulRD6YT<#lWOvA4&T+q;3F(w9rux0XeXX-TpVe9NANGU+x2GZ zt11C~L`_q!je3bdj7;*Qetc(Tn1OIIGo}b8GI`?(FR3z@v7Avzx)#(02P4rngWqj(Nc5j2 z(pX`xwuRjKo@+lW81Y!X<@Lp*Pts1!Q)u)1V_1|TOwz~a=bA=F*&rnP36&IKOdH4p zO$18l1S~3D!BI7C10=0iJB-?y&DI+-_zZfW|8z*{9ueu}2Ay?lCiW$*^vcT0Y?E)Z z2r<0SV&Ja(HXW0ccW2*yQCY^R3K`o+DPK`)w>Lj?GVW*J$)Dlx0-ndY$XHwDk&%&U zB=^oQr2tksfQF4d^&_;K;TRR5&6l(z4Sb9`2XKAt=4lVXN-ANU6XluLKVSMuV@fK*2 z&i(U8cvQZhmt4S2NlED|uwOp_rX(E)^t;CI)(f9OMSP!SAAZymES>lh)^=||%4??q z#H<0mG_Bk_159goYh-g}zTWtZ8gg@W_7kx$Q{Z!9c=VvJR0p3~M=gUz(B>&H$xl|} zMp%3NzvI%%ycQ8L;Pg+YlHQquKyGesxExpOo%qOhrph4{_t#6b(rb__nJAL+smSdy zAb8Hg9E0+Yz_u(7iMzYIgPy+$jI7eH)thP#TB?g^8dd#!uj>s~eyyTds}uwQUC&t9jGF;|&MNAK+qs|GOfVSx=$ z`Q^YhL0a_ z^7D$#Wq10GWT753kdF}R?WNM>!nj$bx~N^y9a`^U@zw=Us=Eo>)v(VE<0LJIQ-L)zO0oy8-W%iY`|s*r6uMgx2`^#%AX44(@L6Tg<8o_^&uOF+!B ztXZN}BSqqSEelrW7(Mfywp^ZQB$j6%^b^?ALkO6eB&B3NeD{6l*WBFv$peOAJ0=Ba zb<;)NXayRGlHCK2Y(5gkw0(b-sY3NZrcT zI2+UgXMtH=sF>B^dq{g9;vn^zTXeGE_||f+F+91>Jc{ZeRe^gbesp%gBOleC9Jpa$ zqM&ZH3e>|nj!t!}apvrxkN*MJ)#Hy<>-|)cK$K)WQ#5Hmac{mx8kD;vP;!_n+y$0p ziD)GD#52-^%~{_R9?b+*ZvH#WS6MPs+pxq5fuJgbFSdSj6guz0c3kD^|4_(j?6lTq zCKnjQB0%I2;I&SKrM?wLVQ!y9++M5J+ZoiDM-f>o`&1+6N>dGaS9Gk;y47yFTW&UW`dd^TVu3JL}k#DG|#B8(|vi9a03yp%# zfCVBx-1%^B_N=TE*pf0RqLFg5!k~3@4KZLjTb_wU%9~9sn_Or$@o9?pA;j-yd+#6~ zlU`pg^EcJ$q920S7t#C>+vkJ-Lu}aAl~^eLSapH&>2QmlN(;>UK;<}h)3(T+MvOTrx)KY3~ zY5*N(j+Cr4=pnX}TPXe&yr>MYHI{Srd|MDGl#-yfR5tz&4YG=jDu|A9|9-Wd;@3cd zvqXMqbG6k3Q_ZIl@ha21WdtI7+k%L!l;G|(T0}Z-IDM`OS=rX_yx@7~9qzHssEYn* zWL-8zrNO}jdXBjExV=N{=XTDt+(Beh1VsVOZS-t+<~bqZkGyn5kSRQA#YX}kc%C1? z!_?791v9DWjFk6pN-*x}rdfvWF@q*RTu!$C1Z+*$q<}+fs|mU5^Ic^R@xX^dC*SdzLKNP8{Bc6Fp7QVsasE*b{MEekgs$eN@9S%8 zIiPmZY8B-8qjNxNvQQ?8mH2j`$paL=+isnz7ph2tMH)?6V=<#=w zWmL`ZGvz1@p^fxy>DIwSfO#SUshS)o`{|x? z#oYsI0Pd^jE|$V+JDfaLq*`Gp=Da;GP!{w6a^Zj9x0@CM>GFMn6*OL~nFWoCvRr`q zgUx=eP`~OPq)S_e6%$gwYY&e4@uO!~TAW7|9Y~r%0$-tn3qyV>JfJ}SNf`3-0rIEl zGa!WqfqbO>@)Y^=vDnl5;D=u(x(M>mw|K(;JNbVD_PM(cx))MPu{{ z7QMMo9JqJtyzZJ@K7sxFNVY5^I{N3uBLYfEwDbLufcoPG?nW6iISH$&=zzVug<6xC zGx5EF@mhuiY4ECawBbP7SX66}gLNw8WAY>WjT)Cz%o6=NElJ5>C9HubH1cEW??#;^WWqYHQqAu3o$#f-H(3**r~;3?JG_8c`{9BFf3mHa~+4%^$HC z8yk16In140(a66T8D}J+l63J5OqDZZijd=V`_^ z+Nd8R0ZyJUb1^er6u5k9JzY{m@JEm5(7{9|my)X*RJur=X$%JSG;UxJ z6H6Oyxnq+WQlzP&Tvm7Yyr$*meowl;pDWff5NFbW_X6jqGL0NZ z!y^8iem;}UOs>d0>gU#YRb&JiPpk>7p+%ed+(#)o=5*y`e{FM<%GTEQbXVvDXNqj} zszXC*;=WYEn<9B3ANk)D6oooDg@69gSxq?lK2b}mQC4;NJL!JL# zz7CKUDOQ=i)M(HFK-RE~!SnsWB~i3$laYrt+mm7z1qH~VNyQ3+-7G6}m`}Dr%hb+i zi;(};qzMXK=B|!TPvbH&TF73otK9qi`D!ba1%L(WK?Azaowu?rCko2IeEN{U#K)*p z|IOoUFYn@!J#tV4Sa&3IC}|2-Zs%4vH*>&%sv{@Ux-7|Pjy5xD&{w*x(QbyabCFqh zo2i?WJ*$POTaBTgKz3BQTH~-tZ+UBTtpM!`OJT^cox}0!oY#?8i-pB(Zl%$VLTbqi77-CWviUtBp^j458MZwP zt=l2m+?va4X@+_ujmRieo%}hIj!Gzg-x%W7(^>sAd2d(Wrc{f#;$6w!rz}3Yg@WA~ zPOJXH=bG!03vq9>OfnV)Q(uSmEU7!CM^WPW|q-oo9?85uk7q zdJUotO*44vxDhZsB7el(-BxEpQy@Ry^-KuUN~SfP#kMXDPRh^svP!n6@)d!lLn#UK z@c~aZru9=oP>`&!Z~&EL1&i5pd^N>r6`06m&PykY?RD8Jn_Dd&UAfEaOX&o|9@DDs z-WhGOTKZ1=i$P)Vg@^rwBR@nhuP~cm4x?XQT+p(xC}IVz6t2KuY>@7GXXlyu#J>Wj z#&h`W@GOe5X4|d>$hPE%oj7rPn$}T5J$LcCy#XdExx^?jH8vYWr(M8oYz(AF&3%Hi zmKwH)6lqi}_jJ`C;V6PQO%=T;d!;kdawDQ-Yp@|D;)P)TUx4QJ7WehK^~Td@l-S?< zwYxKa&D&GSkj8B9r~o z*!eVzqU7tMc2X#t*t30YB+)3FM%Q2pl7~t?ccrZEJ{M3W@%2s|A9ABexi!kP7^Q&% zy&CDV)(T1N)1ucopQ89frRQ@fQ+(rB?3deEY-Y>VU@#vC9yXx}`xhIw6c&cL8eH4c|Jo5M}1zF;H^S2`o4 zqK8L@0m4tsY`f2}jYqGhqo|mdEuB#MVK~zt4NrypgZDLd;}TU;i?oc4LffB)t;=^p z=SIB=;`z2MpSR{*{rKo?I8zvU8x2!wBFrM#5PwiXNU7^6M6f(iwBQ%S{&BE1M1xzX zx6;q=-B`)QL2k#jM3EYka_7L@=!PwdYO`B#Q5e14_1RtnJAz(_ZR&?2uFXQDBoPr2 zonG_TFaDw`r6P%BfBD|cd7de*(Ycyh=|Jq7rb!yKNybZ}`l$orDmFiV;xKVckIINF zy~+C4_wDuQ{%;B}9g1`k){~3+J%s}vS+#W~ zR3A+6NPVc!AC1LB4T>EWjpQ!{k-621?X1&7h=USLKvZLGpsCublha$kr}%|j9xq~v z^f#tKT(z%$HtcfCu|bE)d2e_n#0IFjNDAF@i@^k8ba;4_ks$tyRK(ZWIj-zrYYjY*W@s$M_|PzNN;}z$Yo@9i!cJYQ^@VTo1iA$@|jPh)232B%Juoy48{uZSjLWqS0H{e zaYZU)#TmJUUgps~@mM|EORM*!D^(vprrqTKHUg)sA}wdU}8JZe*#~w3ccFPOVsuC^)h;j zrJwxPWMGi8y9Ao>!0rHcBQIR|1IVo0@bD)D{a2>*g!S^U*U_)Xj%vlbXrI5R2Te-Q%M9?;m+k zLi~#AOW+`c{n5-VnrNT+vN=`#O?RGyUgUiUX7;S0Xriqx9SY>#=e5SfiPv5F++>jN zw#W#0m0jIKFY=}Dk{je53>gD|Un#x8B$t$ulNu~0Z?V6tGTcnnArb!5{wVo$g-c3a zdJMcgbjG+{+HhkzJgnq=@i9AffWGBw6W(-n2rei90%||))!8ZY{QQ8x zs6f?3b(;zE;!AjqHGKrniaUJIawACR^Y{u!2Up}6T`;&zM>B2~*cmP>ct8nkC%=W)_wY*J6hw@owGfdnu<)yM- zDqp%?|MiEP7RpS$-JNd$9zI56k+?v3+FSKCdX;fzA$f-~a>A$i6AS4-UI(39PMj@N zvm3_CkqH$m?#rqi;!vpXAhdefSck{*7L2aUXXU3WZ2W6&^s<&-FMOx;<}^rm`LysO zPI|-QBxs=s?$@$i1Q+lA1Rw0P&@a#~`|WYNi$SNxuMyv#lL=86vlC z4V0TZo5~(bNj@Rr0!548hoiIq=C4@J9|@wOJ|9}l1UVKKv#c7cYuhWEIVCFUwj%S2 z=)GOlvMGhkoSwoDQHD~IvWk6EBbY@_d0#|(^7Z`juk5!j7_M8!kJ`SvF>0Pvy~4kt z1eu+aFei66H>qfTetxH&u6tC}9os9ecT|#?jH+^Px+RI? zll8euiH9!qYWa1cUJ*=dZbXwg{6)U2gaA#iLw9GVy59V}$I2MZ$z|fiSi=k?;A?#M z&Vy;0_?5Jg6v+jy18EDE8hdUIClUEUg7|B?cJw?2GJwh)QPMvZ89;o zNEFD+1te70^_(nPpRv2Cm~sSGGDq#65OZTmcrktN$H&8-ZGhz2T~|uZKWAemugQo? zwU3UoPw}A&2r@qYuLp4_%Z~>6K7qvX98&uAj|wOYl=LFO%`ns(;~}xV|2x#D^7-m9 zXbRxK%?biWNj0*FHtLihSv@_$%Bgx!VsL`rmx6Z>GEEv{)8pxy1OOa)gw}m@b{1Eb z(PGUhSBu0AX*G_#0W-xVCkO1rM@M0cHB-Br;EJmo`XBW4=G_HeAV0a@;_|9Geq>X@ zQZVB80!aRVXTp;f(M*Qy&;XgO_9FdiQP8a<7%9xv+w+1M`6AxaOHTA%A`6FxRCj?e zq4qV0p7f~r2|&UC_mvWD>p!?uzH$1rGUrDG3GyX(fCliUp>FRi?qhD%BZ1FE8W1v> zG#q)LPbn#MJ^`=%;=e>#(IC1uoh+B@7R&)@J3718W`9f+WPq@-;mu^oFAc$?MP)+g zvYE{XDoR^v=kp2ut)k@6J6JNiO)jAQZcYwmfkZ&iA38M^AjN4?H5D5hoBd>aqPvoT z!)jhcSXfxA&ZVRo3SG0~j!jPfd7$Rn+u13xxw$!A?gNdEi5X+%*AWZB>)uXyg#5e) z>IFSk4@R-j=P`4iCdL`HLAIi#Vx^-1D99o+{CaMmvO%KsWV4gpYCVz-ICQ0E8APRk zv1i$Ob?R}Jv;>F&BaO~h96qz$VtZ2{&zx%kzkV2&N zT6B;f+Qk9BjU+d@zF6PyJ()jVDH<0+Ax5z!!lT#88kpCAGAcQqJyy2dI2WXtkEnpjFk zW)z$Ws4st;Bx7c|LM17*_j|ekngm~3d(^>1h0(4Kr)2=sA1z|360!*axjyaf0Y7U@^u+D3i4KNO#UzS zsAL0dC%TTBPEN6a#z7}(Irt+JfDW%Ed0kHOk%{s6c-}!>*EY6`@`8k7R%U9a_*H20%_D3HAm6=# zeMhDhUiQ@`gn4*)^elCyy|fDxNAQuabYbXh{4XE!zkJC5@*)2Z@8N%m!vB9WtL|&3 zz~HT=!BJKpwFT&==#EY=K|o5H*+1ls6JJ(faP5foA;9nG=A2K{8V|m!1y1tB(+*yQ{P4#QRP7hU(_=z53_>IFz4TqoO86f7$qD zyoNM{zXTp{5KkJ`r+@Mc&Y?la zUEl$vj^&?&I@W%6NydYiyGsCRS=<)^sV`a3rV(fomV&MUdaiQ|oHx0hK?0>bj_b3d zwRhLkzBNFL$vT{#j!p{DI`vB5+kmvnyyv+As(7x{=Q184TsEq^U&#+w#!%FIzo@{%SLKGXm7%$5|=W%W<6GkLhu>Fjzjmn`H9!)MU= zG45KYrlK;+F>wd?MPx00{|JPn|K`@}14=FGbIhLcNu>7%6y{C08@Qr@_vX6buD)Re zFM1CoG%zr_&uYhu^-4h9z185NFmSJ7h7FOCkpS(X1sRE3-aB<5g>ch8q;Iht-FkF* zG#D7kOIMxPBADX&4&@$Si#gO>{A zVB;x3JZ24>TustB0Ly42ncLzgU}`GLQ{9ufT+<`Sg-RE3eqa4y)+LG zvlBi{re`}od$lDqqx-hEcmxD_$V-7aWVz_~xlj=r=yASPg4-!i%gL#>ALb*0L4Z38 zpQ*Rc`Xc=RFhs>9BkpSlo3y}3G@VYz#)UVM&1JN4iYqc~%(vp4H zwVaU5rLgO~dmgoHdNz}86Cl&-FWenKVT5-9dUyNVA;G~>lxaMkYE`CiCBWJ$om>bb zVyiYEdc~+&`~LRod}Jk3U=P-JwA}92um4sbG#=4pT51MYKDmwE=U9(nnXd2-5+u1! zLXPm5m3omuR-lK^>amnOT;UL>{dD__{^_zb^Hz>8lroR)IFQ#&E@LR#L>f?nGLRgw}X>x*(ZMBhr~SW;Z@JfN*)*(_3bK6*+QxI-$QEu%WL2xR&BA0$Q)d z!v^71zG|08YanUJz6#@`Op*1U*QXb#0k3XQv?EhqK_y%AFzd(Hsl2VBD0&W?1037`a8 zjCxG)cks99R3qN$`AVJblweLy&L$wdNktQLTIK_8I`nK|;-tNvncd^W{PgdTC`d&N zulsL2$H&R93mP5ijd968Orxqc@GV}(85!F>eZFT?m;bDIyhgN2hwkdQF@|_Lv$EsP z9j~|I++l4+MVtpTkO{&%cyQP!_L||mm!s_Ny0k+`9yO}y$^{7{3 zrEYo!N;#uN>ddEi+CpGPo#mcSl<5nBrbqHNT6Ky^tS0P;-a~chv)+Bsma*Dv%0V`b zRrQWgPzPvhs;>3KIWMQpsO}a2#1_1I2l^)|0-Ha!hG4QBZ=O=Eb3UJQ-TsV5 zrVzO&z;3trD~0U13LZYXKz`kn%wf%1`wad!8b!}_TLc-L=64TdGs z>+t6FYh9d#pk%Oq076j7F#j0&Ar7M8&89R$_PKpUWQhNAV{PW8Q$wl_H6|ePX6f)E zvB}kD0BgwQy)Fy#^ZJ^B#1sBGY<1@)Qzsk==zKp0xZ{z4fBg#~|3{7lmYBtE>tu310HS6-#^)8j`n>_)J0e~bdF1gXm;FS#FK33J#vZ>i% zr)E+==Qx}R26@x-!0i$pm`Igrf6w8q1jCIgK-to;y-AZXUY^-HTI;Osyf>){Hbqr`k0+y9Gj~<43kHfh`Y4?( zP13sf*;?NX%QOr_p>>2!)E$W$IH$4|Vz@>>C4Jsl%Ivdr%r_bCL)!vrzfC^>9msy~ zvx)2eHCnw?`NvE`_=daN_ar_xSJMdr&-M6C8AHQY08R}f5(xB@x>s#A69F^_(a3y3 zZ#s6H;nW1Wst(wPrZQpWQ;NkAtlM(ojgbbS0FVX#1x%Uj3k1qMBC=H5$mimy*a z&?$~OlvAEEX;yx{I^7=GYj;WIw*NBtT|tH#*S6)96SNaES;-C8fqKL_ZVcReN@@o?hy{uHiDkDCe57BdXArCi?L_Jm_7Ew3z3)|x2Cn+&O! zEpKmRm}t&EJ_-25)nLB8a;^2e8{E=%x;$cFPH#C*4?4sL*1URO z6SqAJOLiVfgTo~&S-Rfj)Rg9gen^P%f{J-VTw+Imx>`rnv_Dl@1i9dgPFOeYz~^Nrx*E z+Wu!twFn^2i)F#S25$)yIjrzOfflG2AI!GEZN~IGoXBpWRWE%3p^}bo8ykxQ6)8%M zP8Q%D#rh8h!(kR*Up%3>uzOnuJtO|yi()^@CupbE+Z7t+b=_a*g=m4_dz))goec_s z(w5ty7Q@*X7>5`@zxHtIPS9vM1J}J`F7yE!+JfU3JU&lPfD2bLN=r(DGyx+cqx7v| zseb1K*K3QD!_(L69w!3ZY4P!aMMWf#`SwsVZT(Co6S;Ks4UNAbSSpJ$Nz2lUY-*CI zwE@h5`+g-QjGVQ#i89pe#7`cYnL$eRBRe`Lup#BW5{8W>6V8yt|Fqcgbl5(K)F zwcaEFI*-73*E?F3=8XVS2PY1V8AE*?>nfz)8;OKQ2~ zt~?{bwetm^JfpmbSSH=8&)3_Rmhy66Gk_#DubaEa;bCHARh+wT8(Oo_G76;DW}!pd zwx#`tE80+_Gd(5&lOdyeg@{<-6QYi(Dl?+HQ#XL^e~pR3oojNIC>l9h%EvxDI~zD` z*mC0Kb=e%~`IVD|Z8kAjtLwS5OImD#l#lCKlSb#m|DrUQsZ8L+OPbRgYcRcl`$nyq z+rwL&U<8Qu;?x^I!;0`oeJnQ>v9Yr(l05Vcj5BdKb*FQxkQx3PF5BUssx#KxDeSht zL05*!CQmd@&RVCP;U}J+o>Fm4tziW*b}lXrR=rvOuB2D4763ePa2E7M3)+#Ckju`c z7CLPFma5rkE-0H^^Q(1a`?HBWn5SEDy+Ey)VPg9jzDe?AE z7ezmejm8%_td7AKp-9(-TR+N_J6FN7#cOy!kNxko=66Tsq!_hyo-)0{gHxBNg{l?x z?pywAJP$LJf3TH?F@0f;KbXpmD*gHIDXY=<)1F$gYk{t?CSQ`wV#Tw%y>1_!%&7cx z*0(&h&v&^cV_W4_j^FpP;DjY=DVL4X>Wcr`V~1X47G()>>CD&op+r?vU)v4E3v|+6 zsIH=fi~Fk;iRM*By;u16Li`6Rlwa$NW!PT_fBAPbMffp>v6cV5If`_}=%<+fUbKx( z3Pc4!YV7yZPuXwk{(9ee$@qga&W|*~ z|6Z8(V|vKfw^RZvp*2tCUx)U%VW%)Inx`z(Jp1<%)!r8hREQQn)Hh%^YtH!hzG!JN zokYyprCDha+N%PL|J*kwa(r`>j+!2d@Vg(u@c%g*RqV4YdbnxQJMkMXO_dJohW|W^ zy89_llNl~`Q79bb8=G7Hx$66S3LkMrs=N&F?+lI*|2bb6oS)N_f5EK$f4=lf`S8V` zAp19T(cfPOS&Wp5`lz=9mHHK(CYQLt@Z@z@RWeDv2_eQgnck>g`^q|#{tGN~4q`*x zkqg6dpR*wS_TI55Y`xOYL7^yR;S1kQW+t>%7fsiT8TTG~x&-N&j!Tq4E_@CRj~s^1|UV^PC<#^j+kU68!gpV}7|mBXaT5)8$iV zr)b8S+2NOlomp6128<-KM;KSe(h^hOi!~&$=6?Qjg+PP-$1eq$_FyyC)8+AjHH8OK z2?UNh6Hlzv*=*OVt$QQMALT3*%@t@C;MpXP8x51Ud}N(V|KhN}mv-Uyw?MG~Pdw}J zb0?4MIy=hM$?EhNJ;cd;?rTLxxX;dULluHli)+a@OG2=mDbh%O@_C3SaXGEn!R9tZ zCear?X6nyRdHo;yDIsB3p4ftV53z2B{G$Xc6-r`h&=7xr4>FAvUt6e-B$R*CVCtNI z@bNl|R%`qK^~ZZf0&wW-8O}$wX|I@A!pBy4hqDX0g5)J|z1De~M3^h~wj-+ZmwLmV zbXI8K5tLC4=NMQ-qy|$jYrX0m=v)Pz>i?NJKYzGwX`?3;&-w-B5kS)Q0(i*qr8YhZ z44Sj|B?~6K5(^o+3Lu!OGb5mo@ZaQGv;hVaGJ;6&y5V8FSc4bT*Eg~n4!(mCo%5`~ zZiByGaD(F9Z$pXVtsVUbOnah>(E&FHyWC*cF`s^wH3q14Z_jq;#O(Ejh}kVe{QXf< zxQann>IXN{v6K{o5g~E+X?5y9#CzyBcm=CB)@EHf)?92xcBi6auUup3gO*V}-+1uhq}pI#6}}aheWRPp0Er@aJ=x&>$i?N1-DU`sf6GHS(R8z2 z8ve9Vq zf`dDncP-S8>WsR-@Y?QRRdqRLII))Lv7hOfH?3+P)rwYHM8}X`ryn_I%%{qOxjT-w z&XIS)Kbdaoxf2JEE1IWjakNST^%#2`cSIO&_4C`lUJ8fpsXof;{*u8k)e67}{;T9Qg+MOnGh> zh~F=8Eg1Ibw-|AzG$f_=bvRr79j)OzC1yAMojNm9rcs;7K_3sKdP)1SaXWAv1(HF3 z;`??2ywlUegidxsEr^3B`o4#6p!xvQTRSlr*jPA?jDU3@4<;Z%`BnHUrx;TQ^B4I0 z2r*bUS?=;&GB~)I?FaUX;N90O39OOJfOYG+=BTdFXVssM_&r^BKekS*R@+W}OX=<= zl*M;>0evrHNcYBqniEyG&6-r5%BWMfEr2NU`}yfVPvwYIL5O&iZNw8ud< zjQUY6FKMWdjYP?jSYplQgQ$s^dYlv)7HpbSQkyB?S1Y`(pArs-k}U%$UW5}Hty+uDz};&%qs1BaPvfa+GqvB7lQI4D_S;g$=-o=-DXt~1z?%f#O=wKa??#3n zmK?xGj)_g|im*ZU4a>40i3f`yM3!V6M3Hvz?H^;7?)(0c_b%-E?hfd52>q6Qd}Pw9 zzR9;FzZ?90J6mt>NsjF(h+U)^$|;j9NMO_$Ra0~yG<9@)N$ZOjid(+es6M6rO!%a~TPjvg`|f_wcK@P*y7=?gYbVP1rR9si39_ zDZQIeiRGpJg$z5NDAQ17V5D^AH7Q}G`aNZ3!U_nOuYUUuO>UOP& z5e;@*PGwWZ)m=o{$VT{Ql`_rVMxWt*p(^hgQy5~8U}tByfy_G`XCMcIhq$jBw==29 z_<0lo^Fi-9?q1`avz40CZiAK=$C3oK*Xfk#Sd(X@p3K#X`aJ1m5Z6OkZ}3WIqM-XL z+b158Bp4XhbJgkRf)|PDW#_1iH3_^G)`$1?Jk~f;b#*48MkK$5(KK6J;+!)$Yl)aQ z8mi1_wRV{DI3={n*4+@W&ioIG{f3`2XS-Ey$c;hv_>PB#$c}{{dS>enEb{TiqE_8kYC?R_}ISf{3R>Pp$$iQ5^ z-}b%k%6|Q%I|yb941^%e=drgho}-zp(fX+5XtC~j>DsIS|D|J(4E{ipWKoARH}F|@ zH?|cEWi)4J=QgKHsanyV7+-V^xysW1$M0{Dh>4DlEsA{aXlLhL`~GNPl{Ml)o=V(KfEMPiNp<}48#Dx$-6%b%0_-dn{RS8GLCW6 zS`&LQ98Pq6c;|2o(JH|&Q9B=;$w_j^;T6Fn#NzztexCrvgsA;;LCeQ%NyGb1^XBZV zK*IP77U}BZ=i7_xgm+~VLQ15L%YUOj^79J}S?>FiyG;y`0`gm+DB3wL35mq!unD)@ zMBIHV@or`B~4`<*=<-{4p^767ql_hE%9op=vn`u?jFB9nk zg^E`Iq02SQ>G|&N4ZGOEZH9)fY;JagzCD_i=7JJmu#MPrmsVDKqgsWFHKEbB*NERE zy^qw@>#oyhlE>($Y8=smu*iPR&!+>yrPaUFY5dP3p#CT>E-pND8QRv?N9Pw{$PT$# z81fu$=?kW;I{;rA3{4jees9)-L0<`>%c~pi9I1p%AOApr6X8533RcT|O+k?kNQMq) zeHLJLG`O77VMSo2KE}>-h@-=!U+*7!{ky)Q!TuG0DsheF%HCcCa5)w~duuR+OgpD{ zj*rJ@Nqmu%=UV>zw+-|ItaoGiLNMdnY?cOejT;EDt6Uw&@#OCb8kl}D~wFgWa zM{Cj#n6#zZ0*=9*F9VM<(Af$jUumpyVXnz_c)LdMZ(3?1zr`!~M2WnL3f}Gf3qKad z@XKRU<)8X@p5yeORg0B++3Z@prkc?F+S*87@Rw)&6oVFM6yPNM{P}?`*Z!=J z2q^DFHU*)9d;SI7U)@G$8unH6H*X4J8B{|q>bdDuv3?n|+Mj%Q3-q`=vd=+mJqurH zD^Kl-Vfi||CXP<=-p6g0FjkwTujReu7i_c&?`?d5ligD-#33i>1f*UFx99p}j6?fW zej+)$K?=*U($=jw`VY0oOQINmU1P12||zc(-taBy&9ymZq5v7Rl* zQdf7CilzOWpl+~rQ-o@JUYbTYGF_`TF$L zep3HBO?e~@d8>|&j@x#c@WsVt1*;toxQ}|XDL|B7*;?;#sGPSxu}dKzJl7zSPsCS>=x6rSB^p^y>YD*6Oq)iDUL_07~2jdYSPQ$@4;pz z4Vv_t^u|kM4T092bjh5y!djMe79C;lw-Th;a;Wzc_);xL7+2pg)}E z_Tu8ApPFk^QJHvFrt1P15Uj@f9ptY8bHW&!D2<9u{4kh_GTZDPvtrN*$$z1M%Ejdh z&^;L~ExI$TGdwyKbbtc^%new7fNUeJNlrd(!~^p9abb%0rEWMZ3^YgCYZ)|Kd)3+5iSpfT-M#WJ_FxQ5$1)8v%H)S3@dRse)z*M3yWD9NdvJ( z)~gdwwi#}C&pp-38&VRIUf^5g1EuucY1fa#Z9)J8uJ?2sthR6r+Z2Zkr1RM94i^rY zm(M4(^gg-rI3+2!Z}r&O-34%O%YF^M$Tx{YN<$5~RY{Tj*gwXsJ$~lEy>jm`W)+Mo zXR~siZ5+CYZ3u zdE4S;Ne&MV+WPy$J7kktK%WKncejDzm850BeP>7&N|^9Chiuz;$nuHPuv#m;&}^^fJt{+#od4V(0^uI&fAc`oejS+Em>M!r}e%iy#*Eg+o+ zs-mTXd+)V^q+2_N0njn zthzq=daE7d_weKG)) z{7Teh0}b#XOP!G;1-(#hs(`12ljffbl1cbE1)6&AE zR`y62_^Jr}rxB_Zio?1UG2z2}mYp1XK24}l&((P{N&@6tvrkOc#V$$Iy@ zZ5Fp>VVJhZ4a_83NhU1Wg}bx!mMiqjZ(Quzx;_~0M1#-DVUu`TOL7}mXzO0%-njGQ zS(5I+)&%lv4W61iJDs2qWtpy$km@1<@qpC*H8-?~#DznhjVMY<{0 z<-8#C``@{-s5wZh!hnyvxhXC~Z3J^IX~@t97EFZMn9?f<-Zq?@%jFp%aok#IhsiTl zxxPqM`=UWgOrM|+zkOJMpuvtYiFJMeL|=bi;b=54W2UJ|KjT6J$B0nS@NtsDL|9w} z9(ER!ztxV^q2i@@!l%u6O7CH!UANYy+AMHV4_d`1JvV`-@i_8CnRvdc7_#8@q#`Q8 zkI?<_(W>h?7=sIUo~Zaw2JNs9SZNXME#>z-wMnTw4v^xiPLo0RW@Ni!~FB#arJm| zx>DI;=esLq7c|jFf=ut<)8Kk>rsIgEPCj)aB9|9XWO>6d=N+YzMWoM6^M`r7!yy2Opl>)Pl+^W$x`;vo68u!n|m7vumG2>~= zX#~A&wlnS_o~D%Ac}!F|3V-65*U8o*UAHwNV^S>*FNFw*Ml<<8-@P|25Cx~YkVme^ zbs&m`oPsB`V-5R%({8C982|Zc^mtz@c+Hc^(5y%-kG^lG#O$(O0%Ej5o~5FQLb0)8 zUp<1n2XLU*SROA#JSM<4;p^d4k*enZ!QOjDHJP<-pg7Kmj)id)l|CqlbODhXu#7Tv z>C#6jp(DKnh!sYqNbdwhdXp|)1nCgzJ)yS%0YXh6Id|s$zH@$^e`l?;&ROrQvx^LQ6C}Jx>xEkZcAfdvA!IYfhLXL zdt=!6m)R9Av_7w}x*~Bct|4e>zSD=hETHg;_4}DD=IOG6Wyd;^>2RrRKbdg#MgwV2 z?ibhfel2y!K3It&{k{6~QJ_~&TM6Q_vZT>J4YqSXa<1S4isiM$q>k--d_UFUEjRmj z7V0VS@>>?LleGV?4yK22u)mf*DCS-g7u|7t%IWZHeb?qcKNmJWolNp29&bN7+{6gcXaB{R$CHo1?+l&W0Pjxq3(%cZ{ge}>5dH(gZtcNU8 zh0Ph-qxk6l*bAesbHDbU${BL?ylqrFxub^nj>+W}kGRXgmVDyXtFsSGIHbLJ-!h;L za-*JQnCJDa%5XYPQoEx6jObT$ka3OHO@C82-5Yh|>DP9%*7#24fxIOjtlJloEH1~z zO4JXr6D6{s<`)q@(tImHU-;hpl?TuTtagQR^tV++K(eYB|Gw|?shYSu?H^B^qxZM` zBlLGVWGG{l)0g>qrrvw)iEG!y%u6U&&-b?1KI&uxBa-#6e}i9h#mD(9Y;^h)W7#MY zd?R~l}4t{Z!8mO@{_Fl$EMaDTIau9Y;$N=`KxE?qh0dywmE4mPWat} znyd8L+GP*BE_8Kz*X7oRPm$fWlh!-Yg}}$LHPWLl!V--&0oNo|59GomJ@4K?upUU{ z>%0+?D9Fq(zOsLh1xM5me^Ybyi}#Urj4c1x?!Q~3zXba$@qL=kZ%F=HB{Mob|5P)q zg}Uvqf`zqqGePLj@1OoIxj~S@6WOV2J%segw?8dVKqSa%`0jQuv(S%{t&!X# zt=it|Tex+W0+kezT@WccBi%0=hEhJ-`zu~AQoR2Fe=kbt=@dzZd&!;kU6ov4ZlDw` z(f{@RtfvfTF%9}V^{r&sp$y0(o?=+)FyrNgsw!felMy$!&)pv2N7r~1KeY7%Q6cVX z_gm=hZIB{34}6FKN(@E^#~C^@Hc{W2-}}{BQ1fHI-RH_d?dHM8>h-EVr^DZ`^cd9T zX|;^U@9{ChnzjD3Qe{Z|jVVX=oWVcNm$TEEZBs&xcN70xQUlu(Y=sx!T!R8*h|B$S|c32nzYQ<>? zyteVt1GK-ETtm)EdkdTZ(OqEgoqiC8cq%5+-Zu6s_NMVg(IJu0n$oTr{@32O!O8Nc zLip?dcwOeC;&s6k{Wl=`e;erqYl`k~@r!U$L*i&Fy%rU^1={ZzKVg3*jy^e&Hchtv}|_nZRCMb7-UzJSg)~~n3|Rf4cpur8Izs! z*~*CeC0z^^5p52AKn;SznR5LXLZlwNy7!5$WCQC#n>9x5vgr{qCXEk-s2tGQM_Avc!R_OzXy?>=!rfeA(txHdp%G7=J=#RVt z%G*r9uXbky|2UO)f{hLQG@SC|@eoQs_P2P3g?PsC1CEw|eOlhqzwiYzKY#rwtF&=w z=-h{z9C!q0%vHo>oO2k%hkqODop%O3&Rpr<_{c~*haSvt*xgqJ1qiTzBHQD#lw$t9 zYIDx~OpQ*x`lyw?z32;YVm4oRVqx(favD-NH2+mq`FM*gMjGZ-@7dVfhiUNj-#QKB z`+%Wmuup^;nYZaPoP=0rXjQr5p0 z_{_Gqex`JmzP_1$paa&i**jnIpbRi zt(r6c@dMgx3IxvonZom0HhIF3-pjp|=AUo=t4+@Z)h09xh5i1m77+g7){W-h@BOPc zZ{g6_xd&`++1xtV`f{J%Z|RVSl*gt{|7lVbh}>NThSQVu1!w<#B&PN4{r|L>Rr}9< zpAWw+;JAZZZvFoI@2-wCA7KTBl9!@|HezAf_Ua5+xCbMw}8t(%*ZM|9>jbnNWK@ zGPdBCFx=eyCyq<(r8=9`)cSf)jN`bAr)R*DZ<2z4Lf{UeH`CGBx+}>8>uOMBrWw*J z_P@CRRzw`aV>EtH)tta?*Az zfAkvfD+Uc;go1)X)D5kfLPE6m;eHd7?%C`vLY?pCbQ{ysScb%4uI`ha6}{1ppx3WA z4v-izr`5-SXE>Vy#+9>LR;gh(-qXK<-|+Z3O6Wi7bX#9!diD@YSW&Ogh$6kg(J%|A|fJZIK`u$KZnG# z$&Okdx8?B~DFBmAGul}h+IoLU0J-Y~!DWZ6f{*iZf*ro{jK#S!J$<1aR1 zzID_W!rAWKnPERG~Mm%1xU8*xy z)S`Xn`$c~3sKu1LaQfnzi>QqoS+Qf8x`jdxDAO9@NJa%wG~U`YUm$ zV1u-i=9D}Z%q15CQ2juW2eVT8^uZqt!p66<3?r{IB*~#-0QXR**iWV-TDZx+&TPIb zzJJJHR&IH7Q=|nTb$tqrBwK>1aj-X}<(8NG_$ikI-P6NQnTIj{y;8Ou%%+%PfZBQ5 zH`ngBy=en!ACUR#ZSMm)qzRIFaNY#7A8<1OxkXzE7RV~5RzEEk(AaEDZ(ESvlajZC z52ySCXlno$el0z5`BMXx{<2VQQ=YH=4i=<#y#ovDKwf8wRpAM9O zIZA{u2-Kz7)(~EvxonTo<^HUAg#D#RT1xYDYN`;XsWtSCc)-MC;52hEjrs85>%NNx zi=vYdM0lVygn;Mg=XV(SoC2_u$UGezn;S4;f16r|rM0#FMSsAyZKd-_U!_nF7~g-7Sd%RS#y)$usn(aSj7$z zgC7lb=&gx1?3?g{Hatx{s{=3<+Hi^QLB={1;58UXIZZrb?ugYf<6K{%oj9ip)dmLi zCUlEm0&tL?3O_U;;5B?N*Y1whV7V$1DlsoIv;O|^jIYvQl|`$=j`StgT=<|@XEf+A zJOkMH^!I_*hK|5XEcgniRqix;FC!}}@ArTJ`cIIxJG3@bWL^X}?Vyh{K8{t#+*XHI zS$VamlnsCtxz|Hj`Qg67Lc{xK(av$=tRZ$2BSrMLPbo50mCQHSlyn+-1b1}wPpUrv zZMk6GpwL7sLpA0R^n7KrPc`@G{TlsxR!N6cs0nfNtAmGm0Fb`J@bZw4ZsFs*^v3JP zNX+x|(}nO`xp(hJ#`;hd{%8Pk$u%hW_w|a8-mlEBPewyS0^pc;n)s4dXj+p=f7x_j z$&4uN^oODJ;bs-Lc~mhxyT3WRQkQnWz=KoP^0QHyO`5FV{%2PY7k&DW6VI|`%6hG2 z!_9-VZmDQ(fpMS7>K==SVfrfk1cl4OCN_v6Hh+o$ytw0J^?V93@3Td#Ztpj zN$($j3>N#N=#v@F<(C?zy>fU!>S*fLJD`wrwYWlckMMriX3Jq#9f~r3(=4qGeRApj zce2bz16Dx?x=7xR)XXQ0mpNrX2PD0E*rE4OI$^hw>Qyj)1xB*q!+Y}V9zK73?vt9X z<)>WMN2Ap^{IF68my}2+jR5j?M$DY0`^6#(`%!>`X5~@uU3Y zr1;Zk3rHI5#?rM?XADjNK=1i823{VJ=ws-+yYysI^UptDm*@A|=(e}F4>c$SY}C^i z_-V&+%e?*x8Ml&Edm^rmzDz>cZr9FAY8J0UvG+CVo4~+q@eCECScCjWW_7S^Boz7? znM%wS>-~L^GGWB}qZTr;tgK9E=MeC53TPtJi+SIgn)JFCbCHIgD+9{*b^1j{8ShY+ zJ4V_7qF?;l#>M9(m!!fr%!isq#t zd>^9=&FXRoOn?6d=~_9z2h`^-xDD!2E|2rW?hp_XS}mjiCZz!;n595|!lahID!WTm6ScRh1!1HTsw0RJ zF5*)!|06C*?!!F_kY17LJ00{n9T+3%T6f}YtwNEW6)1DDBW0@77lNn<(b^vmRYXM7 zNEEoG58{72!N{0rrVrBg6==P>A(9@IHpWe~Xu}z5%c5b6NN!u=y6!A^daoPJKb%8>zNBT@GYR zgCBh)==(eOSoGk=9!|&28mk{I5QY6KGw6(#0O@*Rn3tGd?PINSAS7q@T#aJ|O@Mwq z?EWs-eML?lV-2auQfR_59-gdLhO-@E8nm0W~i zW_oN?E>3vOeWCtnw{RZHe!a^LL<0^EkQzzSckuy7*#NX^+6hMI#`jorF`JFwJl7`L z;jl8Sv6dvB<$wf$(|()t2Hrl*3n2Fnw`ZBa%xMn4*2wUQjd*0k0J0y_7#^2B76lpF(q7L^o8u=eLqURYaokf?rdts$;c<;t*##kArT z)8DUkFbjI`P2QD}U3m5L&p)U3FEeC6O8d$9w)1t7+bF4~A3uVf{^F2zf8Dhy-9Xz! z#)!+*neFvwyI3ThIl*95?v(7a`sg`Gb;fh-45|LD=Y}@#nfo1Nc;I_`dl{1CeImlS zEW51tI-MJ4mzLsY$yHTVPwbJMP#FyQomb`ER&D+K{6hD_b-e0}zJ+lyPNj!EG~|<) zeKy6Q0$g<>kAVLKBC!xwW`#AO(w$JAZCoV6EU4!@v-A6mVaBog{TWr?y0OUfO?ZpJ zg!0jr>!3_*^ouqCsSh(l;2DkP)^HZ^E~ zItG>(I|D9&!eQpS%*zn4zsVwS4+i_uoo`6pM3xQWdr&YVReNXK#dYMUVm%K$K71Im~-X6_ci|b=DEZnE^~7{ zDn`B51(o!*VAIfxgvHf4j8`xtk;rOKLJQ^sUx|OKMjN~6lUu9@)Ec4N@#w{~7TXTxMe3FpkK^Ve)f zKguk71xsAKColV6+kI&y9ALIe+!i&!PJ5f2oa|Aj{q9GXnBNXT!8tj380jg4muJ<0d; zh6-FfJgj=Zy`isFC{?r)q#W(xi=Ebt0``~rPMm3rd(2DreW?0DKkoq~Jjs%@PN>E* z&MYr0Z%i{{f2z8A^8+9@z$%u*1uSxOSxZFn9UVPh)cf?8)>t_zT+&{jg!`O!c-UD) zPrX{7kU7EBVY7)k+8G8@P{=Gw>~RBZQ5eLI>M%gPbKPFjiV-&L`0=FaSx3@=xX|jA zhw0~yjg39myT~`*8hgDLUQl9Eh(7+)<5FMA7cX86yEOzb=??%Pfy40i^*agH<5)3dnh_rYeOQ ze1YWj#;%xd3cq=pJXmaL;2gN+jQi<1=w4GWNB|HZ?Y3|RY6COC4^}H@^BWje1KDOF zvrnEtttm5xGT{dXf6~MB=^7$B^=@6YpTx9%?fc8vlAg)QR?hVypDTwrDG0NqCV+24wJ`h}2hVk#{ov*{Yf zIl6+MjZX19_uKPyF>imrGrogwD?Q&CO;{o z&qLnMXCMf_Dk8so;7^+kgIWCSu&fQcR~g_Y3QFT8ZO5X#a2!z?HA_okm>;zdP7`*) zFJuX{Px&V2+hBEN>sE<7IXOX5X@B-z|D>d(n}-CJci$x%KIfZMz1)Aj&!}K>hO_bEDl?MvlQ*K2&J<3WMQ-3fZ!rLa=SLpf0pUw1N6?QG1N_ z;pIY{*B?&l&sYNh#mi#RND@GZUWF|CF)CnZZuo@hbFN86|H$(Lsg z4FKyQ@Ej^^bGF5rP@uJVuxJQQw9wE{_w}S+NtdH)kLKp)NAi%jbJq$|)3U)~3aNJS zl5Qt3eq*q_>k_Ue)8z($aW~7#kkVR zY9W|~VY~A%_?K+Gyi0PP4#=S{7WdVGQ*3N(*4TtocSx6nKbMz(pX}y%4y<3U2U}L} zE(v$QX;{hux$7!wO-)UwPM=PPV?V#M_Pp2oq9Re4Kgggge%AOCd6%FBd{*U&Yr>Ee zY7HLaE{tNBMzUP`pk1juzdyUMam7rfIhyzS?U)Kj^RJDKFMwqgp8K7IRir(-f8I%1 zC18KZfcy2Aew|*cTYMnVJ77I8kK|kcl#UFC(URKH2E`{&TI2Q?xkfvroN-B#grVO0 z`-~Bh5%eTTj}&wY5xpNCrWAH;if{`51Kima#F3sJ4!M2&qPhHMPM~U-ZqnjeJNXOHAtB-*Vp%)XwuvW^^(D9d0P! zR0;b1xFLWeEsci7`74e3T=t~WCYJ5e%?iwfJM~JeEZ(UXi)FG|Brx>E6wj4=Hn*qDj#b>UN>K76CMFnhxA6Wv^t}ca-$j!}7^6!zUq6V_LZFR+0QL71C%@;K1bQ<S2ma^TrKpFZ)TcL9$20x-=eIjk^GCBo+ zG{;BE(F_o;obFbq3;mGrTv<2YEraY?^&=(XyY@FPR9EKY<#jG9&<+YWshf*#y>j^Y(#0+fJ-vDE zR`SH6A|@drVZx`C!)I6y7v8C`-RK7dpo#*swNu|tlXksouZ1DVHn`oGN*Fszd+q!o zi8O3ZcCI2!4GI?xd{T~#8NKt2~@$3P_c=>TBKttGm;{)aSuLaVsaP9a{$O^7U5o z;UtYb-h{({4)8yaz)vKD8^G;0;@6+Q%_wiiQW5>5tGo6PJVe z(K1_|vGH-Yc|C(2yE6gvo#IQf z%ckyHPPNMmhlGXz38o1&T2@ZZZN)ZgUI#<2GuaIfW8Wbi+rAwe{L@C@VuTihhHfi4 zqF1WR^dZ3GdVA2}3h&%K1<+d0U;h3YU|oLUrE>!QsscDs3>TvS#*yL14Laf9qepaX znbg$(-tga1`tQ>Ce_%hv{JCH9zqtVa-F^QRIsd&f#x z^Eghe2yJaGm0Y`sm9bABODY+Ez9R|{w9kY%Y*dVleNh)hKTa@b6$qQ6?N%bj>gy0U z7<}VYA0C@}Y&+(a)H3zn#&MZdl;I7~VoW`@u~m^BgL{fJl#GYJ9J_G_4lE^Oq*9;f z%ltF-kBn7^_mAE9nf~SYTi5ud@T8?##wq3t@VGLMvc27tW|#~9hYYn(;e-pV=;^_6 zHWJ)EhH*VN=(6Brn97#hay!8$Re+80Vam!3`1>K&-(J9;;OlOwT!Nmh*3~xvP%JH_ zkr{7qjjIfZ#$UM^wm;OJ&2yz)rrtyi$w%vXnZyRshH2x1v))??&Q^oTQrjI>$E52Y zbN~FDv#%PhX>GO>ujY$YDzTsMJ6u2?`pBLub1(Y6heF0@<`!yd(6BV#PV`pts!8!J zHyWm)9ji!cOxj-Xvc^~I$?Tmr@0m5HU`88UOk0xBPgnzdjB#GwqNV=rhe8K9#Vo7E z-C+OD8gYUSrZaLlK#nq0pxpW*W_A3>2Sd!ELk9=Ps5o(&N7;i$R-?7?p$@Wsy5n4O z?<51lawNZ0d>O%|T$PT{#FHa~6^Jhl{Mv0WX-#gp%QOYeNk8?m#6;IdgF=m}bi(Fu z^P!<#v{+H(Rz~b12W^pYRGwHldN^`lIl`YXY`M#Y7UN5%h042)Yf7xY^g=erFIvxh z=jz(-RB%yK6lG{tgjqslr|;b1q~U4T)`Zn%NK70%g8TLpc2V7KBlV`O ztG#E{jita}fD|VuNg;PkD=i1th8L5~6~~?!sGK;X(9!zyRJnJ7+lb&i=#J830s;{| zp%$)7u1KUlZ8cD6e*vF`{d`_#P4Q^7S5f)R>3{a^G3Yj;W zZ^U3nFt4WN?42j{@ASz-80pxsp#fh1u*2+M8E|nLpUq8w`g$s}!g<&#&XF-0DEpPnVrnXuW9&-e5Q8COd)O?YXh7*);i zR<7ANV-J18I0i{Y#={?=^w1iQtMhSPo=NvzjV=9~I&14}FE>NcEO?w^D(6+ZgnECu z8~ft8aM6-H*oWf=l7)`OFoF`A4J&&LxHeFg*^duXmr#b|pJlH-4DIUse%iIwQHs-L zd^a>yC~j|G3qd27pB*i+)sdBz>l+*L)eSpxz0`%0cU)6C_pPF0h;B>LA0yKC;&{<2 zOK2Ek>qj1|fs&HaSlC9XcMknACP413G`gavnN?UQ5^F+!!PDL@q1o-#a1^pSQFTo) z#Uuf^XEKs3hkMq5fJq^`x>`Xw0=}A;YN!`A@LP>kj&Sxn;v!Md8?8AxYre0)gPpNZ zti-9}=xEBywzt^rTz9h{+0_MEQm%WZUYR8&;sC7OT_fs`-g|tmtjuypLUS=#VsUO7 zuMh{6t=E-%5X5ffoG3udSp2ApOAOuJ^&^?r$6#s8X!RUjl?X^Qlc~h{`RC^4^=E0~ zO#6zc z1U*@I!NC_f`ME0o{{9Nv(}I0NxEQto-|ogMBJ(9-Cecu?Y}Ma#Ha=jBziK%UV@ad> z!^C%5aUG3t6OXnods$9a2fBN@FC-OHNhlwz3q&P0({^3<6Kl5PY1=V z{bL#z=_|VgV%OF)pZJ4nZ$Futa~Q4VR~GkcZZp}~^|HcU2(#|}<`a)vPmJW@3*m_6 zkgLcjD3=5n-p-Bl*J>;_4E6N%s%9)OY7j1{-|40$E=yQ1Ks>$&f@xuWFnSa2O<|?J zgVV7J>!YP|P{H1Xq>>id~1g{|sNWC5JiO<`(;6y(3}ki1wgo_Or8;*zn4)J50S3|>5SNO6&bCY$#Ujm1dS9XQe*SNMY= z#jKp-%?GWtLk!o~w_icfKZ_b18sw-@85$Uf8XGHAdUS^Mmb7wT1MPWPIj3*@Oui1b zEmBjY9TI58Q9Tn)p$ON3I5-kLJaY5w`^J3#h>m84WHUmb%G%RI8n9ov=sz{;6u9KPpal*#8v(~2eelnM) zLhbb8r{dY_R>`Mx*-br)sR2{m)6qO`Jql9Bw;j7+_jb!44ZK^ABQKXLcC~4x*BEHj zC;|o}i=F`&?Ra_eyX6gc{Xz_r< z=F6rox%v5BOw`$xTabv4R@&^_^S_};rg~``8itEmv{?1cV{wq@>F+8u~ zK$GQ>;?4U(=g_|>W51!F&c1KYIq_l^Jx`OW@-&UqngRs=$c7l*u*&i9&TWKI7 z7tX3rwxXA$_7-1I)-?bcQel5O%$l<6s2s6*;kp&@rXB3%HH7!{^q7$And(;P3reNC zn-aF%??PB1b96Qx|JrG1YwP!mF-KwL!e@*LsjimvanEvO<9twU{b75?Xqn4>9JS|t z*vdiukuxnx({DwQ!}INRhB{(-&y%7kW>5w~O-QfM*yqP~g1%1`|G?p5hyfIH3I7%~ zv6GJ;>7P92-6Yow|C=&w^?mb0-3q*0|MhGA*O~YKo(D5Gw~}$gE-JFjizj(L1>yXF zKPOjB1%U#j`R5iZuTOsKadk~a*sd*ozgxyD8LjN7XYdF3+LD$+DStQ+)e^=cT$ze~ zt&bZD{Zt^aY@DoHH3A%D(v2L$hW)tsTjiWwV*B~7RrYFY$#sa|q0!;RFXz53R1^}= zayoY~wgQ#fv#WAWuRfPwcH$)2xyPU&a|m`tQNIh@Ye&924#_7iz~L($F?;)@Typ35xb&Vt*?4{;r(= z){im-G4v)SaeOPK>Ouq$DFqj!$F?_@pVQ=qJ=_Ueb;EUO75N6k!&^&dJ>P6(+;W(V z%_Ev56q!jW%I#I~D6e~4s#B-z_pApO>JD(GnhRL!{4jrPWKWOlr6OE-BDz!{Ie9*+ zv%M(Rp#%P@sM;&lZ_V=c;d+_>p>2}*N_|N<`BGcW09U-u@~V&*5zyE-9=c5w{#GAV zvsloXw0y0^%~^FlrBcDbL|W1SUv&!|Fv>!Ix6;ZSN&FWNm)tx-k`hr-Qn8MfbE~3k zJ`9Jpi{vh~z zugf^rO_MX8gbe8sT`W+KaG2)?2#0^MKOYDnd5mw1%N z#^g#pyxdyOti?tXycWxp@7EZdq%9p~)j3==^V|J$IO{>`H`Ktn42|eUsVGOhOK6SCX-A`#?hW;@1P%#~su4U{nw&8tBu9V_3$%S<^wj0o*BJM@ z7ZtsI_wcltEs6fVv2ZU8LLu_u&|)#Ou`8}CylQ`Sb8@^Z;pVOKL9G8kWr}Pyg*L2j zuyC3yF*0O)&&ymy)@u8!iO+P|y5l(!R@fP}Y5!y2+6)cupwuaAh-Zh2LutAyEA}YV zMTXj>n3$H~GQ53q6T_5^58aIQn84{~ZvFf?TJC%cL^YoAv^xH#ZvaN98Sd@PhtSt+ zGO#}~#dD-X4W_>{Y~ZQK;D7P8JBJdh`~9_y zbXxWHiRbBQb#>)ReA@6DN~)UJC!)I1b9W-faMC^~lraPXbxchmQa`|UpJD1}eUaFH z=wxey^LeVWb)2uU0a^_uC9=`cdI96Vetpt0?a^J|`G00QzyfcFxrd?d2(q8D&g?0kPT~-_*gDr;^dP#pArPmR9=+iBvK& zM(y|-Y5XxLS+^zm`1xf$5wFa;Bkv@r|ChcgMR%HLo8&qt&0 zuQDLQJ=j-*Hqc(dk2m?$R}A4$ml*jtAz;ezRnR~r)@x_EAN}Wp99=t26|e2x=g`cO zm{+t)#_l_J|M-mr9A|f4x$FEpzvUvTI0pxI(tBatfM&e@ytHZ{UK7dbGLbUrs|2M!mlbCCQEMIaoi!O3!xj(2d@H9FDH8 z)AL36qg6(~xZKw-d>qcghe_&9Z>^oXa#_?Uj&4o1hOWi?bmVuTW;W@W8x}1|QDA3T zH6Hpb;~UUD@`QZg>H`zjxr3fPN^T_awI3dkz+cp!lbziLwVyG^j-Qy@?#H$@F)1ef zLQ46|)YLRnJuwCvx8D5tU>Qh;padwkYB41d995&GNX-Srrgx?$CY(dgb8cIrM|#E^ zsQ!|n9LO5#0Azd?x*YHoh#D_q$AlYJ)ym2$Miea$$#-40S(xxAO@UTJ`8qm!deOY^ zFK0lB5nPE}uUgE2#qL2Rz#-Dw7CJUouc#XFO7L~5k(_J?erZjs&IoAf1vTaAj@P%X zT%Cq)3p~nTA-c|<9iETUv7T?Y>MViuDTj$NF}TX6N1l{F;W;i0s~jszBtIA%8xwyr z(`-UX=i+c(eenL$Clisy-EpVRFD|nsNbEXP^8Z11@f__<#u{d&B-8{@NvpWJJQOA~ zGgF?P3c|kFtHi0S=iwnME9<(*Q8^zhf$dlW-sqW-$4u$Q-%t}!8@vSJiS=&X}{?VM_-t6@F8F>S4Zybp=Lzdj!r7+RoG))98J z7C0Px^{EeGD|MFA4^&D_L(HctgVr4Z*ke-B%*>+=XmiMUPkwuW@47N8E350+GSYG` zTdQhgjo1b`a}hien(@(hUoj&0H_qESIrTu*(n8``1HSQCFOcYowP~+hKTiz1yeD`_ zTS7S!!7mq?+0t?*yNHOZ055Y^vM2fUxknx06D}0TgI9`&4nu{GZBUePZae_ck69F+ z{z>1oJ<_E7GpzDAoc+gtfm9YXRnqyfBOgj^#P)JOAB8D!oq80+NaWAz@@=<}#~AP zbu!huAuFrImb75v+K%!?{iT$=2-!JbRq5wLenkaQJ*al~TL^iV!4%O_0mh;Z8Crf9{zI3B3PT9b(9*AIW@r zqvwV5@(5B{x&r038zD{lCnGHfvb91ovjMPtKi%S|oCbqP(y=vGeu zecnpLvS)YUxS&?|NYLK4f_Okjaha2v4i_33BX}zDzN$t>W=CgDK`6yg*6aA}*@kLY zN6ho01!J-x1q(!X&!pGUp$AvdlkOD|GQ}+R5oP7&3qy&{e&5GM(XNJ&O;u{qx_b!D zcg|9mV+>z6)5>n=_z~ZK|2smre&oIKmK2&KRa3v6AvzHsr4V8HRg7cy{nF}ETc$c& zq-#mQQFW4hseN%tMg}hw&~`%Yt+>Pf#_D7#A8hcpe$4_k*!H*c%`uSLCjNA;O;=af z5>6L*Q!oPa`Ww)+^;zOrl|*-UHweq($)dH1DhVi?|4YZhBJdm=oOQRe%F89sp3T!) z7eY>beX(pqZ2S1;bjgz@a`2U2kI-wQwLUs9Mk!3_xpbMGvi?%*om^dgJu7%hyu6i) zfM8yU!*T>fHX!`f$ewFRv3^>q;P_=_H$ zxudF@Yi|dI5RoBlLs{hok{|^UGu~$V2Sc~>l_7XwXJ=R9-aZ4W-C`jI9}fpiFuSCH zJsWF@7cm7+YrdvHCdE~6MkAvVo9UNusPQ)ip2fm>T&?H^|3NWQ0u(L=<4fxA}+g9Yd`7A?vs*Y z0tfTNA$QhcyqcG?N7STIk6;*9APUFhPu^BmRaFh}q!idXICRbN!n0tJ33^kEZURhZ&;E8rtDVd22 z-mnrfE|Hc%cB??_B7v1HlE-Vp*9|y{!d*KIYi*wFu8t-@IDOf2Y@4NuRACHaJM!H- z?MotY7>@Bu`VrhSm!m_PDk5S*cQfDe1HV3GnMRgWq;a3MuTBx&-`3HDdiI?7y=Dt~ zkcrc0KaCy+0Qa?F-AerZUG!Wf6Nl???_gd^J7^@mM|+i~8@s_Aa)lHklfpAJT~qMg zUYhV!9Tpfa$)Kwt=9-G-;vpd+2ZNIh^pWWQO#Xpxg_K*~_m@1ADbx5&bi?9Hui3yI z!|@**5tyR1C+a|pi}%No$>cNaw{A!vJrM7ng0G#$t#3rW-1Py(D-uDwgEK#)u1;9F z#CeA#URR{^5Sc@=D-^%{L zKuL)ElyoJ@2tuNv^Dc2bHd;_0!UD-G2y3-<@eJxNNp^~jxF)Nx@n3n zwzqK>Z4l;1c7J#Q!y$TfHS?ziN*; z6*%&G68t?OI+k2K+hTPP*;!)S-;%Ul`=zrHNv&f|yzST)kL>Olaha9)`;Pzan%tqx z3pS~I>^2J@vbil><|w%IoR9O&r*^}d3hs~)3VkJxRadX?<>uyr9p}-uE4q7-Qk0u3 zAX#3{@@%6jAM~>{R1@kh*QRqWBe+>LDScb@m42?X^UIZ0HkW%KaV#foO3s z)+Pcvh2w9!^l=kIu84`Jw(v>Wr zZwEV?tUyF=IgIFo{G|!)NqH9d?h=L-QA~xu7MWL8pfZXMYiX}3I|K{@<;lg~w7!k< zjNX|-iqwdg)x^*udBMPN$G`GGlEYx{jXU40!t`JyP zIv-cKEBE;KALMY2>>L#Y9FeU+P|ARbxtZOVtDpjc3CJm}SptZG+6pTW zMV*xU(H25#x)^{j_rq}|IR?nRlo@+%%CY?ZR=FtNPY1E!h`K=!7~dSHQ2n#n8woFn zaP?AF0!=3mTG|`z7Xtv>%C*Gv3tROkBBv`@ue}c&Gr>^>;Hyz8(euf3P z;&I*i{!v{O@;})&oCW~h_$dvIZV8`B=l5xs9z~ERqjkJ`bBch1y+6);{lLKx%cfV) z1#sh0z^dunszSv39-@O@5S2FuI4HfadZZ2Mp91iH<8PH&0Ffy04iv zP(P!#ma%LgIbnB^DFal?ayzLaTuwp(M9<~QcpSH}8K7dKW52K<2pubdEu$p4*sF6q zLbtkw@>Ex2^Wgz)ulpxg6BB?7MuP7Agb5UES*TyXs4{74@^xFu{Z+Fz6SI_t5Q}9k zI&&rE)cg6;FJMV3MmIUG%*=GH*sunrer)o$Rc0 z!G(eI;otbU`4w@*>H@Tp30NE2Hz_P2@xcTX6%q!Xh?=djg#9Z*PiC+&=+(WDi$IfD ztXUd9cAnugTMW=Sn>}8gM_dW4_9i8O+RC9CA`>MrmUi!Pyhf_jG7AGVZ}rR-9>`7a8|Jp9=y_OC%!Nzt{>(Y9!PFb`z$1ghkY3VZbU$-P(1 z!y%9Ee0cP-B-KC@Ub~^?Vx#S(ju>^;?b*;G#buUx@$w!g#pty*%H@!(Z9iVT1E{X`)1| zzQOAzU9>8!&aXgw#A34aS&7Y5vDx8sk*Cg2A9HoB%29n5tkgS%uOsp{&6h2-HDXjNr-L?l<&)(Yg7<=6kbq2$wY zV~}`iLMQYhyI&WZFv4JGF0HahmzX#T8yoxaQ)KI<(-yJl_ZoA;1VP(WC<y9>v|%<5l+MaF@T=+x5u+Fg^1cDETq$0Ty)}azAG|| zaq~DI^l5sGNl^PG0YVcJQziHn1A}SmQBtyp2dEOT1beAebG&u#>2+y+&e}OpB|g1W zCwdr<*i(R?o}Z3XXh$%<-1`T+$u7;6*tKWiA04gqKq~Q2QCc6bY$2-1=;$E#aj7CI zs<9z-DU4EXr6`cQTw-HInVFgC?;o(RxGDdt&Ly<|3X}GHF$(bCkcs5hRMqMjy#0f! zs?6JNMz{kfC**Cge30ej{k+lOm>B2?_aMF}{mn-ptIK|F@ytD-uFm7DhwDkb1$p~G zyo~Uw2lhY~cNhm6X>68dZhk(}tRz;-*Corck{eI{r=`FNdBD6Q?1+Mdre&2kYkq0T zfArIG?T2v{Rp%o@k9RXa{_dH|W8c5uF8Mfh(o`LfwtJe>+%W$8_rYX6ud`yy7I%px z9vrYD-hfI+d$;SW-ir%f0b7lTz-*=xdwXk*#-q?JFHbK`nDu(Z?DkV8CZ>nPQ70w~ z_wL?Bw}V2?oCdDYF}Y2LJIDjR+RoICJjrFq&DB0}+AyKq*d+HFAs>cri52LB^7x-V zSy|9fjl1fg$MWy-T|~E&~0? z!h+oQ<;zvE=vzWHDm!OcMpFc2;!zU#MPeWY`p!&gX<-TYEfzv4qIDM825*D+<*Cq@ z+_-HvOkN{gdAGAY`J^3tylpBotQns_j~^aZRu+Jm^x7;Ub%))|pEva%~+X-(FV*5uIa?BZLWtI(C*4!+RIF-7sJh3gM~lsQs2P-hx1Dw_mT z=0<3F$A<_!xO;MfqNyQwPWI9@|fZycm@B7{6VeLvEeP z-6u@6e2Z#o%C4dT!rfe<+gX;FpXY5+wM#K3YUpiEn3SP45?8(>cr7_4P}SSp{!jmq z4L>_$Z+!f*O{DJE+Xpbz zSfHnUTc!m3Ll$jF`5US0_kaKXWUy@(qw}ljM&j*Eb>5RXKE|tn-q{<$xR)SwIU!3L zm(^c$b9pC*(-7vXMQX`_xw8)p7BjV@6yy5BPF&vJ-m7KhA!-&D*Z?V4<|R(&-F<=~ z8(S~l7(gabc=;i1kP9z@PCxDTnHL|)Y#xh|r9F9~<2Cv#w0?Vvw~|8S;Qqx0_2ML- zVOknqb$!a#=IM@c2dB{Ar9NT)aycFfcjL{-`m+lgRp_Jc3 zLU`4zXx>R4V!is}XXog+isG!du=2R}YHYUVI<;7MiuUC$y4%%hxa$F zFGFF5PqQrd`u?_gile9)l}_ktWcNV}nx}4~pH?_ETB(XILlBZ@vcM-RwW-D)ceaj) z25OnQ&Z7nvkPD%CA*=&eTi4jwJFDwVblGEC9ln=Xu8@mBPab$Cy!mzG8(X2 zqZSr^n04eri;@pECX=dWy(0#)-mT~t)rZH#Fv-c$fLfhh&_f!brUXbs&&ly@dU<)M zOh`*>6?xS&6qQl&}JheSA+b%jYN`uE>jvyCTkS7Yaq& z_Zp9xc24M$>7VhFpy%Z0FMP|s3T;fGTV7tin2#9XS}fC?wrhDaRsM~EX_dLC;fS;+ zx(F2IO(-6G&*&}ojZh+X(8MR#67IU2sjg9LjiHWD<=$Q^ke%^8b|T#0zjA4r=B9~O zMuzlorLC263MjGwXa&@ai$!olrROq5N0D%m&j)#pM`9A)BzVldysn4#WSC)pR^zIy ze0Ds8ZyEioa~I~{iJNMp`-=eg_@q?kp1KY<+Gllrk0?-WZufZgO2Og{7uwbo)1ZMs z;+U)gXzg577qGi1dAEsA5roT%wF)!}oXG=TIxFJ;`t=K#1|#ngFQlZ<1MH1F7MBFW zUP(#09YkWSkCurVEG(Hk5fTall?l*`-4C@CQfq55(SY7SKqqnc#nrAS{ofXi26y}f z0mwYGu`Z^hYUY)uCIu?&`un^(;o>(*iQtHc2pb<`TZ+C|Zo&D@&5*MFI3|zHjg788 z(f04nekTt7>Q39!kr@QvtY_;p<-8^*v2d_=veVLhfpY?WWKP+LO1O+9Cd7w6vkGhz zc&-1=Wko-tmTU7;-knG5R@YTu|AFrh_P6ogPs@vSPM^hZOxFq@LrCvoU^tv_Rh1ep zni0@-JrNPP|Jet2@!Zfb4Ua;sv$7#D5DT#V$kU>$Xac!!pk9e1(p6$y7m5$w-CH*| z;VjFAt>w0`-$mvpqbZu@PfEf@fo~AXSRrtUHf&JK{LIPR{H~r$uEFX z48o_{`OeP%0!O%mJXz1cNF5+&9IL}><_!M;g#cE+pt5C!B>XI=^UYYHHYCBOhm$!Y zt2RDA-|H!3rSWmHc)o&Om~S)83rnNXkYNkX^gS7d^|`4D4HQtfb8w&+Vh*}kJO!S{ zPyw3-Air*C3{c*V%fF=FD9!{IrEscbDP@1r;rv!(k@1y%0+=2=JZs-4lED(FMVw;2&W}|-Xv!Tm z`+fYV<^Ue84Nw1T6fFOCqJlMPv15M{X3TVYih-P{=}|)*Q1Ev3^`YbO@RXtT3wcYL z^tL!t4{&R6uXZ$m$2fCdfiy{;nhi%+Z;4vqApav zADJ|YoHm)SL0s%0Gs7&Sf2pgAwlf&sYB3yl!7*mw3`=MahiInGh?%+wmHoS6mS;hQ{MYGQTO2H3L;sDAw8a{oc307{3Y<)X&W^keh>bAzyHRcZKK5o zs(CLrzP!4^7|`N`QDa8-8y`M=h_phz`fqweH_BFSzM7Scjqc1-b+T+RE_XD&qe40F zXk!C0T|LdiHz1xB*AR01Jo}Gi=i>g^ICoIb2_vlV$qgyZ$R13fVnGdGUDi-kY( zhSxAzR(14Z(fv#QEwTRp`sYF@paB=W1^Y)?ZoLT)v#mVkRS7% z7=eHy2<+zG5!QZuxsPp{=@o_E}KpSF`X^C1+B4cV^q zHH`>IAGv3Ms*mfX@_4r2j&IyX3{|Q25Org*ddCKS>Y=6R7z)eFWoIp6*LFo6BCo9J z(Z=?8lxNSda8VR;dn3n=Y|iMa7#*>KM{l2p(_*GxbiP~(qk64q|K!>E5f=pcE2H5! zxS+5PA~rDV#yhb7rV-(CwWa>SW$i`O@;RATSE+1!=^Bh`f`8d3_V5syBp3y*g}`kW z8?U>szUoh1H7rI!Z-!rqs0&v0ar?fk(HcaqJ`0x!hdgMp$f8?E?^ zlki7ODQQ_+a@e(ZG&`ixgi1Y9KY88|bzj>1KE^As+eEt%zXH^yAH_1tctGLNflyRO zP;N-j+GNU1N(#2Scfk~w(Uj2wguEkM4mC9e@@7ac^<1O!YesTA#e2iMP)waBFaI|d zV920?XRYE}D}pQh=QS*-ZSrfA|I=_=9sC#DEByB=Q2q%%kA*jX;MC;{*05|^Stn9}bzXUk`iyT&AWMG@TK)fq$A{E{v;3Lj_sztSXeTSE>ChbL@7lmAn$f ztUusr)2zFB4Fw2sWBQ6r87J>KIWD~Lm6cLS(S*^>F^9FzZ||++87<*eWiE@D&-4B; zvpuIjAAEu)ke#KhtqovT&nP^~`p;v7f^-yaxf8jxR{QH-<>nhZWp#%!4oK}V7gu6p zYS+E-E>lJG20ZyipH)U`dEIvjk;Zm}cv&(jFI~S@q}m+|(7WS57Bpo?bIYZ|M%fF; zGRg{i$>$j|*dOjLpB(j>7mQn5q#CGCS@uLg5DM-2awfg;{-+I7d&*-E#~L{A?0)-6 zGy6_@pvQiHA3arUHPxmrc$%RP^$)G3Yj`O(X-0hKo2|@QD9)0N=DQfIl=jh8>6Ds; z@e=6ls*}eBM5rb?Hm9$v=b7`lfJixHK(HrTSzW!ORJBW^K#iV({=~E2T5Nhov(b(C z@bEBSt193UtE_7xK}J>eP_qNsiYk-Yf61g&XxKC36ZnbV^Nm$akSv$ z?X-hAiQux~Z-9}0plg1l6@9yVrI08tE?jE6tZ7xq!t(TR#^-~&=8#E^@120<)|9dw zcGl&=?ypPv@)D+tyb!3tuFeX=o4>~#=p**rYPonb;lng@V%yL+k7I!!P?ZX=ppWt(=(%jyx)cWZ;#Ua4z_3g zE+;3E;&z`a?d{FQ8G$Dt=|(&R(L6#<-4F0JeYqdf_kv z$9f!xl9QsrZ1`?K{Ur-g1LnjT#nBZT{W(E<*C@&7;=+cmvops(xcgn2DGD-r1im;l z_Jr8<)n4X2W{QklPjHSuKXw=^Xf0Bu>OFJ6yD0iSrZoI4K8n|9<(p=W%H1rF7hF<^ zB;T@Z^(bSU?TN5T!P%E^DpV6!DC6c|j!i|dg&vu1(=(UwI;;M=*UEHvZic?SGD+Qa z$J!}KQ(t9biP1!PdID#`Wx~k7qPdm_psN7g%#4nhR`V-_V zk0js%m8F!8A~&d8702|(z5CNM(?Q{(MwcsnPH&N)M?V2X7XcyR3@IahqwTG*b@^WA z{mf9;ee&)Hi`8W6(<{^8tIUSIGt_zwKlfTDpU#|ekuou zLjC&C@KxQx(^6Eb6~m+)x!QA8vMYYJm^+?E!M5V))&jm_TgWzyDwA6+OZ2 zNdm5FeaZ6R*6s=tV!jov;4-Yto1Fzd3#qq4=6?perN;lz?0;C}|J&`hVczjF!;g*h+15F-OuNraMd7a<4i^o`#A zw@Pm-(dE&0UA66r>LXGT?(JEG1L%}lZF+DhB)ZvX=AjW;A1sKV8ldyO<^q5wg=-V~ z08sr^)+&bMZ(Ca%F!eg0z2&{J)Lb5r!Q*?*kJ4nT3en1KM}ORWhYryG{29G>&Xi0) zU%Guj$>$%Dt$<;0nbim8&TV>zS9?s?nwnQ+dfu-)Ewrs<6ji&E-6;Of%S#-qlyHHV zwoMe}(?l|^BWrZ5SHF8ySLwy6Q6Aeo9-g6?4Y1uv8WXdvukCU9gK9P>1EQ$mbVbp8 zHC8&sc!iW^^@oTWR^#pZ1f*0T8aclioAGvSf+*^m#u!zTQZ-8j==lhXkOtF0rJEE` zNu$_RE^2XmxJvR^;s6^ueJ#2mqSx*hA^B291_L;cZm7MapZxo}c)osTkM(9~1iNZ_m&*3CuK~?Gf6=6`sNqmRa9SX|GO2DC ziOV_l$yy0`vV1&tQ=B$tB9t1=caLNAva(iMd*_pT6$)Wjucutr`>!S9-*o7Cxgm2U zFXw6{qylz3qXsSv#2r_Cq>5fjt+|}O$Wln~1ou#IpbKM) z;#q75op+FI${F#dplxXZqWsw}Q10d|G`Atn}p#gwZm#BB*dWu47 zaZZpbqHsCw9A&(1KJsRua}IKSl3j59D{p+T2+Eo4eeuDzVf{7m&&2UUB+yjMJ(Z+2 z$fYm|gtktinV9zBb*QC7A6Vx>T$yVgcjz59t;$gIs~vLav`Bz?^10|f8T-o2b$vT( zyCHB@&+{A0i1~X>%`RO+u^>xg%s9S%a)P8hl1@=;>NH` zW^)rq>Pz07xsf4n0PB}ECd(RpmPD}W{k=k3dwO|Vzr>w8((V|{HYcnF6|7B@M7dM- zC2e8z?r6aIW4%Rtw&mr79Q5s=bARv|63w=phkF(}kVS8R$u{pWLLtA}pTkEv(M#BQTrHHz_ z{%c51Zh`6SUzt!EM_y1YH=Aa$_=(>-qKb1|AIk3KerPbmVt8CT-$KEC@ky&)dVg7c zpPc*RIVK@>lNWU69rgk2aGlmNM7s^ExLknb?c$j&|iv9-43A5#zPEgeOw@C4)xu(d}3Lm4(86qbi7#;>4)cvB}e3 zcu0`(f4eyd)a~YWXyt$Oh?K{=)8Oo2qpSa5#y{}u`7`MvubywxUw^6)EB4iyN^mG!y~wO%{%rF#7i6CP=5YEg7d2UF%+!KO z2y%~vK$is9)5GgxvrBh9uT2}c%bh!Ss_W|Zx3TC-T_^9~Ob=cg)p6BL#T4#!Fdvg9n57c$Wnat9-?5!(>et#ejs0?r7$3D*(jHt?Fm!a5v0 zytxp|s01BGx8b$OanOR#pnckz5LJJDN4!?gW$d!1y4tQMOwev;$8ehx3a5bM(+Yhq zB4b-hDS|a`Rn7kW-RO}z>2iJ5fvdxdd`o-QLL7R%W_*R3Y_waQ(B3`RnTy&*Ei5gy zLg?HQIg&)CDt8Ze=SRt|BhnX)u1=>mLmQUOZb$}m?jW#*U2=dxTkPKaIe9>HHMihM zl{)9P+s-kykn2t&AY8I5lE@p1nh`sXh;colDKIj!@ z3zr2Ek?)WYu4k6>>T^bs;A_|!XQeHox!PkpTlUx%jhwWBhhk1hkK z*(A#^ly93p#qAv6G*~0?n}E)t{caCk4vW!WfFY*c#++BvYir|iqOu>t|2(6as#*-G z(ciBkyO!(T_*EVz>E)u~P1z#&234^9<2Rw`Lm)>}3Np%nMjE$2d`R7TQZ;KWfY&{V zfrYtu#J$pa6*O$5QCBC^b3b$@pcPR_m>Bas>DLDS;dF`{ znY`aO3p5-fy7(tQbU=$O#+nE!{(TWv@5(wAS zJT1~~Inu53R&L~7?SImkI+r-L-QSs7Uw?rGe+K>sLI!5zmz#ap#kM1_;f;vPwHCS$ zTTqF_gD%2w^MZgtkBUa4|x z)B0R2fv#)@I3%chBxjMef79uY%+0S2=;#!a4}M!cvj5MM>GED#`LC)UmgBW9bbfVJ zR%!c#i@DjuN93IiRfv}gPJD2e?c$1}KZRzS;WST+G^Kzy!vs9IbjbuHOziCKrQ0O9 z1_y^sM#Xi3-@v_hi$!_;O55kkEqh4A$sTb*oA0i0>fLLaXOPvX_Y{^5UqO4XuHR9$ zIbOC0hYV0UY*UIxpQGwS(0qIdsKxfF_Wo>qQm(QwUhn>;Q7|@ggjH<1srF+$KMe+> zrIeHwck82s=~fZsjouGSgAVPPnwXdZuYdD2#(rF#!5y`{U&Ez5(fK;f{Rd9kDeE3b z#ZJp_!Cd<_5xkgDBzr{urYD-a5*CxM z@lw?o!PH^$PR%R|4D{60ic(&!Fg2t6pxn!J8{YAQgf$)?0f%lGbp?|=iusB5UQ4YV zP))#Au9+|48Uy5BV1z_-8Y6ts(EwDu1n_`bz8-Lx{VGU##;RWI+!4+Q7vSRq68MbK zE9Td)5|g(3fKu;dzC(i>90?dk61WH#6hUam|Dlj(`!2ERE(&{!SSdm*|+hFz>7 z4!uBm$;>E!^qE1{QKQ%dXEKn0A0F6Q6!xD(%KvM?L(+%ctNQ&}K+hwcCqm9NM);q& zqCH%gDu!NrYo7zcL^Bp;s02#GG)w&#emn%;zSu@rqS4V&(n_Jh4OJ(Io}(qGs7zLv z6KX@ZQ>E%$tPwT)p%t2&8|3z*s=a5Ql%keX`Y#g4*o|}Y{}}D=E%$|wOk?2Ml?o9( ze59}f^Fr&@ccM3utq3yNSWQvU*B@XSk;s&)J3{tI44Tjp&Ck1fc#IVEn3^E_j25FG zi7-9mw(8dbFUNWd=#>*bj}v~De*{XP1sUDDsY;tl?)9G&zFVSD{x};^yPd}*B)`fb z2~DA#X8beiYNXm#kZfZk`Vq6fjI7GB%9*J!Dq+_Xt}Wt+CuUs_Zw}Wt2@@JX8Pu$@ zbSztpt@NTw5`9`?boe}cX{nf3oJLSx&?r6q6xqXRX<=cokgZ}#hKRoS;(5e(Rr;4t zQR3V|0EC>$+o2G-Y%lH*f-Sg;nx2xq98Q6ek;Ql=yIyi~{!F!RhRar0XE(hgLT{?Q zZRn3F-b-KK?4&A`m9nh&9aioQ8FXRRIJOnaxZsqyx$ zywYaNILDw1}6njd(cmm>1l$+v$Tz=$qLjrhHJSKB50JLCzx@3`J695*2 ztY#Df`-ZnR=90U2XLOYBjJbhAdaR%6>VOH3%E`H#CCQWP8R#t6(Vd z15&&=g&kfw3@RzYW2OBAd98O0hASau75Ml#Y4z!eKpSM{V6m(%+JYBrSpgS3FPPI3 zwfHBvExexwkIEC{1G|)EF>b#c(>G4gN^j=#U&0N z_S2>TIsjW-(CR34S$qx7f%hLjhNcwJ!6YOAoFm0$7FJ|QgDyAF`7x*Zp$TQz^<+eF=z;46T=yu#It{IZgo>$;p3wQkAu|o^t+P! z`rXf;S6}znbxJsWA6WJ7nlbL+uS>D73TTW4In}Y>Qw_jY z2d-_f!4yWUkMOU;7-V;^I*9F^9b4KaalIBZ0^Of8z&!7K2{;4^|x!PxJ(C95)VbSa(>Dsss^Yp|a#AN`i=*a&&k1Rx#47e0SGwV6Hokvj`hhN6aO3FnO!&if*!`vO3H09M6#aR_2OBfOLcgaNql{NNA@DP1ob;w+{4A%sNJs8qnh z)>dCDOB4(7NNT|{^CaYZzZ5-(7MsXtmjw4wc7#R+Cg9OST0i@(9Q`Y z6UC-9q-^VQ7y}VaL_%Sy9X-gHoXab@3Q#7C`@;HrZR(Do;e|O;Q7|$gp+_Be#W;Sj zOnbcjyj0D;?^lbr3wu)3k67;W5XN&ViG#Ohy~$xI5k*_G)oR?_3fD%YSKi)etihJ84Kt5iYAx3+15#{#uuvQT9N9EVW21+L zz_GJaT+Ha4oNo+{Gw>Q01Be)a$8;s6sPS*#XqbPs0GRwZZeueS9zgi)?jM?VoAZ*3 zthM&$b`}i@Ule`-Q4w#vvhvhZCM}&W1hf*ZvzJ)a={`OlG4u{YyqXy>}&O=Z3(-h1C&WN=$kYCy3RJ)!gRg1XcxG#Y;v)Y{<$AcJnF-&WV`*5cHkLwU#p&h z9;dCaY4(3(0k(!iN~O+As;d`BXuC?v!$4>qyD1V9v#_)+XM$h_L)-s%Vjq{`pBk4Dkjy!(mRb~ zr=^c}T35M%5J}$n>q)?N8UN>tXK>jO+WXj&kd42037&DLFRVSey8tgcFZ-s^(-W^7 zU1dsxKc+=cp!3GBf)o*5aswAJ^Ad+kc=~1nkJHdi1IzAUqr8PVW+fh(W~#)(el6rX zy(KjHP%7!SLwmVnOQh&@&1&N;eMC29a!pbbXgLJs0)U2ltLs@T8Z8>Ao7>)%knr_oW%XWP z_iI!gmTH*q#<|y>k7Oi#V;Vsr*-WJ7=J~&t(f&opBnpwf^{$NA<^yW{QmF2O1tfYg7$39QYx89<$^>b)YnZGPp z&O`&R{8{?y@{+Ay56mrPafR$NG{oZW)YIYg;}>GD7;bjC*-izuhFDDEVVCp~jlZSC zg0dC>HYDESo@BZuE7<|%y2l9hI@)@uLR9vzZK^1osn$m%%V+3TiKxd+ypTIoaw%7L z3?59U;eJ3bT-vGXnK*0StUfz4CV%fH7Bj=kID8n{=vg&WAs8iar8<3qULqta2v-Ma znGLHyle@d{e~-w9w=2)Va-V^% zti6&=Q2~*GFna?WN?hn875)V}4WXzCK->b}TnQV#5xzo}wJ5HuEnRW?Y z=N!q{7u^)^7XWXQ^;{BFZrV?qY>r&q`O|iT5nA_U>n1QKmOOKT*OnjarS(V+=%oH8 zvP_<*$~Yc#=o$)915XkY)%SgnYU0CqDmtJ8Ar|@8AVO_RU>UYbNmtKm=`VTY&JOjW{ zF)Wg2?45lGp1-1>ocrr`;bRp&z5e!p>`oSlWJol}Y1ie&gS^r1 z*~{~OZJ(oj25|;-L3Q=|hW-o9V0wBos#Qk|24H72qCuh1i_43h-J`6^%4pNPAI)gy zA;i1(Ho%%ol^wSQicnx)AfDmS-g32&=m?KUJkWEZpA}{3!YixmY9hVX9ylK!hEjUt zTM61^n2*~c`+FawCb&VH$(ogJ);qvOxR?jbh=qk$pHt{!j<-jwZDwa@Luu`4K+OW6 z047ylnMn5cE8ROe9cg^04}4iKRaHrrNtO@Rhe8ske}fq2@ZexmP+pohcy_ar?;k!A6-hl_t#x2APj*P*ZME{=PyF#C{`K&#F(}|-ynj>>ndDiRsBS_d? zO1qKbc?~wqI89A7>5ew#k01SG0<@0DhkE@czZthJ|2r7ps1_rcc>?_Idw&xmIfnRZu|oz zR%&IEjPW!d>ehxbagAj|pC0{tYXIs6P;k3quTwOpJcM&R{i*BWLQF#@pEoymU-%Wg z+;r)CS}Tph+{EsptGl}cSf7xP(D(RgwY063EqJxtKa2LTdK^wpPR~!{jN%M_6DLAi zd{!>W2t1-lD|w6?L>oTH+t|-~3Gie3sL(95f;h=hgPrz^$>o-USB2wizM) zeDoyEX?-x#o&j>VX%>c*PO(j@1H?clHeH`KQNty6hYi_=|RuocG z;8_N&C5Ave_G@8Q(F7!t+Ll5b6L?M8}xRt8$! z|4fvXl^I`5B(R1quFbt48(==yFiIBj8q?CzsZ7ZZ)@k7ac`oDuzTdi#7`XOO$KQIo z`U)eh^Z$fgTwDNey*D7Pb=gpeYWK}1R*xM|^PELgox6HO#U;sDzY=3lP-nviH3HC> zSyWhkP2}Ga4#2oq)rM=k)1(BpdhA(vP<1r5$(z4CfY*Ixz9| z6Z;i*J^wbN=AIGa&vRydu~n`_-vS92=jWBK^}L>kt%5Rj>8A4bPEM^xC3EZZn)8Qq zsb;;p^+6|oSi8GNY0)ucU~RqH^+EYa>x<3PGRsZ1y}f-qXL}Y-Jfm9Rrh4Kr{gw+%J>nkmL7Lv^m*uQD<+6IH9XfP)My zeOQb+=*-(2IYH7@|189=7~0eUuX_atyujsnrGw_~ycMqB;QsU}t(Y|vqWNwvf@6??GM*WCG2cfHc{rcf_zFQa@ z=AB_)o}EJ7x>wxTfa#KXjIHp~F z9=^LAVIjncm0fy`L>BI{) z&sw}!+9bAUlM}A_uFQ1Bv5!4Q0VD07G#Ci@SunjXoIi!JVBR5UZQW}rYD~KP;-_-R zTFmBRTGRiLrYza4r*}18TiO=;`3-i_+TaW+Tp4cHW!4+#arD|lR$IEJxZ6Q>{AQVs z&XNN34x>3Y*p(9B4euEP)8~EG>WZj$>Y9Zc@rQicW`)}wucZqqNpA2-0tOXYHZ=1; zMq7J<8sdFBK2WD@-`{@wL*e7&Gt9#y>_SgLNJyyNQNsp`cRVS#p$ZNO>8YB5qQoz@ zW}qAs(6Qb)}ec9m_z8VuB` zV>D{PjONh%TkE`8_!hPwXV(tT3)56x4&>%<+@)t( zXO_I$LFa^>s7G@Wnu98(VppxiNJL|b)VFV;w7i&rtNtOp7eT{T)l#|{j{^f^>3a?C z$Bg3a_$?=w9%~sHPovp16*M%+X+)gv8GtG*;AX-)mGLru8%pUdtqRxk0>N()5iNOY zHl*xYtvmBAcVZK)H~zbF8dh@GA+g3vfAsEhNrrKPL8*8~(? z0!0jd0O#13)BT-4sv$4^Fk_A(l#l!Yj=>5<Js`Dm-N9{Ax)Bq^K(c7C_TN>mq0FH z?fUD6{Rs0WZQ(y}tQ)vc;EM5AO+OH7&fjEDeas&zP1etjmJ6R-rP zL0>$-iK#iL7P{!^0(FeE?u zm+Kc2!g}ML79PkE}Ct?!NzXt%0*6kgK}X;>p!05Yt;njsW!M0WAr*_4CzGY1NHkFA2>-M9 z}xqKn8t9U?RzzS#;R^#+WE$(PPqM~y3CDF{y zS4$hQ?%C}a+yx~={y5cxWN8%*jm{r!@~&Hl<9U3npDS{LfSj-!XgD@9hjgG5N(6zO zw+LyZ0f(@H0@{6CT;bU7@7Kw*k4N}=rZDgE%?)+XwiuQCY?M|1G+vk#N+BA4etlsM z>e#qmqfqv!hGTArf6N=FXM6L3bPJoCxJ}~-&z9^I@4saFN^G3UGSW&aFM+D89Hvmc zuausi4sJ8erV6L!jTYUGeF4sb;QP1$Z76QQHGi~wunw;pw#mK(F8ALZp@t~fWulP% z7f_Eg_Taz?RDkWzNw8hu^l=LY>>PWt93yr}4K|&De?vU6C7y zyIXST7?|_m`#bln`_El>omuOB7cQ7@JhAtFKKrvjo5-cZ)BkoGNIq>?{hcVn{s7KA zUD31_SHJSX!j6lJduxZM?<8&$OuOL~L}bN~ zW@LS*7H2p+PljN%?(?=$nT(>G&f&WNLl(ExZ9rRzO3d-M?PNWWsT?lyKF1~nb&pOe zC+tDs4~XlNh{YF~1_1G7ysH*l>B9`h0``LaC8CClv*r&cmMonkS=A{1^1e67y83 z=H08F777m0eLISyoB^-GqjSpt7i7wg>zIOt;Z&52{T}vm&FMdLY#p?E@0(ZJH4D8) zh8&5&&&Tl;a(D3~xD*unh5K@BLQrxo<#OuqM4`+TGq&%$&0Q2#d~cK&)-A6WY2;Gr zNO2H-aohVyorX9{j>~o(wlEpV zV$J74m-lHFd~xx`Zp7GMq!-iO(Tt@Pc&1?r(#B=9Xy(G-2$rmNbm^V+wOZ&Dcpi-= zMQiljYdaj*`wSlZ|LU`rG8(Xb_tq$*ywu0X&Yjw939k&@?xiF#RlbM4wnlGzj4I2e z=v-h`KGxCUTWmxoRWcSUGhYb1%3%Il)ZH zg2pF2C9^kF?<||s*QDI-H*aE&$;qOpgu-=8`b*Ma^BJlt0N?6cm_PxKU#t-r==ltN z{TUAp%hPTYkFg0k$!zm~(SP^5n2Zc(e6HyU!L=aAlJM|rBAwueu_-eDGogy3f?{I) zgIquCci5cT1lD$UCv^E!04i!$<_ZB*vZMIw)xHODfK%j)XHUDS?ROs1s~-QiOpJ4T zFm)_qD5I^O_fC+5$5r`fh)?ULgg(r9;I!=;?%@2NKMwksQN+`YE7$B!@6>ZLg$1tt zbJMAb)Fjg+NyM8Ea*Fsn-0bg9!S}v<=7kMVsyFeh|A3BivNxKT;eVSfL+echB4!8) zhpw!?9Sw*nSAto@4*M&HxZL-3S5{a0uHCk#P};Zz5?mtc_nWXkgc>MizbjBb*_%uR zp6<9Up4Q_cHW+?%By=#i252V0W<_NPC=JnwVA&D)n8|EDVq{`k+1d(eZbqWbf-QNJ z;g1ZyM*4`k{vE4bt9{P%{PJq2#1j$PrB!N(f^v$wHC?c2##AE2!op%|4mfB;E*}$u zx?Y4a(oYinGZVTd!`#C@FW{`eD{Gedpxs=Z0KtlnpzQu|R9P(c%NKEZ`3L0N0$W^Ds-%a9KwNXi9b~8s zy}S=XLvXMng(xd4?>C<7r~6%5(K9e?&*k$VZ+90xJUkBnvX%fTJn{V`hc2zlMb91E zOt*vFU%%qp140{){<6+_%@zTnM_ZS_bD@d)exVi_o(W&RJWBUpo|DorHl~LOxvc!g zpnWVN5aR z#?_Do(&*?s?rzEqHF45?&$R5CSKT)H)7zLce8j;L0FmO#t=QtwPDvwP%gy=1kb7Kn z{n9}KNaXx8xgogOc{#^J(YHO}oDJQv40mHL0s(P-X0fu~->b3p63$!V55i1yzr%=G zyL)?$7v?-&_GZ4ew6s)oA`r*OgmXQbCMv**u5;VzO?hFX)#M}CCWF&8Fc3MFL55eP(|-{Oq>R*MW05khL<5&IDp zqujQO4;Yn8qh5T($*QuXv~;=0wy}?|?~t&PxcI%2<};yY#p_7>=9`4OZ;XwpF)1O! zKu?WQHXm}^nX2zfFrkLCD;t-CVVHgM^;x-dct~2f`aiS*tOHnozd{po-#gv=R&eH* zZ5xp5-X1T|xO^6qmBj#=uXn#2-bhORGVt)yk;RDZd-$R!k2xC=k08ax#SoXBnaCeM zo<727asrX|LrTKF*1+(P@bD~|AsXVVMW3Aki^%Pbzw+wpBoGo|Y+#=hlA(du`}NW7 z?UJ`=gNsRs8&d@0MpiTy=jCjbE&TK}$F~cQO{*>*oD*VUumRzNhJb(oR6063)9l0| zyDDs(V1Hl#2iW0L&GvVv&A8Om*LHRsK-G?n1U0VoeX_7|pZdL2J}lFN zrbZzDGFqrr;Y?ywH2W)8eNWqEeZzghk|BYnrP_0@w14?ND2El{?m@)@?bw-M{xc^8O^AuvZo z=%6wKKm~xN_uIJ4ux!iQhalhUKWyil)tk2q*Vo=-`EP@hdCZSm!#N>_#;U5S_g<=~ z5P;FVD_{WUq^4Bf(1BF`xpQ@QMZv{|S0;+e2q^hBZ`MLq)t3Imya9*VZj)${&IZq8Ol)iu z&kLsnc0K8lVMg^rO_hbb@>+1S&!8kY<90ExrlzLU18#ihqRVso?!3U8z5)_zVV5;! z|0_a3go?7QH}YLqXa0)`=Qr2h{uqxL89x^mvemimg@IF~NZt7ofM$TQ z(PidQBsG76hJo+bXwj>X)UJ62UMIBOJe|cP5J3Mn@|~xUI*3 zc32>9B1qG{Kkn<*gAxz_iGlZ@0s^cSn?$mtqm04Petotpg1Rkio%Q0VFSOx!Ul@b~ zk4Bh2gFQ|HB9E(+?Y5%oh2AM{6VOerV&oDuTNtcR7V9HL-h8^V;{YUpBD&io_rg|y zzI=|S!*RF}iT}xv|4rQbavzozVQa)T4Hz<2g3~J^3T?3)kv(^X<*ka&1qkK%hKT^4F;1nOM3J3l6&f)!v;;i;!PcRH;k z$r1X`5P`ta?#&g6oTOxSm{s7L_fo8%>7DZbxp{C@bPD(veg1#{q$;ttxmjUZFR)gk5OS3IcB24ovdVV86{uZ< zs|y^!e`y!pflEzdV%;BJ1ps+`Y~bmsUdyj6T+W8d78pV@6)2@GIs zPh1ZhNUy~F;o`H6c+AWk4CMI%!~!bI?jXNn-r4kdm0ydo&)c0SpM`}=#Ow2) zKW}o=Q!F}vErJv)&UT=@I{>$A8E2UvF{%;4?hO(U6dSqGVOp zie*}B`sDyfzDIpFZ95I^CCO^zxe% zx^;u3pre(UjY=0ztQ@`T<^i1Mj>Trt8+07g?G-72=-mtNh>9d2NP||UB*S2esa3Wp zmc!-5fOjcbXdrX*0TP5uD`#$we!Mw|_Dv&0^!^K*&cEXYmVds{iFlvp{`gVqBR1H- zw(>WuqJjrhM}u9Us;jD?I->H35Z^e3DM;lkNrc&gd6$}Muy--v*@B9iYP!jnQm@{1 z^8#)%=dJfCblps9>U?@&35>*b928uq?M7AV*z5?nWd#M*(P$l zwi6NSeF=}o3d!~LD>=4(NeS|!{#ga5NKY@X*z^Rvm)6z~XlQ8qlg=m`uTRXj4hcXM z(8dLli71bSSqnxSl}KgMu8t>W?KX?W<|QWW=E1Rx{>2O8582sSz+!M)Oemn^Q!6ze z6G?4vo7Fxa8Ff5AG#n2j9y=}S+bUZkX-9KU9yU-@*Qi29KZq zAP0vy7Nj5#Lx8PL_0saPoMycnl{{2d+xpopKryTBK)!BE&F|-4Etv&kdwCn|OZM$a zdr0*n_eTJ4TaN#HK?v%)5r_tP_`LQ$dMp!l9$m{B|k+dyU%`*S_;b@Yp zd~@2ssxz&n1#DmR+Qx=M7tB2_A*C1eoC%4HqT}Z${Qx{A&*in8CQd*UObC=yM%CA2 zfb_^_YDshUg2?&m^0t=fM(Apnrhq?3uGL7{+s(V}#{_~j5YQ12?2vnoT5b#`2ZCm@ zw|=+xc`ZE#=hF4{+dDcRR@yLwa-`TS=~aWpj1b(NyXOXwEol6-meZ)*c+F+|*M4`V z7Q4-u>-G#wa(`+Nm=GcolIP~T1BtxpA+oNfx{{P24^n&o@=`DtocAMpIy&?+_9WN7 z{FhG#r+&)#I|u}P{{Q_%DXXYRXMCe=oQV%{crKWH^6S@Y3Sd-33)OefGD_UprD&%# zmN#8()XU7rAGk@|RckQ;t}Fk)Y}}-#m-e#)4URl52keniRbV!Gn3b905)!=RRwWM> zWk3Xo1uxTJ-=Q5y<=LTszQ>8IAGdzOzyQX!ek9XuryxxUumgi>6gYuLjFj4&kz2e) zg~RhD=Ife(laO;d!h(R!^;+QsMLsNiTg;Nn+g*lWE|a*ju~F-nA&u`fu3=9mT8+0y zO!8MSFMq%GSy4U;(9)y$e9Wepdq9E->5f~TJ5^?jox}vHL_pFUh@s0vp-@m^ z&6dF`%t{FITPnitfB1e;*Vfkd!(PC={BFT)0eND1KYf}u>4p0rtWwz0_$k4LNrUNb zjrv2vH-+vOmquUqu&ktNB#AnMa+=(2yhkt?X zPNvo0*xDfkB(v&jLZAH$N6rwXEni= z)G((DazvJ60x^F|&)(0J(%2se)zo`~u!0W^P=82l7CF?E4EWh~=i0j3(Iyj_Pbp{> z9?OVw-=epPC;>rl)HG$m43?4T^vzNRy*f{CwE9!Z9U6)S#yw(YVF8Dd`{yoMGE*?# zljp``WHXfq-3#;e1zXf$O*?`h3DDIjj!S4?`slLRii?A=%S2-h4+?DrLHo0+7dXJh zcM$3LbEYYb79KKxsE)?hN=^pbn9Fpk`)KbJmEw)qx8LMgy-b374b!uUzEnSelcN@P z{r>GSEvW|oMt_=8^n&k?9i5U^-KeN2m)$w?im~kMU!PBHG(fJ#TAn@MLj$&0V>_Sv|&XRuKOdAIpL&+7G+r=rxrL#!G~w+dGYAZWbmypK^q&U z>V?AyyP{E--t~ps>hOaJVNPJzmjnq7y*jr%le}U!T`p425^Vtip@itot+n=%C)kb- z4v&`#Gf&J z)7X4g=wrwj^r}wrE}~uEw2%oHMraRe{TH-ZQH(`gp`c7U{VnL^RXp2|%9I;@oWQ2v z5~5-P40E|_p>=2lFCOko98) zZ=k{Lfl_tRNEn-<3}R<``gd$%Vw*}!%sQJfl;Kt>gt~enewD6x;BspJ(!|^VA;*4j zboPVFTzEpl_2`sAMvWnz^z`fhA@amLI9V;cuMNsw@oF8D%(o5R;e>Uszxjs* z6W7#vDZId0i+=3#sFUK=(zFYqTvN?t6?(5ta64>!0z+Q2zy0P`$+pAe2%U#>H+}Y`dQuq z?x0u9-}si^@93(U5GCa%qr88{{$jJH))VobO!|9~MCcvGkjF*ScS(xKZ)_r{A+|-*||P0S>jnD(6NZ!>D8za63fYaBl+`F zV-YHNV%=&roZfLbrY9OC8e}O##(e&J`3L=JmFI?JAiW?_Y?A9)vb^9OR%8+)JCOSc zSb*%XWb;ovo-(T+uBry~~EnyJ~`#VtO`j}vr8}aXIS9*l8tZy0Yp2fX$C`U7gpOd*%+$CUS z(J8t24o+Q)DQ$P2(od!&*AGG$cS12trka2ZU%LLn=gI?VSe;qMP9j^9yuQAY=PVx` z4AYKWUQQ0Ho}S<7r27@ciwBvunxt$b*gNW+tpy4AbVhpJgT013KxK_&jc}rg@)jjh z4ocRRuBSuBeWe6>&NDhy=1L6`pm$dnX+~c^_VOCf^)C4h*`1?#rKCh6a*ZQ$&{V9` z7!udh)xz~dXEI}O{t9>(-AhOA*U2yTgz=a=ofg|TSJu~ktuW5w^@RDpq$Vn!rw4Er zApa5{!;3U%LE^p1Rm6Sac+D*Gtsa?OqjmeHAYi!sykW^AM`I33BC=QKeyZA*MZ3f+bVzh%fm`9y51YHT1X{l8TeW!cTD`P}mMlsVi0N-}cc0 zzG&zrI%L%EGRz>?N2wl91hTbY!-X0g!D?Uc*yd!6CkY1$nBTKqiEY6v7iPVi**WQK z_Re4tfI;!x+Z%!xEXGjJPOL61X&Mb$HTx)rCwzzAtYCn;rHIAbF6B6}thbYX5O97- zIcU0Nwr}Exe>uxhlx1d`J7xsy>8Rw~;4wdQxL#~(@>!n-I0rm`zvfYQPSHPGB6`0? zBLf*3^M?a^d8pWMxuC&|ZP(_kJlDIGdXSTyo%Dj0WNB};Z()9x%(t&Gg36cKJF5!b z97=0=up}$e3}Nd$ShONt+k_uBN^FT)@={K6GM8Fm-yR{n1qAC4Z}^eM$DLot&Wiiy z&qWkvrw$U`r4JURF1{q=kULU(XI34xWg{6FL;^YHubun$@ZocW)kle>3(SSS{7siD zzD~{+o*Nzb9B>%OOD_D}d+^;V1s&|Qq>Xj|z^C+87E6&T2pBLDY8K!3>Kxg&9|8aa zqHw#|HOJFLdLD5grQ5HM*Gc;rJQQecgJL=LG^DxdmSsWIwXe&PWG<+99^>fh_4#qh zc?L5p4@~;w0Be7o2Uk!j-aL0iISveVj9huebI8_6kr>)#ZK0-6!ARPo?+rm* z)74?&9`|s4vf!zXxe`qBWdI=S#UC|uE^ZD*Bfh#L;q1reH!tm!_&A%+upn`M`k`5I zqk_?0u?$!d&BrR8b55K*d48|0tXRZ+&i#K`kABox1YH9oPUNr0^LK`r4HTGPtltkO zRWMsGuiXusYZnu}jRiDm9yn0H<)}&@F9CgMLNgj)SEmPDEXC1wcgupo{CY<1UGBOH zBIBa>i{%OWzCkdaiV#`FceWas6%V5E`CU?ebf5mt_Evf|!sWhsMJkF^SVND8ef<~^ zz<`H`2buGmG&$K8dj!$YaCbYH5ueD<#BH%=r>Che6{TN^e64W)R*N zKh!hS*D)}k)exDQDxB>9G`IZuC}@EaXmh*oxg;ohy@osEzU!$zn7;Wmq);lby=(u8 zs2eBk%{8g&+bEMt68H{2vSVT4BRX#C7w{A}`8O4syC1>=lP`k{IDCD4;P{r3=H@ow ztMNoSf(b4mWz`GHMKhwj_QO@Xbsaol;86Ja@hLF)nAg|1!CYa!e-zD>^~GP)@F6a1l?48gn4pC1Zww{K>6ef6|p$-6@sN&o`e`E5KR#k^Jk|PdTba zD!U-6N;D>&_KPt|4+l8kif)cZ>vtj~h%3!TtY_Cl`fY}7BCMOaa)M#fjc8ndUr*(HW%7V2ze(l2+! ze(dZF)s2Vezn#Pv_V&Kp=6B0WukJk0szROk3Z#IHEi8tbL}o+77bLF8wOF4Y;#;;4 zQWKbF=Q$Z7i{*7}!Lk^OCv5N9%Tm(w|I3fLW+={UmCp=LH$0%nkB8T|XhBAOFe&g> zVAV-Vdp$tdQ>B!v^98bK|Y7e}fr zCaozv@vx^)He>)VQW`!xBTFCFLRcW^h{0dybaCFq;I;u$^eDe7CnDdakHnBfwnbJA1=*?CphwhTl`0Nrl!7k_o1(RKNxG@vOOwmd! zsx5Yl&bjT+@Tue@a*VQaH>r{P*XbLxp~$5Jdoj6}FIOLQ99jtqY5{K}0tj9_E^8j( zpr$l0yUAhgJ-IyG+z{H>*rs)}ySwXglFxquuSc3J&t+`S7182S^0zH4qyw|^u*sCE zdhurJ2{k^z!7^vmb(7J$p5y#E4WOR{edHR`ZfpQKq`T7LdzX^zxRCGL>r`=!RO?i+g-Gc9_o1(5!=FgcYKl;7|^*qh>HYGiP^J zS6(X$_D5S1YrYLk_y`LXX^6AEezuT))u+K%Zi0MZUXm`WuJiz=jlZ!^P_klppte0F z*HhMTbGr8nXSYB6+y}K~@z$LbBkt_*oO$DLJR(uR#m7GoPEGyHA1|&uQr6%Q7!aQA zooInld*)~;+^douw7Mx)t7OL&i;IJ$Fl3#9xkE70b~JFV}I;XbMQZiy-v|If@mM>xz53d{>e|e{BVV*uS~b zB%fo43#=x~#S~G+AzR~iINmGtTeHgv^0UY?0UjKHl}%Asr*A~g5Y9C4LpTxJHu3|i z=0%8xkun|Lzabr^e;OQRs`jUhYBXFqxbJkImY@S>WJ!#*R_SZ$x6?9rdD&xH=-=!oUh1$~51KYrz z1w|4OSpjbYxy3TB-d`>@dkEfRmPst|Vf>HW!(mX_cr2&>n|r=oXH95zd)L4gIY5vv zPwKVX-h?WXBx-}|=U2(XR_fF@WhWpkSZ?YG8VYM)k|tYREuQyz95($XeXpUb+Zs_z zIbFN+_@LQ$vg4p#FgtHkS9Oqwm_+;1w$tmD_dDmTTl;XnLau9q8Sz)VePioE z3AJEK?~0i)&}%fkteUzoVZ(}ElXQ{}tir-jcGx` z_NF2}TrCh^+0Ia)zLdA-t9N6*S1R(H#>NqgC0~mTYw`ETq%4P@m~#~e50_cp)yYiy zsx+OPv3va&Xno^%(-23KKuAO3F%w7V;aB0FAF#4?8)_7Jp59*w1>S7B zPNb@kEHc7mIt*rNzK-&z%=hghe3wE9@VWB$>4nVqA(9%de`8dJ(W@zm1y z>l{%`IghAQ+c%21KUdlImlF&3VK=qOsA^UiapKjjyZ*jB0w z{=V&^&nu1`oUd<^>#wt-V_g097x4KHnosXjzkV+;f! z`mQ%ONFJ7LzYI*nCb7@@@gs>T@^6`d4+h$0GwJ6pB~ynJO$xC3Vq;?^`j#N{?oZUn zu3eD1A5Sm#yNDrd29?;0b^44nqCG(a7+ZgeSw_umA%-)BxMT3MmbQW9?F#-kF$V3Q zp6FQ3))9cT2?S&h%Q=i`AqA6rMiy%KLWAFe3a*ad?Qv@xBqKkiQ2k(4Bud1Qw|2~x_spA%Jn%~s9;h7IyiLB3p`UlBHecQtVjqr~*;vsXcF<=hKp^tI=n`h>HWej%r z_GDiVEsqXua{K8jvylXB4E(JCEAt|N*^qmo?;8#^wG}SbWW2hO;1r0?$`S#sVY8(iAcFpy ziY|_ht<%#478YGcwtz%2$Pq<+MJt#hKV-*l^=PYaAglhJkW3r8YshPX-)AuRulEFR zjCMzFP${ohwwFP57mdEY-Prj#Q)w9|%I(4!9NxarR5%qLE;e|Zl+{iRRa*I8{;u9! z@djR?fgPT++pq9(40Ggva{=18KwT64wkE~z0fb;my!2SQ`@r%;3|}O?7!)YYYp<;ne{q4r_Vu(Mj*Ue#Mf!4j_yGU-ALvf$;ON#q zI{FpQNKr60O{=LfA(4_Le;SgVYyP%Afl@!+E<0xv;=sk740a|J^`7V9!mIuyN|XfJq-r=|DeONi93Lh?ojYeq&orenc`J$ui?h7NSH9RB{&686Rp z3^a2#HQ%_mv}QS!f#i*$(bG8`wYPZ|anp4xSc?YCbNV?t$Ukx5Y!SA$G<^pPW6`+^ z8z`xCEDK{=QHBHrP?M7jUR;lxPs}NP{$z>br>(`%M2Bf5_Ws4XtHV;2`r&GDHi5osbQ*R=_m za)Iz`Fy907yuYEaqmC=+@iZBH7s!G2&!MGqD{hVCU!rYRR{$mp$!NJ0YRZe#J15|H z0J|xxs3>*yW?Q_e`2_T9nC51}SDBybS8r`>^miLkCtvJ6qImHmpxI#hG!!OuAHG^r zVD<3L=BHyFHgvowx5Dr#nUejY!D+j-KX?kaR>XK`Zuj{Mlq+&?!LxB)=6walo$NZi zk41Q*^DFoa?JZ<9XImbaAQu+JXD1>ZT%bsq3_8>q*s~vs+GzaQu6^iVt8B(IQN(Vl zg02r}T86Vk+n-Pczh(71Cb93B>^d$K#of8)@^x$1k~*NprJjmH`qD;eu=B$>p3zTA zRyMMZ2LiAv&F9OzzR9JfrIruBZw`-yu4xxW&-SN>qHJU5F7sxGkcv-AT~q{aFityu z|Nh>3mV{dNexc^;hai-b-T9){9oANj<*hB*5>b(7})^oWT z24|auG`ZXf7-aC9cW(h*a(AIITEDR#)GK7r>?r8)*)nWzad*x^{}xV0J)z#z9N`vk z69GbC* z2?>Lv(akU8azA=pk8KQNq=RybA0t&0^sGR5R}?1XUSr6O83ThwTB#RG`QKPk82Af@ zE+q^II$dCLLT?_Z+e~j$0cwQD^7D;BeTUy=i{3l5X31ei{TYpi^yd>*8$)_7Qa7*V z<-2>I3KRc)c8)yJ^pE#hVa)KoLi@fqqdvn7-XG*YRE%z)U?=Li1^qn})we_faJ=$p zENf_#@dpbzd9u&NLKm?TD97=3%yu72rB$>0^)lqlJs1)H+Az{<=C-TaVUt)^5h!fvOi8Zecd;AP@>eOrR z=|q91iUIbS^jm_5`6Jox`~WWgcm=B{7A zsP|xh!`a4bM&^vA4hw^vS8a7C&DTn|Y1JXZS2qQRx}KhR8v|M`_^VB=TsYd5R-t|C z=I~EBE(g<*)U)o7NYn!fdzgVpDHq^Tm5REdI_~fvE-^?5ro?prh;Ck~i?Y>fuq0O& zxrHA1T`*~BY580YlfIuVhw0cINM!JsFQ+LnA2goQML{t*62m7Ky#hTekc_`{F(=*k zTRLkL*$~cHLrsT_&5CKgb(M~gldVzMAis<0^p~&wDWa61_V+kJahrb$ugY$rvxd8Q zY2Az=)9XIIn48$P)po=2hAa@41FYYM!`=!_HuVMn9f{{W6+d*E`DXe#qw*9|IGyE>;1gG7K?MEYVh1H!A9?vs;xWbq-eAC{ss+**2cwT2X ztUaY|LSg|)vv^j6sJsmSPmzwPLxKqzK5!IBK8qFfdM&*DK&<Iu$>i*W$8XwRTLyJCRHMQXFNy3@vQaQhN*>oJ$x^X<1g;k!I_qqGuf-`BNA>s$Wr zi>wEoYltmpAunNi#C7jynNkCEi~Q;bKHf!mcR}tE=qk9pspED;_F-xI#pXk>)?2 z`_O-N-E6h4>Xb7xFswde8+g>#82dnU!reiLfyfb^R;j~N!c zVW59o(A+hbn>Poo4+3vU?B)@8C;O4n5l{0e{~shOUrR)h>57#6g!(ViTE4y1_cZYc z-INzfmt24Ll`CR2F5h;ha-}--?e;^TkJZQ4dtmUNA`H=6NtawNXF*D)~{%t5Qdnp?0V!*@_q-GlDNAGsF|be2hf=;l1iYNj1(6-#{6J09B_jiVd>t#$p zj?z4ytZV(`Ft=p@_#hmrIQJraHjy6Nl}7Qbw7q$>zOUx$A|Yc18i^>V*C;UwDV^Ww zN{G^sBItNcsnUv>S`7{?t1?LOAyIzfOCLYpqtSn>3E6efIb7vyQ+K}AJ4juBu@<0$ zkB-4vtpJaJ#$nrOs^OixdPQ3CVA{RNj&Nz8j!H$#t3@;muU ze`RK{Zh}%awuwGJ)Tk~fe|+C#X_?NlXu8oEf9S?S0BlJV8Nx@GW(djme3Zc}_{&a0 z@FOAL;1Zw$0H7lo(7$xL!HRV_^2q;V!JF?M!;WE~hvjs#mQ)q2UqG(b>AL_GUamrV zlo1)ILMtS9_BfjE;(Uti?xp~x|JBv}C?BObdGg7%0>JBf+FqnyTwNZGRUkRIJIk`V ze&}aW$g>oxW6#_D6(50{_P)bpZ$K~fjxGilJIZ18+>DHcTwGijrZ8Qfcko;V!^xgC z3v7+$FaU$-^tQw<(DVNN3)I6YLVWlHTmi1br4=_s)537=dJ0@T>6Y5=SF;snMxCWk!zRWPqc6FsG&G7 z10Sr169rhLh`N%HlQVlR#nM51Ui=lN@m~4ex@iYiME->D< zukPp}@M9i|>wYdcqG)xsSxvf{b%|FUvw4&F?c2+dQ64fe^Ls;$7esgeeJvpJM<0z| zjLKQmb3Z&OMgGY)lwR|Brp6ZCXIpM@uK!e1Txg>!qvmo#zfiw6!TX>-9D|0uRU&#` z8H7AjrGA8>O$&@va`ypa_j9Xe?nFniGMk*W{4)?cEDg9DFhHRZUs=^xLAmhoXGD^> z+X3n~c@JVdTIi<cU9DqGTtF9jf}7ZfhQ=SLB0pUA)?43y$86!+^1k~?ePr)f zRu!IdA$%w_>0FfJ_YMA{OvQ#GAfb_$6-=U*`WryCv@TzxIXfSFoCole^C7Hm4+2!A zj*7^&P`#sIA6g*RlcDs?%ov>ZC9TFs;@1ambpn2DSRbY;cPyuWc07q*4{VzBc0YK+ z&W_yo^e@ttIHeeW{az?WKe(HG46z)~eZsS;yu5By0tnhU@Dz^^~Xo7Q|~s2Tpdo;IV>*UfEEt7MgUMLT;8x3&d4 zeNX~1x@{!iAs_+^e7N%0?cLx*K0AtmUo1R;M@=5|+;WUJB_;O7mY0bIFqcPK&4@Ef z2aF6~#1n$?f>@aE^3WMMeBb8IL(kdq1K;oGKCg6|LeOwM5!`UJNQzlPf!?x&v2Qlk zWx##3S}mCD_$4vjKT_z_nzVDQx<}n}w_o*xzS4=wy!Vm=q>`&X4 zlKq=oqHKlp)wsgdL6wEw)2D?$Z?5D85yt5Fi{RsG~)V| zzQ5=F2TwJ=L)mp+Z1z~tY|dAUN`7|`f`JIhLCCc>{T+koQlN#w_g4X4YT-W~wB+KZ zCO@}&mOE>Dh>9A%RuJFH#RfXlLt>JXr5dkX!%ug7ZfMVrDjLowFo1DTZ4wvUZ?uQx zTBSR)EqWceHZ(SdchvnkuhYM&i=e&120n$U!CGKAlESc3Gpw>mry1{ELE!O9MakOG zAZ`;S1h~*~>t!6@Ls995GaCD7guT(Og@MPH>P4#1!~tx3drp0!q3hfB(deT=N`|uO z_a2_pWM@Y_X|hTzPW^W?KQB@$M%!@g&!%64lh^k9&^b+b+v9n(*aea8Otm?v+rR|M z_!T9hE2AZ%Y|xEu<{&uYFt2QO?@8yy#__?A*>A;cw8Seoy(Mjou!>C76lN$a`~qX- z>h1cXV|m^v*yuOCyX;HK8#Dtb>pR=f&ku7XB+N^dljbP&bl?8cw_spC*D);3b0$DO zHGw;eejCH(P-dq8Tyynx`bm~HYi(rtLnNcNT_i`b2v}WT{e$URP+L@#HyMh(WL_P$yYyR^K?!qA+f{a!5NAuLMI@sp@{1E(!q>7na&kt4k0GbF@^7KI|Ot_d} zXcZ3J|GWSH`+oxKKeeAf1HaNO`Zb|X?q=8ajq)L? zWF{8eMSq3jyDSFYH?3fEzfw+~cvU>*yeFRzp)8nz55+_JWNr4o#=@Q(y@7w@_F`QoPx6!!UHD^K=gBJgdF0N#3qk@=@WxsNx!73Hk z^)zz};sR>6KUFvES`J#Zf4nbx*I2<$J1$u8Gn12vX8LJX`jHK1pA47)-S}TrjRq|A zBCz0xq*lryB|mqz89|7oo(u!|%$pG@BKr?#oR-cB^Q4sc$WMHCQ_-QK26uTNuFDqb zV;5LMq9p=rnYOfbYn;MTC>o#~EUVa&8VXKlO$Q6bk>u}OA`@iEDV1&BC%#QCSuzN1 z_`8Y#iA34zn`bs|R2GLoUu?>qHz1HQ>hR5hWp2@&vT9u;mvq42?=J7D24mOQ66^*I z{*2_yz5DqN$MA314Wq?Wcv^*{sV7^|ih~o0yGz79!^vgdiCt(^b95xfz)u2-t;pFi z1eb;IDZTjCSoSkW#UxGpWM3=pz)x5;GWbimX4&b}pifGQKMB-tcbmKH1Y=qgP+8{Y zb559Zzl`oF=+$O;*G;Sj3(i`q7TklSsrTK(oYwyROYpT9ZleZqvU97W> z)%s1>U3kd*u@H)5s$6XpiM3Z<5alAONtf4erosH)+7FnlPqZIb*^YjDFmz_Ce&N-7 zMJ+>MXvxF|LDrs#?`@0OB3@zvwyp~UPs98ZraLcLy_1kH^> zs`EXUvZQ=OoU!e3Z82U-=0FyZFQSxbb~KGS>OQ9u_?*n5+gXwJyms$`9Im(*T%4Ro z`kap8y7+Cl@9(Omim7)I64Uf=D!bfkS)V{`hMN^G!rB|>|H8KhBB%YhzVde#t-o<1 zVYk*;E26W-I;ntfTxgQm2`Gd#^D8eb#P4&DZzFcqQl#D)4n(c?`y)xxZ_J;}uDsov zfo5`*r}XFF(FGn5vxn(?D@NL7lC!lhWX%jAP?YvsqbYQ>u-V*&5I@;0zxa z%_DtKG~*U=?x&~RD<>?XJ-*a|!tQyPB{fz|(fWr5ck8;7O4KJC)!$Z3dTyVQH^zZJ zoY~Gsd~wI7V)YG0Nwi+u@|wa7KaNRzOqI_IXI4k=FWvo)U@u8aIr*5(X>0TU=8D2U1RZ3PhUe99d9}mLGYPYVfvQRj*x7&xVSn_oS|xi%#)qJNP_4FJIqk zI)9k#Dqg-~TK;99JOwVC(b%W~%0ZlvbfEd>M@ zuLguVQYrVLJQiV%F5{e&Eo~-kW}oewJ+JO~6ILBul_h1%f*$y=Fx0cyqryVTaHyCau`~F+;Rs>4@4qG` zqDY8~L|4E~t?UsYk)ke2 zQjZxTAR4N{A<wg`&4<5l^-VYHAV0`(m7xKQdG5(5^)e+Tx>+vspGHFdgv8eR!T(@yUYjwPAlT}cm+E~(#S0S{P<{A)lHi2j=ob!#bck)6{gV=5Tj1la3vW3ux4qHcFGQRBhyEIn0r18ym+MbBqU9j zG)VO+ntHhS*JND&Yfq)6}5RW=~fM3i2n*957d zqavsjQ9xQK3Ib9>hY(7lAiYFtfB>NeDWQ`P63AJ&_j|wZ-1B^Y?)`gs;9+I0%sJPb zbM!Ih7$sTP*CWj$kEy#xJj@KtucNepLS;HQl^w{fi2j}`t7_l%RI#RT-|zKCl5+L^ z`1Va!6cGM)`uWdmeg+sK;v-#ZIcZfRpbEXJVFtY`-)fB$%#oTJP7RiS;}Pvl6+@ym zF6ZhvFTkD_eGWp*Qn@>5FFwzS-_DL3{qpFssMx~swaEVXjup?@im9o1{M*n_KRx$% zP(Vto{%jR@E=ksx?9`);uTc2`t2ll!3>reXKk+bQx-Nui|AkN)uT;#?)gwJBvV3N5 z1(OF4{{Uq!BBtV@sALsfmZon^_Sdba&1LjLLxg2jjXS`WWsa46>qL1A1e*N?2nbGN ztEczg6dNx#U{%KQ!rrrR69qZq27xhFbB+7V#bUwY{0HCx1yit|(@No)4#{!dL{s_L zIyI9=AWaex%+pk zSZ%-Os_DqQ(a5pj+-zn8prSLTrQ2yD+e;pT7vlDkoY{$I6aiX z-Go~&IAa`67{lIuhLE~q-4P|1XmNHeOVg_oBwR%`+r2lXwb#7H%D8|mWYCNthM^$V_Qp3Y(tel+Y8xD@#G12TWWvT7u3cAYWl3SuLTAW#*c>W=1Gw@(R!tMX{N%_db!eg!>A%+yG+J&Eqi{r4JyYpc z1+y_nN+*S-zR340pHU-Q{)MEAe3 z^!5iq7As0&%CD2FAH+bw{RDi4hzVlN| znB@<;4MMxnh2~B)esGA_0#I+#tQu|XMjpqvIqwX=%--{|Nv`ze*zK}5 zwmexeIeGCl(|JUN+3W&nX_|tyxzn4cehA`MVJX;|mbH=4%Sp3(Qz0e>*p>VM3(_BZ zEj5y!nDUzn3aKiGM70*pVwi=JlDEs!Vsi%YlY}C)rp3P$6c&D^vKjJ5E7GtHt;4r6 zuDq%FJfW#9S#fjw0gp!9{siKT>yj|31eD6*(Z0n>f`$Dj766|zJtqZp@jwul(=%Z0kJqUG2&8wk5l&v^aSfzEFAm5YSI zhp~5Adb$F)@J3bsL92>G)x6Od_Y1(rO+N`X;dI0>=l^ob(-1pfLf%H z?z6%VXovUSFpky*%Qq^}aC{nJc>cS97VGiQCGBjMclWP}gG2h1aiQ#%`J{Of8nbg? zJkTJ;fTPLj@tf*BX~(n8nlD6j>w~w5!P;=Db(wwxaG@A;BBe}kDBjRRuyny@HQK2Gpo}LW+4Sp zu27?$d)KV$TV2CRI}(n#dUUgwqyOx;oaDBO1(b8ot3fxI4+hG$_nn$sNL>d*|2vkas-F-{#3R_x03_1;*wzXisO#L%=Li6i+^g~)_MG%!IJAIq z`6t54`|)`ar&6W2VjL|?%Xdhct{!X&1p2DPUVfe-Bx~L{%K{8ccQ$&ZW53j*%1ND= zwSJ=$@8nt)HfT-UoPM$%23;6qKPCZ>PmQy|Qyc0;E#M(amY!o}mOWD$CU0=4*Iyf% zw)RPqKI<*bExP)*FWzaL3!?t<$G~JoOH;ky!R+x3Y!)*^q_|xdQQRk*@W>>|qVbsm z7VbrN4NT~14Tp|ZTnB>I#VzeRpMOt$A4cVrD607}1z2!c|FPhdSFXzEk2haWy&Sjv zIVI120Z+G%ZOIN{x>6H#tPdPY9&ZX&nXt(4!q(6fv>!~~aUCKO0`hdw^9&jO) znB@L=rFn38XpFp9;DFe~)g&{m9p7m5VRSiCf6LC%# zN=DxR8<)_}J@(g=qEmBE_|H|CW`m$(3%-a4JQTb%!Mw)zJe?=aQoAMEmOpyWA-%@m6Kk}NAZA};^U2fk?kz~SwEU1+PE%6pr**kVWE*~Q4y11^|1KC zZQ}s$qz^f!Ukqx}0|B4(2LQ@G_={CN3w`+T+>5*48*IS9$s>8s(a-L^^ItQRH089F%ff!cf8g^g|r|dquYm$l_-rjt`ngd6E*S2=HmAq%{M@vkK zeQ)pQLzBW(|D=7p)7%^mWbaUSZ33pNYoIGAshGCjm;5S4G;$z9-fJXl=kl$~9sPC7 z58-zmugG~kXn$Z&Hxggc);QX$tE+oMP3_|tL4~y`L`eniU1*%lPA2&JS=2a4L#9bx zXPZR<>|Xj0cB{Za_;KKLtoCRD26i-F$4{GtoAk==(!@guw%V2#=-i7%+Am1L!!wARYm)RDM)yt`z_3r6=nGY4fwM`Dkl+QEKc40MKj75Fs`Bg8SG{pWe5EL_~DY zoT2CFKGN5sobHjj;MX_M^8?$-abl zkcVwsM}G#bGo>|SB5oxOO8^d1>cgS&dylJSjH*M(H$tYza(@Ymp91_5H;C(A_1qh9fav?d$XQL!n6Ul7?xl#E8VOwkh_kh4$&-ril=J!0V)TZulV{-jLYN%C0R?VM zg?;zSi4;k@5AT2aMq@=yOIq`SWxtxLB^8Qew|dhnq^7~jkhSnJ^X#U52Y~PbMchts z@tV}R=WPrm4vEVNCBx4P7+p-Ne9b@96UsMxM(jE(!Tr8rN7T3RC3@>61so%r}o9|Cy!>T8Ieq<7-&ll6#l*@Z|>EdYW~y#TVxGd!)b%ux0b zGc1Y^pcFkgO`#qOv1c>rO2&!b9hxB^4RZcdN&VH7|8 zP+wWUoW^EEh6vX<0xUAm>jB(u9;SqLm~Ze#)Zy-gU2qxsdm_~nUh{NAF0->X-{Wrs zqar`!dtrL%tC#Ol^Sc|*0(>toJ`OzjSLqY>eQWW*-tl}fB^;XBNDBicr@HMpen0md zsVyinxx{h3#4y~b2-qDCeU}qc`hVw|z!&dX=l?@Ffy+0ZDgPMJFQ=xYf9tvFXVF46 zdL+4ZYR7D7#>NVbh=hFiW0h3i81U;fxb06DY8W`T*4%8(+_~BE#V@=6gC=F7#*vYM zxbb7jpB=1`@d1d|eDz4hh>El+vzAHwsD}rR4q4JMuTSf)m$XMGa_Xx3z6U`a7_mI# zP5C1OlE7O&xNr}6t@#To5$oqJ--;fr^tq^qH|&%HR^joFi3@P0VL`e}e_0|QFXSXW zYjC^!tE+eEp$NyTH8V;6JQvU(QTL?8QdE)VSlim(m#o`my#6CPsp+@KadmcKLGR&x;o}!~f%ud% zYgLW{pq%&6N(a=U&i|^CMMeXVqKvY)P~p7GXbYVhZ;~92IQp>lCe!Il!8{-5{RcZf zAv4O)0cj-R!pD;LsY&`zp9vRtcmDEU=x>+?n~G<@wl{bC*>G03^RKP}RbAi^B@?vv z%cG?du`Jok0BJJJ(bx3sr)DGr#5X9E@~FTg;N8`?LUTV))&GEf?gT-qo(*ienK~EJrar6;vT4e4Fh#fXeKBIrjh(1K;D;AF9gtinV#G z%{pA}H#hEuYtD(HKpg8Pwsu)=uW=R@2-Ns?s{%o|^a)m%cpf2V>!J@6GUvK_7rxE# za>hj8nj4iCV^4Z_^Vz|y?L3Jv9|HwD9<3GtgnYqY`KxiM#QE1OmF`D&pLYFXaq!Zv z+fU9{4CEn9swSJivj3?IYh*H)^H#2ZiD80v(9IGEQE)g*RVh30I;_X1UikkS;{F+} zK^H_h@V&9ZX&jKRl#%nm|E|^bR}D!{F%JRQZD_4^uLhZj>Cv*@VhMu$;vZ93JGWWR z&CecBe<>75PR`bX&xEf$)0A9Snux9_H7)I)I~@uHn|ua!g3}A3#B6Y$HmvbW*6nxN z!-bk=e?)1%0S_f<_ha9uKYP%}`skl967P7K0uZ$hbXMSq zu?6k{`Ig(66sIlCFaB|G$nX<};TWFvPYW8f<{GW}usfuUV#wU8`#LfPsJ%6gu36oGT#45B8RfB|pdGU>UjykV( zn3tJyf4{yo&PFo(5Jpg%0O0-1>y$Q+I{P|H3srArX@c^ppY@K4;PvUWgeuXu;IY^S z`CcF8od^3hg0C(u^Z#SyyU1RFL$}tZQTjWdZ;|fMPhfieyb>`l)`}iXg4_2TuTRvReeV zgW1pKfY+@UPgAikA^nq&OCHrd!yc=td^P2d1#vqT4}UsurP#sl&JQS?(BE|OZ;|2m z#h;kPb8|53Cf1WRqE=oQ>CMyf4WpV2B5)wVAFWLG<#^^ zaX@$>w*UL-P&z>BNq})aKRBg}+WSY9E`ts+_WjY|v0l+y9_DCJ( z4cE5G%YToR8#%OISB9X@0#1DZHyV&ud{bZ!ZVc>;ZJWz-H+g69{Yq6bU`E7mYJB+! zFhTA?3iqx*nM`;Dj` zkP8@KVh^B#1H&Tcd9=U->@-y%zZzgF`(LoNO* z;HI8uJF6o<#kXEcFIr%oGoW*BPRW_b6}rQ7!t;P|1JBKP>y6P&D<-Y@n1|mwhlAgG zYD;ALGkHp7T%AWtJ}6c-5(1p!%`#T&|D_J6rHOG}{>|6_9}a$^DElNNy*#0qCoI3* z_|c_*HR&Jn|8Uro1`XQ(%<|-0|C>_nyblC400O^!tLQfjNg-u}{uce4Gxt=pQ!d>E zob=CaUz_gMtP{DOx1EGBNF>Toeg*_fwE!YSef{s1_rKRxIG{1MNcPgaOftIR67AA|zALg6T7Cp) z4v_XUl5jM`O9wh%1$q0Y^#lundc;58UiRyK%CS`XYxgQ?kHN5!x)!;HHQU`e@UWes zu`O8t@ltL$a8YK93uBQOye!0k*>A3l*h6Ca8FQ6lL8$WAP_#>`p~fGY^7&~T3Hx1< z+0-j(y^H;b+O-IKgr%5N@b1kBmy`?5wK0H6ykm9c%x~Fl&>LsTMftv>(N=dQ-GALN zDT7(I!@T&f&u`yJXDD{xYvW{Xd;aaI!9%5U6~z5=v#kga&P)4XZR=w)>&ps z(0m=yF&Yu|kIM53m-fN;Znb9Fi3!{krYq6*Xq?*U_`v8VWZrfE$&`N@bn8kctSWX~ zi+ytXmxS*?S#m6cZJ=>QhD)pK*bN{WQSoi;*pJOeVmU4%W3DK=U$Q@=z3IfL(|=?- zg#UflDLnbxdukoL?uv&t%KPTE`@_3n#fTS`9y#B0`nBJZNu0BV4_>(p--==q9iTOl zPFFPC_Gvx{l-#7YVmeuxn~gY7aHfDit(jAg?B~8QKM{L94ZVDx0&71Ec?SQ^x3G)b zT%$C^#hc_S2>xj-__6dZRiZsG*+ zcc-lY^~c5FA*n*Qsu|Ls9AFMk`gI)mXKhROp)$Ey#Nj0A4U3_qnPKM?>Y*9%7F_*F$V%@62U zTjPoj#Gd5dmB`C8c-JAi5z+d@fsY%muoCzh+G53KHO6_{*M7}1il~_JO2^~fx=B3e zny?bBY1;o+MlNkjlIPXvZ5vK`50Eb@GIrn(1vmSl%7X5A>Gnvz$XREh0B9zU$``j0 zWsL#Nird8)eLpAEe3WkR!NFzpXRxEe@LFFDvCaTKzcA8Jr%Q?e2X=_$gkf~aw=&I; zf$!5!zqp>MVAFpCA@A;49X`EM@yr05ad6fRX_L~cgHpM@4R-)NMvbzEM zGi1R|^inWaUhwa0G|=Zk#A-w+A3b@x_&0;Bww1Yb=e>fGOl?q~!?LQ6{s(COc+=>$ zFMmY)FsLm%bht6*M;(gyIUxAP%NrK1vpRpPX6#J# z%gAd^uK3L+fLJUah9n2wAN0Eu@xuS8A6(yYMb6#Tp{|ver7?EtC*>+hiDFriVd0e} zGt4G>Tv-|NBATDUdgN=q1^h=VOw+n7%hTS|YY?{ywOPg|+t;^ZOeMQ&MCl=0g?AZ~ zmo0q$UiLvT!^{{3qqm>Sg0!1axffV4hk8+nYZfG zNjFiJfL;6`v{~2S(c`$D+~^Vu4b+`y_!p56kLzRYT~W5^N<8{TwCT?Sdbgy+Th`3x zGSm0gZ$#mcB4jx3%bDoN_Z<%1cE00ecE$4yY)iM6b4x=&2E^{9G^W&)1hu$UE=13$ zoW)8z7Qb{Wr+EU}ZX(_+J?Zm3{7ld>D+8roBnL6gU6j8_Wo5ZF55Y?!tX2D;cE$`V ztxq^eMGmdJY`B+B&vg2jfX7p&{1MXzZRx``neN@OZ%I>))8j3;J5h;uf^EGqdYs|O5+oxBLkaE$IU>EJXVz-kE_KXrGVOS`F3>wv* zE^K!_9Fvp}TJmIAP*+ydtD9A1F+gzv4Iy_1VOMNB*)jmZdT>fH}(2LH5HX zjBEVgN`l!pf`_zn?}BS3lxR3))y@W*dD352+93U?cVZ#?{^Kc}QY1Se^*b4JwnIYQ zMk->rFad+~d!N11>d-BT(Qa{CNEl@1LA?J^!YMmsD)zc&f8Tmx1m?ZHRM)SPuDVyk zD(QW2h$>p>@DHrtWA5s3ieDj=);V^VowS3cA{3&hDa@T9nKUSknu zo9Datuk_MYjTJhTuL^|I6+GB>Pn}ku7>zj=z-l>#O@JrN+;f;WQA%%Ku8(#+mqeZRcqSuqrgE z86br<>DCDBf`ikW311ve7geM+go3svU{#-w0!ebgKCdg75{HR2xxZC z+P2#1@&8Bp@gJwm?_T~-^U^=UX?2M53%x4OWy3;)qlBI(ta zysEMN5d~NC!_B@p(inSS!30aLoBxsS!-{Hte|+((KW3Ug7xT}gwR|}*L69W$Xyo};xQS*1L6h*u3E{wn3)l;ee*Jjr9D93QtYn><-ULW2R zxFI0DEu8KB>z<|M6(AY-7m9(JdA+~%?s1k?TZx#RXWQ}ykijF1&d$-}&Gb4Y(lMt` z$*P&R>D@Cq>5t{v(D$jS?RyhdQ54I98q zDn5R269&oGS)7VDH4kD)18%5TexB-)dgtk!n_o+!n|gYSQ1pk57Dj-7^6q#vwmEwaN=44Faza8Npe z3p#-bK(V;&H!8su&}4Hv+mM+zUPwUT3|*5NM`?B6D1BlAm#pA44mb8Ne=I^ZxcVq; z+ZwEBSOh)L)O|%yNzP1dZ@p>tOhgS+;zcX^R5)h2G#@lsoBrp66^PA)etGfo7n7d~ zO_g;#tEH!h2kABnGgiLmr;P1CWA&DeFF4J>yr*aY_JL}P!$ziiJl17DD?ONwbExxO zbB~dMia1Ywnqvofl*+V_>@%^_BX??NPT%5cmw+esj+pM+aQC%3&Hj7=Y0p}XbC>3y zYQUzm%eMY#V2R5t*C#2)+|bLo%|lF%GKk~)tbdeg1vN2%VVSOuS75%qfM`2hNf<1* zh%wEjb1Gzrd}Z31V!F+xzA7Czq<0q-=&^?HJ){SmyeCT}(NtQ0E#9UkrkhpheTsk9 zhFZuUmF|u_>4$ntV=Q#$`nxY(@0e9g_e&ct zcbqrc?I|RapDC8kL-8YN9jwj zHMZIcIegK1Kb^$f%3qz^$?Cptkl2^&KzS5b8rnPWACzm&FYoa@cWSa8^=?4XU!)pl z&_uk*<4;?T+H6`Yy-u1LJ^nJ?BJm9qL|IYU+*7k_W+q(uS?8C01Isq+8;$>LboB|n zO093=BXEAEdr8hW(>wO*75O6rqY1#*UXh68(xGdL`|?8tz2O;FL72ou7vWR zSODa>?7!jiJ$a>pKzMyo;b|tl0jY~emRehnIQfUd6LKZw@o-$k+K8YAG?Unnz*6VH z`(XO@XFs}xZb8th;e}C-YuB#v>qb4wyavi_W9xWk!sYnU{-RQA@B@+ltF@GXv+^lG z=a#mo6-0)QnaLm(OSyoDjoDbKYJHfD0et(NOfWxYey?d9>Fz)J{q*MH1x0j_(x_SC zXZDv@l!G49KDgO1f6E(5ljpycsMO%y>1I;;^ZE050a$;gCa@i;db}q80Pf!t&z{UY z##@JlpMJ2OJYegiyA7O&kWta5#eI&Vua%W{?1+cn?m0PO@Eg!o7fNhoZXGMeU~iX+ zJz$Ug$4=qHBwABrC!_fvpMM%gWA)03mE^>3af!q5Hyb>;{vT}^|HXr-8lfcKLNl9X z8ZBUBMI`yX_W2h-O@7%tG#)uS{lwZVHoAUer>>l0)!SHJ+F+ms!R%9P7)w*={&*>D zb4rBW!|nR#SFf(gzrWYxbU3w4(z!42{^5;uF7$$rjkVJTB@2C|?uyS{4%ncmU%q+n z6dm2I2bcb!qkEik?F=2=SK+(o=;$6Azq~|8_bKnpbvn8`TIcW4(LIm->lGax-T!Uy ze^=@Mg2w+Je2`C%s$BcH`KUZhoF2fv14p?xQI%m%{DG6{;Q)f&a=*l2Pw3>X8rG`# zehs)0nL-b`RA0|bHG34;JVl7h`{f1_N9|7U8``Z*N>H33d}!C;GxGfRf7W zT9kQA;4J`SHLgY}oJ;uZ-(An~XqtE}QQa$j=BJ$fH&~Z%jUEWu^Lt_f_Wz}Vb z$81Uk!ZIq|B?6_fH%KYYh9bR_h9D6Cz|4prKpZa0w-;WWWht=UU-NY&ipHRjGRpv+ z_?mh*G8&*1&oME-iO1JlXXoO=rTY&*T97mKfp${o9t)l8RjvLr6x62<461oA;ALf3 zqM}1?oNclNaD)EE>Y2GaYmqlsf+nyWD_wWi;I_jVzypQPJ!k9(k(=`)9(6*fNc zNsxarD{A6-U@E=-sZ()39vI_)`l-CCxvKsuJ6rTvwV3w_o6_y>1_2N> zM9|jHMwdO!Ib!(d!z|p847CUva_wXbr3GGAHifoJ?1%Vh8?!NhS1razdZ2N6D+gm^ zV+d%-UvzYwtMuWQ0wEBpu0<+`fEJ}NC;Izm;T`=XM@uUm>$+msQ*_Tidp%^Zhmc}z zk|xESGmN1j?DT2XeM6S8h=-n@l+(axpq~;CXvlLWk=|_bcOo6b_taTyuh5IudzNM3kNcQb6Nq z#mMhl1j84gCgOt}z2*xH0wY)O z0^Q}qf&DU&P;RHok4J{L+0yOQ(aLqx46o)CG=e?A~;>*F?2IH-pC3cj_-HUrQtE<#qeENMQQk z;<$g@J(xeHszv4$acE?p{6ToP%Xxf${F$K)@<1LDyqEnVX^617T;%)ntbzi%$YVY` zyC%Yzm3MaJ@s36j3Z;Re?Hp1GxMPr^>f8_iz`$}+fGy5q z@Q%I=+1-SSNPe8kekggRU8Db_nQeyOfVz;bOyK%B^~m*Dy``l^vXNM%epDQ`or`Sv z?$~m;TDmLr`bZbtv|le~S?fkv2oDceTJkLN+EW5JcnQqMgr%|ykA~)&pdcPei(m8K z^|&}H^qL_LKdvL!Hk5!`K7NbFK*o|xyP2)tJ%19AQ{$#kR?xk2_({gy79;lJi5 zhD0|Nt#hG+I6At7U6?C@|hkSwCi?{ek+ z?U(X#dFI?MlI(8ieJUZR3G8j;ZFsz#yxX!FF3HQwD>+Uo@L#S zT?sps%^d)W(E?S=lM^G?7s&X?NLedT-4i6)4rkp&z#cDx$a}8p*@5?p)sKm5BU~tWfzCkfGuYj@XCVEmgKLTTy~6Qax!+ z_)O30eg*88;$jh?lvM9N#wItKkwd*HVrN4m5@Km-<+}!hBX2EV28i~%>)fw25Wj>n zq`>zClm2wi$4*sXLsn} zQD?~Q>wCcSnte0R=wtFC7`LgUYmUFBL+i$;o@5X>H4XD>u_lfTG7$ost0{ z>#=l2|rbu}n>bK=X+@tfcwcQE%mZDqZ_A%6e z@&>|N19gC_Y^@&VpKo1rQTy1L<`3H)Pj$rZB%mf$Xe55hQYwX`DDqR^aBrhuhdj&D zVQ9{Sq$%33*eP{ycf%%+2yiZ3PSLy|vsfF;xvlgd0OEfKQX9W;midl_91yzbAZ=)JK=O!_Y^Yxt=b;xwts3(oM@A?=Ln}PrsZUj=+~kEA4Wy6qG`)!POcBG#Wf2Txfw4tC$Gl?SY5^ zA64pO9Ya(|go}E^;@OG-?9@#%F1No|({F*mzjn;Rfbj5dd}HsKT6dHnBF(^v2=G{|7s6sq8N0_aGCZyFUMP3v zff?>jPjz3~K+eY_$W>?)Ux=XD6!GtkP_N(t1o$p9&b8Ef531w0$=~a@c3Mwn_j|#F zYmqdW=qF<8|a#}m>YRpl(mytFV^XM>pOw&%d%#6ch}Ze&;d24&Gu7UMdvW(Cdw{UDbBYG%(~N`JCi$*b6nRc4T2hz zXD+jx42bj@3=cl4J&!oRcls_lazpb1bVij=K4R8)a#}8)^u9;xKtLnb@ys6yWs4D6+hT zQtVF}^BX>jt0uj3^c|nEaw)9>k$0o&2l`1B$V^gwOMTZXV_3$~R&^LPyGZ~|KMsIx zjn@-9<_XZfq(vPRl@Q|E>~}oW?3^erJRhNs?mL@Z> zncTxeBb5VJimYz~wYzGbXtQ_^U?kF$E3z6~fooezn4XC^|750&y}zfaKhb5Ax6^6) zEx+tT$(r``dh3HVooat)?^M{T)$(oPjE{afbpLUW44KSpR4A4V-LOk(2^<&Gt)A$@ zk9cSeAR_J_`=j1+O^l_BYr|dk-W1l#VTMM`7ljnnGf!|*wyj-JBQx)QL?9)(_;NRa zMZ$)6007cYaspA6RiC?Dj~zVJ_fZh19emqv0n0k^C8vH80nYOwQY8XyExo%vbvT)g-lr=e6w@Q8yC9$_xG0lbPUlN zSw72*?-o)S*j@YH;Ju^^#ZTU}?ALm4S3T}XWRH|aUJorQsw+-43WK;HY2Bv*LPD%# ztnPm`r?d@HaT?(7#bnq6ZPyz{yLfzNzlhmPB#Y-j+E1iQ>lmj?g55uS+J2KZM{R`) zMf%*et?>GuZJLkC45X_*qrU%ZmhW7a|335Lmo|ugQXiSH6SlFZGCXaCHZz)%#nH&r zI!eR=uga32>r@85!}0{gW9sAr;rvTyJ6#)l?_HWwUwPvQr56#YT9a>*ML-#ykVTkkpZFZ4&irRX6 z(}uiAa>4iKHVJk{EObmMK)M<0$xXBt{#@!zA2qUQpl=!7AbNCKl4?G`Gk~PxXSCMB z69BNqi)v)n49br6F0$fUKm?j%V2fEbE{>N47~YSm+nbB9Qo5{33m={gT^F~U$)@en zz};tQTQPN{Q`QT)iPQY~G4e|^o5MRBStz+=MZTQe>0>8tChwly4Ml07rtk;rP)zGZ zPxrp7*u;U$aP<$)r6Ien4Ep*>dqj-i1``Vatf@Tp=+w2cBW#h`5(h-QZ$vkLr?Gfp zXxI$B1Xs%eC~UJI4JPZWMq=jqnx;fcL-hmL)$E9SD-yd{&yeOc_R?ADX|n2=*9zm&=^y%at3$BZoUZxHYQgBc$z!3a=(5#*6_)h$!8Yh_4L0|_ccj`d zoa^HUkO9I0&zjLp-fDJ-_uK8`SON8@fvH>cmEttY4Ro=55Nn3Msi;AG2@0jqS67!x z?&hv{H4sy&iyb;O*u_DM<~$(|+@3jc!bd-F+}9~^Cd3J#;8XtlOiq&u`8zG5SM`&o zq`lpVY!kl)w z1-fCCB9c5=^UU3B013Vekj7qbyjaimCW9`;$dE|o&_j%hPLlQ38V2w6ObM`ZZ4%dZ!ew{jGn;byOwj(nKclic zU4pC}W8iWw#J1j@ZOBHFz(i=LG{C%Ena7>I3PG~ByeC1ss>m<0p_{h_bpg&;5Zrs! z%BklIQ9olOM}TW)GCd9)CIFEP5{yfQR6sgT7sdBp#e!08;Jn#J5fGOdT4DEugT9pn zb)Vm&v0Aa$bRPzJcd{^{S8Y@IB8Y&46{=jUrU3;`=Y=xt{V}g zDHVz{Y{*Eks3LEah8+44ri}`k;5jGk1hNhv-P{M#%hAQ8UJs7m0tk>y$ltrliI=qi zMk#qQQ?l6`9=3A2Y5D!Mf{}$Fjhsx`eG5zybvp=(13fN3_Okag(Nu0cOcsEvRvt_Q z#MP~7VA6@HVqt}%x;tHYPb?1f&3?-whfG+!cVc=qV1&1-L7Vh8%6bHu{C7ewVU;*F z%VQ##&;vklZ#%l_f^r!`+K%?qmRxgM5Z}8zRr7tpqPkiLV4!1HJ6+nKvj4KB=1l`E zzJdj}eG{#6Vgz!&Z+o3*Vl2fw(u)@6(=nYvTI`AEok&83qDR*2+FT?X?TiJmAoUw| z>dQHDa%>a6`c{JR;C@X;6<-4AjbvR?`L+s`nf0xvf1D2@1Vv7xfS5 zoIuyZ!!`0e1=4#WzM!%-RICy}dL|2pR!zXdqH}}IQpazNUxUTB6|D;<>v~l+%BmN7bs_SzoxN2qrpzlje!$MBx zdTob*cuR^>?$9gV}F#bq>fLcAQJ?=G+x?^V6$%y*PU(Gat| z>cu@$k!`Bd?6;_D!Xja80^g~jtQdJX04b`25zbQ3NLSOqX%NZGIn^1hn*5|xS;l8g z7qLa^8u|Eb2PsyW=GUt-46+z za$jDr!JS(3^25RuT?f=^WPF`MLqbD$iZ;U_IUyeQ<&^eOE)}qKV^ia9D*Q;de%yBf z?0|CWo}ZH59cw03LcB(fff<#$-lw;ZjEs~(?aepuz9Bg|g@ztdC_q{O4wr+iIWEPB zR>u0sS!Aee*pD}=J6N0xW*TDD+4GUS6o9%9F*74h)1Ip~&wf@V|6rYP?iq>aQc(bm z2?)vnR@UK1*T~wwJ~jY8r9#QsW7%`eeh^@369u~0Eg-HK;5?F{icxoq-$u7&MUz5*fB%F}QMPR8*!0TA%D_g` zW=87R7>AlSaMahGIOVzNRYO=5=&pzi4&P{8Ag&`w>T6z**}E0!l;Yu?t(UKTmw9Dt zMt{I+C^?}^-!Qyc&b>Lmd)_?UMVU7pt%q(HLLiX93>ooj}Ce z0Jdh?wiJT`E@Yj1YQ0cL?{t%da)VS^B!h&pAe1JsH>G`zYXj@ZW!vESx1F*-ak;u! zFKFlnQnrauh1uLE0Z>?mdmq;O>*Z^_^J?u1*97tqzn+j_ptSfCj^Ak7u-+t+aFKTG z;vvdFm*M5j>q+t48rNYUScYj=ZbA_HK9;}jK#H00qO#|#W(00Jcf`>*V^Fk(- z6s;kkRJC3Gt67vgA&pJ4Yvn4kYa1%VI9x7eqXoi$3>XHUr#W`nUib3wuwAZ_H(OG0 z>KSo&_vB6$a0i12D(CoR!Vcc3-X_HW087HD2%SEC+M;2ryUcR*LpCHMQvh*PZ}#Zyi`g^^h?#O>Xj>D4}D}=8W09N91StJg4(iY#uUWF#l_u0pniT9$@|7^7LESy zIhGAl&KBh@tK%ah%>!6dhShzJ=7fEOnQ*Qr9tL0hOx~>~vs<~KTS(p1U>5w0QqArn ztC3T8Qaia9b-bmF^+vX*o`7{mC@oy1SB5mLBIIo0@?CSc8oLpC^8I9irVZrsWcbxg z+S6Dz1Y}^Ht?ou5l{mLvIcykwFn1O}Y`rAa!!B>x;3fh9)6EKivH*r3y(4!=FT17{ z1G-?Xax3_-zrt=$_J!6MpGMp3#I9e1P-kp6)_K{c&}FK@rpp&tAUEjW~KD1$dTIx!kW!`; z3`#X-+_x9DQzI(T3uO-Xlt3*O1wY$3t(&BwGLNo7A5)f_{m=tlfAVAKm#?(NuoxM$ zA=c+}zf@Exst}s>Iudi=pfaOQ|Z2RVy?34xL+_`ZQr z4U|kvcuO$W-rERfYDK9ZUbZD?DT6}F8}^D2_>$ZL6iRY4Xt_z&VX>>s94Y3ly7*HL zF1I`e_3le|T>uQ1LHjUT^~ANGqoacvi_vy0hpN*yHtz23y4yFrYr+aPP~L`h7l&_0qdddhr8T>hliKtvX(+OLzl7{ ztS4)N^u>0p1`7^#8uvr~9sRv9tP%!V9Ds&g05s!Rdcw`Tye{$X!t<^6ZLd*f@L(BE zHbv0tdB9Zs^-3)~TR3*U%s}mQl%4V76!0H#nmvgkisw69uuo5CE8bi>b>h$|oiD7f zpJM>6iu`WYXLhdF5;XfNuWQFZjWNm?kta-VI5lE&BIzi&MM>#eT25|C_U=q;eQ;IS z7rS`X*+N3k_wWC52ldbgToTl^!gj4Ccbrb%!JqVU*T)ey=0CfNVx*&+B}uz!Xf~*; zsg=&CNjmxcXZ6Wk6H+oT{APHmjeRe|yx@E@ZOE>uh0Ym}Chve^}U%2ZtZSd=@%d z^7etgWAg6uz0x!L$S3YW)~g++EEAd`V=3rt)Ys;EdnnC zbg7h&X%`%%bODuTRH5PO$-XGMMOBLJrY4Lp_j89y z?3*ENSGlbLP5S9q?&iDcUNwJGypC=dA$RtBI4n+Xzu~UE+wj@@a`@YDZSX-iQ&{P* zIG7n5x9xu(o(QL&rN6y4A&2xkW9VbKHmgkEcC(obOJgSs9izb8$5U}8i$+QFZ)M1R znyx&4gG8aMP@EnNgTbVQlO{BPD3$Iyj7@T$uTo z4f_mh>neu^xroRKCc8*wE%Fk6dl)rE8EL7o+qK3rdxz*?UfQW9g|w|(_P*hR1-T3k z>dNFHf<3Ny;vU4R^(^8;G7{xdmY9n#E0)y+W#ls?>3C`(Ed-K#Q_^6o>&HP!-D~$}QFv?0eY;78QS9MC zahEZchC{i|ZHh@*qjXd(tKTh=#+7hV4*DdX8jTq;(`>2br5#b-twxgvm zM-n*F95H6JkTo6$b67TvDrK`)QD|1Q)IZO&vP#j(tjN|`sAig+tlwDKu;D~`@p0kH zGfHy5XXLJb4v4`UWWf!TAR`o9A&>xahO+fsPf=i3mYcr#*>Q*zb2nT{y&b0|+S?0} zjbb|)+Te&vcY9^%FcM6CFW#VTMAOz=zdWg8=Q9gr+WV**ezqBpIA6%_^{L|ROqb)* z%8V0JmCH+^8>j~*t=!y)AQ;e58j__%Y_4+{jvzrxJ5j$)i`zN znIZ}JI2J!|m;ot|`jms`ad{hRxi0WdUTU=6C?sv zyHR|)Za2&w%ob)@TVIq%DdHk~98x%}%Xbe`?rsts5Ep|SckJELD8RiNj34eFyYPqE zu<&*|jXuq`-2n>u-sC~>sPDD916Yk{oG zgf*{OCnQtX^0oZ-1liP9Z&mRqMutw=xs*-Vpj=#BEP6I1auK{vCJI_uKQ{Fr!+Pq$ z+r#T&p1`=W??uAo4z^WjzvK+YPVonJRPK7bQd* zye~As{5%VZ@hMBshM09q%hu#?nzJf}Gm5<%{Vj0hQl47!X*YPb*}zAKj4X70Wdqln znIiR-amOZ29jKT}0$d^!=QeW*_Ypql0fbtwvX7(;ks3x?x7&*iAq7UTt-iPW*~=xg zBVegfhyR2Q*E`W%H+G+ma9;TyxTJ+Rt@c{e@`+cNcCvzd^()#UKy_Xx!j_4JZd1^) zWYl1ZX{#sXfG^QU7464WEs)?im~W|%^L#&=@ZOMp^Fm;C+qlTBtyo2~hKD>KaZh&h zOZXw7?0JZgzJ!-M>X|j2yL#ka>f%#;t+GU|$x<@|T_La586y3=TPHBn*ywkb=FZqq z-NomC9LFA>tJjm$qpCXN58OJJ876h|juLcuLB$&D$Ow8VtKQpy;@UO`vJZekojKz8 zpIS0t9R43Hpc|^lA)?z*6W{U%ajh->u*{#phTdEDIg8!!oGViDsZ=R4*2v@|KdDBm z%We0M*lPmnMHk$DWA^ijGj>P8xn!OB41Dg#Hvj}#)%Nug9E{L?rDVhPSx08z*9cj}3#a7kcPNz1{yX<@YYz<7oTT=!(kA7ohG)PpZ%QK)ch{j-$bADm6_W+t0tY6-k zm4WtlJ#g^wWVb7NdpL%l;Z49lWmii|;b8V;I{>i4;@&mo9vH|SH(q}|a(*TycRnku z7?~XMkVW=#TudO%#dS`mrly;3>nOcf+665;b1WnA{3s)dHw^B*B@9#W)751e18QVT zcB<=E;d&U-FvKER*Vg1R>a_*3MV@CXz=;Jmpb7Zk90!)Sx``gluy zxs!!xR55RIG~31EvF_@RkYe6LW&NJBzQCz^wLht%^XELhGDj0Fw(x?ZL3Uc?c#3eZ zv=6-2s_wobg;y*XVAK6AS;u{^NrvetLx&pKWrVAZPJv$9rrW4QwF*Wi-pJiebNk$j z54pu=nKrrov_ z6CTex-Pd(En=asYc5JwS8YPC}yj)%}aDDFdLJ%ibdfq{-ROBHu$sAl^Ec4OsYF z3E=ael>p>p4CwkK`+9G10FoRvXpJ^Ok4WrRnDZsznz0i`L`jsI`IN&IL zxb4?lV%n(S;okUpqY7hxPYy00F1^I7{3S>%N*nJW)w7$659AH_V&Rd@_XwR2?gMz1XE?-f@r^DePpC){M z|K2j_B)+Y7+x1{+PeIw97qyos3{JuO$nAVOvYQ;}J8xm*$REQM%IxI&vL? z*MfuUrE%U$ahA2U-wWNW$Mjf0S#Ek!hV+;hQv)NlbGeopq4^_3twWk#Yk%e=n4zbM z%ZSsn)VBafzdNaooX>oz8MtA&&JJ8 zP2Hs;(yZq^=*oK%C#-3)`*dt?eq_TB2=|g>?t6uMg1bADDLkAnQ#~6c+i(vn+&Oi} zAQ}Qh+kjG?ig_p;ovE_Bj-y6!ql$en*GUmZB|cE7vrm59;(CEWDMEB!X{0N`5f`f% zTxrWJb1?W?D#YTg{vjVwTt`Y1%awNCNXf5P)sw~*pW?OGu|C0{k+&SJSi7Fg6;c=8 za~jH~obR^ZZWfk(Hz##saA0@awrq3LxKg2W!nIX`b^<+duu-NQoL;eBTGCs8mRHha zF!Y9JYxVW!pUi|eWENP1{Min++~F<}AgFj$xMzL2!Abn=m~nG+(*ryvtQ_^|H4S8; zJyKbd(Q~7y88K;r1%9#uwnw2Qn-Y^jYgc>HZ+j@WoZ_sL;iNzNk#7`+Qt42wyQDK9-voPoy>lkM~iDWB_&80iH-7$4$iiH(wCK(7sM$ zIn`55r@$z2vus+H3yUCMmBC)=>ZmL> z9=*hEOc${uE`a(ojs7pK1Zo8fJg&;u+ZN2%tFy=$K1)`IZ*(h$Cn{sX8LWltTi29%hf4Je$W~b{0#7_BK&#$1ipI@jz04Y2b3P1MfP$p zz)5c<$6@84L_K=!%^Vyy0GQITeh}v8~&ctX0^+7Pw8Et2rAy(5h77k6j!gj7hc@u zrmRWr!Fgabmf~eQ3Z$Ik4a@<9zt~#i23UNBxl4;*qZu2-ba{g1cfqrZI1PYc6RH0+ z{r{gIWx?SOx1=P02|3OmAE`wto4q8GznS`U#aLeHR_1jLpY)I3cPC=O&sI&pk6t&@ z-FWZX^7h$3FNuh6N#1xcHu%O}sDzv8+pfAuI*^OZkclKcW~ay)~%W{LdD_BLY8OfQXh4M)T4qjm3!(a2=| z94nDJS}y%l2)^; z&35>1;I?`m{xG5E-xB^ufu>sOszE#QO&;m&N$fKe?0&lo1SP}z6nk={p~fkb?fSXBl1Ojeim5=1pA54xWWyR^|Pz1rTnf48{)3> z?|Z!M2>mw_<>ZK{X8lV|{+!{>@3GZ{U(P>)w zch#m$)Wn53sIbAhc>F}ITBpVkU%ej&fty>6QMsHWD3%(zD?xb`46uv$Ye9F0M?m^u zMzil|-{jM5a@2d`i002hv;9nC7ehqi&c!*yn-mWvU0Bn-b;Aqv8g^ZCzc6Dv6)9(4 znv@5VsAfnMc`y}B7?Dv@iq=U4ai>IvF(E}lD`bB~AmqynSDV8RrbKXB6WNS#H_4ZcUgl+|4&NUM4!y$jG+b`QvM zM!uxPP_ec0@s&RQtREY?U`xARc-dFkfo ze9a+jqCwgG;P63=gA=J`Q`0WR%{kMJjdkevD{`LmXB@)=cGMG2_}Aoi{`JHi+~$Xv zd>;Y8Rx9@Q9vib0ct}VjoSZC3Yyg9YbJ+@IC2E&&P1$cg+ug^qT2H>J__=-0M?^7l zLCQG?il<^s!Shun-qV#q{9W_0oJIb(ROA^%I4bN})$GFrM%Sq5R5q)(vJZ`q%t<2% z&@G4hg&MVOsoGZb-N59XuXc5;>)Zim4-A)Wj69I2^kAxcu((S>wlq^SmhxJzxjC?|F|U=G(8mBXT4B2Hy7EA?J!<-4uz0PO0N0h;m=Zz8Dx^)V4vN2ni>pjM6D} zOKoMm%4P<}fN^?97#Q}e1#xb{ezxqT_#yR%2>pH+VkvhbM@oW=L+@-rSdW)^a0O0# zH>rGSVF`Kt?D-+n)4lP#BQrKvw0xnS9GX6IPfbY7R)s=tov;S@yezsnV!f0_P3lL- zaT-@j^L8aYx8Id~KKv0w^8M=7ms~&OIj^2+zRJ1vm$ewpM|(w7kb!}OgMJPI?R-st zdt>`YB5axM^YdbMnK0xpZB7tQGOI?mC-cXwS<$Uqw|276BpE5CFG{~Z+r`ZEf~g`R{d z?R>_@W?#ggtq;^5=VN)idwY9@wyo=yLu*g$CW`Z4o{ck6Eb-~-Ss6AJnP;ZRcBcx~ z7L9d2b|2LO@GS@K=3431%gR)_S#H-|sCZv=%g;xJvp|}c_!K3#I<&Ig&0D2J-cPKM z^yFE+qs6(jn8Uw>)h_)9Ew}D-KK}Sq5_#<>sGLG6oc&E}Z-nj?j6n`0_t}@w1DA7K z`uLct2H)dy?&a-)pH~n1N!NEa19Bl7qDuyL~hDdt-S%rgY0JGVusRZwT7tkqoQvIMSxre8he#8OM%+ZY*T< z);9nb=^>pg17roi>Ki< z(p1~o3HBWpeGO36kU>eeI3FFA7R&(c^SS&%;gg{XeuXU#l;Fzjk3gxfU1F1ASVL zpYO<4a$4EB9ha}C_~Fg1Xm<5x6pAW$sPlMPDJm+d^ULxJ08Escasn!#+!i71&?wJK zLzyJ@zUw3Wh+1+h*P(Yt@7=@OvD}XQVzeJiOI_I2(t;xjDNWMSLRk(Ni%4mUJhx5u zuvq2SuOaO-Z9#c&OH9+ugOpV~F37#Sy>F6J%${T`J+@l;V8SdH9(=c14=EWAa@%~j zLPd+HzLbde?w@~XnkI}m05DgJG#ON5v(isGolncHsx$sY8N|dCOUQD_Z%34leSN-g zu1;HjhN13o9;i2O)uG6~`2|5-;b~&u*IQ}Kr))tT9V+kx*8no+KNF)T+9Z_OY8HHP zd3r$raO_Pg=l6FRA+W~K0A(|%0?aGh9)Se9DrNL!#8t5W2L4LSlO ztL|oSd54Z7WdU z?#bfa^i7EcJP{&*UGY6aSC|n3s-Qy&;iHU5o7_D*T3#fc2Y@DlSx~)Ps5Bw|Z2@HS z;U+R%T3KWSQa+QaFqot;NDfqIemY6iz#u$rwv$0grJu|0{B9Y$dE~rvaL|ImbUL#J zM8~D|v^!EA@1)+legaB5ihtwDN;EW4z4tG^4aj0!+h|z-r>REHIGh zYzS0dq1R^#+m$E+A47%+Mw|S5Uqn2?dD)4SWVYzf^YPNE>mM6F608mWy58_fA1@z3>p>vo8&Hm6_qzBJ`Kxit|Z%>4T3AWxlv?U z)70=^nI8I>QTfqI1<$DzQU40p-9P;2LE)R@nt+-xM|j2oQc`9=t%m&dmYD2j0}2&V z2=+lK5N`2^DCN?0=TMtODs8d6guS>C7{S=m+)U`;zypX&H;;hoTp)}2Sh2Sh;WnQa zgL!mo*l5)zZIY@(}@ug?R*;`!*}B@ca)%S|7!;ODm& z7MH2Y`I!kqT0XDKuT~A742S1+2uMLS1bIUzJr7OxMr-Z2TUQ{6I;%6(e4A#yiE*0O zsXhgxq)R`90J?E{_hSm9)D7S&XXg&AjN_a|V?xrCUnQQ~vX|0!+0jwc`?`8!5|T`_ z3s{Zpq9tJK^zN1iG&JD7%+waY+^jo3Wq=*6s2RW{JrB2uzP&*Hk`|wlO}7~VZ{sdv zkYjq)a8}#75uofeRor8Zx{}sj-~zZ{U6w;FjV(|9`ka`3`rD8t;I?#)N=6R4C zRSvWxEF@eM%cZsQ+V!uQeDSF47B0(p-AWcW_ZmJ~65rn|+wB*hZHhG5-920a^x3r) zs4nP*y89=MUg@`1cJXRV9-Eu@q@UQ3jY&w5F`bJO2+CApPjo@%NCUuVdjcmF7ywvT z(y-c#GJX{=4-(ci7FBHvwnhFtcXIqXRcO-AKXkX~?{x9;C4q#F7Q-(*yOTvNN86W! zergjZZ=MJ1qg%rg#4Wi+4HI9dZ8slLT6AT`>($tKGm_4o%IwhE-QRCodvDp~-+K$!YJ=R{X}BgW3}*E3@E8mV z%`~qryIv^vQ}+^pIY9P|@hvy7Q?5--z#2$7@1KKyOh#JSW5ljbNBJ=@kf=8d@zQ3^ zy7gRll?nK9lY~?ZCTST#fK6 zl8l>Y75<4H<1Wwuxv1o16yPrWWW3!7I3bryM%D)}18w6T!pSTvefIrlb)u;GZrJNr zYT$7O2jD!hZlP7mEQ_Wc{^H6PqgD(8J#eATMt5Mg>jGDW9G4{S?UvV9NkyGg4(Crd z0Q$6}VmZwf`Zv&JC>SmHKz#%R^Z8ef+e>BLhb$@re;qts_5TX(xr_XF`ubV_v=p*R zakNcpkaQGbf(%)DaSB9}Og|b5J&%k^`R47|0du8D@59R#i5SU$#40>5nfW)?hH^#v z?;HnFZOFNQZKr>tU*nZnJx{%d9(XiZpb-=c-iF<pG8B$IF7*+w38j9;Sq>5N6}5x#eYOhn-BdTXS1m zV6kOq)OM%9C~jVzba#K}o1-3;D8t6Vc zGBF6Xn0~Z5Dd3SZF=4dCZ}bsBFIrTP53(PNItihjb*lVrkguI@lxVIT#HcFW8uuRa zQ?pVCXNkI_pTRzTzQgq15Xt^PFw&cd;wyloQ!A;FnI_TA*PVpjoRFAcLK6Wx2~jBE z4cl)*p{eGZbbopsw=4tf#dekGA7ku**3B}|{g#T~&FPCMz@#Q*DXSbY^XgarQ$4As z<3^KjSk~0sJl}*2D9cYLrX;N{bGUWo{BuR!sg$=W$0#eS{s1G_{_vY<|DJU0xt1Ve zJCVSB`}QA)@u$57<<3wQma?r;$Bs&FU6O3AYB#&l;>2p$(QJ>2;J?RbCG2pS2aS!R zcA}#1G4e3*jcYiZiygY)&AoHCEk?laCV?OB?#^0ta%uLaWNEhS$hWOcd?$?M)~Vt2 z9kTiHx=n5Hmxixa4p)k@I;%8qsM_W@?M5`<&TdFd)6%2sTy1|nfBqZ4^DG=8{s}mr zFF5JhSGCCZTyZ$d>Br4qFSKubd!}vdwv9mrMRSlkdB9+As&)8)na+X@{bFNd29|iT z5FD<@yF5)#6LLK_o|b1`2VH8$O7KtTDZ0{IX)i@h61wv8TfLk_eYLAtmE!APKt*9u zCR=eyiK95<{%U_TH*N9xvcVBM938-O+#Pb|6(Y4ClA3+2mwS?{%aJGVFOMO-AY2{J z5$@nnyG2`1(bd#D^jnW?58GQfE|IE6AT7gh!sc4Jw_6PB|E2FduBe3mt}3-QRG=__ zdZ1rzCPN$)bp33~SqUU9gBMoP(V;>4;1W$H?dz?kro%0(jFyFsIHPm~1PB1r@gb4j zSkmR8IA;ROyXCtJ`#U?|K-1MEOOkOhf0lq1#%Mz9kQ+NKm(hGs9osSO?&;}ETz_5_ z(n?NmvR`FJcR6oxEIS}hZ&r|-QqXrugB)X?Jue#dh|enZVNV3u+EFOTlsYLf1<6A; z@tg6${fdGl1$ZB(yZayhMvmoNt^R$bpsm;?)g*x5aEIld?>Yx-*D=Dq;9{4dS9hYL zbKVr3pI|uSd6%j^7{4AH|*$z-e#tG0S4c|((qX{$8{4dRPRZ+FMMcBkc>ns z%cQ>2PtG?rbl0Qf8ToUqui}}4+xDccl_eI4BT3_U*&jWLmB5-i8grJ!&(E4QVlc_T zP&rBRg$cnecXoD6f$@>>68XM^Op}XPiQ~h6sC$PJ>8+Cbe6>D?xXUPqMTON^n!dqq zyT#fr^hL6=G0CZ&4d)lG)=0r86~ks96?XQ*kZ+>eIq$%9gdtX=QE!c%=u)3yA^v@*It6(-Jak3?L6?4b9 zNgV6f^{qEo-Tu&5=JL}?MjU0Z$MxUPc7)PZ!r0m9DzYpVQ_koNv7uoM9w-o;pRVIU8P6b!|l=gZdw`UBgCNxq@6q3ErzVVup@K?<4?&|Uu<6j<>ffQ{3YGrkc%*p&-KtE>k z-qlO5qTd1?&C$1Ru?smHG;ds~>jvOR)zSI@BWj z%KFV)cgAQzxYg0UMU=2pmL!22XB~ z(Y>^?TDco$+xP7Obkh6iN%z3-*pM*{Ob?SZY5!SunYUJkPJ50NOyWD)=IhTD1=oEkt;UKU z-EC^3lL7M{tc!7$s(0p4A8ulx{t)P*0WugoOvLi01l-L-KLYR~Hs!URv}iTj*)mw@ zB&g~>lPDegcQ2&UNmLGmjlfBZ?db~(2=s0Ye)qM;VxJHlZaXl`zy@nr*;}PO=KLzb z-Son&tj%3rT_W3c>uM!7i=9j3Pi$4P(bizFMS?)>@n;G94LYot7JI$XIYJ#2d< zD;Oi1uZkAwcC{Tzs;sO8%gcWr`({%6`64Rn4ryxygVeByDmoNY-Xjg&N=f|s)l?Kn zDWr&-UBUudkDq&W3&`RpN->*R&9jf|Q)R0AP9`wh%-F$>=q&WA2y}DYTGZyvwB0~O zWTod(A=tzu&0}ql%X#J_F1|Y^HP!fQ%hIwf{CJiqYtmi}K%DP_f_p>^k#=T4)(-1W z%brNZq4JoCB2WT8G5T3Bkdg)0ylxyLm+x>OI+D^Fde8L9NSa{R79R3sUXu)qdtjoN z);zf_^;ZTm{}kLRvqs0o#88XAAHUf!QR(~>Bo=Imiu>mczevop2$_K9*_l;H+5-_0 zzlsWR$x>U?+EDI2QLlfudy`sb6$14t-(45ZQm@}AqF|P8xF9^GVrEwBO~ZT8xBmXF zaE(rKL3wUYhw$syuMNQ8Keco2$ao%dkB)##1Wjk2!jEyDTVI0AFlG5MeCwc{)6jdu zC%}C%E8b=}O!bi|aQnWWpI=`tgi_RD>JJ7DF%m_uX!H_(7)kYBb#7ylK3xGFqcS2M z!v;BVhYQjF-nhQoc(yI#* zEWvEr5Gg|wa8Tql{Z-lvWpuEAwX&VUVn^i@Vq*LNch1DbgupK_5ZmBOsgd!5oB$}F zZ#_RLVq<4+2J?hiC+%+xMYEVMqq!yN)z#IVoSXoQe^TI8TwKiWnKvX?2yXm*J;ho4 zH_VWwGRh3cd+-_VF^2bH~^b)i=H0HRV?x+a!K(peC!G(1L%KD z=DUi9Jhq<@SIf@Q6wG^w@IEDe_UsvL=PVI$w`xyXcaw;!rj}OWDO9K;SF8x+PBUKS zQ0;$~bYj|Dx3>D%R?zbl`F~g)cr@A>w^*yc?W|r8uX*Av>7b4XAA;5SRT>q@pB-%s zC9e(rLVq_ljN{kW{5=_qh^Y4=04@)znIrS?Se9eat%bSTiDFK|;wx7LrKK5xW0W{a z_gjVahxANRuIa8WA_Ra)WoBmfyeO9)NnqgZ?UW(MNRe5 zqeqKT*yZZIYd^I(g6BKh0C19Q1S~j^+3RbsEI#!3*p-ZBmztPZNy1}~jg{Zaeqhnb z3~3;6x{+~dE51ye0~;+y#qNSstr6MI@%&!L{cR!+Q=iQg0*fKNX|K2Hfq1`XajDoE zr9=P3Ui03tm%RvuD1}?> zrYrJ->S|^Vfd2Y^YOvDaN^d?+U|~S5^k;}WFOiYb@}*mb(2%jnZWLn+4Xfk+{CN(p z!BSL1x0g!P$3lv_8KmV?Pwjx)hybJwj9b$i-Hg;GCyl# z1iMT0-2#1XAO4L&ZNG_63&ENuuPp?iw~elRh#TC1t2jFMznP|{h^?OcViSx|qN{*} zOEq{Ca`PHO2wqoB{&Q*TzSBkG2R5lT69&LEU-{fGtu^d)Z(a*D+pDQoXb&?>kj@sm z+Ny9e_o$F+VCm>aVJr{!NUj+Gj*5}e4RndqhC`K9z$IfZt+nz~R8-m^6a+*-8cbkl>E;O1Dcz{39D{HuY3Ue3K$-y+Y3c4} zfB}Xc80x(@`dRB;zxDq6`{Vn*vrfQf&)&~|;(o6Cx~_Y_RZ@_mJV|>Jg+fuHr5`Gz zP{%%_P)BC|Iu1v!duXk~{|-4QOWj9hH`C9;FGn5jqgDTckLO=c|3jfJqtFlUsk%nb z54q@ub?w(K3simoo0|Hk|MW1O!&C!~pSRJO9%NfFOo4>u1@Ru5IEQS)n+m4vW@FP2Jcr@d$Y=CW zehbo!-=BR2=Ff-z{Cwx$VcI`G{(J8K)WZQn40k1!m2b+R(T^=GGCuI!u6de{i;0Pu zpPzqfOT0`$;UOd_nx}H?_me*NVG#-L`jzdkb@KuX8=JAvm?yWhvvWfzM-e>(`r(n^ zXML+$l4kiFj5%wZt7#?tCNVM59^Y)#!SkoLP=T!%=&7!4WkxA#$~HKk6=r4CHc*I* z9do0iqQb4MaP$RG{W*1ylkqLvXU+@nF_^PVbDv5a@b%r!^xwaK&sfqdIo;Bets*7j zyja>Y^XL50IO0dw!@@#BH#(foojX_HP#aLzz(7ymm0X9#+H?`cy{*{yaKo(p5rB~Z}#W$ZXn}=0L$6HWX zI74-|-8?|A%-Ld>>@>H(gL0bf@6r~ya^)VA*Jk`&4;G85exLqe+qwUXH*VciQ}feG zi6C}_*Q>y!$%22yJWYn0-E0(_b6r$)P@7ody&KX}trgCtmlx*6Z}bMvwX9y5(u)1l zmnhNGLb1;kY2Lf>RCDxow>FbFI`Ier^*lOINMHs`s1i&36Eq> z#ZDfpx9~lEef_zvvZBL>4|ifssj{>SZp$-CondPGsVLDEtXQbFdwjiM6d%`>>XPG^ z$9CsW2lmPY*na+o)m`W>=ty9ZHMX_QiNDC5b`R%HHrsJ^S{$s_Ce3vzSr3+LRllPS z=8&Bfs>Xfv6N(FOWOmn?&-@bL93JpiPg^1s|%*K{lHm91YiN<{O+4r{8f<>ng zEqaBGjg5MF=}Q>XHsKQ{I5;bgG~ag(&8|}z?YLU)hdIySHKdJSvF2tz`uhQ%OSMVd zIkYf8o?y|1#p;&W&$2$ZHg7jQ=QO$WWEbx*Fcl_I$)#UzCdOq@@$u4PZ+h^_i(Hwu zrap;cWiA954|(!DFN1`$oS4UYp7_*F^hK_tbBR>79D>)SbqcN82IIU#-oJm}?=o{4 zy~0J{M8jbE344*h7FrKwbYV5bI)(nsP$5npp2w#ya&>Mj3=oBj9PkR9y2VP*o;^Ex z>Qu+tY)6OaT14#DDju0hn6Ks?tejeT57X1lix$>Nq@EwIPdUvV68+m9W)bqefByN$ zyy4olYXx>=_qWJo?Z$+}!~(dcb^llN*w`3J%voUzx_M$^0)oqVJMp{6PF?Ucn~;>0 zv{*?(W0Ky+DJX@?P)53gxHX+#*Bcj2k5G}2O4>#~`dwJ{>qvm1v?=O#OS9*Izt~5mnShgiR>Myd5 zW4^)1r|L^V)&23B)cL>v{`{os&5c{PmMUsnP9SmHndN6~qsxeR=uNeT(wvp5AQ2ZE zN+zDGRjWnBt-(@xItEckO%a!QB_+Eci_WwgOiWBu5UJ(qq$ntwqW1IeSP!NxE!pv# zw?CX>^!VjI+}J2vMq2D3MhS3maVaeH7xkaXSir8G*bpMzpxN0V5=`AD0uzy<-76m@$CBL#Q_W=DJQAf>jt)K0*-KT zaW%w<6cvor_|sYLlGl-i#(YCmR5w*IPTilDKhp_jztd#hKd;}s$+-Xer2D)HBT>B! z!y?Ph&Mp&m$66WU0T(i~bCimIoaI4~s!sS5uyZD9}of9Wcy!-1^PkK~fy~OF}=H}T7$6ME4pSq|{ zVia4{;#Q0mo$D-dc!P&=Ow-4C=ty|Dx<0*e=J)fDjwC!zlqBzxMzT)KjQH&9iHnPO zA9aMyB8=N0n^DYtX0XITyVNnA(P!rw^tY^0&4;$loHBDg z$DlHM72kq|E~7HGX8P(ge}UeK&>foSo0!p)mVJ_%W7<*h_w%6`G4AVgH(>n2xee4^ zT@S{OyiPFHtd#zE)qSx9zqZz)h`r}~92Seln{}j#LuD?FmlBajfQxo}#V;%@@Oy6B z;YhY{;5S4qt$rdqzu# z5CZ}z;jn8W;!%RUc`A`2_ApXIcj*IWvVGkpkQe5H32r$Xj9RS=#0!6<#n#GWnr( zF>lT=WF*Umv%4$~M)F&Bk7U;;&M}o8tJUB&sjII?W6smjak#jUqR-rD%qxwd_}#$g zbi$4^pNSRQ3h((tLBn(6R3}sVfL?Yw8 zd-pI&AtC3R;v@`m%2(cXXX&KEbkkf7cUf6mQ|-!7E3g?7cJuIPY;5Fc+TzqHybe2+ z`_6JLa)qw6#}7EvGhP36@cguwdtwGpA)2!&=6>BJdoWVmt8`%V9TsOj)y=tE*W<$PHo7%`fx}3`z@jUJLxXFoK3FQ0Ln92( z?MYbK@L*^Rz+BjjdEDIG>YJMtj2pv3u~_VSoSv?(YTi>cEFl^Aa$W#oVSy9CDZfRh zG{m3aqM{<&J66|STwJI*)aCZ}_F#sGM@L7i<{CbV5phun6x-<4(|LBDTVEGo7>rH` zU|A^5VmJ-oDdNyds;&IduE|Vhxc-I?9N|Ty323brQBiC zm8dh`wYIj_lX`^Vr_@|+w(n5Pjzxw*(ZiaQj% zm1AH4`-`ymPBDZB?9ZPM8H63B+uGVPR8j+Aw_!%#uBijSfp3Y^$Th51#ko86_4T1K z+4?wk35iNR+u9EhY2)JJw7;4?;T-t##1~={yQwMuX3M!htz6UqG-_>23`!=$QgvU= zD(Xx$RovR#tb_IH73-~iMBnvFqmhWmKYwNK7oW8DdA_@ED-P`oQ5EX*z>gpTmKn2?{JUsJxn=M`vaYRX38=f}G{ zIr$U`7d4k&`}uJ&Ge3j?_YtxNc8%=J<`@xK0eqoF7dvi`OqlD-Pd!NUY`Fyl1a={+ z&kdCosb}k^L7-V54 zCH?dhH*sETF?bMMMM}Csr}TMRY#@|VM;$O7O#YY@g_sOcw-sc6ggyvYh!r)@(nwck zLAQVRr9dzqj8Huc@QoV+8xa8?KBxi=%38Cs^FT)I~{ z{nwwdqWW>|<8x<6M^y`pcQ-FIKA`2+DBSoEmh}C#=lPGA&rUq4ENBM3M$7-oT1E=% zLpj2c<%^b)k!fFB7xQAfH_-~t0g;n^+QFhardY2s=Y_Ry9ot!GjYaR5cw{Gl$dcx|Jg!}6 z6;bL!czRzpg+_^MXl`kgs zrxC7y)7JnByOZi$T>bs~V`t|L2nSxHUITr7H(VA)NuizLOh`xw2@3j*32kD~gCwDQH)XeCo7$e`p~n6D=g*!^ z=mFf4=>D!tM*go)#%=oM+M; zoh}$_cN`cvxZN&Z!ggli%7e-2>9CG56e{oSQ9ii--s9<}C@fE+R^RD_c?0xK{AjwnLAanaaIY15*l6j?#sVd6$o2bTt_@y z@%h0O8QmZly3bZ${OaD75q;(uD##bepA_QsugAmXZVs^S?y`P-4&3dxYqsB3cMGg$ z+oR7MK`s3?(}gYa-keQEehWhHxNt!8T$ALDlV$mw8bW^mRTNpY&!xH} z`LSI&wg$^NnW*OjeFFoQdo8CrOJu{5``S?*%%UY}Bsf{9Vl$SOmXPC1e<9&FAV%=U z#ft|gE(!F$L^h|uBpL&6^a>3sC>{c%mynmcg5M!jF-7d}*0Mp)$)BOt1;7(|if`9$ z`t0opyU!N`H2j2g!0m76VDM0`lb);VH!&@!=N^Fj+XW1KVmh8cPai*F27eW`@6OhW z>bNrcPiKO$ENmjIsZ}V(G)QOW(#5@0;|;5$&rC5{u4kwTF&svX+6Mi;ISZhBsldI6 zg^goFL+gD>^(X%-|C}~+>Ghk}mIE6M7Q^LRNBqxGX#k!oFq~pL(V!Il3ePP*<2lu1`sGP= zgtpT2=QFue;z7kQn;CyUxH3F=@IoPg{nma1pAPI13X95|9La!7+3Aq4 zriF(COPSoPS1Q`7xx5rD?xlTBVplcJdnH%CmPW|p>s)6R5W+T0gw&GN5D^zHeRdgd-`cW@C;0am^z68;uI)TOL|(c!knP7Avb@n< z_c&24-99<`3`;Gg>)0bsol?~Ti|*E^wkvL#YD5o^NxZXss$*sC%Vv#50kSPQapl@`iT4dqF|D8dg0+3mZmHPftyMJ_*vi z>p<{gI*ZZgx`29+y}+$rv?w+?9j1tHK4vzHsh$nc`+WBhZi7tdFLlB~I%UM^0w|8o zwjvp!ihG)4FiC8&X7Q!#a}AK99Ks*|I$0==Rn5wPtlxaE+^y7Z>}7%HWZL8Y;wOn` z7)65?H@8+w(oJ)a`~m2sm))@SE5wP1&WaPdv8hNdx-s83*MT=LDJe1EF3eh4UsubY zZtD7c?+8%ymH?Aa3c7H~S32OE)uOtsRex4J@T1x?pQ!QA(8>?fF0f%i_XF+?*j*2I z(xsrFSfB5bh39;1A@eSPK_X?nNK8!FuJ!Wk*RK(NW}P6=nyn{NU`n#7uC3*`eOno$ zTIOtBKx`e$cKYxA`;LooJ~q39dBz-F*(y2!F@&8OB&zmRD*?C7^=Cyfr|Xq%tSl|H zcHU6Q#6t?U?l@AxuAKDW{g)5E{`P*hV>lL&hrL5%PL@tln(c@w zGg{1jO|`^+iqCju@^sIvf_1E?GGvQEIGnFS+)58zy(8LZKDV~B;(-+K7J$n&Il{OV z^W%1Ki@1sw(QBrr7a2tD?q+Eh6=pZ-IZ*!9im7(lzj@uDqFmT%@ur6d#R6%)D}Z63 zBSxyyyCuUhqP-!QEpVk|N(DHuul7?ij~+gh#>Bd31R`vS&l1H{fYJf@o zcy)?)7kf=tU;VF0p^l`Kl*rN1e;vr3H-e+1tpGQgXGwSu3W8imeeQ3&<{h`m|CUJ- z<{x;bu{6B5=||uL$D||k!VTQ(FB!=dW$S&w<_AZ>s0L#&hWB%Hc`hD4A`jGgU-7fV z`nZ;^VquYpLoFnMnoAvv^>@t4XnA@52Y#m&Tn5ipzo*#`W|IdpLCAeAt;C+?C#2O8 zY&u2Fj&a1(42EaVp8W;FN)rrf6Kn?ZK;+R#*hIg5|DG4QiLznB2`VZ9HFgb|4kA#* z^3u}(z5VaMTL67cey}k`efo6CVeH4Ta~Cg~j*pMa1=rHiXqd81OwB&=^zxFIk$HFG zFF{k7SW%sws=RLP%$B;T3R_VssXNGxmPsT5Tin*0XHL$y0cB5DEl=2H65ew|ca8E% z!SkIsefsMLe9e@Vg{AuFv13|2?pt}rv)NCIxb!~6jodT(zymDZ9dB<6f76dYreoqC z$iXf9zM2Q0ICJJsIF~+O&82+j1zO92ms>->fY=?0R(W1ROKSmy_!jXK56v)#f#+Tc zWJ0$?In<>|JJs(mHeHdZJUxJK!CM6EZKVp4@o_ZW2R-r^l>n>*etdHbZ{;o-i!>Vg z_zoJ4o(BGd_3~&m{K(@$n&;7vWi(%W4MIZ{B)1Mr-wvH)VzO0;7JO3P7*eWk?nxV$!VAT z;e3eucR)Xw9_-6!WoK_-W@C#oC5y)_>Q?MJ7p{AsI$8Rx(rIC^@BA;K$sODNr^+fS zO|TiAgT8`<{+H=Fw&B&%VYjDz;T~SxiveTrkY`m%l`HHv@B9*vOc4NT)4w8kS;fVN zU%h(ush{66sbs@vC$|yMW6kE)Rs|u!O(n((0nf1EN+@vBu+kpURwwtuBr3Jb@9{Dp(Kr0ER3yqtOSN+nd&wF)(qX zdV4i3UoJEo{ahNWG0goWTws}uKCD-4_Z?Xncd~S7-d7t|>@VsNwt9}ALAuoB=h74N zvjSS^yHvEB89X(gHWvD6VdAQECjQq4$+?@)uNgVgR&(o>noCBkisWC&h3too{RX#f6X}iLTMOp@?y}Y*fvnBh{XqvL1gPd{S3i`Jin6WP z>n}ZS+8oWpUAdOP9}wrg)AsPuBQ1$I;VD}~BSX1ecc+e@9v+9u%@g@=&-qsy@`#l> zA@L=}2^)qy7Fqy5F*pBI(x1NQ+;+nMyZ}!=@O1aKi+tcQSsJj_k{Hnrht1rGb9>p3 z30Vy321U(H1o2dS%*#ot4J>$b4is4*GXvb(J?&F{a^GfEKT5WLjNaSwoyh zVO)gRHl4%9ywuo;~H^2K&1C_}pN6BJ{B7wpeg7(bduM>lTFA%s2^piDs*T(u3Ib zWAT{0*$j35lU;XY1dydAPB|A?6=~(ZKb)drL}E z(kFFJZDbr&Ds;7waOyDA8ebXH5hGzFd78-vnYf={Qgv{5@O#LBbfQ@PcA|&>AIzm>tjC5`Hb|_DkS=_N54jC(85*n`&#JR5( z=WJh2OtgvVzq5@yiYk_v{|((`?Ck6c`>EGgFwE68HN*Kfs;VK{e74z7h&T?ky^D)R ztY`@fTEDtHa~;Tv_W8zA^Qz;p4`S^@*=IM(Jo}WBlae$ISf_y)lMIx|j>ESEGXtG1 z3F;}CxJ}d<_!T9f;lm)7fhUt%y31K^0CSI7n+&#|TGW^3;o-3$J*D#!cd`g0^VFF2 ze>KM>A%e)yw-cqFTY12Br7_P2#l<1aMN1Q>d>IhEG8jZzG_3aLqzW6kdFrpfl$4Z$ z*wspNTL`g3WolSSkTT91H--jR8-9$6ibACBh1HfsDW~zxWx491uUfna^cZR0lcW={ zDK5F7$PSojLVWX^wk`Rqd8z%>JA~v)cKR4%q7%rE)o2_Z)~9Ay4`CJ~>H1jrl|BM7 z>uYz!ZF9`ZbQ3m=Q#)lK4&Zr0wv@4;J~U-zvdKfkEm1NMD<8!Mf@CuAwA42>mux30 zAOot{B3lyhg+RdoVMYw3G2+z0S%fOkO8~*?o@(x~jq{oB<=L%p%P$=oOkw6At&CT{ z-@8-u3pg+3u8b~KtU4#U9lQW)sR0a`tL(}@AH;d?d^9w)Dd$_ssoZNPI}tK<3Z8BZ zm$@z{+_CEYcQ?9Cm(ZD!T>aiVH;h5ZQ*(P`sMncAP*4*>t_n}i{;p{K2N<26wnQm@ zpJ7qUK8k%W0~kMxp%nju`DQ}oM-elZ^VyoJDZR*{e^b^HqjjZkXXp^>i`dzN+$;&=iYay| zH0WE$C@A>S%^Tab&LdeD>mCrICcj7hQ ztj*SQPce`nTsm{+4B2?hpIHqI4Aiu=8EI)>-_|Y+6k(QA(%3c0y1qUk+1*P>qeSOI{WXnrzH~d!x`iJ_a zH=G#A&1J%)F49-xHJmo<;^tNznb06|1{WwOD;uU5Zmg`dM+-^JOujv5j>&+eL_Plg z>+=kB;nPc?8x$3}q>w9EfL{lFUR$EJx)zP0XRs|-PW{i%ufNbb+=PDQ2nEY*_S0R? zTpk{Pq;ZQvXm<5qjby*q1C$;_J+&O|M_n1J>JYdu?Bz)_GSZBQ6m2+FB;S4U5|Lt? z4T^(Uhb_9WjX0%vK8Io4b|9a%mzS5?sL2lz4wAjH@M>Ma$7^q}RwefRxdf^hArFNS zF9|7X+r58AyJ@Wka(SD|OGz_ViUzXpnJkLEuf~`RewaqIJ-)a^qxQY|QrEGBc?SAC zKB>T~j~4qagIEs^zgA*7cIWQ*_z3-aT@>{IZ0E zgk=IdP$)x_PW2d;toj8V8^x93SbAAS*iiJ{$Bylic1fV8oGP&DlDQ`3uO4xG*xkDI zo7K8Ghb>M_lj#Ek*4DJ8J78hJas1~Ae(Qd@j*6YzI=f!{mSMW9Q!0LE7P7d5LXs>$ z_MB&A%+}ENp3T1FQXx(BMpCy%Vx?uxhcdif!-&Mt9T8`%9?P74DHfTKR>;p(v$Xkf zx9vVpx5Qv~w@Fxd-0pC(6gIF5V#BxQYm3D>*AHU zX(q$+#xVJF4?q#gB$=x(PEiTl7Lo^?^*6P-2i_3==DK}d+>MBt+% z25wp!RX^IlmW94*TEvC7=~`d8aRbdF8(NMP1%2@(6;&m1k9QSUcm0iy&F+)Qs%I09 zGfVMh%>#4fBdEO+*8}2ppZ3(nn@Yhc^6QYmn>P#%otn*WVVzyB@kb;m&^nN$_yI*_ zR`vS}^)1BD4H{`ncMUZmrh)R1vN%+RwJ5M4KWQd)I{>7Tjg5`97%a{$$oyPZ2HGp@ zh?f9ngr`lQkm|aA`KyBFbDY}4>z&IBk$mPEV6u1=&aKyuum|JM5A|1$Xf#C%v^d8V zwily~^eQ~Hh!wo5SdE8{kz2Pl z#h)%qD&n^B;#N{pOkN=u#hG~x(D@5oS{Mm}h>ZbM9bUN_PJKrsop&vjpIE`$v%B77 zz&@v?dg>x)cT#<@TEfEfAj zgr;TkxvJk&R}#|ub#&^bF`)O9Ixb{%q}paWKq`7Vf^I)J)QL+y^V4zMM&!GT+}VJS zXCRT_H|e~N*k6D$>bg?t3yi-S1eT6pNIF|W?|>#fBPI)IfP~3ukgE)PUE>=4?JAeb zoJax7?3as+-FY?sQFR?1Djv?nih}``=6-_^7$vLIU^lznUuGIOJ~1(+pQaQkPCvge zH9zm!<0FKLw;H5CJ@;Y#O|wR zKqTfsa^z39ACDyGU{gwnCzh|?HI$Z<>&$}{+%C(_0ptr-BP%afC)cxN*o!2#%V%35 z9<%fovbVJcY)(U8^&^!OxiIF1!IB3bKYpYY@h%6=g%h-CN=D%|+x_6}73v1dxr+vA z_Mr`kd4M;zdp_<35>Ywu*imWzF8-9SUZ5{MLt3lT_yFQGuEpj z5|Ekjv+-BLj`L~va&Xxr-n(jGM+stH=&jI08fY}Up9A{wBy=9goo1t-dnR3$K6~%) zlH_s*BD=6oOQZLi_CUdk7JPi=%6AZaOJbRB-A$AX_`)jW&*Q!;VS62nE++qP(8WzV;JzE6q&<{YWmR;wA%Ay$xLK{-uOPc%$?8Iu=I?eY24tOi>RDY#u1#D>woTR z^XaXvN`NKbv3lQ`eepBbYE~Jy(twQ=A+hOIdJkzc($J(g#|T?=3*_@Z8#lGLb5y;1 z_qqGVO!hTDDp>)8gv}LMZgXz7?uL zO-F}?h;Fu4_qcDdL%m{a+EOi36%Fu<3GphhDMq)#qgE1ThdsLo4guCL6qzI?F7pq%Z z6vawiIxcpk5E^0ZcqI4s*t!yRH1x_{d8(Y5eAYffmLAVzG~CnsYywfSfwuv(#!Ty# zNa&q!%O6&sUJ5>1iy zBWP!o?T(Z|^QO$Bcjq6wYJM8_GEM8w(P1-Q(QmE;YXILwBlr5;w-=yBhi}XeXMqGj;~v5+7fm|U_rG6de56U*v1e);1^L+Z`&3>mQjZ>BR3}3@fmyj zQ4xBa2#l^)E$cq|IpyTJBSZl)knqbav#j}ZDAM6a?eeGTtco#_>10xA1!;VGI)#SU zl>PeTyQ?`;grxl&>t53lRwPgis{ZJ2!Z4m{_gq}`N?jiV$6Ke)WLvQwhWJU%hUI|& z^cdwCZcL5yOi&cx+l&uWb!$GAD zWH^|qwUF@2UA~EVgCoU)|CF~2>>LLSJIn$WfP0131MFbl1LF%4BhQ~>NR0D|wapqy zGFJiHuA;%9+(SrQRXs?HAANDYp0r!nA-RYWu!5kseu?TY4#ghE2siz5Z*9g+;al?E6D+Jc#0 z4KX>KVG?FWXXq4V!!%A6a+to(EC?#;|C1h)Zs{lso0Mw4Iwu%U*}=ao8_xCk{{0{R z%X3{SjbT}93-h*}`KH{|?7EuB-HZQyfw9fTGDBXGMp99%@5oQPdM(Gi4V-EVpNg`p zf#_FDG~o>8BxC^rlckj}i9d2Ub6xTSeL9$Pb$T9R5U6$a+O_q@AFs6{O%XcYDXJTs zX05QtIDvlAv^y(BfDqXT=)5_v$fecWz+j;_cQ^Z!h|6Mvr;W|K|NiR92<3ouGjdV$ z!J9K`Z!s*0OAk@yq6BOUhvNW|sey$H4%!?$cFY`P36MQn+Div*T0i%6CYkdJl@*(T z`>~%`W&ljWug-EC9>xNq}VSU(V#ek ziP91G67eX64S#P#q;2Od+gIl%TF~#*F<+-hoWc%^(X{*)%$2?ry!1yv)qROfU&bpy zmyw*#lh`eQ|M>;m5)Yys)Lh~n-P$vsD_^gn1D;X4z;!%(W2n@T2MBF>ppXzI4SsBz zx2?E?@NJzpeDb}v3;EV1$V(3#*5}j+_%B~>O%Mo{KYsl9RpGTQ2WB~hx#x^Cv&mY8 zN(I&%H|ifsN=80+z{~0CM)#E%`Mvdjo7-LDm5I=zMjwRiMxU2B&L;F#`cxvfd-38$ zievrR?mLa)oX|dp*>)BO7UrMPRDa!qKOxcvO}2)~_(B^l>aD*&Kez#T0ju)^q?qmm z<E?h9tjfs(aJ6`=^zdjft-{{5M zJ9SIW8iIWAD>zHXlq}rx|16q_?E@Xl%Ei@;kjdxR6h0;T?3p7RAF%K3gB;%8-iS&7 z{^#LWuU|*tJT^YT^yPK$taGfzIdOtJ-||OQRny+CKsVRz+wFYTJH-XDK(Z1mRuv)( z5ndK%o=iu}`yibAF>go2#8{mu9q)aY6Q;PE?|6zbjy6Ft1sI8 z6nMHca4W_!e9BuxLR1dFIujSXdnm4EY z_se6mh!0myRPWn$UE-~R?<* z7n>(SvTU)Cqmrs{^30j;VCwj{!KUB)*|k&3AX&+S*EwSFG`{ zAK?PmM21FXY(*@;>{Mf@+JZJezhm9s{5FFR!MFRh+ri$xy)a8hwbF(GDSgt)H_0%n zRmjlHO<>e21V++lj?q06&ZVkT9?eJ-@UJ zOxgD`X;(G|A~&ipB19(Aqhw3pOgtnR*{Sa9%Kmd*syPbQAcfB0o>XPwE2DU&5F2|I zZ5P7m@@E<-1_YdmM72<^m(3i7g_(8)4)FzWKPec-PAwN}oYe)q*;3!weyy3(^)WG`5b ze!geX|25rxeI|ZR4O>?xXU?ft`tX`mpgIJ37C>YQp}vW}NS#fP1c%$vY@-GsX-OOoY@! zF7|`2Pfy!V33P>~^N>(iALh6x<1ZG5g2Hs=8P!X;D)h^y?`#*!8 zY&B3YAzNoggN;297AoIt{@ZTyM2+{qLzt^&%Rs{_JbLFk6C5a^3}-UW{-KpY0;fQ? zcaId+RaHG$pX&)P^SAU7BXYKxLt~XQN+qQ!no!>>=GPEn+yy-~g;ss1CrTVLMSvSb?CoH9#DG$O0&?q&*Xd+Vz>`Gu*LxRmS zUm$K&bM(auEQVz2FT5XS64PFO_ROL$FBDAUA<@y9{&WJ;m@sag6i}pR{RKukXY);+ z!0YrT@X{5qX?xVT%ceHR6c`UU>lHg{;kk7?DU5&{pI_J~`8)&hmfBP2<)ZrS;kM

Zh}>Y9v5}ig(@;7 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%0D{b3z7?WOKSX&d0QT$Es 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 z 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 literal 0 HcmV?d00001 diff --git a/apps/web/design/zopu-core-product-experience-polish-1.png b/apps/web/design/zopu-core-product-experience-polish-1.png new file mode 100644 index 0000000000000000000000000000000000000000..bec65b43ca75817e2dbfc2f44ed37f65f227600c GIT binary patch literal 127613 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