Public links: show linked notes (Linked notes permission) #106

Closed
opened 2026-09-25 17:30:23 +00:00 by kayg · 4 comments
Owner

Context

DESIGN §22 decides that a public link can have a Linked notes permission: the visitor sees the notes that link to the shared item. The share audit (branch job/share-audit, 2026-09-25) found that this was never built: no public endpoint returns linked notes and the link page shows none. The toggle was stored but did nothing.

The audit removed the option until it is built:

  • POST/PUT /api/v1/files/links returns 400 for permissions.linked_notes = true.
  • A stored link with linked_notes: true gets nothing from it (public.rs authorize clears the flag).
  • The Share dialog has no Linked notes toggle.

Work

  1. Server: GET /api/v1/public/{slug}/linked-notes lists the notes whose links or embeds target the shared item (the Notes plugin keeps them in note_links, see backlinks_for_path in crates/plugins/notes/src/lib.rs; the files plugin already depends on calternal-plugin-notes). Return only title and a note ID that is valid inside this link. GET .../linked-notes/{id} returns the Markdown of one of them.
  2. Nothing private beyond those notes: embeds and links inside a linked note that point outside the shared item must not resolve for the visitor (no thumbnails, no bytes, no names of other files). Decide whether the note body is shown as-is or with foreign links removed.
  3. Respect password, expiry, hidden names (a hidden-names link must not show note titles that contain the file name, or must hide them), and the Notes plugin being disabled.
  4. Web: a Linked notes section on the link page, rendered with the shared TextView.
  5. Re-enable linked_notes in validate() in crates/plugins/files/src/public.rs and the toggle in ShareDialog.svelte.
  6. Tests: extend tests/adversarial/attack2.py section share-options (only notes that link to the item; nothing from other notes; dead after expiry) and apps/web/e2e/share.mjs.
## Context DESIGN §22 decides that a public link can have a **Linked notes** permission: the visitor sees the notes that link to the shared item. The share audit (branch `job/share-audit`, 2026-09-25) found that this was never built: no public endpoint returns linked notes and the link page shows none. The toggle was stored but did nothing. The audit removed the option until it is built: - `POST/PUT /api/v1/files/links` returns 400 for `permissions.linked_notes = true`. - A stored link with `linked_notes: true` gets nothing from it (`public.rs` `authorize` clears the flag). - The Share dialog has no Linked notes toggle. ## Work 1. Server: `GET /api/v1/public/{slug}/linked-notes` lists the notes whose links or embeds target the shared item (the Notes plugin keeps them in `note_links`, see `backlinks_for_path` in `crates/plugins/notes/src/lib.rs`; the files plugin already depends on `calternal-plugin-notes`). Return only title and a note ID that is valid inside this link. `GET .../linked-notes/{id}` returns the Markdown of one of them. 2. Nothing private beyond those notes: embeds and links inside a linked note that point outside the shared item must not resolve for the visitor (no thumbnails, no bytes, no names of other files). Decide whether the note body is shown as-is or with foreign links removed. 3. Respect password, expiry, hidden names (a hidden-names link must not show note titles that contain the file name, or must hide them), and the Notes plugin being disabled. 4. Web: a Linked notes section on the link page, rendered with the shared `TextView`. 5. Re-enable `linked_notes` in `validate()` in `crates/plugins/files/src/public.rs` and the toggle in `ShareDialog.svelte`. 6. Tests: extend `tests/adversarial/attack2.py` section `share-options` (only notes that link to the item; nothing from other notes; dead after expiry) and `apps/web/e2e/share.mjs`.
Author
Owner

Starting work on job/share-edit, based on dev at 44b15bec4f. I am implementing linked-note reads alongside #105 public-link guest editing, with adversarial coverage.

Starting work on job/share-edit, based on dev at 44b15bec4f4e78e788c85b7ec60d54892a293b84. I am implementing linked-note reads alongside #105 public-link guest editing, with adversarial coverage.
Author
Owner

Decision for the details that DESIGN §22 left open: when a link hides names, linked Note titles use Linked note N. Linked Note detail is returned as text/plain after the server removes Markdown links, embeds, reference-style links, and active HTML references. Each detail request rechecks the exact shared item and Note ID. Deep links keep the stable Note ID in ?note=<id>.

Decision for the details that DESIGN §22 left open: when a link hides names, linked Note titles use `Linked note N`. Linked Note detail is returned as `text/plain` after the server removes Markdown links, embeds, reference-style links, and active HTML references. Each detail request rechecks the exact shared item and Note ID. Deep links keep the stable Note ID in `?note=<id>`.
Author
Owner

Contract finding: generating OpenAPI for #106 produced duplicate operation IDs. Both GET /api/v1/notes/linked-notes and GET /api/v1/public/{slug}/linked-notes were exported as linked_notes; generated TypeScript referenced the same operation type for both. I am giving the guest endpoint a unique operation ID and regenerating the contract/client before proceeding.

Contract finding: generating OpenAPI for #106 produced duplicate operation IDs. Both `GET /api/v1/notes/linked-notes` and `GET /api/v1/public/{slug}/linked-notes` were exported as `linked_notes`; generated TypeScript referenced the same operation type for both. I am giving the guest endpoint a unique operation ID and regenerating the contract/client before proceeding.
Author
Owner

Merged into dev at 371e53a0 (share e2e incl. live guest editing passes; authorization matrix 916 requests clean). Deploy status on #203.

Merged into dev at 371e53a0 (share e2e incl. live guest editing passes; authorization matrix 916 requests clean). Deploy status on #203.
kayg closed this issue 2026-09-27 05:37:28 +00:00
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
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
kayg/calternal#106
No description provided.