Post-Security-Hardening Operational Runbook¶
Date: 2026-04-01 Context: After applying 14 security fixes (C-1 through L-3) to the seat manager, the following operational items require attention before running in production with writes enabled.
1. Signing Key Isolation¶
The API currently loads SEAT_MANAGER_OWNER_PRIVATE_KEY into the same process that serves HTTP. This means a remote code execution vulnerability in the API process would expose the treasury signing key.
Current state: API runs with writes=disabled. Signing key is not loaded.
Before enabling writes:
- Migrate to KMS-backed signing via
SEAT_MANAGER_KMS_KEY_ID(AWS KMS). The raw private key never leaves the HSM. - If KMS is not available, ensure the API process runs in a restricted network namespace with no inbound access from the public internet. The admin frontend should proxy through a separate gateway.
- Never set
SEAT_MANAGER_OWNER_PRIVATE_KEYand expose the API on a public interface simultaneously.
2. HMAC Secrets Must Be Persisted¶
The C-1 fix made SESSION_HMAC_SECRET and STEP_UP_HMAC_SECRET mandatory. These are currently set only in the shell environment of the running process. They will be lost on reboot.
Action required:
- Store both secrets in a
.envfile at the project root withchmod 600owned by the service user, or use a secrets manager. - If the secrets are lost, all active sessions become invalid (users must re-login). No data loss, but operational disruption.
- Never commit these secrets to git.
Current values were generated on 2026-04-01:
SESSION_HMAC_SECRET=34c0c421cc509d876d9a7bded5139fb29118bceadf29d0231d42fa20aa8c7073
STEP_UP_HMAC_SECRET=efce4c68b40a6329962f522f3208f961966161df384d7ccfd8a4f1005d1c2a9f
These should be rotated periodically. Rotation invalidates all existing sessions.
3. Admin Password Rotation¶
The current admin password (Admin!Temp2026#) appears to be a temporary credential. For a system that authorizes 32 CTN validator deposits, this must be rotated.
Action required:
- Log in to the admin console and change the password via the user management page, or use the API:
- Use a password manager to generate a high-entropy password (20+ characters, mixed case, symbols, digits).
- After rotation, verify login works before closing the old session.
4. Required Environment Variables (Post-Hardening)¶
The following env vars are now mandatory in non-test environments:
| Variable | Purpose | Notes |
|---|---|---|
SESSION_HMAC_SECRET |
HMAC key for session token hashing | 256-bit hex, must be high-entropy |
STEP_UP_HMAC_SECRET |
HMAC key for step-up token signing | 256-bit hex, must be high-entropy |
DATABASE_URL |
PostgreSQL connection string | Required by Prisma |
The following are required when writes are enabled:
| Variable | Purpose | Notes |
|---|---|---|
SEAT_MANAGER_OWNER_PRIVATE_KEY |
Treasury signing key (local mode) | Prefer KMS instead |
SEAT_MANAGER_KMS_KEY_ID |
AWS KMS key ID (preferred over local key) | Requires AWS credentials |
The following are optional but affect security posture:
| Variable | Default | Purpose |
|---|---|---|
SEAT_MANAGER_STATIC_ADMIN_TOKEN_SUNSET |
none | ISO-8601 date after which static token auth is rejected |
SEAT_MANAGER_SECURE_COOKIES |
true in production |
Set Secure flag on session cookies |
SEAT_MANAGER_VERIFY_DEPOSIT_SIGNATURE |
true |
BLS signature verification on deposit-data submission |
SEAT_MANAGER_REQUIRE_STAKER_AUTH |
true |
Require auth on staker-facing read routes (set to false only for isolated local development) |
5. Database Migration Checklist¶
After any code update that modifies prisma/schema.prisma, run:
npx prisma migrate status # Check for pending migrations
npx prisma migrate deploy # Apply pending migrations (production)
npx prisma generate # Regenerate Prisma client
npm run build # Rebuild dist/
Never start the API if prisma migrate status shows unapplied migrations. The Prisma client will reference columns that don't exist and requests will fail with 500 errors.
6. Restart Procedure¶
# 1. Ensure migrations are current
npx prisma migrate status
# 2. Build
npm run build
# 3. Stop existing process
fuser -k 8080/tcp
# 4. Start with required env vars
SESSION_HMAC_SECRET=<secret> \
STEP_UP_HMAC_SECRET=<secret> \
SEAT_MANAGER_OWNER_PRIVATE_KEY=<key> \
node dist/index.js api --port 8080 --host 127.0.0.1
# 5. Verify
curl -s http://127.0.0.1:8080/health
7. Pro Audit Addendum (2026-04-10): CSM Controls¶
The 2026-04-10 professional audit introduced additional controls (CSM-01 through CSM-04).
Use this matrix to verify they remain enforced after every security-sensitive change.
| Finding | Control (must remain true) | Primary routes |
|---|---|---|
CSM-01 |
API rejects caller-supplied treasury; trusted treasury is server-controlled and enforced before irreversible seat ops |
POST /v1/seats/generate, POST /v1/seats/with-vault, POST /v1/seats/:id/(approve|deposit|settle|force-exit) |
CSM-02 |
Session admins must pass step-up for irreversible operations; static admin bearer is blocked on step-up routes | POST /v1/seats/generate, POST /v1/seats/:id/(settle|force-exit) |
CSM-03 |
Global audit and dashboard reads are ADMIN-only |
GET /v1/audit-logs*, GET /v1/dashboard/(health|alerts) |
CSM-04 |
Staker API key reads on pubkey routes must be address-bound via X-Staker-Address |
GET /v1/validator/:pubkey* |
8. CSM Regression Test Map¶
Run these tests on every PR touching authz, schemas, or seat lifecycle handlers:
npx vitest run test/security.test.ts test/schemas.test.ts -t "CSM-01 regression"
npx vitest run test/security.test.ts -t "CSM-02 regression"
npx vitest run test/security.test.ts -t "CSM-03 regression"
npx vitest run test/security.test.ts -t "CSM-04 regression"
Exact regression test names:
| Finding | Test name |
|---|---|
CSM-01 |
CSM-01 regression: POST /v1/seats/generate rejects caller treasury override |
CSM-02 |
CSM-02 regression: POST /v1/seats/generate requires step-up for session admins |
CSM-03 |
CSM-03 regression: GET /v1/audit-logs rejects VIEWER |
CSM-04 |
CSM-04 regression: /v1/validator/:pubkey requires X-Staker-Address |