Centurion Seat Manager¶
Validator seat lifecycle manager for the Centurion DepositContractCTN allowlist model.
What Is a Seat?¶
A seat is a tracked (pubkey, withdrawal_credentials) pair bound to an on-chain allowlist intent on the deposit contract. Each seat maps to exactly one validator, one WithdrawalVault, and one beneficiary wallet.
Each seat has an intent hash — keccak256(pubkey || wc || amountGwei || authorizedDepositor || ownershipEpoch) — that the contract uses to allow or deny deposits.
The Seat Manager handles vault deployment, allowlist management, and lifecycle tracking — from seat creation through validator activation on the beacon chain.
At a Glance¶
| Capability | What It Does |
|---|---|
| BYO-BLS onboarding | External operators bring their own BLS key — foundation deploys vault, operator submits deposit data |
| Generate keys (internal) | Auto-generate BLS validator keypair for foundation-managed validators |
| Deploy vault + create seat | Deploy a per-validator WithdrawalVault, derive withdrawal credentials, create the seat |
| Approve / Revoke | Send addAllowedDeposit / removeAllowedDeposit transactions |
| Deposit | Simulate or send deposit() with full verification of on-chain events |
| Force-exit (EIP-7002) | Treasury can force-exit a validator without BLS key via the vault's triggerValidatorExit() |
| Watch | Monitor EL nodes for DepositEvent logs and CL nodes for validator visibility |
| Control | Toggle the allowlist switch and manage the irreversible freeze |
| Admin UI | Next.js admin frontend for seat/operator/user management |
| API | Fastify REST API with session auth, RBAC, idempotency, and audit logging |
| Audit | Full audit trail with userId, requestId, ipAddress on every action |
Quick Links¶
Architecture¶
centurion-seat-manager/ (root workspace — CLI + API)
├── apps/
│ └── admin/ (Next.js 15 admin frontend)
├── packages/
│ └── shared/ (@centurion/shared — shared TS types + constants)
├── prisma/ (Prisma schema + migrations)
├── src/ (Fastify API + CLI source)
├── test/ (vitest integration tests)
└── scripts/ (env-check, live test scripts)
Operation Modes¶
The primary operational mode. The Fastify API serves both the admin frontend and programmatic access.
CLI commands for scripting, automation, and direct operations.
Without SEAT_MANAGER_OWNER_PRIVATE_KEY, admin commands generate calldata and evidence bundles but never send transactions.
Quick Start¶
# 1. Install dependencies
npm install
# 2. Set up PostgreSQL
export DATABASE_URL="postgresql://user:password@localhost:5432/seat_manager?schema=public"
npx prisma migrate dev
# 3. Build
npm run build
# 4. Health check (read-only)
node dist/index.js status
# 5. Start API + watchers (two terminals)
node dist/index.js api-v2 --port 8080
node dist/index.js watch
# 6. Start admin frontend (third terminal)
cd apps/admin && npm run dev
CLI Commands¶
| Command | What It Does |
|---|---|
status |
Run preflight checks, show allowlist state |
seat create --operator <id> --pubkey <0x..> --wc <0x..> |
Store a new seat locally |
seat create-with-vault --operator <id> --pubkey <0x..> --beneficiary <0x..> [--treasury <0x..>] [--principal-ctn 32] |
Deploy per-validator vault, derive wc, store seat |
seat approve <seatId> [--send] |
Propose or send addAllowedDeposit |
seat deposit <seatId> --amount-ctn <N> [--simulate-only] [--send] |
Simulate or send a deposit |
seat revoke <seatId> [--send] |
Propose or execute revocation |
seat force-exit <seatId> [--send] |
Trigger EIP-7002 validator exit via vault (no BLS key needed) |
seat list [--status CREATED,ALLOWLISTED,...] |
List seats with optional status filter |
switch on/off [--send] |
Toggle the allowlist |
freeze schedule --delay-seconds <N> [--send] |
Schedule irreversible allowlist disable |
freeze cancel [--send] |
Cancel a pending freeze schedule |
freeze execute --dangerous [--send] |
Permanently disable the allowlist |
watch |
Run EL + CL watchers continuously |
api-v2 [--port 8080] [--host 127.0.0.1] |
Start Fastify API server |
API Endpoints¶
Public (no auth)¶
| Method | Route | Description |
|---|---|---|
| GET | /health |
Health check |
| GET | /v1/validators/:address |
Validators by wallet address |
| GET | /v1/validator/:pubkey |
Single validator by pubkey |
Authenticated (session or admin token)¶
| Method | Route | Role Required | Description |
|---|---|---|---|
| POST | /v1/auth/login |
— | Login, returns session token |
| POST | /v1/auth/logout |
Any | Revoke session |
| GET | /v1/seats |
VIEWER+ | List all seats |
| POST | /v1/seats |
OPERATOR+ | Create seat (manual) |
| POST | /v1/seats/with-vault |
ADMIN | Deploy vault + create seat (BYO-BLS) |
| POST | /v1/seats/generate |
ADMIN | Generate keys + deploy vault (internal only) |
| POST | /v1/seats/:id/deposit-data |
OPERATOR+ | Submit operator deposit data (BYO-BLS) |
| POST | /v1/seats/:id/approve |
ADMIN | Allowlist on-chain |
| POST | /v1/seats/:id/deposit |
ADMIN | Send 32 CTN deposit |
| POST | /v1/seats/:id/revoke |
ADMIN | Revoke seat |
| POST | /v1/seats/:id/force-exit |
ADMIN | EIP-7002 force-exit (no BLS key needed) |
| POST | /v1/seats/:id/settle |
ADMIN | Start exit settlement on vault |
| GET/POST | /v1/operators |
VIEWER+ / OPERATOR+ | Operator CRUD |
| GET/POST | /v1/users |
ADMIN | User management |
| GET | /v1/audit-logs |
VIEWER+ | Audit log query |
Seat Lifecycle¶
stateDiagram-v2
[*] --> CREATED : seat create / generate
CREATED --> ALLOWLISTED : approve (addAllowedDeposit)
CREATED --> REVOKED : revoke
ALLOWLISTED --> DEPOSITED : deposit 32 CTN
ALLOWLISTED --> REVOKED : revoke (removeAllowedDeposit)
DEPOSITED --> SEEN_BY_CL : CL watcher sees validator
DEPOSITED --> REVOKED : revoke (operational)
SEEN_BY_CL --> ACTIVE : CL watcher confirms activation
SEEN_BY_CL --> REVOKED : revoke (operational)
ACTIVE --> REVOKED : revoke (operational)
One-way progression
Seats only move forward or to REVOKED from any state. There is no backward transition. State transitions use optimistic concurrency (version column) to prevent races.
Storage¶
PostgreSQL 18 via Prisma ORM. DATABASE_URL environment variable required.
| Table | What It Stores |
|---|---|
users |
Admin users with roles (ADMIN, OPERATOR, VIEWER) and scrypt password hashes |
sessions |
Session tokens (HMAC-SHA256 hashed), expiry, status |
operators |
Operator entities (name, description, enabled) |
seats |
Lifecycle state per (pubkey, wc) pair, vault metadata |
seat_events |
Append-only lifecycle transition history |
seat_bls_material |
BLS keystores and deposit data (1:1 with seat) |
seat_operations |
Multi-step workflow journal (keygen → vault → seat) |
allowlist_actions |
On-chain tx records (add/remove) |
deposits |
Observed DepositEvent logs |
cl_observations |
Beacon API visibility checks |
audit_log |
All actions with userId, requestId, ipAddress |
idempotency_keys |
Request deduplication for POST routes |
Configuration¶
seat-manager.config.json in the repo root:
{
"centurionInfraRoot": "/path/to/centurion-infra",
"centurionNetworksRoot": "/path/to/centurion-networks",
"endpoints": [
{
"label": "node-1",
"host": "<node-ip>",
"rpcPort": 8545,
"elClient": "geth",
"clClient": "lighthouse",
"sshUser": "<ssh-user>"
}
]
}
Environment Variables¶
| Variable | Required | Purpose |
|---|---|---|
DATABASE_URL |
Yes | PostgreSQL connection string |
ADMIN_TOKEN |
For API | Static admin token (legacy auth, alongside session auth) |
SEAT_MANAGER_OWNER_PRIVATE_KEY |
For --send |
Contract owner private key (or use KMS) |
SEAT_MANAGER_KMS_KEY_ID |
For --send |
AWS KMS key ID/alias — alternative to raw private key |
SEAT_MANAGER_INTENTS_MNEMONIC |
For key generation | BLS mnemonic (never passed as CLI arg) |
SEAT_MANAGER_INTENTS_KEYSTORE_PASSWORD |
For key generation | Keystore encryption password (never passed as CLI arg) |
SEAT_MANAGER_VAULT_DEPLOYER_PRIVATE_KEY |
For vault deploy | Falls back to owner key |
SEAT_MANAGER_DEPOSITOR_PRIVATE_KEY |
For deposits | Falls back to owner key |
SESSION_HMAC_SECRET |
For API | Session token HMAC key (auto-generated if unset) |
OTEL_EXPORTER_OTLP_ENDPOINT |
Optional | OpenTelemetry collector endpoint |
Tests¶
| Suite | Tests | Coverage |
|---|---|---|
prismaStore.test.ts |
68 | Operators, seats, state machine, concurrency, users, sessions, auth, idempotency, audit |
security.test.ts |
34 | RBAC, timing attacks, key material, CSP, error handling |
schemas.test.ts |
30 | Zod schema validation |
intent.test.ts |
16 | Intent hash computation |
validator_payload.test.ts |
25 | Validator API response mapping |
gate_status.test.ts |
10 | Contract gate status logic |
validation.test.ts |
10 | Input validation |
deposit_policy.test.ts |
3 | Deposit policy enforcement |
vault.test.ts |
3 | Vault credential derivation |
adversarial.test.ts |
53 | Money-risk paths, auto-settlement, watcher heartbeat |
byoBls.test.ts |
30 | BYO-BLS deposit data validation, force-exit, RBAC |
vault_contract_compile.test.ts |
5 | WithdrawalVault ABI + constructor verification |
Staker Console Integration¶
For the full user-facing read layer, claim rewards flow, and wallet-scoped validator visibility, see: Staker Console Integration