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
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
71 lines
3.3 KiB
Markdown
71 lines
3.3 KiB
Markdown
# 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 (18–20 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 (8–10 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.
|