AES-256-GCM API key encryption and color-based PDF redline extraction

Jeroen1991z closes the plaintext-keys-in-Supabase gap with per-user AES-256-GCM encryption, and adds redline extraction for both DOCX tracked changes and color-annotated PDFs - though the two features land in a single unbundled commit.

contract-reviewsecurity

The encryption work is self-contained. A new backend/src/lib/encryption.ts handles AES-256-GCM using a 32-byte hex key from MIKENL_ENCRYPTION_KEY. On write, PUT /user/api-key encrypts before storing. On read, userSettings.ts decrypts. The frontend is rewired to save keys through the backend endpoint instead of writing directly to Supabase. If MIKENL_ENCRYPTION_KEY is absent the keys fall through unencrypted, which preserves backward compatibility.

The redline extraction is two-layer. DOCX: docxTrackedChanges.ts gains renderParagraphRedline() which walks w:ins and w:del OOXML nodes and emits {++ins++}, {--del--}, and {>>by AUTHOR: comment<<} markers. PDF: scripts/redline_extract.py uses PyMuPDF to read per-span color values and strikethrough flags, mapping red/strikethrough to deletions, blue to insertions, and green to comments. The Node side shells out to the script via stdin/stdout. extractPdfText() tries the redline path first; if the output is under 50 characters it falls back to pdfjs.

The system prompt is extended with a TRACKED CHANGES AND REDLINE MARKUP section so the model knows how to interpret the markers and how to reason about current vs. original versions.

The two features are in a single commit. Cherry-picking one requires manually splitting the diff - the encryption changes are in encryption.ts, userSettings.ts, routes/user.ts, and UserProfileContext.tsx; the redline changes are in docxTrackedChanges.ts, pdfRedlineExtract.ts, redline_extract.py, and chatTools.ts.

So what Worth a look for the encryption piece alone if your fork stores user-provided LLM keys. The redline extraction is useful for contract review workflows, but check whether Python + PyMuPDF fits your deployment before committing to the subprocess approach.

View this fork on GitHub →

Spotted something wrong? Or know the PR text has fresher detail than the writeup above?

Commits in this thread

1 commit from Jeroen1991z/mikeNL, oldest first. Source extracted verbatim from the harvested git log.

SHA Subject Author Date
7735ccfd Add API key encryption and DOCX/PDF redline extraction Claude 2026-05-05 ↗ GitHub
commit body
- AES-256-GCM encryption for user API keys at rest (encryption.ts)
- userSettings.ts decrypts keys on read; user.ts PUT /user/api-key encrypts on write
- Frontend routes API key saves through backend endpoint instead of direct Supabase writes
- DOCX tracked-changes extraction: renderParagraphRedline surfaces {++ins++}, {--del--}, {>>comments<<}
- PDF redline extraction via Python/PyMuPDF subprocess (pdfRedlineExtract.ts + scripts/redline_extract.py)
- System prompt updated to explain tracked-change markers to the AI
- .env.example documents MIKENL_ENCRYPTION_KEY and PYTHON_BIN

https://claude.ai/code/session_016JxWnFc3baeDgkzDgxY6ex

Capture this thread into my fork

Download a single Markdown prompt that tells Claude how to port every commit above into your working tree — adapting paths and structure to match your repo. Run it via claude -p < capture-thread-14.md from inside the repo you want the changes in.

⬇ Download capture-thread-14.md