[VS-NfD] Store read events in their own table with retention and partitioning #224

Closed
opened 2026-07-30 01:43:53 +02:00 by fable-5 · 1 comment
Collaborator

Plan reference: 20-massnahmenplan.md -> Phase 3, Variante A
ADR: ADR 0023
Effort: M (2 AT)
Depends on: #222

Context

Read events have a different volume profile, a different retention period
and a different legal basis than audit_log. Mixing them would force one
policy onto both.

Current state

  • audit_log (apps/api/prisma/schema.prisma:74–91) holds auth and admin
    events and has no retention job yet (#196) and no partitioning.

Acceptance criteria

  • Separate table with an index set matched to the expected queries
    ("who read page X", "what did user Y read", both within a period).
  • Time-based partitioning, with the partition-maintenance job included
    and tested — not left as an operational chore.
  • Own configurable retention period, independent of #196, with a
    documented default and the deletion itself logged.
  • A Site-Admin query path exists (or its deliberate absence is
    documented) — evidence nobody can read is not evidence.
  • Growth measured and stated (rows and bytes per 1000 reads) so an
    operator can size storage.
  • docs/architecture/data-model.md documents the table.

Out of scope

SIEM forwarding of read events beyond the catalogue from #201, and
tamper-proofing.

**Plan reference:** `20-massnahmenplan.md` -> Phase 3, Variante A **ADR:** ADR 0023 **Effort:** M (2 AT) **Depends on:** #222 ## Context Read events have a different volume profile, a different retention period and a different legal basis than `audit_log`. Mixing them would force one policy onto both. ## Current state - `audit_log` (`apps/api/prisma/schema.prisma:74–91`) holds auth and admin events and has no retention job yet (#196) and no partitioning. ## Acceptance criteria - [ ] Separate table with an index set matched to the expected queries ("who read page X", "what did user Y read", both within a period). - [ ] Time-based partitioning, with the partition-maintenance job included and tested — not left as an operational chore. - [ ] Own configurable retention period, independent of #196, with a documented default and the deletion itself logged. - [ ] A Site-Admin query path exists (or its deliberate absence is documented) — evidence nobody can read is not evidence. - [ ] Growth measured and stated (rows and bytes per 1000 reads) so an operator can size storage. - [ ] `docs/architecture/data-model.md` documents the table. ## Out of scope SIEM forwarding of read events beyond the catalogue from #201, and tamper-proofing.
fable-5 added this to the M29 — VS-NfD: read-access audit trail milestone 2026-07-30 01:43:53 +02:00
fable-5 added the
vs-nfd
area:storage
effort:M
labels 2026-07-30 01:43:53 +02:00
Author
Collaborator

Implemented in PR #280 (commit 2bdb0ec), contained in the merged chain (PR #281, run 596 green; own run 593 failed only on runner infra). Monthly RANGE partitions with DEFAULT safety-net partition; daily read-trail-maintenance job (job fence 9->10) creates months ahead incl. per-partition dedup indexes and prunes under readTrail.retentionDays (default 365) - whole months by DROP, remainder by ranged delete, audited as read_trail.pruned (catalogue v1.2). Site-Admin query path GET /admin/system/read-events (pageId/actor/channel/period). Growth measured: ~1 MB per 1000 events incl. indexes (data-model.md). Fresh-DB test runs the real migration chain and proves partition shape, per-partition P2002 and DROP pruning. Live: read_events on test is relkind=p with 4 partitions.

Implemented in PR #280 (commit 2bdb0ec), contained in the merged chain (PR #281, run 596 green; own run 593 failed only on runner infra). Monthly RANGE partitions with DEFAULT safety-net partition; daily read-trail-maintenance job (job fence 9->10) creates months ahead incl. per-partition dedup indexes and prunes under readTrail.retentionDays (default 365) - whole months by DROP, remainder by ranged delete, audited as read_trail.pruned (catalogue v1.2). Site-Admin query path GET /admin/system/read-events (pageId/actor/channel/period). Growth measured: ~1 MB per 1000 events incl. indexes (data-model.md). Fresh-DB test runs the real migration chain and proves partition shape, per-partition P2002 and DROP pruning. Live: read_events on test is relkind=p with 4 partitions.
Sign in to join this conversation.
No project
No Assignees
1 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: stwaidele/dorfteich#224
No description provided.