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

  1. The component stores data linked to a Koder ID subject (via

    external_login_user or equivalent). If not, the row is N/A with one-line rationale.

  2. Open a phase-1 ticket on the component for the listener +

    production store + unit tests (model: FLOW-117).

  3. 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.

  4. Reference the Flow row (#3) as a worked

    example.

Cross-references