A security-focused release. No breaking changes; existing tokens, secrets, and deployments continue to work, with the new defences enabled automatically.
@goldtopcollective/keiko-mcp is bumped from 1.1.0 → 1.2.0 for the sanitizer extensions (the only MCP-side change in this release). Update on each machine with npx -y @goldtopcollective/keiko-mcp@latest or npm i -g @goldtopcollective/keiko-mcp@1.2.0.
Added
- Versioned encryption keys. The backend now resolves keys from
ENCRYPTION_KEY(v1),ENCRYPTION_KEY_V2,ENCRYPTION_KEY_V3, etc. Each secret row records the version it was encrypted under, so the vault can hold a mix of versions during rotation. - In-place key rotation.
POST /api/admin/rotate-encryptionre-encrypts every secret under the latest configured key in a single SQLite transaction. The Sessions admin page exposes a status display (current version, secrets per version) and a guarded Rotate button.GET /api/admin/encryption-statuspowers the same data via API. - Token scoping. Tokens carry an optional JSON array of secret-name glob patterns (
["railway_*", "deploy_token"], etc.). The token-auth middleware enforces scope on every secret endpoint: list filters, resolve / update / metadata-patch / history return404 Not Foundoutside of scope (404 not 403 so a narrow token can't enumerate what it doesn't have access to). Tokens issued before this release are treated as legacy unrestricted and visually flagged on the Tokens admin page. - Per-token rate limit. 120 requests/minute/token, layered on top of the existing 60 requests/minute/IP cap. Catches a compromised token routed through multiple IPs.
- CSRF protection on every mutating session-authed endpoint. A per-session token is exposed via a
<meta name="csrf-token">tag in the header and validated on theX-CSRF-Tokenrequest header. - Threat-model documentation as a first-class block in
GET /api/guide. Lists what the proxy pattern prevents and — just as explicitly — what it does not (active exfiltration by a compromised model via out-of-band channels, compromise of the MCP host, compromise of the operator account holdingENCRYPTION_KEY).
Changed
- Sanitizer covers more encoding variants. Output redaction now also catches
base64url, hex (upper- and lower-case), JSON-string-escaped, and HTML-entity-escaped representations of every injected secret value, in addition to the existing raw, standard base64, and URL-encoded forms. - Sentry capture is scrubbed.
beforeSendandbeforeSendTransactionfilter sensitive keys from request bodies, drop cookies and Authorization headers, and refuse to attach default PII.tracesSampleRatedrops from1.0to0.1.
Security
- `SESSION_SECRET` no longer silently falls back to a hardcoded default in production. Previously, a missing env var would cause the process to start with a known-weak session secret, making admin sessions trivially forgeable. The process now exits with
FATALifSESSION_SECRETis unset andNODE_ENV=production.
Migration
- Existing deployments need no manual action. The schema migrations (
key_versiononsecrets,scopesontokens) run idempotently on startup. - Existing tokens become "legacy unrestricted" and continue to work; visit
/ui/tokensand use Edit Scopes on each one to tighten their access. - To rotate the encryption key: add
ENCRYPTION_KEY_V2to your environment, redeploy, then click Rotate to Latest Key on the Sessions page.
See CHANGELOG.md for the full entry.