Skip to content

Configuration Reference

Containment Chamber is configured through a YAML file, environment variables, and CLI flags. This page documents every available option.

A fully commented example configuration file is available at config.example.yaml. Copy it and customize for your deployment:

Terminal window
curl -O https://raw.githubusercontent.com/unforeseen-consequences/containment-chamber/main/config.example.yaml
cp config.example.yaml config.yaml
# Edit config.yaml to match your setup
containment-chamber -c config.yaml
Full 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 settings
server:
listen_address: "0.0.0.0" # CLI: --server-listen-address
listen_port: 9000 # CLI: --server-listen-port
# Metrics endpoint
metrics:
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: --network
network: mainnet
# Key sources: filesystem, DynamoDB, or both
key_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-jobs
max_concurrent_signing_jobs: 100
# Signing queue buffer size
# CLI: --signing-queue-buffer-size
signing_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: null

Configuration values are resolved in the following precedence order (highest wins):

  1. CLI flags — e.g., --server-listen-port 9001
  2. Environment variables — e.g., CONTAINMENT_SERVER__LISTEN_PORT=9001
  3. Config file — e.g., config.yaml
  4. Built-in defaults

To start with a config file:

Terminal window
containment-chamber -c config.yaml

Or use CLI flags directly:

Terminal window
containment-chamber \
--filesystem-keystores-path ./keystores/raw \
--filesystem-keystores-path ./keystores/pbkdf2 \
--antislashing sqlite \
--antislashing-sqlite-path ./slashing.sqlite

All configuration options can be set via environment variables using the CONTAINMENT_ prefix. Nested keys use __ (double underscore) as the separator.

