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 -c config.yamlFull config.example.yaml
# Copy this file to config.yaml and customize for your deployment.## Usage:# containment-chamber -c config.yaml## CLI flags override values from this file.# Environment variables with CONTAINMENT_ prefix also override (use __ for nesting).# Example: CONTAINMENT_ANTISLASHING__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
# Ethereum signing configuration# Network name (for logging only — signing is 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 key_manager_api: enabled: false # 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 # key_refresh_interval_minutes: 10 # unseal_timeout_minutes: 30 # keygen: # enabled: false # max_items_per_request: 100 # writable: # enabled: false # max_concurrent_writes: 16 # request_timeout_seconds: 600
# Maximum concurrent signing jobs# CLI: --max-concurrent-signing-jobsmax_concurrent_signing_jobs: 100
# Signing queue buffer size# CLI: --signing-queue-buffer-sizesigning_queue_buffer_size: 3000
# Anti-slashing backend# postgres is recommended for production (multi-instance safe)antislashing: backend: postgres url: "postgresql://user:password@localhost:5432/slashing?sslmode=require" pool_size: 8 # 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:# antislashing:# backend: sqlite# path: ./slashing_protection.sqlite# antislashing:# backend: noop # WARNING: no slashing protection
# ──────────────────────────────────────────────────────────────# Key Manager API — hot-load/remove EIP-2335 keystores at runtime# Disabled by default.# ──────────────────────────────────────────────────────────────key_manager_api: enabled: false # max_concurrent: 10## To enable:# key_manager_api:# enabled: true## Access to /eth/v1/keystores is controlled by auth_policies (list_keys, import_keystores,# delete_keys scopes). Without auth_policies, the endpoint is open.## Usage:# curl http://localhost:9000/eth/v1/keystores # no auth# curl -u "x:my-secret-token" http://localhost:9000/eth/v1/keystores # with auth_policies
# ──────────────────────────────────────────────────────────────# Auth Policies (optional, disabled by default)# ──────────────────────────────────────────────────────────────## When configured, all routes require HTTP Basic auth.# Clients embed the token in the URL:# http://x:my-token@signer:9000## Each policy maps a token to allowed scopes, keys, and signing operations.# Omit 'allowed_scopes'/'denied_scopes' to allow all routes.# Omit 'allowed_keys' to allow all loaded keys (sign scope only).# Omit 'allowed_signing_operations'/'denied_signing_operations' to allow all signing ops.# Use allowed_* OR denied_* (not both) per field.## API scopes:# sign, public_keys, list_keys, import_keystores, delete_keys, chamber_keys_generate, chamber_keys_write, chamber_keys_list, chamber_keys_delete## 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## Token validation rules:# - Tokens must be at least 16 characters, alphanumeric + dashes only# - Tokens can reference environment variables using the "env:" prefix:# token: "env:MY_ENV_VAR" — resolved from $MY_ENV_VAR at startup# - If the env var is not set, startup will fail with a clear error## 1. Full access — all scopes, all keys, all operations:# auth_policies:# vc-default:# token: "env:VC_DEFAULT_TOKEN"## 2. Tenant isolation — sign scope only, specific keys:# auth_policies:# vc1:# token: "env:VC1_TOKEN"# allowed_scopes: [sign, public_keys]# allowed_keys:# - "0xabc..."# - "0xdef..."# vc2:# token: "env:VC2_TOKEN"# allowed_scopes: [sign, public_keys]# allowed_keys:# - "0x123..."## 3. Operator + signer separation:# auth_policies:# operator:# token: "env:OPERATOR_TOKEN"# # Full access — manages keys, triggers keygen, etc.# vc1:# token: "env:VC1_TOKEN"# allowed_scopes: [sign, public_keys]# denied_signing_operations: ["VOLUNTARY_EXIT"]# exit-admin:# token: "env:EXIT_ADMIN_TOKEN"# allowed_scopes: [sign]# allowed_signing_operations: ["VOLUNTARY_EXIT"]## 4. Unauthenticated policy — applies to requests without any auth token:# auth_policies:# exit-admin:# token: "env:EXIT_ADMIN_TOKEN"# allowed_scopes: [sign]# allowed_signing_operations: ["VOLUNTARY_EXIT"]# unauthenticated_policy:# allowed_scopes: [sign, public_keys]# denied_signing_operations: ["VOLUNTARY_EXIT"]## 5. Unauthenticated policy — allow all unauthenticated requests except exits:# unauthenticated_policy:# denied_signing_operations: ["VOLUNTARY_EXIT"]
# 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 -c config.yamlOr use CLI flags directly:
containment-chamber \ --filesystem-keystores-path ./keystores/raw \ --filesystem-keystores-path ./keystores/pbkdf2 \ --antislashing sqlite \ --antislashing-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"
# antislashing.url → CONTAINMENT_ANTISLASHING__URLCONTAINMENT_ANTISLASHING__URL="postgresql://user:pass@db/slashing"
# antislashing.backend → CONTAINMENT_ANTISLASHING__BACKENDCONTAINMENT_ANTISLASHING__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 | --cors-allowed-origins | CONTAINMENT_SERVER__CORS_ALLOWED_ORIGINS |
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 | 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.key_refresh_interval_minutes | integer | 10 | CONTAINMENT_KEY_SOURCES__DYNAMODB__KEY_REFRESH_INTERVAL_MINUTES | 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. |
key_sources.dynamodb.keygen.backup.enabled | bool | false | CONTAINMENT_KEY_SOURCES__DYNAMODB__KEYGEN__BACKUP__ENABLED | Enable age-encrypted mnemonic backup during key generation. |
key_sources.dynamodb.keygen.backup.recipients | list of strings | — | CONTAINMENT_KEY_SOURCES__DYNAMODB__KEYGEN__BACKUP__RECIPIENTS | age public key recipients for mnemonic backup encryption. |
key_sources.dynamodb.keygen.enabled | boolean | false | CONTAINMENT_KEY_SOURCES__DYNAMODB__KEYGEN__ENABLED | Enable the keygen endpoint for DynamoDB-backed keys. |
key_sources.dynamodb.keygen.max_items_per_request | integer | 100 | CONTAINMENT_KEY_SOURCES__DYNAMODB__KEYGEN__MAX_ITEMS_PER_REQUEST | Maximum keys per keygen request. |
key_sources.dynamodb.keygen.max_concurrent_keygen | integer | 16 | CONTAINMENT_KEY_SOURCES__DYNAMODB__KEYGEN__MAX_CONCURRENT_KEYGEN | Parallel keygen workers. |
key_sources.dynamodb.keygen.request_timeout_seconds | integer | 600 | CONTAINMENT_KEY_SOURCES__DYNAMODB__KEYGEN__REQUEST_TIMEOUT_SECONDS | Keygen request timeout in seconds. |
key_sources.dynamodb.writable.enabled | boolean | false | CONTAINMENT_KEY_SOURCES__DYNAMODB__WRITABLE__ENABLED | Enable DynamoDB key import/list/delete endpoints. |
key_sources.dynamodb.writable.max_concurrent_writes | integer | 16 | CONTAINMENT_KEY_SOURCES__DYNAMODB__WRITABLE__MAX_CONCURRENT_WRITES | Parallel DynamoDB write workers for import/delete. |
key_sources.dynamodb.writable.request_timeout_seconds | integer | 600 | CONTAINMENT_KEY_SOURCES__DYNAMODB__WRITABLE__REQUEST_TIMEOUT_SECONDS | Import request timeout in seconds. |
key_sources.dynamodb.writable.max_items_per_request | integer | 100 | CONTAINMENT_KEY_SOURCES__DYNAMODB__WRITABLE__MAX_ITEMS_PER_REQUEST | Maximum keys per import request. |
Concurrency
Section titled “Concurrency”| Option | Type | Default | CLI Flag | Env Var | Description |
|---|---|---|---|---|---|
max_concurrent_signing_jobs | integer | 500 | --max-concurrent-signing-jobs | CONTAINMENT_MAX_CONCURRENT_SIGNING_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. |
Anti-Slashing
Section titled “Anti-Slashing”| Option | Type | Default | CLI Flag | Env Var | Description |
|---|---|---|---|---|---|
antislashing.backend | enum | "sqlite" | --antislashing | CONTAINMENT_ANTISLASHING__BACKEND | Anti-slashing backend (noop, sqlite, postgres, dynamodb). |
antislashing.path | string | "./slashing_protection.sqlite" | --antislashing-sqlite-path | CONTAINMENT_ANTISLASHING__PATH | SQLite database path (sqlite backend only). |
antislashing.url | string | — | --antislashing-postgres-url | CONTAINMENT_ANTISLASHING__URL | PostgreSQL connection URL (postgres backend only). |
antislashing.pool_size | integer | 16 | --antislashing-postgres-pool-size | CONTAINMENT_ANTISLASHING__POOL_SIZE | PostgreSQL connection pool size. |
antislashing.force_ipv4 | boolean | false | --antislashing-postgres-force-ipv4 | CONTAINMENT_ANTISLASHING__FORCE_IPV4 | Force PostgreSQL DNS resolution to IPv4 only. |
antislashing.table | string | — | --antislashing-dynamodb-table | CONTAINMENT_ANTISLASHING__TABLE | DynamoDB table name (dynamodb backend only). |
Key Manager API
Section titled “Key Manager API”| Option | Type | Default | Env Var | Description |
|---|---|---|---|---|
key_sources.key_manager_api.enabled | boolean | false | CONTAINMENT_KEY_SOURCES__KEY_MANAGER_API__ENABLED | Enable the Key Manager API. |
key_sources.key_manager_api.max_concurrent_reads | integer | 50 | CONTAINMENT_KEY_SOURCES__KEY_MANAGER_API__MAX_CONCURRENT_READS | Maximum concurrent Key Manager API reads (GET/DELETE). |
key_sources.key_manager_api.max_concurrent_writes | integer | 10 | CONTAINMENT_KEY_SOURCES__KEY_MANAGER_API__MAX_CONCURRENT_WRITES | Maximum concurrent Key Manager API writes (POST). |
key_sources.key_manager_api.request_timeout_seconds | integer | 30 | CONTAINMENT_KEY_SOURCES__KEY_MANAGER_API__REQUEST_TIMEOUT_SECONDS | Key Manager API request timeout in seconds. |
key_sources.key_manager_api.max_items_per_request | integer | 100 | CONTAINMENT_KEY_SOURCES__KEY_MANAGER_API__MAX_ITEMS_PER_REQUEST | Maximum items per Key Manager API import/delete request. |
Auth Policies
Section titled “Auth Policies”| Option | Type | Default | Env Var | Description |
|---|---|---|---|---|
auth_policies | map | — | CONTAINMENT_AUTH_POLICIES | Named authentication policies keyed by policy name. |
auth_policies.<policy>.token | string | — | CONTAINMENT_AUTH_POLICIES__<POLICY>__TOKEN | Basic auth token for the policy (supports env: prefix). |
auth_policies.<policy>.allowed_keys | list of strings | — | CONTAINMENT_AUTH_POLICIES__<POLICY>__ALLOWED_KEYS | Public keys allowed for the policy (omit to allow all). |
auth_policies.<policy>.allowed_scopes | list of strings | — | CONTAINMENT_AUTH_POLICIES__<POLICY>__ALLOWED_SCOPES | API scopes allowed for the policy. |
auth_policies.<policy>.denied_scopes | list of strings | — | CONTAINMENT_AUTH_POLICIES__<POLICY>__DENIED_SCOPES | API scopes denied for the policy. |
auth_policies.<policy>.allowed_signing_operations | list of strings | — | CONTAINMENT_AUTH_POLICIES__<POLICY>__ALLOWED_SIGNING_OPERATIONS | Signing operations allowed for the policy. |
auth_policies.<policy>.denied_signing_operations | list of strings | — | CONTAINMENT_AUTH_POLICIES__<POLICY>__DENIED_SIGNING_OPERATIONS | Signing operations denied for the policy. |
unauthenticated_policy | object | — | CONTAINMENT_UNAUTHENTICATED_POLICY | Policy applied to requests without an auth token. |
unauthenticated_policy.allowed_keys | list of strings | — | CONTAINMENT_UNAUTHENTICATED_POLICY__ALLOWED_KEYS | Allowed keys for unauthenticated requests. |
unauthenticated_policy.allowed_scopes | list of strings | — | CONTAINMENT_UNAUTHENTICATED_POLICY__ALLOWED_SCOPES | Allowed scopes for unauthenticated requests. |
unauthenticated_policy.denied_scopes | list of strings | — | CONTAINMENT_UNAUTHENTICATED_POLICY__DENIED_SCOPES | Denied scopes for unauthenticated requests. |
unauthenticated_policy.allowed_signing_operations | list of strings | — | CONTAINMENT_UNAUTHENTICATED_POLICY__ALLOWED_SIGNING_OPERATIONS | Allowed signing operations for unauthenticated requests. |
unauthenticated_policy.denied_signing_operations | list of strings | — | CONTAINMENT_UNAUTHENTICATED_POLICY__DENIED_SIGNING_OPERATIONS | Denied signing operations for unauthenticated requests. |
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 for logging only — signing is fork-agnostic.
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 with one or more AWS KMS keys using Shamir’s Secret Sharing. Supports key generation and import via dedicated API endpoints.
key_sources: dynamodb: table: containment-keys kms_key_arns: - arn:aws:kms:us-east-1:111111111111:key/aaa... - arn:aws:kms:us-east-1:222222222222:key/bbb... kms_threshold: 2 key_refresh_interval_minutes: 10 keygen: enabled: false max_items_per_request: 100 backup: recipients: [] # age public keys for offline mnemonic recovery writable: enabled: false max_concurrent_writes: 16 request_timeout_seconds: 600See DynamoDB + KMS Key Management for the full setup guide.
Concurrency
Section titled “Concurrency”Controls backpressure for signing request processing.
max_concurrent_signing_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.
max_concurrent_signing_jobs: 100signing_queue_buffer_size: 3000Anti-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)”antislashing: backend: postgres url: "postgresql://user:password@localhost:5432/slashing?sslmode=require" pool_size: 8 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”antislashing: backend: sqlite path: ./slashing_protection.sqliteantislashing: 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.
key_manager_api: enabled: trueAccess to Key Manager API routes (/eth/v1/keystores) is controlled by auth_policies using the list_keys, import_keystores, and delete_keys scopes. See Auth Policies below.
Auth Policies
Section titled “Auth Policies”Unified access control for all routes. When configured, requests must include HTTP Basic auth. Clients embed the token in the URL:
http://x:my-token@signer:9000When no auth_policies are configured (the default), all routes are open without authentication — Web3Signer compatible behavior.
API Scopes
Section titled “API Scopes”| Scope | Routes |
|---|---|
sign | POST /api/v1/eth2/sign/{pubkey} |
public_keys | GET /api/v1/eth2/publicKeys |
list_keys | GET /eth/v1/keystores |
import_keystores | POST /eth/v1/keystores |
delete_keys | DELETE /eth/v1/keystores |
chamber_keys_generate | POST /api/v1/chamber/keys/generate |
chamber_keys_write | POST /api/v1/chamber/keys |
chamber_keys_list | GET /api/v1/chamber/keys |
chamber_keys_delete | DELETE /api/v1/chamber/keys |
Token Format
Section titled “Token Format”- Minimum 16 characters, alphanumeric and dashes only
- Supports environment variable references with the
env:prefix:
token: "env:MY_ENV_VAR" # resolved from $MY_ENV_VAR at startupIf the referenced environment variable is not set, startup fails with a clear error.
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
Example: Full Access
Section titled “Example: Full Access”auth_policies: vc-default: token: "env:VC_DEFAULT_TOKEN" # No restrictions — full access to all scopes and operationsExample: Tenant Isolation
Section titled “Example: Tenant Isolation”auth_policies: vc1: token: "env:VC1_TOKEN" allowed_scopes: - sign - public_keys allowed_keys: - "0xabc..." - "0xdef..." vc2: token: "env:VC2_TOKEN" allowed_scopes: - sign - public_keys allowed_keys: - "0x123..."Example: Operator + Signer Separation
Section titled “Example: Operator + Signer Separation”auth_policies: operator: token: "env:OPERATOR_TOKEN" # Full access — manages keys, triggers keygen, etc.
vc1: token: "env:VC1_TOKEN" allowed_scopes: - sign - public_keys denied_signing_operations: - "VOLUNTARY_EXIT"
exit-admin: token: "env:EXIT_ADMIN_TOKEN" allowed_scopes: - sign allowed_signing_operations: - "VOLUNTARY_EXIT"Unauthenticated Policy
Section titled “Unauthenticated Policy”The unauthenticated_policy applies to requests that arrive without any auth token. It supports the same fields as a named policy, except token.
auth_policies: exit-admin: token: "env:EXIT_ADMIN_TOKEN" allowed_scopes: - sign allowed_signing_operations: - "VOLUNTARY_EXIT"unauthenticated_policy: allowed_scopes: - sign - public_keys denied_signing_operations: - "VOLUNTARY_EXIT"For the complete behavior matrix and detailed policy evaluation rules, see the Auth Policies guide.
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.
key_sources: dynamodb: 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: trueScaling Recommendations
Section titled “Scaling Recommendations”Recommended configuration values by validator count:
| Parameter | 1K validators | 10K validators | 100K+ validators |
|---|---|---|---|
max_concurrent_signing_jobs | 100 (default: 500) | 500 (default) | 500–2000 |
signing_queue_buffer_size | 3000 (default: 10000) | 10000 (default) | 10000–50000 |
antislashing.postgres.pool_size | 8 | 16 (default) | 32–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.key_refresh_interval_minutes to a non-zero value to periodically reload keys added via the API without restarting:
key_sources: dynamodb: key_refresh_interval_minutes: 60 # reload every hour