Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
c46646d
first pass:
JasonVranek Jun 2, 2026
1ae26ba
modernize overview to reflect unified binary
JasonVranek Jun 2, 2026
82b55ef
fix broken link
JasonVranek Jun 2, 2026
c02da1a
replace occurences of module -> service
JasonVranek Jun 2, 2026
ead0342
add prop-commit-signing to sidebar
JasonVranek Jun 2, 2026
28a67c7
Update configuration.md
JasonVranek Jun 2, 2026
7bd433d
Update mux-key-loaders.md
JasonVranek Jun 2, 2026
4bd683c
use ethstaker relay list
JasonVranek Jun 2, 2026
b7cfefb
Update docker.md
JasonVranek Jun 3, 2026
bfebac7
Update binary.md
JasonVranek Jun 3, 2026
75d75e2
Update metrics.md
JasonVranek Jun 3, 2026
ff44583
Update troubleshooting.md
JasonVranek Jun 3, 2026
2933e2b
Delete commit-module.md
JasonVranek Jun 3, 2026
c48dab7
Delete custom-modules.md
JasonVranek Jun 3, 2026
1c0ff0c
Update commit-modules.md
JasonVranek Jun 3, 2026
e03355e
Update prop-commit-signing.md
JasonVranek Jun 3, 2026
41a97cc
Update extending-pbs.md
JasonVranek Jun 3, 2026
12f56de
Delete signer-api.md
JasonVranek Jun 3, 2026
06dc9d9
Update commit-modules.md
JasonVranek Jun 3, 2026
8a78082
Update sidebars.js
JasonVranek Jun 3, 2026
441f5f9
Update mux-key-loaders.md
JasonVranek Jun 3, 2026
16f0ea9
Update prop-commit-signing.md
JasonVranek Jun 3, 2026
53572d8
Update signer-api.yml
JasonVranek Jun 3, 2026
5cd41b3
Create consensus-key-sign.png
JasonVranek Jun 5, 2026
a286065
Create proxy-key-sign.png
JasonVranek Jun 5, 2026
aba58f4
allow localhost viewing of signer-api.yml
JasonVranek Jun 5, 2026
2235b84
allow swagger to be deployed on a forked repo for testing
JasonVranek Jun 5, 2026
2015307
trigger docs ci from main instead of stable
JasonVranek Jun 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: Docs
on:
push:
branches:
- stable
- main
# Review gh actions docs if you want to further define triggers, paths, etc
# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#on

Expand Down
195 changes: 179 additions & 16 deletions api/signer-api.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,41 @@ openapi: "3.1.1"
info:
title: Signer API
version: "0.2.0"
description: API that allows commit modules to request generic signatures from validators
description: |
API that allows commit modules to request generic signatures from validators.

## Authentication

All endpoints (except `/status`) require a Bearer JWT in the `Authorization` header.

### Module JWT claims

HS256-signed token with the module's pre-shared secret (`CB_SIGNER_JWT` env var):

- **module** (string, required) — Module `id` from `[[modules]]` in `cb-config.toml`.
- **route** (string, required) — Exact request path, e.g. `/signer/v1/get_pubkeys`.
- **exp** (integer, required) — UNIX expiry timestamp. Expires after 5 minutes.
- **payload_hash** (string, POST only) — Keccak-256 hash of the JSON request body, `0x`-prefixed. Prevents JWT replay attacks.

Refresh is client-side — the module generates a new JWT locally. No refresh endpoint.

### Admin JWT

Admin endpoints use a separate secret (`CB_SIGNER_ADMIN_JWT` env var) and include `admin: true` in claims.

### Rate limiting

Per-IP rate limit on JWT auth failures. Default: 3 failures within 5 minutes. Configurable via `[signer].jwt_auth_fail_limit` and `jwt_auth_fail_timeout_seconds` in `cb-config.toml`.
tags:
- name: Signer
- name: Management
paths:
/signer/v1/get_pubkeys:
get:
summary: Get a list of public keys for which signatures may be requested
description: >
description: |
This endpoint requires a valid JWT Bearer token.

