This repository builds a security tool. The same discipline applies to agents that develop it.
- Never read
key.txtorsecrets.encdirectly (test fixtures excepted, and even then only via the store package API). - Never print, log, or commit a secret value — including in test output.
- When debugging the store, use the store's own
List()(masked) or assert on masked forms, not rawGet(). e2e_test.pyintentionally asserts plaintext does NOT appear in MCP output. Keep it that way.verify_redeem.pyasserts on hashes,/proc/<pid>/environand/proc/<pid>/cmdline— never print a value to check one. Keep it that way.- If you must inspect an on-disk blob, read it as bytes and check only for the armor markers / absence of plaintext — never dump the decrypted data.
secrets.encmust never contain plaintext key material (age-encrypted).key.txtmust be0600, store dir0700.- Every MCP tool result and resource must be masked — no plaintext in tool results, ever.
putmust accept avalue_filepath, read it, and delete it after.- Masking rules live in
internal/mask— keep the entropy/prefix/URL segmentation tests green. - Redemption fails closed (
internal/redeem, docs/THREAT-MODEL.md). On any error — unknown session, expired session, name not issued in that session, missing or empty value, malformed reserved prefix, token inargv, audit write failure — the command is NOT started. There is no fallback that forwards the literal token and none that forwards the plaintext, and no error message may contain a value. - Redemption never puts a value in
argv, and never logs a value or a full argument list (<store>/audit.logrecords key names only). - A bound token must be enforced. A token issued with
--allow-host,--allow-pathor--allow-headermay only be redeemed whenkeysmith run --targetsatisfies every recorded binding (host match, path prefix, header present). A missing--target, a non-matching target, a non-https target (except loopback http) and a target carrying a query string all refuse. The binding is checked before the audit write and before the child starts, and a re-issue that would widen or drop a binding must fail withErrBindingConflict.
- Run
go test ./...before committing — all packages must pass. - Run
go vet ./...— no warnings. - Run
python3 e2e_test.pyafter any change tointernal/mcporinternal/store— the protocol round-trip must stay green. - Run
python3 verify_redeem.pyafter any change tointernal/redeemor to thetoken/runcommands — the fail-closed end-to-end checks must stay green. - Keep the Go SDK (github.com/modelcontextprotocol/go-sdk) on a version with no open OSV advisories; bump deliberately.