Skip to content

Add keyed hmac256 mask strategy (CDC_MASK_HMAC_KEY) - #11

Merged
emoss08 merged 1 commit into
mainfrom
claude/custom-cdc-review-zth3ax
Aug 11, 2026
Merged

emoss08 merged 1 commit into
mainfrom
claude/custom-cdc-review-zth3ax

Conversation

@emoss08

@emoss08 emoss08 commented Aug 11, 2026

Copy link
Copy Markdown
Owner

What does this PR do?

Adds an hmac256 mask strategy to the declarative transforms, closing a real weakness in the existing sha256 mask: an unkeyed hash of low-entropy PII (emails, phone numbers, SSNs) can be reversed by hashing candidate values and comparing digests. hmac256 is HMAC-SHA256 keyed with a new CDC_MASK_HMAC_KEY env var — still deterministic, so masked columns keep joining and deduping across events and sinks, but useless to an attacker without the key.

Details:

  • transform.Options carries the key; the new CompileWithOptions threads it into strategy resolution (Compile keeps its old signature for keyless use).
  • Using hmac256 in any transform without CDC_MASK_HMAC_KEY set fails at startup with an error naming the env var — consistent with how bad CEL expressions and unknown strategies already behave.
  • The key stays out of the sink YAML so the config file remains secret-free; it's loaded in config.Load and wired through newSinks.
  • Docs note the rotation caveat: rotating the key changes every digest, so previously-emitted masked values stop matching new ones.

Type of change

  • New feature / sink

Checklist

  • go test ./... passes (including -race)
  • gofmt / go vet are clean
  • Tests added or updated for changed behavior (determinism under one key, key-dependence, divergence from unkeyed sha256, missing-key compile error)
  • README.md / CLAUDE.md updated if env vars or behavior changed (env table, transforms section, sinks.example.yaml)
  • If this touches delivery semantics (LSN acknowledgment, sink error handling, TOAST handling): not touched — masking runs inside the existing transform layer, outside resilience/DLQ

Notes for the reviewer

  • Existing sha256 behavior is unchanged; hmac256 is opt-in per column, so nothing shifts under current configs.
  • Parked DLQ entries hold already-transformed events and retries bypass transforms, so a key change between park and retry has no effect on parked payloads.

🤖 Generated with Claude Code

https://claude.ai/code/session_019dEj24Ta2o9QxzW9ZrSPtb


Generated by Claude Code

The plain sha256 mask is deterministic but unkeyed, so low-entropy PII
(emails, phone numbers, SSNs) can be recovered by hashing candidate
values and comparing digests. hmac256 is HMAC-SHA256 keyed with the new
CDC_MASK_HMAC_KEY env var: still deterministic — masked columns remain
joinable across events and sinks — but useless to an attacker without
the key.

- transform: Options carries the key; CompileWithOptions threads it to
  strategy resolution. Using hmac256 without the key fails at startup,
  like every other transform misconfiguration.
- config: load CDC_MASK_HMAC_KEY; wire it through newSinks.
- Tests: determinism, key-dependence, divergence from unkeyed sha256,
  and the missing-key compile error.
- Docs: README (features, env table, transforms section, key-rotation
  caveat), CLAUDE.md, sinks.example.yaml.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019dEj24Ta2o9QxzW9ZrSPtb
@emoss08
emoss08 merged commit dc886e9 into main Aug 11, 2026
6 checks passed
@emoss08
emoss08 deleted the claude/custom-cdc-review-zth3ax branch August 11, 2026 19:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants