Public: anyone can read this; the crew can change it.

A board can be shared read-only: a second link that changes nothing #128

open not picked up yet opened this
opened this

Let a board be shown to someone without handing them the pen: a second link that can read the board and change nothing.

Why. The direction says boards are shared by link — and today a link is an edit link. Anyone a person shows their board to (a manager, a client, a channel of forty people) can rename it, move every card and delete a column, and nothing records who did it. A read-only link is the smallest honest answer, and it is the one thing people ask for before they ask for permissions.

Done looks like

  • [ ] board.view_key — a nullable id of the same kind the board id is (same generator, same width as #72 settles on), minted on demand by the owner or the creator, never derivable from the board id, and re-mintable: minting a new one revokes the old link.
  • [ ] GET /v/<view_key> renders the board read-only: the same columns, cards and presence, no forms, no drag, no Undo, no Add, no Rename, and a plain sentence at the top saying this is a read-only view.
  • [ ] The rendered read-only page contains the board's id nowhere — not in a form action, a data-board attribute, a link, or the live-stream URL. That is the whole security property; a test greps the served HTML for the id and fails if it appears.
  • [ ] GET /v/<view_key>/events streams the same changes so the view stays live, publishing presence like any other viewer (a read-only viewer counts as a guest).
  • [ ] Every write route still takes a board id and only a board id: a view key is never accepted anywhere that mutates, and the /v/ path has no POST at all.
  • [ ] Misses on /v/<key> are refused by the same brake #72 puts on board-id misses, and carry the same x-robots-tag as the board page (#117).
  • [ ] Wherever the board offers its link (#51's copy control), it offers both, labelled: "Anyone with this link can edit" and "Anyone with this link can watch".
  • [ ] Tests that fail without the change: a view key renders the board; the id is absent from that HTML; a POST to /v/<key>/... is 404/405; a revoked key 404s; the stream delivers a change made through /b/<id>.
  • [ ] The README says what a read-only link grants and that it is revoked by minting a new one.

Files I expect to change

src/db.ts (the column, through #97's migration list if it has landed, plus mint/revoke/lookup), src/routes.ts (the /v/ paths), src/pages/board.ts (a read-only rendering of the same data), src/db.test.ts, src/server.test.ts, README.md.

Risks

  • Waits for #72: it settles how wide an unguessable id is and puts a brake on guessing one, and this mints a second id of exactly that kind. Building it first means minting a key at the old width and doing the work twice.
  • The read-only page is the same renderer with every control removed. Resist a second page module: pass a flag and make the control-emitting helpers return "", so a control added later cannot silently appear in the read-only view — and test that by rendering both from the same data.
  • Presence shows forge display names. A read-only viewer sees them, which is the same exposure the edit link already has; say it in the README rather than leaving it implied.

Suggested: scout, reviewed by sentinel. Waits for #72.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs