{"id":50810016,"url":"https://github.com/mathu97/dbveil","last_synced_at":"2026-06-13T04:02:37.764Z","repository":{"id":364196251,"uuid":"1266817253","full_name":"mathu97/dbveil","owner":"mathu97","description":"A local read-only, PII-redacting proxy that lets AI agents query your database safely.","archived":false,"fork":false,"pushed_at":"2026-06-12T02:37:05.000Z","size":26,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-12T04:15:15.794Z","etag":null,"topics":["claude-code","pii","pii-redaction","postgresql","safety","sql"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mathu97.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-12T01:27:48.000Z","updated_at":"2026-06-12T04:14:59.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/mathu97/dbveil","commit_stats":null,"previous_names":["mathu97/dbveil"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/mathu97/dbveil","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mathu97%2Fdbveil","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mathu97%2Fdbveil/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mathu97%2Fdbveil/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mathu97%2Fdbveil/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mathu97","download_url":"https://codeload.github.com/mathu97/dbveil/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mathu97%2Fdbveil/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34271500,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-13T02:00:06.617Z","response_time":62,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["claude-code","pii","pii-redaction","postgresql","safety","sql"],"created_at":"2026-06-13T04:02:37.253Z","updated_at":"2026-06-13T04:02:37.759Z","avatar_url":"https://github.com/mathu97.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# veil\n\n**A local read-only, PII-redacting proxy that lets AI agents query your database safely.**\n\nPoint Claude Code (or any MCP client) at `veil` instead of your database. Every query is\nforced through three deterministic guarantees before a single row reaches the model:\n\n1. **Read-only guard** — the query is parsed with Postgres's real grammar (`libpg_query`).\n   Only `SELECT` / `SHOW` / `EXPLAIN` survive. Writes, DDL, multi-statements, data-modifying\n   CTEs, `SELECT INTO`, and row locks are rejected *before execution* — not by asking the model\n   nicely, by refusing to run them.\n2. **PII redaction** — results are scrubbed before they leave your machine: deterministic\n   column rules + always-on regex for structured PII (emails, phones, cards, SSNs), with an\n   optional NER/LLM backstop for free-text.\n3. **Audit** — every query and verdict is appended to a log you can tail live in a TUI.\n\nA guarded chokepoint in front of the DB, shrunk to a single open-source command with zero\ninfrastructure to stand up.\n\n```\nClaude Code ──MCP──▶  veil  ──READ ONLY txn──▶  your database\n                       │\n                       ├─ guard:   parse → allow SELECT only\n                       ├─ redact:  column rules + regex + (optional) NER/LLM\n                       └─ audit:   veil-audit.jsonl\n```\n\n## Why\n\nYou want an agent to act as a data analyst over real tables — \"compare what we drafted vs what\nwas actually sent\" — without (a) risking a destructive query or (b) shipping customer PII to a\nmodel provider. Handing an agent raw DB credentials and hoping it only writes `SELECT` is not a\ncontrol. `veil` makes the unsafe paths impossible at the layer the agent can't talk its way past.\n\n## Install\n\n```bash\npip install dbveil          # or: uv pip install dbveil\n# optional extras:\npip install 'dbveil[ner]'   # Presidio NER backstop for names/addresses\npip install 'dbveil[llm]'   # local-LLM (Ollama) redaction\n```\n\n## Quickstart\n\n```bash\nveil init          # interactive: pick a secret source (1Password / URL / env) + auto-detect PII\nveil doctor        # verify guard, connectivity, and that READ ONLY actually blocks writes\nveil test-query \"SELECT email, created_at FROM users LIMIT 5\"   # try it without an agent\nveil up            # run the MCP proxy on stdio (what Claude Code connects to)\n```\n\nTry a write to see the guard refuse it:\n\n```bash\nveil test-query \"DELETE FROM users\"\n# BLOCKED — write or DDL operation detected: DELETE\n```\n\n### Connect Claude Code\n\n```bash\nclaude mcp add veil -- veil up\n```\n\nor commit a `.mcp.json` so your whole team gets it:\n\n```json\n{\n  \"mcpServers\": {\n    \"veil\": { \"command\": \"veil\", \"args\": [\"up\"], \"env\": { \"VEIL_CONFIG\": \"veil.yaml\" } }\n  }\n}\n```\n\nNow the agent has these tools — `query`, `list_tables`, `describe_table`, `list_databases` —\nand physically cannot write or see raw PII.\n\n## Databases \u0026 secret resolution\n\nYou never put a DSN (or a secret) directly in the file. Each database's `url` is a *reference*\nresolved at connect time, chosen by its scheme:\n\n| Reference | Resolved by |\n|---|---|\n| `op://vault/item/field` | **1Password** CLI (`op read`) — uses your existing 1Password auth; `OP_SERVICE_ACCOUNT_TOKEN` in CI |\n| `env://VAR_NAME` | environment variable |\n| `${VAR}` | inline env expansion |\n| `postgresql://…` | a literal DSN |\n\nConfigure one or many named instances; the agent picks one per query (`query(sql, database=\"prod\")`),\nor the CLI with `--db`:\n\n```yaml\ndatabases:\n  staging: { url: \"op://Engineering/veil-staging-db/dsn\" }\n  prod:    { url: \"op://Engineering/veil-prod-db/dsn\" }\ndefault: staging\n```\n\n```bash\nveil instances list            # show configured DBs and how each resolves (no secrets read)\nveil instances add             # add another database instance (interactive)\nveil doctor --db prod          # verify a specific instance\nveil test-query --db prod \"…\"  # query a specific instance\n```\n\nA single `database.url:` still works and becomes the lone `default` instance.\n\n`veil init` sets up a single database interactively: it asks how to source the connection\nstring — **browse 1Password** (lists your vaults → items → fields and writes the `op://`\nreference), paste a URL/reference, or name an env var. Choosing 1Password requires the `op` CLI\n(≥ 2.0) installed and signed in; if it isn't, init tells you exactly how to fix it and lets you\npaste a reference instead. To run against multiple environments, add a `databases:` map by hand\n(see above) — init configures one.\n\nIf your config uses 1Password, `veil setup` checks that the `op` CLI is connected and prints the\nexact desktop-app steps to enable if it isn't — without triggering Touch ID, so it's safe to run\nafter install. When `op` is set up but locked, veil tells you to unlock it (`op whoami`) rather\nthan to re-configure.\n\n### Watch it live\n\n```bash\nveil monitor       # TUI tailing veil-audit.jsonl: allowed / blocked / redaction counts\n```\n\n### Debugging\n\nAdd `--debug` (or set `VEIL_DEBUG=1`) to print logs to stderr — config loading, secret\nresolution, the exact `op` commands run and their errors, guard decisions, and row/redaction\ncounts. Secrets are never logged (DSNs are shown host-only, `op read` output as a byte count).\n\n```bash\nveil --debug doctor\nveil --debug test-query \"SELECT 1\"\nVEIL_DEBUG=1 veil up        # logs go to stderr, safe alongside the MCP stdout protocol\n```\n\n## Configuration\n\n`veil init` writes a commented `veil.yaml`. Full reference in\n[`examples/veil.example.yaml`](examples/veil.example.yaml). The essentials:\n\n```yaml\ndatabase:\n  url: ${DATABASE_URL}          # env refs kept out of the file\n\nguard:\n  allow_select_star: false      # block SELECT * on PII tables; force explicit columns\n  max_rows: 1000\n  statement_timeout_ms: 15000\n  pii_tables: [contacts, users]\n\nredact:\n  builtin_patterns: { email: true, phone: true, credit_card: true, ssn: true, ip: false }\n  columns:\n    - { column: email,     strategy: hash }      # sha256, still join-able\n    - { column: full_name, strategy: mask }      # -\u003e [redacted]\n    - { column: ssn,       strategy: partial, keep: 4 }\n  ner: { enabled: false, engine: presidio }      # optional backstop\n```\n\n## How redaction is layered (and its honest limits)\n\n`veil` defends from the **deterministic** side first, because that's the only kind you can trust\nnot to leak:\n\n| Layer | What it catches | Deterministic? |\n|---|---|---|\n| **Column rules** | Known PII columns (`email`, `ssn`, …) by name | ✅ yes |\n| **Built-in regex** | Emails, phones, Luhn-valid cards, SSNs, IPs — even aliased or in free-text | ✅ yes |\n| **NER (Presidio)** | Names / addresses in free-text the above miss | ⚠️ probabilistic |\n| **LLM (Ollama)** | Same, via a local model | ⚠️ probabilistic, experimental |\n\n**Use the probabilistic layers only as a backstop.** ML/NER *will* eventually miss a name or an\noddly-formatted address — that's a leak. For columns you already know are sensitive, the column\nrules are the real control. The LLM redactor fails *closed*: if the model errors, the cell is\nmasked, never passed through.\n\n## Security model\n\n- **Two independent read-only layers.** The parser rejects non-reads, *and* every query runs\n  inside a `SET TRANSACTION READ ONLY` transaction — so even a parser gap can't write.\n- **Give veil a least-privilege credential.** Best practice is a `GRANT SELECT`-only database\n  role (ideally on a read replica). Then \"read-only\" is enforced by the database itself, and the\n  credential `veil` holds is low-blast-radius: a leak exposes already-masked reads and can write\n  nothing. `veil doctor` confirms the READ ONLY transaction rejects writes against your DB.\n- **PII never leaves your machine unmasked.** Redaction happens in-process, before results are\n  serialized to the MCP client.\n\n## Secure connectivity\n\n`veil` connects to whatever DSN you give it, so the network path is yours to choose:\n\n- **Tailscale** — put your DB behind a tailnet and point `database.url` at the tailnet host. No\n  public DB port.\n- **Short-lived credentials** — `${DATABASE_URL}` is expanded at load, so you can inject an\n  ephemeral token (RDS IAM auth, Cloud SQL IAM, a Vault dynamic user) instead of a static\n  password.\n- **Railway / managed PaaS** — use the provided TLS endpoint with a dedicated read-only role.\n\n## Development\n\n```bash\nuv venv \u0026\u0026 source .venv/bin/activate\nuv pip install -e '.[dev]'\npytest\n```\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmathu97%2Fdbveil","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmathu97%2Fdbveil","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmathu97%2Fdbveil/lists"}