[VS-NfD] Instrument read paths for classified content #222

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

Plan reference: 20-massnahmenplan.md -> Phase 3, Variante A
ADR: ADR 0023
Effort: L (4 AT)
Depends on: #204, #205

Context

Platform logging cannot answer which classified page was read — proxy
logs know URLs, not classifications. That is the gap this milestone closes,
and only for classified content, which is what keeps the purpose limitation
defensible.

Current state

  • The audit trail is deliberately write-only in scope:
    apps/api/src/audit/audit.service.ts — "Content activity (pages, files,
    exports, labels) intentionally stays log-only — the trail answers 'who
    changed access/configuration', not 'who edited what'." No read events
    exist (the single action: 'read' occurrence,
    apps/api/src/mcp/mcp.service.ts:243, is a permission-check parameter,
    not an audit action).
  • Read paths to instrument: SPA page fetch, public API GET
    (apps/api/src/public-api/), attachment download
    (apps/api/src/files/files.controller.ts:51,71), exports
    (apps/api/src/import-export/), no-JS shell
    (apps/api/src/public/html-shell.ts), and the collab WS join.
  • Constraint for the WS join: authorization there is token-only
    (apps/collab/src/server.ts:108–125 — signature plus
    claims.pageId === documentName); the collab server has no permission
    context and therefore cannot know a page's classification. The api is the
    natural emission point, and it is a good one: collab tokens live 60
    seconds
    (apps/api/src/pages/pages.service.ts:57,384), so a live
    session re-requests one every minute — that gives per-minute granularity
    for free, which the dedup window in #223 then collapses. Decide and
    document.

Acceptance criteria

  • Every listed read path emits an event for pages with
    classification = VS_NFD, and none for unclassified pages.
  • Each event carries: timestamp, actor (or documented anonymous
    marker), page, channel, and the classification at read time.
  • The collab WS join is covered by the decided mechanism, with the
    reasoning recorded in ADR 0023.
  • Events survive an in-request failure of the trail without breaking
    the read? — no: for classified content, a failed write must be a
    hard failure or an explicit, documented degradation. State which, and
    test it. (This is the deliberate difference from AuditService,
    which swallows failures.)
  • One test per channel proving both the event and its absence for
    unclassified pages.
  • The logging section of docs/architecture/security.md created by
    #196 documents the channel list.

Out of scope

Auditing reads of unclassified content (Variant B, rejected in
ADR 0023), and the dedup window (#223).

**Plan reference:** `20-massnahmenplan.md` -> Phase 3, Variante A **ADR:** ADR 0023 **Effort:** L (4 AT) **Depends on:** #204, #205 ## Context Platform logging cannot answer *which classified page* was read — proxy logs know URLs, not classifications. That is the gap this milestone closes, and only for classified content, which is what keeps the purpose limitation defensible. ## Current state - The audit trail is deliberately write-only in scope: `apps/api/src/audit/audit.service.ts` — "Content activity (pages, files, exports, labels) intentionally stays log-only — the trail answers 'who changed access/configuration', not 'who edited what'." No read events exist (the single `action: 'read'` occurrence, `apps/api/src/mcp/mcp.service.ts:243`, is a permission-check parameter, not an audit action). - Read paths to instrument: SPA page fetch, public API GET (`apps/api/src/public-api/`), attachment download (`apps/api/src/files/files.controller.ts:51,71`), exports (`apps/api/src/import-export/`), no-JS shell (`apps/api/src/public/html-shell.ts`), and the collab WS join. - **Constraint for the WS join:** authorization there is token-only (`apps/collab/src/server.ts:108–125` — signature plus `claims.pageId === documentName`); the collab server has no permission context and therefore cannot know a page's classification. The api is the natural emission point, and it is a good one: collab tokens live **60 seconds** (`apps/api/src/pages/pages.service.ts:57,384`), so a live session re-requests one every minute — that gives per-minute granularity for free, which the dedup window in #223 then collapses. Decide and document. ## Acceptance criteria - [ ] Every listed read path emits an event for pages with `classification = VS_NFD`, and none for unclassified pages. - [ ] Each event carries: timestamp, actor (or documented anonymous marker), page, channel, and the classification at read time. - [ ] The collab WS join is covered by the decided mechanism, with the reasoning recorded in ADR 0023. - [ ] Events survive an in-request failure of the trail without breaking the read? — **no**: for classified content, a failed write must be a hard failure or an explicit, documented degradation. State which, and test it. (This is the deliberate difference from `AuditService`, which swallows failures.) - [ ] One test per channel proving both the event and its absence for unclassified pages. - [ ] The logging section of `docs/architecture/security.md` created by #196 documents the channel list. ## Out of scope Auditing reads of unclassified content (Variant B, rejected in ADR 0023), and the dedup window (#223).
fable-5 added this to the M29 — VS-NfD: read-access audit trail milestone 2026-07-30 01:43:52 +02:00
fable-5 added the
vs-nfd
area:storage
effort:L
labels 2026-07-30 01:43:52 +02:00
Author
Collaborator

Implemented in PR #278 (commit 05a979b), merged fast-forward; CI run 591 green, chain run 596 green; CD 599 green, readyz test+int ok. All six channels instrumented (page_view incl. embeds and plugin-API, no_js_shell, public_api incl. MCP read_page, attachment via #212 effective classification, export in all shapes, collab_join at token issuance as the api-side WS-join proxy per ADR 0023). Events carry actor/anon marker, session key, page, pond, channel, classification at read time. Hard-failure semantics decided, tested and documented (ADR 0023, security.md incl. channel list and recorded residuals). One e2e test per channel proves event and absence for unclassified pages (11 tests).

Implemented in PR #278 (commit 05a979b), merged fast-forward; CI run 591 green, chain run 596 green; CD 599 green, readyz test+int ok. All six channels instrumented (page_view incl. embeds and plugin-API, no_js_shell, public_api incl. MCP read_page, attachment via #212 effective classification, export in all shapes, collab_join at token issuance as the api-side WS-join proxy per ADR 0023). Events carry actor/anon marker, session key, page, pond, channel, classification at read time. Hard-failure semantics decided, tested and documented (ADR 0023, security.md incl. channel list and recorded residuals). One e2e test per channel proves event and absence for unclassified pages (11 tests).
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#222
No description provided.