[VS-NfD] Pass the classification through feeds, public API, search results and the no-JS shell #211

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

Plan reference: 20-massnahmenplan.md -> P1-2 (output channels)
ADR: ADR 0022
Effort: M (2–3 AT)
Depends on: #204

Context

These four channels emit content without going through the SPA, so each can
leak unmarked classified content. The plan groups them under one effort
figure, so they stay one issue.

Kept as one issue because the plan gives the four channels a single effort
figure (2–3 AT) and they share one mechanism: the serializer that renders a
page representation outside the SPA.

Current state

  • Atom feeds: apps/api/src/public/feed.service.ts, routes in
    apps/api/src/public/public.controller.ts:28,43.
  • No-JS shell: apps/api/src/public/html-shell.ts (server-rendered HTML
    for crawlers and no-script clients).
  • Public REST API: apps/api/src/public-api/, gated by api.enabled plus
    pond apiEnabled.
  • Search results: apps/api/src/search/postgres-search.provider.ts
    (snippets contain page text).

Acceptance criteria

  • Feed entries carry the classification in a documented element, and
    the feed document states the highest classification it contains.
  • Public API page representations include the classification field;
    the API documentation (docs/self-hosting/public-api.md) is updated.
  • The no-JS shell renders the marking in the same places as the SPA
    (top and bottom) — note the shell is a separate render path from the
    TipTap view, so it needs its own assertion.
  • Search results show the classification per hit, and a snippet of a
    classified page is never shown unmarked.
  • One test per channel; the public API test runs with the instance
    switch on.
  • Documented in #228.

Out of scope

Whether these channels should be available at all in the reference config
(#227 turns them off), and MCP (no content egress beyond the public API's
model).

**Plan reference:** `20-massnahmenplan.md` -> P1-2 (output channels) **ADR:** ADR 0022 **Effort:** M (2–3 AT) **Depends on:** #204 ## Context These four channels emit content without going through the SPA, so each can leak unmarked classified content. The plan groups them under one effort figure, so they stay one issue. *Kept as one issue because the plan gives the four channels a single effort figure (2–3 AT) and they share one mechanism: the serializer that renders a page representation outside the SPA.* ## Current state - Atom feeds: `apps/api/src/public/feed.service.ts`, routes in `apps/api/src/public/public.controller.ts:28,43`. - No-JS shell: `apps/api/src/public/html-shell.ts` (server-rendered HTML for crawlers and no-script clients). - Public REST API: `apps/api/src/public-api/`, gated by `api.enabled` plus pond `apiEnabled`. - Search results: `apps/api/src/search/postgres-search.provider.ts` (snippets contain page text). ## Acceptance criteria - [ ] Feed entries carry the classification in a documented element, and the feed document states the highest classification it contains. - [ ] Public API page representations include the classification field; the API documentation (`docs/self-hosting/public-api.md`) is updated. - [ ] The no-JS shell renders the marking in the same places as the SPA (top and bottom) — note the shell is a separate render path from the TipTap view, so it needs its own assertion. - [ ] Search results show the classification per hit, and a snippet of a classified page is never shown unmarked. - [ ] One test per channel; the public API test runs with the instance switch on. - [ ] Documented in #228. ## Out of scope Whether these channels should be available at all in the reference config (#227 turns them off), and MCP (no content egress beyond the public API's model).
fable-5 added this to the M26 — VS-NfD: classification metadata milestone 2026-07-30 01:43:44 +02:00
fable-5 added the
area:export
vs-nfd:blocker
vs-nfd
effort:M
labels 2026-07-30 01:43:44 +02:00
Author
Collaborator

Done in PR #270 (commit 521ea51, CI runs 555+556 green, fast-forward merged on Stefan's standing merge instruction).

Evidence per channel (one test each):

  • Feeds: classified entries carry a standard Atom category element (term=vs_nfd, scheme=urn:dorfteich:classification, label=the fixed wording); the feed document states the HIGHEST contained level once; all-open feeds carry none. Documented in docs/self-hosting/public-api.md (section Feeds). Test: feed.e2e.db.test.ts.
  • Public API: classification field in list+get page representations; OpenAPI schemas updated; docs updated. Test runs with the instance switch on (public-api.e2e.db.test.ts).
  • Search: every hit carries the level; the search palette renders the marking with every snippet (compact banner form, text token only, AT prefix label de+en) - a classified snippet is never shown unmarked. Test: search.service.db.test.ts.
  • No-JS shell: banner above and below the content in the server-rendered document (own render path, own markup+CSS in the shell, both color schemes). Test: public.e2e.db.test.ts asserts exactly two banners around the body.

Bonus fix ridden along: per-job container names for the fidelity CI sidecars (run 547's red fidelity job was a fixed-name collision between parallel runs; a fixed-name cleanup could even kill a sibling's live sidecars).

Done in PR #270 (commit 521ea51, CI runs 555+556 green, fast-forward merged on Stefan's standing merge instruction). Evidence per channel (one test each): - Feeds: classified entries carry a standard Atom category element (term=vs_nfd, scheme=urn:dorfteich:classification, label=the fixed wording); the feed document states the HIGHEST contained level once; all-open feeds carry none. Documented in docs/self-hosting/public-api.md (section Feeds). Test: feed.e2e.db.test.ts. - Public API: classification field in list+get page representations; OpenAPI schemas updated; docs updated. Test runs with the instance switch on (public-api.e2e.db.test.ts). - Search: every hit carries the level; the search palette renders the marking with every snippet (compact banner form, text token only, AT prefix label de+en) - a classified snippet is never shown unmarked. Test: search.service.db.test.ts. - No-JS shell: banner above and below the content in the server-rendered document (own render path, own markup+CSS in the shell, both color schemes). Test: public.e2e.db.test.ts asserts exactly two banners around the body. Bonus fix ridden along: per-job container names for the fidelity CI sidecars (run 547's red fidelity job was a fixed-name collision between parallel runs; a fixed-name cleanup could even kill a sibling's live sidecars).
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#211
No description provided.