Capability-scoped plugin API endpoints #74

Closed
opened 2026-07-04 14:52:39 +02:00 by fable-5 · 1 comment
Collaborator

Context

Plugin data access runs through viewer-scoped API endpoints — a plugin can never read more than the person looking at it (ADR 0008).

Scope

Implement the plugin API surface behind /api/v1/plugin/: listPages, getPageOutline, getPageContent (markdown), getBlock (cross-page block content), each executing with the requesting user's session and standard permission guards; host-side bridge wiring these to RPC capabilities (readCurrentPage, readPond, readBlock); block-data read/write (blockData) routed through the editor document (write requires page write permission).

Acceptance criteria

  • a plugin viewed by a label-restricted reader gets filtered listPages results (permission matrix test)
  • getBlock returns content only from readable pages
  • block-data writes by a read-only viewer are rejected end to end
  • endpoints reuse existing guards (no parallel permission logic — grep-provable)

Technical notes

  • ADR 0008, permissions.md.

Dependencies

Depends on #52, #73.

Size: ~1.5 days


Conventions: English code/comments, clear human-readable code, no hard-coded UI strings (ADR 0012, add de and en), permission checks only via the shared guard (docs/architecture/permissions.md). Read the referenced ADRs before starting.

## Context Plugin data access runs through viewer-scoped API endpoints — a plugin can never read more than the person looking at it (ADR 0008). ## Scope Implement the plugin API surface behind `/api/v1/plugin/`: `listPages`, `getPageOutline`, `getPageContent` (markdown), `getBlock` (cross-page block content), each executing with the requesting user's session and standard permission guards; host-side bridge wiring these to RPC capabilities (`readCurrentPage`, `readPond`, `readBlock`); block-data read/write (`blockData`) routed through the editor document (write requires page write permission). ## Acceptance criteria - [ ] a plugin viewed by a label-restricted reader gets filtered `listPages` results (permission matrix test) - [ ] `getBlock` returns content only from readable pages - [ ] block-data writes by a read-only viewer are rejected end to end - [ ] endpoints reuse existing guards (no parallel permission logic — grep-provable) ## Technical notes - ADR 0008, permissions.md. ## Dependencies Depends on #52, #73. **Size**: ~1.5 days --- *Conventions: English code/comments, clear human-readable code, no hard-coded UI strings (ADR 0012, add `de` **and** `en`), permission checks only via the shared guard (docs/architecture/permissions.md). Read the referenced ADRs before starting.*
fable-5 added this to the M7 — Plugins milestone 2026-07-04 14:52:39 +02:00
fable-5 added the
backend
plugins
labels 2026-07-04 14:52:39 +02:00
Author
Collaborator

Done in 48798d4. Pipeline fully green (8 contexts + the warm-up gate; the fidelity flake stayed fixed, passing first-try).

Viewer-scoped plugin data API behind /api/v1/plugin/, backing the SDK readCurrentPage and readPond capabilities:

  • GET plugin/ponds/:id/pages (listPages), plugin/pages/:id/{outline,content,meta} — each reuses the existing @RequiresPondRole/@RequiresPagePermission guards and PagesService, so there is no parallel permission logic (grep-provable) and a plugin sees exactly what its viewer could.
  • Host side: host-capabilities builds the handlers from a per-surface context (the host holds the current page/pond ids — a plugin can only ask about the current page or this pond); wired into the sandbox runtime + PluginFrame, with ui.openPage/toast routed to host callbacks.

Acceptance criteria:

  • a label-restricted reader gets filtered listPages results and 404 on the hidden page (api db test)
  • endpoints reuse existing guards, no parallel permission logic
  • getBlock from readable pages / [ ] read-only blockData write rejection — deferred to #76, where the plugin_block node introduces block addressing (the current schema has no block ids). Agreed with the maintainer.

Tests: api db test (owner sees all, label-restricted reader filtered + 404, non-member 404, anonymous 401); web unit test pins the endpoint mapping, id-encoding, and missing-context rejection.

Done in `48798d4`. Pipeline fully green (8 contexts + the warm-up gate; the fidelity flake stayed fixed, passing first-try). Viewer-scoped plugin data API behind `/api/v1/plugin/`, backing the SDK `readCurrentPage` and `readPond` capabilities: - `GET plugin/ponds/:id/pages` (listPages), `plugin/pages/:id/{outline,content,meta}` — each reuses the existing `@RequiresPondRole`/`@RequiresPagePermission` guards and `PagesService`, so there is no parallel permission logic (grep-provable) and a plugin sees exactly what its viewer could. - Host side: `host-capabilities` builds the handlers from a per-surface context (the host holds the current page/pond ids — a plugin can only ask about the current page or this pond); wired into the sandbox runtime + `PluginFrame`, with `ui.openPage`/`toast` routed to host callbacks. **Acceptance criteria:** - [x] a label-restricted reader gets filtered `listPages` results and 404 on the hidden page (api db test) - [x] endpoints reuse existing guards, no parallel permission logic - [ ] `getBlock` from readable pages / [ ] read-only `blockData` write rejection — **deferred to #76**, where the `plugin_block` node introduces block addressing (the current schema has no block ids). Agreed with the maintainer. Tests: api db test (owner sees all, label-restricted reader filtered + 404, non-member 404, anonymous 401); web unit test pins the endpoint mapping, id-encoding, and missing-context rejection.
Sign in to join this conversation.
No Milestone
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#74
No description provided.