dorfteich/docs/architecture/adr/0023-read-access-audit-trail.md
Claude Opus 5 fd07f716f6
All checks were successful
CI / Lint, typecheck, test (pull_request) Successful in 4m42s
CI / Build container images (pull_request) Successful in 1m11s
CI / Auth e2e pack (pull_request) Successful in 7m47s
CI / Import/export fidelity gate (pull_request) Successful in 55s
CD / Build and push images (push) Successful in 18s
CD / Deploy to Test (push) Successful in 14s
CD / Smoke tests against Test (push) Successful in 1m16s
CD / Promote to Int (push) Successful in 11s
CI / Lint, typecheck, test (push) Successful in 4m50s
CI / Build container images (push) Has been skipped
CI / Auth e2e pack (push) Successful in 7m38s
CI / Import/export fidelity gate (push) Successful in 56s
docs: VS-NfD readiness planning (ist-aufnahme, plan, ADRs 0019-0026, issue drafts)
Add docs/vs-nfd/: the analysis brief, the as-is assessment (42 findings,
all verified against the code), the prioritized action plan rev. 2 with
issue references written back to every checkbox, the two-stage issue/ADR
brief, and the full reviewed draft used to create the forge state.

Add eight proposed ADRs 0019-0026 covering the VS-NfD architecture
decisions: no security base functions (par. 52 VSA anchor), HKDF token
key separation, external authentication, page classification, read-access
audit trail (variant A), reproducible offline deployment, plugin trust
model, and backup target restriction.

Forge state created alongside this commit: 11 labels, milestones M24-M31,
issues #188-#236 (docs-only change, no code touched).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0168Ph5uBmHm8X28CSVpbpnJ
2026-07-30 01:48:05 +02:00

71 lines
3.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# ADR 0023: Read-access audit trail limited to classified content
- Status: proposed
- Date: 2026-07-29
## Context
The existing audit trail (`audit_log`, issue #86) is deliberately scoped to
"who changed access or configuration", and its own service comment states
that content activity stays log-only. There is no record of _reads_.
For "operable in an approved environment", read logging is not a mandatory
product feature — evidence collection can be a platform function. In
practice platform logging cannot answer the question that matters: a proxy
log knows URLs, not classifications, so it cannot say which _classified_
page was read. Leistungsbeschreibungen tend to list this as a must.
Two variants were considered. Variant B logs all reads (1820 AT) and
brings volume, latency and retention problems, plus the requirement that no
event may be lost. Variant A logs reads of classified pages only (810 AT).
A live editing session is the volume hazard: Yjs sync means continuous
traffic per open document.
## Decision
**Variant A: read events are recorded only for pages with
`classification = VS_NFD`.** Requires ADR 0022.
1. **All read channels are instrumented**, or the feature is worthless:
SPA page fetch, public API GET, attachment download, export, no-JS
shell, collab WS join.
2. **A dedup window** (session + page + channel within N minutes = one
event) keeps Yjs sync from flooding the trail. The recorded event states
that it represents a window, so the evidence is not overread.
3. **Its own table**, with time partitioning and its own retention period —
independent of `audit_log`, because volume, purpose and legal basis all
differ.
4. **Failure is not silent.** `AuditService` swallows write failures by
design; for classified reads a lost event is a gap in evidence, so the
behaviour is either hard failure or an explicitly documented
degradation. Which one is decided in #222 and stated in the security
documentation.
5. **Switchable, with a written purpose limitation.** Off means nothing is
written anywhere; a startup log line states the trail is off so a gap is
never ambiguous.
6. **Variant B is rejected**, and the rejection is recorded rather than
left open: unbounded volume, the no-loss requirement, and a purpose
limitation that is much harder to defend.
## Consequences
- The scope limit is the feature's strongest argument in the works-council
discussion at the customer: only classified content is observed.
- Reads of unclassified content are not evidenced. Deliberate, and it goes
into the residual-risk list.
- The collab WS join is the awkward channel: authorization there is
token-only (signature plus `pageId` match) and the collab server has no
permission context. Either the event carries what the token asserts, or
the api emits it at token issuance. #222 decides and documents; the
choice affects what the trail can prove about live sessions.
- Retention and partition maintenance are operational obligations that
must ship with the feature, not after it.
- Classification at read time is stored with the event: a later
reclassification must not rewrite history.
## Implementing issues
#222 (instrumentation), #223 (dedup window), #224 (table, retention,
partitioning), #225 (switch + purpose limitation). Depends on #204/#205.