Your users table has held plaintext PII for years, a dozen services read it directly, and the migration has been “next quarter” since 2022. The blocker was never the vault — it was the diff, and that is the part Claude or Cursor now writes for you.
The Challenge
Every service reads PII straight from the table, so this is dozens of call sites, not one migration — and nobody wants to own that diff. Meanwhile one SQL injection or over-permissive GraphQL query dumps the lot, and disk encryption does not help, because the query is authorised.The Solution — Four Capabilities
1. Fuzzy search on encrypted data
Find a record from a misspelled name or partial email — no decryption, no plaintext index.
2. Bulk reads are default-deny
A stolen app token cannot dump the vault: bulk reads need a separate 60-second unlock.
3. Record versioning and audit trail
Every create and update keeps an immutable, integrity-checked version of the profile.
4. Access control and tenant isolation
CRBAC scopes who may read which fields; row-level security keeps tenants apart.
How It Works
Two prompts and a review queue: inventory the PII, then rewrite the call sites.# the agent writes the diff; your engineers review the PR
- user = db.query("SELECT name,email,card FROM users WHERE id=?", id)
+ user = bunker.userGet("token", row.user_token)
# users table is now: id SERIAL PRIMARY KEY, user_token UUID
Go service by service — the vault runs alongside your database, so there is no big-bang cutover. Backfilling a very large vault is database-bound and takes hours.
The Payoff
- An injection or over-fetch returns tokens, not customers.
- PII disappears from logs, backups and staging dumps.
- Joins that used
emailnow join on the token, with no round trip. - Reviews stop stalling on who can read the users table.
Honest limits: the code does change — the agent writes the diff, your engineers review it. Tokenized data remains personal data under GDPR.