feat(privacy): audited redaction that keeps history's shape and removes its content

Rows in append-only history are never deleted; their content can now be
made irrecoverable through one audited path. A redaction replaces the
content columns of an event, memory, message, model call, belief or
assertion with a fixed marker, keeps identifiers, links, timestamps,
scores and breakdowns, writes an append-only ledger row with a SHA-256 of
the removed content, appends a redaction event, optionally cascades to
derived rows and to the events that quote them, and scrubs stored snapshot
archives before re-signing them. noema_redact() is the only write the
append-only trigger admits, for UPDATE only, under a transaction-local flag
the application never sets; plain updates and all deletes stay refused.
Replay labels redacted stimuli instead of inventing them.

Cryptographic erasure was considered and deferred with reasons (ADR-0009);
docs/privacy.md defines retained, redacted, irrecoverable and deleted and
states what backups and earlier exports are not covered. Migration 0023.
This commit is contained in:
Andy Dixon
2026-08-23 23:52:07 +01:00
parent 9eed6e2a18
commit 7d608c2783
33 changed files with 1245 additions and 45 deletions
+20
View File
@@ -0,0 +1,20 @@
# ADR-0009: Redaction as the only write on append-only history
Date: 2026-09-15
Status: Accepted
## Context
ADR-0002 made cognitive history append-only and promised a "dedicated, audited maintenance path" for removal. Privacy law and ordinary decency require that personal content can be made irrecoverable, while provenance, replay and forensic reconstruction require that the *shape* of history survives.
## Decision
Content is redacted, rows are never deleted. A `redactions` ledger (append-only) records target, reason, policy, requester and a SHA-256 of the removed content. One SQL function, `noema_redact()`, performs the replacement under a transaction-local flag that the append-only trigger admits for UPDATE only; the function is `SECURITY DEFINER`, revoked from `PUBLIC`, and the application never sets the flag. Content columns are replaced by a fixed marker; identifiers, types, timestamps, links, scores and breakdowns are kept. A `redaction` event is appended. Cascade follows recorded derivations one level (episodes, messages, model calls, assertions, beliefs rooted at the event, and the events that quote each of them). Stored snapshot archives are scrubbed and re-signed.
## Consequences
- Irrecoverability covers everything the system controls; backups, exports and replicas made earlier are the operator's responsibility and are documented as such.
- History keeps its structure: replay labels redacted stimuli instead of inventing them; belief provenance remains inspectable without its text.
- The append-only guarantee is weakened by exactly one, auditable, content-only write path.
## Alternatives considered
- Cryptographic erasure (per-row keys destroyed on request): the strongest option, rejected for now because search, retrieval, contradiction detection, replay and the interface read the columns in clear and per-row decryption would touch every read path; recorded as future work and recommended for backups.
- Disabling the trigger and deleting rows: breaks provenance links and replay, and hides that anything happened. Rejected.
- Leaving content in place with access controls: does not satisfy erasure. Rejected.