Soundstripe Engineering · System architecture · Reflects the repo on 2026-09-10
Keymaster
Soundstripe's internal config and secrets manager. One place where people define what every application needs in every environment, and one contract by which those applications receive it. This page explains what Keymaster is, how a value moves through it, and how a consumer host connects.
There is no secret. Only keys. Config and secrets are one model. Sensitivity is a per-key attribute.
Orientation
What Keymaster is
Keymaster is a thin, opinionated policy layer over a self-hosted secrets backend. It does not hand-roll cryptography or storage. It owns everything around the values: who can see and change them, how changes are audited, what a complete export looks like, and how consumers fetch it.
Encrypted backend
Infisical, self-hosted
Stores every value encrypted at rest with versions, environments, and imports. Keymaster provisions a project per app and an environment per managed environment.
Its native UI is break-glass only. Normal human and machine access never touches it directly.
Policy and control plane
Keymaster UI + API
A Rails 8 application. GitHub org/team sign-in, per-app and per-environment roles, the apps × environments × keys matrix, production step-up, append-only audit, expected-key manifests, machine credentials, and the export API.
Consumer client
The keymaster CLI
A standalone Go binary and the only supported consumer integration. It fetches a validated export and either serves values from an encrypted snapshot over a Unix socket or renders a 0600 env file.
Management plane: people change values
Engineers sign in with GitHub, edit cells in the matrix, and apply audited batches. Production writes need a stricter role plus fresh re-authentication.
Delivery plane: applications receive values
A consumer host holds an exact-scope machine credential. The CLI pulls a complete, provenance-checked export and exposes it locally. Keymaster never pushes.
Staging live, manually deployed production is separately gated (P0/P1 open)
Big picture
The system at a glance
Three boundaries. People and custody on the left, the Keymaster service in the middle, and a consumer host on the right. Values cross a boundary only over the labeled arrows.
Who owns what. Keymaster owns policy, audit, expected-key manifests, and the machine-credential lifecycle. Infisical owns encrypted values. A plaintext value exists in exactly three places: inside Infisical, inside the daemon's memory, and inside the age-encrypted snapshot on the consumer host. Dashed arrows are custody or emergency paths, not normal operation.
Nobody talks to Infisical directly. Humans go through the Keymaster UI. Machines go through the CLI. Direct Infisical API, CLI, UI, or reader-identity access is unsupported for consumers.
Keymaster's database is metadata-only. App, environment, and key definitions, role bindings, credential digests, manifests, and audit records. No values, ever.
Pull, not push. Consumers pull at deploy time and at a refresh interval. Keymaster has no push path, so a Keymaster outage stalls refreshes but does not take down warm nodes.
Management plane
How a person changes a value
Every write passes a chain of gates. Each gate fails closed, and the last step records an audit intent before the backend write and a result after it.
Authentication and authorization are separate. GitHub org/team membership permits sign-in. Only an explicit RoleBinding permits access to an app. Session freshness is bounded: membership evidence and idle time expire at 15 minutes, absolute lifetime at 8 hours, after which a new OAuth exchange is required.
Who can do what
Role
Read values
Write non-production
Write production
Administer
Binding scope
viewer
yes
no
no
no
global, app, or environment
nonprod_editor
yes
yes
no
no
global, app, or environment
prod_editor
yes
yes
with step-up
no
global, app, or environment
keymaster_admin
yes
yes
with step-up
yes
global only for Access, machine credentials, audit, and creating apps
Sensitive values are masked by default. Revealing one is a separate audited action, rate limited to 30 per minute per user. Reveal, copy, and edit all go through it.
Onboarding before first sign-in. A global admin can invite a verified work email with app- or environment-scoped roles. The invitation is claimed on first GitHub sign-in when the verified email matches, expires after 30 days, and never grants global scope.
Drafts are volatile on purpose. Staged edits live in a RAM-backed store on the Keymaster host, keyed by an opaque handle in the session. A restart or deploy discards them, and the UI asks you to restage.
Writes are versioned, never destructive. Infisical keeps value history. The matrix can reveal and restage a prior version through the same audited path.
Data ownership
What is stored where
Keymaster and Infisical hold mirrored structure but different content. Keymaster's rows describe and govern; Infisical's rows hold the encrypted values.
The credential table stores a digest, not a token. Raw bearer tokens are shown once at issuance with Cache-Control: no-store and never persisted. Audit records keep the historical target IDs even after an app, environment, or key is deleted, and the ordinary update and delete paths on them are blocked.
Inheritance
Non-production may inherit. Production never does.
Within one app, a non-production environment can inherit a key's value from production or from another non-production environment. Resolution happens at read time through Infisical imports, and an explicit override always wins.
One-directional by design. Inheritance is per app; cross-app sharing is out of scope. Keymaster inspects import provenance on every export and every matrix read, so a wrong-environment value can never be materialized into a production write.
Explicit override wins. An inheriting environment that sets its own value stops inheriting until that override is removed.
Sensitive inheritance needs read rights on the source. Inheriting a sensitive production value downward widens who can see it, so the editor must be allowed to read the source environment.
Manifests track provenance. An inherited key appears in the consuming environment's expected-key manifest with source kind inherited and its source environment, so the export checks both completeness and origin.
Delivery plane
How an application receives values
The CLI asks for one app and one environment. The export path checks the credential, the manifest, the backend listing, and the provenance of every resolved value before it returns anything. The CLI then re-validates the payload before it becomes a local delivery surface.
Every failure has one name. Backend behavior is normalized into a stable error taxonomy so a consumer adapter can decide what to do without parsing Infisical responses. A successful export records an audit event with the key count, fingerprint digest, and a count of ignored backend-only keys, never a key name or value.
The fingerprint is a SHA-256 over the sorted resolved entries, including values. It is computed in memory and only the digest is ever stored or logged. Key names reserve the KEYMASTER_ prefix for delivery metadata.
Normalized errors and CLI exit codes
Exit
Outcome
Consumer does
10
auth-fail
Warm node may reuse bounded last-known-good if configured. New node fails.
11
tool-unreachable
Same bounded last-known-good policy. New node fails.
12
unknown-key
Fail loud.
13
unknown-environment
Fail loud.
14
wrong-environment-import
Fail loud. Keep the existing valid surface untouched.
15
partial-export
Fail loud. Keep the existing valid surface untouched.
16
malformed-output
Fail loud.
17
stale-export
Local surface is valid but behind. Refresh, then recheck.
18
legacy-snapshot
Re-bootstrap or refresh. Never hand-edit a header.
19
sensitive-reference-refused
Use a permitted key. Do not retry with delivery authority.
Encrypted serve
On the consumer host
The production-oriented delivery mode. A long-lived daemon owns the decrypt key in memory, keeps the value set encrypted on disk, and answers local lookups over a Unix socket that the kernel restricts to one UID. Application processes never receive the machine credential or the decrypt material.
Encryption at rest buys disk and backup posture, not runtime secrecy. Plaintext exists in daemon memory either way. Deploy hooks and operators refresh through the same socket with keymaster refresh --socket; a failed refresh keeps the last valid payload. Startup refuses to proceed unless core dumps are disabled and memory locking succeeds, unless the development-only override is used, which never counts as fleet evidence.
Daemon lifecycle
Situation
What the daemon does
If Keymaster is unreachable
New node / code boot
Runs the bootstrap command, fetches the current export, validates it, writes the encrypted snapshot, opens the socket.
Fails closed. The node must not complete boot.
Power cycle / restart
Memory is gone. Re-runs the bootstrap command, then refreshes from Keymaster.
Fails closed by default, because the decrypt material may have rotated while the daemon was down.
Warm deploy
Daemon keeps running. The deploy hook asks it to refresh through the socket.
Bounded last-known-good may be used only for auth-fail and tool-unreachable, and only when configured.
Runtime lookup
Answers from memory. Remote Keymaster is never in the per-lookup path.
Unaffected.
Automatic refresh
Every 5 minutes plus up to 30 seconds of jitter (minimum 1 minute, or explicitly disabled). Same validation and lock as a manual refresh.
Keeps the last valid disk and memory payload, records a value-free result, retries next cycle.
Socket protocol keymaster.serve.v1
Request
Response
{"key":"NAME"}
The value, sensitive or not, for the authorized peer UID.
{}
Value-free ping: {"ok":true}.
{"action":"refresh"}
Fetch, validate, encrypt, replace snapshot and served payload.
{"action":"status"}
Readiness and current / stale-export against the remote fingerprint.
One connection carries one request and one response, then the server closes. Requests are capped at 64 KiB, connections at 64, and work at 30 seconds. No TCP listener exists.
Custody modes
daemon_only, preferred. The host custodian supplies the machine credential and bootstrap command only to the daemon. Application processes see neither.
managed_runtime, accepted exception. A platform such as Cloud66 that cannot isolate the daemon's channel may expose the exact-scope credential through managed configuration. Bootstrap material still never reaches the application, and the credential can never widen into direct Infisical access.
Plaintext render remains supported as the reference and fallback path: a 0600 env file written atomically, sourced by non-interactive bash, with metadata keys for contract version, app, environment, generation time, and fingerprint.
Machine identity
Machine credentials
A machine credential is a consumer host's secret-zero for Keymaster. It is an exact grant to one app and one environment; app-wide and global machine credentials do not exist.
Property
Rule
Issued by
A global keymaster_admin only, from Admin → Access → Machine credentials. Every issue, use, and revoke is audited.
Scope
Exactly one app and one environment. A valid token for a different app or environment normalizes to auth-fail.
Authority
delivery may call export and health. reference may call only the reference endpoint, which returns non-sensitive local values and refuses sensitive keys before any backend read. Neither can use the other's endpoints.
Token handling
Generated in memory, shown once in the issuance response with no-store and no-referrer, never in a redirect, flash, session, log, or audit record. Only the digest is stored. Lost tokens are replaced, not recovered.
Lifetime
Non-production defaults to 30 days and refuses anything past 90. Production issuance is disabled until separately authorized.
Rotation
Issue the replacement, install it through the host custody path, verify exact-scope access, then revoke the old one. Revocation preserves the record; running processes keep working until their next fetch.
Rate limits
60 exports per minute per credential and 300 per minute per client IP, counted before backend work. Limits return 429; a counter-store failure returns 503 without touching the backend.
Infisical reader identities are still in flux. The design calls for one least-privilege Infisical identity per app and environment. The pinned Infisical release rejected the additional-privilege API on staging, so staging runs in keymaster_api mode: Keymaster's own exact-scope credential is the consumer boundary, and no direct backend reader is created. A production-compatible scoped-reader design is an open gate.
Runtime topology
How it is deployed today
Staging runs from the source-managed bundle in deploy/staging/: Docker Compose under systemd on one host, with managed databases outside it. Production will use the same shape through a separately approved manual procedure until Control Room takes over.
The host keeps no persistent secret except one. A root-owned 1Password service-account token. Everything else (Infisical's ENCRYPTION_KEY and AUTH_SECRET, database URIs, the GitHub client secret, Rails SECRET_KEY_BASE, the Infisical integration token) is fetched at start into a tmpfs volume and disappears when the stack stops. Keymaster and Infisical share one PostgreSQL cluster but never a database; Redis belongs to Infisical alone.
Local development mirrors the shape.deploy/local/ runs Postgres 18 on host port 55432, Redis 7 for Infisical only, and Infisical, with per-worktree Rails and CLI processes on top.
Publishing is a pinned SSH path, not the public hostname. Releases are pushed from a trusted operator checkout to the host's direct administrative SSH endpoint with a pinned host key. Caddy is never a deployment path.
Recovery is rebuild plus restore. No host backups are taken. The application is rebuilt from source and the database restored, with the historical session scrub before it serves traffic.
Reference
Surfaces
The HTTP routes and CLI commands an engineer is most likely to meet. Machine routes require HTTPS; only explicitly named loopback development URLs may use HTTP, and redirects are refused.
HTTP
Route
Who
Purpose
GET /auth/github
human
Start GitHub OAuth. Callback at /auth/github/callback.
GET /apps/:slug
human
The app's environments × keys matrix.
POST /apps/:slug/changes
human
Stage edits. /changes/apply applies the audited batch; DELETE discards.
POST …/keys/:name/reveal
human
Audited reveal of a masked value.
POST /production_step_up
human
Re-authenticate for production writes, with a reason.
GET /admin, GET /audit
global admin
Invite, Grant, Machine credentials; audit and activity reports (30 days default, 90 max).
GET /api/v1/export
delivery
Validated export for app + environment. X-Keymaster-Contract: keymaster.export.v2 selects v2.
GET /api/v1/reference
reference
One non-sensitive key's value; sensitive keys are refused.
GET /api/v1/health
delivery
Reachability and token check for the CLI.
GET /up
anyone
Rails health check.
CLI keymaster v2.1.0
Command
Purpose
export
Fetch, validate, and write a 0600 env or JSON artifact.
get
Read one non-sensitive key via the reference endpoint.
fingerprint, canary
Value-free metadata from a validated export.
status
Compare a rendered file or a daemon socket with the current export. Stale exits 17.
health
Check the health endpoint. Does not prove convergence.
serve
Run the encrypted-serve daemon.
refresh
Live refresh through the socket, or offline snapshot refresh.
serve-get
Operator lookup through the socket. Prints a value; never where output is logged.
rotate
Re-encrypt a snapshot with old + new material while the daemon is stopped.
mode-check
Verify one delivery mode is selected for the fleet unit.
version
Print the installed version.
Common flags default from KEYMASTER_BASE_URL, KEYMASTER_TOKEN, KEYMASTER_APP, and KEYMASTER_ENV. Passing a token on the command line requires the explicit --unsafe-token flag.
Orientation
Repo map and where to read next
web/The Keymaster UI and API. Rails 8, Ruby 3.4.8, PostgreSQL, Hotwire, RSpec. Services under app/services hold the domain: Authorization, AuditEvent, Api::Exporter, Infisical::*, Matrix::*, MachineCredentials::*.cli/The keymaster CLI. Go module github.com/SoundstripeEngineering/keymaster/cli. Export, render, encrypted snapshot, daemon, socket protocol, peer credentials.fixtures/consumer/A dependency-free Go consumer harness. Consumes only CLI-generated artifacts and exposes value-free diagnostics. Used for plaintext and encrypted-serve rehearsals.deploy/local/Compose stack for local development: Postgres 18, Redis 7, Infisical.deploy/staging/The manual staging bundle: Compose, Caddyfile, systemd units, 1Password bootstrap scripts, publish script, and tests.docs/specs/The interface contract and consumer requirements. The contract is normative; when this page and the contract differ, the contract wins.docs/consumer/The consumer package: onboarding, delivery modes, credentials and operations, CLI distribution, Rails on Cloud66, socket adapters.docs/system/Current repo shape, local workflow, configuration and entrypoints, access and operations policy, production deployment runbook.docs/plans/Active and completed plans. Work starts from WORKFLOW.md at the repo root.
Read next
Keymaster interface contract· the normative definition of export, errors, delivery modes, and the socket protocol