The token **must include** the following claims:
- `exp` (integer): Expiration timestamp
- `route` (string): The route being requested (must be `/signer/v1/get_pubkeys` for this endpoint).
Expand Down Expand Up @@ -68,9 +92,9 @@ paths:
/signer/v1/request_signature/bls:
post:
summary: Request a signature for a 32-byte blob of data (typically a hash), signed by the BLS private key for the requested public key.
description: >
description: |
This endpoint requires a valid JWT Bearer token.

The token **must include** the following claims:
- `exp` (integer): Expiration timestamp
- `module` (string): The ID of the module making the request, which must match a module ID in the Commit-Boost configuration file.
Expand Down Expand Up @@ -147,7 +171,7 @@ paths:
message:
type: string
example: "Unauthorized"

"404":
description: You either requested a route that doesn't exist, or you requested a signature from a key that does not exist.
content:
Expand Down Expand Up @@ -216,9 +240,9 @@ paths:
/signer/v1/request_signature/proxy-bls:
post:
summary: Request a signature for a 32-byte blob of data (typically a hash), signed by the BLS private key for the requested proxy public key.
description: >
description: |
This endpoint requires a valid JWT Bearer token.

The token **must include** the following claims:
- `exp` (integer): Expiration timestamp
- `module` (string): The ID of the module making the request, which must match a module ID in the Commit-Boost configuration file.
Expand All @@ -237,15 +261,15 @@ paths:
required: [proxy, object_root, nonce]
properties:
proxy:
description: The 48-byte BLS public key (for `proxy_bls` mode) or the 20-byte Ethereum address (for `proxy_ecdsa` mode), with optional `0x` prefix, of the proxy key that you want to request a signature from.
description: The 48-byte BLS public key, with optional `0x` prefix, of the proxy key that you want to request a signature from.
$ref: "#/components/schemas/BlsPubkey"
object_root:
description: The 32-byte data you want to sign, with optional `0x` prefix.
$ref: "#/components/schemas/B256"
nonce:
$ref: "#/components/schemas/Nonce"
example:
pubkey: "0xa3ffa9241f78279f1af04644cb8c79c2d8f02bcf0e28e2f186f6dcccac0a869c2be441fda50f0dea895cfce2e53f0989"
proxy: "0xa3ffa9241f78279f1af04644cb8c79c2d8f02bcf0e28e2f186f6dcccac0a869c2be441fda50f0dea895cfce2e53f0989"
object_root: "0x3e9f4a78b5c21d64f0b8e3d9a7f5c02b4d1e67a3c8f29b5d6e4a3b1c8f72e6d9"
responses:
"200":
Expand Down Expand Up @@ -295,7 +319,7 @@ paths:
message:
type: string
example: "Unauthorized"

"404":
description: You either requested a route that doesn't exist, or you requested a signature from a key that does not exist.
content:
Expand Down Expand Up @@ -364,9 +388,9 @@ paths:
/signer/v1/request_signature/proxy-ecdsa:
post:
summary: Request a signature for a 32-byte blob of data (typically a hash), signed by the ECDSA private key for the requested proxy Ethereum address.
description: >
description: |
This endpoint requires a valid JWT Bearer token.

The token **must include** the following claims:
- `exp` (integer): Expiration timestamp
- `module` (string): The ID of the module making the request, which must match a module ID in the Commit-Boost configuration file.
Expand Down Expand Up @@ -443,7 +467,7 @@ paths:
message:
type: string
example: "Unauthorized"

"404":
description: You either requested a route that doesn't exist, or you requested a signature from a key that does not exist.
content:
Expand Down Expand Up @@ -512,9 +536,9 @@ paths:
/signer/v1/generate_proxy_key:
post:
summary: Request a proxy key be generated for a specific consensus pubkey
description: >
description: |
This endpoint requires a valid JWT Bearer token.

