From 76306961c992403b5b90671b9e8009eba5496f7b Mon Sep 17 00:00:00 2001 From: "rivet-docs-sync[bot]" Date: Tue, 3 Nov 2026 23:20:48 +0000 Subject: [PATCH] docs(docs): sync from rivet-dev/rivet@ac94186 --- vendor/docs/docs/content/architecture.mdx | 50 +- vendor/docs/docs/content/authentication.mdx | 16 +- vendor/docs/docs/content/benchmarking.mdx | 24 +- vendor/docs/docs/content/cli.mdx | 230 +- vendor/docs/docs/content/connect.mdx | 40 +- vendor/docs/docs/content/debugging.mdx | 22 +- vendor/docs/docs/content/endpoints.mdx | 71 + .../docs/content/environment-variables.mdx | 8 +- vendor/docs/docs/content/jwt.mdx | 90 +- vendor/docs/docs/content/namespaces.mdx | 14 +- vendor/docs/docs/content/nodes.mdx | 2 + .../docs/docs/content/pool-configuration.mdx | 69 + vendor/docs/docs/content/regions.mdx | 23 + vendor/docs/docs/content/statuses.mdx | 18 + vendor/docs/docs/content/troubleshooting.mdx | 83 + vendor/docs/docs/content/versions.mdx | 264 +- vendor/docs/docs/content/workers.mdx | 30 + vendor/docs/docs/sidebar.json | 72 +- .../docs/engine/artifacts/config-schema.json | 3046 +++++++++-------- .../actors-quickstart-next-js/registry.ts | 16 - .../{ => backend}/index.ts | 0 .../{ => frontend}/Counter.tsx | 6 +- .../actors-quickstart-react/frontend/main.tsx | 9 + .../actors-versions/programmatic-version.ts | 10 - .../docs/examples/docs/api/actors-destroy.ts | 2 +- .../examples/docs/api/actors-list-names.ts | 2 +- vendor/docs/examples/docs/api/actors-list.ts | 2 +- .../examples/docs/api/actors-reschedule.ts | 2 +- vendor/docs/examples/docs/api/actors-sleep.ts | 2 +- .../examples/docs/api/connection-actions.ts | 2 +- .../examples/docs/api/connection-connect.ts | 2 +- .../examples/docs/api/connection-errors.ts | 2 +- .../examples/docs/api/connection-events.ts | 2 +- .../docs/api/control-plane-datacenters.ts | 2 +- .../docs/api/control-plane-metadata.ts | 2 +- .../examples/docs/api/gateway-metadata.ts | 2 +- .../docs/examples/docs/api/gateway-queue.ts | 2 +- .../examples/docs/api/inspector-action.ts | 14 - .../docs/api/inspector-connections.ts | 7 - .../docs/api/inspector-database-execute.ts | 17 - .../docs/api/inspector-database-rows.ts | 12 - .../docs/api/inspector-database-schema.ts | 7 - .../docs/api/inspector-queue-delete.ts | 10 - .../examples/docs/api/inspector-queue-get.ts | 7 - .../examples/docs/api/inspector-queue-post.ts | 14 - .../docs/examples/docs/api/inspector-rpcs.ts | 7 - .../examples/docs/api/inspector-state-get.ts | 7 - .../docs/api/inspector-state-patch.ts | 14 - .../examples/docs/api/inspector-summary.ts | 7 - .../examples/docs/api/inspector-traces.ts | 7 - .../docs/api/inspector-workflow-history.ts | 7 - .../docs/api/inspector-workflow-replay.ts | 14 - .../examples/docs/api/namespaces-create.ts | 2 +- .../docs/examples/docs/api/namespaces-list.ts | 2 +- vendor/docs/examples/docs/api/pools-delete.ts | 16 + vendor/docs/examples/docs/api/pools-get.ts | 13 + .../examples/docs/api/pools-list-nodes.ts | 14 + ...{workers-configs-list.ts => pools-list.ts} | 8 +- ...kers-configs-upsert.ts => pools-upsert.ts} | 8 +- .../docs/examples/docs/api/tokens-inspect.ts | 2 +- .../docs/api/workers-configs-delete.ts | 13 - .../api/workers-configs-refresh-metadata.ts | 2 +- vendor/docs/examples/docs/api/workers-list.ts | 12 - .../api/workers-serverless-health-check.ts | 2 +- .../docs/cookbook-cron-jobs/daily-report.ts | 18 - .../examples/docs/durable-streams/client.ts | 14 + .../examples/docs/durable-streams/create.ts | 11 + .../examples/docs/durable-streams/producer.ts | 21 + .../docs/durable-streams/read-history.ts | 10 + .../docs/durable-streams/read-live.ts | 14 + .../examples/docs/durable-streams/write.ts | 13 + .../docs/examples/docs/general-edge/client.ts | 12 + .../docs/general-jwt/quickstart/client.ts | 2 +- .../examples/docs/onboarding-actors/client.ts | 20 + .../examples/docs/onboarding-actors/index.ts | 43 + vendor/docs/examples/docs/package.json | 4 +- vendor/docs/examples/docs/tsconfig.json | 9 +- 77 files changed, 2501 insertions(+), 2174 deletions(-) create mode 100644 vendor/docs/docs/content/endpoints.mdx create mode 100644 vendor/docs/docs/content/pool-configuration.mdx create mode 100644 vendor/docs/docs/content/regions.mdx create mode 100644 vendor/docs/docs/content/statuses.mdx create mode 100644 vendor/docs/docs/content/troubleshooting.mdx create mode 100644 vendor/docs/docs/content/workers.mdx delete mode 100644 vendor/docs/examples/docs/actors-quickstart-next-js/registry.ts rename vendor/docs/examples/docs/actors-quickstart-react/{ => backend}/index.ts (100%) rename vendor/docs/examples/docs/actors-quickstart-react/{ => frontend}/Counter.tsx (85%) create mode 100644 vendor/docs/examples/docs/actors-quickstart-react/frontend/main.tsx delete mode 100644 vendor/docs/examples/docs/actors-versions/programmatic-version.ts delete mode 100644 vendor/docs/examples/docs/api/inspector-action.ts delete mode 100644 vendor/docs/examples/docs/api/inspector-connections.ts delete mode 100644 vendor/docs/examples/docs/api/inspector-database-execute.ts delete mode 100644 vendor/docs/examples/docs/api/inspector-database-rows.ts delete mode 100644 vendor/docs/examples/docs/api/inspector-database-schema.ts delete mode 100644 vendor/docs/examples/docs/api/inspector-queue-delete.ts delete mode 100644 vendor/docs/examples/docs/api/inspector-queue-get.ts delete mode 100644 vendor/docs/examples/docs/api/inspector-queue-post.ts delete mode 100644 vendor/docs/examples/docs/api/inspector-rpcs.ts delete mode 100644 vendor/docs/examples/docs/api/inspector-state-get.ts delete mode 100644 vendor/docs/examples/docs/api/inspector-state-patch.ts delete mode 100644 vendor/docs/examples/docs/api/inspector-summary.ts delete mode 100644 vendor/docs/examples/docs/api/inspector-traces.ts delete mode 100644 vendor/docs/examples/docs/api/inspector-workflow-history.ts delete mode 100644 vendor/docs/examples/docs/api/inspector-workflow-replay.ts create mode 100644 vendor/docs/examples/docs/api/pools-delete.ts create mode 100644 vendor/docs/examples/docs/api/pools-get.ts create mode 100644 vendor/docs/examples/docs/api/pools-list-nodes.ts rename vendor/docs/examples/docs/api/{workers-configs-list.ts => pools-list.ts} (52%) rename vendor/docs/examples/docs/api/{workers-configs-upsert.ts => pools-upsert.ts} (59%) delete mode 100644 vendor/docs/examples/docs/api/workers-configs-delete.ts delete mode 100644 vendor/docs/examples/docs/api/workers-list.ts delete mode 100644 vendor/docs/examples/docs/cookbook-cron-jobs/daily-report.ts create mode 100644 vendor/docs/examples/docs/durable-streams/client.ts create mode 100644 vendor/docs/examples/docs/durable-streams/create.ts create mode 100644 vendor/docs/examples/docs/durable-streams/producer.ts create mode 100644 vendor/docs/examples/docs/durable-streams/read-history.ts create mode 100644 vendor/docs/examples/docs/durable-streams/read-live.ts create mode 100644 vendor/docs/examples/docs/durable-streams/write.ts create mode 100644 vendor/docs/examples/docs/general-edge/client.ts create mode 100644 vendor/docs/examples/docs/onboarding-actors/client.ts create mode 100644 vendor/docs/examples/docs/onboarding-actors/index.ts diff --git a/vendor/docs/docs/content/architecture.mdx b/vendor/docs/docs/content/architecture.mdx index f7a6e484..334a1240 100644 --- a/vendor/docs/docs/content/architecture.mdx +++ b/vendor/docs/docs/content/architecture.mdx @@ -4,38 +4,30 @@ description: "How object storage, the control plane, and nodes work together to skill: true --- -Rivet has three parts: **object storage** keeps Actor data, the **control plane** schedules Actors and routes requests, and **actor nodes** run your code. - - - - - BACKEND - - - Clients - Browsers, apps - - - Actor nodes - Your code - - - Control plane - Schedule, route, persist - - - Object storage - S3, GCS or Azure - - - - +Rivet has three parts: **object storage** keeps Actor data, the **control plane** schedules Actors and routes requests, and **nodes** run your code. + + + + + + + + + + + + Object storage + S3, GCS, or Azure + Control plane + Schedule, route, persist + Nodes + Your code -## Actor nodes +## Nodes -An actor node is a process running your app with the Rivet SDK, started with `registry.start()`. It opens an outbound connection to the control plane and hosts the Actors assigned to it. Nodes need no public URL or inbound port for Actor traffic. Add nodes when you need more capacity. +A node is a process running your app with the Rivet SDK, started with `registry.start()`. It opens an outbound connection to the control plane and hosts the Actors assigned to it. Nodes need no public URL or inbound port for Actor traffic. Add nodes when you need more capacity. ## Control plane @@ -55,4 +47,4 @@ A namespace separates Actors, keys, and node pools. For example, use `production ## Node pools -A node pool groups nodes by name within a namespace. Configure a pool before connecting nodes to it. A node joins the pool selected by `RIVET_POOL`, which defaults to `default`. For example, create a `gpu` pool for Actors that need GPUs alongside a `default` pool for the rest. See [Nodes & Node Pools](/docs/nodes). +A node pool groups nodes by name within a namespace. Configure a pool before connecting nodes to it. A node joins the pool selected by `RIVET_POOL`, which defaults to `default`. For example, create a `gpu` pool for Actors that need GPUs alongside a `default` pool for the rest. See [Node Pool Configuration](/docs/pool-configuration). diff --git a/vendor/docs/docs/content/authentication.mdx b/vendor/docs/docs/content/authentication.mdx index 2df9255e..eb24d290 100644 --- a/vendor/docs/docs/content/authentication.mdx +++ b/vendor/docs/docs/content/authentication.mdx @@ -1,15 +1,19 @@ --- title: "Authentication" -description: "The credentials the control plane accepts and where each one belongs." +description: "Choose credentials for a self-hosted control plane or Rivet Cloud." skill: true --- -The control plane checks credentials before serving management requests or routing Actor traffic. Application-level authorization still belongs in your Actor. See [Permissions](/actors/docs/permissions). +The control plane checks credentials before serving management requests or routing Actor traffic. Application-level authorization still belongs in your Actor. See [Authentication](/actors/docs/authentication). -## Credentials +## Self-hosted control plane -Your Rivet token is the `auth.admin_token` value in the control plane config. Your nodes and backend use it to connect, create namespaces, and manage node pools. Keep it server-side. +The configured `auth.admin_token` grants every operation in every namespace. Keep it in your backend or secret store. Use it to create namespaces and issue [scoped JWTs](/docs/jwt) for applications. -For browser clients, issue [scoped JWTs](/docs/jwt). A JWT grants access to one namespace and an explicit set of resources and operations, so give each client only the permissions it needs. +A JWT grants access to one namespace and an explicit set of resources and operations. Give browser clients only the permissions they need, and keep tokens that can register nodes or manage pools on your backend. -Pass credentials in `Authorization: Bearer `, or use an [endpoint URL](/docs/connect) such as `https://production:@rivet.mycompany.com`. +Pass credentials in `Authorization: Bearer `, or use an [endpoint URL](/docs/endpoints) such as `https://production:@rivet.mycompany.com`. + +## Rivet Cloud + +Get your namespace endpoints and credentials from the [Rivet Cloud dashboard](https://dashboard.rivet.dev). Keep secret credentials on your backend. See [Rivet Cloud](/docs/deploy/cloud/) for managed deployment and token settings. diff --git a/vendor/docs/docs/content/benchmarking.mdx b/vendor/docs/docs/content/benchmarking.mdx index 1fbba5a8..2d9a776f 100644 --- a/vendor/docs/docs/content/benchmarking.mdx +++ b/vendor/docs/docs/content/benchmarking.mdx @@ -17,7 +17,7 @@ These measurements come from the October 25, 2026 Rivet Actors benchmark. Use th | **Software** | Debian 13, Node.js 24, release builds | | **Object storage** | Azure Blob Storage in the same region, with locally redundant storage (LRS) | -Requests use plain HTTP inside the region over reused connections, with one request in flight. Create and wake requests have 20 ms gaps. Each row reports all successful samples from its condition, with nearest-rank percentiles and no trimming. All conditions below completed without errors. +Requests use plain HTTP inside the region over reused connections, with one request in flight. Create and wake requests have 20 ms gaps. The warm-read condition has 1,000 warmups; inserts run without gaps. Each row reports all successful samples from its condition, with nearest-rank percentiles and no trimming. All conditions below completed without errors. ## Results @@ -29,6 +29,12 @@ Milliseconds, p50 / p99. | **Wake** | 1,200 | 4.37 | 6.80 | Client send to full ping reply from a Rivet Actor asleep for at least 10 seconds. | | **Durable insert** | 1,200 | 0.69 | 0.80 | One 128-byte SQLite row inserted in autocommit mode. Timed around the query on the node, including the write to a majority of disks across zones. | | **Warm read** | 1,200 | 0.059 | 0.080 | A primary-key read of one 128-byte row from SQLite's memory cache, timed around the query. | +| **Random read after sleep, 50 MB database** | 1,200 | 1.33 | 100.1 | First random point read after at least 13 seconds asleep. Timed around the query, with two network page fetches. | +| **Random read after sleep, 1 GB database** | 1,024 | 41.8 | 168.4 | The same condition with three network page fetches. | + +The query interval includes the SQLite call, transport, and materializing the result on the node. It excludes the client's HTTP round trip. Full HTTP p50 is 0.59 ms for a warm read, 17.8 ms for the 50 MB read after sleep, and 61.6 ms for the 1 GB read after sleep, including wake time. + +The node reaches the control plane in its own zone for reads; durable writes require a cross-zone acknowledgement. Sleeping does not guarantee a cold object-storage read. This setup tests replicated writes across zones, but its LRS bucket does not test object-storage availability during a zone failure. ## Reproducing it @@ -36,19 +42,25 @@ Milliseconds, p50 / p99. -Run three control plane instances on VMs in three zones of one region, each on raw local NVMe, with a bucket in the same region. Follow the [VM](/docs/deploy/self-host/control-plane/vm/) guide. Kubernetes does not give pods raw NVMe, so it cannot reproduce these numbers. +Run three control plane instances in three zones of one region, each on local NVMe, with a bucket in the same region. Follow the [Kubernetes](/docs/deploy/self-host/control-plane/kubernetes/) or [VM](/docs/deploy/self-host/control-plane/vm/) guide. -Run one node in the first zone, with a Rivet Actor that has a `ping` action and a SQLite table, and a load generator on the same machine. See [Deploy nodes](/docs/deploy/self-host/nodes/). +Run one node in the first zone, with a Rivet Actor that has a `ping` action and a SQLite table, and a load generator on the same machine. See [Deploy nodes](/docs/deploy/self-host/workers/). + + + + + +For the warm-read condition, seed one 128-byte row and run 1,000 reads before recording samples. Keep startup and warmup measurements separate from measured requests. -Use the sample counts and pacing above, with a new key for each create. For wakes, explicitly sleep each Actor and wait at least 10 seconds. +Use the sample counts and pacing above, with a new key for each create. For wakes, explicitly sleep each Actor and wait at least 10 seconds. For random reads, seed 128-byte rows into databases of the stated sizes, sleep the Actors for at least 13 seconds, and verify each measured query fetches pages over the network. Record HTTP and query intervals separately. @@ -59,3 +71,7 @@ Sort the successful samples and take nearest-rank percentiles. Report the sample + +## Under load + +These latency results use one request at a time. Concurrent create workloads that overlapped mass teardown produced timeouts. Unpaced 10 MiB writes produced errors, and a seven-minute write run slowed substantially. Measure sustained throughput and error rates for your workload separately from these latency conditions. diff --git a/vendor/docs/docs/content/cli.mdx b/vendor/docs/docs/content/cli.mdx index d052b5b7..633fda57 100644 --- a/vendor/docs/docs/content/cli.mdx +++ b/vendor/docs/docs/content/cli.mdx @@ -1,10 +1,13 @@ --- title: "CLI" -description: "Use the optional Rivet CLI to deploy to a Rivet Cloud cluster, read its logs, and call the Rivet Cloud API." +seoTitle: "Rivet CLI Documentation" +description: "Use the optional Rivet CLI to deploy Rivet Actors to Rivet Cloud and manage Rivet Cloud projects and clusters." skill: true --- -The `rivet` CLI (`@rivetkit/cli`) is optional. It deploys your app to a [Rivet Cloud](/docs/deploy/cloud/) cluster. Run it with your package manager: +The `rivet` CLI (`@rivetkit/cli`) is optional. You only need it to deploy to Rivet Cloud with `rivet deploy` and to manage Rivet Cloud projects and clusters. + +Run it with your package runner: ```bash npx @rivetkit/cli @@ -13,27 +16,70 @@ npx @rivetkit/cli | Command | Description | | --- | --- | | `rivet login` | Sign in to Rivet Cloud in the browser. | -| `rivet link` | Link the current directory to a cluster. | -| `rivet deploy` | Build and deploy your app to a cluster. | -| `rivet cluster logs` | Read or follow a cluster's logs. | +| `rivet link` | Link the current directory to a project or cluster. | +| `rivet deploy` | Build and deploy the project to Rivet Cloud. | +| `rivet logs` | Read or follow a project's compute logs. | +| `rivet pool` | List or delete compute pools in a namespace. | +| `rivet cluster logs` | Read or follow a cluster's engine and runner logs. | | `rivet api` | Make an authenticated request to the Rivet Cloud API. | +| `rivet engine` | Run the bundled `rivet-engine` binary directly. | +| `rivet setup-ci` | Install the GitHub Actions deploy workflow. | +| `rivet token` | Mint a namespace-scoped token. | ## Signing in and linking -`rivet login` opens a browser to sign in and stores the session in `~/.rivet/credentials`, refreshing it as needed. Pass `--no-browser` to print the sign-in URL instead. `rivet logout` removes the session and `rivet whoami` shows the signed-in account and linked cluster. +`rivet login` opens a browser to sign in and stores the session in `~/.rivet/credentials`, refreshing it as needed. Pass `--no-browser` to print the sign-in URL instead. `rivet logout` removes the session and `rivet whoami` shows the signed-in account and linked project. ```bash rivet login +rivet link # pick an organization and project rivet link --org my-org --cluster my-cluster ``` -`rivet link` writes `.rivet/project.json` in the current directory, so later commands there default to that cluster. It prompts for anything not passed as a flag. `rivet unlink` removes the link. +`rivet link` writes `.rivet/project.json` in the current directory, so later commands there default to that project or cluster. It prompts for anything not passed as a flag. `rivet unlink` removes the link. -Every command resolves its token in this order: `--token`, `RIVET_CLOUD_TOKEN`, then the `rivet login` session. Use `--token` or `RIVET_CLOUD_TOKEN` in CI. `--cluster` (or `RIVET_CLUSTER`) selects the cluster, with `--org` (or `RIVET_ORG`) naming the organization it belongs to. `--cloud-api` points at a different Cloud API endpoint. +Every command that talks to Rivet Cloud resolves its token in this order: `--token`, `RIVET_CLOUD_TOKEN`, the `rivet login` session, then a token saved by an earlier `rivet deploy --token`. Use `--token` or `RIVET_CLOUD_TOKEN` in CI. The project comes from `--project` and `--org` (or `RIVET_PROJECT` and `RIVET_ORG`). Otherwise an API token names its own project, and a `rivet login` session uses the linked directory or prompts for one. `--cluster` (or `RIVET_CLUSTER`) selects a cluster instead, with `--org` naming the organization it belongs to. `--cloud-api` points at a different Cloud API endpoint. ## `rivet deploy` -The CLI builds the image, uploads it as a build, rolls the pool out to that build, and prints the build ID. +Builds and pushes your project's Docker image and upserts the managed pool, printing the dashboard URL. Deploys target the `default` pool unless you pass `--pool`; a new pool is created on first deploy. See [Deploying to Rivet Cloud](/docs/deploy/self-host/control-plane). + +```bash +rivet deploy +``` + +A token passed with `--token` is saved to `~/.rivet/credentials`, so later deploys can omit it. + +| Flag | Default | Description | +| --- | --- | --- | +| `--token` | env / credentials | Rivet Cloud API token. | +| `--namespace` | `production` | Cloud namespace. | +| `--pool` | `default` | Compute pool to deploy to. | +| `--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. | +| `--yes` | — | Skip prompts. | +| `--reuse-image` | — | Keep the pool's current image and skip the Docker build and push. Use it to change resources without a rebuild. | + +The pool's resources and scaling are set with these flags. Each one left out keeps its server-side default. + +| Flag | Server default | Description | +| --- | --- | --- | +| `--cpu` | `1` | vCPUs per actor: `1`, `2`, `4`, `8`, or a value from `0.08` to below `1` with at most two decimals. | +| `--memory` | `512Mi` | Memory per actor, from `512Mi` to `4Gi`. | +| `--min-scale` | `0` | Actors kept running, `0` to `100`. | +| `--max-scale` | `1` | Most actors to scale to, `1` to `5000`. | +| `--max-concurrent-actors` | `1000` | Most actors the pool runs at once, `1` to `50000`. | +| `--instance-request-concurrency` | `80` | Concurrent requests per actor instance, `1` to `500`. | +| `--drain-grace-period` | `1800` | Seconds a draining instance waits for its actors to finish, `5` to `3200`. | +| `--drain-on-version-upgrade` | `true` | Drain instances on the old version when a new one deploys. Pass `--drain-on-version-upgrade=false` to disable. | + +### Deploying to a cluster + +Pass `--cluster` to deploy to a cluster instead of a project. The CLI builds the image, uploads it as a build, rolls the pool out to that build, and prints the build ID. ```bash rivet deploy --cluster my-cluster --org my-org @@ -49,7 +95,7 @@ rivet deploy --cluster my-cluster --build | Flag | Default | Description | | --- | --- | --- | -| `--cluster` | `RIVET_CLUSTER` / linked cluster | Cluster to deploy to. | +| `--cluster` | `RIVET_CLUSTER` / linked cluster | Cluster to deploy to instead of a project. | | `--org` | `RIVET_ORG` / linked organization | Organization the cluster belongs to. | | `--region` | the cluster's only region | Region of the pool. Required when the cluster has several regions. | | `--pool` | `default` | Pool to roll out. | @@ -59,34 +105,7 @@ rivet deploy --cluster my-cluster --build | `--image` | cluster name | Image name recorded on the build. | | `--tag` | git short SHA | Image tag recorded on the build. | -The saved image must be 5 GiB or smaller. See [Actor Nodes](/docs/deploy/cloud/compute) for what the image needs. - -## `rivet cluster logs` - -Prints a cluster's most recent control plane and node logs, oldest first. `-f` keeps following new lines. `--source engine` selects control plane logs and `--source runner` selects node logs. - -```bash -rivet cluster logs --cluster my-cluster -rivet cluster logs --cluster my-cluster --severity error -n 50 -rivet cluster logs --cluster my-cluster --source runner --pool default --contains timeout -f -``` - -Each line shows its timestamp, severity, source, and message. This format is not stable; pass `--json` to print one JSON object per line with `id`, `timestamp`, `severity`, `message`, `source`, `runner_pool`, and `instance_id`. - -| Flag | Default | Description | -| --- | --- | --- | -| `--cluster` | `RIVET_CLUSTER` / linked cluster | Cluster to read. Required. | -| `--source` | both | `engine` for the control plane or `runner` for nodes. | -| `--pool` | all pools | Only this node pool's logs. Cannot be combined with `--source engine`. | -| `--severity` | all | Only lines at this severity or above: `trace`, `debug`, `info`, `warn`, `error`, or `fatal`. | -| `--contains` | none | Only lines containing this text, case-insensitive. | -| `-n`, `--limit` | `100` | Number of recent lines to print first, up to 1000. | -| `-f`, `--follow` | none | Keep printing new lines as they arrive. | -| `--before` | none | Page back: only lines older than this line `id` from `--json` output. | -| `--json` | none | Emit JSON lines instead of formatted output. | -| `--token`, `--org`, `--cloud-api` | none | Same as `rivet deploy`. | - -Lines can arrive up to a minute late, and `--follow` prints them when they do. +The saved image must be 5 GiB or smaller. `--namespace`, `--env`, `--reuse-image`, and the resource and scaling flags apply to projects only and are rejected for a cluster. `rivet pool`, `rivet logs`, and `rivet token` do not support clusters. Read a cluster's logs with [`rivet cluster logs`](#rivet-cluster-logs). ## `rivet api` @@ -98,7 +117,7 @@ rivet api '/v2/clusters/{cluster}/builds' rivet api '/v2/clusters/{cluster}/builds/' -X DELETE ``` -The path is sent as written. `rivet api /v2/openapi.json` lists every endpoint. `{cluster}` and `{org}` are filled from the matching flag or the linked directory. +The path is sent as written, so `/v2/...` paths reach API v2 and unprefixed paths reach API v1. Each API serves its OpenAPI spec, which lists every endpoint: `rivet api /v2/openapi.json` for API v2 and `rivet api /doc` for API v1. `{cluster}`, `{org}`, and `{project}` are filled from the matching flag or the linked directory. Fields become a JSON body, and the method defaults to `POST` when a body is given. With `-X GET`, fields are sent as query parameters. @@ -118,3 +137,136 @@ rivet api '/v2/clusters/{cluster}/regions/us-east4/runner-pools/default' -X PUT | `--token`, `--cloud-api` | Same as `rivet deploy`. | The command exits with a non-zero status when the API responds with an error, after printing the response body. + +## `rivet logs` + +Prints a project's compute logs for one pool, oldest first. `-f` follows new lines live. For a cluster, use [`rivet cluster logs`](#rivet-cluster-logs). + +```bash +rivet logs +rivet logs --pool workers --contains error -n 200 +rivet logs --region us-east4 -f +``` + +| Flag | Default | Description | +| --- | --- | --- | +| `--namespace` | `production` | Cloud namespace. | +| `--pool` | `default` | Compute pool. | +| `--region` | all | Regions to include, comma-separated. | +| `--contains` | — | Only lines containing this text, case-insensitive. | +| `-n`, `--limit` | `100` | Number of history lines. Ignored with `--follow`. | +| `-f`, `--follow` | — | Keep printing new lines as they arrive. | +| `--before` | — | Page back: only lines before this ISO 8601 timestamp. | +| `--json` | — | Emit JSON lines instead of formatted output. | +| `--token`, `--project`, `--org` | — | See [Signing in and linking](#signing-in-and-linking). | + +## `rivet pool` + +Manages the compute pools in a namespace. A namespace can hold multiple pools; `default` is the one used when a `deploy` or `logs` command omits `--pool`. + +### `rivet pool list` + +```bash +rivet pool list +``` + +Each pool is printed as a status line (colorized by status) followed by its config (display name, image, resources, environment) and any error. Colors are disabled when stdout is not a terminal or `NO_COLOR` is set. This human-readable format is not stable; pass `--json` for machine-readable output. + +| Flag | Default | Description | +| --- | --- | --- | +| `--token` | env / credentials | Rivet Cloud API token. | +| `--namespace` | `production` | Cloud namespace. | +| `--project` / `--org` | from token | Override project/org. | +| `--json` | — | Emit raw JSON instead of formatted output. | +| `--with-env-vars` | — | Include environment variable values, which are secret and omitted otherwise. | + +### `rivet pool delete` + +Tears down a compute pool, then waits until it is fully removed, printing status changes as it goes. Prompts for confirmation unless `--yes` is passed, and refuses to run without confirmation when stdin is not a terminal. + +```bash +rivet pool delete --yes +``` + +| Flag | Default | Description | +| --- | --- | --- | +| `--namespace` | `production` | Cloud namespace the pool belongs to. | +| `--yes` | — | Skip the confirmation prompt. | +| `--token` | env / credentials | Rivet Cloud API token. | +| `--project` / `--org` | from token | Override project/org. | + +## `rivet cluster logs` + +Prints a cluster's most recent engine and runner logs, oldest first. `-f` keeps following new lines. Use `rivet logs` for a project's logs instead. + +```bash +rivet cluster logs --cluster my-cluster +rivet cluster logs --cluster my-cluster --severity error -n 50 +rivet cluster logs --cluster my-cluster --source runner --pool default --contains timeout -f +``` + +Each line shows its timestamp, severity, source (`engine`, or `runner/`), and message. This format is not stable; pass `--json` to print one JSON object per line with `id`, `timestamp`, `severity`, `message`, `source`, `runner_pool`, and `instance_id`. + +| Flag | Default | Description | +| --- | --- | --- | +| `--cluster` | `RIVET_CLUSTER` / linked cluster | Cluster to read. Required. | +| `--source` | both | `engine` or `runner`. | +| `--pool` | all pools | Only this runner pool's logs. Cannot be combined with `--source engine`. | +| `--severity` | all | Only lines at this severity or above: `trace`, `debug`, `info`, `warn`, `error`, or `fatal`. | +| `--contains` | — | Only lines containing this text, case-insensitive. | +| `-n`, `--limit` | `100` | Number of recent lines to print first, up to 1000. | +| `-f`, `--follow` | — | Keep printing new lines as they arrive. | +| `--before` | — | Page back: only lines older than this line `id` from `--json` output. | +| `--json` | — | Emit JSON lines instead of formatted output. | +| `--token`, `--org`, `--cloud-api` | — | Same as `rivet deploy`. | + +Lines can arrive up to a minute late, and `--follow` prints them when they do. + +## `rivet engine` + +Runs the bundled `rivet-engine` binary directly. 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 +``` + +## `rivet token` + +Mints a namespace-scoped token for connecting to Rivet. Only the token is printed to stdout, so it can be captured directly by scripts (e.g. `TOKEN=$(rivet token create --kind secret)`). + +```bash +rivet token create --kind secret +``` + +| `--kind` | Prefix | Use | +| --- | --- | --- | +| `secret` | `sk_` | Server-side runner/runtime token. | +| `public` | `pk_` | Client token: resolve and manage actors. | +| `connection` | `ck_` | Client token limited to resolving actors and opening connections. | + +By default the namespace must already exist. Pass `--create-namespace` to create it first (useful for per-branch CI namespaces). + +| Flag | Default | Description | +| --- | --- | --- | +| `--kind` | _(required)_ | Token kind: `secret`, `public`, or `connection`. | +| `--namespace` | `production` | Cloud namespace. | +| `--create-namespace` | — | Create the namespace if it does not exist. | +| `--token` | env / credentials | Rivet Cloud API token. | +| `--project` / `--org` | from token | Override project/org. | + +## Engine binary resolution + +`rivet engine` resolves 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 Cloud](/docs/deploy/self-host/control-plane) diff --git a/vendor/docs/docs/content/connect.mdx b/vendor/docs/docs/content/connect.mdx index e71e630b..b6024eae 100644 --- a/vendor/docs/docs/content/connect.mdx +++ b/vendor/docs/docs/content/connect.mdx @@ -8,11 +8,31 @@ skill: true No configuration is needed. RivetKit runs a local control plane on your machine. -## Production +RivetKit runs Rivet Actors entirely on your machine. Configure `RIVET_ENDPOINT` when connecting to a separate control plane. ### Private endpoint -The private endpoint tells your nodes and backend where the control plane is. It carries a secret token, so keep it server-side. +Everything that talks to Rivet is configured with one URL, the Rivet endpoint. It carries three pieces of information: + +``` +https://:@api.rivet.dev +``` + +- **Host**: The control plane to connect to. Rivet Cloud is `api.rivet.dev`. Self-hosted deployments use their own host. +- **Namespace**: The isolated environment your Actors live in, such as `production` or `staging`. See [Namespaces](/docs/namespaces). +- **Token**: Authenticates the connection and scopes it to that namespace. The token prefix tells you where it is safe to use: + +| Prefix | Kind | Where it lives | +| --- | --- | --- | +| `sk_` | Secret | Your backend and workers only. Full access to the namespace. | +| `pk_` | Public | Browsers and mobile apps. Can resolve, create, and connect to Actors. | +| `ck_` | Connection | Browsers and mobile apps. Can only resolve Actors and open connections. | + +Create tokens in the dashboard under your namespace. + +## Connect Your Backend + +Set `RIVET_ENDPOINT` with a secret token wherever your workers run. This is the only setting most deployments need. @@ -34,13 +54,13 @@ RIVET_ENDPOINT=https://my-namespace:@rivet.example.com ## URL auth syntax -Endpoint URLs carry the namespace and token: +Clients reach your Actors through the control plane's gateway, never through your backend directly. Point them at the same endpoint with a public token instead of a secret one. -``` -https://namespace:token@host/path -``` +Pass the public endpoint directly when constructing a [JavaScript](/actors/docs/clients/javascript), [React](/actors/docs/clients/react), [Swift](/actors/docs/clients/swift), or [Rust](/actors/docs/clients/rust) client. + +## URL Auth Syntax -This is the recommended form. Alternatively, set them separately: +Embedding the namespace and token in the URL is the recommended form because it is one value to manage. If your platform cannot store credentials inside a URL, the same three values can be set separately: ```bash RIVET_ENDPOINT=https://rivet.example.com @@ -48,7 +68,11 @@ RIVET_NAMESPACE=my-namespace RIVET_TOKEN= ``` -On a self-hosted control plane, use your [namespace slug](/docs/namespaces) and your Rivet token, the `auth.admin_token` value from the control plane config. For the `default` namespace: `https://default:@rivet.example.com`. +| Environment Variable | Config Option | Description | +| --- | --- | --- | +| `RIVET_ENDPOINT` | `endpoint` | Control plane URL for your backend | +| `RIVET_NAMESPACE` | `namespace` | Namespace to run Actors in | +| `RIVET_TOKEN` | `token` | Token for the backend connection | ## Reference diff --git a/vendor/docs/docs/content/debugging.mdx b/vendor/docs/docs/content/debugging.mdx index e8fc9cb3..470f2b6a 100644 --- a/vendor/docs/docs/content/debugging.mdx +++ b/vendor/docs/docs/content/debugging.mdx @@ -18,7 +18,7 @@ export RIVET_TOKEN="" # export RIVET_API="http://localhost:6420" RIVET_NAMESPACE="default" RIVET_TOKEN="default" ``` -Send the token as a bearer token on every request except `GET /metadata`. Use a token authorized for the selected namespace. On a self-hosted control plane, your Rivet token works in every namespace. See [Namespaces](/docs/namespaces). +Send the token as a bearer token on every request except `GET /metadata`. Use a token authorized for the selected namespace. The self-hosted admin token can access every namespace. See [Namespaces](/docs/namespaces). ## Rivet Actors @@ -38,7 +38,7 @@ curl "$RIVET_API/actors?actor_ids=id1,id2&namespace=$RIVET_NAMESPACE" \ -H "Authorization: Bearer $RIVET_TOKEN" ``` -When `actor_ids` is set, `name` and `key` are ignored. +`actor_ids` cannot be combined with `name` or `key`. ```json { @@ -55,7 +55,7 @@ When `actor_ids` is set, `name` and `key` are ignored. } ``` -Keys in HTTP requests separate components with `/`, escaping `/` and `\` inside a component with a backslash. An empty component is `\0`. For example, the key `["chat", "42"]` is `chat/42`. Use `curl --get --data-urlencode` for keys containing special characters. The single-component key `my-key` needs no escaping. +Keys in HTTP requests use comma-separated components, with commas and backslashes escaped inside a component. Use `curl --get --data-urlencode` for keys containing special characters. The single-component key `my-key` needs no escaping. ### Create a Rivet Actor @@ -68,7 +68,9 @@ curl -X PUT "$RIVET_API/actors?namespace=$RIVET_NAMESPACE" \ -d '{ "name": "my-actor", "key": "my-key" }' ``` -The Rivet Actor runs in the `default` [node pool](/docs/nodes). +The Rivet Actor runs in the `default` node pool. + +Set `"pool": "gpu"` in the request body to select another configured [node pool](/docs/pool-configuration). ### Sleep or destroy a Rivet Actor @@ -85,7 +87,6 @@ curl -X DELETE "$RIVET_API/actors/{actor_id}?namespace=$RIVET_NAMESPACE" \ ```bash curl "$RIVET_API/actors/names?namespace=$RIVET_NAMESPACE" \ -H "Authorization: Bearer $RIVET_TOKEN" -``` ## Nodes @@ -99,8 +100,15 @@ curl "$RIVET_API/pools?namespace=$RIVET_NAMESPACE" \ -H "Authorization: Bearer $RIVET_TOKEN" ``` -Pool configuration persists even when no nodes are connected. See [Nodes & Node Pools](/docs/nodes#read-pools-and-nodes). +Pool configuration persists even when no nodes are connected. + +To check that nodes are connected, call a Rivet Actor from a client. A Rivet Actor stays **Pending** while no node in its node pool is connected. See [Statuses](/docs/statuses). + +## OpenAPI Spec + +An OpenAPI specification covering many of the management and actor endpoints is available: -## API reference +- In the repository at [`rivetkit-openapi/openapi.json`](https://github.com/rivet-dev/rivet/tree/main/rivetkit-openapi) +- Served at `/doc` when running locally Every control plane endpoint, with its parameters and responses, is listed in the [HTTP API reference](/docs/api). diff --git a/vendor/docs/docs/content/endpoints.mdx b/vendor/docs/docs/content/endpoints.mdx new file mode 100644 index 00000000..516bb2e3 --- /dev/null +++ b/vendor/docs/docs/content/endpoints.mdx @@ -0,0 +1,71 @@ +--- +title: "Endpoints" +seoTitle: "Rivet Endpoint Configuration" +description: "Configure private and public Rivet endpoints for your nodes and clients, including namespace credentials and environment variable options." +skill: true +--- + +## Local development + +No configuration is needed. RivetKit runs a local control plane on your machine. + +## Production + +### Private endpoint + +The private endpoint tells your nodes and backend where the control plane is. It carries a secret token, so keep it server-side. + + + + + +```bash +RIVET_ENDPOINT=https://my-namespace:sk_xxxxx@api.rivet.dev +``` + + + + + + + + + + + +### Public endpoint + +The public endpoint is what your app hands to browser clients. It is exposed to the internet, so use a public token (`pk_`), never a secret token (`sk_`). + +```bash +RIVET_PUBLIC_ENDPOINT=https://my-namespace:pk_xxxxx@api.rivet.dev +``` + + +## URL auth syntax + +Endpoint URLs carry the namespace and token: + +``` +https://namespace:token@host/path +``` + +This is the recommended form. Alternatively, set them separately: + +```bash +RIVET_ENDPOINT=https://api.rivet.dev +RIVET_NAMESPACE=my-namespace +RIVET_TOKEN=sk_xxxxx +``` + +On a self-hosted control plane, use your [namespace slug](/docs/namespaces) and admin token. For the `default` namespace: `https://default:@rivet.example.com`. + +## Reference + +| Environment Variable | Config Option | Description | +|---------------------|---------------|-------------| +| `RIVET_ENDPOINT` | `endpoint` | Control plane URL for your nodes and backend | +| `RIVET_NAMESPACE` | `namespace` | Namespace for Rivet Actor isolation | +| `RIVET_TOKEN` | `token` | Token for the control plane connection | +| `RIVET_PUBLIC_ENDPOINT` | | Client-facing endpoint | +| `RIVET_PUBLIC_TOKEN` | | Client-facing token | diff --git a/vendor/docs/docs/content/environment-variables.mdx b/vendor/docs/docs/content/environment-variables.mdx index 75a08495..e3665770 100644 --- a/vendor/docs/docs/content/environment-variables.mdx +++ b/vendor/docs/docs/content/environment-variables.mdx @@ -5,8 +5,7 @@ skill: true --- These variables wire your process to a Rivet control plane. Variables that -configure the RivetKit SDK itself, including logging, storage, and the -inspector, are listed in [Environment Variables](/actors/docs/environment-variables) +configure the RivetKit SDK itself, including logging and storage, are listed in [Environment Variables](/actors/docs/environment-variables) in the Actors docs. ## Connection @@ -22,8 +21,7 @@ in the Actors docs. | Environment Variable | Description | |---------------------|-------------| | `RIVET_POOL` | Node pool to join (default: "default"). See [Architecture](/docs/architecture#node-pools). | -| `RIVET_ENVOY_VERSION` | Version number of this node. Increase it on every deploy. See [Versions & Upgrades](/docs/versions#setting-the-version). | - +| `RIVET_NODE_VERSION` | Version number of this node (default: 1). Increase it on every deploy. `RIVET_ENVOY_VERSION` is still read as an older name. See [Versions & Upgrades](/docs/versions#setting-the-version). | ## Local Control Plane @@ -35,5 +33,5 @@ In development, RivetKit starts a local control plane for you. These variables o | `RIVET_RUN_ENGINE_HOST` | Host the local control plane binds to. Defaults to `127.0.0.1`. | | `RIVET_RUN_ENGINE_PORT` | Port the local control plane binds to. Defaults to `6420`. | | `RIVET_RUN_ENGINE_VERSION` | Version of the local control plane to download | -| `RIVET_RUN_SERVICES` | Set to `1` to run Services with the local control plane, or `0` to disable it. By default, Services runs whenever the local control plane does. | +| `RIVET_RUN_SERVICES` | Services starts by default when RivetKit manages a local Engine. Set to `0` to disable it. | | `RIVET_SERVICES_BINARY` | Path to a local `rivet-services` binary. | diff --git a/vendor/docs/docs/content/jwt.mdx b/vendor/docs/docs/content/jwt.mdx index 34c83bac..85b4d540 100644 --- a/vendor/docs/docs/content/jwt.mdx +++ b/vendor/docs/docs/content/jwt.mdx @@ -4,45 +4,21 @@ description: "Issue short-lived JWTs with explicit permissions in one namespace. skill: true --- -A scoped token is a JWT that gives its holder selected permissions in one namespace. Issue it on your backend with your Rivet token, using the RivetKit client or the HTTP API, then return only the scoped token to the application. +A scoped token gives its holder selected permissions in one namespace. Issue it on your backend with the control plane's admin credential, then return only the scoped token to the application. -## Issue a token +## Quickstart - - + + -Create a client on your backend with your Rivet token in the endpoint: - - - -For one Actor, call `issueToken` on its handle. Without `permissions`, the token can only send traffic to that Actor: - - - -Pass `permissions` to replace the default. The handle accepts `actor_gateway` and `actor` permissions and scopes each grant to the Actor's ID: - - - -For namespace-wide grants, such as creating Actors, call `client.auth.issueToken` with an explicit grant list: - - - -`expiresIn` is in seconds. `issuedAt` and `expiresAt` are Unix timestamps in milliseconds. - - - - -Call `POST /auth/tokens` with your Rivet token. This grants traffic to every Actor in the `production` namespace; use `{"id":""}` as the target for one Actor. +This example grants read access to Actor traffic in the `production` namespace. Every holder can reach every Actor in that namespace; use a specific Actor ID for private per-user access. ```bash -export RIVET_API=https://rivet.example.com - curl -X POST "$RIVET_API/auth/tokens" \ - -H "Authorization: Bearer $RIVET_TOKEN" \ + -H "Authorization: Bearer $RIVET_ADMIN_TOKEN" \ -H 'Content-Type: application/json' \ -d '{ "namespace": "production", - "subject": "user-123", "duration": 900, "grants": [ {"resource":"actor_gateway","target":"any","operations":["read"]} @@ -50,36 +26,22 @@ curl -X POST "$RIVET_API/auth/tokens" \ }' ``` -The response contains `token`, `issued_ts` and `expires_ts`. Timestamps are Unix milliseconds; `duration` is seconds. See [Create token](/docs/api/tokens/create). - - - +The response contains `token`, `issued_ts`, and `expires_ts`. Timestamps are Unix milliseconds; `duration` is seconds. -Return only the scoped token to the client. Never send your Rivet token to a browser. + + -## Use the token - - - - -Give the browser client a `getToken` function that asks your backend for a token. The client calls it on first use and again when the token expires. - - - - - - -Send the token as a bearer token. This request reaches the Actor's [request handler](/actors/docs/request-handler): +Set `$RIVET_TOKEN` to the returned token and call an Actor's gateway endpoint: ```bash -curl "$RIVET_API/gateway/$ACTOR_ID/request/" \ - -H "Authorization: Bearer $SCOPED_TOKEN" +curl "$RIVET_API/gateway/$ACTOR_ID/health" \ + -H "Authorization: Bearer $RIVET_TOKEN" ``` - - +This grant does not allow Actor creation, listing, node registration, or pool management. -A gateway grant does not allow Actor creation, listing, node registration or pool management. + + ## Grants @@ -92,29 +54,13 @@ Each grant names a `resource`, a `target`, and `operations` drawn from `create`, | `node` | Register or inspect nodes. | | `pool` | Read or manage node pool configuration. | -Actor management and gateway access require separate grants. Permission to reach one Actor does not allow the holder to create other Actors. Namespace administration and token issuance require your Rivet token. +Actor management and gateway access require separate grants. Permission to reach one Actor does not allow the holder to create other Actors. Namespace administration and token issuance require the admin credential. ## Inspect and renew ```bash curl "$RIVET_API/auth/tokens/inspect" \ - -H "Authorization: Bearer $SCOPED_TOKEN" -``` - -The response contains the namespace slug, grants, subject, and issue and expiry timestamps. Renew expiring credentials through your authenticated backend. Do not give clients your Rivet token to renew tokens themselves. - -## Signing keys - -The control plane signs tokens with keys it rotates automatically every 7 days. A token lasts 1 hour by default and at most 24 hours. - -To revoke every outstanding token at once, rotate the signing keys immediately. Run these on any control plane instance; they read the instance's configuration: - -```bash -rivet-engine depot jwt show -rivet-engine depot jwt emergency-rotate \ - --request-id \ - --expected-coordinator-epoch \ - --expected-ring-version + -H "Authorization: Bearer $RIVET_TOKEN" ``` -Every instance stops accepting the old tokens within 5 minutes. If the command's reply is lost, run `show` again and retry with the same request ID. +The response contains the namespace slug, grants, subject, and issue and expiry timestamps. Renew expiring credentials through your authenticated backend. Do not give clients the admin credential to renew tokens themselves. diff --git a/vendor/docs/docs/content/namespaces.mdx b/vendor/docs/docs/content/namespaces.mdx index 1d65b0f2..8c2763ea 100644 --- a/vendor/docs/docs/content/namespaces.mdx +++ b/vendor/docs/docs/content/namespaces.mdx @@ -11,11 +11,11 @@ A namespace separates Actors, Actor keys, and node pools on one control plane. E -Use your Rivet token, the `auth.admin_token` value from your control plane config: +Use your control plane's admin token: ```bash curl -X POST "$RIVET_API/namespaces" \ - -H "Authorization: Bearer $RIVET_TOKEN" \ + -H "Authorization: Bearer $RIVET_ADMIN_TOKEN" \ -H 'Content-Type: application/json' \ -d '{"name":"staging"}' ``` @@ -31,12 +31,12 @@ Create a pool in the namespace before connecting nodes: ```bash curl -X PUT "$RIVET_API/pools/default?namespace=staging" \ - -H "Authorization: Bearer $RIVET_TOKEN" \ + -H "Authorization: Bearer $RIVET_ADMIN_TOKEN" \ -H 'Content-Type: application/json' \ -d '{"metadata":{"environment":"staging"}}' ``` -See [Nodes & Node Pools](/docs/nodes) for pool settings. +See [Node Pool Configuration](/docs/pool-configuration) for pool settings. @@ -44,9 +44,11 @@ See [Nodes & Node Pools](/docs/nodes) for pool settings. Set the namespace slug in the node's private endpoint and start the registry with `registry.start()`: ```bash -RIVET_ENDPOINT=https://staging:@rivet.mycompany.com +RIVET_ENDPOINT=https://staging:@rivet.mycompany.com ``` +Use a [scoped token](/docs/deploy/self-host/control-plane/rbac/) for applications. The admin token can access every namespace. + @@ -54,7 +56,7 @@ RIVET_ENDPOINT=https://staging:@rivet.mycompany.com ```bash curl "$RIVET_API/namespaces?limit=100" \ - -H "Authorization: Bearer $RIVET_TOKEN" + -H "Authorization: Bearer $RIVET_ADMIN_TOKEN" ``` The response contains `namespaces` and `pagination.cursor`. Pass the cursor as `cursor` on the next request. Filter a single namespace with `?name=staging`. diff --git a/vendor/docs/docs/content/nodes.mdx b/vendor/docs/docs/content/nodes.mdx index aa8dfed9..135583d0 100644 --- a/vendor/docs/docs/content/nodes.mdx +++ b/vendor/docs/docs/content/nodes.mdx @@ -24,6 +24,8 @@ curl -X PUT "$RIVET_API/pools/default?namespace=$RIVET_NAMESPACE" \ The control plane refuses a node whose pool does not exist. Local development is the exception: when the endpoint is on `localhost`, RivetKit creates the pool itself. +If registration or a reconnect fails with `pool.not_found`, the node stops and reports its namespace and pool. A cached serverless runtime returns the same error and reports unhealthy until restarted. Check the configured namespace and pool, or restore the original control-plane storage before restarting the node. Recreating a pool does not restore lost Actor data. Transient connection failures still reconnect automatically. + diff --git a/vendor/docs/docs/content/pool-configuration.mdx b/vendor/docs/docs/content/pool-configuration.mdx new file mode 100644 index 00000000..58a92301 --- /dev/null +++ b/vendor/docs/docs/content/pool-configuration.mdx @@ -0,0 +1,69 @@ +--- +title: "Node Pool Configuration" +description: "Create persistent node pool configuration and connect nodes by pool name." +skill: true +--- + +A node pool groups nodes by name within a namespace. Configure the pool explicitly before connecting nodes to it. For example, use `default` for general workloads and `gpu` for Actors that need GPUs. + +## Quickstart + + + + +Set `$RIVET_API` to your control plane URL, `$RIVET_NAMESPACE` to the namespace slug, and `$RIVET_TOKEN` to a token with pool management access: + +```bash +curl -X PUT "$RIVET_API/pools/gpu?namespace=$RIVET_NAMESPACE" \ + -H "Authorization: Bearer $RIVET_TOKEN" \ + -H 'Content-Type: application/json' \ + -d '{"metadata":{"hardware":"gpu"}}' +``` + +Pool configuration persists independently of connected nodes. Repeating `PUT` updates the configuration. + + + + +Set the same namespace and pool on each node that should serve this workload: + +```bash +RIVET_ENDPOINT=https://staging:@rivet.mycompany.com +RIVET_POOL=gpu +``` + +Start your registry with `registry.start()`. Metadata describes the pool; it does not provision hardware. Run the nodes on the machines you want to use. + + + + +## Read configuration + +```bash +# One pool +curl "$RIVET_API/pools/gpu?namespace=$RIVET_NAMESPACE" \ + -H "Authorization: Bearer $RIVET_TOKEN" + +# All configured pools in the namespace +curl "$RIVET_API/pools?namespace=$RIVET_NAMESPACE" \ + -H "Authorization: Bearer $RIVET_TOKEN" +``` + +The single-pool response has a `pool` object. The list response has a `pools` array whose entries include their `name`, plus a `pagination.cursor` for the next page. A configured pool remains listed when no nodes are connected. + +## Remove a pool + +Stop its nodes before deleting its configuration: + +```bash +curl -X DELETE "$RIVET_API/pools/gpu?namespace=$RIVET_NAMESPACE" \ + -H "Authorization: Bearer $RIVET_TOKEN" +``` + +The control plane rejects deletion while nodes are connected. + +## Related + +- [Nodes](/docs/workers) +- [Namespaces](/docs/namespaces) +- [Deploy Nodes](/docs/deploy/self-host/workers/) diff --git a/vendor/docs/docs/content/regions.mdx b/vendor/docs/docs/content/regions.mdx new file mode 100644 index 00000000..09c5ab30 --- /dev/null +++ b/vendor/docs/docs/content/regions.mdx @@ -0,0 +1,23 @@ +--- +title: "Regions" +description: "Choose the region a Rivet Actor runs in on Rivet Cloud, and how regions work when you self-host." +skill: true +--- + +A Rivet Actor lives in one region for its lifetime, and its data stays in that region. + +## Region selection on Rivet Cloud + +By default, a Rivet Actor is created in the region nearest the client. Override the region at create time: + + + +See [Actor-Actor Communication](/actors/docs/communicating-between-actors) for the full set of create options. + +## Where a Rivet Actor stays + +A Rivet Actor does not move between regions. Reads and writes to its data never cross a region boundary. A call between Rivet Actors in different regions goes over the network, so treat it like any other remote call. + +## Self-hosting + +A self-hosted control plane runs in one region. Multi-region self-hosting is coming. diff --git a/vendor/docs/docs/content/statuses.mdx b/vendor/docs/docs/content/statuses.mdx new file mode 100644 index 00000000..b550e3b7 --- /dev/null +++ b/vendor/docs/docs/content/statuses.mdx @@ -0,0 +1,18 @@ +--- +title: "Actor Statuses" +description: "Understand the lifecycle statuses of Rivet Actors, what they mean, and how to troubleshoot common issues." +skill: true +--- + +## Statuses + +| Status | Description | +|---|---| +| **Starting** | The Rivet Actor has been created but is not yet accepting connections. | +| **Running** | The Rivet Actor is live and accepting connections. | +| **Sleeping** | The Rivet Actor went to sleep after a period of inactivity. It wakes automatically on the next request. | +| **Pending** | The Rivet Actor is waiting for a node. This happens when no node in its node pool is connected. See [Troubleshooting](/actors/docs/troubleshooting#actor-status-is-pending) for common causes. | +| **Crashed** | The Rivet Actor failed to start or hit a fatal error. See [Troubleshooting](/actors/docs/troubleshooting#actor-status-is-crashed) for common failure reasons. | +| **Destroyed** | The Rivet Actor was destroyed. | + +{/* TODO(code): document the status timestamps in the actor API (`create_ts` plus start, sleep and destroy times) once the control plane returns them. Today `GET /actors` returns only `create_ts`, and destroyed Rivet Actors are not listed. Also confirm whether "Crash Loop Backoff" still exists. */} diff --git a/vendor/docs/docs/content/troubleshooting.mdx b/vendor/docs/docs/content/troubleshooting.mdx new file mode 100644 index 00000000..44906a7b --- /dev/null +++ b/vendor/docs/docs/content/troubleshooting.mdx @@ -0,0 +1,83 @@ +--- +title: "Troubleshooting" +description: "Troubleshoot Rivet control plane connectivity, deployment configuration, and persistent logs with practical diagnostic checks." +skill: true +--- + +## 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](/actors/docs/logging) for more options. + +## Persistent logs require durable object storage + +If startup reports `storage.log_directory requires durable object_storage.primary`, configure a durable primary object store. Setting `storage.log_directory` alone persists log shards, but cluster metadata and cold data live in `object_storage.primary`. Its default, `in_memory`, loses those objects when the control plane process exits. + +For a single control plane process, set `RIVET__OBJECT_STORAGE__PRIMARY__FILE_SYSTEM__PATH` to a directory on a persistent volume. Multiple control plane processes must share a durable object store such as S3, GCS, or Azure Blob Storage. See [Storage](/docs/deploy/self-host/control-plane/storage/). + +### Missing records after a restart + +If logs report `a volume has no stored record but a replica reports a cold position above zero` and requests fail with `depot.corrupt`, the primary object store is missing data that the surviving log shards expect. This can happen after restarting with an in-memory primary store, or after pointing an existing deployment at an empty store. + +Preserve the log directory and any remaining object storage before attempting recovery. If the original durable object store still exists, check its configured location, bucket or container, and prefix. Pointing at a new empty store does not recover existing data. Storage inspection and repair cannot reconstruct cold data that has already been lost and trimmed from the log shards. Contact support with the configuration and logs before changing stored data. + +## 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, hosting platform in use (e.g. Kubernetes, Railway, AWS ECS) + - If applicable, HTTP router in use (e.g. Hono, Express, Elysia) + +## Actor status is crashed + +See [Actor Statuses](/docs/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. + +{/* TODO(code): list the allocation failure reasons the control plane reports for a Rivet Actor once it records them. Today only a crash in your own code is reported. */} + +### `internal_error` + +An unexpected error occurred within Rivet. If this persists, please [contact support](https://rivet.dev/actors/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 + +During a rollout, old and new nodes both run Rivet Actors until the old nodes stop. A Rivet Actor started on an old node keeps running the old code until it sleeps or its node stops. Finish the rollout by stopping every old node. See [Versions & Upgrades](/docs/versions). + +## Actor status is pending + +See [Actor Statuses](/docs/statuses) for more about this status. + +An actor stays in "pending" status while no node in its node pool is connected to accept it. + +- Check that at least one node is running and that its `RIVET_ENDPOINT` points at the right control plane and namespace. +- Check that the node joined the node pool the actor runs in. A node joins the pool named by `RIVET_POOL`, or `default`. +- Check your node logs for connection errors. diff --git a/vendor/docs/docs/content/versions.mdx b/vendor/docs/docs/content/versions.mdx index 0da41316..e2120550 100644 --- a/vendor/docs/docs/content/versions.mdx +++ b/vendor/docs/docs/content/versions.mdx @@ -1,244 +1,25 @@ --- title: "Versions & Upgrades" -description: "Version node deployments, route new Rivet Actors to current code, and drain older nodes without downtime." +description: "Roll out new code to your nodes and upgrade Rivet Actor state without downtime." skill: true --- -## How Versions Work +## Rolling out a new version -Each node has a **version number**. When you deploy new code with a new version, Rivet handles the transition automatically: +Deploy new code by rolling your nodes like any other service. When a node stops, its Rivet Actors sleep and save their state, then wake on a running node on their next request. During the rollout, old and new nodes both run Rivet Actors, so keep each deploy compatible with the state the previous one wrote. See [Upgrading Actor State](#upgrading-actor-state). -- **New actors go to the newest version**: When allocating actors, Rivet always prefers nodes 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 node connecting with a newer version number gracefully stops old actors so they are rescheduled to the new version -- **Versions only go up**: Deploying a lower version number never moves allocation back to it. To undo a bad deploy, redeploy the previous code with a new, higher version number. +### Setting the version - -Versions are not configured by default. See [Setting the Version](#setting-the-version) to configure the node version. - +Set `RIVET_NODE_VERSION` to a number that increases on every deploy. Within a node pool, new and waking Rivet Actors are only placed on nodes running the newest version that has a connected node, so as soon as the first node of a deploy connects, the previous deploy stops receiving new actors. Actors already running on older nodes keep running until those nodes stop. A version whose nodes have all disconnected is ignored, so a failed deploy does not block placement. -### Example Scenario +Without `RIVET_NODE_VERSION`, every node reports version 1 and placement spreads across all connected nodes in the pool. The version of each node is shown in the node listing API as `version`. - +### Drain on version upgrade - +Placement alone leaves Rivet Actors that never sleep on the old nodes until those nodes stop. Set `drain_on_version_upgrade` on the pool to move them as well: once a node of a newer version is connected, actors on older nodes are stopped the same way a node shutdown stops them, and they wake on the newest version on their next request. Eviction starts after `actor_eviction_delay` seconds and is paced by `actor_eviction_period` and `actor_eviction_rate`, so a large pool is not rescheduled all at once. See [Pool settings](/docs/nodes#pool-settings). -When a new version is deployed, existing actors are gracefully stopped on the old node and rescheduled onto the new version. +The older nodes stay connected and keep receiving heartbeats, but no new actors are placed on them. Rivet never exits a node process; stopping or replacing the old nodes is up to whatever runs them. If every node of the newer version disconnects, the drain stops and the older nodes serve again. A pool's settings are read when a node connects, so changing them applies to nodes that connect afterwards. -
-
- - - - - - - Node v1 - - - Node v2 - - Currently running - - Deployed - - Drain old actors - - - Reschedule actors - - - Shut down when all actors stopped - -
-
- - - - - -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 they sleep. - -
-
- - - - Node v1 - - - Node v2 - - Currently running - - Deployed - - Actor 1 sleeps from inactivity - - Actor 1 wakes up when prompted - -
-
- -
- - - -## Configuration - -### Setting the Version - -Configure the node version with an environment variable or in your registry setup: - - - -```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. - - - -The version **must** increase on every deployment. Never roll a deployment back to an older version number. New actors keep going to the nodes still on the higher version, so as the rollback replaces them the set of nodes accepting actors shrinks until it reaches zero and nothing can be scheduled. Allocation only recovers once every higher-version node has disconnected and expired. To undo a bad deploy, redeploy the previous code with a new, higher version number. - - -### Example Configurations - -We recommend injecting a build-time value that increments with every deployment. Here are concrete examples for common setups: - - - - - -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:24-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. - - - - - -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; -``` - - - - - -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)) - ), - }, -}); -``` - - - - - -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) -``` - - - - - -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 `drain_on_version_upgrade` setting on a node pool controls whether old actors are stopped when nodes with a newer version connect. Set it when you create or update the pool. See [Nodes & Node Pools](/docs/nodes) for every pool setting, including how to rate-limit actor eviction during the drain. - -```bash -curl -X PUT "$RIVET_API/pools/default?namespace=default" \ - -H "Authorization: Bearer $RIVET_TOKEN" \ - -H 'Content-Type: application/json' \ - -d '{"drain_on_version_upgrade": true}' -``` - -| Value | Behavior | -|-------|----------| -| `false` (default) | Old actors keep running. New actors go to the new version. Versions coexist. | -| `true` | Old actors sleep, saving their state, and wake on a node running the new version. | ## Upgrading Actor State @@ -274,30 +55,11 @@ Use [Zod](https://zod.dev/) to parse persisted state on wake. Zod's `.default()` For anything beyond simple defaults, consider moving to [SQLite](/actors/docs/sqlite) where you get proper migration tooling. -## Advanced - -### How Version Upgrade Detection Works - -When `drain_on_version_upgrade` is enabled and a node connects with a newer version number, the control plane drains every older node in the same pool. - -### SIGTERM Handling - -When a node process receives `SIGTERM`, it gracefully stops all of its actors before exiting: - -- Each actor's `onSleep` hook runs, giving it time to save state. -- Actors are rescheduled to other available nodes. -- If the process is killed before its actors finish stopping, for example with `SIGKILL`, the control plane reschedules them once the node is considered lost. - -Give each node at least 60 seconds between `SIGTERM` and `SIGKILL`. - -### Shutdown Timeouts +## Stopping a node -| Timeout | Default | Description | Configuration | -|---------|---------|-------------|---------------| -| `sleepGracePeriod` | 15s | Total graceful sleep budget for `onSleep`, `waitUntil`, `keepAwake`, and async raw WebSocket handlers | [Actor options](/actors/docs/lifecycle#options) | -| `envoy_lost_threshold` | 40s | How long the control plane waits before rescheduling the actors of a node that disappeared without stopping | [Control plane config](/docs/deploy/self-host/control-plane/configuration/) (`pegboard.envoy_lost_threshold`) | +When a node receives `SIGTERM`, it puts each of its Rivet Actors to sleep before exiting. Each Rivet Actor gets up to 15 seconds to save its state. If a node disappears without stopping, the control plane reschedules its Rivet Actors after 15 seconds. ## Related -- [Nodes & Node Pools](/docs/nodes): Pool settings and connecting nodes -- [Lifecycle](/actors/docs/lifecycle): Actor lifecycle hooks including `onSleep` +- [Architecture](/docs/architecture) +- [Deploy nodes](/docs/deploy/self-host/workers/) diff --git a/vendor/docs/docs/content/workers.mdx b/vendor/docs/docs/content/workers.mdx new file mode 100644 index 00000000..463bda31 --- /dev/null +++ b/vendor/docs/docs/content/workers.mdx @@ -0,0 +1,30 @@ +--- +title: "Nodes" +description: "Run your registry as a node, select a node pool, and add capacity." +skill: true +--- + +A node is a long-running process running your app with the Rivet SDK. Start it with `registry.start()`. It connects outbound to the control plane and hosts the Actors assigned to it. + +## Connect a node + +Create a [namespace](/docs/namespaces) and [node pool](/docs/pool-configuration), then set the node's connection variables: + +```bash +RIVET_ENDPOINT=https://production:@rivet.mycompany.com +RIVET_POOL=default +``` + +`RIVET_POOL` defaults to `default`. The pool must be configured in the selected namespace before the node connects. + +Actor traffic uses the outbound connection, so nodes need no public URL or inbound port. Your app can expose its own [health and metrics routes](/actors/docs/http-server#add-health-and-metrics-routes). + +## Add capacity + +Run more copies of your app with the same namespace and pool. Use your hosting platform's autoscaler to change the number of node processes. Keep spare capacity so Actors can wake while another node restarts. + +When a node stops, its Actors sleep and can wake on another node in the pool. See [Versions & Upgrades](/docs/versions) for rolling deployments. + +## Deploy + +Use the shared [node deployment guides](/docs/deploy/self-host/workers/) for Kubernetes, Docker, virtual machines, and other platforms that run a persistent process. Nodes can connect to Rivet Cloud or a self-hosted control plane. diff --git a/vendor/docs/docs/sidebar.json b/vendor/docs/docs/sidebar.json index 24dd667a..e507a9da 100644 --- a/vendor/docs/docs/sidebar.json +++ b/vendor/docs/docs/sidebar.json @@ -15,15 +15,26 @@ ] }, { - "title": "Connect", + "title": "Running Workloads", "pages": [ { - "title": "Connect", - "href": "/docs/connect" + "title": "Namespaces", + "href": "/docs/namespaces" + }, + { + "title": "Nodes", + "href": "/docs/workers" + }, + { + "title": "Node Pool Configuration", + "href": "/docs/pool-configuration" + }, + { + "title": "Endpoints", + "href": "/docs/endpoints" }, { "title": "Authentication", - "collapsible": true, "pages": [ { "title": "Overview", @@ -33,60 +44,55 @@ "title": "Scoped Tokens", "href": "/docs/jwt" } - ] + ], + "collapsible": true + }, + { + "title": "Environment Variables", + "href": "/docs/environment-variables" + }, + { + "title": "Versions & Upgrades", + "href": "/docs/versions" + }, + { + "title": "Regions", + "href": "/docs/regions" } ] }, { - "title": "Operate", + "title": "Operating", "pages": [ { - "title": "Namespaces", - "href": "/docs/namespaces" + "title": "Statuses", + "href": "/docs/statuses" }, { - "title": "Nodes & Node Pools", - "href": "/docs/nodes" + "title": "Tracing", + "href": "/docs/tracing" }, { - "title": "Versions & Upgrades", - "href": "/docs/versions" + "title": "Debugging", + "href": "/docs/debugging" }, { - "title": "Monitoring", - "collapsible": true, - "pages": [ - { - "title": "OpenTelemetry", - "href": "/docs/tracing" - }, - { - "title": "Management API", - "href": "/docs/debugging" - } - ] + "title": "Troubleshooting", + "href": "/docs/troubleshooting" } ] }, { "title": "Reference", "pages": [ - { - "title": "Environment Variables", - "href": "/docs/environment-variables" - }, { "title": "CLI", "href": "/docs/cli" }, { - "title": "API Specs", + "title": "API Reference", "collapsible": true, "pages": [ - { - "title": "HTTP API", - "href": "/docs/api" - }, { "title": "OpenAPI", "href": "https://github.com/rivet-dev/rivet/tree/main/rivetkit-openapi", diff --git a/vendor/docs/engine/artifacts/config-schema.json b/vendor/docs/engine/artifacts/config-schema.json index 2cb4c431..77abed1f 100644 --- a/vendor/docs/engine/artifacts/config-schema.json +++ b/vendor/docs/engine/artifacts/config-schema.json @@ -20,28 +20,6 @@ "auth": { "$ref": "#/definitions/Auth" }, - "compute_gateway": { - "default": null, - "anyOf": [ - { - "$ref": "#/definitions/ComputeGateway" - }, - { - "type": "null" - } - ] - }, - "depot": { - "default": null, - "anyOf": [ - { - "$ref": "#/definitions/DepotConfig" - }, - { - "type": "null" - } - ] - }, "epoxy": { "default": null, "anyOf": [ @@ -75,17 +53,6 @@ } ] }, - "metadata": { - "default": null, - "anyOf": [ - { - "$ref": "#/definitions/Metadata" - }, - { - "type": "null" - } - ] - }, "metrics": { "default": { "hardware": { @@ -101,6 +68,17 @@ } ] }, + "migrate_from_v2": { + "default": null, + "anyOf": [ + { + "$ref": "#/definitions/MigrateFromV2" + }, + { + "type": "null" + } + ] + }, "object_storage": { "default": null, "anyOf": [ @@ -164,6 +142,17 @@ } ] }, + "storage": { + "default": null, + "anyOf": [ + { + "$ref": "#/definitions/StorageConfig" + }, + { + "type": "null" + } + ] + }, "telemetry": { "default": { "enabled": true @@ -189,7 +178,7 @@ "additionalProperties": false, "definitions": { "AlarmConfig": { - "description": "Optional overrides for the tablet frontier and worker-owned callback pool.", + "description": "Optional overrides for the partition frontier and worker-owned callback pool.", "type": "object", "properties": { "handler_timeout_ms": { @@ -448,7 +437,7 @@ "minimum": 0.0 }, "disk_cache_directory": { - "description": "Directory for the local disk cache of cold-store object parts. Defaults to `cold-cache` inside `depot.log_directory`. Its contents are disposable.", + "description": "Directory for the local disk cache of cold-store object parts. Defaults to `cold-cache` inside `storage.log_directory`. Its contents are disposable.", "type": [ "string", "null" @@ -462,7 +451,7 @@ ] }, "disk_cache_on_flush": { - "description": "Writes each SST a tablet flush uploads into the disk cache as well, so its first reads are local. Defaults to true.", + "description": "Writes each SST a partition flush uploads into the disk cache as well, so its first reads are local. Defaults to true.", "type": [ "boolean", "null" @@ -478,7 +467,7 @@ "minimum": 0.0 }, "l0_max_ssts": { - "description": "L0 SSTs a tablet database holds before SlateDB stalls memtable flushes until a compaction drains them. One compaction takes up to this many sources, so it drains a full L0 at once. Higher values absorb flush bursts at the cost of more SSTs per read. Defaults to 16.", + "description": "L0 SSTs a partition database holds before SlateDB stalls memtable flushes until a compaction drains them. One compaction takes up to this many sources, so it drains a full L0 at once. Higher values absorb flush bursts at the cost of more SSTs per read. Defaults to 16.", "type": [ "integer", "null" @@ -487,7 +476,7 @@ "minimum": 0.0 }, "max_concurrent_compactions": { - "description": "Concurrent compactions per tablet database. A worker maintaining more than 8 tablets runs one per database. Defaults to 2.", + "description": "Concurrent compactions per partition database. A worker maintaining more than 8 partitions runs one per database. Defaults to 2.", "type": [ "integer", "null" @@ -505,7 +494,7 @@ "minimum": 0.0 }, "unflushed_budget_bytes": { - "description": "Unflushed memtable bytes for the whole process, split evenly across the tablets this worker maintains (at most 512 MiB and at least 8 MiB per tablet). Defaults to 1 GiB.", + "description": "Unflushed memtable bytes for the whole process, split evenly across the partitions this worker maintains (at most 512 MiB and at least 8 MiB per partition). Defaults to 1 GiB.", "type": [ "integer", "null" @@ -517,7 +506,7 @@ "additionalProperties": false }, "CommandConcurrency": { - "description": "Maximum simultaneous handlers per pegboard command class on one storage worker. Each value is further capped by `depot.kv_commands.max_inflight_commands`.", + "description": "Maximum simultaneous handlers per pegboard command class on one storage worker. Each value is further capped by `storage.kv_commands.max_inflight_commands`.", "type": "object", "properties": { "actor": { @@ -530,7 +519,7 @@ "minimum": 0.0 }, "actor_event": { - "description": "Actor state machine events, including envoy lifecycle reports. Defaults to 256.", + "description": "Actor state machine events, including node lifecycle reports. Defaults to 256.", "type": [ "integer", "null" @@ -559,29 +548,6 @@ }, "additionalProperties": false }, - "ComputeGateway": { - "type": "object", - "required": [ - "auth_token", - "cloud_url" - ], - "properties": { - "auth_token": { - "description": "Token for authenticating with the Cloud API's internal route endpoint.", - "allOf": [ - { - "$ref": "#/definitions/Secret" - } - ] - }, - "cloud_url": { - "description": "URL of the Cloud API (e.g., \"https://cloud.rivet.dev/internal/route\")", - "type": "string", - "format": "uri" - } - }, - "additionalProperties": false - }, "Datacenter": { "type": "object", "required": [ @@ -653,233 +619,201 @@ } ] }, - "DepotConfig": { + "Epoxy": { + "description": "Epoxy runs in a single datacenter with fixed membership, so it has no settings yet. The section stays so multi-region settings have a home.", + "type": "object", + "additionalProperties": false + }, + "FileSystemObjectStoreConfig": { + "description": "A local directory used by one engine process at a time. Conditional writes are atomic only among requests of that process.", "type": "object", + "required": [ + "path" + ], "properties": { - "alarms": { - "description": "Bounded, deadline-driven volume alarm delivery.", - "default": { - "handler_timeout_ms": null, - "io_timeout_ms": null, - "max_callbacks": null, - "max_candidates": null, - "max_operations": null, - "page_size": null, - "retry_max_ms": null, - "retry_min_ms": null - }, - "allOf": [ - { - "$ref": "#/definitions/AlarmConfig" - } - ] - }, - "auto_provision_tablets": { - "description": "Creates this many tablets over this storage worker when the cluster has none, so a single-node engine serves actors without an operator. A restart finishes a run that stopped partway; tablets it did not start are left alone. Clusters with several storage workers leave it unset and run `depot tablet provision` instead.", - "type": [ - "integer", - "null" - ], + "path": { + "description": "Created when it does not exist.", + "type": "string" + } + }, + "additionalProperties": false + }, + "FilesystemConfig": { + "type": "object", + "properties": { + "max_cache_bytes": { + "description": "Bytes one filesystem's row cache may retain, a fairness ceiling inside `max_process_cache_bytes`.", + "default": 8388608, + "type": "integer", "format": "uint", "minimum": 0.0 }, - "balancing": { - "default": { - "destination_stability_ms": 60000, - "enabled": true, - "max_disk_probes": 4, - "missing_worker_ms": 120000, - "tablets_per_core": 2 - }, - "allOf": [ - { - "$ref": "#/definitions/TabletBalancingConfig" - } - ] - }, - "bulk_read_bytes_per_sec": { - "description": "Bytes per second that bulk runs may read across every tablet of this process, with a burst of a quarter second (at least 4 MiB). Bulk runs wait for their bytes; point reads never wait. Unset or 0 leaves bulk runs unpaced.", - "type": [ - "integer", - "null" - ], - "format": "uint64", + "max_conflict_window_bytes": { + "default": 4194304, + "type": "integer", + "format": "uint", "minimum": 0.0 }, - "bulk_read_cache_blocks": { - "description": "Whether SST blocks a bulk run fetches enter the shared block cache. Defaults to true; false keeps large scans from evicting other volumes' hot blocks.", - "type": [ - "boolean", - "null" - ] + "max_memory_bytes": { + "default": 67108864, + "type": "integer", + "format": "uint", + "minimum": 0.0 }, - "bulk_read_fetch_tasks": { - "description": "Concurrent SST block fetches inside one bulk run's scan. Defaults to 1; 0 is treated as 1.", - "type": [ - "integer", - "null" - ], + "max_operation_bytes": { + "default": 8388608, + "type": "integer", "format": "uint", "minimum": 0.0 }, - "bulk_read_min_chunks": { - "description": "Chunk runs of at least this many chunks (4 KiB each) are read with the bulk scan settings below, as a table scan's read-ahead windows are. Unset or 0 reads every run with the normal settings.", - "type": [ - "integer", - "null" - ], + "max_pending_commits": { + "default": 128, + "type": "integer", "format": "uint", "minimum": 0.0 }, - "checkpoints": { - "default": { - "max_named_checkpoints_per_volume": 64, - "retention": null - }, - "allOf": [ - { - "$ref": "#/definitions/CheckpointConfig" - } - ] + "max_process_cache_bytes": { + "description": "Bytes all filesystem row caches in one process may retain together.", + "default": 1073741824, + "type": "integer", + "format": "uint", + "minimum": 0.0 }, - "cold_store": { - "default": { - "block_cache_bytes": null, - "compacted_sst_bytes": null, - "disk_cache_bytes": null, - "disk_cache_directory": null, - "disk_cache_on_compaction": null, - "disk_cache_on_flush": null, - "disk_cache_part_bytes": null, - "l0_max_ssts": null, - "max_concurrent_compactions": null, - "meta_cache_bytes": null, - "unflushed_budget_bytes": null - }, - "allOf": [ - { - "$ref": "#/definitions/ColdStoreConfig" - } - ] + "max_snapshot_age_ms": { + "default": 30000, + "type": "integer", + "format": "uint64", + "minimum": 0.0 }, - "cold_store_compression": { - "description": "Codec for SlateDB SST blocks in the cold store; defaults to `lz4`. Each SST records its own codec, so changing it keeps existing SSTs readable.", - "anyOf": [ - { - "$ref": "#/definitions/ColdStoreCompression" - }, - { - "type": "null" - } - ] + "max_snapshots": { + "default": 128, + "type": "integer", + "format": "uint", + "minimum": 0.0 }, - "command_timeout_ms": { - "description": "Floor for every typed Depot command deadline, in milliseconds. Each command keeps its own default (5 seconds generic, 30 seconds for actor storage); when set, the larger of the two bounds both the client's retry budget and the maintainer's handler timeout. Unset keeps the per-command defaults.", - "type": [ - "integer", - "null" - ], + "max_transaction_age_ms": { + "default": 300000, + "type": "integer", "format": "uint64", "minimum": 0.0 }, - "coordinator_request_capacity": { - "description": "Exact coordinator subject capacity. Defaults to 256.", - "type": [ - "integer", - "null" - ], + "max_transaction_bytes": { + "description": "Maximum application bytes in one filesystem transaction.", + "default": 134217728, + "type": "integer", "format": "uint", "minimum": 0.0 }, - "durable_max_events_per_commit": { - "description": "Most events of one durable state machine applied in one commit. Events that arrive while a commit is in flight are grouped up to this bound. Defaults to 16.", - "type": [ - "integer", - "null" - ], - "format": "uint32", + "max_unapplied_bytes": { + "description": "Most payload bytes one opening may have admitted ahead of the newest position its partition's maintainer reported applied. A new write past it is refused with a retry hint before it is built, so a writer faster than the apply, as under a slow cold store or compaction, waits at admission instead of growing an unbounded backlog.", + "default": 268435456, + "type": "integer", + "format": "uint", "minimum": 0.0 }, - "endpoint_cache_capacity": { - "description": "Maximum cached endpoint hints, including missing workers. Defaults to 4096.", - "default": null, - "type": [ - "integer", - "null" - ], + "max_value_bytes": { + "default": 1048576, + "type": "integer", "format": "uint", "minimum": 0.0 }, - "filesystem": { - "default": { - "max_cache_bytes": 8388608, - "max_conflict_window_bytes": 4194304, - "max_memory_bytes": 67108864, - "max_operation_bytes": 8388608, - "max_pending_commits": 128, - "max_process_cache_bytes": 1073741824, - "max_snapshot_age_ms": 30000, - "max_snapshots": 128, - "max_transaction_age_ms": 300000, - "max_transaction_bytes": 134217728, - "max_unapplied_bytes": 268435456, - "max_value_bytes": 1048576, - "note_ttl_ms": 1000, - "request_timeout_ms": 30000 - }, - "allOf": [ + "note_ttl_ms": { + "description": "How long a write's overlay notes (\"this page is at this sequence\") outlive the write before the filesystem drops them and keeps their rows only as cache. A later miss on one of those rows reads it from the maintainer once it has applied the write. About ten times the usual apply latency.", + "default": 1000, + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "request_timeout_ms": { + "default": 30000, + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + "ForkConfig": { + "description": "Global bounds for source hold cleanup and background cross-region copies.", + "type": "object", + "properties": { + "check_grace_ms": { + "description": "Minimum age before an unfinished fork's destination is checked.", + "default": 60000, + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "materialize_batch_chunks": { + "description": "Further bounded by the maintainer's row and byte budgets.", + "default": 1024, + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "max_concurrent_materializations": { + "default": 2, + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + "GcsObjectStoreConfig": { + "description": "Google Cloud Storage; defaults to the adapter's application default credentials.", + "type": "object", + "required": [ + "bucket" + ], + "properties": { + "bucket": { + "type": "string" + }, + "prefix": { + "default": null, + "type": [ + "string", + "null" + ] + }, + "service_account_key": { + "description": "Optional JSON service account credential override.", + "default": null, + "anyOf": [ { - "$ref": "#/definitions/FilesystemConfig" + "$ref": "#/definitions/Secret" + }, + { + "type": "null" } ] - }, - "flush_piggyback_min_bytes": { - "description": "Unflushed applied bytes at which a volume joins a tablet flush another volume requested. Defaults to 16 KiB.", + } + }, + "additionalProperties": false + }, + "Guard": { + "type": "object", + "properties": { + "host": { + "description": "Host for HTTP traffic", "type": [ - "integer", + "string", "null" ], - "format": "uint64", - "minimum": 0.0 + "format": "ip" }, - "flush_stage_concurrency": { - "description": "Volume records whose old rows one tablet filesystem flush reads at once while it stages. Staging excludes every other store operation of the tablet. Defaults to 16.", + "port": { + "description": "Port for HTTP traffic", "type": [ "integer", "null" ], - "format": "uint", + "format": "uint16", "minimum": 0.0 }, - "forks": { - "default": { - "check_grace_ms": 60000, - "materialize_batch_chunks": 1024, - "max_concurrent_materializations": 2 - }, - "allOf": [ - { - "$ref": "#/definitions/ForkConfig" - } - ] - }, - "heartbeat_gc": { - "description": "Background heartbeat reclamation; separate from membership lease expiration.", - "default": { - "batch_interval_ms": null, - "batch_size": null, - "batch_timeout_ms": null, - "delete_concurrency": null, - "pass_interval_ms": null, - "retention_ms": null - }, - "allOf": [ - { - "$ref": "#/definitions/HeartbeatGcConfig" - } - ] - }, - "idle_unload_after_ms": { + "route_auth_check_timeout_ms": { + "description": "Timeout for guard-owned route authorization checks in milliseconds.", "type": [ "integer", "null" @@ -887,28 +821,8 @@ "format": "uint64", "minimum": 0.0 }, - "kv_commands": { - "default": { - "evicted_idle_warn_ms": null, - "max_command_bytes": null, - "max_eviction_closes": null, - "max_inflight_command_bytes": null, - "max_inflight_commands": null, - "max_open_volumes": null, - "max_worker_open_volume_bytes": null, - "max_worker_open_volumes": null, - "request_capacity": null, - "short_idle_close_ms": null, - "volume_idle_close_ms": null - }, - "allOf": [ - { - "$ref": "#/definitions/KvCommandConfig" - } - ] - }, - "learning_deadline_ms": { - "description": "How long a tablet replica-set change may stay in Learning before the coordinator aborts it and the tablet returns to its old members. Defaults to one hour and must be at least 45 minutes. It counts from the phase's start, not from the learner's progress, so it must outlast the membership holder wait (15 minutes) plus the copy of the largest log suffix a learner may have to catch up.", + "route_compute_timeout_ms": { + "description": "Timeout for resolving compute routes in milliseconds.", "type": [ "integer", "null" @@ -916,113 +830,99 @@ "format": "uint64", "minimum": 0.0 }, - "log_client_inflight_per_worker": { - "description": "Log shard requests a client keeps in flight to one storage worker. Later requests queue and go out together as one batch when a slot frees. Defaults to 2.", + "route_pegboard_auth_check_timeout_ms": { + "description": "Timeout for pegboard actor route authorization checks in milliseconds.", "type": [ "integer", "null" ], - "format": "uint", + "format": "uint64", "minimum": 0.0 }, - "log_directory": { - "description": "Existing directory owned by one storage process. Required when storage services start.", + "tcp_nodelay": { + "description": "Enables TCP_NODELAY on accepted Guard sockets. Defaults to true.\n\nGuard carries actor nodes' WebSockets and client WebSocket and HTTP traffic, where most writes are small frames. With Nagle's algorithm on, a small frame waits for the peer to acknowledge the previous one, and a peer that delays its ACK holds it for about 40 ms. That delay landed on every actor's first response.", "type": [ - "string", + "boolean", "null" ] }, - "log_request_capacity": { - "description": "Exact replica subject capacity, shared by local clients and the shard. Defaults to 2048.", + "websocket_keepalive_interval_ms": { + "description": "Interval in milliseconds at which Guard sends a ping frame on every client WebSocket. Keeps idle connections alive through proxies that close quiet WebSockets, such as Cloudflare's roughly 100 second idle timeout. Must be greater than 0. Defaults to 30000 ms.", "type": [ "integer", "null" ], - "format": "uint", + "format": "uint64", "minimum": 0.0 - }, - "log_shard": { - "description": "Optional overrides for the shard's disk, memory and operation bounds.", - "default": { - "checkpoint_bytes": null, - "checkpoint_timeout_ms": null, - "disk_read_concurrency": null, - "inbox_capacity": null, - "manifest_refresh_timeout_ms": null, - "max_admission_wait_ms": null, - "max_append_admission_wait_ms": null, - "max_batch_bytes": null, - "max_disk_bytes": null, - "max_disk_reads": null, - "max_loaded_volume_bytes": null, - "max_loaded_volumes": null, - "max_memory_bytes": null, - "max_memory_requests": null, - "max_retained_payload_bytes": null, - "max_retained_payload_bytes_per_volume": null, - "max_write_bytes": null, - "overload_retry_after_ms": null, - "preallocate_segments": null, - "recovery_timeout_ms": null, - "release_idle_payloads_after_ms": null, - "remove_segments_timeout_ms": null, - "segment_bytes": null, - "write_to_cold_at_disk_percent": null, - "write_to_disk_timeout_ms": null - }, - "allOf": [ - { - "$ref": "#/definitions/LogShardConfig" - } + } + }, + "additionalProperties": false + }, + "HardwareMetrics": { + "description": "Configuration for host hardware metrics.", + "type": "object", + "properties": { + "disk_probe": { + "description": "Times sequential writes and fsyncs on each data directory once at startup, in the background. Defaults to true.", + "type": [ + "boolean", + "null" ] }, - "lookup_request_capacity": { - "description": "Concurrent topology endpoint lookups and heartbeat inspections admitted per process. Defaults to 128.", + "disk_probe_bytes": { + "description": "Bytes the probe writes sequentially to each data directory. The fsync phase writes a further 400 KiB. Defaults to 64 MiB; at most 1 GiB.", "type": [ "integer", "null" ], - "format": "uint", + "format": "uint64", "minimum": 0.0 - }, - "maintainer_inbox_capacity": { - "description": "Queued events one tablet maintainer accepts before it rejects new requests as overloaded. Every volume open, read, apply and status notice for the tablet passes through it, so a burst of actor starts on one tablet needs room for several events per start. Must be positive. Defaults to 4096.", + } + }, + "additionalProperties": false + }, + "HeartbeatGcConfig": { + "description": "Optional overrides for topology's bounded heartbeat cleanup service, which runs under the storage owner's singleton election. Topology defines the resolved defaults.", + "type": "object", + "properties": { + "batch_interval_ms": { + "description": "Pause between the batches of one pass. Defaults to 10 seconds.", "type": [ "integer", "null" ], - "format": "uint32", + "format": "uint64", "minimum": 0.0 }, - "maintainer_max_loaded_volumes": { - "description": "Volumes one tablet maintainer holds loaded at once, before opens of more are refused as overloaded. A loaded volume costs about 1.8 KiB of maintainer heap, so the default of 500,000 holds under 1 GiB per tablet. Must be positive.", + "batch_size": { + "description": "LIST entries examined per batch, 1 to 1000. Defaults to 128.", "type": [ "integer", "null" ], - "format": "uint32", + "format": "uint", "minimum": 0.0 }, - "maintainer_max_queued_opens": { - "description": "Volume opens queued in one tablet maintainer's inbox, not yet taken by its runner, at which a new open is refused as overloaded at once instead of joining the inbox. Other inbox events do not count. Opens queued past this would mostly expire before admission. Must be positive. Defaults to 1024.", + "batch_timeout_ms": { + "description": "Time budget of one batch. Defaults to 2 seconds.", "type": [ "integer", "null" ], - "format": "uint32", + "format": "uint64", "minimum": 0.0 }, - "maintainer_max_requests": { - "description": "Client requests one tablet maintainer tracks at once before it rejects more as overloaded. Must be positive. Defaults to 1024.", + "delete_concurrency": { + "description": "Conditional deletes in flight per batch, 1 to `batch_size`. Defaults to 4.", "type": [ "integer", "null" ], - "format": "uint32", + "format": "uint", "minimum": 0.0 }, - "manifest_refresh_ms": { - "description": "Fallback interval for re-reading the tablet manifest. Routing misses and `NotMaintainer` replies refresh it at once. Defaults to 30 seconds.", + "pass_interval_ms": { + "description": "Time between the starts of consecutive passes over both heartbeat prefixes. Defaults to one hour.", "type": [ "integer", "null" @@ -1030,44 +930,61 @@ "format": "uint64", "minimum": 0.0 }, - "max_background_cold_reads": { - "description": "Size of each background cold-read window per tablet maintainer: held-image source reads for forks, replica log reads for applies, fork jobs and fork cleanup jobs are each bounded by it separately, so they never take client read slots. Defaults to 4.", + "retention_ms": { + "description": "Defaults to 24 hours; clean shutdown markers remain available during this window.", "type": [ "integer", "null" ], - "format": "uint32", + "format": "uint64", "minimum": 0.0 + } + }, + "additionalProperties": false + }, + "Jwt": { + "type": "object", + "properties": { + "audience": { + "type": [ + "string", + "null" + ] }, - "max_cold_reads": { - "description": "Client cold-store reads in flight per tablet maintainer: session opens, lifecycle row lookups and session reads share this window. A cold wake's reads fan out through it, so a narrow window serializes them. Object storage throughput scales almost linearly to about 32 parallel GETs, and a read slot is not always a GET, so 64 keeps a cold wake's reads from waiting on slots; on the load cluster a fully cold wake took about 850 ms at 4, 430 ms at 64 and 390 ms at 512. Defaults to 64.", + "default_duration": { + "description": "Default access-token lifetime, in seconds.", "type": [ "integer", "null" ], - "format": "uint32", + "format": "uint64", "minimum": 0.0 }, - "max_cold_reads_per_volume": { - "description": "Client session reads of one volume a tablet maintainer admits while reads of other volumes wait, so one large scan cannot hold every client cold-read slot. Slots no other volume waits for still go to that volume. Unset or 0 admits client reads in arrival order.", + "enabled": { + "description": "Controls verification, issuance, and key maintenance. Defaults to true.", "type": [ - "integer", + "boolean", "null" - ], - "format": "uint32", - "minimum": 0.0 + ] }, - "max_read_sessions": { - "description": "Filesystem read sessions per tablet maintainer. A session lives while its volume is being read and ends after `read_session_idle_ms` without a read, so this bounds how many volumes a tablet serves reads for at once. Opens past it are refused as overloaded. A held session retains about 1.2 KiB of maintainer heap, so the default of 16,384 holds under 20 MiB per tablet. Must be positive.", + "issuance_enabled": { + "description": "Pauses only new token minting. Defaults to true while JWT is enabled.", + "type": [ + "boolean", + "null" + ] + }, + "issuer_generation": { + "description": "Monotonic topology fence. Increment when changing the derived issuer or signing leader.", "type": [ "integer", "null" ], - "format": "uint", + "format": "uint64", "minimum": 0.0 }, - "membership_refresh_cooldown_ms": { - "description": "Minimum milliseconds between miss-triggered membership refresh attempts, including failures.", + "key_max_signing_lifetime": { + "description": "Fail-closed maximum signing lifetime, in seconds (fourteen days by default). Normal rotation is still at day seven; this deadline only prevents A from signing forever if rotation stalls.", "type": [ "integer", "null" @@ -1075,8 +992,8 @@ "format": "uint64", "minimum": 0.0 }, - "memtable_flush_throttle_ms": { - "description": "Throttle for memtable flushes that nobody urgently waits on: a pass that comes due waits up to this long, so passes that come due close together share one upload and one L0 SST. Defaults to ten seconds.", + "key_publish_lead": { + "description": "Time to publish a pending public key before activation, in seconds (ten minutes by default). It provides verifier propagation time and does not extend the active key's lifetime.", "type": [ "integer", "null" @@ -1084,8 +1001,8 @@ "format": "uint64", "minimum": 0.0 }, - "read_cursor_session_idle_ms": { - "description": "How long a session opened at the latest position stays registered after its last read, in milliseconds. An HTTP directory cursor resumes such a session between pages, and no client reopens it at its position, so a paging client keeps its cursor for this long between pages. Must be positive. Defaults to 30,000, the session lifetime.", + "key_rotation_interval": { + "description": "Normal active-key age at rotation, in seconds (seven days by default).", "type": [ "integer", "null" @@ -1093,8 +1010,8 @@ "format": "uint64", "minimum": 0.0 }, - "read_session_idle_ms": { - "description": "How long a read session stays registered after its last read, in milliseconds. Each read extends it up to the session's lifetime, and a client reopens an ended session on its next read, so sessions track volumes being read rather than volumes open. Must be positive. Defaults to 2,000.", + "max_duration": { + "description": "Maximum access-token lifetime, in seconds. V1 is capped at one day.", "type": [ "integer", "null" @@ -1102,8 +1019,7 @@ "format": "uint64", "minimum": 0.0 }, - "simulated_fs_read_cost_us": { - "description": "Simulation only: runner time each filesystem read request costs a tablet maintainer, in microseconds, spent on the simulated clock so a maintainer busy with reads queues them as a loaded one does. Unset in deployments, where real processing time passes on its own.", + "unknown_kid_refresh_cooldown": { "type": [ "integer", "null" @@ -1111,8 +1027,7 @@ "format": "uint64", "minimum": 0.0 }, - "stop_poll_ms": { - "description": "Stop-permit retry interval. Defaults to one second.", + "verifier_cache_ttl": { "type": [ "integer", "null" @@ -1120,17 +1035,23 @@ "format": "uint64", "minimum": 0.0 }, - "stop_teardown_ms": { - "description": "Teardown budget after a stop permit or timeout. Defaults to one minute.", + "verifier_max_stale": { "type": [ "integer", "null" ], "format": "uint64", "minimum": 0.0 - }, - "stop_timeout_ms": { - "description": "Maximum coordinated stop wait from the first signal. Defaults to 30 minutes.", + } + }, + "additionalProperties": false + }, + "KvCommandConfig": { + "description": "Optional limits for typed commands and maintainer-owned filesystems.", + "type": "object", + "properties": { + "evicted_idle_warn_ms": { + "description": "Warns on eviction below this idle time; defaults to 15 seconds.", "type": [ "integer", "null" @@ -1138,44 +1059,43 @@ "format": "uint64", "minimum": 0.0 }, - "store_admission_scan_limit": { - "description": "Queued store items one admission pass looks at past work that cannot start yet. Work beyond it waits for the next pass. Must be positive. Defaults to 1024.", + "max_command_bytes": { "type": [ "integer", "null" ], - "format": "uint32", + "format": "uint", "minimum": 0.0 }, - "store_max_flush_staging": { - "description": "Tablet filesystem flushes one tablet maintainer lets stage at once. Staging writes the flush's records into the store. Must be positive. Defaults to 1.", + "max_eviction_closes": { + "description": "Idle KV command volumes one partition's host closes at once to make room past its cap or the worker's budget. Each open refused at a full cap starts one such close, so a burst admits at this many closes per close latency. A closing volume keeps its memory charge until its close finishes. Defaults to 32.", "type": [ "integer", "null" ], - "format": "uint32", + "format": "uint", "minimum": 0.0 }, - "store_max_flush_uploads": { - "description": "Tablet filesystem flushes one tablet maintainer keeps in flight at once, staging or uploading; a staging flush counts because it uploads next. Must be at least `store_max_flush_staging`. Defaults to 2.", + "max_inflight_command_bytes": { + "description": "Received bytes of the KV commands one partition's host has admitted and not finished. A command that would exceed it is refused at once as unavailable, which callers retry within their deadline. Must hold at least one command of `max_command_bytes`. Defaults to 64 MiB, so a worker maintaining four partitions holds at most 256 MiB of command payloads.", "type": [ "integer", "null" ], - "format": "uint32", + "format": "uint", "minimum": 0.0 }, - "store_max_keyed_in_flight": { - "description": "Filesystem applies and loads one tablet maintainer admits to its store at once. They run concurrently, one task each, and their replica reads stay bounded by the cold byte budget, so the cap sizes concurrency for a burst of volume creates rather than memory. Must be positive. Defaults to 256.", + "max_inflight_commands": { + "description": "Maximum simultaneous handlers per registered command class.", "type": [ "integer", "null" ], - "format": "uint32", + "format": "uint", "minimum": 0.0 }, - "store_volume_lock_stripes": { - "description": "Volume lock stripes of each tablet filesystem store. Two volumes on one stripe serialize their store work. Must be positive. Defaults to 1024.", + "max_open_volumes": { + "description": "KV command volumes one partition's host holds open at once, closing ones included. Defaults to the worker-wide cap, so by default only the worker's memory budget limits a partition.", "type": [ "integer", "null" @@ -1183,35 +1103,17 @@ "format": "uint", "minimum": 0.0 }, - "store_volume_locks": { - "description": "Which store work takes volume stripes. `core` (the default) trusts the maintainer core to order applies, loads, session opens, lifecycle lookups and the flush retention publish, so they take only the tablet lock; stripes then guard only checkpoint-session opens against hold releases, checkpoint deletions and sweeps. `stripes` keeps a stripe on every operation, as before.", - "anyOf": [ - { - "$ref": "#/definitions/StoreVolumeLocks" - }, - { - "type": "null" - } - ] - }, - "volume_client": { - "default": { - "close_after_idle_ms": null, - "max_loaded_volumes": null, - "max_requests": null, - "max_volume_key_bytes": null, - "open_timeout_ms": null, - "payload_codec": null, - "sweep_interval_ms": null - }, - "allOf": [ - { - "$ref": "#/definitions/VolumeClientConfig" - } - ] + "max_worker_open_volume_bytes": { + "description": "Memory, in bytes, that the KV command volumes of this worker may hold together across every partition it maintains, closing ones included. Each open volume is charged 80 KiB, its measured fixed cost with allocator headroom: its host slot, volume client and filesystem owner, plus the log replicas and maintainer state its open loads. Caches and command payloads are bounded separately. One worker-wide budget bounds memory however many partitions the worker holds, and a create burst opens one volume per new key, so the default of 5 GiB holds 65,536 volumes (default 5 GiB).", + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 0.0 }, - "volume_request_capacity": { - "description": "Per-subject lifecycle request capacity, isolated from other UPS traffic. Defaults to 1024.", + "max_worker_open_volumes": { + "description": "A count backstop to `max_worker_open_volume_bytes`; unset by default, so the byte budget alone sets the worker's cap.", "type": [ "integer", "null" @@ -1219,17 +1121,17 @@ "format": "uint", "minimum": 0.0 }, - "volume_request_timeout_ms": { - "description": "Entire lifecycle RPC budget, including route refreshes. Defaults to five seconds.", + "request_capacity": { + "description": "KV commands one worker holds received and unanswered, and that one caller may have outstanding to one worker, isolated from volume lifecycle requests. A full window refuses a command at once as overloaded, and the caller retries it within its deadline. Each partition's host admits at most the sum of its commands' concurrency caps (4,608 with the default caps and the engine's commands), so the default of 32,768 lets those caps, not this window, refuse load first on a worker that maintains up to seven partitions. Defaults to 32,768.", "type": [ "integer", "null" ], - "format": "uint64", + "format": "uint", "minimum": 0.0 }, - "write_to_cold_above_bytes": { - "description": "Committed suffix threshold, default 64 MiB and maximum 256 MiB.", + "short_idle_close_ms": { + "description": "Idle close for volumes of short-lived commands, such as one Epoxy key per volume. Closing also begins the volume's unload. Defaults to 2 seconds.", "type": [ "integer", "null" @@ -1237,8 +1139,8 @@ "format": "uint64", "minimum": 0.0 }, - "write_to_cold_after_ms": { - "description": "Cold-write age from the first unflushed entry, plus up to a tenth of it as jitter. Defaults to one minute. Storage workers require the age with its jitter to stay below `stop_timeout_ms`, so a stopping worker never waits on a slower tiering cadence than its own stop budget.", + "volume_idle_close_ms": { + "description": "Defaults to 60 seconds.", "type": [ "integer", "null" @@ -1249,206 +1151,79 @@ }, "additionalProperties": false }, - "Epoxy": { - "description": "Epoxy runs in a single datacenter with fixed membership, so it has no settings yet. The section stays so multi-region settings have a home.", - "type": "object", - "additionalProperties": false - }, - "FileSystemObjectStoreConfig": { - "description": "A local directory used by one engine process at a time. Conditional writes are atomic only among requests of that process.", - "type": "object", - "required": [ - "path" - ], - "properties": { - "path": { - "description": "Created when it does not exist.", - "type": "string" - } - }, - "additionalProperties": false - }, - "FilesystemConfig": { + "LogShardConfig": { + "description": "The log shard owns resolved defaults and validates relationships between these limits.", "type": "object", "properties": { - "max_cache_bytes": { - "description": "Bytes one filesystem's row cache may retain, a fairness ceiling inside `max_process_cache_bytes`.", - "default": 8388608, - "type": "integer", - "format": "uint", + "checkpoint_bytes": { + "type": [ + "integer", + "null" + ], + "format": "uint64", "minimum": 0.0 }, - "max_conflict_window_bytes": { - "default": 4194304, - "type": "integer", - "format": "uint", + "checkpoint_timeout_ms": { + "type": [ + "integer", + "null" + ], + "format": "uint64", "minimum": 0.0 }, - "max_memory_bytes": { - "default": 67108864, - "type": "integer", - "format": "uint", + "disk_read_concurrency": { + "description": "Disk reads executing at once beside the shard's write queue. Each holds one scan buffer of two maximum batches from `max_memory_bytes`. Defaults to 8.", + "type": [ + "integer", + "null" + ], + "format": "uint32", "minimum": 0.0 }, - "max_operation_bytes": { - "default": 8388608, - "type": "integer", + "inbox_capacity": { + "type": [ + "integer", + "null" + ], "format": "uint", "minimum": 0.0 }, - "max_pending_commits": { - "default": 128, - "type": "integer", - "format": "uint", + "manifest_refresh_timeout_ms": { + "type": [ + "integer", + "null" + ], + "format": "uint64", "minimum": 0.0 }, - "max_process_cache_bytes": { - "description": "Bytes all filesystem row caches in one process may retain together.", - "default": 1073741824, - "type": "integer", - "format": "uint", + "max_admission_wait_ms": { + "description": "Longest wait, in milliseconds, of a request other than an append in the shard's inbox. A request that waited longer is refused as overloaded before any work, with a retry hint, so a saturated shard sheds load instead of queueing past the volume client's quorum timeout. Defaults to 1000.", + "type": [ + "integer", + "null" + ], + "format": "uint64", "minimum": 0.0 }, - "max_snapshot_age_ms": { - "default": 30000, - "type": "integer", + "max_append_admission_wait_ms": { + "description": "The same bound for appends, commit announcements, trims and forgets, which carry writes a volume client already admitted or free memory and disk, so opens and other new work are shed first. At least `max_admission_wait_ms` and just under the volume client's 2 s append timeout, past which the client has resent the append anyway. Defaults to 1800.", + "type": [ + "integer", + "null" + ], "format": "uint64", "minimum": 0.0 }, - "max_snapshots": { - "default": 128, - "type": "integer", - "format": "uint", + "max_batch_bytes": { + "type": [ + "integer", + "null" + ], + "format": "uint32", "minimum": 0.0 }, - "max_transaction_age_ms": { - "default": 300000, - "type": "integer", - "format": "uint64", - "minimum": 0.0 - }, - "max_transaction_bytes": { - "description": "Maximum application bytes in one filesystem transaction.", - "default": 134217728, - "type": "integer", - "format": "uint", - "minimum": 0.0 - }, - "max_unapplied_bytes": { - "description": "Most payload bytes one opening may have admitted ahead of the newest position its tablet's maintainer reported applied. A new write past it is refused with a retry hint before it is built, so a writer faster than the apply, as under a slow cold store or compaction, waits at admission instead of growing an unbounded backlog.", - "default": 268435456, - "type": "integer", - "format": "uint", - "minimum": 0.0 - }, - "max_value_bytes": { - "default": 1048576, - "type": "integer", - "format": "uint", - "minimum": 0.0 - }, - "note_ttl_ms": { - "description": "How long a write's overlay notes (\"this page is at this sequence\") outlive the write before the filesystem drops them and keeps their rows only as cache. A later miss on one of those rows reads it from the maintainer once it has applied the write. About ten times the usual apply latency.", - "default": 1000, - "type": "integer", - "format": "uint64", - "minimum": 0.0 - }, - "request_timeout_ms": { - "default": 30000, - "type": "integer", - "format": "uint64", - "minimum": 0.0 - } - }, - "additionalProperties": false - }, - "ForkConfig": { - "description": "Global bounds for source hold cleanup and background cross-region copies.", - "type": "object", - "properties": { - "check_grace_ms": { - "description": "Minimum age before an unfinished fork's destination is checked.", - "default": 60000, - "type": "integer", - "format": "uint64", - "minimum": 0.0 - }, - "materialize_batch_chunks": { - "description": "Further bounded by the maintainer's row and byte budgets.", - "default": 1024, - "type": "integer", - "format": "uint32", - "minimum": 0.0 - }, - "max_concurrent_materializations": { - "default": 2, - "type": "integer", - "format": "uint32", - "minimum": 0.0 - } - }, - "additionalProperties": false - }, - "GcsObjectStoreConfig": { - "description": "Google Cloud Storage; defaults to the adapter's application default credentials.", - "type": "object", - "required": [ - "bucket" - ], - "properties": { - "bucket": { - "type": "string" - }, - "prefix": { - "default": null, - "type": [ - "string", - "null" - ] - }, - "service_account_key": { - "description": "Optional JSON service account credential override.", - "default": null, - "anyOf": [ - { - "$ref": "#/definitions/Secret" - }, - { - "type": "null" - } - ] - } - }, - "additionalProperties": false - }, - "Guard": { - "type": "object", - "properties": { - "disable_gateway_auth": { - "description": "TODO: Remove eventually and reenable gateway auth permanently. See RVTEE-672.", - "default": false, - "type": "boolean" - }, - "host": { - "description": "Host for HTTP traffic", - "type": [ - "string", - "null" - ], - "format": "ip" - }, - "port": { - "description": "Port for HTTP traffic", - "type": [ - "integer", - "null" - ], - "format": "uint16", - "minimum": 0.0 - }, - "route_auth_check_timeout_ms": { - "description": "Timeout for guard-owned route authorization checks in milliseconds.", + "max_disk_bytes": { + "description": "Disk bytes the shard's log may hold. When unset, a storage worker sizes it from the free space of its filesystem at startup, shared with the cold-store disk cache.", "type": [ "integer", "null" @@ -1456,17 +1231,17 @@ "format": "uint64", "minimum": 0.0 }, - "route_compute_timeout_ms": { - "description": "Timeout for resolving compute routes in milliseconds.", + "max_disk_reads": { + "description": "Most reads waiting on the shard's disk at once; a read that needs disk beyond it is rejected as overloaded and retried by the caller. Defaults to 256.", "type": [ "integer", "null" ], - "format": "uint64", + "format": "uint32", "minimum": 0.0 }, - "route_pegboard_auth_check_timeout_ms": { - "description": "Timeout for pegboard actor route authorization checks in milliseconds.", + "max_loaded_volume_bytes": { + "description": "Memory, in bytes, that one log shard's loaded replicas may hold, charged at 1.5 KiB per replica, the measured heap of a quiet loaded replica with headroom. A load past it is refused as `too_many_loaded_volumes`, a typed refusal its open retries. The default of 1.5 GiB holds 1,048,576 replicas (default 1.5 GiB).", "type": [ "integer", "null" @@ -1474,45 +1249,16 @@ "format": "uint64", "minimum": 0.0 }, - "tcp_nodelay": { - "description": "Enables TCP_NODELAY on accepted Guard sockets. Defaults to true.\n\nGuard carries actor runners' Envoy WebSockets and client WebSocket and HTTP traffic, where most writes are small frames. With Nagle's algorithm on, a small frame waits for the peer to acknowledge the previous one, and a peer that delays its ACK holds it for about 40 ms. That delay landed on every actor's first response.", - "type": [ - "boolean", - "null" - ] - } - }, - "additionalProperties": false - }, - "HardwareMetrics": { - "description": "Configuration for host hardware metrics.", - "type": "object", - "properties": { - "disk_probe": { - "description": "Times sequential writes and fsyncs on each data directory once at startup, in the background. Defaults to true.", - "type": [ - "boolean", - "null" - ] - }, - "disk_probe_bytes": { - "description": "Bytes the probe writes sequentially to each data directory. The fsync phase writes a further 400 KiB. Defaults to 64 MiB; at most 1 GiB.", + "max_loaded_volumes": { + "description": "A count backstop to `max_loaded_volume_bytes`; unset by default, so the byte budget alone sets the shard's replica cap.", "type": [ "integer", "null" ], - "format": "uint64", + "format": "uint32", "minimum": 0.0 - } - }, - "additionalProperties": false - }, - "HeartbeatGcConfig": { - "description": "Optional overrides for topology's bounded heartbeat cleanup service, which runs under the storage owner's singleton election. Topology defines the resolved defaults.", - "type": "object", - "properties": { - "batch_interval_ms": { - "description": "Pause between the batches of one pass. Defaults to 10 seconds.", + }, + "max_memory_bytes": { "type": [ "integer", "null" @@ -1520,17 +1266,16 @@ "format": "uint64", "minimum": 0.0 }, - "batch_size": { - "description": "LIST entries examined per batch, 1 to 1000. Defaults to 128.", + "max_memory_requests": { "type": [ "integer", "null" ], - "format": "uint", + "format": "uint32", "minimum": 0.0 }, - "batch_timeout_ms": { - "description": "Time budget of one batch. Defaults to 2 seconds.", + "max_retained_payload_bytes": { + "description": "Encoded bytes of synced log entry payloads the shard keeps in memory for reads; defaults to a sixteenth of `max_memory_bytes`. Older payloads are read from disk.", "type": [ "integer", "null" @@ -1538,17 +1283,8 @@ "format": "uint64", "minimum": 0.0 }, - "delete_concurrency": { - "description": "Conditional deletes in flight per batch, 1 to `batch_size`. Defaults to 4.", - "type": [ - "integer", - "null" - ], - "format": "uint", - "minimum": 0.0 - }, - "pass_interval_ms": { - "description": "Time between the starts of consecutive passes over both heartbeat prefixes. Defaults to one hour.", + "max_retained_payload_bytes_per_volume": { + "description": "Synced payload bytes one volume replica keeps after each disk write; defaults to 32 KiB.", "type": [ "integer", "null" @@ -1556,29 +1292,16 @@ "format": "uint64", "minimum": 0.0 }, - "retention_ms": { - "description": "Defaults to 24 hours; clean shutdown markers remain available during this window.", + "max_write_bytes": { "type": [ "integer", "null" ], "format": "uint64", "minimum": 0.0 - } - }, - "additionalProperties": false - }, - "Jwt": { - "type": "object", - "properties": { - "audience": { - "type": [ - "string", - "null" - ] }, - "default_duration": { - "description": "Default access-token lifetime, in seconds.", + "overload_retry_after_ms": { + "description": "Retry hint, in milliseconds, on a request refused because the inbox or a request budget is full. Defaults to 100.", "type": [ "integer", "null" @@ -1586,31 +1309,15 @@ "format": "uint64", "minimum": 0.0 }, - "enabled": { - "description": "Controls verification, issuance, and key maintenance. Defaults to true.", - "type": [ - "boolean", - "null" - ] - }, - "issuance_enabled": { - "description": "Pauses only new token minting. Defaults to true while JWT is enabled.", + "preallocate_segments": { + "description": "Writes log segments into files zero-filled to `segment_bytes` ahead of use, so each commit overwrites allocated blocks and syncs only its data instead of journaling a file size change. On the load cluster's disks this halves the sync and commit latency. The unused allocation of the active segment and one spare segment count toward `max_disk_bytes`. Each write also records, in a header slot synced with it, how far earlier syncs reached, so a disk that returns synced pages as zeros is reported as corruption rather than read as free space. Needs `segment_bytes` above 8 KiB. Defaults to true.", "type": [ "boolean", "null" ] }, - "issuer_generation": { - "description": "Monotonic topology fence. Increment when changing the derived issuer or signing leader.", - "type": [ - "integer", - "null" - ], - "format": "uint64", - "minimum": 0.0 - }, - "key_max_signing_lifetime": { - "description": "Fail-closed maximum signing lifetime, in seconds (fourteen days by default). Normal rotation is still at day seven; this deadline only prevents A from signing forever if rotation stalls.", + "recovery_timeout_ms": { + "description": "How long log shard recovery may go without finishing another file before the shard fails (default 120 s). It does not bound the whole scan, which grows with the log.", "type": [ "integer", "null" @@ -1618,8 +1325,8 @@ "format": "uint64", "minimum": 0.0 }, - "key_publish_lead": { - "description": "Time to publish a pending public key before activation, in seconds (ten minutes by default). It provides verifier propagation time and does not extend the active key's lifetime.", + "release_idle_payloads_after_ms": { + "description": "How long a volume replica appends nothing before its synced payloads leave memory; defaults to 5000.", "type": [ "integer", "null" @@ -1627,8 +1334,7 @@ "format": "uint64", "minimum": 0.0 }, - "key_rotation_interval": { - "description": "Normal active-key age at rotation, in seconds (seven days by default).", + "remove_segments_timeout_ms": { "type": [ "integer", "null" @@ -1636,8 +1342,7 @@ "format": "uint64", "minimum": 0.0 }, - "max_duration": { - "description": "Maximum access-token lifetime, in seconds. V1 is capped at one day.", + "segment_bytes": { "type": [ "integer", "null" @@ -1645,92 +1350,146 @@ "format": "uint64", "minimum": 0.0 }, - "unknown_kid_refresh_cooldown": { + "write_to_cold_at_disk_percent": { + "description": "Early cold-write watermark, 1..99 percent of the disk cap; defaults to 75.", "type": [ "integer", "null" ], - "format": "uint64", + "format": "uint8", "minimum": 0.0 }, - "verifier_cache_ttl": { + "write_to_disk_timeout_ms": { "type": [ "integer", "null" ], "format": "uint64", "minimum": 0.0 - }, - "verifier_max_stale": { + } + }, + "additionalProperties": false + }, + "Logs": { + "type": "object", + "properties": { + "redirect_logs_dir": { + "default": null, "type": [ - "integer", + "string", "null" - ], - "format": "uint64", - "minimum": 0.0 + ] } }, "additionalProperties": false }, - "KvCommandConfig": { - "description": "Optional limits for typed commands and maintainer-owned filesystems.", + "Metrics": { + "description": "Configuration for the metrics service.", "type": "object", "properties": { - "evicted_idle_warn_ms": { - "description": "Warns on eviction below this idle time; defaults to 15 seconds.", + "hardware": { + "description": "Host hardware metrics, including a short disk probe of each data directory at startup.", + "default": { + "disk_probe": null, + "disk_probe_bytes": null + }, + "allOf": [ + { + "$ref": "#/definitions/HardwareMetrics" + } + ] + }, + "host": { "type": [ - "integer", + "string", "null" ], - "format": "uint64", - "minimum": 0.0 + "format": "ip" }, - "max_command_bytes": { + "port": { "type": [ "integer", "null" ], - "format": "uint", + "format": "uint16", "minimum": 0.0 + } + }, + "additionalProperties": false + }, + "MigrateFromV2": { + "description": "Settings for moving one V2 namespace's actors into this cluster.\n\nThe section is read through `config.dynamic()`, and a dynamic config update replaces it as a whole. `migrate_seed` is the exception: it is fixed at startup, because a new seed reshuffles which actors fall under `migrate_ratio`, so raising the ratio would no longer only add actors.", + "type": "object", + "required": [ + "endpoint", + "namespace" + ], + "properties": { + "api_urls": { + "description": "V2's public API per datacenter label, which the migrator lists and moves actors through.", + "default": {}, + "type": "object", + "additionalProperties": { + "type": "string" + } }, - "max_eviction_closes": { - "description": "Idle KV command volumes one tablet's host closes at once to make room past its cap or the worker's budget. Each open refused at a full cap starts one such close, so a burst admits at this many closes per close latency. A closing volume keeps its memory charge until its close finishes. Defaults to 32.", + "confirm_delay_ms": { "type": [ "integer", "null" ], - "format": "uint", + "format": "uint64", "minimum": 0.0 }, - "max_inflight_command_bytes": { - "description": "Received bytes of the KV commands one tablet's host has admitted and not finished. A command that would exceed it is refused at once as unavailable, which callers retry within their deadline. Must hold at least one command of `max_command_bytes`. Defaults to 64 MiB, so a worker maintaining four tablets holds at most 256 MiB of command payloads.", + "create_ratio": { + "description": "Share of new creates that land in this cluster. Keyless creates draw against it; keyed creates move here only at 1.0.", + "default": 0.0, + "type": "number", + "format": "double" + }, + "endpoint": { + "description": "V2's public gateway, where requests for actors this cluster does not hold are proxied.", + "type": "string" + }, + "max_actors_per_sec": { "type": [ "integer", "null" ], - "format": "uint", + "format": "uint32", "minimum": 0.0 }, - "max_inflight_commands": { - "description": "Maximum simultaneous handlers per registered command class.", + "max_copy_bytes": { + "description": "Largest actor the migrator copies, by V2's reported SQLite size plus its KV estimate. Larger actors are skipped as `too_large` on every pass, so they never hold a slot; raise it once the rest of the namespace has moved to migrate them. Defaults to 10 GiB.", "type": [ "integer", "null" ], - "format": "uint", + "format": "uint64", "minimum": 0.0 }, - "max_open_volumes": { - "description": "KV command volumes one tablet's host holds open at once, closing ones included. Defaults to the worker-wide cap, so by default only the worker's memory budget limits a tablet.", + "max_in_flight": { "type": [ "integer", "null" ], - "format": "uint", + "format": "uint32", "minimum": 0.0 }, - "max_worker_open_volume_bytes": { - "description": "Memory, in bytes, that the KV command volumes of this worker may hold together across every tablet it maintains, closing ones included. Each open volume is charged 80 KiB, its measured fixed cost with allocator headroom: its host slot, volume client and filesystem owner, plus the log replicas and maintainer state its open loads. Caches and command payloads are bounded separately. One worker-wide budget bounds memory however many tablets the worker holds, and a create burst opens one volume per new key, so the default of 5 GiB holds 65,536 volumes (default 5 GiB).", + "migrate_ratio": { + "description": "Share of existing V2 actors the migrator moves, picked by a stable hash of the actor id.", + "default": 0.0, + "type": "number", + "format": "double" + }, + "migrate_seed": { + "description": "Seed of the hash behind `migrate_ratio`.", + "default": 0, + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "min_sleep_ms": { "type": [ "integer", "null" @@ -1738,35 +1497,35 @@ "format": "uint64", "minimum": 0.0 }, - "max_worker_open_volumes": { - "description": "A count backstop to `max_worker_open_volume_bytes`; unset by default, so the byte budget alone sets the worker's cap.", + "namespace": { + "description": "The V2 namespace being migrated.", + "type": "string" + }, + "pass_interval_ms": { "type": [ "integer", "null" ], - "format": "uint", + "format": "uint64", "minimum": 0.0 }, - "request_capacity": { - "description": "KV commands one worker holds received and unanswered, and that one caller may have outstanding to one worker, isolated from volume lifecycle requests. A full window refuses a command at once as overloaded, and the caller retries it within its deadline. Each tablet's host admits at most the sum of its commands' concurrency caps (4,608 with the default caps and the engine's commands), so the default of 32,768 lets those caps, not this window, refuse load first on a worker that maintains up to seven tablets. Defaults to 32,768.", + "target_namespace": { + "description": "The namespace here that the actors land in. Defaults to the V2 namespace's name.", + "default": null, "type": [ - "integer", + "string", "null" - ], - "format": "uint", - "minimum": 0.0 + ] }, - "short_idle_close_ms": { - "description": "Idle close for volumes of short-lived commands, such as one Epoxy key per volume. Closing also begins the volume's unload. Defaults to 2 seconds.", + "token": { + "description": "The namespace-wide V2 token for V2's migration API and the public API calls routing makes. Never logged.", + "default": null, "type": [ - "integer", + "string", "null" - ], - "format": "uint64", - "minimum": 0.0 + ] }, - "volume_idle_close_ms": { - "description": "Defaults to 60 seconds.", + "wake_horizon_ms": { "type": [ "integer", "null" @@ -1777,11 +1536,20 @@ }, "additionalProperties": false }, - "LogShardConfig": { - "description": "The log shard owns resolved defaults and validates relationships between these limits.", + "ObjectStorageConfig": { "type": "object", "properties": { - "checkpoint_bytes": { + "primary": { + "default": "in_memory", + "allOf": [ + { + "$ref": "#/definitions/ObjectStoreBackend" + } + ] + }, + "read_first_attempt_ms": { + "description": "Milliseconds the first attempt of a small GET or HEAD may wait for response headers before the client cuts it off and retries it with its normal timeouts. A request on a stalled connection otherwise waits the full 30 s client timeout. Default 250, set from the load cluster's Azure account, where about 0.02% of small requests took longer; 0 disables it. Response bodies are never cut off.", + "default": null, "type": [ "integer", "null" @@ -1789,7 +1557,9 @@ "format": "uint64", "minimum": 0.0 }, - "checkpoint_timeout_ms": { + "write_first_attempt_bytes_per_second": { + "description": "The upload rate a conditional PUT's first attempt may fall to before it is cut off, which extends `write_first_attempt_ms` by the body's size. Default 8 MiB/s.", + "default": null, "type": [ "integer", "null" @@ -1797,288 +1567,367 @@ "format": "uint64", "minimum": 0.0 }, - "disk_read_concurrency": { - "description": "Disk reads executing at once beside the shard's write queue. Each holds one scan buffer of two maximum batches from `max_memory_bytes`. Defaults to 8.", + "write_first_attempt_ms": { + "description": "Milliseconds the first attempt of a small conditional PUT (a lease, heartbeat or manifest) may wait for response headers, before the upload time its body needs at `write_first_attempt_bytes_per_second`. Unset by default, which disables it. Healthy conditional PUT latency is calibrated only on Azure; a healthy GCS PUT can take over 250 ms. GCS also allows about one write per second to one object, so a retry shortly after a cut-off on the same lease or heartbeat object risks a 429, an unknown outcome and an extra readback. Set it only from measured conditional PUT latency of the backend.", + "default": null, "type": [ "integer", "null" ], - "format": "uint32", + "format": "uint64", "minimum": 0.0 + } + }, + "additionalProperties": false + }, + "ObjectStoreBackend": { + "oneOf": [ + { + "type": "string", + "enum": [ + "in_memory" + ] }, - "inbox_capacity": { + { + "description": "A local directory, for a single engine process that keeps its data across restarts.", + "type": "object", + "required": [ + "file_system" + ], + "properties": { + "file_system": { + "$ref": "#/definitions/FileSystemObjectStoreConfig" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "s3" + ], + "properties": { + "s3": { + "$ref": "#/definitions/S3ObjectStoreConfig" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "azure" + ], + "properties": { + "azure": { + "$ref": "#/definitions/AzureObjectStoreConfig" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "gcs" + ], + "properties": { + "gcs": { + "$ref": "#/definitions/GcsObjectStoreConfig" + } + }, + "additionalProperties": false + } + ] + }, + "Outbound": { + "description": "Policy for outbound HTTP requests to user-configured destinations, such as serverless node URLs.\n\nThese requests originate from inside the trusted engine network, so without restrictions a caller who can configure a node can reach internal-only services. The defaults deny every non-globally-routable destination except loopback.", + "type": "object", + "properties": { + "allow_cidrs": { + "description": "Additional CIDRs that are always permitted, evaluated after `deny_cidrs`.\n\nUse this to reach a specific internal service without opening up the whole private range.", "type": [ - "integer", + "array", "null" ], - "format": "uint", - "minimum": 0.0 + "items": { + "type": "string" + } }, - "manifest_refresh_timeout_ms": { + "allow_hosts": { + "description": "Hostnames that are always permitted, whatever they resolve to.\n\nMatched exactly and case-insensitively, with no wildcards. Use this for a first-party service whose address is not known ahead of time, such as a Kubernetes service name. Anything served at a listed host becomes reachable by every node config, so list individual hosts rather than a shared entry point.", "type": [ - "integer", + "array", "null" ], - "format": "uint64", - "minimum": 0.0 + "items": { + "type": "string" + } }, - "max_admission_wait_ms": { - "description": "Longest wait, in milliseconds, of a request other than an append in the shard's inbox. A request that waited longer is refused as overloaded before any work, with a retry hint, so a saturated shard sheds load instead of queueing past the volume client's quorum timeout. Defaults to 1000.", + "allow_insecure_scheme": { + "description": "Allow plaintext `http://` destinations. When disabled only `https://` is permitted.", "type": [ - "integer", + "boolean", "null" - ], - "format": "uint64", - "minimum": 0.0 + ] }, - "max_append_admission_wait_ms": { - "description": "The same bound for appends, commit announcements, trims and forgets, which carry writes a volume client already admitted or free memory and disk, so opens and other new work are shed first. At least `max_admission_wait_ms` and just under the volume client's 2 s append timeout, past which the client has resent the append anyway. Defaults to 1800.", + "allow_loopback": { + "description": "Allow destinations that resolve to loopback addresses (127.0.0.0/8, ::1).\n\nEnabled by default so local development against `http://localhost:...` works without configuration.", "type": [ - "integer", + "boolean", "null" - ], - "format": "uint64", - "minimum": 0.0 + ] }, - "max_batch_bytes": { + "allow_private_networks": { + "description": "Allow destinations that resolve to private, link-local, shared (CGNAT), or otherwise non-globally-routable addresses.\n\nSelf-hosted deployments that point nodes at addresses on their own network, such as a Docker Compose service name, need this enabled.", "type": [ - "integer", + "boolean", + "null" + ] + }, + "deny_cidrs": { + "description": "Additional CIDRs that are always denied. Takes precedence over every allow rule.", + "type": [ + "array", "null" ], - "format": "uint32", - "minimum": 0.0 + "items": { + "type": "string" + } }, - "max_disk_bytes": { - "description": "Disk bytes the shard's log may hold. When unset, a storage worker sizes it from the free space of its filesystem at startup, shared with the cold-store disk cache.", + "max_redirects": { + "description": "Maximum number of redirects to follow. Every hop is re-checked against this policy.", "type": [ "integer", "null" ], - "format": "uint64", + "format": "uint", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + "PartitionBalancingConfig": { + "description": "Static partition placement policy; membership and lease timings remain topology-owned.", + "type": "object", + "properties": { + "destination_stability_ms": { + "description": "How long a new worker must be listed in discovery, and pass one disk probe, before the coordinator places partitions on it. Must exceed the topology worker lease plus one renewal and the takeover grace, about 35 s, so a worker that crashes soon after it first appears has left discovery before it qualifies. Startup refuses a shorter value.", + "default": 60000, + "type": "integer", + "format": "uint64", "minimum": 0.0 }, - "max_disk_reads": { - "description": "Most reads waiting on the shard's disk at once; a read that needs disk beyond it is rejected as overloaded and retried by the caller. Defaults to 256.", - "type": [ - "integer", - "null" - ], + "enabled": { + "description": "Whether the partition coordinator places, splits and replaces partitions on its own. When disabled, the layout changes only through operator commands: a new cluster gets no first partition until an operator creates one, and a missing worker is never replaced.", + "default": true, + "type": "boolean" + }, + "max_disk_probes": { + "description": "Maximum concurrent disk admission requests made by the partition coordinator.", + "default": 4, + "type": "integer", "format": "uint32", "minimum": 0.0 }, - "max_loaded_volume_bytes": { - "description": "Memory, in bytes, that one log shard's loaded replicas may hold, charged at 1.5 KiB per replica, the measured heap of a quiet loaded replica with headroom. A load past it is refused as `too_many_loaded_volumes`, a typed refusal its open retries. The default of 1.5 GiB holds 1,048,576 replicas (default 1.5 GiB).", + "missing_worker_ms": { + "description": "How long an assigned worker must be missing from discovery before its replicas are replaced. Counted from when discovery drops it, which is itself one lease after its last renewal.", + "default": 120000, + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "partitions_per_core": { + "default": 2, + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + "PeerTransport": { + "description": "Connections and flow-control windows per peer. Omitted values use the defaults.", + "type": "object", + "properties": { + "channel_large_request_bytes": { + "description": "Requests with a larger payload use a peer's large-request channels, so they never queue ahead of small requests on one stream (default 64 KiB).", "type": [ "integer", "null" ], - "format": "uint64", + "format": "uint", "minimum": 0.0 }, - "max_loaded_volumes": { - "description": "A count backstop to `max_loaded_volume_bytes`; unset by default, so the byte budget alone sets the shard's replica cap.", + "channel_max_write_bytes": { + "description": "Most bytes a peer channel writes at once from its queue (default 256 KiB).", "type": [ "integer", "null" ], - "format": "uint32", + "format": "uint", "minimum": 0.0 }, - "max_memory_bytes": { + "channel_queue_bytes": { + "description": "Bytes of requests a peer channel queues for writing; a request that would exceed it is refused, though an empty queue always takes one (default 64 MiB).", "type": [ "integer", "null" ], - "format": "uint64", + "format": "uint", "minimum": 0.0 }, - "max_memory_requests": { + "channel_queue_frames": { + "description": "Frames a peer channel queues for writing; a full queue refuses the request (default 8192).", "type": [ "integer", "null" ], - "format": "uint32", + "format": "uint", "minimum": 0.0 }, - "max_retained_payload_bytes": { - "description": "Encoded bytes of synced log entry payloads the shard keeps in memory for reads; defaults to a sixteenth of `max_memory_bytes`. Older payloads are read from disk.", + "channel_reply_queue_bytes": { + "description": "Bytes of replies one served peer channel queues for writing. A channel at its cap refuses new requests as overloaded until its caller reads or the queued replies expire (default 64 MiB).", "type": [ "integer", "null" ], - "format": "uint64", + "format": "uint", "minimum": 0.0 }, - "max_retained_payload_bytes_per_volume": { - "description": "Synced payload bytes one volume replica keeps after each disk write; defaults to 32 KiB.", + "incoming_channel_capacity": { + "description": "Peer channels this worker may serve across all remote workers (default 4096).", "type": [ "integer", "null" ], - "format": "uint64", + "format": "uint", "minimum": 0.0 }, - "max_write_bytes": { + "outgoing_channel_capacity": { + "description": "Peer channels this worker may open across all remote workers (default 4096).", "type": [ "integer", "null" ], - "format": "uint64", + "format": "uint", "minimum": 0.0 }, - "overload_retry_after_ms": { - "description": "Retry hint, in milliseconds, on a request refused because the inbox or a request budget is full. Defaults to 100.", + "reply_queue_bytes": { + "description": "Bytes of replies queued across every served peer channel of this worker. At the cap every channel refuses new requests as overloaded (default 1 GiB).", "type": [ "integer", "null" ], - "format": "uint64", + "format": "uint", "minimum": 0.0 }, - "preallocate_segments": { - "description": "Writes log segments into files zero-filled to `segment_bytes` ahead of use, so each commit overwrites allocated blocks and syncs only its data instead of journaling a file size change. On the load cluster's disks this halves the sync and commit latency. The unused allocation of the active segment and one spare segment count toward `max_disk_bytes`. Each write also records, in a header slot synced with it, how far earlier syncs reached, so a disk that returns synced pages as zeros is reported as corruption rather than read as free space. Needs `segment_bytes` above 8 KiB. Defaults to true.", + "request_connection_window_bytes": { + "description": "HTTP/2 connection window of a request connection (default 16 MiB).", "type": [ - "boolean", + "integer", "null" - ] + ], + "format": "uint32", + "minimum": 0.0 }, - "recovery_timeout_ms": { - "description": "How long log shard recovery may go without finishing another file before the shard fails (default 120 s). It does not bound the whole scan, which grows with the log.", + "request_connections": { + "description": "Connections per peer for unary requests, each carrying one peer channel (default 4).", "type": [ "integer", "null" ], - "format": "uint64", + "format": "uint", "minimum": 0.0 }, - "release_idle_payloads_after_ms": { - "description": "How long a volume replica appends nothing before its synced payloads leave memory; defaults to 5000.", + "request_stream_window_bytes": { + "description": "HTTP/2 stream window this worker grants a peer channel's replies (default 4 MiB). A worker's server grants each incoming stream a fixed 4 MiB window.", "type": [ "integer", "null" ], - "format": "uint64", + "format": "uint32", "minimum": 0.0 }, - "remove_segments_timeout_ms": { + "server_connection_capacity": { + "description": "TCP connections the internal HTTP listener may serve at once (default 8192).", "type": [ "integer", "null" ], - "format": "uint64", + "format": "uint", "minimum": 0.0 }, - "segment_bytes": { + "stream_connection_window_bytes": { + "description": "HTTP/2 connection window of a stream connection (default 32 MiB).", "type": [ "integer", "null" ], - "format": "uint64", + "format": "uint32", "minimum": 0.0 }, - "write_to_cold_at_disk_percent": { - "description": "Early cold-write watermark, 1..99 percent of the disk cap; defaults to 75.", + "stream_connections": { + "description": "Connections per peer for long-lived streams such as tunnels (default 2).", "type": [ "integer", "null" ], - "format": "uint8", + "format": "uint", "minimum": 0.0 }, - "write_to_disk_timeout_ms": { + "stream_stream_window_bytes": { + "description": "HTTP/2 stream window of a long-lived stream (default 1 MiB).", "type": [ "integer", "null" ], - "format": "uint64", + "format": "uint32", "minimum": 0.0 } }, "additionalProperties": false }, - "Logs": { + "Pegboard": { "type": "object", "properties": { - "redirect_logs_dir": { - "default": null, + "actor_index_buckets_per_name": { + "description": "Hashed index bucket volumes for each namespace and actor name (default 8, 1..=256).\n\nThis is part of the index address format. Changing it on a running cluster strands existing rows in buckets that listing and destroy no longer target, so treat it as fixed per cluster.", "type": [ - "string", + "integer", "null" - ] - } - }, - "additionalProperties": false - }, - "Metadata": { - "type": "object", - "required": [ - "project" - ], - "properties": { - "project": { - "type": "string" - } - }, - "additionalProperties": false - }, - "Metrics": { - "description": "Configuration for the metrics service.", - "type": "object", - "properties": { - "hardware": { - "description": "Host hardware metrics, including a short disk probe of each data directory at startup.", - "default": { - "disk_probe": null, - "disk_probe_bytes": null - }, - "allOf": [ - { - "$ref": "#/definitions/HardwareMetrics" - } - ] + ], + "format": "uint64", + "minimum": 0.0 }, - "host": { + "actor_index_list_concurrency": { + "description": "Maximum actor index bucket pages one listing reads at once (default 32).", "type": [ - "string", + "integer", "null" ], - "format": "ip" + "format": "uint", + "minimum": 0.0 }, - "port": { + "actor_index_reaper_max_concurrency": { + "description": "Lazy orphan deletes an actor listing runs at once (default 16).", "type": [ "integer", "null" ], - "format": "uint16", + "format": "uint", "minimum": 0.0 - } - }, - "additionalProperties": false - }, - "ObjectStorageConfig": { - "type": "object", - "properties": { - "primary": { - "default": "in_memory", - "allOf": [ - { - "$ref": "#/definitions/ObjectStoreBackend" - } - ] }, - "read_first_attempt_ms": { - "description": "Milliseconds the first attempt of a small GET or HEAD may wait for response headers before the client cuts it off and retries it with its normal timeouts. A request on a stalled connection otherwise waits the full 30 s client timeout. Default 250, set from the load cluster's Azure account, where about 0.02% of small requests took longer; 0 disables it. Response bodies are never cut off.", - "default": null, + "actor_index_reaper_max_pending": { + "description": "Rows an actor listing's lazy orphan deletes may queue at once (default 1024).", "type": [ "integer", "null" ], - "format": "uint64", + "format": "uint", "minimum": 0.0 }, - "write_first_attempt_bytes_per_second": { - "description": "The upload rate a conditional PUT's first attempt may fall to before it is cut off, which extends `write_first_attempt_ms` by the body's size. Default 8 MiB/s.", - "default": null, + "actor_key_cache_capacity": { + "description": "Actor key reservations and key epochs cached in one process, each (default 100000). Reservations never change, so a cached one is kept until evicted.", "type": [ "integer", "null" @@ -2086,178 +1935,103 @@ "format": "uint64", "minimum": 0.0 }, - "write_first_attempt_ms": { - "description": "Milliseconds the first attempt of a small conditional PUT (a lease, heartbeat or manifest) may wait for response headers, before the upload time its body needs at `write_first_attempt_bytes_per_second`. Unset by default, which disables it. Healthy conditional PUT latency is calibrated only on Azure; a healthy GCS PUT can take over 250 ms. GCS also allows about one write per second to one object, so a retry shortly after a cut-off on the same lease or heartbeat object risks a 429, an unknown outcome and an extra readback. Set it only from measured conditional PUT latency of the backend.", - "default": null, + "actor_start_notice_timeout_ms": { + "description": "Timeout of one start notice to a waiting gateway worker. Unit is in milliseconds (default 2000, 100..=30000).", "type": [ "integer", "null" ], "format": "uint64", "minimum": 0.0 - } - }, - "additionalProperties": false - }, - "ObjectStoreBackend": { - "oneOf": [ - { - "type": "string", - "enum": [ - "in_memory" - ] }, - { - "description": "A local directory, for a single engine process that keeps its data across restarts.", - "type": "object", - "required": [ - "file_system" + "actor_start_threshold": { + "description": "How long the engine waits after sending an actor's start for the node to report it running. Past it the start is given up: the node is told to stop that epoch, and the actor starts again once the node reports it stopped or is proven gone.\n\nUnit is in milliseconds (default 30000).", + "type": [ + "integer", + "null" ], - "properties": { - "file_system": { - "$ref": "#/definitions/FileSystemObjectStoreConfig" - } - }, - "additionalProperties": false + "format": "uint64", + "minimum": 0.0 }, - { - "type": "object", - "required": [ - "s3" + "actor_start_waiters_capacity": { + "description": "Gateway workers one actor keeps waiting for its start (default 64, 1..=1024). A request that finds the list full waits on its fallback poll alone.", + "type": [ + "integer", + "null" ], - "properties": { - "s3": { - "$ref": "#/definitions/S3ObjectStoreConfig" - } - }, - "additionalProperties": false + "format": "uint32", + "minimum": 0.0 }, - { - "type": "object", - "required": [ - "azure" - ], - "properties": { - "azure": { - "$ref": "#/definitions/AzureObjectStoreConfig" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "gcs" - ], - "properties": { - "gcs": { - "$ref": "#/definitions/GcsObjectStoreConfig" - } - }, - "additionalProperties": false - } - ] - }, - "Outbound": { - "description": "Policy for outbound HTTP requests to user-configured destinations, such as serverless runner URLs.\n\nThese requests originate from inside the trusted engine network, so without restrictions a caller who can configure a runner can reach internal-only services. The defaults deny every non-globally-routable destination except loopback.", - "type": "object", - "properties": { - "allow_cidrs": { - "description": "Additional CIDRs that are always permitted, evaluated after `deny_cidrs`.\n\nUse this to reach a specific internal service without opening up the whole private range.", - "type": [ - "array", - "null" - ], - "items": { - "type": "string" - } - }, - "allow_hosts": { - "description": "Hostnames that are always permitted, whatever they resolve to.\n\nMatched exactly and case-insensitively, with no wildcards. Use this for a first-party service whose address is not known ahead of time, such as a Kubernetes service name. Anything served at a listed host becomes reachable by every runner config, so list individual hosts rather than a shared entry point.", + "actor_stop_threshold": { + "description": "How long a node may take to stop its actors once asked, advertised to the node on connect. A node shutting down drains its actors within this budget, and RivetKit uses it as the default shutdown grace period.\n\nUnit is in milliseconds (default 1800000, 30 minutes).", "type": [ - "array", + "integer", "null" ], - "items": { - "type": "string" - } - }, - "allow_insecure_scheme": { - "description": "Allow plaintext `http://` destinations. When disabled only `https://` is permitted.", - "type": [ - "boolean", - "null" - ] - }, - "allow_loopback": { - "description": "Allow destinations that resolve to loopback addresses (127.0.0.0/8, ::1).\n\nEnabled by default so local development against `http://localhost:...` works without configuration.", - "type": [ - "boolean", - "null" - ] + "format": "uint64", + "minimum": 0.0 }, - "allow_private_networks": { - "description": "Allow destinations that resolve to private, link-local, shared (CGNAT), or otherwise non-globally-routable addresses.\n\nSelf-hosted deployments that point runners at addresses on their own network, such as a Docker Compose service name, need this enabled.", - "type": [ - "boolean", - "null" + "command_concurrency": { + "description": "Per-class handler concurrency for the storage commands pegboard registers on storage workers.", + "default": { + "actor": null, + "actor_event": null, + "actor_read": null, + "index": null + }, + "allOf": [ + { + "$ref": "#/definitions/CommandConcurrency" + } ] }, - "deny_cidrs": { - "description": "Additional CIDRs that are always denied. Takes precedence over every allow rule.", + "gateway_port": { + "description": "Port for the new partition-backed gateway and node Hyper listener (default 6422).", "type": [ - "array", + "integer", "null" ], - "items": { - "type": "string" - } + "format": "uint16", + "minimum": 0.0 }, - "max_redirects": { - "description": "Maximum number of redirects to follow. Every hop is re-checked against this policy.", + "gateway_retry_budget_max_tokens": { + "description": "Retries one gateway process can bank for a burst of refusals; the budget starts full (default 100, 0..=100000).", "type": [ "integer", "null" ], - "format": "uint", + "format": "uint32", "minimum": 0.0 - } - }, - "additionalProperties": false - }, - "PeerTransport": { - "description": "Connections and flow-control windows per peer. Omitted values use the defaults.", - "type": "object", - "properties": { - "channel_large_request_bytes": { - "description": "Requests with a larger payload use a peer's large-request channels, so they never queue ahead of small requests on one stream (default 64 KiB).", + }, + "gateway_retry_budget_percent": { + "description": "Retries of refused requests one gateway process allows, as a percentage of the requests it receives (default 10, 0..=100). A refused request past this budget fails with `guard.service_unavailable` and its retry hint, so the client owns the next attempt.", "type": [ "integer", "null" ], - "format": "uint", + "format": "uint32", "minimum": 0.0 }, - "channel_max_write_bytes": { - "description": "Most bytes a peer channel writes at once from its queue (default 256 KiB).", + "gateway_start_poll_after_ms": { + "description": "Time after a gateway request starts waiting for an actor's start notice before its first fallback read. Unit is in milliseconds (default 2000, 100..=30000).", "type": [ "integer", "null" ], - "format": "uint", + "format": "uint64", "minimum": 0.0 }, - "channel_queue_bytes": { - "description": "Bytes of requests a peer channel queues for writing; a request that would exceed it is refused, though an empty queue always takes one (default 64 MiB).", + "gateway_start_poll_interval_ms": { + "description": "Time between later fallback reads while a gateway request waits for an actor's start. Unit is in milliseconds (default 2000, 100..=30000).", "type": [ "integer", "null" ], - "format": "uint", + "format": "uint64", "minimum": 0.0 }, - "channel_queue_frames": { - "description": "Frames a peer channel queues for writing; a full queue refuses the request (default 8192).", + "gateway_start_waits_capacity": { + "description": "Actors one gateway process waits on at once for a start notice (default 65536, 1..=1048576). When full, a new request waits on its fallback poll alone.", "type": [ "integer", "null" @@ -2265,17 +2039,17 @@ "format": "uint", "minimum": 0.0 }, - "channel_reply_queue_bytes": { - "description": "Bytes of replies one served peer channel queues for writing. A channel at its cap refuses new requests as overloaded until its caller reads or the queued replies expire (default 64 MiB).", + "gateway_websocket_open_timeout_ms": { + "description": "Time an actor WebSocket upgrade may wait for its actor, as while storage recovers after an engine restart. Past it the gateway accepts the upgrade and closes it with `guard.service_unavailable` and a retry hint, so a client without a connect timeout retries instead of waiting forever. Unit is in milliseconds (default 15000, 1000..=45000).", "type": [ "integer", "null" ], - "format": "uint", + "format": "uint64", "minimum": 0.0 }, - "reply_queue_bytes": { - "description": "Bytes of replies queued across every served peer channel of this worker. At the cap every channel refuses new requests as overloaded (default 1 GiB).", + "node_actor_handles_capacity": { + "description": "Retained actor handles per node connection (default 16384, 1..=65536).", "type": [ "integer", "null" @@ -2283,17 +2057,17 @@ "format": "uint", "minimum": 0.0 }, - "request_connection_window_bytes": { - "description": "HTTP/2 connection window of a request connection (default 16 MiB).", + "node_actor_inbound_capacity": { + "description": "Events, KV and SQLite requests queued from one actor on a node connection (default 128, 1..=65536).", "type": [ "integer", "null" ], - "format": "uint32", + "format": "uint", "minimum": 0.0 }, - "request_connections": { - "description": "Connections per peer for unary requests, each carrying one peer channel (default 4).", + "node_actor_lane_capacity": { + "description": "Commands and tunnel messages queued toward one actor on a node connection (default 128, 1..=65536).", "type": [ "integer", "null" @@ -2301,8 +2075,8 @@ "format": "uint", "minimum": 0.0 }, - "request_stream_window_bytes": { - "description": "HTTP/2 stream window this worker grants a peer channel's replies (default 4 MiB). A worker's server grants each incoming stream a fixed 4 MiB window.", + "node_connect_admission_burst": { + "description": "node connects one process can admit in a burst; the budget starts full (default 400, 1..=1000000).", "type": [ "integer", "null" @@ -2310,8 +2084,8 @@ "format": "uint32", "minimum": 0.0 }, - "stream_connection_window_bytes": { - "description": "HTTP/2 connection window of a stream connection (default 32 MiB).", + "node_connect_admissions_per_second": { + "description": "node connects one process admits per second once its burst is spent (default 200, 1..=100000). A connect past the budget is closed at once with `node.connect_overloaded` and a retry hint, before any work, so a fleet reconnecting after an outage cannot overwhelm guard.", "type": [ "integer", "null" @@ -2319,32 +2093,26 @@ "format": "uint32", "minimum": 0.0 }, - "stream_connections": { - "description": "Connections per peer for long-lived streams such as tunnels (default 2).", + "node_connect_retry_after_ms": { + "description": "Retry hint sent with a shed node connect. The node waits between one and two hints, so shed connects return spread out, and caps the hint at 5 s so a shed node still retries well inside the lost threshold. The same hint goes with a connect refused because storage could not register the node yet. Unit is in milliseconds (default 1000, 100..=5000).", "type": [ "integer", "null" ], - "format": "uint", + "format": "uint64", "minimum": 0.0 }, - "stream_stream_window_bytes": { - "description": "HTTP/2 stream window of a long-lived stream (default 1 MiB).", + "node_directory_cache_ms": { + "description": "Maximum age of the process-local node directory that actor allocation reads instead of listing the node index on every allocation. Must be greater than 0.\n\nUnit is in milliseconds. Defaults to 1000.", "type": [ "integer", "null" ], - "format": "uint32", + "format": "uint64", "minimum": 0.0 - } - }, - "additionalProperties": false - }, - "Pegboard": { - "type": "object", - "properties": { - "actor_index_buckets_per_name": { - "description": "Hashed index bucket volumes for each namespace and actor name (default 8, 1..=256).\n\nThis is part of the index address format. Changing it on a running cluster strands existing rows in buckets that listing and destroy no longer target, so treat it as fixed per cluster.", + }, + "node_directory_max_stale_ms": { + "description": "Maximum age of a node directory listing that allocation still uses while one background listing replaces it. Allocation waits for a listing only past this age. Must be at least `node_directory_cache_ms`.\n\nUnit is in milliseconds. Defaults to 5000.", "type": [ "integer", "null" @@ -2352,17 +2120,17 @@ "format": "uint64", "minimum": 0.0 }, - "actor_index_list_concurrency": { - "description": "Maximum actor index bucket pages one listing reads at once (default 32).", + "node_event_delivery_timeout_ms": { + "description": "Time a lifecycle event may spend retrying storage command deadlines before its node socket is closed for reconnection. Defaults to 120000 ms.", "type": [ "integer", "null" ], - "format": "uint", + "format": "uint64", "minimum": 0.0 }, - "actor_index_reaper_max_concurrency": { - "description": "Lazy orphan deletes an actor listing runs at once (default 16).", + "node_index_reaper_max_concurrency": { + "description": "Stale node row deletes one reader runs at once (default 8).", "type": [ "integer", "null" @@ -2370,8 +2138,8 @@ "format": "uint", "minimum": 0.0 }, - "actor_index_reaper_max_pending": { - "description": "Rows an actor listing's lazy orphan deletes may queue at once (default 1024).", + "node_index_reaper_max_pending": { + "description": "Stale node row deletes one reader may queue at once (default 256).", "type": [ "integer", "null" @@ -2379,8 +2147,8 @@ "format": "uint", "minimum": 0.0 }, - "actor_key_cache_capacity": { - "description": "Actor key reservations and key epochs cached in one process, each (default 100000). Reservations never change, so a cached one is kept until evicted.", + "node_lost_threshold": { + "description": "How long a disconnected node keeps its actors before stopping them, advertised to the node on connect. The engine waits as long after a connection closes before it judges the node's actors lost, so both sides agree. It must exceed the node client's longest reconnect backoff (30 s), or an engine restart stops every actor.\n\nUnit is in milliseconds (default 40000).", "type": [ "integer", "null" @@ -2388,49 +2156,34 @@ "format": "uint64", "minimum": 0.0 }, - "actor_start_notice_timeout_ms": { - "description": "Timeout of one start notice to a waiting gateway worker. Unit is in milliseconds (default 2000, 100..=30000).", + "node_ping_timeout": { + "description": "Max time since last pong before the node connection is terminated. Unit is in milliseconds.", "type": [ "integer", "null" ], - "format": "uint64", - "minimum": 0.0 + "format": "int64" }, - "actor_start_waiters_capacity": { - "description": "Gateway workers one actor keeps waiting for its start (default 64, 1..=1024). A request that finds the list full waits on its fallback poll alone.", + "node_record_cache_capacity": { + "description": "node records cached for routing fallbacks in one process (default 10000).", "type": [ "integer", "null" ], - "format": "uint32", + "format": "uint64", "minimum": 0.0 }, - "command_concurrency": { - "description": "Per-class handler concurrency for the Depot commands pegboard registers on storage workers.", - "default": { - "actor": null, - "actor_event": null, - "actor_read": null, - "index": null - }, - "allOf": [ - { - "$ref": "#/definitions/CommandConcurrency" - } - ] - }, - "envoy_actor_inbound_capacity": { - "description": "Events, KV and SQLite requests queued from one actor on an Envoy connection (default 128, 1..=65536).", + "node_record_cache_ms": { + "description": "Lifetime of a cached node record read on a routing fallback, in milliseconds (default 2000).", "type": [ "integer", "null" ], - "format": "uint", + "format": "uint64", "minimum": 0.0 }, - "envoy_actor_lane_capacity": { - "description": "Commands and tunnel messages queued toward one actor on an Envoy connection (default 128, 1..=65536).", + "node_socket_queue_capacity": { + "description": "Messages queued for one node socket writer (default 128, 1..=65536).", "type": [ "integer", "null" @@ -2438,26 +2191,26 @@ "format": "uint", "minimum": 0.0 }, - "envoy_connect_admission_burst": { - "description": "Envoy connects one process can admit in a burst; the budget starts full (default 400, 1..=1000000).", + "node_start_wait_ms": { + "description": "Longest time a node connection holds a gateway request for an actor generation whose start command it has not forwarded yet, in milliseconds (default 1000). A gateway sends a request right behind the start, so this keeps the request from reaching the node first. When it runs out the request is forwarded anyway, and the node refuses it as not found.", "type": [ "integer", "null" ], - "format": "uint32", + "format": "uint64", "minimum": 0.0 }, - "envoy_connect_admissions_per_second": { - "description": "Envoy connects one process admits per second once its burst is spent (default 200, 1..=100000). A connect past the budget is closed at once with `envoy.connect_overloaded` and a retry hint, before any work, so a fleet reconnecting after an outage cannot overwhelm guard.", + "node_tunnel_queue_capacity": { + "description": "Messages queued toward one gateway tunnel stream on a node connection (default 128, 1..=65536).", "type": [ "integer", "null" ], - "format": "uint32", + "format": "uint", "minimum": 0.0 }, - "envoy_connect_retry_after_ms": { - "description": "Retry hint sent with a shed Envoy connect. The Envoy waits between one and two hints, so shed connects return spread out, and caps the hint at 5 s so a shed runner still retries well inside the lost threshold. The same hint goes with a connect refused because storage could not register the Envoy yet. Unit is in milliseconds (default 1000, 100..=5000).", + "node_update_ping_interval": { + "description": "Ping interval for node updates in milliseconds.", "type": [ "integer", "null" @@ -2465,8 +2218,8 @@ "format": "uint64", "minimum": 0.0 }, - "envoy_directory_cache_ms": { - "description": "Maximum age of the process-local Envoy directory that actor allocation reads instead of listing the Envoy index on every allocation. Must be greater than 0.\n\nUnit is in milliseconds. Defaults to 1000.", + "sqlite_remote_idle_ms": { + "description": "Idle time after which a remote SQL session is closed and reopened on next use.\n\nUnit is in milliseconds (default 30000).", "type": [ "integer", "null" @@ -2474,44 +2227,48 @@ "format": "uint64", "minimum": 0.0 }, - "envoy_directory_max_stale_ms": { - "description": "Maximum age of an Envoy directory listing that allocation still uses while one background listing replaces it. Allocation waits for a listing only past this age. Must be at least `envoy_directory_cache_ms`.\n\nUnit is in milliseconds. Defaults to 5000.", + "sqlite_remote_max_response_bytes": { + "description": "Largest response, in logical bytes, that one remote SQL request may return. A larger result fails the request instead of being sent.\n\nUnit is in bytes (default 25165824, 24 MiB; at most 64 MiB).", "type": [ "integer", "null" ], - "format": "uint64", + "format": "uint", "minimum": 0.0 }, - "envoy_event_delivery_timeout_ms": { - "description": "Time a lifecycle event may spend retrying storage command deadlines before its Envoy socket is closed for reconnection. Defaults to 120000 ms.", + "sqlite_remote_worker_threads": { + "description": "Threads shared by every remote SQL session in this process (default 4).", "type": [ "integer", "null" ], - "format": "uint64", + "format": "uint", "minimum": 0.0 }, - "envoy_index_reaper_max_concurrency": { - "description": "Stale Envoy row deletes one reader runs at once (default 8).", + "sqlite_server_prefetch": { + "description": "Sends SQLite pages the client is likely to read next along with the pages it asked for (default true). When off, the engine sends only requested pages but keeps maintaining its prefetch hints, so turning it back on needs no rebuild. Runtime configurable.", "type": [ - "integer", + "boolean", "null" - ], - "format": "uint", - "minimum": 0.0 + ] }, - "envoy_index_reaper_max_pending": { - "description": "Stale Envoy row deletes one reader may queue at once (default 256).", + "sqlite_sessions": { + "description": "Maximum actor storage lanes retained by one node connection (default 4000). Explicit overrides must be positive; each lane retains its database volume until stop.", "type": [ "integer", "null" ], "format": "uint", "minimum": 0.0 - }, - "envoy_lost_checks_capacity": { - "description": "Lost checks one process holds for closed Envoy connections that may not reconnect (default 4096, 1..=65536). When full, the check due soonest is dropped and its actors are found lost on their next request.", + } + }, + "additionalProperties": false + }, + "ProcessClassCapacity": { + "description": "The process-wide caps of a class that travels on unreserved subjects.", + "type": "object", + "properties": { + "incoming_capacity": { "type": [ "integer", "null" @@ -2519,238 +2276,47 @@ "format": "uint", "minimum": 0.0 }, - "envoy_lost_threshold": { - "description": "How long a disconnected Envoy keeps its actors before stopping them, advertised to the Envoy on connect. The engine waits as long after a connection closes before it judges the Envoy's actors lost, so both sides agree. It must exceed the Envoy client's longest reconnect backoff (30 s), or an engine restart stops every actor.\n\nUnit is in milliseconds (default 40000).", + "outgoing_capacity": { "type": [ "integer", "null" ], - "format": "int64" + "format": "uint", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + "PubSub": { + "description": "Exact-subject worker requests and streams.", + "type": "object", + "properties": { + "classes": { + "description": "Per-class request caps. Every class has a defined cap; omitted values use the defaults.", + "anyOf": [ + { + "$ref": "#/definitions/RequestClasses" + }, + { + "type": "null" + } + ] }, - "envoy_ping_timeout": { - "description": "Max time since last pong before the envoy connection is terminated. Unit is in milliseconds.", + "max_message_bytes": { "type": [ "integer", "null" ], - "format": "int64" + "format": "uint", + "minimum": 0.0 }, - "envoy_record_cache_capacity": { - "description": "Envoy records cached for routing fallbacks in one process (default 10000).", + "max_request_subjects": { + "description": "Maximum concurrently reserved request subjects with isolated capacity.", "type": [ "integer", "null" ], - "format": "uint64", - "minimum": 0.0 - }, - "envoy_record_cache_ms": { - "description": "Lifetime of a cached Envoy record read on a routing fallback, in milliseconds (default 2000).", - "type": [ - "integer", - "null" - ], - "format": "uint64", - "minimum": 0.0 - }, - "envoy_socket_queue_capacity": { - "description": "Messages queued for one Envoy socket writer (default 128, 1..=65536).", - "type": [ - "integer", - "null" - ], - "format": "uint", - "minimum": 0.0 - }, - "envoy_start_wait_ms": { - "description": "Longest time an Envoy connection holds a gateway request for an actor generation whose start command it has not forwarded yet, in milliseconds (default 1000). A gateway sends a request right behind the start, so this keeps the request from reaching the Envoy first. When it runs out the request is forwarded anyway, and the Envoy refuses it as not found.", - "type": [ - "integer", - "null" - ], - "format": "uint64", - "minimum": 0.0 - }, - "envoy_tunnel_queue_capacity": { - "description": "Messages queued toward one gateway tunnel stream on an Envoy connection (default 128, 1..=65536).", - "type": [ - "integer", - "null" - ], - "format": "uint", - "minimum": 0.0 - }, - "envoy_update_ping_interval": { - "description": "Ping interval for envoy updates in milliseconds.", - "type": [ - "integer", - "null" - ], - "format": "uint64", - "minimum": 0.0 - }, - "gateway_port": { - "description": "Port for the new partition-backed gateway and Envoy Hyper listener (default 6422).", - "type": [ - "integer", - "null" - ], - "format": "uint16", - "minimum": 0.0 - }, - "gateway_retry_budget_max_tokens": { - "description": "Retries one gateway process can bank for a burst of refusals; the budget starts full (default 100, 0..=100000).", - "type": [ - "integer", - "null" - ], - "format": "uint32", - "minimum": 0.0 - }, - "gateway_retry_budget_percent": { - "description": "Retries of refused requests one gateway process allows, as a percentage of the requests it receives (default 10, 0..=100). A refused request past this budget fails with `guard.service_unavailable` and its retry hint, so the client owns the next attempt.", - "type": [ - "integer", - "null" - ], - "format": "uint32", - "minimum": 0.0 - }, - "gateway_start_poll_after_ms": { - "description": "Time after a gateway request starts waiting for an actor's start notice before its first fallback read. Unit is in milliseconds (default 2000, 100..=30000).", - "type": [ - "integer", - "null" - ], - "format": "uint64", - "minimum": 0.0 - }, - "gateway_start_poll_interval_ms": { - "description": "Time between later fallback reads while a gateway request waits for an actor's start. Unit is in milliseconds (default 2000, 100..=30000).", - "type": [ - "integer", - "null" - ], - "format": "uint64", - "minimum": 0.0 - }, - "gateway_start_waits_capacity": { - "description": "Actors one gateway process waits on at once for a start notice (default 65536, 1..=1048576). When full, a new request waits on its fallback poll alone.", - "type": [ - "integer", - "null" - ], - "format": "uint", - "minimum": 0.0 - }, - "gateway_websocket_open_timeout_ms": { - "description": "Time an actor WebSocket upgrade may wait for its actor, as while storage recovers after an engine restart. Past it the gateway accepts the upgrade and closes it with `guard.service_unavailable` and a retry hint, so a client without a connect timeout retries instead of waiting forever. Unit is in milliseconds (default 15000, 1000..=45000).", - "type": [ - "integer", - "null" - ], - "format": "uint64", - "minimum": 0.0 - }, - "sqlite_remote_idle_ms": { - "description": "Idle time after which a remote SQL session is closed and reopened on next use.\n\nUnit is in milliseconds (default 30000).", - "type": [ - "integer", - "null" - ], - "format": "uint64", - "minimum": 0.0 - }, - "sqlite_remote_max_response_bytes": { - "description": "Largest response, in logical bytes, that one remote SQL request may return. A larger result fails the request instead of being sent.\n\nUnit is in bytes (default 25165824, 24 MiB; at most 64 MiB).", - "type": [ - "integer", - "null" - ], - "format": "uint", - "minimum": 0.0 - }, - "sqlite_remote_worker_threads": { - "description": "Threads shared by every remote SQL session in this process (default 4).", - "type": [ - "integer", - "null" - ], - "format": "uint", - "minimum": 0.0 - }, - "sqlite_server_prefetch": { - "description": "Sends SQLite pages the client is likely to read next along with the pages it asked for (default true). When off, the engine sends only requested pages but keeps maintaining its prefetch hints, so turning it back on needs no rebuild. Runtime configurable.", - "type": [ - "boolean", - "null" - ] - }, - "sqlite_sessions": { - "description": "Maximum SQLite actor sessions retained by one Envoy connection (default 256).", - "type": [ - "integer", - "null" - ], - "format": "uint", - "minimum": 0.0 - } - }, - "additionalProperties": false - }, - "ProcessClassCapacity": { - "description": "The process-wide caps of a class that travels on unreserved subjects.", - "type": "object", - "properties": { - "incoming_capacity": { - "type": [ - "integer", - "null" - ], - "format": "uint", - "minimum": 0.0 - }, - "outgoing_capacity": { - "type": [ - "integer", - "null" - ], - "format": "uint", - "minimum": 0.0 - } - }, - "additionalProperties": false - }, - "PubSub": { - "description": "Exact-subject worker requests and streams.", - "type": "object", - "properties": { - "classes": { - "description": "Per-class request caps. Every class has a defined cap; omitted values use the defaults.", - "anyOf": [ - { - "$ref": "#/definitions/RequestClasses" - }, - { - "type": "null" - } - ] - }, - "max_message_bytes": { - "type": [ - "integer", - "null" - ], - "format": "uint", - "minimum": 0.0 - }, - "max_request_subjects": { - "description": "Maximum concurrently reserved request subjects with isolated capacity.", - "type": [ - "integer", - "null" - ], - "format": "uint", + "format": "uint", "minimum": 0.0 }, "peer_transport": { @@ -2761,10 +2327,13 @@ "channel_queue_bytes": null, "channel_queue_frames": null, "channel_reply_queue_bytes": null, + "incoming_channel_capacity": null, + "outgoing_channel_capacity": null, "reply_queue_bytes": null, "request_connection_window_bytes": null, "request_connections": null, "request_stream_window_bytes": null, + "server_connection_capacity": null, "stream_connection_window_bytes": null, "stream_connections": null, "stream_stream_window_bytes": null @@ -2815,7 +2384,7 @@ "additionalProperties": false }, "RequestClasses": { - "description": "Per-class request caps. Depot classes travel on reserved subjects whose window is their incoming cap, so they take only an outgoing cap across every target.", + "description": "Per-class request caps. storage classes travel on reserved subjects whose window is their incoming cap, so they take only an outgoing cap across every target.", "type": "object", "properties": { "actor_start_notice": { @@ -2829,41 +2398,41 @@ } ] }, - "depot_coordinator": { - "description": "Depot tablet coordinator requests (default outgoing 1024).", + "gateway_http": { + "description": "Gateway HTTP tunnels (default 4096 each way).", "anyOf": [ { - "$ref": "#/definitions/SubjectClassCapacity" + "$ref": "#/definitions/ProcessClassCapacity" }, { "type": "null" } ] }, - "depot_kv_command": { - "description": "Depot KV commands (default outgoing 8192).", + "gateway_websocket": { + "description": "Gateway WebSocket tunnels, held for the socket's life (default 65536 each way).", "anyOf": [ { - "$ref": "#/definitions/SubjectClassCapacity" + "$ref": "#/definitions/ProcessClassCapacity" }, { "type": "null" } ] }, - "depot_log": { - "description": "Depot log shard requests (default outgoing 8192).", + "node_command": { + "description": "Actor commands to node connections (default 4096 each way).", "anyOf": [ { - "$ref": "#/definitions/SubjectClassCapacity" + "$ref": "#/definitions/ProcessClassCapacity" }, { "type": "null" } ] }, - "depot_stop_readiness": { - "description": "Depot stop-readiness checks (default outgoing 1024).", + "storage_coordinator": { + "description": "storage partition coordinator requests (default outgoing 1024).", "anyOf": [ { "$ref": "#/definitions/SubjectClassCapacity" @@ -2873,8 +2442,8 @@ } ] }, - "depot_volume": { - "description": "Depot volume lifecycle and filesystem requests (default outgoing 4096).", + "storage_kv_command": { + "description": "storage KV commands (default outgoing 8192).", "anyOf": [ { "$ref": "#/definitions/SubjectClassCapacity" @@ -2884,33 +2453,33 @@ } ] }, - "envoy_command": { - "description": "Actor commands to envoy connections (default 4096 each way).", + "storage_log": { + "description": "storage log shard requests (default outgoing 8192).", "anyOf": [ { - "$ref": "#/definitions/ProcessClassCapacity" + "$ref": "#/definitions/SubjectClassCapacity" }, { "type": "null" } ] }, - "gateway_http": { - "description": "Gateway HTTP tunnels (default 4096 each way).", + "storage_stop_readiness": { + "description": "storage stop-readiness checks (default outgoing 1024).", "anyOf": [ { - "$ref": "#/definitions/ProcessClassCapacity" + "$ref": "#/definitions/SubjectClassCapacity" }, { "type": "null" } ] }, - "gateway_websocket": { - "description": "Gateway WebSocket tunnels, held for the socket's life (default 65536 each way).", + "storage_volume": { + "description": "storage volume lifecycle and filesystem requests (default outgoing 4096).", "anyOf": [ { - "$ref": "#/definitions/ProcessClassCapacity" + "$ref": "#/definitions/SubjectClassCapacity" }, { "type": "null" @@ -2932,19 +2501,548 @@ "format": "uint32", "minimum": 0.0 }, - "interval_ms": { - "type": "integer", + "interval_ms": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + "Runtime": { + "type": "object", + "properties": { + "force_shutdown_duration": { + "description": "Time (in seconds) after which the engine process will forcibly exit after receiving SIGTERM. Must be greater than or equal to both worker_shutdown_duration and guard_shutdown_duration. Defaults to nine minutes and 50 seconds, allowing the storage stop wait, singleton handover and teardown reserve.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "guard_shutdown_duration": { + "description": "Time (in seconds) to allow for guard to wait for pending requests after receiving SIGTERM. Defaults to nine minutes.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "worker_cpu_max": { + "description": "Adjusts worker curve around this value (in millecores, i.e. 1000 = 1 core). Is not a hard limit. When unset, uses /sys/fs/cgroup/cpu.max, and if that is unset uses total host cpu.", + "type": [ + "integer", + "null" + ], + "format": "uint", + "minimum": 0.0 + }, + "worker_max_concurrent_workflows": { + "description": "Maximum concurrently running workflows of a given workflow name for the **entire cluster**.", + "type": [ + "object", + "null" + ], + "additionalProperties": { + "type": "integer", + "format": "uint", + "minimum": 0.0 + } + }, + "worker_max_deduped_workflows_per_pull": { + "description": "Maximum deduplicated workflows considered in a single pull. Defaults to 10000.", + "type": [ + "integer", + "null" + ], + "format": "uint", + "minimum": 0.0 + }, + "worker_max_wake_condition_clears_per_pull": { + "type": [ + "integer", + "null" + ], + "format": "uint", + "minimum": 0.0 + }, + "worker_max_wake_keys_per_workflow_name_per_pull": { + "description": "Maximum wake condition keys read per workflow name in a single pull. Defaults to 20000, or 5000 for the storage compaction workflows. Set the \"default\" key to apply to all workflow names.", + "type": [ + "object", + "null" + ], + "additionalProperties": { + "type": "integer", + "format": "uint", + "minimum": 0.0 + } + }, + "worker_max_workflows_per_pull": { + "description": "Maximum wake condition keys a worker clears when leasing workflows in a single pull. Defaults to 40000. Maximum workflows a worker attempts to lease in a single pull. Defaults to 1000.", + "type": [ + "integer", + "null" + ], + "format": "uint", + "minimum": 0.0 + }, + "worker_poll_interval_ms": { + "description": "Poll interval for pulling workflows and deciding whether a short sleep stays in memory. Unit is in milliseconds. Defaults to 16000.", + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 0.0 + }, + "worker_shutdown_duration": { + "description": "Time (in seconds) to allow for the gasoline worker engine to stop gracefully after receiving SIGTERM. Defaults to 30 seconds.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + "S3ObjectStoreConfig": { + "type": "object", + "required": [ + "bucket" + ], + "properties": { + "access_key_id": { + "default": null, + "type": [ + "string", + "null" + ] + }, + "bucket": { + "type": "string" + }, + "endpoint": { + "default": null, + "type": [ + "string", + "null" + ] + }, + "prefix": { + "default": null, + "type": [ + "string", + "null" + ] + }, + "region": { + "default": null, + "type": [ + "string", + "null" + ] + }, + "secret_access_key": { + "default": null, + "anyOf": [ + { + "$ref": "#/definitions/Secret" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + "Secret": { + "type": "string" + }, + "StorageConfig": { + "type": "object", + "properties": { + "alarms": { + "description": "Bounded, deadline-driven volume alarm delivery.", + "default": { + "handler_timeout_ms": null, + "io_timeout_ms": null, + "max_callbacks": null, + "max_candidates": null, + "max_operations": null, + "page_size": null, + "retry_max_ms": null, + "retry_min_ms": null + }, + "allOf": [ + { + "$ref": "#/definitions/AlarmConfig" + } + ] + }, + "auto_provision_partitions": { + "description": "Creates this many partitions over this storage worker when the cluster has none, so a single-process engine serves actors without an operator. A restart finishes a run that stopped partway; partitions it did not start are left alone. Clusters with several storage workers leave it unset and run `storage partition provision` instead.", + "type": [ + "integer", + "null" + ], + "format": "uint", + "minimum": 0.0 + }, + "balancing": { + "default": { + "destination_stability_ms": 60000, + "enabled": true, + "max_disk_probes": 4, + "missing_worker_ms": 120000, + "partitions_per_core": 2 + }, + "allOf": [ + { + "$ref": "#/definitions/PartitionBalancingConfig" + } + ] + }, + "bulk_read_bytes_per_sec": { + "description": "Bytes per second that bulk runs may read across every partition of this process, with a burst of a quarter second (at least 4 MiB). Bulk runs wait for their bytes; point reads never wait. Unset or 0 leaves bulk runs unpaced.", + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 0.0 + }, + "bulk_read_cache_blocks": { + "description": "Whether SST blocks a bulk run fetches enter the shared block cache. Defaults to true; false keeps large scans from evicting other volumes' hot blocks.", + "type": [ + "boolean", + "null" + ] + }, + "bulk_read_fetch_tasks": { + "description": "Concurrent SST block fetches inside one bulk run's scan. Defaults to 1; 0 is treated as 1.", + "type": [ + "integer", + "null" + ], + "format": "uint", + "minimum": 0.0 + }, + "bulk_read_min_chunks": { + "description": "Chunk runs of at least this many chunks (4 KiB each) are read with the bulk scan settings below, as a table scan's read-ahead windows are. Unset or 0 reads every run with the normal settings.", + "type": [ + "integer", + "null" + ], + "format": "uint", + "minimum": 0.0 + }, + "checkpoints": { + "default": { + "max_named_checkpoints_per_volume": 64, + "retention": null + }, + "allOf": [ + { + "$ref": "#/definitions/CheckpointConfig" + } + ] + }, + "cold_store": { + "default": { + "block_cache_bytes": null, + "compacted_sst_bytes": null, + "disk_cache_bytes": null, + "disk_cache_directory": null, + "disk_cache_on_compaction": null, + "disk_cache_on_flush": null, + "disk_cache_part_bytes": null, + "l0_max_ssts": null, + "max_concurrent_compactions": null, + "meta_cache_bytes": null, + "unflushed_budget_bytes": null + }, + "allOf": [ + { + "$ref": "#/definitions/ColdStoreConfig" + } + ] + }, + "cold_store_compression": { + "description": "Codec for SlateDB SST blocks in the cold store; defaults to `lz4`. Each SST records its own codec, so changing it keeps existing SSTs readable.", + "anyOf": [ + { + "$ref": "#/definitions/ColdStoreCompression" + }, + { + "type": "null" + } + ] + }, + "command_timeout_ms": { + "description": "Floor for every typed storage command deadline, in milliseconds. Each command keeps its own default (5 seconds generic, 30 seconds for actor storage); when set, the larger of the two bounds both the client's retry budget and the maintainer's handler timeout. Unset keeps the per-command defaults.", + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 0.0 + }, + "coordinator_request_capacity": { + "description": "Exact coordinator subject capacity. Defaults to 256.", + "type": [ + "integer", + "null" + ], + "format": "uint", + "minimum": 0.0 + }, + "durable_max_events_per_commit": { + "description": "Most events of one durable state machine applied in one commit. Events that arrive while a commit is in flight are grouped up to this bound. Defaults to 16.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "endpoint_cache_capacity": { + "description": "Maximum cached endpoint hints, including missing workers. Defaults to 4096.", + "default": null, + "type": [ + "integer", + "null" + ], + "format": "uint", + "minimum": 0.0 + }, + "filesystem": { + "default": { + "max_cache_bytes": 8388608, + "max_conflict_window_bytes": 4194304, + "max_memory_bytes": 67108864, + "max_operation_bytes": 8388608, + "max_pending_commits": 128, + "max_process_cache_bytes": 1073741824, + "max_snapshot_age_ms": 30000, + "max_snapshots": 128, + "max_transaction_age_ms": 300000, + "max_transaction_bytes": 134217728, + "max_unapplied_bytes": 268435456, + "max_value_bytes": 1048576, + "note_ttl_ms": 1000, + "request_timeout_ms": 30000 + }, + "allOf": [ + { + "$ref": "#/definitions/FilesystemConfig" + } + ] + }, + "flush_piggyback_min_bytes": { + "description": "Unflushed applied bytes at which a volume joins a partition flush another volume requested. Defaults to 16 KiB.", + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 0.0 + }, + "flush_stage_concurrency": { + "description": "Volume records whose old rows one partition filesystem flush reads at once while it stages. Staging excludes every other store operation of the partition. Defaults to 16.", + "type": [ + "integer", + "null" + ], + "format": "uint", + "minimum": 0.0 + }, + "forks": { + "default": { + "check_grace_ms": 60000, + "materialize_batch_chunks": 1024, + "max_concurrent_materializations": 2 + }, + "allOf": [ + { + "$ref": "#/definitions/ForkConfig" + } + ] + }, + "heartbeat_gc": { + "description": "Background heartbeat reclamation; separate from membership lease expiration.", + "default": { + "batch_interval_ms": null, + "batch_size": null, + "batch_timeout_ms": null, + "delete_concurrency": null, + "pass_interval_ms": null, + "retention_ms": null + }, + "allOf": [ + { + "$ref": "#/definitions/HeartbeatGcConfig" + } + ] + }, + "idle_unload_after_ms": { + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 0.0 + }, + "kv_commands": { + "default": { + "evicted_idle_warn_ms": null, + "max_command_bytes": null, + "max_eviction_closes": null, + "max_inflight_command_bytes": null, + "max_inflight_commands": null, + "max_open_volumes": null, + "max_worker_open_volume_bytes": null, + "max_worker_open_volumes": null, + "request_capacity": null, + "short_idle_close_ms": null, + "volume_idle_close_ms": null + }, + "allOf": [ + { + "$ref": "#/definitions/KvCommandConfig" + } + ] + }, + "learning_deadline_ms": { + "description": "How long a partition replica-set change may stay in Learning before the coordinator aborts it and the partition returns to its old members. Defaults to one hour and must be at least 45 minutes. It counts from the phase's start, not from the learner's progress, so it must outlast the membership holder wait (15 minutes) plus the copy of the largest log suffix a learner may have to catch up.", + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 0.0 + }, + "log_client_inflight_per_worker": { + "description": "Log shard requests a client keeps in flight to one storage worker. Later requests queue and go out together as one batch when a slot frees. Defaults to 2.", + "type": [ + "integer", + "null" + ], + "format": "uint", + "minimum": 0.0 + }, + "log_directory": { + "description": "Existing directory owned by one storage process. Required when storage services start.", + "type": [ + "string", + "null" + ] + }, + "log_request_capacity": { + "description": "Exact replica subject capacity, shared by local clients and the shard. Defaults to 2048.", + "type": [ + "integer", + "null" + ], + "format": "uint", + "minimum": 0.0 + }, + "log_shard": { + "description": "Optional overrides for the shard's disk, memory and operation bounds.", + "default": { + "checkpoint_bytes": null, + "checkpoint_timeout_ms": null, + "disk_read_concurrency": null, + "inbox_capacity": null, + "manifest_refresh_timeout_ms": null, + "max_admission_wait_ms": null, + "max_append_admission_wait_ms": null, + "max_batch_bytes": null, + "max_disk_bytes": null, + "max_disk_reads": null, + "max_loaded_volume_bytes": null, + "max_loaded_volumes": null, + "max_memory_bytes": null, + "max_memory_requests": null, + "max_retained_payload_bytes": null, + "max_retained_payload_bytes_per_volume": null, + "max_write_bytes": null, + "overload_retry_after_ms": null, + "preallocate_segments": null, + "recovery_timeout_ms": null, + "release_idle_payloads_after_ms": null, + "remove_segments_timeout_ms": null, + "segment_bytes": null, + "write_to_cold_at_disk_percent": null, + "write_to_disk_timeout_ms": null + }, + "allOf": [ + { + "$ref": "#/definitions/LogShardConfig" + } + ] + }, + "lookup_request_capacity": { + "description": "Concurrent topology endpoint lookups and heartbeat inspections admitted per process. Defaults to 128.", + "type": [ + "integer", + "null" + ], + "format": "uint", + "minimum": 0.0 + }, + "maintainer_inbox_capacity": { + "description": "Queued events one partition maintainer accepts before it rejects new requests as overloaded. Every volume open, read, apply and status notice for the partition passes through it, so a burst of actor starts on one partition needs room for several events per start. Must be positive. Defaults to 4096.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "maintainer_max_loaded_volumes": { + "description": "Volumes one partition maintainer holds loaded at once, before opens of more are refused as overloaded. A loaded volume costs about 1.8 KiB of maintainer heap, so the default of 500,000 holds under 1 GiB per partition. Must be positive.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "maintainer_max_queued_opens": { + "description": "Volume opens queued in one partition maintainer's inbox, not yet taken by its processing task, at which a new open is refused as overloaded at once instead of joining the inbox. Other inbox events do not count. Opens queued past this would mostly expire before admission. Must be positive. Defaults to 1024.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "maintainer_max_requests": { + "description": "Client requests one partition maintainer tracks at once before it rejects more as overloaded. Must be positive. Defaults to 1024.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "manifest_refresh_ms": { + "description": "Fallback interval for re-reading the partition manifest. Routing misses and `NotMaintainer` replies refresh it at once. Defaults to 30 seconds.", + "type": [ + "integer", + "null" + ], "format": "uint64", "minimum": 0.0 - } - }, - "additionalProperties": false - }, - "Runtime": { - "type": "object", - "properties": { - "force_shutdown_duration": { - "description": "Time (in seconds) after which the engine process will forcibly exit after receiving SIGTERM. Must be greater than or equal to both worker_shutdown_duration and guard_shutdown_duration. Defaults to 35 minutes, allowing the Depot stop wait and teardown reserve.", + }, + "max_background_cold_reads": { + "description": "Size of each background cold-read window per partition maintainer: held-image source reads for forks, replica log reads for applies, fork jobs and fork cleanup jobs are each bounded by it separately, so they never take client read slots. Defaults to 4.", "type": [ "integer", "null" @@ -2952,8 +3050,8 @@ "format": "uint32", "minimum": 0.0 }, - "guard_shutdown_duration": { - "description": "Time (in seconds) to allow for guard to wait for pending requests after receiving SIGTERM. Defaults to 10 minutes.", + "max_cold_reads": { + "description": "Client cold-store reads in flight per partition maintainer: session opens, lifecycle row lookups and session reads share this window. A cold wake's reads fan out through it, so a narrow window serializes them. Object storage throughput scales almost linearly to about 32 parallel GETs, and a read slot is not always a GET, so 64 keeps a cold wake's reads from waiting on slots; on the load cluster a fully cold wake took about 850 ms at 4, 430 ms at 64 and 390 ms at 512. Defaults to 64.", "type": [ "integer", "null" @@ -2961,8 +3059,17 @@ "format": "uint32", "minimum": 0.0 }, - "worker_cpu_max": { - "description": "Adjusts worker curve around this value (in millecores, i.e. 1000 = 1 core). Is not a hard limit. When unset, uses /sys/fs/cgroup/cpu.max, and if that is unset uses total host cpu.", + "max_cold_reads_per_volume": { + "description": "Client session reads of one volume a partition maintainer admits while reads of other volumes wait, so one large scan cannot hold every client cold-read slot. Slots no other volume waits for still go to that volume. Unset or 0 admits client reads in arrival order.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "max_read_sessions": { + "description": "Filesystem read sessions per partition maintainer. A session lives while its volume is being read and ends after `read_session_idle_ms` without a read, so this bounds how many volumes a partition serves reads for at once. Opens past it are refused as overloaded. A held session retains about 1.2 KiB of maintainer heap, so the default of 16,384 holds under 20 MiB per partition. Must be positive.", "type": [ "integer", "null" @@ -2970,58 +3077,53 @@ "format": "uint", "minimum": 0.0 }, - "worker_max_concurrent_workflows": { - "description": "Maximum concurrently running workflows of a given workflow name for the **entire cluster**.", + "membership_refresh_cooldown_ms": { + "description": "Minimum milliseconds between miss-triggered membership refresh attempts, including failures.", "type": [ - "object", + "integer", "null" ], - "additionalProperties": { - "type": "integer", - "format": "uint", - "minimum": 0.0 - } + "format": "uint64", + "minimum": 0.0 }, - "worker_max_deduped_workflows_per_pull": { - "description": "Maximum deduplicated workflows considered in a single pull. Defaults to 10000.", + "memtable_flush_throttle_ms": { + "description": "Throttle for memtable flushes that nobody urgently waits on: a pass that comes due waits up to this long, so passes that come due close together share one upload and one L0 SST. Defaults to ten seconds.", "type": [ "integer", "null" ], - "format": "uint", + "format": "uint64", "minimum": 0.0 }, - "worker_max_wake_condition_clears_per_pull": { + "read_cursor_session_idle_ms": { + "description": "How long a session opened at the latest position stays registered after its last read, in milliseconds. An HTTP directory cursor resumes such a session between pages, and no client reopens it at its position, so a paging client keeps its cursor for this long between pages. Must be positive. Defaults to 30,000, the session lifetime.", "type": [ "integer", "null" ], - "format": "uint", + "format": "uint64", "minimum": 0.0 }, - "worker_max_wake_keys_per_workflow_name_per_pull": { - "description": "Maximum wake condition keys read per workflow name in a single pull. Defaults to 20000, or 5000 for the depot compaction workflows. Set the \"default\" key to apply to all workflow names.", + "read_session_idle_ms": { + "description": "How long a read session stays registered after its last read, in milliseconds. Each read extends it up to the session's lifetime, and a client reopens an ended session on its next read, so sessions track volumes being read rather than volumes open. Must be positive. Defaults to 2,000.", "type": [ - "object", + "integer", "null" ], - "additionalProperties": { - "type": "integer", - "format": "uint", - "minimum": 0.0 - } + "format": "uint64", + "minimum": 0.0 }, - "worker_max_workflows_per_pull": { - "description": "Maximum wake condition keys a worker clears when leasing workflows in a single pull. Defaults to 40000. Maximum workflows a worker attempts to lease in a single pull. Defaults to 1000.", + "simulated_fs_read_cost_us": { + "description": "Simulation only: processing time each filesystem read request costs a partition maintainer, in microseconds, spent on the simulated clock so a maintainer busy with reads queues them as a loaded one does. Unset in deployments, where real processing time passes on its own.", "type": [ "integer", "null" ], - "format": "uint", + "format": "uint64", "minimum": 0.0 }, - "worker_poll_interval_ms": { - "description": "Poll interval for pulling workflows and deciding whether a short sleep stays in memory. Unit is in milliseconds. Defaults to 16000.", + "stop_poll_ms": { + "description": "Stop-permit retry interval. Defaults to one second.", "type": [ "integer", "null" @@ -3029,74 +3131,137 @@ "format": "uint64", "minimum": 0.0 }, - "worker_shutdown_duration": { - "description": "Time (in seconds) to allow for the gasoline worker engine to stop gracefully after receiving SIGTERM. Defaults to 30 seconds.", + "stop_teardown_ms": { + "description": "Teardown budget after a stop permit or timeout. Defaults to one minute.", "type": [ "integer", "null" ], - "format": "uint32", + "format": "uint64", "minimum": 0.0 - } - }, - "additionalProperties": false - }, - "S3ObjectStoreConfig": { - "type": "object", - "required": [ - "bucket" - ], - "properties": { - "access_key_id": { - "default": null, + }, + "stop_timeout_ms": { + "description": "Maximum coordinated stop wait from the first signal. Defaults to eight minutes.", "type": [ - "string", + "integer", "null" - ] + ], + "format": "uint64", + "minimum": 0.0 }, - "bucket": { - "type": "string" + "store_admission_scan_limit": { + "description": "Queued store items one admission pass looks at past work that cannot start yet. Work beyond it waits for the next pass. Must be positive. Defaults to 1024.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 }, - "endpoint": { - "default": null, + "store_max_flush_staging": { + "description": "Partition filesystem flushes one partition maintainer lets stage at once. Staging writes the flush's records into the store. Must be positive. Defaults to 1.", "type": [ - "string", + "integer", "null" - ] + ], + "format": "uint32", + "minimum": 0.0 }, - "prefix": { - "default": null, + "store_max_flush_uploads": { + "description": "Partition filesystem flushes one partition maintainer keeps in flight at once, staging or uploading; a staging flush counts because it uploads next. Must be at least `store_max_flush_staging`. Defaults to 2.", "type": [ - "string", + "integer", "null" - ] + ], + "format": "uint32", + "minimum": 0.0 }, - "region": { - "default": null, + "store_max_keyed_in_flight": { + "description": "Filesystem applies and loads one partition maintainer admits to its store at once. They run concurrently, one task each, and their replica reads stay bounded by the cold byte budget, so the cap sizes concurrency for a burst of volume creates rather than memory. Must be positive. Defaults to 256.", "type": [ - "string", + "integer", "null" - ] + ], + "format": "uint32", + "minimum": 0.0 }, - "secret_access_key": { - "default": null, + "store_volume_lock_stripes": { + "description": "Volume lock stripes of each partition filesystem store. Two volumes on one stripe serialize their store work. Must be positive. Defaults to 1024.", + "type": [ + "integer", + "null" + ], + "format": "uint", + "minimum": 0.0 + }, + "store_volume_locks": { + "description": "Which store work takes volume stripes. `core` (the default) trusts the maintainer core to order applies, loads, session opens, lifecycle lookups and the flush retention publish, so they take only the partition lock; stripes then guard only checkpoint-session opens against hold releases, checkpoint deletions and sweeps. `stripes` keeps a stripe on every operation, as before.", "anyOf": [ { - "$ref": "#/definitions/Secret" + "$ref": "#/definitions/StoreVolumeLocks" }, { "type": "null" } ] + }, + "volume_client": { + "default": { + "close_after_idle_ms": null, + "max_loaded_volumes": null, + "max_requests": null, + "max_volume_key_bytes": null, + "open_timeout_ms": null, + "payload_codec": null, + "sweep_interval_ms": null + }, + "allOf": [ + { + "$ref": "#/definitions/VolumeClientConfig" + } + ] + }, + "volume_request_capacity": { + "description": "Per-subject lifecycle request capacity, isolated from other UPS traffic. Defaults to 1024.", + "type": [ + "integer", + "null" + ], + "format": "uint", + "minimum": 0.0 + }, + "volume_request_timeout_ms": { + "description": "Entire lifecycle RPC budget, including route refreshes. Defaults to five seconds.", + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 0.0 + }, + "write_to_cold_above_bytes": { + "description": "Committed suffix threshold, default 64 MiB and maximum 256 MiB.", + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 0.0 + }, + "write_to_cold_after_ms": { + "description": "Cold-write age from the first unflushed entry, plus up to a tenth of it as jitter. Defaults to one minute. Storage workers require the age with its jitter to stay below `stop_timeout_ms`, so a stopping worker never waits on a slower tiering cadence than its own stop budget.", + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 0.0 } }, "additionalProperties": false }, - "Secret": { - "type": "string" - }, "StoreVolumeLocks": { - "description": "Which tablet store operations take volume lock stripes.", + "description": "Which partition store operations take volume lock stripes.", "oneOf": [ { "description": "Only work the maintainer core does not order takes a stripe.", @@ -3129,45 +3294,6 @@ }, "additionalProperties": false }, - "TabletBalancingConfig": { - "description": "Static tablet placement policy; membership and lease timings remain topology-owned.", - "type": "object", - "properties": { - "destination_stability_ms": { - "description": "How long a new worker must be listed in discovery, and pass one disk probe, before the coordinator places tablets on it. Must exceed the topology worker lease plus one renewal and the takeover grace, about 35 s, so a worker that crashes soon after it first appears has left discovery before it qualifies. Startup refuses a shorter value.", - "default": 60000, - "type": "integer", - "format": "uint64", - "minimum": 0.0 - }, - "enabled": { - "description": "Whether the tablet coordinator places, splits and replaces tablets on its own. When disabled, the layout changes only through operator commands: a new cluster gets no first tablet until an operator creates one, and a missing worker is never replaced.", - "default": true, - "type": "boolean" - }, - "max_disk_probes": { - "description": "Maximum concurrent disk admission requests made by the tablet coordinator.", - "default": 4, - "type": "integer", - "format": "uint32", - "minimum": 0.0 - }, - "missing_worker_ms": { - "description": "How long an assigned worker must be missing from discovery before its replicas are replaced. Counted from when discovery drops it, which is itself one lease after its last renewal.", - "default": 120000, - "type": "integer", - "format": "uint64", - "minimum": 0.0 - }, - "tablets_per_core": { - "default": 2, - "type": "integer", - "format": "uint32", - "minimum": 0.0 - } - }, - "additionalProperties": false - }, "Telemetry": { "type": "object", "required": [ diff --git a/vendor/docs/examples/docs/actors-quickstart-next-js/registry.ts b/vendor/docs/examples/docs/actors-quickstart-next-js/registry.ts deleted file mode 100644 index 4afe732a..00000000 --- a/vendor/docs/examples/docs/actors-quickstart-next-js/registry.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { actor, setup } from "rivetkit"; - -export const counter = actor({ - state: { count: 0 }, - actions: { - increment: (c, x: number) => { - c.state.count += x; - c.broadcast("newCount", c.state.count); - return c.state.count; - }, - }, -}); - -export const registry = setup({ - use: { counter }, -}); diff --git a/vendor/docs/examples/docs/actors-quickstart-react/index.ts b/vendor/docs/examples/docs/actors-quickstart-react/backend/index.ts similarity index 100% rename from vendor/docs/examples/docs/actors-quickstart-react/index.ts rename to vendor/docs/examples/docs/actors-quickstart-react/backend/index.ts diff --git a/vendor/docs/examples/docs/actors-quickstart-react/Counter.tsx b/vendor/docs/examples/docs/actors-quickstart-react/frontend/Counter.tsx similarity index 85% rename from vendor/docs/examples/docs/actors-quickstart-react/Counter.tsx rename to vendor/docs/examples/docs/actors-quickstart-react/frontend/Counter.tsx index a62ee024..26aa5be7 100644 --- a/vendor/docs/examples/docs/actors-quickstart-react/Counter.tsx +++ b/vendor/docs/examples/docs/actors-quickstart-react/frontend/Counter.tsx @@ -1,16 +1,16 @@ import { createRivetKit } from "@rivetkit/react"; import { useState } from "react"; -import type { registry } from "./index"; +import type { registry } from "../backend/index"; const { useActor } = createRivetKit("http://localhost:6420"); -function Counter() { +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"] + key: ["my-counter"], }); // Listen to realtime events diff --git a/vendor/docs/examples/docs/actors-quickstart-react/frontend/main.tsx b/vendor/docs/examples/docs/actors-quickstart-react/frontend/main.tsx new file mode 100644 index 00000000..a8e4ea9d --- /dev/null +++ b/vendor/docs/examples/docs/actors-quickstart-react/frontend/main.tsx @@ -0,0 +1,9 @@ +import { StrictMode } from "react"; +import { createRoot } from "react-dom/client"; +import { Counter } from "./Counter"; + +createRoot(document.getElementById("root")!).render( + + + , +); diff --git a/vendor/docs/examples/docs/actors-versions/programmatic-version.ts b/vendor/docs/examples/docs/actors-versions/programmatic-version.ts deleted file mode 100644 index 9763d971..00000000 --- a/vendor/docs/examples/docs/actors-versions/programmatic-version.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { actor, setup } from "rivetkit"; - -const myActor = actor({ state: {}, actions: {} }); - -const registry = setup({ - use: { myActor }, - envoy: { - version: 2, - }, -}); diff --git a/vendor/docs/examples/docs/api/actors-destroy.ts b/vendor/docs/examples/docs/api/actors-destroy.ts index 7718ad8c..d76fa102 100644 --- a/vendor/docs/examples/docs/api/actors-destroy.ts +++ b/vendor/docs/examples/docs/api/actors-destroy.ts @@ -1,4 +1,4 @@ -const endpoint = "https://rivet.example.com"; +const endpoint = "https://api.rivet.dev"; const namespace = process.env.RIVET_NAMESPACE ?? "default"; const token = process.env.RIVET_TOKEN ?? ""; const actorId = "00000000-0000-0000-0000-000000000000"; diff --git a/vendor/docs/examples/docs/api/actors-list-names.ts b/vendor/docs/examples/docs/api/actors-list-names.ts index ce5f865f..19ca05e2 100644 --- a/vendor/docs/examples/docs/api/actors-list-names.ts +++ b/vendor/docs/examples/docs/api/actors-list-names.ts @@ -1,4 +1,4 @@ -const endpoint = "https://rivet.example.com"; +const endpoint = "https://api.rivet.dev"; const namespace = process.env.RIVET_NAMESPACE ?? "default"; const token = process.env.RIVET_TOKEN ?? ""; diff --git a/vendor/docs/examples/docs/api/actors-list.ts b/vendor/docs/examples/docs/api/actors-list.ts index fcca97ee..55a79fcb 100644 --- a/vendor/docs/examples/docs/api/actors-list.ts +++ b/vendor/docs/examples/docs/api/actors-list.ts @@ -1,4 +1,4 @@ -const endpoint = "https://rivet.example.com"; +const endpoint = "https://api.rivet.dev"; const namespace = process.env.RIVET_NAMESPACE ?? "default"; const token = process.env.RIVET_TOKEN ?? ""; diff --git a/vendor/docs/examples/docs/api/actors-reschedule.ts b/vendor/docs/examples/docs/api/actors-reschedule.ts index 3a2f9b23..3bb1a563 100644 --- a/vendor/docs/examples/docs/api/actors-reschedule.ts +++ b/vendor/docs/examples/docs/api/actors-reschedule.ts @@ -1,4 +1,4 @@ -const endpoint = "https://rivet.example.com"; +const endpoint = "https://api.rivet.dev"; const namespace = process.env.RIVET_NAMESPACE ?? "default"; const token = process.env.RIVET_TOKEN ?? ""; const actorId = "00000000-0000-0000-0000-000000000000"; diff --git a/vendor/docs/examples/docs/api/actors-sleep.ts b/vendor/docs/examples/docs/api/actors-sleep.ts index e1232b21..a1d07784 100644 --- a/vendor/docs/examples/docs/api/actors-sleep.ts +++ b/vendor/docs/examples/docs/api/actors-sleep.ts @@ -1,4 +1,4 @@ -const endpoint = "https://rivet.example.com"; +const endpoint = "https://api.rivet.dev"; const namespace = process.env.RIVET_NAMESPACE ?? "default"; const token = process.env.RIVET_TOKEN ?? ""; const actorId = "00000000-0000-0000-0000-000000000000"; diff --git a/vendor/docs/examples/docs/api/connection-actions.ts b/vendor/docs/examples/docs/api/connection-actions.ts index d90ec127..3de81cd1 100644 --- a/vendor/docs/examples/docs/api/connection-actions.ts +++ b/vendor/docs/examples/docs/api/connection-actions.ts @@ -1,5 +1,5 @@ const ws = new WebSocket( - `wss://rivet.example.com/gateway/${process.env.ACTOR_ID}/connect`, + `wss://api.rivet.dev/gateway/${process.env.ACTOR_ID}/connect`, ["rivet", "rivet_encoding.json", `rivet_token.${process.env.RIVET_TOKEN}`], ); diff --git a/vendor/docs/examples/docs/api/connection-connect.ts b/vendor/docs/examples/docs/api/connection-connect.ts index ee4bf891..88e74ed3 100644 --- a/vendor/docs/examples/docs/api/connection-connect.ts +++ b/vendor/docs/examples/docs/api/connection-connect.ts @@ -1,7 +1,7 @@ // Open a connection by actor name. The selector query parameters are the same // ones used by the HTTP endpoints; the token and encoding travel as // WebSocket subprotocols because browsers cannot set headers on an upgrade. -const url = new URL("wss://rivet.example.com/gateway/counter/connect"); +const url = new URL("wss://api.rivet.dev/gateway/counter/connect"); url.searchParams.set("rvt-namespace", process.env.RIVET_NAMESPACE!); url.searchParams.set("rvt-method", "getOrCreate"); url.searchParams.set("rvt-key", "my-counter"); diff --git a/vendor/docs/examples/docs/api/connection-errors.ts b/vendor/docs/examples/docs/api/connection-errors.ts index f8e796c7..6345825a 100644 --- a/vendor/docs/examples/docs/api/connection-errors.ts +++ b/vendor/docs/examples/docs/api/connection-errors.ts @@ -1,5 +1,5 @@ const ws = new WebSocket( - `wss://rivet.example.com/gateway/${process.env.ACTOR_ID}/connect`, + `wss://api.rivet.dev/gateway/${process.env.ACTOR_ID}/connect`, ["rivet", "rivet_encoding.json", `rivet_token.${process.env.RIVET_TOKEN}`], ); diff --git a/vendor/docs/examples/docs/api/connection-events.ts b/vendor/docs/examples/docs/api/connection-events.ts index 71ad178a..7e85c61b 100644 --- a/vendor/docs/examples/docs/api/connection-events.ts +++ b/vendor/docs/examples/docs/api/connection-events.ts @@ -1,5 +1,5 @@ const ws = new WebSocket( - `wss://rivet.example.com/gateway/${process.env.ACTOR_ID}/connect`, + `wss://api.rivet.dev/gateway/${process.env.ACTOR_ID}/connect`, ["rivet", "rivet_encoding.json", `rivet_token.${process.env.RIVET_TOKEN}`], ); diff --git a/vendor/docs/examples/docs/api/control-plane-datacenters.ts b/vendor/docs/examples/docs/api/control-plane-datacenters.ts index 6f14c5eb..d5afca6c 100644 --- a/vendor/docs/examples/docs/api/control-plane-datacenters.ts +++ b/vendor/docs/examples/docs/api/control-plane-datacenters.ts @@ -1,4 +1,4 @@ -const endpoint = "https://rivet.example.com"; +const endpoint = "https://api.rivet.dev"; const token = process.env.RIVET_TOKEN ?? ""; const response = await fetch(`${endpoint}/datacenters`, { diff --git a/vendor/docs/examples/docs/api/control-plane-metadata.ts b/vendor/docs/examples/docs/api/control-plane-metadata.ts index ea784880..43300b91 100644 --- a/vendor/docs/examples/docs/api/control-plane-metadata.ts +++ b/vendor/docs/examples/docs/api/control-plane-metadata.ts @@ -1,4 +1,4 @@ -const endpoint = "https://rivet.example.com"; +const endpoint = "https://api.rivet.dev"; const response = await fetch(`${endpoint}/metadata`); const metadata = await response.json(); diff --git a/vendor/docs/examples/docs/api/gateway-metadata.ts b/vendor/docs/examples/docs/api/gateway-metadata.ts index 95da90f4..d677d2dd 100644 --- a/vendor/docs/examples/docs/api/gateway-metadata.ts +++ b/vendor/docs/examples/docs/api/gateway-metadata.ts @@ -1,4 +1,4 @@ -const endpoint = "https://rivet.example.com"; +const endpoint = "https://api.rivet.dev"; const response = await fetch( `${endpoint}/gateway/${process.env.ACTOR_ID}/metadata`, { diff --git a/vendor/docs/examples/docs/api/gateway-queue.ts b/vendor/docs/examples/docs/api/gateway-queue.ts index 73f95114..8d6a2aa6 100644 --- a/vendor/docs/examples/docs/api/gateway-queue.ts +++ b/vendor/docs/examples/docs/api/gateway-queue.ts @@ -1,4 +1,4 @@ -const endpoint = "https://rivet.example.com"; +const endpoint = "https://api.rivet.dev"; const actorId = process.env.ACTOR_ID; const response = await fetch(`${endpoint}/gateway/${actorId}/queue/increment`, { method: "POST", diff --git a/vendor/docs/examples/docs/api/inspector-action.ts b/vendor/docs/examples/docs/api/inspector-action.ts deleted file mode 100644 index ac8aca2c..00000000 --- a/vendor/docs/examples/docs/api/inspector-action.ts +++ /dev/null @@ -1,14 +0,0 @@ -const endpoint = "https://rivet.example.com"; -const response = await fetch( - `${endpoint}/gateway/${process.env.ACTOR_ID}/inspector/action/increment`, - { - method: "POST", - headers: { - Authorization: `Bearer ${process.env.INSPECTOR_TOKEN}`, - "Content-Type": "application/json", - }, - body: JSON.stringify({ args: [1] }), - }, -); -console.log(await response.json()); -export {}; diff --git a/vendor/docs/examples/docs/api/inspector-connections.ts b/vendor/docs/examples/docs/api/inspector-connections.ts deleted file mode 100644 index cebb890d..00000000 --- a/vendor/docs/examples/docs/api/inspector-connections.ts +++ /dev/null @@ -1,7 +0,0 @@ -const endpoint = "https://rivet.example.com"; -const response = await fetch( - `${endpoint}/gateway/${process.env.ACTOR_ID}/inspector/connections`, - { headers: { Authorization: `Bearer ${process.env.INSPECTOR_TOKEN}` } }, -); -console.log(await response.json()); -export {}; diff --git a/vendor/docs/examples/docs/api/inspector-database-execute.ts b/vendor/docs/examples/docs/api/inspector-database-execute.ts deleted file mode 100644 index 0786a92e..00000000 --- a/vendor/docs/examples/docs/api/inspector-database-execute.ts +++ /dev/null @@ -1,17 +0,0 @@ -const endpoint = "https://rivet.example.com"; -const response = await fetch( - `${endpoint}/gateway/${process.env.ACTOR_ID}/inspector/database/execute`, - { - method: "POST", - headers: { - Authorization: `Bearer ${process.env.INSPECTOR_TOKEN}`, - "Content-Type": "application/json", - }, - body: JSON.stringify({ - sql: "SELECT * FROM users WHERE id = ?", - args: ["user-123"], - }), - }, -); -console.log(await response.json()); -export {}; diff --git a/vendor/docs/examples/docs/api/inspector-database-rows.ts b/vendor/docs/examples/docs/api/inspector-database-rows.ts deleted file mode 100644 index a21e1be3..00000000 --- a/vendor/docs/examples/docs/api/inspector-database-rows.ts +++ /dev/null @@ -1,12 +0,0 @@ -const endpoint = "https://rivet.example.com"; -const query = new URLSearchParams({ - table: "users", - limit: "100", - offset: "0", -}); -const response = await fetch( - `${endpoint}/gateway/${process.env.ACTOR_ID}/inspector/database/rows?${query}`, - { headers: { Authorization: `Bearer ${process.env.INSPECTOR_TOKEN}` } }, -); -console.log(await response.json()); -export {}; diff --git a/vendor/docs/examples/docs/api/inspector-database-schema.ts b/vendor/docs/examples/docs/api/inspector-database-schema.ts deleted file mode 100644 index e6d5acdc..00000000 --- a/vendor/docs/examples/docs/api/inspector-database-schema.ts +++ /dev/null @@ -1,7 +0,0 @@ -const endpoint = "https://rivet.example.com"; -const response = await fetch( - `${endpoint}/gateway/${process.env.ACTOR_ID}/inspector/database/schema`, - { headers: { Authorization: `Bearer ${process.env.INSPECTOR_TOKEN}` } }, -); -console.log(await response.json()); -export {}; diff --git a/vendor/docs/examples/docs/api/inspector-queue-delete.ts b/vendor/docs/examples/docs/api/inspector-queue-delete.ts deleted file mode 100644 index 748c1b4c..00000000 --- a/vendor/docs/examples/docs/api/inspector-queue-delete.ts +++ /dev/null @@ -1,10 +0,0 @@ -const endpoint = "https://rivet.example.com"; -const response = await fetch( - `${endpoint}/gateway/${process.env.ACTOR_ID}/inspector/queue`, - { - method: "DELETE", - headers: { Authorization: `Bearer ${process.env.INSPECTOR_TOKEN}` }, - }, -); -console.log(await response.json()); -export {}; diff --git a/vendor/docs/examples/docs/api/inspector-queue-get.ts b/vendor/docs/examples/docs/api/inspector-queue-get.ts deleted file mode 100644 index a55ec407..00000000 --- a/vendor/docs/examples/docs/api/inspector-queue-get.ts +++ /dev/null @@ -1,7 +0,0 @@ -const endpoint = "https://rivet.example.com"; -const response = await fetch( - `${endpoint}/gateway/${process.env.ACTOR_ID}/inspector/queue?limit=50`, - { headers: { Authorization: `Bearer ${process.env.INSPECTOR_TOKEN}` } }, -); -console.log(await response.json()); -export {}; diff --git a/vendor/docs/examples/docs/api/inspector-queue-post.ts b/vendor/docs/examples/docs/api/inspector-queue-post.ts deleted file mode 100644 index d5c71989..00000000 --- a/vendor/docs/examples/docs/api/inspector-queue-post.ts +++ /dev/null @@ -1,14 +0,0 @@ -const endpoint = "https://rivet.example.com"; -const response = await fetch( - `${endpoint}/gateway/${process.env.ACTOR_ID}/inspector/queue`, - { - method: "POST", - headers: { - Authorization: `Bearer ${process.env.INSPECTOR_TOKEN}`, - "Content-Type": "application/json", - }, - body: JSON.stringify({ name: "increment", body: { amount: 1 } }), - }, -); -console.log(await response.json()); -export {}; diff --git a/vendor/docs/examples/docs/api/inspector-rpcs.ts b/vendor/docs/examples/docs/api/inspector-rpcs.ts deleted file mode 100644 index 356e615e..00000000 --- a/vendor/docs/examples/docs/api/inspector-rpcs.ts +++ /dev/null @@ -1,7 +0,0 @@ -const endpoint = "https://rivet.example.com"; -const response = await fetch( - `${endpoint}/gateway/${process.env.ACTOR_ID}/inspector/rpcs`, - { headers: { Authorization: `Bearer ${process.env.INSPECTOR_TOKEN}` } }, -); -console.log(await response.json()); -export {}; diff --git a/vendor/docs/examples/docs/api/inspector-state-get.ts b/vendor/docs/examples/docs/api/inspector-state-get.ts deleted file mode 100644 index 783deb57..00000000 --- a/vendor/docs/examples/docs/api/inspector-state-get.ts +++ /dev/null @@ -1,7 +0,0 @@ -const endpoint = "https://rivet.example.com"; -const response = await fetch( - `${endpoint}/gateway/${process.env.ACTOR_ID}/inspector/state`, - { headers: { Authorization: `Bearer ${process.env.INSPECTOR_TOKEN}` } }, -); -console.log(await response.json()); -export {}; diff --git a/vendor/docs/examples/docs/api/inspector-state-patch.ts b/vendor/docs/examples/docs/api/inspector-state-patch.ts deleted file mode 100644 index 36ef34dc..00000000 --- a/vendor/docs/examples/docs/api/inspector-state-patch.ts +++ /dev/null @@ -1,14 +0,0 @@ -const endpoint = "https://rivet.example.com"; -const response = await fetch( - `${endpoint}/gateway/${process.env.ACTOR_ID}/inspector/state`, - { - method: "PATCH", - headers: { - Authorization: `Bearer ${process.env.INSPECTOR_TOKEN}`, - "Content-Type": "application/json", - }, - body: JSON.stringify({ state: { count: 2 } }), - }, -); -console.log(await response.json()); -export {}; diff --git a/vendor/docs/examples/docs/api/inspector-summary.ts b/vendor/docs/examples/docs/api/inspector-summary.ts deleted file mode 100644 index 37f0f793..00000000 --- a/vendor/docs/examples/docs/api/inspector-summary.ts +++ /dev/null @@ -1,7 +0,0 @@ -const endpoint = "https://rivet.example.com"; -const response = await fetch( - `${endpoint}/gateway/${process.env.ACTOR_ID}/inspector/summary`, - { headers: { Authorization: `Bearer ${process.env.INSPECTOR_TOKEN}` } }, -); -console.log(await response.json()); -export {}; diff --git a/vendor/docs/examples/docs/api/inspector-traces.ts b/vendor/docs/examples/docs/api/inspector-traces.ts deleted file mode 100644 index 9a3c1a62..00000000 --- a/vendor/docs/examples/docs/api/inspector-traces.ts +++ /dev/null @@ -1,7 +0,0 @@ -const endpoint = "https://rivet.example.com"; -const response = await fetch( - `${endpoint}/gateway/${process.env.ACTOR_ID}/inspector/traces?startMs=0&limit=100`, - { headers: { Authorization: `Bearer ${process.env.INSPECTOR_TOKEN}` } }, -); -console.log(await response.json()); -export {}; diff --git a/vendor/docs/examples/docs/api/inspector-workflow-history.ts b/vendor/docs/examples/docs/api/inspector-workflow-history.ts deleted file mode 100644 index 64d289d0..00000000 --- a/vendor/docs/examples/docs/api/inspector-workflow-history.ts +++ /dev/null @@ -1,7 +0,0 @@ -const endpoint = "https://rivet.example.com"; -const response = await fetch( - `${endpoint}/gateway/${process.env.ACTOR_ID}/inspector/workflow-history`, - { headers: { Authorization: `Bearer ${process.env.INSPECTOR_TOKEN}` } }, -); -console.log(await response.json()); -export {}; diff --git a/vendor/docs/examples/docs/api/inspector-workflow-replay.ts b/vendor/docs/examples/docs/api/inspector-workflow-replay.ts deleted file mode 100644 index 3bf3cdc8..00000000 --- a/vendor/docs/examples/docs/api/inspector-workflow-replay.ts +++ /dev/null @@ -1,14 +0,0 @@ -const endpoint = "https://rivet.example.com"; -const response = await fetch( - `${endpoint}/gateway/${process.env.ACTOR_ID}/inspector/workflow/replay`, - { - method: "POST", - headers: { - Authorization: `Bearer ${process.env.INSPECTOR_TOKEN}`, - "Content-Type": "application/json", - }, - body: JSON.stringify({ entryId: "entry-123" }), - }, -); -console.log(await response.json()); -export {}; diff --git a/vendor/docs/examples/docs/api/namespaces-create.ts b/vendor/docs/examples/docs/api/namespaces-create.ts index c2f92ff5..3bda46bc 100644 --- a/vendor/docs/examples/docs/api/namespaces-create.ts +++ b/vendor/docs/examples/docs/api/namespaces-create.ts @@ -1,4 +1,4 @@ -const endpoint = "https://rivet.example.com"; +const endpoint = "https://api.rivet.dev"; const token = process.env.RIVET_TOKEN ?? ""; const response = await fetch(`${endpoint}/namespaces`, { diff --git a/vendor/docs/examples/docs/api/namespaces-list.ts b/vendor/docs/examples/docs/api/namespaces-list.ts index 5b598685..34700884 100644 --- a/vendor/docs/examples/docs/api/namespaces-list.ts +++ b/vendor/docs/examples/docs/api/namespaces-list.ts @@ -1,4 +1,4 @@ -const endpoint = "https://rivet.example.com"; +const endpoint = "https://api.rivet.dev"; const token = process.env.RIVET_TOKEN ?? ""; const response = await fetch(`${endpoint}/namespaces`, { diff --git a/vendor/docs/examples/docs/api/pools-delete.ts b/vendor/docs/examples/docs/api/pools-delete.ts new file mode 100644 index 00000000..ad9c5134 --- /dev/null +++ b/vendor/docs/examples/docs/api/pools-delete.ts @@ -0,0 +1,16 @@ +const endpoint = "https://api.rivet.dev"; +const namespace = process.env.RIVET_NAMESPACE ?? "default"; +const token = process.env.RIVET_TOKEN ?? ""; +const pool = process.env.RIVET_POOL ?? "default"; + +const params = new URLSearchParams({ namespace }); +const response = await fetch( + `${endpoint}/pools/${encodeURIComponent(pool)}?${params}`, + { + method: "DELETE", + headers: { Authorization: `Bearer ${token}` }, + }, +); +console.log(response.status); + +export {}; diff --git a/vendor/docs/examples/docs/api/pools-get.ts b/vendor/docs/examples/docs/api/pools-get.ts new file mode 100644 index 00000000..852a5f71 --- /dev/null +++ b/vendor/docs/examples/docs/api/pools-get.ts @@ -0,0 +1,13 @@ +const endpoint = "https://api.rivet.dev"; +const namespace = process.env.RIVET_NAMESPACE ?? "default"; +const token = process.env.RIVET_TOKEN ?? ""; +const pool = process.env.RIVET_POOL ?? "default"; + +const params = new URLSearchParams({ namespace }); +const response = await fetch( + `${endpoint}/pools/${encodeURIComponent(pool)}?${params}`, + { headers: { Authorization: `Bearer ${token}` } }, +); +console.log(await response.json()); + +export {}; diff --git a/vendor/docs/examples/docs/api/pools-list-nodes.ts b/vendor/docs/examples/docs/api/pools-list-nodes.ts new file mode 100644 index 00000000..b55c107f --- /dev/null +++ b/vendor/docs/examples/docs/api/pools-list-nodes.ts @@ -0,0 +1,14 @@ +const endpoint = "https://api.rivet.dev"; +const namespace = process.env.RIVET_NAMESPACE ?? "default"; +const token = process.env.RIVET_TOKEN ?? ""; +const pool = process.env.RIVET_POOL ?? "default"; + +const params = new URLSearchParams({ namespace }); +const response = await fetch( + `${endpoint}/pools/${encodeURIComponent(pool)}/nodes?${params}`, + { headers: { Authorization: `Bearer ${token}` } }, +); +const { nodes, pagination } = await response.json(); +console.log(nodes, pagination.cursor); + +export {}; diff --git a/vendor/docs/examples/docs/api/workers-configs-list.ts b/vendor/docs/examples/docs/api/pools-list.ts similarity index 52% rename from vendor/docs/examples/docs/api/workers-configs-list.ts rename to vendor/docs/examples/docs/api/pools-list.ts index 1f1c8521..eac08588 100644 --- a/vendor/docs/examples/docs/api/workers-configs-list.ts +++ b/vendor/docs/examples/docs/api/pools-list.ts @@ -1,12 +1,12 @@ -const endpoint = "https://rivet.example.com"; +const endpoint = "https://api.rivet.dev"; const namespace = process.env.RIVET_NAMESPACE ?? "default"; const token = process.env.RIVET_TOKEN ?? ""; const params = new URLSearchParams({ namespace }); -const response = await fetch(`${endpoint}/runner-configs?${params}`, { +const response = await fetch(`${endpoint}/pools?${params}`, { headers: { Authorization: `Bearer ${token}` }, }); -const { runner_configs } = await response.json(); -console.log(runner_configs); +const { pools, pagination } = await response.json(); +console.log(pools, pagination.cursor); export {}; diff --git a/vendor/docs/examples/docs/api/workers-configs-upsert.ts b/vendor/docs/examples/docs/api/pools-upsert.ts similarity index 59% rename from vendor/docs/examples/docs/api/workers-configs-upsert.ts rename to vendor/docs/examples/docs/api/pools-upsert.ts index 50a37d3d..0097c1c7 100644 --- a/vendor/docs/examples/docs/api/workers-configs-upsert.ts +++ b/vendor/docs/examples/docs/api/pools-upsert.ts @@ -1,18 +1,18 @@ -const endpoint = "https://rivet.example.com"; +const endpoint = "https://api.rivet.dev"; const namespace = process.env.RIVET_NAMESPACE ?? "default"; const token = process.env.RIVET_TOKEN ?? ""; -const workerName = process.env.RUNNER_NAME ?? "default"; +const pool = process.env.RIVET_POOL ?? "default"; const params = new URLSearchParams({ namespace }); const response = await fetch( - `${endpoint}/runner-configs/${encodeURIComponent(workerName)}?${params}`, + `${endpoint}/pools/${encodeURIComponent(pool)}?${params}`, { method: "PUT", headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json", }, - body: JSON.stringify({ datacenters: { local: { normal: {} } } }), + body: JSON.stringify({ drain_on_version_upgrade: true }), }, ); console.log(await response.json()); diff --git a/vendor/docs/examples/docs/api/tokens-inspect.ts b/vendor/docs/examples/docs/api/tokens-inspect.ts index c562d121..7573b89f 100644 --- a/vendor/docs/examples/docs/api/tokens-inspect.ts +++ b/vendor/docs/examples/docs/api/tokens-inspect.ts @@ -1,4 +1,4 @@ -const endpoint = "https://rivet.example.com"; +const endpoint = "https://api.rivet.dev"; const token = process.env.RIVET_TOKEN ?? ""; // Inspect reports on the token used to authenticate the request itself. diff --git a/vendor/docs/examples/docs/api/workers-configs-delete.ts b/vendor/docs/examples/docs/api/workers-configs-delete.ts deleted file mode 100644 index cdc01d9e..00000000 --- a/vendor/docs/examples/docs/api/workers-configs-delete.ts +++ /dev/null @@ -1,13 +0,0 @@ -const endpoint = "https://rivet.example.com"; -const namespace = process.env.RIVET_NAMESPACE ?? "default"; -const token = process.env.RIVET_TOKEN ?? ""; -const workerName = process.env.RUNNER_NAME ?? "default"; - -const params = new URLSearchParams({ namespace }); -const response = await fetch( - `${endpoint}/runner-configs/${encodeURIComponent(workerName)}?${params}`, - { method: "DELETE", headers: { Authorization: `Bearer ${token}` } }, -); -console.log(response.ok); - -export {}; diff --git a/vendor/docs/examples/docs/api/workers-configs-refresh-metadata.ts b/vendor/docs/examples/docs/api/workers-configs-refresh-metadata.ts index 337d7c97..1f1a7c02 100644 --- a/vendor/docs/examples/docs/api/workers-configs-refresh-metadata.ts +++ b/vendor/docs/examples/docs/api/workers-configs-refresh-metadata.ts @@ -1,4 +1,4 @@ -const endpoint = "https://rivet.example.com"; +const endpoint = "https://api.rivet.dev"; const namespace = process.env.RIVET_NAMESPACE ?? "default"; const token = process.env.RIVET_TOKEN ?? ""; const workerName = process.env.RUNNER_NAME ?? "default"; diff --git a/vendor/docs/examples/docs/api/workers-list.ts b/vendor/docs/examples/docs/api/workers-list.ts deleted file mode 100644 index 06f1d56e..00000000 --- a/vendor/docs/examples/docs/api/workers-list.ts +++ /dev/null @@ -1,12 +0,0 @@ -const endpoint = "https://rivet.example.com"; -const namespace = process.env.RIVET_NAMESPACE ?? "default"; -const token = process.env.RIVET_TOKEN ?? ""; - -const params = new URLSearchParams({ namespace }); -const response = await fetch(`${endpoint}/envoys?${params}`, { - headers: { Authorization: `Bearer ${token}` }, -}); -const { envoys: workers, pagination } = await response.json(); -console.log(workers, pagination.cursor); - -export {}; diff --git a/vendor/docs/examples/docs/api/workers-serverless-health-check.ts b/vendor/docs/examples/docs/api/workers-serverless-health-check.ts index 860536f6..c6e0c54f 100644 --- a/vendor/docs/examples/docs/api/workers-serverless-health-check.ts +++ b/vendor/docs/examples/docs/api/workers-serverless-health-check.ts @@ -1,4 +1,4 @@ -const endpoint = "https://rivet.example.com"; +const endpoint = "https://api.rivet.dev"; const namespace = process.env.RIVET_NAMESPACE ?? "default"; const token = process.env.RIVET_TOKEN ?? ""; diff --git a/vendor/docs/examples/docs/cookbook-cron-jobs/daily-report.ts b/vendor/docs/examples/docs/cookbook-cron-jobs/daily-report.ts deleted file mode 100644 index c434d36c..00000000 --- a/vendor/docs/examples/docs/cookbook-cron-jobs/daily-report.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { actor } from "rivetkit"; - -export const dailyReport = actor({ - state: { lastRunAt: 0 }, - onCreate: async (c) => { - await c.cron.set({ - name: "daily-report", - expression: "0 9 * * *", - action: "runReport", - }); - }, - actions: { - runReport: (c) => { - // Do the job's work, then record the run. - c.state.lastRunAt = Date.now(); - }, - }, -}); diff --git a/vendor/docs/examples/docs/durable-streams/client.ts b/vendor/docs/examples/docs/durable-streams/client.ts new file mode 100644 index 00000000..97fdedf0 --- /dev/null +++ b/vendor/docs/examples/docs/durable-streams/client.ts @@ -0,0 +1,14 @@ +import { DurableStream } from "@durable-streams/client"; + +const stream = await DurableStream.create({ + url: "http://127.0.0.1:8642/durable-streams/v1/stream/demo", + contentType: "application/json", +}); + +await stream.append(JSON.stringify({ message: "hello" })); + +// Read from the beginning, then keep receiving new messages live +const res = await stream.stream<{ message: string }>(); +res.subscribeJson(async (batch) => { + for (const item of batch.items) console.log(item.message); +}); diff --git a/vendor/docs/examples/docs/durable-streams/create.ts b/vendor/docs/examples/docs/durable-streams/create.ts new file mode 100644 index 00000000..582ec8f2 --- /dev/null +++ b/vendor/docs/examples/docs/durable-streams/create.ts @@ -0,0 +1,11 @@ +import { DurableStream } from "@durable-streams/client"; + +// One stream per chat session +const stream = await DurableStream.create({ + url: "http://127.0.0.1:8642/durable-streams/v1/stream/chat/session-1", + contentType: "application/json", + // Delete the stream after a day with no reads or writes + ttlSeconds: 86400, +}); + +console.log("created", stream.url); diff --git a/vendor/docs/examples/docs/durable-streams/producer.ts b/vendor/docs/examples/docs/durable-streams/producer.ts new file mode 100644 index 00000000..eb2f8a16 --- /dev/null +++ b/vendor/docs/examples/docs/durable-streams/producer.ts @@ -0,0 +1,21 @@ +import { DurableStream, IdempotentProducer } from "@durable-streams/client"; + +const stream = new DurableStream({ + url: "http://127.0.0.1:8642/durable-streams/v1/stream/chat/session-1", + contentType: "application/json", +}); + +// Keep the producer ID the same across restarts. With autoClaim, a restarted +// agent takes over and the old one can no longer write. +const producer = new IdempotentProducer(stream, "agent-session-1", { + autoClaim: true, + onError: (error) => console.error("append failed", error), +}); + +for (const token of ["It's", " sunny", " today"]) { + producer.append(JSON.stringify({ token })); +} + +// Wait until every token is stored, then close the stream +await producer.flush(); +await producer.close(); diff --git a/vendor/docs/examples/docs/durable-streams/read-history.ts b/vendor/docs/examples/docs/durable-streams/read-history.ts new file mode 100644 index 00000000..457570d7 --- /dev/null +++ b/vendor/docs/examples/docs/durable-streams/read-history.ts @@ -0,0 +1,10 @@ +import { stream } from "@durable-streams/client"; + +const url = "http://127.0.0.1:8642/durable-streams/v1/stream/chat/session-1"; + +// Everything written so far, then stop +const res = await stream<{ token: string }>({ url, offset: "-1", live: false }); +const messages = await res.json(); + +console.log(messages); +console.log("read from here next:", res.offset); diff --git a/vendor/docs/examples/docs/durable-streams/read-live.ts b/vendor/docs/examples/docs/durable-streams/read-live.ts new file mode 100644 index 00000000..362854f3 --- /dev/null +++ b/vendor/docs/examples/docs/durable-streams/read-live.ts @@ -0,0 +1,14 @@ +import { stream } from "@durable-streams/client"; + +const url = "http://127.0.0.1:8642/durable-streams/v1/stream/chat/session-1"; + +// "-1" starts at the beginning. Pass a saved offset to pick up where you left off. +const savedOffset: string = "-1"; + +const res = await stream<{ token: string }>({ url, offset: savedOffset, live: "sse" }); +res.subscribeJson(async (batch) => { + for (const item of batch.items) console.log(item.token); + + // Save this to resume from the same place after a refresh + console.log("offset", batch.offset); +}); diff --git a/vendor/docs/examples/docs/durable-streams/write.ts b/vendor/docs/examples/docs/durable-streams/write.ts new file mode 100644 index 00000000..c68ab7ce --- /dev/null +++ b/vendor/docs/examples/docs/durable-streams/write.ts @@ -0,0 +1,13 @@ +import { DurableStream } from "@durable-streams/client"; + +const stream = new DurableStream({ + url: "http://127.0.0.1:8642/durable-streams/v1/stream/chat/session-1", + contentType: "application/json", +}); + +// Each append is one message +await stream.append(JSON.stringify({ token: "It's" })); +await stream.append(JSON.stringify({ token: " sunny" })); + +// Close with a final message: readers know the reply is complete +await stream.close({ body: JSON.stringify({ token: "." }) }); diff --git a/vendor/docs/examples/docs/general-edge/client.ts b/vendor/docs/examples/docs/general-edge/client.ts new file mode 100644 index 00000000..fde898f5 --- /dev/null +++ b/vendor/docs/examples/docs/general-edge/client.ts @@ -0,0 +1,12 @@ +import { createClient } from "rivetkit/client"; +import { actor, setup } from "rivetkit"; + +const example = actor({ state: {}, actions: {} }); +const registry = setup({ use: { example } }); + +const client = createClient("http://localhost:6420"); + +// Create actor in a specific region using getOrCreate +const actorHandle = client.example.getOrCreate(["my-actor"], { + createInRegion: "atl" +}); diff --git a/vendor/docs/examples/docs/general-jwt/quickstart/client.ts b/vendor/docs/examples/docs/general-jwt/quickstart/client.ts index bf78e5d5..49cbb1ed 100644 --- a/vendor/docs/examples/docs/general-jwt/quickstart/client.ts +++ b/vendor/docs/examples/docs/general-jwt/quickstart/client.ts @@ -14,7 +14,7 @@ async function fetchToken(): Promise<{ actorId: string; token: string }> { const { actorId } = await fetchToken(); const client = createClient({ - endpoint: "https://my-namespace@rivet.example.com", + endpoint: "https://rivet.example.com", namespace: "production", // Called whenever RivetKit needs a credential, including after one expires. diff --git a/vendor/docs/examples/docs/onboarding-actors/client.ts b/vendor/docs/examples/docs/onboarding-actors/client.ts new file mode 100644 index 00000000..307c6365 --- /dev/null +++ b/vendor/docs/examples/docs/onboarding-actors/client.ts @@ -0,0 +1,20 @@ +import { createClient } from "rivetkit/client"; +import type { registry } from "./index"; + +// Connect to the server from index.ts. The registry type checks every call. +const client = createClient("http://localhost:6420"); + +// Get the room keyed "general", creating it on first use. +const room = client.chatRoom.getOrCreate(["general"]); + +// Open a realtime connection and print each message as it arrives. +const connection = room.connect(); +connection.on("newMessage", (message: { sender: string; text: string }) => { + console.log(`${message.sender}: ${message.text}`); +}); + +// Send a message. The Actor saves it to SQLite before broadcasting it. +await connection.sendMessage("alice", "Hello, Rivet!"); + +// The history is still there after the Actor sleeps or restarts. +console.log(await room.getHistory()); diff --git a/vendor/docs/examples/docs/onboarding-actors/index.ts b/vendor/docs/examples/docs/onboarding-actors/index.ts new file mode 100644 index 00000000..0cf38744 --- /dev/null +++ b/vendor/docs/examples/docs/onboarding-actors/index.ts @@ -0,0 +1,43 @@ +import { actor, setup } from "rivetkit"; +import { db } from "rivetkit/db"; + +// A chat room Actor. Every room key gets its own instance with its own SQLite +// database, which survives restarts and redeploys. +export const chatRoom = actor({ + // Create the messages table the first time a room starts. + db: db({ + onMigrate: async (db) => { + await db.execute(` + CREATE TABLE IF NOT EXISTS messages ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + sender TEXT NOT NULL, + text TEXT NOT NULL + ) + `); + }, + }), + + actions: { + // Save a message, then push it to everyone connected to this room. + sendMessage: async (c, sender: string, text: string) => { + await c.db.execute( + "INSERT INTO messages (sender, text) VALUES (?, ?)", + sender, + text, + ); + + c.broadcast("newMessage", { sender, text }); + }, + + // Read the room's history, oldest first. + getHistory: async (c) => { + return (await c.db.execute( + "SELECT sender, text FROM messages ORDER BY id", + )) as { sender: string; text: string }[]; + }, + }, +}); + +// Register the Actor and start serving it on http://localhost:6420. +export const registry = setup({ use: { chatRoom } }); +registry.start(); diff --git a/vendor/docs/examples/docs/package.json b/vendor/docs/examples/docs/package.json index a0e98db3..3ddcea78 100644 --- a/vendor/docs/examples/docs/package.json +++ b/vendor/docs/examples/docs/package.json @@ -1,4 +1,5 @@ { + "license": "MIT", "name": "docs-snippets", "version": "2.0.21", "private": true, @@ -11,10 +12,7 @@ "rivetkit": "workspace:*", "@rivetkit/react": "workspace:*", "@rivetkit/engine-api-full": "workspace:*", - "@rivetkit/next-js": "workspace:*", - "@rivetkit/cloudflare-workers": "workspace:*", "@rivetkit/rivetkit-wasm": "workspace:*", - "@rivetkit/supabase": "workspace:*", "@rivetkit/effect": "workspace:*", "effect": "4.0.0", "@effect/platform-node": "4.0.0", diff --git a/vendor/docs/examples/docs/tsconfig.json b/vendor/docs/examples/docs/tsconfig.json index 498e3a9a..ee88fd1d 100644 --- a/vendor/docs/examples/docs/tsconfig.json +++ b/vendor/docs/examples/docs/tsconfig.json @@ -13,5 +13,12 @@ "jsx": "react-jsx" }, "include": ["**/*.ts", "**/*.tsx"], - "exclude": ["node_modules"] + "exclude": [ + "node_modules", + "actors-quickstart-next-js", + "general-http-server/elysia-handler.ts", + "general-http-server/hono-handler.ts", + "general-http-server/serve.ts", + "actors-actions/calling-backend.ts" + ] }