Skip to main content
Reversi’s configuration is files. Keep them in git, review changes in pull requests, test them in CI, and apply the merged result with one command. This page covers the project file, the versioned folders, reversi apply, hot reload and a CI example.
There is no Terraform provider and no Kubernetes operator. Neither is built. reversi apply (below) plus the Helm chart is the alternative: a directory in git is the desired state, apply publishes only the differences, and --dry-run --detailed-exitcode is the drift check.

The project file: reversi.config.yaml

The CLI, the server and the gateway all read it. It is found in the current folder or any parent (or REVERSI_CONFIG, or --config). Every key is optional. reversi init writes a starter, and reversi show-config prints what was found.
reversi.config.yaml
The rest of the keys are described on their own pages: notifications (channels and routes, notifications), otel (endpoint, headers, service_name, monitoring), anchor (sinks of {type: file, path} or {type: webhook, url, secret}, plus interval, evidence), egress (allow, block_private, allow_metadata, region) and alerts (interval, remind, denial_spike).
  • Relative paths resolve against the folder holding the file. http(s) URLs and absolute paths are kept.
  • ${NAME} is replaced from the environment, and ${NAME:-default} falls back. An unset variable without a default is an error, so secrets stay out of git.
  • Durations: 30s, 15m, 2h, 7d.
  • An unknown key is an error naming it. All problems are listed together.
Precedence: command-line flags, then environment variables, then the file.

Registry and policy versions

A plain folder of manifest YAML is fine for a start. For review, history and editing, publish into a registry folder: every change is a new numbered version with author, note, checksum, status (draft, approved, deprecated) and optionally an Ed25519 signature. A policy versions folder does the same for the policy, with one active version.
reversi.config.yaml
  • A folder counts as a registry once it holds index.json, and as policy versions once it holds policy-index.json. Both appear on the first publish. The server and gateway treat an empty folder as an editable registry or policy folder, so the first publish turns it into one.
  • The server, gateway and console only offer editing when those folders are in use. With plain files they are read-only.
  • Draft manifests are not enforceable, and a registry that holds only a draft of a tool simply has no manifest for it, so a call reports “no manifest” rather than “is a draft”. Approving one writes a new, signed version; with --four-eyes the author cannot approve their own.
  • reversi manifests verify --trust sign.pub recomputes every checksum and checks every signature.
  • Deprecating keeps a tool working and rolling back but flags new calls.
  • reversi validate, lint, test, doctor and plan read a registry folder as well as a folder of files (for a registry, the current non-draft version of each tool).

reversi apply

apply makes a registry (and policy folder) match a directory.
If state/manifests/ does not exist, the manifest files are read from state/ itself. YAML and JSON both work. Every manifest is validated first and a bad one aborts before anything changes; the same tool twice is an error.
  • Idempotent. A second run prints up to date and changes nothing.
  • Nothing is deleted. --prune deprecates; the old versions stay in the registry.
  • Status. New versions are approved by default, on the basis that the directory is the reviewed source of truth. --status draft publishes drafts for a person to approve in the console or with reversi manifests approve.
  • Signing. --key signs each new manifest version. Policy versions are not signed.
  • Not atomic. Manifests are applied before the policy. If the policy step fails (for example the policy folder does not exist), the manifests are already applied; fix the cause and run again.
  • --detailed-exitcode exits 2 when there are changes, and only together with --dry-run. Without it a dry run exits 0.

Local or server

A server whose manifests: points to plain files refuses apply --server with no manifest registry folder is configured: start the server on an empty folder (the first apply --server turns it into a registry), or bootstrap with reversi manifests publish or a local reversi apply. Both modes were exercised by hand and by the apply tests; the server routes are in the admin API.

Hot reload

The server and the gateway re-read manifests, the policy and the manifest public key while running. A configuration that fails to load is rejected and the previous one keeps serving. A successful change writes a reloaded record to the ledger.
  • File watch: on by default, polling every 2 s. Turn off or change with reload: { watch: false } or reload: { interval: 10s }.
  • SIGHUP: kill -HUP <pid>.
  • On demand: POST /v1/reload (admin) on the server, reversi.reload (admin) on the gateway’s operator endpoint.
  • Publishing from the console, apply --server and the registry endpoints reload as part of the request. If the new configuration does not load, the request answers 409 saved, but the new configuration did not load: ....
API key files reload separately (SIGHUP or a change to the file). The gateway’s own config file and reversi.config.yaml keys other than manifests and policy are read at start.

CI example

Lint and test on every pull request; apply on merge; check for drift on a schedule. The commands are the ones run locally for this page; the workflow file itself has not been run on GitHub.
.github/workflows/reversi.yml
  • lint --strict and test exit 1 on warnings or failures, which fails the job.
  • The pull-request step prints the pending changes and passes on exit 2; the scheduled step fails on exit 2, which is drift (someone changed the registry by hand).
  • Use a dedicated admin key for the apply job and keep it in the CI secret store. Keys are managed in the console or authentication.
  • The server must be reachable from the runner. Reaching a private server from GitHub-hosted runners needs a self-hosted runner or a tunnel, which is outside Reversi.

What is not built

  • A Terraform provider and a Kubernetes operator (custom resources reconciled in-cluster). Use reversi apply from CI.
  • Applying is a push. There is no agent in the cluster that pulls from git.