Repository navigation
Production log redaction + retention + cost + log sink #273
Description
Activity
- changed the title
[-]Deploy: production log redaction + retention + cost + log sink[/-][+]Production log redaction + retention + cost + log sink[/+]on Jul 30, 2026 Escalation from #404 (PR #405) — 2026-08-02
Two of the exposures listed above stop being latent when #405 merges. Recording it here so this issue is scoped against what is actually shipping, not against the pre-#404 behaviour.
#404 switches the Production console sink from a human
outputTemplatetoCompactJsonFormatter. The template rendered exactly six things — Timestamp, Level, TraceId, SpanId, Message, Exception — and silently dropped every other property on the event. Compact JSON serializes all of them. Nothing about what the app attaches changed; what reaches the collector did.Concretely, for the item above about
/client-errorswriting caller-controlled content into the log pipeline:ClientErrorEndpoints.csattachesStackandComponentStack(up to 8K chars each),AppVersionandClientTraceIdviaBeginScope. Those are anonymous, caller-supplied, CRLF-sanitized but not content-redacted, and as of #405 they are retained by whatever collects stdout. Browser stacks routinely carry URLs with query strings, which can carry tokens.The startup-logging item (real email addresses) is worth re-checking on the same basis — any property that was invisible only because the template didn't name it is now emitted.
One thing that gets better: the log-forging risk behind the control-character stripping is specific to a plain-text sink. A JSON writer escapes control characters, so no property value can break out of its string and forge a record. That risk now applies to Development's template, not Production. The stripping stays unconditional regardless — the sink format is a configuration choice the emitting code cannot see.
This was raised by two independent reviewers on #405 (codex CLI and pi) and the decision there was to ship the format change and escalate here rather than pull redaction into a config PR — redaction is this issue's acceptance criterion, and defining the forbidden-field set is the substance of it.
Suggested acceptance addition: the production-shaped captured-log scan should be run against a sample containing a real
/client-errorsreport, since that is the highest-volume anonymous free-text path into the sink.- added a commit that references this issue
on Aug 3, 2026 Correction to my 2026-08-02 escalation above. That comment read as though redaction were unstarted work. It is not: PR #349 (
feat/273-log-redaction-security-events, open as a draft and updated today) already implements it, and its body states the/client-errorsStack/ComponentStackleak was confirmed and fixed end-to-end viaSensitiveDataRedactionEnricher, wired once so it covers every sink.So the exposure #404/#405 makes live is bounded by #349 landing, not open-ended. The escalation still stands as a sequencing note — whichever of the two merges second should be re-verified against the other — but the fix exists.
The two PRs barely overlap: the only shared file is
tests/Cluckwork.Api.IntegrationTests/ClientErrorReportTests.cs, where #405 only rewrites a comment that its own change made false. Worth noting for whoever rebases second: #349 touchesCluckworkTelemetryServiceCollectionExtensions.csto insert the redaction pipeline, while #405 changes onlyappsettings*.jsonand the formatter selection, so they compose rather than conflict — an enricher redacts before the formatter serializes.One consequence worth checking when they meet: #405 makes compact JSON emit every property, where the old template rendered six and dropped the rest. #349's enricher is therefore doing strictly more work post-#405 than the tests on its own branch exercise — any property that was invisible only because the template did not name it is now both emitted and in scope for redaction. Its
LogRedactionTestsshould be re-run against the JSON formatter, not only the template, before the second of the two merges.Closing: the in-repo scope shipped in PR #349 (redaction enricher + stable security events + per-event integration tests, all mutation-checked) with the forbidden-field scan pinned by LogRedactionTests against the #404 compact-JSON production shape, and the policy documented in docs/security/log-redaction-policy.md. The residue this issue names — concrete sink choice, retention window, and alert rules on the stable event ids — is deploy-side by the host-agnostic boundary and the 2026-07-31 amendment's own note, and belongs in the private deploy tracker.
- added a commit that references this issue
on Aug 18, 2026
Deployment-readiness gap (#244), RECOMMENDED — flagged by 2 of 3 reviewers.
Problem
Production logging has no redaction, retention, or cost policy, and no aggregation sink:
/client-errorsendpoint writes caller-controlled messages, routes, and full stack traces into the log pipeline (ClientErrorEndpoints.cs) (input is CRLF-sanitized + byte-capped, but not content-redacted).No repository policy names forbidden fields or retention limits.
Fix
Sample production-shaped logs before launch; redact credentials, tokens, cookies, connection strings, emails, phone/address, and sensitive free text at ingestion; choose a log sink with defined retention, access control, a sampling/volume budget, and deletion handling.
Verify
Grep a captured production-shaped log sample for the forbidden-field set → zero hits; confirm retention + access on the chosen sink.
Part of the #244 deployment-readiness audit; tracked in epic #15.
Security-event amendment — 2026-07-31
The audit found that sink selection and redaction are only half of the detection control: several security-relevant outcomes are not emitted as stable structured events. This issue now also owns safe event emission. Any host-specific alert configuration should be addressed separately in a private deploy/infra tracker.
Acceptance additions