LGPD Erasure — Conformance & Coverage Registry
Tracks which Koder components implement the cross-service erasure cascade contract per policies/identity-data-retention.kmd § R5 and specs/identity/erasure-flow.kmd.
When a user issues DELETE /v1/me on the Koder ID side, the request enters a 24-hour grace window then publishes identity.user.erased on the cross-service event bus. Every Koder component that stores data linked to a Koder ID subject MUST subscribe to that event (or be explicitly opted out per the Skip table below) and apply the LGPD cascade — purge linked data, drop sessions, anonymize audit events for the 6-month forensics window.
This registry agglomerates the per-component conformance state. Pre-release release engineering MUST verify the row for the releasing component is at PASS (or PASS¹ — see the status enum) before tagging.
Status enum
| Status | Meaning | Effect on AI / release engineering |
|---|---|---|
| PASS | Listener wired into the long-running entrypoint, cascade asserted end-to-end against a real fixture (integration test green in CI), audit event emitted on koder:events:flow-equivalent stream, regression suite locking the contract surface in place. |
Release engineering green-lights. AI can recommend the component as an LGPD-cascade reference. |
| PASS¹ | Same as PASS modulo a single deferred sub-ticket named in the notes — typically an end-to-end integration test still landing while the listener + production store + audit emission + regression are already shipped. | Release engineering green-lights with a follow-up gate (cited sub-ticket). |
| PARTIAL | Some cascade elements live, others pending. Notes column enumerates what is and isn't. | Release engineering blocks; sub-ticket required to close. |
| TODO | Component stores Koder-ID-linked data but no listener exists yet. | Release engineering blocks; cascade ticket required before next major. |
| N/A | Component does not store Koder-ID-linked data (read-only proxy, stateless renderer, etc.). | Release engineering green-lights with no cascade requirement. |
| SKIP | Component is itself the identity provider (Koder ID) or otherwise outside the consumer surface — cascade contract does not apply. | Release engineering green-lights. |
Cascade surface
For each Koder component the registry tracks:
- listener — the long-running goroutine that subscribes to
the erasure stream
- store — production binding to the component's data layer
(the adapter that actually deletes / anonymizes)
- audit — completion-event emission (
<service>.erasure.completed)on the canonical
koder:events:<service>stream - integration — end-to-end test against a real fixture
- regression — structural test locking the contract surface
- wiring — listener actually booted from the entrypoint
A row is PASS when all six columns are green for the component's storage surface.
Components
| # | Date | Component | Status | listener | store | audit | integration | regression | wiring | Notes |
|---|---|---|---|---|---|---|---|---|---|---|
| 1 | 2026-05-24 | services/foundation/id/engine/services/auth |
PASS | ✓ | ✓ | N/A | ✓ | ✓ | ✓ | In-process listener services/auth/internal/listener/erasure_listener.go consuming the same event bus directly inside idengine. No audit emission needed — idengine is the source-of-truth for the cascade state. |
| 2 | 2026-05-24 | services/foundation/id/engine/services/sso |
PASS | ✓ | ✓ | N/A | ✓ | ✓ | ✓ | Sibling of #1; same architecture. |
| 3 | 2026-05-24 | products/dev/flow/engine/services/erasure (Koder Flow) |
PASS | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | Promoted PASS¹ → PASS on 2026-05-24 when FLOW-135 shipped — end-to-end cascade integration test landed in tests/integration/erasure_cascade_test.go (2 tests, both green in 5.5s against the standard XORM SQLite fixture). Full FLOW-119 epic done (FLOW-117 Phase 1 + FLOW-130..139). Concrete coverage: listener services/erasure/listener.go (FLOW-117 — 11 unit tests against fakeStore covering no-linkalready-purgedhappy-pathlookup-errorpurge-errormalformed-envelope). store [`serviceserasureproduction_store.go`](........productsdevflowengineserviceserasureproductionstore.go) (FLOW-131 — 1:1 adapter to `usermodel.GetUserIDByExternalUserID + usermodel.GetUserByID + userservice.DeleteUser(ctx, u, true), compile-time var _ FlowUserStore assertion). **audit** [serviceserasureaudit_publisher.go`](........productsdevflowengineserviceserasureauditpublisher.go) (FLOW-132 — emits flow.erasure.completed on koder:events:flow with `sourceeventid / flowuserid / flowusername / elapsedms data fields, best-effort semantics, 7 unit tests including 2 negative-firing assertions for no-link and already-purged paths). **integration** [testsintegrationerasurecascadetest.go`](........productsdevflowenginetestsintegrationerasurecascadetest.go) (FLOW-135 — TestErasureCascade_RealForgejoData drives full cascade against fixture user 2 + linked Koder ID subject, asserts user row purged + externalloginuser gone + audit envelope shape; TestErasureCascade_NoLinkedUser_AckedNoOp asserts no-op path doesn't emit audit). regression [`testsregressionerasure001-no-eventbus-mirror.test.sh](../../../../products/dev/flow/engine/tests/regression/erasure/001-no-eventbus-mirror.test.sh) (FLOW-136 — anti-mirror-reintroduction guard locking the FLOW-130 Option A decision: go.mod require+replace pair, upstream import present, four types declared as aliases, Flow-namespaced constants stay local; negative-tested with struct injection). **wiring** [routersinit.go`](........productsdevflowengineroutersinit.go) erasure.Boot(ctx) sibling of backup_scheduler.Boot(ctx) (FLOW-133, RFC-004 Phase 2 pattern), env-var + app.ini [flow_erasure] config plumbing (FLOW-134 — setting.FlowErasure with KODER_FLOW_ERASURE_ENABLED + KODER_REDIS_URL + KODER_FLOW_ERASURE_CONSUMER_ID overrides). CI .koder-flow/workflows/erasure-cascade.yml (FLOW-137 — path-filtered; structural + unit + integration jobs all active as of 2026-05-24). Privacy template contrib/legal/privacy.html.sample §"Data Retention and Deletion of Data" rewritten 2026-05-24 (FLOW-138) to reflect the 24h-grace + cascade + 6-month-anonymized contract (deployment to live instances gated on owner signoff per memory [KDS owner-curated content]). Auth-coverage cross-ref: koder-id-auth-coverage.md Flow row 2026-05-12. Production posture: cascade is dormant on flow.koder.dev until operator sets KODER_REDIS_URL (default-off-on-empty-URL per FLOW-133 boot logic). FLOW-143 (2026-05-25): the listener now cascades on admin-driven identity.user.deleted (no-grace purge) in addition to the grace-finalized identity.user.erased — closing the contract gap where an admin delete in Koder ID left the Flow user row + repos alive. identity.user.erasure_requested (grace start) gets an observability log only (grace_started outcome), no cascade. Admin deletes emit the new sibling audit type flow.deletion.completed (distinct from flow.erasure.completed to keep existing consumers wire-stable per always-on.kmd §2); both audit envelopes now carry an additive source_event_type field. New coverage: TestHandle_AdminDelete_PurgesLinkedUser, TestHandle_ErasureRequested_LogsButNoCascade, TestAuditPublisher_Emit_DeletionPath, integration TestErasureCascade_AdminDelete_RealForgejoData. |
Skipped components (explicit out-of-scope decisions)
Components reviewed and declared outside the LGPD cascade surface with rationale. A Skip is reversible — when concrete demand surfaces, open a new cascade ticket on the component and remove the row.
| # | Component | Decision | Rationale |
|---|---|---|---|
| S1 | services/foundation/id (itself) |
SKIP (2026-05-24) | Koder ID is the cascade source, not a consumer. The DELETE /v1/me endpoint + the cascade orchestrator + the 24h-grace timer + the bus publisher all live inside idengine; there is no separate listener to write. The in-process listeners in `servicesauth and services/sso` (#1, #2 above) are separate Sectors counted independently. |
Adding a new component
- The component stores data linked to a Koder ID subject (via
external_login_useror equivalent). If not, the row is N/A with one-line rationale. - Open a phase-1 ticket on the component for the listener +
production store + unit tests (model: FLOW-117).
- Open phase-2 sub-tickets following the FLOW-119 split
(FLOW-130..139): eventbus dep decision, production_store, audit_publisher, startup wiring, config knobs, integration test, contract-drift regression, CI workflow, privacy template (if applicable), and this registry row.
- Reference the Flow row (#3) as a worked
example.
Cross-references
- Policy:
policies/identity-data-retention.kmd § R5 - Spec:
specs/identity/erasure-flow.kmd § R4 - Spec:
specs/messaging/cross-service-events.kmd(envelope contract, stream naming, consumer-group semantics) - Sibling registry:
koder-id-auth-coverage.md(OAuth client surface conformance — every cascade consumer is also an OAuth client and should be green in both registries)