942f7b13d3
23 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
| 942f7b13d3 |
#304: scope the legal spec's status locator to its own form
Some checks failed
CI / Lint, typecheck, test (pull_request) Successful in 6m31s
CI / Build container images (pull_request) Successful in 1m15s
CI / Auth e2e pack (pull_request) Successful in 8m57s
CI / Import/export fidelity gate (pull_request) Successful in 53s
CD / Build and push images (push) Successful in 36s
CD / Deploy to Test (push) Successful in 11s
CD / Smoke tests against Test (push) Successful in 1m24s
CD / Promote to Int (push) Successful in 12s
CI / Lint, typecheck, test (push) Failing after 7m12s
CI / Auth e2e pack (push) Has been skipped
CI / Import/export fidelity gate (push) Has been skipped
CI / Build container images (push) Has been skipped
The font manager's upload live regions made `getByRole('status')` ambiguous
on /admin, and legal.spec.ts — which asserts the legal form's success message
— started failing in the e2e pack. That is the documented trap in CLAUDE.md:
a new label or region makes an existing page-wide locator ambiguous, and the
fix is to scope the SPEC, not to drop the region a screen reader needs.
The section gets a named class for exactly that purpose.
Verified locally against the running stack: legal, fonts, admin-users,
admin-quotas and the a11y pack all pass.
|
|||
| f8c241b11a |
#304: custom fonts in the pickers, an admin screen, and the licence page
The backend from #303 could store an operator's font but nothing could choose one: no list endpoint outside the Site-Admin routes, no @font-face rules for a family that only exists at runtime, and no management UI. Found while wiring it up — a real defect in #303, invisible to its tests: `fontStack` cannot tell an uploaded family from a deleted one, so the PDF exporter embedded the face and then never named it. Every export of a pond using an operator font rendered in the system font while the job reported success. Both `fontStack` call sites now take the uploaded families (`buildPdfHtml`, `pondFontVariables`); `pdf-html.test.ts` pins the regression from both sides. Verified against a real Gotenberg: with the families the PDF embeds PlayfairDisplay-Bold, without them NotoSans-Bold — that was the whole bug, in one diff of two PDFs. - `GET /fonts/custom` is readable by any signed-in user, not Site Admins only: the pickers, the licence page and the injected `@font-face` rules all need it, and gating it would have forced a second, admin-only UI. - Bundled and uploaded families are told apart by their `<optgroup>`, not by a badge — the grouping is then part of the control's semantics, so a screen reader announces it and the native mobile select keeps it. Within each source the catalog's category grouping is preserved. - The delete confirmation names how many ponds use the family and what happens to them; focus moves to it and back on cancel. Deletion stays unblocked (the api's decision, #303) — the ponds degrade, they do not break. - The licence page grew a second table. That is what makes an attribution obligation satisfiable: a commercial licence that requires naming the foundry needs a page to name it on. Verified in the browser end to end (upload two weights → listed and rendered in its own font → chosen in a pond → page renders in it → deleted → pond falls back): api suite for fonts/export 77 passed, a11y pack 11/11 locally in both schemes, lint/typecheck/i18n:check green. |
|||
| 0d95e1304e |
#245: mode hidden — hide profile-violating options, mark the hiding
All checks were successful
CI / Lint, typecheck, test (push) Successful in 6m24s
CI / Build container images (push) Has been skipped
CI / Auth e2e pack (push) Successful in 8m34s
CI / Import/export fidelity gate (push) Successful in 1m1s
CI / Build container images (pull_request) Successful in 1m13s
CI / Lint, typecheck, test (pull_request) Successful in 6m14s
CI / Auth e2e pack (pull_request) Successful in 8m31s
CI / Import/export fidelity gate (pull_request) Successful in 58s
CD / Build and push images (push) Successful in 19s
CD / Deploy to Test (push) Successful in 14s
CD / Smoke tests against Test (push) Successful in 1m18s
CD / Promote to Int (push) Successful in 11s
In hidden (and later enforced) mode, catalog-listed controls whose only purpose is enabling a violation are not rendered while their saved value is compliant (the four master switches, the Nextcloud backup block); value-listed selects keep only their compliant choices (registration mode, new-page classification, upload policy, SVG policy). Every affected section shows one accessible policy note (i18n de+en) so policy is distinguishable from missing features. A value that was already violating is surfaced exactly like in marked — never silently hidden. The API stays unchanged; enforcement is #246. e2e: hidden half of the marking pack (rows disappear, note visible, already-violating row stays marked, axe WCAG A/AA clean) — verified live locally; CI runs it against a second api (VS_NFD_MODE=hidden, same database) behind its own static server. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AUtYMxwTCMHG9mVHnwbFg8 |
|||
| 5fdef95f67 |
#244: mode marked — flag profile-violating configuration in the UI
All checks were successful
CI / Lint, typecheck, test (pull_request) Successful in 6m34s
CI / Build container images (pull_request) Successful in 1m19s
CI / Auth e2e pack (pull_request) Successful in 8m23s
CI / Import/export fidelity gate (pull_request) Successful in 56s
CD / Build and push images (push) Successful in 23s
CD / Deploy to Test (push) Successful in 12s
CD / Smoke tests against Test (push) Successful in 1m17s
CD / Promote to Int (push) Successful in 12s
CI / Lint, typecheck, test (push) Successful in 6m33s
CI / Build container images (push) Has been skipped
CI / Auth e2e pack (push) Successful in 8m49s
CI / Import/export fidelity gate (push) Successful in 58s
Every catalog-listed control on the admin surfaces carries an accessible deviation marking in mode marked: text + icon under the control (never colour alone), part of the control's accessible description (aria-describedby), i18n de+en. The check runs against the CURRENT control value, so a violating choice is marked before saving. Covered controls: registration mode, new-page classification, upload policy, SVG policy, the four master switches (api/mcp/feeds/plugins), the legal texts (violating while empty), and the Nextcloud backup toggle on the system panel. The profile card (#243) gains the warning summary and the hardening-guide reference. e2e: new vs-nfd-marking pack (marked half in CI — the e2e api now runs VS_NFD_MODE=marked, which also puts the marked state into the a11y admin scan; off half in local default runs; both halves verified live). hidden/enforced follow in #245/#246. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AUtYMxwTCMHG9mVHnwbFg8 |
|||
| da5fd7c770 |
#243: VS_NFD_MODE and the machine-readable hardening-profile catalog
All checks were successful
CI / Lint, typecheck, test (pull_request) Successful in 6m12s
CI / Build container images (pull_request) Successful in 4m2s
CI / Auth e2e pack (pull_request) Successful in 8m29s
CI / Import/export fidelity gate (pull_request) Successful in 54s
CD / Build and push images (push) Successful in 31s
CD / Deploy to Test (push) Successful in 14s
CD / Smoke tests against Test (push) Successful in 1m29s
CD / Promote to Int (push) Successful in 14s
CI / Build container images (push) Has been skipped
CI / Lint, typecheck, test (push) Successful in 6m30s
CI / Auth e2e pack (push) Successful in 8m6s
CI / Import/export fidelity gate (push) Successful in 57s
The deployment declares through VS_NFD_MODE (off | marked | hidden | enforced, default off) how the application treats configuration that violates the VS-NfD reference profile — deploy-level like BACKUP_ALLOWED_TARGETS, so a compromised Site Admin cannot widen it. The catalog in shared (vs-nfd-profile.ts) is the single source of truth: every profile-relevant setting with a decidable compliant value, judgement calls in an explicit advisory list, and a fence test parsing the hardening guide's reference tables so neither can drift (pattern #201). The api evaluates the catalog against the typed settings registry and validated env and exposes mode + verdict on GET /admin/system/vs-nfd-profile; the admin settings view shows the card whenever the mode is not off. Display only — the treatments land with #244–#246 (ADR 0027, proposed). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AUtYMxwTCMHG9mVHnwbFg8 |
|||
| 868b79c8bc |
#213: warn on uploads to classified pages; instance policy can block
All checks were successful
CI / Lint, typecheck, test (pull_request) Successful in 6m15s
CI / Build container images (pull_request) Successful in 4m27s
CI / Auth e2e pack (pull_request) Successful in 9m10s
CI / Import/export fidelity gate (pull_request) Successful in 53s
CD / Build and push images (push) Successful in 17s
CD / Deploy to Test (push) Successful in 15s
CD / Smoke tests against Test (push) Successful in 1m16s
CD / Promote to Int (push) Successful in 20s
CI / Lint, typecheck, test (push) Successful in 5m47s
CI / Build container images (push) Has been skipped
CI / Auth e2e pack (push) Successful in 8m26s
CI / Import/export fidelity gate (push) Successful in 1m0s
The attachments panel of a classified page shows a persistent notice naming the consequence (de+en): the file inherits the page's classification but its content carries no marking (#212). The new instance setting classification.uploadPolicy (default warn, documented; the VS-NfD reference configuration blocks, #227) hardens the warning into a server-side rejection (403 classified_upload_blocked) — enforced in the upload service, not only in the UI. Tests: warning visible in the local attachments pack; block enforced server-side with warn/block both ways and open pages unaffected. Co-Authored-By: Claude Fable 5 (1M context) <noreply@anthropic.com> |
|||
| 183faf7710 |
#204: classification as first-class page metadata (ADR 0022)
All checks were successful
CI / Lint, typecheck, test (pull_request) Successful in 5m42s
CI / Build container images (pull_request) Successful in 3m56s
CI / Auth e2e pack (pull_request) Successful in 8m17s
CI / Import/export fidelity gate (pull_request) Successful in 56s
CD / Build and push images (push) Successful in 24s
CD / Deploy to Test (push) Successful in 10s
CI / Lint, typecheck, test (push) Successful in 6m17s
CD / Smoke tests against Test (push) Successful in 3m32s
CI / Build container images (push) Has been skipped
CD / Promote to Int (push) Successful in 13s
CI / Auth e2e pack (push) Successful in 8m20s
CI / Import/export fidelity gate (push) Successful in 55s
Enum field on Page (UNCLASSIFIED default, VS_NFD), migration backfills existing pages. New pages take the instance-wide default from classification.newPageDefault (admin-visible, de+en). The value rides in every PageView, so no channel needs an extra request. The field is a marking, not a protection mechanism: a test pins that permission decisions are unchanged by it. The marking wording is fixed in ADR 0022 and sourced solely from classificationMarking() in @dorfteich/shared. Co-Authored-By: Claude Fable 5 (1M context) <noreply@anthropic.com> |
|||
| c4c84b33f9 |
#200: hard instance-wide plugins.enabled kill switch
Some checks failed
CI / Lint, typecheck, test (pull_request) Successful in 5m38s
CI / Build container images (pull_request) Successful in 4m11s
CI / Auth e2e pack (pull_request) Successful in 8m55s
CI / Import/export fidelity gate (pull_request) Successful in 1m9s
CI / Import/export fidelity gate (push) Blocked by required conditions
CD / Build and push images (push) Successful in 20s
CD / Deploy to Test (push) Failing after 51s
CD / Smoke tests against Test (push) Has been skipped
CD / Promote to Int (push) Has been skipped
CI / Lint, typecheck, test (push) Successful in 5m37s
CI / Build container images (push) Has been skipped
CI / Auth e2e pack (push) Has been cancelled
plugins.enabled (instance setting, default on — plugins predate the switch; the VS-NfD reference configuration turns it off) makes every plugin surface answer 404 via a shared guard: Site-Admin install/list/mode, pond activation and plugin list, the sandbox frame and asset routes. The dropzone watcher quarantines drops instead of installing. Deliberately NOT guarded: the authenticated fallback-metadata route — it serves no plugin code and existing plugin_block nodes need it to render their declared fallback (an image fallback degrades to the neutral placeholder while off, because its bytes live on the disabled asset surface). The editor offers no plugin blocks because the pond plugin list is one of the 404ing surfaces. Admin settings panel gets the toggle (i18n de+en) with the documented api-restart note (in-process settings cache). Answers "code execution inside the zone?" with one verifiable off-switch instead of per-plugin trust machinery (#232, ADR 0025). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0168Ph5uBmHm8X28CSVpbpnJ |
|||
| afef45732a |
#191: feeds.enabled instance switch, feed-token log masking
All checks were successful
CI / Lint, typecheck, test (pull_request) Successful in 4m52s
CI / Build container images (pull_request) Successful in 3m55s
CI / Auth e2e pack (pull_request) Successful in 7m52s
CI / Import/export fidelity gate (pull_request) Successful in 55s
CD / Build and push images (push) Successful in 16s
CD / Deploy to Test (push) Successful in 14s
CD / Smoke tests against Test (push) Successful in 1m14s
CD / Promote to Int (push) Successful in 11s
CI / Lint, typecheck, test (push) Successful in 4m55s
CI / Build container images (push) Has been skipped
CI / Auth e2e pack (push) Successful in 7m34s
CI / Import/export fidelity gate (push) Successful in 58s
Chosen path: an instance master switch following the api.enabled/ mcp.enabled pattern — while off, both feed routes AND the feed-token management answer 404 (existence hidden). Default ON: feeds predate the switch, existing instances and their subscribed readers keep working; the VS-NfD reference configuration (#227) turns it off. Admin UI gets the toggle next to the API/MCP switches (i18n de+en). Moving the token out of the query string is documented as rejected: a path segment lands in the same proxy and request logs, and feed readers cannot send headers — that is why the credential is in the URL at all. What DID leak was our own request log (pino logs req.url): the req serializer now masks ?token= values (common/mask-token-param.ts), so no code path logs the credential. Refs #191 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0168Ph5uBmHm8X28CSVpbpnJ |
|||
| 418aafd5ec |
#163: Dokumentsprache und Seitentitel der SPA
Some checks failed
CI / Lint, typecheck, test (pull_request) Successful in 4m43s
CI / Build container images (pull_request) Successful in 1m11s
CI / Auth e2e pack (pull_request) Successful in 7m14s
CI / Import/export fidelity gate (pull_request) Successful in 56s
CD / Deploy to Test (push) Blocked by required conditions
CD / Smoke tests against Test (push) Blocked by required conditions
CD / Promote to Int (push) Blocked by required conditions
CI / Auth e2e pack (push) Blocked by required conditions
CI / Import/export fidelity gate (push) Blocked by required conditions
CI / Build container images (push) Blocked by required conditions
CD / Build and push images (push) Has been cancelled
CI / Lint, typecheck, test (push) Has been cancelled
i18n spiegelt die aktive Sprache auf <html lang> (Init + languageChanged; der User-Locale-Wechsel in auth-context läuft über dasselbe Event). Neuer useDocumentTitle-Hook setzt je Route einen sprechenden Titel (Seite — Teich — Dorfteich), verdrahtet in allen Routen-Komponenten; dynamische Titel folgen den geladenen Daten. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AGM8jo3hwoV9wsCVGfy8iq |
|||
| b4247f4832 |
#145: Einstellungsseiten mit Sektions-Sprungnavigation
Some checks failed
CI / Lint, typecheck, test (pull_request) Successful in 4m27s
CI / Build container images (pull_request) Successful in 3m48s
CI / Auth e2e pack (pull_request) Successful in 6m58s
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 12s
CD / Smoke tests against Test (push) Successful in 1m14s
CD / Promote to Int (push) Successful in 11s
CI / Lint, typecheck, test (push) Failing after 4m47s
CI / Auth e2e pack (push) Has been skipped
CI / Import/export fidelity gate (push) Has been skipped
CI / Build container images (push) Has been skipped
Neue SettingsLayout-Komponente leitet die Navigation per MutationObserver aus den section>h2-Blöcken ab (erfasst konditionale und komponenten-eigene Sektionen ohne Verdrahtung), sticky Leiste neben dem Inhalt, auf schmalen Viewports horizontale Chip-Leiste; aktive Sektion über Scroll-Position, am Seitenende gewinnt die letzte. Auf allen vier Einstellungsseiten verdrahtet; die Admin-Grundeinstel- lungen bekommen dafür eine eigene Überschrift. Neuer CI-Pack settings-nav. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0155v2aT8AG1kZDQEZiCLBWC |
|||
| 04abda5724 |
Keep the landing editor off the .legal-editor class
All checks were successful
CD / Deploy to Test (push) Successful in 10s
CD / Build and push images (push) Successful in 1m10s
CD / Smoke tests against Test (push) Successful in 1m10s
CD / Promote to Int (push) Successful in 11s
CI / Lint, typecheck, test (push) Successful in 4m10s
CI / Build container images (push) Has been skipped
CI / Auth e2e pack (push) Successful in 5m37s
CI / Import/export fidelity gate (push) Successful in 48s
Release / Build release images and notes (push) Successful in 1m6s
Release / Release-candidate operations QA (push) Successful in 41s
Prod deploy / Deploy the released images to Prod (push) Successful in 15s
The landing form reused LegalTextField, whose .legal-editor wrapper the legal e2e selects by index (nth(1) = privacy policy). Placed before the legal form it shifted those indices, so the test drove the imprint field and the published privacy text never appeared. Generalize the component to MarkdownTextField with a wrapperClass prop: legal keeps .legal-editor, the landing editor uses .markdown-field. Verified locally: legal, admin-users, admin-quotas packs green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EwZ4jR4KFAPvpjWevfUGX1 |
|||
| abf49c7c0e |
Give the landing-page save button its own label
Some checks failed
CD / Build and push images (push) Successful in 3m53s
CD / Deploy to Test (push) Successful in 10s
CI / Lint, typecheck, test (push) Successful in 4m12s
CI / Build container images (push) Has been skipped
CD / Smoke tests against Test (push) Successful in 1m14s
CD / Promote to Int (push) Successful in 13s
CI / Auth e2e pack (push) Failing after 2m56s
CI / Import/export fidelity gate (push) Has been skipped
The landing form reused the legal namespace's "Save legal pages" label, so two buttons shared that text and the legal e2e's page-wide button lookup hit a strict-mode violation. Use a dedicated settings-namespace "Save landing page" label instead. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EwZ4jR4KFAPvpjWevfUGX1 |
|||
| 9c64166b10 |
Editable landing page for the Site Admin
Some checks failed
CD / Build and push images (push) Successful in 3m50s
CD / Deploy to Test (push) Successful in 10s
CI / Lint, typecheck, test (push) Successful in 4m13s
CI / Build container images (push) Has been skipped
CD / Smoke tests against Test (push) Successful in 1m14s
CD / Promote to Int (push) Successful in 11s
CI / Auth e2e pack (push) Failing after 2m44s
CI / Import/export fidelity gate (push) Has been skipped
The public home page (/) now renders Markdown the Site Admin stores in the new home.content instance setting, through the same sanitizing pipeline as the legal pages; empty falls back to the built-in welcome text. New public GET /home/content, an Admin → Settings editor with live preview, and an e2e test covering default/configured/escaping/ admin-only. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EwZ4jR4KFAPvpjWevfUGX1 |
|||
| 04e21a0aac |
Built-in MCP endpoint (Streamable HTTP) on top of the public API (#105)
All checks were successful
CD / Build and push images (push) Successful in 3m50s
CI / Lint, typecheck, test (push) Successful in 4m2s
CI / Build container images (push) Has been skipped
CD / Deploy to Test (push) Successful in 11s
CD / Smoke tests against Test (push) Successful in 1m14s
CD / Promote to Int (push) Successful in 13s
CI / Auth e2e pack (push) Successful in 5m37s
CI / Import/export fidelity gate (push) Successful in 47s
AI clients talk to the instance directly at /api/mcp — under the /api/ path (deviation from the issue's literal /mcp) so every existing reverse proxy already routes it; no deployment changes anywhere. - Transport: official @modelcontextprotocol/sdk server, STATELESS — each POST builds a fresh server+transport pair, no session store, replicas stay trivial; GET/DELETE answer 405. Auth per PAT bearer (#104 tokens), per-token rate limit (429 + Retry-After). - Own switches, independent of REST: instance mcp.enabled (admin settings, default off; off = 404, feature invisible) + pond setting mcpEnabled (pond-settings toggle, default off) — pinned independent in both directions by tests. - Tools (thin wrappers over the #104 services, same permission gates, audit-logged writes): list_ponds, list_pages, read_page, search, create_page, update_page (replace semantics through the collab-owned restore path — open editors converge), add_comment, list_labels, set_page_labels (exact replace), export_pond (link to the REST ZIP). Tool errors carry the api error codes; results carry stable slugs/ids. MCP resources stay the documented stage-2 stretch goal. - Deliberately on the SDK's low-level Server API with a hand-written tool table (mcp-tools.ts): the typed registerTool generics drove tsc out of memory in a program this size; manual Zod validation keeps the wire behavior explicit. - PublicApiService exposure filtering parameterized ('api' | 'mcp', shared pondFeatureEnabled helper) — one implementation, two switches. - Docs: "Connect Claude Code / MCP clients" section in public-api.md (claude mcp add one-liner + mcp-remote bridge for stdio clients). Verification: 8-test e2e pack driving the real MCP SDK client over Streamable HTTP against a listening api (initialize + tools/list, switch independence in both directions, anonymous/garbage 401, opt-in 404 semantics, page roundtrip incl. restore-NOTIFY, labels/comments, read scope blocked from writes with scope_required); live check through the web proxy against the seeded stack (tools list, create, read, update, search — LIVE CHECK PASSED); full api suite 61/61 files green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EwZ4jR4KFAPvpjWevfUGX1 |
|||
| 0c85293830 |
Public REST API v1: personal access tokens, instance switch + per-pond opt-in (#104)
Some checks failed
CI / Lint, typecheck, test (push) Failing after 1m39s
CI / Auth e2e pack (push) Has been skipped
CI / Import/export fidelity gate (push) Has been skipped
CI / Build container images (push) Has been skipped
CD / Build and push images (push) Successful in 3m51s
CD / Deploy to Test (push) Successful in 9s
CD / Smoke tests against Test (push) Successful in 1m10s
CD / Promote to Int (push) Successful in 11s
Token-authenticated machine access at /api/public/v1 — the foundation for the built-in MCP endpoint (#105). Personal access tokens: - api_tokens table (SHA-256 hash, scope read|write, optional pond restriction, expiry, revocation, throttled last-used) + migration; secrets are dt_pat_<random>, shown exactly once - lifecycle endpoints under /users/me/api-tokens (session-only — a leaked token can never mint more tokens) with audit entries api.token_created/api.token_revoked - settings UI section (create with scope/expiry/pond restriction, one-time reveal with copy, list with status + revoke), de+en Activation (404 semantics per #60 on both levels): - instance setting api.enabled (default off, admin settings switch) - pond setting apiEnabled (default off, pond settings toggle; the PondsService settings-merge learned the key — the #92 lesson) Surface (/api/public/v1, excluded from the SPA's global prefix): - me, ponds, pages (list/read as Markdown+HTML, create from Markdown via the shared pipeline, PATCH title/content, DELETE to trash), search (permission-filtered + narrowed to exposed ponds, highlights as **…**), markdown ZIP export, labels (tree, create/rename/recolour/move/delete, assign/unassign), comments (threads, create, resolve/reopen) - content replacement travels the collab-owned document path: the new state lands as a MANUAL version "API update", then the established restore NOTIFY applies it — open editors converge, history stays append-only, no second lineage (VersionsService.replaceContent) - hand-maintained OpenAPI 3.1 document at /openapi.json, pinned to the controller by a route-coverage test in both directions Enforcement: - PublicApiGuard: instance switch → bearer PAT auth (request.user is the token's user) → per-token rate limit (429 + Retry-After) → scope (403 scope_required) → pond opt-in + token restriction - the shared PermissionGuard then applies the unchanged permission model; PageParamSource gained pondSlugParam for the slug+slug routes - no cookies anywhere → no CSRF surface (pinned by a hostile-Origin test) - every write audit-logged as api.write with the token attributed Tests/verification: - 12-test e2e pack: lifecycle, switches, permission matrix (reader/editor/outsider × scopes), restriction, page roundtrip incl. restore-NOTIFY assertion, labels, comments incl. policy, search narrowing, ZIP export, rate limit; full api suite 60/60 green (quota fixture via per-user override — never the instance default) - new collab-pack test proves an open editor converges onto an API content replacement (green against a local seeded stack) - UI smoke against the built SPA: token create/reveal/revoke, pond opt-in persists, admin switch persists (10/10) - docs/self-hosting/public-api.md + README link Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EwZ4jR4KFAPvpjWevfUGX1 |
|||
| c8aac13dfb |
Add Site-Admin system panel with persistent audit trail (#86)
All checks were successful
CI / Lint, typecheck, test (push) Successful in 3m14s
CI / Build container images (push) Has been skipped
CD / Build and push images (push) Successful in 3m45s
CD / Deploy to Test (push) Successful in 10s
CD / Smoke tests against Test (push) Successful in 1m11s
CD / Promote to Int (push) Successful in 11s
CI / Auth e2e pack (push) Successful in 5m20s
CI / Import/export fidelity gate (push) Successful in 45s
New /admin/system panel (operations.md §Maintenance jobs): the maintenance job list shows every registered job with truthful last-run data (new Job.lastDurationMs recorded by the scheduler) and a manual trigger that respects the run-mutex and is itself audit-logged; a backup card mirrors the sidecar's status.json including the freshness verdict; an audit-log viewer filters by actor, action, and time range with pagination; and a storage overview lists the largest ponds. Auth events and admin actions (grants, members, user/quota admin, plugins, settings, setup) now land in a new audit_log table through a central AuditService — which keeps emitting the established stdout log line — while content activity stays log-only by design. All endpoints are Site-Admin-only; covered by API DB tests and a Playwright pack in CI. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EwZ4jR4KFAPvpjWevfUGX1 |
|||
| fd2bdb3fb8 |
Add instance legal pages with public rendering and footer links (#82)
All checks were successful
CI / Lint, typecheck, test (push) Successful in 3m7s
CI / Build container images (push) Has been skipped
CD / Build and push images (push) Successful in 3m14s
CD / Deploy to Test (push) Successful in 12s
CD / Smoke tests against Test (push) Successful in 1m16s
CD / Promote to Int (push) Successful in 9s
CI / Auth e2e pack (push) Successful in 5m9s
CI / Import/export fidelity gate (push) Successful in 45s
Imprint and privacy policy are two new Markdown instance settings (legal.imprint, legal.privacyPolicy), edited by Site Admins in a new "Legal pages" admin section with a toggleable rendered preview. The preview uses the same shared pipeline the server renders with (markdown → schema doc → escaped HTML), so stored markup can never smuggle script to visitors. The pages render publicly at /legal/imprint and /legal/privacy — as an SPA route plus, like #56, a self-contained server-rendered HTML document under /api/v1/legal/:kind. The endpoints are setup-exempt: legal information stays reachable even while the first-run wizard is pending. Unconfigured pages show a localized notice instead of 404ing, and Site Admins additionally get a warning banner linking to the settings. A new footer with both links appears on every SPA view (editor, auth screens, public pages) and in the server-rendered documents, whose shared shell moved to public/html-shell.ts and now renders its chrome in the instance default locale (ADR 0012). docs/self-hosting/legal-template.md ships imprint and privacy-policy templates in English and German whose sections mirror Dorfteich's actual processing activities (accounts, sessions, rate-limit IPs, proxy logs, transactional mail, content, export, deletion, no third-party requests), with a review checklist tied to security.md §Privacy. New `legal` i18n namespace (de+en); api and web e2e coverage including a new CI legal pack (footer navigation, notice vs. admin banner, and the admin form publishing a text end to end). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EwZ4jR4KFAPvpjWevfUGX1 |
|||
| 46292c7447 |
Add plugin administration UI: instance modes and pond activation (#72)
All checks were successful
CI / Lint, typecheck, test (push) Successful in 2m56s
CD / Build and push images (push) Successful in 3m11s
CD / Deploy to Test (push) Successful in 8s
CI / Auth e2e pack (push) Successful in 4m12s
CD / Smoke tests against Test (push) Successful in 1m6s
CD / Promote to Int (push) Successful in 13s
CI / Import/export fidelity gate (push) Successful in 42s
CI / Build container images (push) Has been skipped
- api: PATCH /admin/plugins/:id/mode (Site Admin) switches disabled/optional/required; new PluginPondController exposes GET /ponds/:id/plugins (effective list: required + optional-enabled, pond read access — the SPA loads it per pond), GET .../plugins/settings and PUT .../plugins/:pluginId (Pond Admin) to toggle optional plugins. Toggling a non-optional plugin is refused (plugin_not_optional). Install/uninstall/mode/toggle are audit-logged. - web: PluginManager in the admin area lists installed plugins with their declared permissions surfaced prominently (security.md), an upload control that shows validation errors, a mode switch with an impact hint, and a link to the sandbox preview. PondPluginSettings adds a per-pond optional-plugin toggle section to pond settings. - shared: PondPluginSetting, mode/toggle input schemas, plugin_not_optional error code + de/en messages, plugins i18n (admin/mode/pond). - tests: api db test covers mode switching, per-pond activation, the required-everywhere and disabled-nowhere propagation, and the not-optional guard; e2e plugin-admin pack drives the admin list, permission display, mode switch, and pond toggle end to end. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EwZ4jR4KFAPvpjWevfUGX1 |
|||
| 30891f99cf |
Add non-image attachments with allowlist, SVG policy, and file managers (#61)
All checks were successful
CD / Build and push images (push) Successful in 4m2s
CI / Lint, typecheck, test (push) Successful in 2m46s
CI / Auth e2e pack (push) Successful in 3m45s
CI / Build container images (push) Has been skipped
CD / Deploy to Test (push) Successful in 8s
CD / Smoke tests against Test (push) Successful in 1m18s
CD / Promote to Int (push) Successful in 12s
Extend uploads (#27, ADR 0011) beyond images to a configurable general attachment allowlist, plus the page attachments section and the Pond Admin file manager. Backend: - Two instance settings: `upload.allowedExtensions` (lowercase, dot-stripped, images always allowed regardless) and `upload.svgPolicy` (reject | sanitize). - FilesService.resolveUpload: raster images still decided by magic bytes; SVG is sanitized with DOMPurify (scripts, event handlers, foreignObject stripped) or rejected per policy; everything else is admitted only if its extension is on the allowlist. A sanitized SVG's stored bytes are re-accounted so pond_usage matches disk. - Downloads set `Content-Disposition: attachment` for every non-raster type (office files, PDFs, SVG) with `nosniff`, so they can never execute inline; raster images stay inline for page embeds. - New endpoints: `GET /ponds/:id/files` (pond_admin: all files + usage + orphan flag), `POST /pages/:id/files` and `GET /pages/:id/files` (page-write/read: the attachments section). New error code `upload_type_not_allowed` (de+en). Frontend: - Page attachments section (AttachmentsPanel): upload, list with type glyph, size, and uploader, insert-as-link into the document (an internal media link that downloads, never renders inline), and delete. Toggled in the editor. - Pond file manager (PondFileManager) in pond settings for Pond Admins: every file with its referencing page (or an orphan flag) and storage usage. - Admin uploads settings form (allowlist + SVG policy). New `files` i18n namespace (de+en). Tests: - files.e2e.db.test.ts: allowlisted non-image accepted and served as a download; disallowed extension rejected; renamed-.html-as-.png still fails; SVG sanitized (scripts/handlers stripped) and reject-mode rejects; page attachment listing; pond file manager usage/orphan; non-admin denied. - New e2e pack apps/web/e2e/attachments.spec.ts (+ CI step): upload → list → insert link (verified attachment disposition + nosniff), disallowed-type error, pond file manager usage/orphan. Local: typecheck, lint, i18n:check, build all green; api-db 184, shared 121, web 50; attachments pack 3/3, members 3/3, content 5/5. Adds dompurify + jsdom to the api for server-side SVG sanitization. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EwZ4jR4KFAPvpjWevfUGX1 |
|||
| 42e97b9df2 |
Add Site-Admin user management (#59)
Some checks failed
CD / Build and push images (push) Successful in 3m12s
CI / Lint, typecheck, test (push) Failing after 2m29s
CI / Auth e2e pack (push) Successful in 3m32s
CI / Build container images (push) Has been skipped
CD / Deploy to Test (push) Successful in 8s
CD / Smoke tests against Test (push) Successful in 1m11s
CD / Promote to Int (push) Successful in 11s
Instance operators get basic user administration for support, abuse handling, and GDPR groundwork (security.md §Privacy). - api `admin/`: Site-Admin-gated `/admin/users` — a searchable, paginated list (username, e-mail, status, role, pond count, last login) plus lifecycle actions: disable/enable (a disabled user is logged out everywhere and login is refused with the distinct `account_disabled`), resend verification, delete, and grant/revoke Site Admin. Guards: you cannot act on your own account (`cannot_modify_self`) and the last Site Admin cannot be dropped (`last_site_admin`). Every action is audit-logged with the actor. - `PseudonymizationService`: account deletion scrubs the PII, removes all login identities + sessions, and trashes the personal pond — the kept row is what authorship references, so shared content the user authored shows as "Deleted user" (no orphaned/cascaded content). - web: the Admin area gains a 'Users' surface — search, pagination, and the actions (destructive ones behind an inline two-step confirm; self-actions hidden). New `users` i18n namespace (de+en). - tests: `user-admin.e2e.db.test.ts` (disable → logout + login blocked; delete → pseudonymized authorship + personal pond trashed + credentials gone; last Site Admin and self protected; Site-Admin gating); a non-destructive browser `admin-users` pack proving disable-in-UI blocks login and enable restores it. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EwZ4jR4KFAPvpjWevfUGX1 |
|||
| 6d3db7db38 |
Add Site-Admin quota override management UI (#58)
All checks were successful
CD / Build and push images (push) Successful in 3m17s
CI / Lint, typecheck, test (push) Successful in 2m34s
CI / Auth e2e pack (push) Successful in 3m27s
CI / Build container images (push) Has been skipped
CD / Deploy to Test (push) Successful in 8s
CD / Smoke tests against Test (push) Successful in 1m14s
CD / Promote to Int (push) Successful in 11s
Site Admins tune quotas per user and per pond on the three-level ladder (pond override → user override → instance default, data-model.md §Quotas). - api `admin/`: a `QuotaAdminService` + Site-Admin-gated endpoints under `/admin/quotas` — look up a user (username/e-mail) or pond (slug), list every quota's override / instance default / effective value (resolved through the existing QuotaService, the single consumption path, so a change takes effect immediately) plus current usage, and set/clear a per-subject override. Every change is audit-logged. A pond's effective values resolve on its own override then its owner's, matching the consumption checks. - web: the Admin area gains a 'Quotas' surface — the instance defaults move into a proper number-input form (was raw settings, #19), and a per-subject panel looks a user/pond up, shows the ladder with usage, flags subjects over their effective limit, and sets/clears overrides. New `quotas` i18n namespace (de+en). - tests: `quota-admin.e2e.db.test.ts` (override → effective changes at once and QuotaService sees it; clear → falls back to the default; lookup; Site-Admin gating); a browser `admin-quotas` pack proving an override raised in the UI immediately lets a user create another shared pond (issue #22 consumption). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EwZ4jR4KFAPvpjWevfUGX1 |
|||
| 0bc80c9f93 |
Add auth, settings, and admin UI to the SPA
The web app grows its account surface: login (with next-redirect, unverified-hint + resend), signup (react-hook-form + shared Zod schemas, field-level api errors, closed-registration state fed by the new public GET /auth/registration), e-mail verification, forgot/reset password; a settings page with profile (locale applies immediately), password change, and active-session management; a Site-Admin page for instance name, default locale, and registration mode. AuthProvider holds /auth/me, applies the profile locale, and backs route guards (RequireAuth/RequireAnonymous/RequireSiteAdmin); the top bar gains a user menu. All strings ship in the new auth/settings namespaces (de+ en); the exception filter now preserves handler-specific error codes. Verified live: signup → Mailpit → verify → login → profile through the Vite proxy. Closes #16 Closes #17 Closes #18 Closes #19 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |