Configuration Reference
Containment Chamber is configured through a YAML file, environment variables, and CLI flags. This page documents every available option.
Example Configuration File
Section titled “Example Configuration File”A fully commented example configuration file is available at config.example.yaml. Copy it and customize for your deployment:
curl -O https://raw.githubusercontent.com/unforeseen-consequences/containment-chamber/main/config.example.yamlcp config.example.yaml config.yaml# Edit config.yaml to match your setupcontainment-chamber server -c config.yamlFull config.example.yaml
# Copy this file to config.yaml and customize for your deployment.## Usage:# containment-chamber server -c config.yaml## CLI flags override values from this file.# Environment variables with CONTAINMENT_ prefix also override (use __ for nesting).# Example: CONTAINMENT_ANTI_SLASHING__URL="postgresql://..." (env var names are automatically lowercased)
# Server settingsserver: listen_address: "0.0.0.0" # CLI: --server-listen-address listen_port: 9000 # CLI: --server-listen-port
# Metrics endpointmetrics: listen_address: "0.0.0.0" # CLI: --metrics-listen-address listen_port: 3000 # CLI: --metrics-listen-port refresh_interval_seconds: 30 # CLI: --metrics-refresh-interval-seconds
# Ethereum signing configuration# Network name used for genesis-validators-root validation. Signing remains fork-agnostic.# CLI: --networknetwork: mainnet
# Key sources: filesystem, DynamoDB, or bothkey_sources: filesystem: paths: - ./keystores/raw - ./keystores/pbkdf2 - ./keystores/scrypt keystore_load_concurrency: 128 raw_load_concurrency: 128 # DynamoDB key source (optional, requires AWS credentials) # KMS key ARNs and threshold are provided during the init ceremony via the API, # not in this config file. See the Seal & Unseal guide for the init ceremony. # dynamodb: # table: containment-keys # refresh_interval_seconds: 1
# Signer state is required when key_sources.dynamodb is configured.# signer_state:# backend: dynamodb# table: containment-state# refresh_interval_seconds: 1# unseal_timeout_minutes: 30
# Signing concurrency and priority queuessigning: max_concurrent_jobs: 2000 queue_buffer_size: 4000 # priority: # enabled: true # concurrency: 50 # operations: [BLOCK_V2]
# Anti-slashing backend# postgres is recommended for production (multi-instance safe)anti_slashing: backend: postgres url: "postgresql://user:password@localhost:5432/slashing?sslmode=require" pool_size: 64 # Force DNS resolution to IPv4 only. Enable for musl/scratch Docker images # with IPv6 routing issues (e.g., NAT64 on Kubernetes). Default: false. force_ipv4: false # TLS is enabled by default. Add ?sslmode=disable to the URL to disable. # AWS RDS CA is baked into the Docker image.# Alternative backends:# anti_slashing:# backend: sqlite# path: ./slashing_protection.sqlite# anti_slashing:# backend: noop # WARNING: no slashing protection
# ──────────────────────────────────────────────────────────────# Key Manager API — hot-load/remove EIP-2335 keystores at runtime# Disabled by default.# ──────────────────────────────────────────────────────────────http: key_manager_api: enabled: false # max_concurrent_reads: 50 # max_concurrent_writes: 10## To enable:# http:# key_manager_api:# enabled: true## Auth policies are now managed via the /api/v1/auth/ API, not in the config file.# See the Signing Auth guide for details: /chamber-api/auth/## The only auth config remaining in this file is unauthenticated_policy (optional):## unauthenticated_policy:# rules:# - effect: allow# scopes: [sign, public_keys]
# OpenTelemetry OTLP tracing export# opentelemetry:# enabled: false# endpoint: "http://localhost:4317"# service_name: "containment-chamber"
# Logging# logging:# level: "info"# format: "text"# log_color: nullConfiguration Layering
Section titled “Configuration Layering”Configuration values are resolved in the following precedence order (highest wins):
- CLI flags — e.g.,
--server-listen-port 9001 - Environment variables — e.g.,
CONTAINMENT_SERVER__LISTEN_PORT=9001 - Config file — e.g.,
config.yaml - Built-in defaults
To start with a config file:
containment-chamber server -c config.yamlOr use CLI flags directly:
containment-chamber server \ --key-sources-filesystem-paths ./keystores/raw \ --key-sources-filesystem-paths ./keystores/pbkdf2 \ --anti-slashing-backend sqlite \ --anti-slashing-sqlite-path ./slashing.sqliteEnvironment Variables
Section titled “Environment Variables”All configuration options can be set via environment variables using the CONTAINMENT_ prefix. Nested keys use __ (double underscore) as the separator.
# server.listen_port → CONTAINMENT_SERVER__LISTEN_PORTCONTAINMENT_SERVER__LISTEN_PORT=9001
# server.listen_address → CONTAINMENT_SERVER__LISTEN_ADDRESSCONTAINMENT_SERVER__LISTEN_ADDRESS="127.0.0.1"
# anti_slashing.url → CONTAINMENT_ANTI_SLASHING__URLCONTAINMENT_ANTI_SLASHING__URL="postgresql://user:pass@db/slashing"
# anti_slashing.backend → CONTAINMENT_ANTI_SLASHING__BACKENDCONTAINMENT_ANTI_SLASHING__BACKEND=postgres
# network → CONTAINMENT_NETWORKCONTAINMENT_NETWORK=mainnetServer
Section titled “Server”HTTP server settings for the signing API.
server: listen_address: "0.0.0.0" listen_port: 9000 seccomp: false # Enable seccomp syscall filter (Linux only). Default: falseServer
Section titled “Server”| Option | Type | Default | CLI Flag | Env Var | Description |
|---|---|---|---|---|---|
server.listen_address | string | "0.0.0.0" | --server-listen-address | CONTAINMENT_SERVER__LISTEN_ADDRESS | Bind address for the HTTP signing server. |
server.listen_port | integer | 9000 | --server-listen-port | CONTAINMENT_SERVER__LISTEN_PORT | Port for the HTTP signing server. |
server.request_timeout_seconds | integer | 30 | --server-request-timeout-seconds | CONTAINMENT_SERVER__REQUEST_TIMEOUT_SECONDS | Per-request timeout for signing HTTP handlers. |
server.graceful_shutdown_timeout_seconds | integer | 25 | --server-graceful-shutdown-timeout-seconds | CONTAINMENT_SERVER__GRACEFUL_SHUTDOWN_TIMEOUT_SECONDS | Graceful shutdown timeout before force-closing requests. |
server.cors_allowed_origins | list<string> | null | null | --server-cors-allowed-origins | CONTAINMENT_SERVER__CORS_ALLOWED_ORIGINS |
server.max_request_body_bytes | integer | 2097152 | “ | CONTAINMENT_SERVER__MAX_REQUEST_BODY_BYTES | Maximum request body size in bytes (2 MiB default). Requests exceeding this limit are rejected with 413. |
server.seccomp | boolean | false | “ | CONTAINMENT_SERVER__SECCOMP | Enable seccomp system call filter on Linux. |
Metrics
Section titled “Metrics”| Option | Type | Default | CLI Flag | Env Var | Description |
|---|---|---|---|---|---|
metrics.listen_address | string | "0.0.0.0" | --metrics-listen-address | CONTAINMENT_METRICS__LISTEN_ADDRESS | Bind address for the Prometheus metrics server. |
metrics.listen_port | integer | 3000 | --metrics-listen-port | CONTAINMENT_METRICS__LISTEN_PORT | Port for the Prometheus metrics endpoint. |
metrics.refresh_interval_seconds | integer | 30 | --metrics-refresh-interval-seconds | CONTAINMENT_METRICS__REFRESH_INTERVAL_SECONDS | How often metrics are refreshed. |
Network
Section titled “Network”| Option | Type | Default | CLI Flag | Env Var | Description |
|---|---|---|---|---|---|
network | string | "mainnet" | --network | CONTAINMENT_NETWORK | Ethereum network name (mainnet, hoodi, sepolia). |
Key Sources — Filesystem
Section titled “Key Sources — Filesystem”| Option | Type | Default | Env Var | Description |
|---|---|---|---|---|
key_sources.filesystem.paths | list of strings | — | CONTAINMENT_KEY_SOURCES__FILESYSTEM__PATHS | Directories containing keystore configuration files. |
key_sources.filesystem.keystore_load_concurrency | integer | 128 | CONTAINMENT_KEY_SOURCES__FILESYSTEM__KEYSTORE_LOAD_CONCURRENCY | Parallel encrypted keystore decryption workers. |
key_sources.filesystem.raw_load_concurrency | integer | 128 | CONTAINMENT_KEY_SOURCES__FILESYSTEM__RAW_LOAD_CONCURRENCY | Parallel raw key loading workers. |
Key Sources — DynamoDB + KMS
Section titled “Key Sources — DynamoDB + KMS”| Option | Type | Default | Env Var | Description |
|---|---|---|---|---|
key_sources.dynamodb.table | string | — | CONTAINMENT_KEY_SOURCES__DYNAMODB__TABLE | DynamoDB table name for validator keys. |
key_sources.dynamodb.status_filter | list of strings | ["active"] | CONTAINMENT_KEY_SOURCES__DYNAMODB__STATUS_FILTER | Validator statuses to load from DynamoDB. |
key_sources.dynamodb.refresh_interval_seconds | integer | 1 | CONTAINMENT_KEY_SOURCES__DYNAMODB__REFRESH_INTERVAL_SECONDS | How often to refresh keys from DynamoDB (0 disables). |
key_sources.dynamodb.max_concurrent_reads | integer | 16 | CONTAINMENT_KEY_SOURCES__DYNAMODB__MAX_CONCURRENT_READS | Parallel DynamoDB read workers for key loading. |
Signer State
Section titled “Signer State”| Option | Type | Default | CLI Flag | Env Var | Description |
|---|---|---|---|---|---|
signer_state.backend | enum | — | --signer-state-backend | CONTAINMENT_SIGNER_STATE__BACKEND | Signer-state backend. Currently supports dynamodb. |
signer_state.table | string | — | --signer-state-dynamodb-table | CONTAINMENT_SIGNER_STATE__TABLE | DynamoDB table name for signer state (master key shares, unseal ceremony, operator credentials). Required when key_sources.dynamodb is configured. |
signer_state.refresh_interval_seconds | integer | 1 | --signer-state-refresh-interval-seconds | CONTAINMENT_SIGNER_STATE__REFRESH_INTERVAL_SECONDS | How often each instance checks state rows and reloads auth policies/tokens. Set to 0 to disable both state and auth refresh. |
signer_state.unseal_timeout_minutes | integer | 30 | --signer-state-unseal-timeout-minutes | CONTAINMENT_SIGNER_STATE__UNSEAL_TIMEOUT_MINUTES | How long operators have to submit all unseal shares before progress resets. Set to 0 for no timeout. |
Concurrency
Section titled “Concurrency”| Option | Type | Default | CLI Flag | Env Var | Description |
|---|---|---|---|---|---|
signing.max_concurrent_jobs | integer | 500 | --signing-max-concurrent-jobs | CONTAINMENT_SIGNING__MAX_CONCURRENT_JOBS | Maximum concurrent signing operations. |
signing.queue_buffer_size | integer | 10000 | --signing-queue-buffer-size | CONTAINMENT_SIGNING__QUEUE_BUFFER_SIZE | Queue buffer size for incoming signing requests. |
signing.priority.enabled | boolean | false | --signing-priority-enabled | CONTAINMENT_SIGNING__PRIORITY__ENABLED | Enable priority signing pool for high-priority operations. |
signing.priority.concurrency | integer | 50 | --signing-priority-concurrency | CONTAINMENT_SIGNING__PRIORITY__CONCURRENCY | Concurrency limit for priority signing pool. |
signing.priority.operations | list of strings | [] | --signing-priority-operations | CONTAINMENT_SIGNING__PRIORITY__OPERATIONS | List of operation types to route to priority pool (e.g., BLOCK_V2). |
Anti-Slashing
Section titled “Anti-Slashing”| Option | Type | Default | CLI Flag | Env Var | Description |
|---|---|---|---|---|---|
anti_slashing.backend | enum | "sqlite" | --anti-slashing-backend | CONTAINMENT_ANTI_SLASHING__BACKEND | Anti-slashing backend (noop, sqlite, postgres, dynamodb). |
anti_slashing.path | string | "./slashing_protection.sqlite" | --anti-slashing-sqlite-path | CONTAINMENT_ANTI_SLASHING__PATH | SQLite database path (sqlite backend only). |
anti_slashing.url | string | — | --anti-slashing-postgres-url | CONTAINMENT_ANTI_SLASHING__URL | PostgreSQL connection URL (postgres backend only). |
anti_slashing.pool_size | integer | 16 | --anti-slashing-postgres-pool-size | CONTAINMENT_ANTI_SLASHING__POOL_SIZE | PostgreSQL connection pool size. |
anti_slashing.force_ipv4 | boolean | false | --anti-slashing-postgres-force-ipv4 | CONTAINMENT_ANTI_SLASHING__FORCE_IPV4 | Force PostgreSQL DNS resolution to IPv4 only. |
anti_slashing.table | string | — | --anti-slashing-dynamodb-table | CONTAINMENT_ANTI_SLASHING__TABLE | DynamoDB table name (dynamodb backend only). |
Key Manager API
Section titled “Key Manager API”| Option | Type | Default | Env Var | Description |
|---|---|---|---|---|
http.key_manager_api.enabled | boolean | false | CONTAINMENT_HTTP__KEY_MANAGER_API__ENABLED | Enable the Key Manager API. |
http.key_manager_api.max_concurrent_reads | integer | 50 | CONTAINMENT_HTTP__KEY_MANAGER_API__MAX_CONCURRENT_READS | Maximum concurrent Key Manager API reads (GET/DELETE). |
http.key_manager_api.max_concurrent_writes | integer | 10 | CONTAINMENT_HTTP__KEY_MANAGER_API__MAX_CONCURRENT_WRITES | Maximum concurrent Key Manager API writes (POST). |
http.key_manager_api.request_timeout_seconds | integer | 30 | CONTAINMENT_HTTP__KEY_MANAGER_API__REQUEST_TIMEOUT_SECONDS | Key Manager API request timeout in seconds. |
http.key_manager_api.max_items_per_request | integer | 100 | CONTAINMENT_HTTP__KEY_MANAGER_API__MAX_ITEMS_PER_REQUEST | Maximum items per Key Manager API import/delete request. |
TEE — Nitro Enclave
Section titled “TEE — Nitro Enclave”| Option | Type | Default | Env Var | Description |
|---|---|---|---|---|
tee.platform | enum | "none" | CONTAINMENT_TEE__PLATFORM | TEE platform. nitro enables AWS Nitro Enclave mode; none (default) runs as a plain binary with no vsock or NSM attestation. When nitro, a tee.nitro: section must also be present. |
tee.nitro.aws.region | string | "us-east-1" | CONTAINMENT_TEE__NITRO__AWS__REGION | AWS region used inside the Nitro Enclave. Must be set explicitly because the EC2 IMDS endpoint (169.254.169.254) is unreachable from inside the enclave. |
tee.nitro.egress.endpoints | list of objects | [] | CONTAINMENT_TEE__NITRO__EGRESS__ENDPOINTS | Egress endpoints the enclave reaches via the parent’s vsock-proxy fleet. Each entry requires: hostname (string), loopback (unique IPv4 in 127.0.0.0/8, not 127.0.0.0/127.0.0.1/127.255.255.255), vsock_port (unique u16, must not collide with ingress or reserved ports 7000-7002), and optional port (default 443). |
OpenTelemetry
Section titled “OpenTelemetry”| Option | Type | Default | Env Var | Description |
|---|---|---|---|---|
opentelemetry.enabled | boolean | false | CONTAINMENT_OPENTELEMETRY__ENABLED | Enable OpenTelemetry OTLP export. |
opentelemetry.endpoint | string | "http://localhost:4317" | CONTAINMENT_OPENTELEMETRY__ENDPOINT | OTLP gRPC endpoint URL. |
opentelemetry.service_name | string | "containment-chamber" | CONTAINMENT_OPENTELEMETRY__SERVICE_NAME | Service name attached to traces. |
Logging
Section titled “Logging”| Option | Type | Default | Env Var | Description |
|---|---|---|---|---|
logging.level | string | "info" | CONTAINMENT_LOGGING__LEVEL | Log level filter (EnvFilter syntax supported). |
logging.format | enum | "text" | CONTAINMENT_LOGGING__FORMAT | Log output format: text or json. |
logging.log_color | boolean | auto | CONTAINMENT_LOGGING__LOG_COLOR | ANSI color output (auto-detected when unset). |
server: listen_address: "0.0.0.0" listen_port: 9000Metrics
Section titled “Metrics”Prometheus metrics endpoint configuration. Metrics are served on a separate port from the signing API.
metrics: listen_address: "0.0.0.0" listen_port: 3000 refresh_interval_seconds: 30Network
Section titled “Network”The Ethereum network name. Used to validate genesis_validators_root on signing requests. Signing remains fork-agnostic because fork data comes from each request.
network: mainnetKey Sources
Section titled “Key Sources”Key sources control where validator keys are loaded from. You can use filesystem, DynamoDB, or both simultaneously.
Filesystem
Section titled “Filesystem”Paths to directories containing key configuration YAML files. Multiple directories can be specified to load keys from different locations or formats (raw hex, PBKDF2, Scrypt).
key_sources: filesystem: paths: - ./keystores/raw - ./keystores/pbkdf2 - ./keystores/scrypt keystore_load_concurrency: 128 raw_load_concurrency: 128DynamoDB + KMS
Section titled “DynamoDB + KMS”Store validator keys in AWS DynamoDB encrypted under the chamber master key. The master key is reconstructed from KMS-wrapped Shamir shares. Supports key generation and import via dedicated API endpoints.
key_sources: dynamodb: table: containment-keys refresh_interval_seconds: 1
signer_state: backend: dynamodb table: containment-state
chamber: keys: generate: enabled: false max_items_per_request: 100 backup: enabled: false recipients: [] # age public keys for offline mnemonic recovery import: enabled: false lifecycle: enabled: falseKMS key ARNs and the KMS threshold are submitted during the init ceremony, not stored in the config file. See DynamoDB Key Source and Seal & Unseal Guide for the full setup.
Concurrency
Section titled “Concurrency”Controls backpressure for signing request processing.
signing.max_concurrent_jobs— Maximum number of signing operations processed simultaneously.signing.queue_buffer_size— Size of the request queue buffer. Requests beyond this limit receive HTTP 503.signing.priority.enabled— Enable a dedicated priority pool for high-priority operations like block signing.signing.priority.concurrency— Concurrency limit for the priority pool.signing.priority.operations— List of operation types to route to the priority pool (e.g.,[BLOCK_V2]).
signing: max_concurrent_jobs: 2000 queue_buffer_size: 4000 # priority: # enabled: true # concurrency: 50 # operations: [BLOCK_V2]Anti-Slashing
Section titled “Anti-Slashing”EIP-3076 slashing protection backend configuration. PostgreSQL is recommended for production deployments as it supports multi-instance setups with full surround vote detection.
Backend Options
Section titled “Backend Options”| Backend | Value | Multi-Instance | Surround Detection | Use Case |
|---|---|---|---|---|
| PostgreSQL | postgres | ✅ | ✅ | Production (recommended) |
| SQLite | sqlite | ❌ | ✅ | Development / single instance |
| DynamoDB | dynamodb | ✅ | ✅ (implicit) | AWS deployments |
| Noop | noop | — | — | Testing only |
PostgreSQL (recommended)
Section titled “PostgreSQL (recommended)”anti_slashing: backend: postgres url: "postgresql://user:password@localhost:5432/slashing?sslmode=require" pool_size: 64 force_ipv4: falseTLS is enabled by default. Append ?sslmode=disable to the URL to disable it. The AWS RDS CA bundle is included in the Docker image.
SQLite
Section titled “SQLite”anti_slashing: backend: sqlite path: ./slashing_protection.sqliteanti_slashing: backend: noopKey Manager API
Section titled “Key Manager API”Hot-load and remove EIP-2335 keystores at runtime via the /eth/v1/keystores endpoint. Disabled by default.
http: key_manager_api: enabled: trueAccess to Key Manager API routes (/eth/v1/keystores) is controlled by auth policies (managed via the /api/v1/auth/ API) using the list_keys, import_keystores, and delete_keys scopes. See Auth Policies below.
Auth Policies
Section titled “Auth Policies”Auth policies are managed via the /api/v1/auth/ API, not in the config file. Policies control access to all routes. When policies exist, requests must include an Authorization: Bearer <token> header. Tokens are bound to one or more policies.
Bootstrap a management token via the containment-chamber operator generate-root-token ceremony. The management token has full access to all scopes and can be used to create policies and client tokens via the API.
For the complete API reference, policy evaluation rules, and detailed examples, see the Auth Policies guide.
Unauthenticated Policy (config-file-based)
Section titled “Unauthenticated Policy (config-file-based)”Uses the same PolicyRule model as API-managed policies — one representation everywhere.
Example: allow unauthenticated signing but block voluntary exits:
unauthenticated_policy: rules: - effect: allow scopes: [sign, public_keys] - effect: deny operations: [VOLUNTARY_EXIT]API Scopes
Section titled “API Scopes”All available scopes for use in policy rules:
sign, public_keys, list_keys, import_keystores, delete_keys, chamber_keys_generate, chamber_keys_import, chamber_keys_list, chamber_keys_patch, chamber_keys_delete, chamber_seal, chamber_rotate, chamber_status, auth_policies_manage, auth_tokens_manage
Valid Signing Operation Names
Section titled “Valid Signing Operation Names”AGGREGATION_SLOT, AGGREGATE_AND_PROOF, ATTESTATION, BLOCK_V2, RANDAO_REVEAL, SYNC_COMMITTEE_CONTRIBUTION_AND_PROOF, SYNC_COMMITTEE_MESSAGE, SYNC_COMMITTEE_SELECTION_PROOF, VALIDATOR_REGISTRATION, VOLUNTARY_EXIT
Canary Keys
Section titled “Canary Keys”Canary keys are validator public keys that should never sign in normal operation. When a canary key signs, the signer logs a warning and increments the containment_canary_signing_total metric. Signing is not blocked.
canary_keys: - "0x1234..." - "0x5678..."See Production Hardening for usage guidance.
DynamoDB Unseal Timeout
Section titled “DynamoDB Unseal Timeout”When using the DynamoDB key source with the seal/unseal feature, unseal_timeout_minutes controls how long a partial unseal (shares submitted but threshold not yet met) stays valid before expiring. Expired partial unseals require operators to resubmit their shares.
signer_state: backend: dynamodb table: containment-state unseal_timeout_minutes: 30 # 0 = no timeoutSee Seal & Unseal for the full init ceremony and operational procedures.
OpenTelemetry
Section titled “OpenTelemetry”OpenTelemetry OTLP tracing export. Disabled by default.
opentelemetry: enabled: true endpoint: "http://localhost:4317" service_name: "containment-chamber"The endpoint should point to an OTLP gRPC collector (e.g., Jaeger, Grafana Tempo, or the OpenTelemetry Collector).
Logging
Section titled “Logging”Structured logging configuration for console output.
logging: level: "info" format: "text" log_color: trueHTTPS listener configuration. TLS is disabled by default. When enabled, a second listener starts on port 9443 alongside the existing HTTP listener on port 9000.
Two modes are available:
file— reads a PEM certificate and private key from disk, polling for changes on a configurable interval. Works with any PKI: self-signed certs, cert-manager, Vault PKI.atls— generates an ephemeral certificate bound to an NSM attestation document. For AWS Nitro Enclave deployments only.
File mode
Section titled “File mode”tls: mode: file listen_port: 9443 file: cert_path: /etc/certs/tls.crt key_path: /etc/certs/tls.key reload_interval_seconds: 60 # 0 = disable pollingAttestation TLS mode
Section titled “Attestation TLS mode”tls: mode: atls listen_port: 9443 atls: cert_validity_seconds: 86400 # 24 hours rotation_interval_seconds: 3600 # rotate every hourSee Attested TLS for the full setup guide, including cert rotation and client-side attestation verification.
Scaling Recommendations
Section titled “Scaling Recommendations”Recommended configuration values by validator count:
| Parameter | 1K validators | 10K validators | 100K+ validators |
|---|---|---|---|
signing.max_concurrent_jobs | 100–500 | 500–2000 | 2000+ (default: 2000) |
signing.queue_buffer_size | 1000–3000 | 3000–4000 | 4000+ (default: 4000) |
anti_slashing.postgres.pool_size | 16–32 | 32–64 | 64+ (default: 64) |
| CPU cores | 1–2 | 2–4 | 4–8+ |
Anti-Slashing Backend at Scale
Section titled “Anti-Slashing Backend at Scale”For 100K+ validators, DynamoDB anti-slashing is recommended over PostgreSQL:
- Multi-instance safe (no single point of failure)
- No connection pool limits
- Scales automatically with DynamoDB on-demand billing
DynamoDB Shard Count
Section titled “DynamoDB Shard Count”The DynamoDB key source uses 16 shards for parallel key loading. This is a compile-time constant and cannot be changed via configuration. 16 shards supports up to ~100K keys efficiently.
Key Refresh Interval
Section titled “Key Refresh Interval”For large deployments, set key_sources.dynamodb.refresh_interval_seconds to a non-zero value to periodically reload keys added via the API without restarting:
key_sources: dynamodb: refresh_interval_seconds: 1 # reload every secondCeremony CIDR Filter (SPEC-AUTH-001)
Section titled “Ceremony CIDR Filter (SPEC-AUTH-001)”The seven unauthenticated chamber ceremony endpoints (init, unseal, unseal/init, unseal/register, unseal/credentials, generate-root-token, attestation) can be restricted to a CIDR allowlist via server.ceremony_allowed_cidrs.
server: ceremony_allowed_cidrs: - "10.0.0.0/8" # operator VPC - "192.168.0.0/16" # staging bastionValidation is strict: 0.0.0.0/0, ::/0, IPv4 prefixes shorter than /8, and IPv6 prefixes shorter than /16 are rejected at startup.
An empty list is backward-compatible — no filter is applied. In that mode the server emits a tracing::warn! event at startup:
ceremony CIDR filter disabled (ceremony_allowed_cidrs is empty); ceremony routes are reachable from any source IPThe containment_ceremony_cidr_filter_enabled gauge reports 0 when empty, 1 otherwise — wire an alert if you expect the filter to always be active.
Rejections are logged (never exposed to the caller) as a structured audit event with outcome="ceremony_cidr_reject" and counted in containment_cidr_rejections_total{layer="ceremony",reason=...}.
Token-Bound CIDRs (SPEC-AUTH-001 REQ-006)
Section titled “Token-Bound CIDRs (SPEC-AUTH-001 REQ-006)”Each client auth token may carry a token_bound_cidrs list. When non-empty, the token is only usable from a TCP peer IP in the listed ranges. Enforcement runs inside AuthContext::require_scope / require_signing / filter_public_keys, so every route gated by one of the 15 ApiScopes — plus lookup-self and renew-self — is CIDR-enforced automatically.
CIDR binding is supplied in the token creation request body, not as a CLI flag. See the Auth guide and API Reference for the request schema.
Rules:
- Management (root) tokens are never CIDR-bound — they are the break-glass path and binding them would block operator recovery.
- Authorization uses the raw TCP peer IP only.
X-Forwarded-Foris not consulted for authz decisions (it continues to driveclient_ip=in audit logs). A leaked token cannot escape its CIDR binding by forgingX-Forwarded-For. - IPv4-mapped IPv6 peers (
::ffff:a.b.c.d) are canonicalized before matching so a token bound to10.0.0.0/8matches both10.0.1.5and::ffff:10.0.1.5. validate_safe_cidrsapplies totoken_bound_cidrsat mint time:0.0.0.0/0,::/0, IPv4/<8, IPv6/<16are rejected with400 Bad Request.
Recommended refresh interval for security-sensitive deployments: keep the default signer_state.refresh_interval_seconds = 1 so a narrowed CIDR binding (implemented today as revoke + re-mint) is effective within about one second across the cluster.