feat(resume): commit redacted client names so builds need no sops key - #143
Conversation
CI and the Netlify build needed SOPS_AGE_KEY only to decrypt the client names and mask them right away. Netlify exposes build-scoped variables to the whole build, including its automatic `pnpm install`, so the key could not be kept away from dependency code there. `pnpm mask-clients` now decrypts locally and writes the masked names as plain fields next to the encrypted ones. Normal builds read those, and only RESUME_MODE=unredacted still calls sops. It has to be run after editing encrypted fields; a build fails if an entry has encrypted fields but no masked ones, which catches a forgotten run for a new client, but not a renamed one. linkedin-sync reads the decrypted file and preferred `entity` over `sopsEncryptedEntity`, so it would now have compared LinkedIn against the masked names. It prefers the decrypted name instead. Masked and unredacted .generated output and linkedin-sync's resume output are byte-identical to before. Verified with `pnpm check:quick` and a Netlify-like build in a node:26 container without any key. Closes #141
The code used "masked" and "redacted" for the same thing, and the resume modes even paired "masked" with "unredacted". The data (`redacted: true`), the redacted-client pages and RESUME_MODE=unredacted already said "redacted", so that term stays: RESUME_MODE=redacted, `pnpm redact-clients`, and `redactEntity`.
✅ Deploy Preview for roschaefer ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (15)
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review. 📝 WalkthroughWalkthroughResume builds now use committed redacted client fields by default. A new command generates those fields from encrypted data. Unredacted generation still decrypts the source. CI and Netlify build configuration no longer require the SOPS key. ChangesResume redaction and generation
Priority: ➖ Normal Estimated code review effort: 3 (Moderate) | ~25 minutes Change: Feature Sequence Diagram(s)sequenceDiagram
participant Build
participant generate as generate-resume-source.ts
participant source as resume.i18n.json
participant resolver as resolveSopsEncryptedFields
participant output as generated resume source
Build->>generate: run with default redacted mode
generate->>source: read committed redacted fields
generate->>resolver: resolve redacted fields
resolver-->>generate: return resume data without encrypted fields
generate-->>output: write resolved source
Merge Risk: ⚪ Minimal · up to Default builds now use committed redacted client names and no longer need the SOPS key. Tests cover the new behavior, and no concrete merge-blocking issue was found. Security Architecture ReviewSecurity architecture risk: 🟡 Moderate · up to Keyless builds reduce routine access to private client identities. However, hosted build setup no longer strips an inherited decryption key before downloading and running tooling. If a previously configured key remains, its exposure expands; removal of that hosted secret has not been confirmed. Retained concerns
Security review detailsSecurity Blast Radius
Security Findings and Attack Paths
Trust Boundaries and Controls
Hardening Proposals
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
CI and the Netlify build needed SOPS_AGE_KEY only to decrypt the client names and redact them right away. Netlify exposes build-scoped variables to the whole build, including its automatic
pnpm install, so the key could not be kept away from dependency code there.pnpm redact-clientsnow decrypts locally and writes the redacted names as plain fields next to the encrypted ones. Normal builds read those, and onlyRESUME_MODE=unredactedstill calls sops. It has to be run after editing encrypted fields; a build fails if an entry has encrypted fields but no redacted ones, which catches a forgotten run for a new client, but not a renamed one.The code used "masked" and "redacted" for the same thing, and the resume modes even paired "masked" with "unredacted". The data (
redacted: true), the redacted-client pages andRESUME_MODE=unredactedalready said "redacted", so that term stays.Redacted and unredacted
.generatedoutput is byte-identical to before. Verified withpnpm check:quickand a Netlify-like build in a node:26 container without any key.Closes #141