The token **must include** the following claims:
- `exp` (integer): Expiration timestamp
- `module` (string): The ID of the module making the request, which must match a module ID in the Commit-Boost configuration file.
Expand Down Expand Up @@ -619,12 +643,151 @@ paths:
type: string
example: "Internal error"

/reload:
post:
summary: Hot-reload signer configuration
description: |
Re-reads cb-config.toml and environment variables, rebuilding the signer's
internal state. Accepts optional body overrides for JWT secrets and the
admin secret.

**Behaviour:**
- New modules in config are registered.
- Removed modules are dropped from the access list.
- JWT secrets and admin secret are reset to env var values.
- Previous runtime changes (from /revoke_jwt or body overrides) are reverted.

**Body overrides** (applied on top of the config baseline):
- `jwt_secrets`: comma-separated `<MODULE_ID>=<SECRET>` pairs.
- `admin_secret`: string to override the admin JWT secret.

Body overrides are **not persisted** across restarts.
tags:
- Management
security:
- AdminBearerAuth: []
requestBody:
required: false
content:
application/json:
schema:
type: object
properties:
jwt_secrets:
description: Comma-separated list of MODULE_ID=SECRET pairs to override module JWT secrets
type: string
example: "module_a=newsecret,module_b=anothersecret"
admin_secret:
description: Override for the admin JWT secret
type: string
example: "my-new-admin-secret"
responses:
"200":
description: Configuration reloaded successfully
"400":
description: Body references a module ID not present in the config
content:
application/json:
schema:
type: object
required:
- code
- message
properties:
code:
type: number
example: 400
message:
type: string
example: "bad request: Module unknown-module not found in config, cannot override JWT secret"
"500":
description: Failed to reload config (previous state preserved)
content:
application/json:
schema:
type: object
required:
- code
- message
properties:
code:
type: number
example: 500
message:
type: string
example: "internal error: failed to reload config"

/revoke_jwt:
post:
summary: Immediately revoke a module's access
description: |
Removes a module from the signer's access list. The module will no longer
be able to authenticate with its JWT.

If the module is still present in cb-config.toml, the next `/reload` will
re-add it. Remove the module from the config to make revocation permanent.
tags:
- Management
security:
- AdminBearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- module_id
properties:
module_id:
description: The ID of the module to revoke
type: string
example: "MY_MODULE"
responses:
"200":
description: Module access revoked successfully
"404":
description: Module ID not found
content:
application/json:
schema:
type: object
required:
- code
- message
properties:
code:
type: number
example: 404
message:
type: string
example: "module id not found"

/status:
get:
summary: Health check
description: Simple health check endpoint. Returns 200 OK with no body. No authentication required.
tags:
- Management
responses:
"200":
description: Signer service is healthy
content:
text/plain:
schema:
type: string
example: ""

components:
securitySchemes:
BearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
AdminBearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
schemas:
B256:
type: string
Expand Down
24 changes: 12 additions & 12 deletions config.example.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@
# A custom object, e.g., chain = { genesis_time_secs = 1695902400, slot_time_secs = 12, genesis_fork_version = "0x01017000", chain_id = 17000 }.
chain = "Holesky"