Terminal window
# server.listen_port → CONTAINMENT_SERVER__LISTEN_PORT
CONTAINMENT_SERVER__LISTEN_PORT=9001
# server.listen_address → CONTAINMENT_SERVER__LISTEN_ADDRESS
CONTAINMENT_SERVER__LISTEN_ADDRESS="127.0.0.1"
# antislashing.url → CONTAINMENT_ANTISLASHING__URL
CONTAINMENT_ANTISLASHING__URL="postgresql://user:pass@db/slashing"
# antislashing.backend → CONTAINMENT_ANTISLASHING__BACKEND
CONTAINMENT_ANTISLASHING__BACKEND=postgres
# network → CONTAINMENT_NETWORK
CONTAINMENT_NETWORK=mainnet

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: false
OptionTypeDefaultCLI FlagEnv VarDescription
server.listen_addressstring"0.0.0.0"--server-listen-addressCONTAINMENT_SERVER__LISTEN_ADDRESSBind address for the HTTP signing server.
server.listen_portinteger9000--server-listen-portCONTAINMENT_SERVER__LISTEN_PORTPort for the HTTP signing server.
server.request_timeout_secondsinteger30--server-request-timeout-secondsCONTAINMENT_SERVER__REQUEST_TIMEOUT_SECONDSPer-request timeout for signing HTTP handlers.
server.graceful_shutdown_timeout_secondsinteger25--server-graceful-shutdown-timeout-secondsCONTAINMENT_SERVER__GRACEFUL_SHUTDOWN_TIMEOUT_SECONDSGraceful shutdown timeout before force-closing requests.
server.cors_allowed_originslist<string>nullnull--cors-allowed-originsCONTAINMENT_SERVER__CORS_ALLOWED_ORIGINS
OptionTypeDefaultCLI FlagEnv VarDescription
metrics.listen_addressstring"0.0.0.0"--metrics-listen-addressCONTAINMENT_METRICS__LISTEN_ADDRESSBind address for the Prometheus metrics server.
metrics.listen_portinteger3000--metrics-listen-portCONTAINMENT_METRICS__LISTEN_PORTPort for the Prometheus metrics endpoint.
metrics.refresh_interval_secondsinteger30--metrics-refresh-intervalCONTAINMENT_METRICS__REFRESH_INTERVAL_SECONDSHow often metrics are refreshed.
OptionTypeDefaultCLI FlagEnv VarDescription
networkstring"mainnet"--networkCONTAINMENT_NETWORKEthereum network name (mainnet, hoodi, sepolia).
OptionTypeDefaultEnv VarDescription
key_sources.filesystem.pathslist of stringsCONTAINMENT_KEY_SOURCES__FILESYSTEM__PATHSDirectories containing keystore configuration files.
key_sources.filesystem.keystore_load_concurrencyinteger128CONTAINMENT_KEY_SOURCES__FILESYSTEM__KEYSTORE_LOAD_CONCURRENCYParallel encrypted keystore decryption workers.
key_sources.filesystem.raw_load_concurrencyinteger128CONTAINMENT_KEY_SOURCES__FILESYSTEM__RAW_LOAD_CONCURRENCYParallel raw key loading workers.
OptionTypeDefaultEnv VarDescription
key_sources.dynamodb.tablestringCONTAINMENT_KEY_SOURCES__DYNAMODB__TABLEDynamoDB table name for validator keys.
key_sources.dynamodb.status_filterlist of strings["active"]CONTAINMENT_KEY_SOURCES__DYNAMODB__STATUS_FILTERValidator statuses to load from DynamoDB.
key_sources.dynamodb.key_refresh_interval_minutesinteger10CONTAINMENT_KEY_SOURCES__DYNAMODB__KEY_REFRESH_INTERVAL_MINUTESHow often to refresh keys from DynamoDB (0 disables).
key_sources.dynamodb.max_concurrent_readsinteger16CONTAINMENT_KEY_SOURCES__DYNAMODB__MAX_CONCURRENT_READSParallel DynamoDB read workers for key loading.
key_sources.dynamodb.keygen.backup.enabledboolfalseCONTAINMENT_KEY_SOURCES__DYNAMODB__KEYGEN__BACKUP__ENABLEDEnable age-encrypted mnemonic backup during key generation.
key_sources.dynamodb.keygen.backup.recipientslist of stringsCONTAINMENT_KEY_SOURCES__DYNAMODB__KEYGEN__BACKUP__RECIPIENTSage public key recipients for mnemonic backup encryption.
key_sources.dynamodb.keygen.enabledbooleanfalseCONTAINMENT_KEY_SOURCES__DYNAMODB__KEYGEN__ENABLEDEnable the keygen endpoint for DynamoDB-backed keys.
key_sources.dynamodb.keygen.max_items_per_requestinteger100CONTAINMENT_KEY_SOURCES__DYNAMODB__KEYGEN__MAX_ITEMS_PER_REQUESTMaximum keys per keygen request.
key_sources.dynamodb.keygen.max_concurrent_keygeninteger16CONTAINMENT_KEY_SOURCES__DYNAMODB__KEYGEN__MAX_CONCURRENT_KEYGENParallel keygen workers.
key_sources.dynamodb.keygen.request_timeout_secondsinteger600CONTAINMENT_KEY_SOURCES__DYNAMODB__KEYGEN__REQUEST_TIMEOUT_SECONDSKeygen request timeout in seconds.
key_sources.dynamodb.writable.enabledbooleanfalseCONTAINMENT_KEY_SOURCES__DYNAMODB__WRITABLE__ENABLEDEnable DynamoDB key import/list/delete endpoints.
key_sources.dynamodb.writable.max_concurrent_writesinteger16CONTAINMENT_KEY_SOURCES__DYNAMODB__WRITABLE__MAX_CONCURRENT_WRITESParallel DynamoDB write workers for import/delete.
key_sources.dynamodb.writable.request_timeout_secondsinteger600CONTAINMENT_KEY_SOURCES__DYNAMODB__WRITABLE__REQUEST_TIMEOUT_SECONDSImport request timeout in seconds.
key_sources.dynamodb.writable.max_items_per_requestinteger100CONTAINMENT_KEY_SOURCES__DYNAMODB__WRITABLE__MAX_ITEMS_PER_REQUESTMaximum keys per import request.
OptionTypeDefaultCLI FlagEnv VarDescription
max_concurrent_signing_jobsinteger500--max-concurrent-signing-jobsCONTAINMENT_MAX_CONCURRENT_SIGNING_JOBSMaximum concurrent signing operations.
signing_queue_buffer_sizeinteger10000--signing-queue-buffer-sizeCONTAINMENT_SIGNING_QUEUE_BUFFER_SIZEQueue buffer size for incoming signing requests.
OptionTypeDefaultCLI FlagEnv VarDescription
antislashing.backendenum"sqlite"--antislashingCONTAINMENT_ANTISLASHING__BACKENDAnti-slashing backend (noop, sqlite, postgres, dynamodb).
antislashing.pathstring"./slashing_protection.sqlite"--antislashing-sqlite-pathCONTAINMENT_ANTISLASHING__PATHSQLite database path (sqlite backend only).
antislashing.urlstring--antislashing-postgres-urlCONTAINMENT_ANTISLASHING__URLPostgreSQL connection URL (postgres backend only).
antislashing.pool_sizeinteger16--antislashing-postgres-pool-sizeCONTAINMENT_ANTISLASHING__POOL_SIZEPostgreSQL connection pool size.
antislashing.force_ipv4booleanfalse--antislashing-postgres-force-ipv4CONTAINMENT_ANTISLASHING__FORCE_IPV4Force PostgreSQL DNS resolution to IPv4 only.
antislashing.tablestring--antislashing-dynamodb-tableCONTAINMENT_ANTISLASHING__TABLEDynamoDB table name (dynamodb backend only).
OptionTypeDefaultEnv VarDescription
key_sources.key_manager_api.enabledbooleanfalseCONTAINMENT_KEY_SOURCES__KEY_MANAGER_API__ENABLEDEnable the Key Manager API.
key_sources.key_manager_api.max_concurrent_readsinteger50CONTAINMENT_KEY_SOURCES__KEY_MANAGER_API__MAX_CONCURRENT_READSMaximum concurrent Key Manager API reads (GET/DELETE).
key_sources.key_manager_api.max_concurrent_writesinteger10CONTAINMENT_KEY_SOURCES__KEY_MANAGER_API__MAX_CONCURRENT_WRITESMaximum concurrent Key Manager API writes (POST).
key_sources.key_manager_api.request_timeout_secondsinteger30CONTAINMENT_KEY_SOURCES__KEY_MANAGER_API__REQUEST_TIMEOUT_SECONDSKey Manager API request timeout in seconds.
key_sources.key_manager_api.max_items_per_requestinteger100CONTAINMENT_KEY_SOURCES__KEY_MANAGER_API__MAX_ITEMS_PER_REQUESTMaximum items per Key Manager API import/delete request.
OptionTypeDefaultEnv VarDescription
auth_policiesmapCONTAINMENT_AUTH_POLICIESNamed authentication policies keyed by policy name.
auth_policies.<policy>.tokenstringCONTAINMENT_AUTH_POLICIES__<POLICY>__TOKENBasic auth token for the policy (supports env: prefix).
auth_policies.<policy>.allowed_keyslist of stringsCONTAINMENT_AUTH_POLICIES__<POLICY>__ALLOWED_KEYSPublic keys allowed for the policy (omit to allow all).
auth_policies.<policy>.allowed_scopeslist of stringsCONTAINMENT_AUTH_POLICIES__<POLICY>__ALLOWED_SCOPESAPI scopes allowed for the policy.
auth_policies.<policy>.denied_scopeslist of stringsCONTAINMENT_AUTH_POLICIES__<POLICY>__DENIED_SCOPESAPI scopes denied for the policy.
auth_policies.<policy>.allowed_signing_operationslist of stringsCONTAINMENT_AUTH_POLICIES__<POLICY>__ALLOWED_SIGNING_OPERATIONSSigning operations allowed for the policy.
auth_policies.<policy>.denied_signing_operationslist of stringsCONTAINMENT_AUTH_POLICIES__<POLICY>__DENIED_SIGNING_OPERATIONSSigning operations denied for the policy.
unauthenticated_policyobjectCONTAINMENT_UNAUTHENTICATED_POLICYPolicy applied to requests without an auth token.
unauthenticated_policy.allowed_keyslist of stringsCONTAINMENT_UNAUTHENTICATED_POLICY__ALLOWED_KEYSAllowed keys for unauthenticated requests.
unauthenticated_policy.allowed_scopeslist of stringsCONTAINMENT_UNAUTHENTICATED_POLICY__ALLOWED_SCOPESAllowed scopes for unauthenticated requests.
unauthenticated_policy.denied_scopeslist of stringsCONTAINMENT_UNAUTHENTICATED_POLICY__DENIED_SCOPESDenied scopes for unauthenticated requests.
unauthenticated_policy.allowed_signing_operationslist of stringsCONTAINMENT_UNAUTHENTICATED_POLICY__ALLOWED_SIGNING_OPERATIONSAllowed signing operations for unauthenticated requests.
unauthenticated_policy.denied_signing_operationslist of stringsCONTAINMENT_UNAUTHENTICATED_POLICY__DENIED_SIGNING_OPERATIONSDenied signing operations for unauthenticated requests.
OptionTypeDefaultEnv VarDescription
opentelemetry.enabledbooleanfalseCONTAINMENT_OPENTELEMETRY__ENABLEDEnable OpenTelemetry OTLP export.
opentelemetry.endpointstring"http://localhost:4317"CONTAINMENT_OPENTELEMETRY__ENDPOINTOTLP gRPC endpoint URL.
opentelemetry.service_namestring"containment-chamber"CONTAINMENT_OPENTELEMETRY__SERVICE_NAMEService name attached to traces.
OptionTypeDefaultEnv VarDescription
logging.levelstring"info"CONTAINMENT_LOGGING__LEVELLog level filter (EnvFilter syntax supported).
logging.formatenum"text"CONTAINMENT_LOGGING__FORMATLog output format: text or json.
logging.log_colorbooleanautoCONTAINMENT_LOGGING__LOG_COLORANSI color output (auto-detected when unset).
server:
listen_address: "0.0.0.0"
listen_port: 9000

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: 30

