Docker
The repository’sDockerfile builds one image with the server (default command), the gateway and the CLI. It is a two-stage
build on node:22-slim (npm ci, tsc -b, then dev dependencies are pruned).
- Runs as the unprivileged
nodeuser, keeps data in the/datavolume (REVERSI_DATA=/data), and has a health check on/readyz. - It sets
HOST=0.0.0.0andPORT=7421. Because of that,REVERSI_CONSOLE=openis refused (open mode is loopback only); useREVERSI_CONSOLE=login. - A gateway that listens beyond loopback needs
authin its config, or it refuses to start.
Docker Compose
deploy/docker-compose.yml runs the server on PostgreSQL 16 with a Postgres health check, JSON logs, and the login console
at http://127.0.0.1:7421/console (paste an API key). Follow the comments at its top:
deploy/keys.json, examples/gateway/manifests and examples/gateway/policy.yaml read-only, publishes only on
127.0.0.1, and requires POSTGRES_PASSWORD (it fails without it). Replace the example manifests and policy with your own.
Kubernetes (Helm)
The chart is atdeploy/helm/reversi. It deploys the server, and optionally the gateway as a second container in the same pod.
image.repository and image.tag. Nothing is created for secrets: you create the
Secrets and ConfigMaps and the chart refers to them by name, so no credential ever sits in values.yaml.
my-values.yaml
Values
Fail-fast checks
templates/validate.yaml stops the render with a message for:
server.console=open: “has no login and is refused on a cluster”.replicaCountabove 1 with a store other thanpostgres.registry.persistence.enabledwithreplicaCountabove 1: “the volume is ReadWriteOnce”.server.console=loginwith neitherkeys.existingSecretnoroidc.enabled.
store.databaseUrlSecret.name when the store is postgres (the default), and
gateway.existingConfigMap when the gateway is enabled.
What the chart does and does not do
- Pod security: non-root user 1000, read-only root filesystem (writable
/dataand/tmp), all capabilities dropped,RuntimeDefaultseccomp, no service account token mounted. Readiness probe/readyz, liveness/healthz. - The server refuses to start without a credential source. The chart does not check that you set
keys.existingSecretoroidc.enabledunless the console islogin. - The gateway container shares the pod’s environment (store, manifests, policy, keys) and
/data. Yourgateway.yamlmust listen on0.0.0.0and the port set ingateway.port, and useauth.keys: /config/keys/keys.json(or OIDC), since a gateway beyond loopback needs authentication. The Service exposes themcpport inside the cluster; the Ingress routes only to the server. - Read-only by default. With the ConfigMap sources, manifests and policy are read-only mounts, so the console’s editors and
reversi apply --serverhave nothing writable to work on. Update the ConfigMaps (helm upgrade) and let hot reload pick them up. I did not verify how quickly Kubernetes propagates a ConfigMap change into the pod or that the file watch sees it, so runkill -HUPor restart if it does not. - Writable registry (
registry.persistence.enabled: true). The console editors andreversi apply --serverthen work, because the server’s manifests and policy are folders on the volume. Setserver.console: loginand use an admin key forapply. An init container creates/state/manifestsand/state/policy, and the server and gateway treat an empty folder as an editable registry, so the first publish (for examplereversi apply <dir> --server <url> --token <admin key>) creates it. The/statemount is in both the server and the gateway container. Checked locally with an empty folder andapply --server; the init container and mounts were read from the templates, not run on a cluster. - There is no Terraform module and no operator. See GitOps.
Server environment
Everything can also be set inreversi.config.yaml; the variable wins.
At least one credential source (keys or OIDC) is required: the server will not start unauthenticated.
node packages/server/dist/main.js --help prints the list.
TLS and networking
Both programs speak plain HTTP. Terminate TLS in a reverse proxy (nginx, Caddy, a cloud load balancer) and setREVERSI_SECURE_COOKIES=1. Keep the server and gateway on a private network; the agent only needs to reach the gateway or the
agent endpoints. Set allowed_hosts on the gateway to your public hostname.
High availability
One engine process per run is the supported model. With PostgreSQL you can run several server or gateway instances, each owning its own sessions and runs, behind a load balancer with session affinity for MCP sessions (theMcp-Session-Id header). Ledger
appends from all of them stay on one chain. Active/active drivers of the same run are not supported, and there is no
clustering with run hand-off.
Upgrades
Deploy the new version: PostgreSQL migrations run at start. Back up first (storage). Manifests, the policy and the manifest public key reload while running (file watch,SIGHUP, POST /v1/reload); other settings and
environment variables need a restart. Key files reload live.

