
PII scrubbing before embedding: what we redact and why
Your codebase contains more secrets than your secrets manager does. API keys hardcoded in old commits, customer emails in fixture files, JWT tokens checked in for a demo nobody cleaned up. Here's exactly what Repotrix redacts before a single byte of your code reaches an embedding model.
The first time you point a code-analysis tool at a real repository, you discover something humbling: developers leak secrets constantly. Not maliciously — accidentally. An AWS key pasted into a test file because the CI environment wasn't set up yet. A bcrypt hash committed during a debug session. An entire customer email list dropped into a fixtures directory for a one-off migration. The repo isn't actively breached, but it's a smorgasbord for anyone who gains access.
For an embedding-and-RAG product, this matters more than for a static analyzer. The static analyzer reads the file and reports findings — the file stays on your disk. The RAG product reads the file, sends 1,000-character chunks to an embedding model, and stores the resulting vectors in a managed database. Every secret in your code becomes a vector that's been transmitted to a third party. Even if the embedding API doesn't train on inputs (Google's developer-tier Gemini doesn't), the secret has now crossed a boundary it shouldn't have.
Repotrix scrubs PII and credentials BEFORE chunking. The scrub happens in-memory inside the ARQ worker process, between the git clone and the chunker. Nothing leaves the worker un-redacted.
What we look for, in priority order:
1. **AWS credentials** — Access keys (`AKIA[0-9A-Z]{16}`), secret keys (40-char base64 in proximity to the word 'aws_secret'), STS session tokens (much longer). Pattern matching is cheap; we run it across every file.
2. **Generic API keys** — `sk_live_...` (Stripe), `xoxb-...` (Slack), `ghp_...` (GitHub), `AIza...` (Google), `eyJ...` (anything JWT-shaped). The list grows whenever we see a new pattern in production telemetry.
3. **Email addresses** — Standard RFC pattern. Customer emails in fixtures are the noisiest false-positive source for this; we tune the regex to also catch obvious obfuscations like `user [at] domain.com`.
4. **JWTs and bearer tokens** — A three-segment base64 string separated by dots and surrounded by either whitespace or quotes. The base64 alphabet check catches incomplete JWT-looking strings that aren't actually JWTs.
5. **Phone numbers and SSN-shaped strings** — North American formats by default; you can disable these for non-US repos to cut false positives.
When a match is found, we don't just delete it. Deletion changes line numbers, which silently breaks every later citation our chunker produces. Instead, we replace each match in-memory with a placeholder of the same length: `[REDACTED:AWS_KEY]` (padded with `_` to match the original). The file's structure stays exactly the same; the secret is gone.
We also keep a SHA-256 hash of every redacted value, never the value itself. The hash is stored in the compliance audit table alongside the file path and line number. If an auditor later asks 'did this specific exposed key ever leave our boundary?' we can prove it didn't by showing the hash entry — without us having to retain the secret to prove it.
False positives are real and have to be managed. The most common one we see is regex matches inside test fixtures — a base64 string in a unit test that LOOKS like an API key but is in fact a deliberately-fake placeholder. We mitigate this with a contextual filter: if the match sits inside a file whose path contains `test/`, `tests/`, `__fixtures__/`, or `mock`, AND the match is surrounded by string literal delimiters, AND no real key prefix matches (like `sk_live`), we lower the confidence score. High-confidence matches still get redacted; low-confidence matches get flagged in the compliance report so a human can review.
The biggest false NEGATIVE we've seen: bespoke internal API keys that don't match any public format. A company invents a key like `RPTX-2026-PROD-xxx` and we don't know about that pattern. Catching these requires either (a) the customer telling us about their key format, or (b) entropy analysis — flagging any high-entropy string above a certain length, accepting more false positives in exchange for fewer false negatives. We do (b) as a fallback for any string above 32 characters with high Shannon entropy, with a lower confidence score than the explicit pattern matches.
The net effect: by the time the ARQ worker hands chunks to the embedding service, the chunks contain zero detectable credentials. The embedded vectors in Pinecone don't carry your secrets. The cached LLM responses don't carry your secrets. The PII you exposed in your codebase doesn't escape your repository's boundary — even though it's right there in the code.
This is the unsexy part of an AI-on-code product, and we spend a lot of engineering time on it. The reason is simple: a single leak undoes years of trust, and we'd rather refuse to embed something ambiguous than send a key to a third party 'just in case it isn't real.' Better a slightly noisier compliance report than a customer's secret in a vector store we don't own.