# Configuration for the PBS module
# Configuration for the PBS service
[pbs]
# Docker image to use for the PBS module.
# Docker image to use for the PBS service.
# OPTIONAL, DEFAULT: ghcr.io/commit-boost/commit-boost:latest
docker_image = "ghcr.io/commit-boost/commit-boost:latest"
# Whether to enable the PBS module to request signatures from the Signer module (not used in the default PBS image)
# Whether to enable the PBS service to request signatures from the Signer service (not used in the default PBS image)
# OPTIONAL, DEFAULT: false
with_signer = false
# Host to receive BuilderAPI calls from beacon node
Expand Down Expand Up @@ -62,7 +62,7 @@ extra_validation_enabled = false
# a fallback if the user's own SSV node is not reachable.
# OPTIONAL, DEFAULT: "https://api.ssv.network/api/v4/"
# ssv_public_api_url = "https://api.ssv.network/api/v4/"
# Timeout for any HTTP requests sent from the PBS module to other services, in seconds
# Timeout for any HTTP requests sent from the PBS service to other services, in seconds
# OPTIONAL, DEFAULT: 10
http_timeout_seconds = 10
# Maximum number of retries for validator registrations per relay
Expand All @@ -79,7 +79,7 @@ validator_registration_batch_size = ""
# OPTIONAL, DEFAULT: 384
mux_registry_refresh_interval_seconds = 384

# The PBS module needs one or more [[relays]] as defined below.
# The PBS service needs one or more [[relays]] as defined below.
[[relays]]
# Relay ID to use in telemetry
# OPTIONAL, DEFAULT: URL hostname
Expand Down Expand Up @@ -167,14 +167,14 @@ timeout_get_header_ms = 900
id = "mux-relay-1"
url = "http://0xa119589bb33ef52acbb8116832bec2b58fca590fe5c85eac5d3230b44d5bc09fe73ccd21f88eab31d6de16194d17782e@def.xyz"

# Configuration for the Signer Module, only required if any `commit` module is present, or if `pbs.with_signer = true`
# Currently three types of Signer modules are supported (only one can be used at a time):
# Configuration for the Signer service, only required if any `commit` module is present, or if `pbs.with_signer = true`
# Currently three types of Signer service are supported (only one can be used at a time):
# - Remote: a remote Web3Signer instance
# - Dirk: a remote Dirk instance
# - Local: a local Signer module
# More details on the docs (https://commit-boost.github.io/commit-boost-client/get_started/configuration/#signer-module)
# More details on the docs (https://commit-boost.github.io/commit-boost-client/get_started/configuration/#signer-service)
[signer]
# Docker image to use for the Signer module.
# Docker image to use for the Signer service.
# OPTIONAL, DEFAULT: ghcr.io/commit-boost/commit-boost:latest
docker_image = "ghcr.io/commit-boost/commit-boost:latest"
# Host to bind the Signer API server to
Expand Down Expand Up @@ -247,13 +247,13 @@ jwt_auth_fail_timeout_seconds = 300
# url = "https://localhost:8882"
# accounts = ["Wallet2", "DistributedWallet"]

# Configuration for how the Signer module should store proxy delegations.
# Configuration for how the Signer service should store proxy delegations.
# OPTIONAL
# [signer.dirk.store]
# proxy_dir = "/path/to/proxies"

# For Local signer:
# Configuration for how the Signer module should load validator keys. Currently two types of loaders are supported:
# Configuration for how the Signer service should load validator keys. Currently two types of loaders are supported:
# - File: load keys from a plain text file (unsafe, use only for testing purposes)
# - ValidatorsDir: load keys from a `keys` and `secrets` file/folder (ERC-2335 style keystores). More details can be found in the docs (https://commit-boost.github.io/commit-boost-client/get_started/configuration/)
[signer.local.loader]
Expand All @@ -275,7 +275,7 @@ key_path = "./tests/data/keys.example.json"
# For lodestar, it's the path to the file containing the decryption password.
# For nimbus, it's the path to the directory where the `<pubkey>` files are located.
# secrets_path = ""
# Configuration for how the Signer module should store proxy delegations. Supported types of store are:
# Configuration for how the Signer service should store proxy delegations. Supported types of store are:
# - File: store keys and delegations from a plain text file (unsafe, use only for testing purposes)
# - ERC2335: store keys and delegations safely using ERC-2335 style keystores. More details can be found in the docs (https://commit-boost.github.io/commit-boost-client/get_started/configuration#proxy-keys-store)
# OPTIONAL, if missing proxies are lost on restart
Expand Down
Loading
Loading