blindfold
by xxgvqxx
README.md
# blindfold
**A local secret broker for coding agents. They use secrets they can never read.**
Claude Code, Codex, and friends need your database strings, API keys, and platform
tokens to do real work — but everything an agent sees lands in context windows,
transcripts, and provider logs, and a prompt-injected agent can be induced to leak
what it holds. Blindfold keeps the values in an encrypted local vault and lets
agents ask for *operations* instead: run this query, set this Vercel env var, run
this Railway deploy. The daemon performs the operation with the real value; the
agent gets results with every vault value scrubbed out.
```text
# what the agent sees
DATABASE_URL = «db_url:prod» # usable, not readable
> db_query("prod", "SELECT count(*) FROM users") → 48112 ✓
```
## The honest sentence
Operations the daemon performs itself (SQL, provider APIs, secret generation —
"Tier 1") can never return a value, by construction. Operations that spawn a
process with secrets in its environment (provider CLIs, your dev server —
"Tier 2") are a **raised bar, not a wall**: hostile code in that process could
read its own env. Tier 2 is defended in depth — no shell (args array only),
binary pinning, injection scoping, policy gating with native approval dialogs,
and output redaction — and every use is audited. If you want zero soft boundary,
deny Tier 2 in policy and live with typed operations only.
Management operations (registering databases and CLIs, adding secrets,
reloading policy, shutting down) require a capability token that lives in a
0600 file and that the MCP surface never sends — so an agent limited to the
MCP tools cannot reconfigure the broker around its own constraints. An agent
with a shell running as you can read that file, which is the same-user case
below.
Out of scope entirely: malware running as your user, root, and compromised
upstream CLI releases.
## Requirements
- macOS (Keychain + native approval dialogs; Linux runs with `BLINDFOLD_MASTER_KEY` set and policy-only approvals)
- [Bun](https://bun.sh) ≥ 1.1
- Postgres for the DB proxy (covers Neon, Supabase, Railway Postgres, RDS)
## Quick start
```bash
git clone https://github.com/xxgvqxx/blindfold && cd blindfold
bun install
bun run bf init # vault + policy + daemon + MCP instructions
bun run bf add api_key:stripe # hidden prompt — paste the value
bun run bf db add mydb --mode ro # hidden prompt for the connection string
bun run bf cli preset vercel # pin the vercel CLI, store its token
bun run bf import .env --project myapp # import a dotenv, then delete the file
# register with Claude Code (bf init prints the exact command):
claude mcp add --scope user blindfold -- bun run /path/to/blindfold/src/mcp/index.ts
```
The daemon starts on demand and holds the vault key from the macOS Keychain
(`BLINDFOLD_MASTER_KEY` env var on Linux/CI). State lives in `~/.blindfold/`.
## What the agent gets (19 MCP tools)
| Tool | Boundary | What it does |
|---|---|---|
| `secrets_list` | hard | names, kinds, tags — never values |
| `secrets_request` | hard | native dialog asks *you* to paste a new secret |
| `secrets_generate` | hard | random secret no one ever sees |
| `db_list` / `db_schema` / `db_query` / `db_explain` | hard | SQL over the daemon's own connection; `ro` aliases run every query in a server-side READ ONLY transaction (single statement per call, so no embedded `COMMIT` can escape it); results capped + scrubbed |
| `env_list` / `env_pull` / `env_set` / `env_rm` / `env_diff` / `env_push` | hard | git-like env sync with Vercel (incl. `preview:<branch>`) and Railway; drift detected, never clobbered |
| `cli_run` | soft | pinned provider CLI, token injected, output scrubbed, policy-gated |
| `env_run` | soft | dev server / build with imported env; `prod`-tagged secrets never injected |
| `cli_list` / `project_list` / `audit_tail` / `policy_explain` | meta | introspection; `policy_explain` dry-runs the gate |
## Policy
`~/.blindfold/policy.toml` — ordered rules, first match wins, sensible defaults
underneath (reads allow; prod+rw SQL and prod pushes require approval; Tier 2
requires approval unless the subcommand is registered read-only). `remember =
"8h"` turns one approval into a session grant. Approvals are native macOS
dialogs that time out to deny.
```toml
[[rule]] # trust vercel passthrough on one project for 8h per approval
op = "cli_run"
where = { tool = "vercel", project = "myapp" }
action = "approve"
remember = "8h"
[[rule]] # never let the agent touch a prod rw database
op = "db_query"
where = { alias_tag = "prod", alias_mode = "rw" }
action = "deny"
```
## Redaction
Everything that leaves the daemon — query rows, CLI output, error messages —
passes one scrubber that knows every vault value plus its base64, URL-encoded,
and JSON-escaped forms, and the individual credentials inside connection
strings. Replacements are labeled (`«db_url:prod»`) so agents can debug without
learning anything. Arbitrary transforms by hostile Tier-2 code are not
catchable — redaction is the last layer, not the boundary.
## Status
v0.1 — working: vault, daemon, policy/approvals/audit, redaction, Postgres
proxy, Tier-2 runner with pinning, Vercel + Railway env sync, dotenv import,
19-tool MCP server, `bf` CLI. 56 tests, including live-Postgres tests that try
to escalate a read-only alias six different ways and an end-to-end suite that
proves injection + redaction with real processes. Untested against live provider APIs: the Vercel
`decrypt=true` pull and the Railway GraphQL schema — treat `env_pull`/`env_push`
as beta until exercised. Not yet built: Modal/Supabase typed verbs (use
`bf cli preset` passthrough), Touch ID helper (osascript dialogs today), MySQL,
Linux keyring, launchd install.
## Pointing your agents at it
Once the MCP server is registered, agents *can* use the vault — the
`docs/AGENTS-snippet.md` file is what makes them *prefer* it. Paste it into
each project's `CLAUDE.md` / `AGENTS.md`, import your `.env` files with
`bf import`, and delete the plaintext originals. When there is no file to
read, the brokered path is the only path.
## Contributing
Adapters are the highest-leverage PRs — the interface is four methods in
`src/daemon/adapters/types.ts` (Fly, Render, Neon, Cloudflare all wanted).
`bun test` runs the suite, including an end-to-end daemon test that proves
injection + redaction with real processes.
## License
MIT
This server cannot be deployed
Maintenance
ActivitySlowing
ResponsivenessNo issues