The Ethereum network name. Used for logging only — signing is fork-agnostic.

network: mainnet

Key sources control where validator keys are loaded from. You can use filesystem, DynamoDB, or both simultaneously.

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: 128

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: 600

See DynamoDB + KMS Key Management for the full setup guide.


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: 100
signing_queue_buffer_size: 3000

EIP-3076 slashing protection backend configuration. PostgreSQL is recommended for production deployments as it supports multi-instance setups with full surround vote detection.

BackendValueMulti-InstanceSurround DetectionUse Case
PostgreSQLpostgresProduction (recommended)
SQLitesqliteDevelopment / single instance
DynamoDBdynamodb✅ (implicit)AWS deployments
NoopnoopTesting only
antislashing:
backend: postgres
url: "postgresql://user:password@localhost:5432/slashing?sslmode=require"
pool_size: 8
force_ipv4: false

TLS is enabled by default. Append ?sslmode=disable to the URL to disable it. The AWS RDS CA bundle is included in the Docker image.

antislashing:
backend: sqlite
path: ./slashing_protection.sqlite
antislashing:
backend: noop

Hot-load and remove EIP-2335 keystores at runtime via the /eth/v1/keystores endpoint. Disabled by default.

key_manager_api:
enabled: true

Access 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.


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:9000

