# Coldrune Documentation --- ## Guides ### Access Control Coldrune uses deny-by-default access control. Without an explicit rule, users with `developer` or `member` roles and all service accounts are denied access. ## Who needs ACL rules? | Subject | ACL required? | |---------|--------------| | Org owner | No — full access | | Org admin | No — full access | | Org developer | Yes | | Org member | Yes | | Service account | Always | | Superadmin | No — bypasses everything | ## Grant access ```bash coldrune acl grant \ --org my-org \ --email alice@example.com \ --project "api-*" \ --env "*" \ --permission write ``` This gives Alice write access to all environments in any project starting with `api-`. For service accounts, use `--subject` with the service account name: ```bash coldrune acl grant \ --org my-org \ --subject ci-deploy \ --project my-app \ --env prod \ --permission write ``` ## Pattern syntax Project and environment patterns support glob-like matching: | Pattern | Matches | |---------|---------| | `*` | Everything | | `api-*` | `api-backend`, `api-gateway`, `api-v2` | | `*-prod` | `us-prod`, `eu-prod` | | `api-?` | `api-1`, `api-x` (single character) | | `{dev,staging}` | `dev` or `staging` | | `api-{v1,v2}` | `api-v1` or `api-v2` | Patterns are matched against the actual project or environment name when a secret is accessed. ## Permission hierarchy | Level | Allows | |-------|--------| | `read` | Get secret values, list keys | | `write` | Everything in `read` + set and delete secrets | | `admin` | Everything in `write` + manage ACL rules | Each level includes all permissions below it. Granting `write` implicitly grants `read`. ACL permissions don't override org role restrictions. A `member` role user cannot write secrets even with a `write` ACL rule — they'd need at least the `developer` org role. ## List rules ```bash coldrune acl list --org my-org ``` ## Revoke access ```bash # By rule ID coldrune acl revoke --org my-org --id # All rules for a subject coldrune acl revoke --org my-org --subject alice@example.com ``` ## Upsert behavior Granting the same subject + project pattern + env pattern combination updates the existing rule's permission level instead of creating a duplicate. ## REST API ```bash # Grant curl -X POST http://localhost:7100/api/orgs/my-org/acl \ -H 'Authorization: Bearer ' \ -H 'Content-Type: application/json' \ -d '{ "subject_type": "user", "email": "alice@example.com", "project_pattern": "api-*", "env_pattern": "*", "permission": "write" }' # List curl http://localhost:7100/api/orgs/my-org/acl \ -H 'Authorization: Bearer ' # Revoke curl -X DELETE http://localhost:7100/api/orgs/my-org/acl/ \ -H 'Authorization: Bearer ' ``` ### Audit Logs Every state-changing operation and sensitive read is logged. Audit logs are immutable and cannot be deleted. ## Query logs ```bash coldrune audit list --org my-org ``` ### Filter by action ```bash coldrune audit list --org my-org --action secret.set ``` ### Filter by actor ```bash coldrune audit list --org my-org --actor ``` ### Filter by resource type ```bash coldrune audit list --org my-org --resource-type secret ``` ### Pagination ```bash coldrune audit list --org my-org --limit 20 --after ``` The cursor value is returned in the previous response when more entries are available. ## Logged actions | Action | Trigger | |--------|---------| | `auth.login` | Login code requested | | `auth.logout` | Session invalidated | | `org.create` / `org.update` / `org.delete` | Organization changes | | `org.member_invite` / `org.member_remove` / `org.member_role_update` | Membership changes | | `project.create` / `project.update` / `project.delete` | Project changes | | `env.create` / `env.delete` | Environment changes | | `secret.set` / `secret.read` / `secret.delete` | Secret operations | | `acl.grant` / `acl.revoke` | Access rule changes | | `service_account.create` / `service_account.revoke` | Service account changes | ## What's captured Each log entry includes: - **Actor**: user email or service account name - **Action**: what happened - **Resource**: type and ID of the affected resource - **Organization**: which org context - **IP address**: client IP (from reverse proxy `X-Real-Ip` header) - **Metadata**: action-specific context (e.g., secret key name, version number) - **Timestamp**: UTC ## Access Audit logs require org admin role or superadmin. ## REST API ```bash curl "http://localhost:7100/api/orgs/my-org/audit-logs?action=secret.set&limit=20" \ -H 'Authorization: Bearer ' ``` Response: ```json { "data": [ { "id": "...", "actor_type": "user", "actor_name": "alice@example.com", "action": "secret.set", "resource_type": "secret", "resource_id": "...", "ip_address": "203.0.113.1", "metadata": {"key": "DB_PASSWORD", "version": 2}, "created_at": "2026-04-01T12:00:00Z" } ], "pagination": { "total": 142, "limit": 20, "has_next": true, "next_cursor": "..." } } ``` ### Authentication Coldrune uses passwordless authentication. No passwords to remember or rotate. ## Magic link login ```bash # Request a 6-digit code via email coldrune auth login --email you@example.com # Verify the code coldrune auth verify --code 123456 ``` The code expires after 10 minutes. An incorrect guess invalidates the code immediately — you'll need to request a new one. Your session token is stored at `~/.config/coldrune/session` with `0600` permissions. ## Check your identity ```bash coldrune auth whoami ``` ## Log out ```bash coldrune auth logout ``` ## Dev mode Set `SMTP_HOST=log` in your `.env` to print login codes to the server terminal instead of sending email. No SMTP credentials required. ## API key authentication For CI/CD and automation, use [service accounts](/guides/service-accounts/) instead of interactive login. Service accounts authenticate with an API key via the `X-API-Key` header. The CLI auto-detects the auth method: | Source | Prefix | Header | |--------|--------|--------| | Session file | (none) | `Authorization: Bearer ` | | `COLDRUNE_API_KEY` env var | `cr_sa_` | `X-API-Key: ` | ## Rate limits | Action | Limit | |--------|-------| | Login requests | 5 per email per 15 minutes | | Failed verifications | 10 per email per hour (hard lockout) | After 10 failed verification attempts in an hour, the email is locked out. Wait for the lockout window to pass. ## REST API ```bash # Request code curl -X POST http://localhost:7100/api/auth/login \ -H 'Content-Type: application/json' \ -d '{"email": "you@example.com"}' # Verify code curl -X POST http://localhost:7100/api/auth/verify \ -H 'Content-Type: application/json' \ -d '{"email": "you@example.com", "code": "123456"}' # Response: {"token": "..."} # Use the token curl http://localhost:7100/api/auth/me \ -H 'Authorization: Bearer ' ``` ### Client Configuration ## coldrune.yml Create a `coldrune.yml` in your project directory to set defaults for CLI commands: ```yaml server_url: http://localhost:7100 org: my-org project: api-backend env: dev ``` With this file, you can omit repeated flags: ```bash # Instead of: coldrune secret get DB_PASSWORD --org my-org --project api-backend --env dev # Just: coldrune secret get DB_PASSWORD ``` ## Resolution order Each parameter is resolved in this order (first match wins): 1. CLI flag (`--org`, `--project`, `--env`) 2. Environment variable (`COLDRUNE_ORG`, `COLDRUNE_PROJECT`, `COLDRUNE_ENV`) 3. `coldrune.yml` in the current directory 4. Error (parameter required) ## Environment variables | Variable | Purpose | |----------|---------| | `COLDRUNE_SERVER_URL` | Server URL (default: `http://localhost:7100`) | | `COLDRUNE_API_KEY` | Service account API key (overrides session token) | | `COLDRUNE_ORG` | Default organization | | `COLDRUNE_PROJECT` | Default project | | `COLDRUNE_ENV` | Default environment | ## Server URL The `--server-url` flag (or `COLDRUNE_SERVER_URL`) sets the server address for all CLI client commands: ```bash coldrune --server-url https://api.coldrune.com secret list ``` Or in `coldrune.yml`: ```yaml server_url: https://api.coldrune.com ``` For server-side configuration (environment variables for `coldrune server start`), see [Self-Hosting / Configuration](/self-hosting/configuration/). ### Organizations Organizations are the top-level tenant boundary. All projects, secrets, and access rules belong to an organization. ## Create an organization ```bash coldrune org create --name my-org ``` You become the owner automatically. ## List organizations ```bash coldrune org list ``` ## Rename ```bash coldrune org update --name my-org --new-name new-name ``` ## Delete ```bash coldrune org delete --name my-org ``` Soft-deletes the org. The name can be reused after deletion. ## Members ### Invite a user ```bash coldrune org members invite --org my-org --email alice@example.com --role developer ``` The default role is `member` if `--role` is omitted. ### List members ```bash coldrune org members list --org my-org ``` ### Change role ```bash coldrune org members update-role --org my-org --email alice@example.com --role admin ``` ### Remove a member ```bash coldrune org members remove --org my-org --email alice@example.com ``` ## Roles Four roles, each inheriting the permissions below it: | Role | Secrets | Projects/Envs | Members | Org settings | |------|---------|---------------|---------|-------------| | **Owner** | read + write | create, update, delete | invite, remove, change roles | rename, delete | | **Admin** | read + write | create, update, delete | invite, remove | — | | **Developer** | read + write | — | — | — | | **Member** | read only | — | — | — | - Multiple owners are allowed - The last owner cannot be removed or demoted - Superadmins bypass all role checks - Developers and members need [ACL rules](/guides/access-control/) for project/env-level access ## Naming rules Organization names must be 2-50 characters, lowercase alphanumeric with hyphens, starting and ending with an alphanumeric character. ## REST API ```bash # Create curl -X POST http://localhost:7100/api/orgs \ -H 'Authorization: Bearer ' \ -H 'Content-Type: application/json' \ -d '{"name": "my-org"}' # List curl http://localhost:7100/api/orgs \ -H 'Authorization: Bearer ' # Invite member curl -X POST http://localhost:7100/api/orgs/my-org/members \ -H 'Authorization: Bearer ' \ -H 'Content-Type: application/json' \ -d '{"email": "alice@example.com", "role": "developer"}' ``` ### Projects & Environments Secrets are organized as `org / project / environment / key`. Projects group related secrets. Environments separate configuration per deployment stage. ## Projects ### Create a project ```bash coldrune project create --org my-org --name api-backend ``` Three default environments are created automatically: `dev`, `staging`, `prod`. ### List projects ```bash coldrune project list --org my-org ``` ### Rename ```bash coldrune project update --org my-org --name api-backend --new-name api-service ``` ### Delete ```bash coldrune project delete --org my-org --name api-backend ``` ## Environments ### Create an environment ```bash coldrune env create --org my-org --project api-backend --name qa ``` ### List environments ```bash coldrune env list --org my-org --project api-backend ``` ### Delete an environment ```bash coldrune env delete --org my-org --project api-backend --name qa ``` ## Naming rules Project and environment names follow the same rules as organization names: 2-50 characters, lowercase alphanumeric with hyphens, starting and ending with an alphanumeric character. ## REST API ```bash # Create project curl -X POST http://localhost:7100/api/orgs/my-org/projects \ -H 'Authorization: Bearer ' \ -H 'Content-Type: application/json' \ -d '{"name": "api-backend"}' # List environments curl http://localhost:7100/api/orgs/my-org/projects/api-backend/envs \ -H 'Authorization: Bearer ' # Create environment curl -X POST http://localhost:7100/api/orgs/my-org/projects/api-backend/envs \ -H 'Authorization: Bearer ' \ -H 'Content-Type: application/json' \ -d '{"name": "qa"}' ``` ### Quickstart 1. **Download the binary** ```bash # Linux (amd64) curl -L -o coldrune https://github.com/user/coldrune/releases/latest/download/coldrune-linux-amd64 chmod +x coldrune sudo mv coldrune /usr/local/bin/ ``` 2. **Generate a master key** ```bash openssl rand -hex 32 ``` Save the output. This 64-character hex string encrypts all your secrets. 3. **Configure the server** Create a `.env` file: ```ini COLDRUNE_MASTER_KEY= SUPERADMIN_EMAIL_0=you@example.com SMTP_HOST=log ``` Setting `SMTP_HOST=log` prints login codes to the terminal instead of sending email. Good for getting started. 4. **Start the server** ```bash coldrune server start ``` The server runs at `http://localhost:7100` by default. 5. **Log in and store a secret** In a new terminal: ```bash # Request a login code coldrune auth login --email you@example.com # Enter the 6-digit code from the server logs coldrune auth verify --code 123456 # Create an org and project coldrune org create --name my-org coldrune project create --org my-org --name my-app # Store a secret coldrune secret set DB_PASSWORD=s3cret --org my-org --project my-app --env dev # Retrieve it coldrune secret get DB_PASSWORD --org my-org --project my-app --env dev ``` ## What's next - Set up [coldrune.yml](/guides/configuration/) to avoid repeating `--org`, `--project`, `--env` flags - [Deploy to production](/self-hosting/deployment/) with systemd and nginx - Connect [AI agents](/agents/overview/) via MCP ### Secrets Secrets are encrypted at rest with AES-256-GCM. Each secret gets its own data encryption key. Values are never stored in plaintext. ## Set a secret ```bash coldrune secret set DB_PASSWORD=s3cret --org my-org --project api --env prod ``` Setting an existing key creates a new version. ## Get a secret ```bash coldrune secret get DB_PASSWORD --org my-org --project api --env prod ``` ## List keys ```bash coldrune secret list --org my-org --project api --env prod ``` Lists keys and metadata only. Values are not returned. ## Delete ```bash coldrune secret delete DB_PASSWORD --org my-org --project api --env prod ``` Soft-deletes the secret. The key can be reused. ## Import from file ```bash # From .env file coldrune secret import --file .env --org my-org --project api --env prod # From JSON coldrune secret import --file secrets.json --org my-org --project api --env prod # From YAML coldrune secret import --file config.yaml --org my-org --project api --env prod ``` Format is detected from the file extension (`.env`, `.json`, `.yaml`/`.yml`). ## Export to file ```bash coldrune secret export --file .env --org my-org --project api --env prod ``` ## Run with injected secrets ```bash coldrune run --org my-org --project api --env prod -- ./deploy.sh ``` This fetches all secrets from the environment, injects them as environment variables, and replaces the current process with the command. The `COLDRUNE_API_KEY` variable is stripped from the child environment to prevent credential leakage. Use `coldrune run` in CI/CD to avoid writing secrets to disk. ## Key format Secret keys must be 1-100 characters, start with a letter or underscore, then contain alphanumeric characters, underscores, dots, or hyphens. Maximum value size is 64 KB. Valid examples: `DB_PASSWORD`, `api.key`, `redis-url`, `_PRIVATE_TOKEN` ## REST API ```bash # Set curl -X PUT http://localhost:7100/api/orgs/my-org/projects/api/envs/prod/secrets/DB_PASSWORD \ -H 'Authorization: Bearer ' \ -H 'Content-Type: application/json' \ -d '{"value": "s3cret"}' # Get curl http://localhost:7100/api/orgs/my-org/projects/api/envs/prod/secrets/DB_PASSWORD \ -H 'Authorization: Bearer ' # List curl http://localhost:7100/api/orgs/my-org/projects/api/envs/prod/secrets \ -H 'Authorization: Bearer ' # Delete curl -X DELETE http://localhost:7100/api/orgs/my-org/projects/api/envs/prod/secrets/DB_PASSWORD \ -H 'Authorization: Bearer ' ``` ### Service Accounts Service accounts provide API key authentication for CI/CD pipelines, scripts, and automated systems. ## Create a service account ```bash coldrune service-account create --org my-org --name ci-deploy ``` The API key is displayed once and cannot be retrieved again. It starts with the `cr_sa_` prefix. Save the API key immediately. It is shown only once. ### Scope to a project ```bash coldrune service-account create --org my-org --name ci-deploy --project api-backend ``` A project-scoped service account can only access secrets within that project. ## List service accounts ```bash coldrune service-account list --org my-org ``` ## Revoke ```bash coldrune service-account revoke --org my-org --id ``` Revoking also soft-deletes any ACL rules associated with the service account. ## Usage ### CLI Set the `COLDRUNE_API_KEY` environment variable: ```bash coldrune secret get DB_PASSWORD --org my-org --project api --env prod ``` The CLI detects the `cr_sa_` prefix and uses the `X-API-Key` header automatically. ### REST API ```bash curl http://localhost:7100/api/orgs/my-org/projects/api/envs/prod/secrets/DB_PASSWORD \ -H 'X-API-Key: cr_sa_...' ``` ### CI/CD example (GitHub Actions) ```yaml steps: - name: Deploy with secrets env: COLDRUNE_API_KEY: ${{ secrets.COLDRUNE_API_KEY }} COLDRUNE_SERVER_URL: https://api.coldrune.com run: | coldrune run --org my-org --project api --env prod -- ./deploy.sh ``` ## ACL requirement Service accounts always require explicit [ACL rules](/guides/access-control/). They never bypass access control, even if created by an org owner. ```bash # Grant the service account access coldrune acl grant \ --org my-org \ --subject ci-deploy \ --project api-backend \ --env prod \ --permission write ``` ## REST API ```bash # Create curl -X POST http://localhost:7100/api/orgs/my-org/service-accounts \ -H 'Authorization: Bearer ' \ -H 'Content-Type: application/json' \ -d '{"name": "ci-deploy"}' # Response: {"id": "...", "name": "ci-deploy", "api_key": "cr_sa_..."} # List curl http://localhost:7100/api/orgs/my-org/service-accounts \ -H 'Authorization: Bearer ' # Revoke curl -X DELETE http://localhost:7100/api/orgs/my-org/service-accounts/ \ -H 'Authorization: Bearer ' ``` --- ## Agents ### llms.txt & AGENTS.md ## llms.txt Coldrune serves machine-readable documentation following the [llmstxt.org](https://llmstxt.org/) convention: - **[/llms.txt](/llms.txt)** — curated index with links to key documentation pages and one-line descriptions - **[/llms-full.txt](/llms-full.txt)** — full documentation concatenated as a single plain text file These files are designed for AI coding agents that need to understand Coldrune's API, CLI, and configuration without parsing HTML. ### When to use which | File | Use case | |------|----------| | `llms.txt` | Quick orientation — what Coldrune does, where to find specific docs | | `llms-full.txt` | Deep context — full documentation in one file for RAG indexing or large context windows | ## AGENTS.md for your projects If your project uses Coldrune for secret management, add an `AGENTS.md` file to help AI agents work with your secrets correctly. ### Template ```markdown # Secret Management This project stores secrets in Coldrune. ## Configuration - Config file: `coldrune.yml` in the project root - Server: https://api.coldrune.com - Organization: my-org - Project: my-app ## Reading secrets Secrets are available through the Coldrune MCP server or CLI: - MCP: Use `secret_get` tool with the key name - CLI: `coldrune secret get ` - Runtime: `coldrune run -- ` injects all secrets as env vars ## Environments - `dev` — local development - `staging` — pre-production testing - `prod` — production (read access requires explicit ACL grant) ## Rules - Never log, print, or commit secret values - Never hardcode secrets in source files - Use `coldrune run` to inject secrets at runtime - Use environment-specific secrets (don't share prod keys with dev) ``` ### Placement Place `AGENTS.md` at the root of your repository. Most AI coding tools (Claude Code, Cursor, Copilot) read context files from the repository root automatically. | Tool | Context file | |------|-------------| | Claude Code | `CLAUDE.md` or `AGENTS.md` | | Cursor | `.cursor/rules/*.mdc` or `.cursorrules` | | GitHub Copilot | `.github/copilot-instructions.md` | You can add the Coldrune-specific section to your existing context file instead of creating a separate `AGENTS.md`. ### MCP Server The Coldrune MCP server (`coldrune-mcp`) exposes secret management tools over the Model Context Protocol using stdio transport. ## Install ```bash # Download the binary curl -L -o coldrune-mcp https://github.com/user/coldrune/releases/latest/download/coldrune-mcp-linux-amd64 chmod +x coldrune-mcp sudo mv coldrune-mcp /usr/local/bin/ ``` Or build from source: ```bash cd mcp cargo build --release # Binary at target/release/coldrune-mcp ``` ## Configure The MCP server reads configuration from three sources (in priority order): 1. **Environment variables**: `COLDRUNE_SERVER_URL`, `COLDRUNE_API_KEY`, `COLDRUNE_ORG`, `COLDRUNE_PROJECT`, `COLDRUNE_ENV` 2. **`coldrune.yml`** in the working directory 3. **Tool parameters** (per-call overrides) ### Authentication Set `COLDRUNE_API_KEY` with a [service account](/guides/service-accounts/) API key: ```bash ``` Or the server falls back to the session token at `~/.config/coldrune/session`. For non-localhost servers, always use HTTPS. The MCP server warns when sending auth tokens over unencrypted HTTP. ### Defaults Create a `coldrune.yml` in your project root so the agent doesn't need to pass org/project/env on every call: ```yaml server_url: https://api.coldrune.com org: my-org project: api-backend env: dev ``` ## Client configuration Add to your Claude Desktop config (`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS): ```json { "mcpServers": { "coldrune": { "command": "coldrune-mcp", "env": { "COLDRUNE_SERVER_URL": "https://api.coldrune.com", "COLDRUNE_API_KEY": "cr_sa_..." } } } } ``` Add to your project's `.mcp.json`: ```json { "mcpServers": { "coldrune": { "command": "coldrune-mcp", "env": { "COLDRUNE_SERVER_URL": "https://api.coldrune.com", "COLDRUNE_API_KEY": "cr_sa_..." } } } } ``` The server uses **stdio transport** (JSON-RPC over stdin/stdout). Launch it as a subprocess: ```bash COLDRUNE_API_KEY=cr_sa_... coldrune-mcp ``` Logs go to stderr. Protocol messages go to stdout. ## Available tools The server exposes 15 tools. See the [MCP Tools Reference](/agents/mcp-tools/) for the complete list. | Category | Tools | |----------|-------| | Secrets | `secret_set`, `secret_get`, `secret_list`, `secret_delete` | | Projects | `project_create`, `project_list`, `project_delete` | | Environments | `env_create`, `env_list`, `env_delete` | | Organization | `org_list`, `org_members_list` | | Access & Audit | `acl_list`, `audit_list` | | Health | `health_check` | ### MCP Tools Reference All tools accept optional `org`, `project`, and `env` parameters. When omitted, defaults from `coldrune.yml` or environment variables are used. ## Secret tools ### secret_set Set (create or update) a secret value in a Coldrune environment. | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `key` | string | yes | Secret key name (e.g. `DATABASE_URL`, `API_KEY`) | | `value` | string | yes | Secret value | | `org` | string | no | Organization name | | `project` | string | no | Project name | | `env` | string | no | Environment name | ### secret_get Get a secret's decrypted value from a Coldrune environment. | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `key` | string | yes | Secret key name | | `org` | string | no | Organization name | | `project` | string | no | Project name | | `env` | string | no | Environment name | ### secret_list List all secret keys in a Coldrune environment. Values are not returned. | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `org` | string | no | Organization name | | `project` | string | no | Project name | | `env` | string | no | Environment name | ### secret_delete Delete a secret from a Coldrune environment (soft delete). | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `key` | string | yes | Secret key name to delete | | `org` | string | no | Organization name | | `project` | string | no | Project name | | `env` | string | no | Environment name | ## Project tools ### project_create Create a new project in a Coldrune organization. Automatically creates `dev`, `staging`, and `prod` environments. | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `name` | string | yes | Project name (lowercase alphanumeric + hyphens, 2-50 chars) | | `org` | string | no | Organization name | ### project_list List all projects in a Coldrune organization. | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `org` | string | no | Organization name | ### project_delete Delete a project from a Coldrune organization (soft delete). | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `name` | string | yes | Project name to delete | | `org` | string | no | Organization name | ## Environment tools ### env_create Create a new environment in a Coldrune project. | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `name` | string | yes | Environment name (lowercase alphanumeric + hyphens, 2-50 chars) | | `org` | string | no | Organization name | | `project` | string | no | Project name | ### env_list List all environments in a Coldrune project. | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `org` | string | no | Organization name | | `project` | string | no | Project name | ### env_delete Delete an environment from a Coldrune project (soft delete). | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `name` | string | yes | Environment name to delete | | `org` | string | no | Organization name | | `project` | string | no | Project name | ## Organization tools ### org_list List all organizations the authenticated user belongs to. No parameters required. ### org_members_list List all members of a Coldrune organization with their roles. | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `org` | string | no | Organization name | ## Access control & audit tools ### acl_list List access control rules for a Coldrune organization. | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `org` | string | no | Organization name | ### audit_list List audit log entries for a Coldrune organization with optional filters. | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `org` | string | no | Organization name | | `action` | string | no | Filter by action (e.g. `secret.set`, `org.create`) | | `actor` | string | no | Filter by actor ID | | `resource_type` | string | no | Filter by resource type | | `limit` | integer | no | Maximum results to return | | `after` | string | no | Pagination cursor | ## Health tool ### health_check Check if the Coldrune server is reachable and healthy. No parameters required. ### AI Agent Integration AI coding agents (Claude Code, Cursor, Copilot) often need access to secrets — API keys, database credentials, tokens — without those values leaking into prompts, logs, or generated code. Coldrune provides two integration paths: ## MCP (recommended) The [Coldrune MCP server](/agents/mcp-server/) exposes 15 tools over the Model Context Protocol. Agents can read secrets, manage projects, and query audit logs through structured tool calls. Benefits: - Secrets stay server-side — agents receive values only when explicitly requested - Agents can create projects, environments, and manage infrastructure - Full audit trail of agent actions - Works with any MCP-compatible client ## CLI Agents can also call the `coldrune` CLI directly. Authenticate with a service account API key: ```bash coldrune secret get DB_PASSWORD --org my-org --project api --env dev ``` Or use `coldrune run` to inject secrets into a subprocess: ```bash coldrune run --org my-org --project api --env dev -- ./migrate.sh ``` ## Authentication for agents | Method | Use case | |--------|----------| | Service account (`COLDRUNE_API_KEY`) | CI/CD, automated scripts, agents | | Session token | Interactive human use | Service accounts are recommended for agents. They: - Don't require interactive login - Can be scoped to a specific project - Always require explicit ACL rules (deny-by-default) - Leave clear audit trails identifying the service account Create one: ```bash coldrune service-account create --org my-org --name claude-agent coldrune acl grant --org my-org --subject claude-agent --project "*" --env "*" --permission read ``` ## Machine-readable docs Coldrune serves machine-readable documentation at: - [`/llms.txt`](/llms.txt) — curated index of key documentation pages - [`/llms-full.txt`](/llms-full.txt) — full documentation concatenated as plain text See [llms.txt & AGENTS.md](/agents/llms-txt/) for details. --- ## Self hosting ### Architecture ## Overview Coldrune is a single Rust binary with an embedded SQLite database. No external services are required for core operation. ``` ┌──────────────────────────────────────────────────┐ │ Coldrune Binary │ │ │ │ ┌───────────┐ ┌──────────┐ ┌───────────────┐ │ │ │ HTTP API │ │ Maintena │ │ Backup Worker │ │ │ │ (Axum) │ │ Worker │ │ (optional) │ │ │ └─────┬──────┘ └────┬─────┘ └──────┬────────┘ │ │ │ │ │ │ │ └──────────────┼───────────────┘ │ │ │ │ │ ┌────────┴────────┐ │ �� │ SQLite (WAL) │ │ │ └─────────────────┘ │ └──────────────────────────────────────────────────┘ │ │ ┌────┴─────┐ ┌─────┴──────┐ │ SMTP │ │ S3 Storage │ │ (email) │ │ (backups) │ └──────────┘ └────────────┘ ``` ## Data model ``` Organization (tenant boundary) ├── Members (user + role) ├── Service Accounts (API keys) ├── ACL Rules (access patterns) ├── Audit Logs (immutable trail) └── Projects └── Environments (dev, staging, prod, ...) └── Secrets (encrypted key-value pairs) ``` All entities use UUIDs as primary keys. Soft deletes via `removed_at` timestamp. ## Request flow 1. Request hits reverse proxy (nginx/Caddy) — TLS terminated, rate limited, `X-Real-Ip` set 2. Axum receives the request on `127.0.0.1:7100` 3. Auth middleware extracts the caller: - `Authorization: Bearer ` → session lookup → `AuthUser` - `X-API-Key: cr_sa_...` → hash lookup → `ServiceAccount` - Both wrapped in a `Caller` enum for handlers that accept either 4. Route handler validates input, checks org membership + role 5. For secret operations: ACL check against project/env patterns 6. Handler executes the operation 7. Audit log entry written (fire-and-forget — failures don't block the response) 8. JSON response returned ## Background workers ### Maintenance worker Runs every hour, unconditionally: - Deletes expired sessions (older than 30 days) - Deletes used/expired magic links (older than 1 hour, preserving the lockout window) ### Backup worker Runs on a configurable interval (`BACKUP_SCHEDULE_HOURS`). Disabled when set to `0` or when S3 is not configured. - Creates an encrypted database snapshot - Uploads to S3 - Runs retention cleanup (daily + weekly policy) ## Database SQLite in WAL (Write-Ahead Logging) mode for concurrent read access. Key characteristics: - File permissions set to `0600` (owner-only) on Unix - `PRAGMA foreign_keys = ON` enforced per connection - All foreign keys use `ON UPDATE RESTRICT ON DELETE RESTRICT` - Migrations embedded at compile time, run on startup - Partial unique indexes enable name reuse after soft deletion ## API error format All errors follow a consistent format: ```json { "error": { "code": "VALIDATION_ERROR", "message": "Human readable description" } } ``` Error codes use `SCREAMING_SNAKE_CASE`. Internal details are never leaked to clients. ## Two modes The `coldrune` binary operates in two modes: - **Server mode** (`coldrune server start`) — runs the HTTP API, accesses the database directly - **Client mode** (`coldrune `) — a pure HTTP client that talks to the server's REST API The CLI never touches the database. All operations go through the HTTP API. Exception: `coldrune server rotate-key` accesses the database directly for offline key rotation. ### Backups Coldrune backs up the entire SQLite database as an encrypted snapshot to S3-compatible storage. ## Configure S3 Add to your `.env`: ```ini BACKUP_S3_ENDPOINT=https://fsn1.your-objectstorage.com BACKUP_S3_REGION=us-east-1 BACKUP_S3_BUCKET=coldrune-backups BACKUP_S3_ACCESS_KEY=your-access-key BACKUP_S3_SECRET_KEY=your-secret-key ``` Works with Hetzner Object Storage, AWS S3, MinIO, and any S3-compatible provider. If `BACKUP_S3_ENDPOINT` is not set, all backup features are disabled. ## Manual backup ```bash coldrune backup create ``` Or via API: ```bash curl -X POST http://localhost:7100/api/backups \ -H 'Authorization: Bearer ' ``` Backup endpoints require superadmin access. ## Scheduled backups ```ini BACKUP_SCHEDULE_HOURS=24 ``` Set to `0` to disable. The backup runs as a background task inside the server process. ## Retention policy ```ini BACKUP_RETAIN_DAILY_DAYS=7 BACKUP_RETAIN_WEEKLY_WEEKS=4 ``` After each scheduled backup, the server cleans up old backups: - **Daily**: keeps the most recent backup from each of the last N days - **Weekly**: keeps one backup per ISO week for the last M weeks - Anything older is deleted from S3 ## List backups ```bash coldrune backup list ``` ## Restore ```bash coldrune backup restore --id ``` This downloads, decrypts, validates the SQLite header, and writes the restored database to `{db_path}.restore.{id}`. Restore does not automatically swap the database. You must manually stop the server, replace the database file, and restart. ### Restore procedure 1. Restore the backup: `coldrune backup restore --id ` 2. Stop the server: `sudo systemctl stop coldrune` 3. Swap the database: `mv coldrune.db coldrune.db.old && mv coldrune.db.restore. coldrune.db` 4. Restart: `sudo systemctl start coldrune` ## Backup format Backups use a custom binary format: ``` CRBU (4 bytes magic) | version (1 byte) | nonce (12 bytes) | ciphertext ``` Encrypted with a key derived from the master key via HKDF-SHA256 (info: `coldrune-backup-key`), using AES-256-GCM. The backup is created using SQLite's `VACUUM INTO` for a consistent snapshot without locking the database. ## After key rotation Old backups remain encrypted with the old master key. After rotating keys, create a new backup immediately. Keep the old master key stored securely if you need to restore from pre-rotation backups. ### Server Configuration The server is configured entirely via environment variables. Use a `.env` file or your system's environment management. ## Required | Variable | Description | |----------|-------------| | `COLDRUNE_MASTER_KEY` | Encryption master key (64 hex chars). Generate: `openssl rand -hex 32` | | `SUPERADMIN_EMAIL_0` | First superadmin email. Add more with `_1`, `_2`, etc. | | `SMTP_HOST` | SMTP server hostname. Set to `log` for dev mode. | | `SMTP_USERNAME` | SMTP auth username (not required if `SMTP_HOST=log`) | | `SMTP_PASSWORD` | SMTP auth password (not required if `SMTP_HOST=log`) | | `SMTP_FROM` | Sender address, e.g. `Coldrune ` | ## Server | Variable | Description | Default | |----------|-------------|---------| | `COLDRUNE_DB_PATH` | SQLite database file path | `coldrune.db` | | `COLDRUNE_HOST` | Bind address | `127.0.0.1` | | `COLDRUNE_PORT` | Bind port | `7100` | | `SMTP_PORT` | SMTP server port | `587` | | `CORS_ALLOWED_ORIGINS` | Comma-separated allowed origins | (empty, CORS disabled) | ## Backup (optional) All backup variables are optional. If `BACKUP_S3_ENDPOINT` is not set, backup features are disabled. | Variable | Description | Default | |----------|-------------|---------| | `BACKUP_S3_ENDPOINT` | S3-compatible endpoint URL | (disabled) | | `BACKUP_S3_REGION` | S3 region | `us-east-1` | | `BACKUP_S3_BUCKET` | Bucket name | (required if endpoint set) | | `BACKUP_S3_ACCESS_KEY` | S3 access key | (required if endpoint set) | | `BACKUP_S3_SECRET_KEY` | S3 secret key | (required if endpoint set) | | `BACKUP_SCHEDULE_HOURS` | Auto-backup interval in hours (0 = disabled) | `0` | | `BACKUP_RETAIN_DAILY_DAYS` | Keep daily backups for N days | `7` | | `BACKUP_RETAIN_WEEKLY_WEEKS` | Keep weekly backups for N weeks | `4` | ## Key rotation | Variable | Description | |----------|-------------| | `COLDRUNE_NEW_MASTER_KEY` | New master key for `coldrune server rotate-key` (optional, falls back to stdin) | ## Example .env ```ini # Server COLDRUNE_DB_PATH=/var/lib/coldrune/coldrune.db COLDRUNE_HOST=127.0.0.1 COLDRUNE_PORT=7100 # Encryption COLDRUNE_MASTER_KEY=a1b2c3d4e5f6... # 64 hex chars # Superadmins SUPERADMIN_EMAIL_0=admin@example.com # SMTP (Postmark example) SMTP_HOST=smtp.postmarkapp.com SMTP_PORT=587 SMTP_USERNAME=your-server-token SMTP_PASSWORD=your-server-token SMTP_FROM=Coldrune # Backup to Hetzner Object Storage BACKUP_S3_ENDPOINT=https://fsn1.your-objectstorage.com BACKUP_S3_BUCKET=coldrune-backups BACKUP_S3_ACCESS_KEY=your-access-key BACKUP_S3_SECRET_KEY=your-secret-key BACKUP_SCHEDULE_HOURS=24 ``` Use `SMTP_HOST=log` during setup. Login codes are printed to the server terminal instead of being sent via email. ## Superadmins Superadmin emails are indexed: `SUPERADMIN_EMAIL_0`, `SUPERADMIN_EMAIL_1`, `SUPERADMIN_EMAIL_2`, etc. On startup, users matching these emails are promoted to superadmin. Users removed from the list are demoted. Superadmins bypass all organization role checks and ACL rules. ### Deployment Coldrune runs behind a reverse proxy that handles TLS, rate limiting, and the `X-Real-Ip` header. Do not expose Coldrune directly to the internet. It trusts `X-Real-Ip` and `X-Forwarded-For` headers unconditionally, so a trusted reverse proxy must set these. ## Process management with systemd Create `/etc/systemd/system/coldrune.service`: ```ini [Unit] Description=Coldrune Secret Manager After=network.target [Service] Type=simple User=coldrune Group=coldrune WorkingDirectory=/var/lib/coldrune EnvironmentFile=/etc/coldrune/.env ExecStart=/usr/local/bin/coldrune server start Restart=on-failure RestartSec=5 # Hardening NoNewPrivileges=true ProtectSystem=strict ProtectHome=true ReadWritePaths=/var/lib/coldrune PrivateTmp=true [Install] WantedBy=multi-user.target ``` Set up the user and directories: ```bash # Create system user sudo useradd --system --shell /usr/sbin/nologin --home /var/lib/coldrune coldrune sudo mkdir -p /var/lib/coldrune /etc/coldrune # Place your .env file sudo cp .env /etc/coldrune/.env sudo chmod 600 /etc/coldrune/.env sudo chown coldrune:coldrune /etc/coldrune/.env # Enable and start sudo systemctl daemon-reload sudo systemctl enable coldrune sudo systemctl start coldrune # Check status sudo systemctl status coldrune sudo journalctl -u coldrune -f ``` ## Reverse proxy ```nginx upstream coldrune { server 127.0.0.1:7100; } # Rate limiting zone limit_req_zone $binary_remote_addr zone=coldrune_api:10m rate=30r/s; server { listen 443 ssl http2; server_name api.coldrune.com; ssl_certificate /etc/letsencrypt/live/api.coldrune.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/api.coldrune.com/privkey.pem; # Pass real client IP proxy_set_header X-Real-Ip $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $host; location / { limit_req zone=coldrune_api burst=60 nodelay; proxy_pass http://coldrune; } } server { listen 80; server_name api.coldrune.com; return 301 https://$host$request_uri; } ``` ``` api.coldrune.com { reverse_proxy localhost:7100 { header_up X-Real-Ip {remote_host} } } ``` Caddy handles TLS automatically via Let's Encrypt. ## Verify ```bash # Health check curl https://api.coldrune.com/health # {"status": "ok"} # Login coldrune --server-url https://api.coldrune.com auth login --email admin@example.com ``` ## Database location By default, Coldrune creates `coldrune.db` in the working directory. For production, set an explicit path: ```ini COLDRUNE_DB_PATH=/var/lib/coldrune/coldrune.db ``` The database file and its WAL/SHM companions are created with `0600` permissions. ### Encryption Coldrune encrypts every secret with AES-256-GCM using a two-level key hierarchy. ## Key hierarchy ``` Master key (64 hex chars, from COLDRUNE_MASTER_KEY env var) │ ├── HKDF-SHA256 (info: "coldrune-kek") │ └── KEK (Key Encryption Key) │ └── encrypts per-secret DEKs │ └── HKDF-SHA256 (info: "coldrune-backup-key") └── Backup encryption key └── encrypts database backups ``` For each secret: 1. A random 32-byte **DEK** (Data Encryption Key) is generated 2. The secret value is encrypted with the DEK using AES-256-GCM 3. The DEK is encrypted with the KEK using AES-256-GCM 4. Stored: `encrypted_value`, `nonce`, `encrypted_dek`, `dek_nonce` The master key never encrypts data directly. It derives the KEK and backup key through HKDF-SHA256. ## Generate a master key ```bash openssl rand -hex 32 ``` This produces a 64-character hex string (256 bits). Store it securely — losing the master key means losing all secrets. The master key cannot be recovered. Back it up in a separate secure location before deploying to production. ## Key rotation If you suspect key compromise or want to rotate keys as a policy: 1. **Stop the server** ```bash sudo systemctl stop coldrune ``` 2. **Rotate** ```bash # Generate a new key automatically coldrune server rotate-key --generate # Or provide a specific new key COLDRUNE_NEW_MASTER_KEY=$(openssl rand -hex 32) coldrune server rotate-key ``` This re-encrypts all DEKs with the new KEK in a single atomic transaction. Secret values and their nonces are unchanged — only the DEK encryption is rotated. 3. **Update `COLDRUNE_MASTER_KEY`** in your `.env` file with the new key (printed to stdout) 4. **Restart the server** ```bash sudo systemctl start coldrune ``` 5. **Create a new backup** — old backups are encrypted with the old key ## What rotation changes | Component | Changed? | |-----------|----------| | Master key | Yes (new value) | | KEK | Yes (derived from new master key) | | Each DEK's encryption | Yes (re-encrypted with new KEK, fresh nonces) | | Secret values | No (encrypted data unchanged) | | Backup key | Yes (derived from new master key) | | Existing backups | No (still encrypted with old key) | ## Tamper detection AES-GCM includes an authentication tag. Any modification to the ciphertext, nonce, or DEK causes decryption to fail. Coldrune does not silently return corrupted data. ### Installation Coldrune ships as a single static binary. No runtime dependencies. ## Download ```bash # Linux amd64 curl -L -o coldrune https://github.com/user/coldrune/releases/latest/download/coldrune-linux-amd64 # Linux arm64 curl -L -o coldrune https://github.com/user/coldrune/releases/latest/download/coldrune-linux-arm64 # macOS (Apple Silicon) curl -L -o coldrune https://github.com/user/coldrune/releases/latest/download/coldrune-darwin-arm64 ``` ## Verify ```bash sha256sum coldrune # Compare against the published checksum ``` ## Install ```bash chmod +x coldrune sudo mv coldrune /usr/local/bin/ coldrune --version ``` ## Supported platforms - Linux amd64, arm64 - macOS arm64 (Apple Silicon), amd64 (Intel) Windows is not supported. ## Build from source Requires Rust 2024 edition (1.85+): ```bash git clone https://github.com/user/coldrune.git cd coldrune cargo build --release # Binary at target/release/coldrune ``` ## Next steps - [Configure the server](/self-hosting/configuration/) - [Deploy with systemd and nginx](/self-hosting/deployment/) --- ## Reference ### REST API Reference Base URL: `https://api.coldrune.com` (or your self-hosted instance) All responses use JSON. Errors follow the format: ```json {"error": {"code": "ERROR_CODE", "message": "Human readable description"}} ``` ## Authentication Most endpoints require one of: - `Authorization: Bearer ` — for human users - `X-API-Key: cr_sa_...` — for service accounts ### POST /api/auth/login Request a magic link login code. ```json {"email": "you@example.com"} ``` Returns `200` with `{"message": "..."}`. The 6-digit code is sent via email (or printed to server logs in dev mode). ### POST /api/auth/verify Verify the login code and receive a session token. ```json {"email": "you@example.com", "code": "123456"} ``` Returns `200` with `{"token": "..."}`. ### DELETE /api/auth/logout Invalidate the current session. Requires auth. ### GET /api/auth/me Return the authenticated user's profile. Requires auth. ```json {"id": "...", "email": "you@example.com", "name": "...", "is_superadmin": false} ``` ## Organizations ### POST /api/orgs Create an organization. Requires auth. Caller becomes owner. ```json {"name": "my-org"} ``` ### GET /api/orgs List organizations the user is a member of. Superadmins see all. ### GET /api/orgs/\{org_name\} Get organization details. ### PATCH /api/orgs/\{org_name\} Rename an organization. Requires owner role. ```json {"name": "new-name"} ``` ### DELETE /api/orgs/\{org_name\} Soft-delete an organization. Requires owner role. ## Members ### POST /api/orgs/\{org_name\}/members Invite a user to the organization. Requires admin+ role. ```json {"email": "alice@example.com", "role": "developer"} ``` Roles: `owner`, `admin`, `developer`, `member`. ### GET /api/orgs/\{org_name\}/members List organization members. ### PATCH /api/orgs/\{org_name\}/members/\{user_id\} Update a member's role. Requires owner role. ```json {"role": "admin"} ``` ### DELETE /api/orgs/\{org_name\}/members/\{user_id\} Remove a member. Requires admin+ role. ## Projects ### POST /api/orgs/\{org_name\}/projects Create a project. Automatically creates `dev`, `staging`, `prod` environments. Requires admin+ role. ```json {"name": "api-backend"} ``` ### GET /api/orgs/\{org_name\}/projects List projects in the organization. ### GET /api/orgs/\{org_name\}/projects/\{project_name\} Get project details. ### PATCH /api/orgs/\{org_name\}/projects/\{project_name\} Rename a project. Requires admin+ role. ```json {"name": "new-name"} ``` ### DELETE /api/orgs/\{org_name\}/projects/\{project_name\} Soft-delete a project. Requires admin+ role. ## Environments ### POST /api/orgs/\{org_name\}/projects/\{project_name\}/envs Create an environment. Requires admin+ role. ```json {"name": "qa"} ``` ### GET /api/orgs/\{org_name\}/projects/\{project_name\}/envs List environments in a project. ### DELETE /api/orgs/\{org_name\}/projects/\{project_name\}/envs/\{env_name\} Delete an environment. Requires admin+ role. ## Secrets Secret endpoints require ACL rules for developers, members, and service accounts. Org owners and admins bypass ACL. ### PUT /api/orgs/\{org_name\}/projects/\{project_name\}/envs/\{env_name\}/secrets/\{key\} Set (create or update) a secret. Requires `write` permission. ```json {"value": "s3cret"} ``` Returns the secret metadata with version number: ```json {"key": "DB_PASSWORD", "version": 2, "created_at": "...", "updated_at": "..."} ``` ### GET /api/orgs/\{org_name\}/projects/\{project_name\}/envs/\{env_name\}/secrets/\{key\} Get a secret's decrypted value. Requires `read` permission. ```json {"key": "DB_PASSWORD", "value": "s3cret", "version": 2} ``` ### GET /api/orgs/\{org_name\}/projects/\{project_name\}/envs/\{env_name\}/secrets List secret keys. Values are not returned. Requires `read` permission. ```json { "data": [ {"key": "DB_PASSWORD", "version": 2, "updated_at": "2026-04-01T12:00:00Z"} ] } ``` ### DELETE /api/orgs/\{org_name\}/projects/\{project_name\}/envs/\{env_name\}/secrets/\{key\} Soft-delete a secret. Requires `write` permission. ## Access Control (ACL) ### POST /api/orgs/\{org_name\}/acl Grant an ACL rule. Upserts if the same subject + pattern combination exists. Requires admin+ role. ```json { "subject_type": "user", "email": "alice@example.com", "project_pattern": "api-*", "env_pattern": "*", "permission": "write" } ``` For service accounts, use `"subject_type": "service_account"` and `"subject_id": ""` instead of `email`. ### GET /api/orgs/\{org_name\}/acl List ACL rules for the organization. Requires admin+ role. ### DELETE /api/orgs/\{org_name\}/acl/\{rule_id\} Revoke an ACL rule. Requires admin+ role. ## Service Accounts ### POST /api/orgs/\{org_name\}/service-accounts Create a service account. The API key is returned once and cannot be retrieved again. Requires admin+ role. ```json {"name": "ci-deploy", "project": "api-backend"} ``` `project` is optional. Omit to create an org-scoped service account. Response: ```json {"id": "...", "name": "ci-deploy", "api_key": "cr_sa_..."} ``` Save the `api_key` immediately. It is shown only once. ### GET /api/orgs/\{org_name\}/service-accounts List service accounts in the organization. Requires admin+ role. ### DELETE /api/orgs/\{org_name\}/service-accounts/\{sa_id\} Revoke a service account. Cascades soft-delete to its ACL rules. Requires admin+ role. ## Audit Logs ### GET /api/orgs/\{org_name\}/audit-logs Query audit logs. Requires admin+ role or superadmin. Query parameters: | Parameter | Description | |-----------|-------------| | `action` | Filter by action (e.g. `secret.set`, `org.create`) | | `actor_id` | Filter by actor ID | | `resource_type` | Filter by resource type (e.g. `secret`, `org`) | | `limit` | Max results (1-100, default 20) | | `after` | Pagination cursor from previous response | Response: ```json { "data": [ { "id": "...", "actor_type": "user", "actor_name": "alice@example.com", "action": "secret.set", "resource_type": "secret", "resource_id": "...", "org_id": "...", "ip_address": "203.0.113.1", "metadata": {"key": "DB_PASSWORD", "version": 2}, "created_at": "2026-04-01T12:00:00Z" } ], "pagination": { "total": 142, "limit": 20, "has_next": true, "next_cursor": "..." } } ``` ## Backups Backup endpoints require superadmin access. Backups must be configured with S3 environment variables. ### POST /api/backups Create a manual backup. Returns the backup metadata. ### GET /api/backups List all backups. ### POST /api/backups/\{backup_id\}/restore Restore from a backup. Downloads and decrypts the backup to `{db_path}.restore.{id}`. The operator must manually stop the server, swap the database file, and restart. ## Health ### GET /health No authentication required. ```json {"status": "ok"} ```