Patch notes — docs landing no longer leaks private section names (2026-06-15, `649ea9a`)
Privacy fix for the embedded docs reader (ethica.no/docs/). The /docs/ landing is a
static Hugo page that lists every section as a card. The per-path access gate correctly
blocked opening a private section (login/403), but it never edited that static HTML — so as
soon as one section was made public (e.g. Change log), an anonymous visitor allowed onto
the landing could read the names of every other section. Those names are customer
companies (e.g. API Test Pilotene, Hetzner), so the list leaked even though the content
stayed protected.
Fix
The landing is now filtered server-side to the viewer’s readable sections:
gate.strip_unreadable_cards()— drops each section card whose/docs/<section>link is a known section the principal may not read. Cards without a docs link (or pointing at the docs root) pass through.gate.filter_index_cards()— resolves the principal’s allowed set (public ∪ grants; admin = all) and applies the strip. No-op in public-by-default mode.gate.is_index_path()— targets only the cross-section landing (the root index and/docs/page/N); section-scoped index pages are already gated as a whole.docs_guard()serves the filtered landing itself (no-store) instead of letting the static handler emit the full card list.
Result: anonymous visitors see only public section cards; a logged-in customer sees public ∪ their own; admins/staff see everything. Per-section content gating is unchanged.
Verification
3 new unit tests (docs_reader/tests/test_public_sections.py) for the card stripper; existing
docs_reader access/public-section tests still green; Ethica.no Docker smoke 157/0.