When no auth_policies are configured (the default), all routes are open without authentication — Web3Signer compatible behavior.

ScopeRoutes
signPOST /api/v1/eth2/sign/{pubkey}
public_keysGET /api/v1/eth2/publicKeys
list_keysGET /eth/v1/keystores
import_keystoresPOST /eth/v1/keystores
delete_keysDELETE /eth/v1/keystores
chamber_keys_generatePOST /api/v1/chamber/keys/generate
chamber_keys_writePOST /api/v1/chamber/keys
chamber_keys_listGET /api/v1/chamber/keys
chamber_keys_deleteDELETE /api/v1/chamber/keys
  • 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 startup

If the referenced environment variable is not set, startup fails with a clear error.

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

auth_policies:
vc-default:
token: "env:VC_DEFAULT_TOKEN"
# No restrictions — full access to all scopes and operations
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..."
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"

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 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.


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 timeout

See Seal & Unseal for the full init ceremony and operational procedures.


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).


Structured logging configuration for console output.

logging:
level: "info"
format: "text"
log_color: true

Recommended configuration values by validator count:

Parameter1K validators10K validators100K+ validators
max_concurrent_signing_jobs100 (default: 500)500 (default)500–2000
signing_queue_buffer_size3000 (default: 10000)10000 (default)10000–50000
antislashing.postgres.pool_size816 (default)32–64
CPU cores1–22–44–8+

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

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.

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