Skip to content

Zero to Production

Go from installation to a production-ready signer in one guided path

This guide takes you from a fresh machine to a production-ready Containment Chamber signer. It links to detailed reference pages at each step so you can dive deeper without losing the thread.

If you only need a local development signer, follow the Quick Start instead. Quick Start is stateless and Web3Signer-compatible; this guide shows the state-backed path you need before relying on runtime auth policies, tokens, or chamber key generation.

Terminal window
docker pull ghcr.io/unforeseen-consequences/containment-chamber:latest
docker run --rm ghcr.io/unforeseen-consequences/containment-chamber:latest --version

Choose one method and verify the binary or container image works.

See Installation for build-from-source, cargo install, and shell completions.

ModelBest ForComplexityDocs
Docker ComposeSingle-node production, fastest pathLowDocker
KubernetesMulti-instance, automated rolloutsMediumKubernetes
Bare Metal + systemdMax control, existing metalMediumBare Metal
Nitro EnclaveAWS, hardware-rooted trustHighEnclave

This guide assumes Docker Compose for concreteness. Adapt the file paths and commands if you chose a different model.

Create a production-ready config.yaml:

server:
listen_address: "0.0.0.0"
listen_port: 9000
# Restrict ceremony endpoints to your admin network
ceremony_allowed_cidrs:
- "10.0.0.0/8"
key_sources:
filesystem:
paths:
- /keystores
signer_state:
backend: dynamodb
table: containment-state
refresh_interval_seconds: 1
anti_slashing:
backend: postgres
url: "postgresql://cc:env:DB_PASSWORD@postgres:5432/slashing"
metrics:
listen_address: "0.0.0.0"
listen_port: 3000

Create the DynamoDB state table before starting the signer, or omit signer_state and skip the auth/key-generation steps if you intentionally want a stateless Web3Signer-compatible deployment.

See Configuration Reference for every option.

Place your EIP-2335 keystores (JSON files) in a keystores/ directory alongside your config.yaml. The directory layout is:

keystores/
├── 0x8f1e...a2b3.json # keystore file
├── 0x8f1e...a2b3.txt # password file (same basename)
└── ...

This filesystem setup loads existing keys; it does not enable chamber-side key generation. To use /api/v1/chamber/keys/generate, configure DynamoDB Key Source plus signer_state, then enable chamber.keys.generate.enabled.

This step assumes the signer_state backend from the config above is available and the chamber has been initialized and unsealed. For a fresh state backend, run the Seal & Unseal Guide first. Then generate a management token and create a policy that restricts signing to specific validator keys:

Terminal window
# Generate the root management token
containment-chamber operator generate-root-token alice \
--signer-url http://localhost:9000 \
--allow-plaintext-signer
# Create a policy that allows signing only for your keys
containment-chamber operator auth policy create \
--name validator-signing \
--rules '[{"effect":"allow","scopes":["sign","public_keys"],"keys":["0x8f1e...a2b3","0xabcd...1234"]}]' \
--token env:ROOT_TOKEN \
--signer-url http://localhost:9000 \
--allow-plaintext-signer
# Create a token bound to that policy
containment-chamber operator auth token create \
--policies validator-signing \
--ttl-seconds 86400 \
--token env:ROOT_TOKEN \
--signer-url http://localhost:9000 \
--allow-plaintext-signer

Save the management token from generate-root-token as ROOT_TOKEN. Save the client token returned by auth token create for your validator client — token secrets are shown only once. In production, add --token-bound-cidrs <validator-egress-cidr> when the validator client’s source network is stable.

See Auth Policies & Tokens for the full model and additional scopes. For the request lifecycle and how policies are evaluated, see API Concepts.

Start the signer and its dependencies:

Terminal window
docker compose up -d

If you used the Docker Compose example, this also starts PostgreSQL and creates the slashing-protection database automatically.

Check health, loaded keys, and metrics:

Terminal window
# Liveness
curl http://localhost:9000/upcheck
# Loaded keys
curl -H "Authorization: Bearer $TOKEN" \
http://localhost:9000/api/v1/eth2/publicKeys
# Prometheus metrics
curl http://localhost:3000/metrics

Connect your validator client. For Lighthouse:

Terminal window
lighthouse vc \
--beacon-node http://localhost:5052 \
--web3-signer-url http://localhost:9000 \
--web3-signer-token $TOKEN

See Validator Clients for Teku, Prysm, Lodestar, and Nimbus examples.

Before you point real validators at this signer, complete the production hardening checklist:

See Production Hardening for the complete checklist.