Skip to content

v1.5.0 — Security hardening pass

Latest

Choose a tag to compare

@goldtopcollective goldtopcollective released this 16 May 10:01

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-encryption re-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-status powers 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 return 404 Not Found outside 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 the X-CSRF-Token request 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 holding ENCRYPTION_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. beforeSend and beforeSendTransaction filter sensitive keys from request bodies, drop cookies and Authorization headers, and refuse to attach default PII. tracesSampleRate drops from 1.0 to 0.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 FATAL if SESSION_SECRET is unset and NODE_ENV=production.

Migration

  • Existing deployments need no manual action. The schema migrations (key_version on secrets, scopes on tokens) run idempotently on startup.
  • Existing tokens become "legacy unrestricted" and continue to work; visit /ui/tokens and use Edit Scopes on each one to tighten their access.
  • To rotate the encryption key: add ENCRYPTION_KEY_V2 to your environment, redeploy, then click Rotate to Latest Key on the Sessions page.

See CHANGELOG.md for the full entry.