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 server -c config.yaml
Full 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 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-seconds
# Ethereum signing configuration
# Network name used for genesis-validators-root validation. Signing remains 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
# 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 queues
signing:
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: 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 server -c config.yaml

Or use CLI flags directly:

Terminal window
containment-chamber server \
--key-sources-filesystem-paths ./keystores/raw \
--key-sources-filesystem-paths ./keystores/pbkdf2 \
--anti-slashing-backend sqlite \
--anti-slashing-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"
# anti_slashing.url → CONTAINMENT_ANTI_SLASHING__URL
CONTAINMENT_ANTI_SLASHING__URL="postgresql://user:pass@db/slashing"
# anti_slashing.backend → CONTAINMENT_ANTI_SLASHING__BACKEND
CONTAINMENT_ANTI_SLASHING__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--server-cors-allowed-originsCONTAINMENT_SERVER__CORS_ALLOWED_ORIGINS
server.max_request_body_bytesinteger2097152CONTAINMENT_SERVER__MAX_REQUEST_BODY_BYTESMaximum request body size in bytes (2 MiB default). Requests exceeding this limit are rejected with 413.
server.seccompbooleanfalseCONTAINMENT_SERVER__SECCOMPEnable seccomp system call filter on Linux.
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-interval-secondsCONTAINMENT_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.refresh_interval_secondsinteger1CONTAINMENT_KEY_SOURCES__DYNAMODB__REFRESH_INTERVAL_SECONDSHow 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.
OptionTypeDefaultCLI FlagEnv VarDescription
signer_state.backendenum--signer-state-backendCONTAINMENT_SIGNER_STATE__BACKENDSigner-state backend. Currently supports dynamodb.
signer_state.tablestring--signer-state-dynamodb-tableCONTAINMENT_SIGNER_STATE__TABLEDynamoDB table name for signer state (master key shares, unseal ceremony, operator credentials). Required when key_sources.dynamodb is configured.
signer_state.refresh_interval_secondsinteger1--signer-state-refresh-interval-secondsCONTAINMENT_SIGNER_STATE__REFRESH_INTERVAL_SECONDSHow 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_minutesinteger30--signer-state-unseal-timeout-minutesCONTAINMENT_SIGNER_STATE__UNSEAL_TIMEOUT_MINUTESHow long operators have to submit all unseal shares before progress resets. Set to 0 for no timeout.
OptionTypeDefaultCLI FlagEnv VarDescription
signing.max_concurrent_jobsinteger500--signing-max-concurrent-jobsCONTAINMENT_SIGNING__MAX_CONCURRENT_JOBSMaximum concurrent signing operations.
signing.queue_buffer_sizeinteger10000--signing-queue-buffer-sizeCONTAINMENT_SIGNING__QUEUE_BUFFER_SIZEQueue buffer size for incoming signing requests.
signing.priority.enabledbooleanfalse--signing-priority-enabledCONTAINMENT_SIGNING__PRIORITY__ENABLEDEnable priority signing pool for high-priority operations.
signing.priority.concurrencyinteger50--signing-priority-concurrencyCONTAINMENT_SIGNING__PRIORITY__CONCURRENCYConcurrency limit for priority signing pool.
signing.priority.operationslist of strings[]--signing-priority-operationsCONTAINMENT_SIGNING__PRIORITY__OPERATIONSList of operation types to route to priority pool (e.g., BLOCK_V2).
OptionTypeDefaultCLI FlagEnv VarDescription
anti_slashing.backendenum"sqlite"--anti-slashing-backendCONTAINMENT_ANTI_SLASHING__BACKENDAnti-slashing backend (noop, sqlite, postgres, dynamodb).
anti_slashing.pathstring"./slashing_protection.sqlite"--anti-slashing-sqlite-pathCONTAINMENT_ANTI_SLASHING__PATHSQLite database path (sqlite backend only).
anti_slashing.urlstring--anti-slashing-postgres-urlCONTAINMENT_ANTI_SLASHING__URLPostgreSQL connection URL (postgres backend only).
anti_slashing.pool_sizeinteger16--anti-slashing-postgres-pool-sizeCONTAINMENT_ANTI_SLASHING__POOL_SIZEPostgreSQL connection pool size.
anti_slashing.force_ipv4booleanfalse--anti-slashing-postgres-force-ipv4CONTAINMENT_ANTI_SLASHING__FORCE_IPV4Force PostgreSQL DNS resolution to IPv4 only.
anti_slashing.tablestring--anti-slashing-dynamodb-tableCONTAINMENT_ANTI_SLASHING__TABLEDynamoDB table name (dynamodb backend only).
OptionTypeDefaultEnv VarDescription
http.key_manager_api.enabledbooleanfalseCONTAINMENT_HTTP__KEY_MANAGER_API__ENABLEDEnable the Key Manager API.
http.key_manager_api.max_concurrent_readsinteger50CONTAINMENT_HTTP__KEY_MANAGER_API__MAX_CONCURRENT_READSMaximum concurrent Key Manager API reads (GET/DELETE).
http.key_manager_api.max_concurrent_writesinteger10CONTAINMENT_HTTP__KEY_MANAGER_API__MAX_CONCURRENT_WRITESMaximum concurrent Key Manager API writes (POST).
http.key_manager_api.request_timeout_secondsinteger30CONTAINMENT_HTTP__KEY_MANAGER_API__REQUEST_TIMEOUT_SECONDSKey Manager API request timeout in seconds.
http.key_manager_api.max_items_per_requestinteger100CONTAINMENT_HTTP__KEY_MANAGER_API__MAX_ITEMS_PER_REQUESTMaximum items per Key Manager API import/delete request.
OptionTypeDefaultEnv VarDescription
tee.platformenum"none"CONTAINMENT_TEE__PLATFORMTEE 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.regionstring"us-east-1"CONTAINMENT_TEE__NITRO__AWS__REGIONAWS 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.endpointslist of objects[]CONTAINMENT_TEE__NITRO__EGRESS__ENDPOINTSEgress 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).
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 to validate genesis_validators_root on signing requests. Signing remains fork-agnostic because fork data comes from each request.

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

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


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]

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
anti_slashing:
backend: postgres
url: "postgresql://user:password@localhost:5432/slashing?sslmode=require"
pool_size: 64
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.

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

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

http:
key_manager_api:
enabled: true

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

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

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

signer_state:
backend: dynamodb
table: containment-state
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

HTTPS 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.
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 polling
tls:
mode: atls
listen_port: 9443
atls:
cert_validity_seconds: 86400 # 24 hours
rotation_interval_seconds: 3600 # rotate every hour

See Attested TLS for the full setup guide, including cert rotation and client-side attestation verification.


Recommended configuration values by validator count:

Parameter1K validators10K validators100K+ validators
signing.max_concurrent_jobs100–500500–20002000+ (default: 2000)
signing.queue_buffer_size1000–30003000–40004000+ (default: 4000)
anti_slashing.postgres.pool_size16–3232–6464+ (default: 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.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 second

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 bastion

Validation 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 IP

The 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=...}.

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-For is not consulted for authz decisions (it continues to drive client_ip= in audit logs). A leaked token cannot escape its CIDR binding by forging X-Forwarded-For.
  • IPv4-mapped IPv6 peers (::ffff:a.b.c.d) are canonicalized before matching so a token bound to 10.0.0.0/8 matches both 10.0.1.5 and ::ffff:10.0.1.5.
  • validate_safe_cidrs applies to token_bound_cidrs at mint time: 0.0.0.0/0, ::/0, IPv4 /<8, IPv6 /<16 are rejected with 400 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.