Skip to content

List the relics this browser holds on the homepage - #97

Merged
jwaldrip merged 7 commits into
mainfrom
feat/home-relics-list
Oct 7, 2026
Merged

jwaldrip merged 7 commits into
mainfrom
feat/home-relics-list

Conversation

@jwaldrip

@jwaldrip jwaldrip commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

Problem

The relics a reader has opened are only listed on /dashboard. The homepage, which is also the installed app's start_url, shows install copy and nothing of theirs.

Change

  • / lists the relics this browser holds keys for, in a "Your relics" section after the lede, newest opened first. Each row is the relic's kind and its title, linking to /{id}#{key}. A link at the bottom points to /dashboard for backup, forget, and relics you only commented on.
  • The list is read from this browser's localStorage key vault, through the same row model the dashboard uses (moved out of viewer.ts into relic-rows.ts so the homepage bundle does not pull in sucrase). It makes no request: no session check, no /api/auth/relics.
  • The section ships hidden and stays hidden when the browser holds no keys, so a first-time visitor sees the page as before. With JavaScript off, / is the static page.
  • New bundle /assets/home.js, 7.4 KB, built separately from the viewer. The service worker does not cache it, so a new version reaches readers on the next load.
  • / CSP gains script-src 'self' and still has no connect-src, so fetch, XHR, WebSocket, EventSource and beacons fall back to default-src 'none'. That is a backstop rather than a wall: it does not govern navigation, and same-origin images still load. What keeps the list in the browser is the script we serve.
  • /install keeps today's body and its script-free CSP. The only difference is 70 lines of inert CSS from the shared template.
  • This reverses the documented "/ runs no script" and "no my relics list" rules. The reversal is recorded in docs/frame.md, docs/spec/viewer.md (6.4, 6.6, 6.7), docs/decisions.md and docs/design/operations.md, with the cost stated.
  • Deploy smoke tests now check /assets/home.js, that / references it, and that /install has no script.

Proof

The real server and real built assets in headless Chromium. Four vault entries were seeded in localStorage.

  • Empty storage: section hidden, display: none, no children.
  • Seeded: rows render newest-opened first. A stored #r1... key yields one #. A missing title falls back to the id. A long title clips to one line with an ellipsis. The section sits between the lede and "How it goes".
  • 1440 light, 390 mobile and 1440 dark screenshots checked.
  • Console and CSP: 0 messages and 0 violations. Requests after DOMContentLoaded: 0. Clicking a row opens the relic with its key.
  • /install with the same storage: no section, no script. / with JavaScript disabled: no list.

bun run verify passes (lint, typecheck, 1476 tests). The new server and client tests were each run against the old source or a deliberately broken one, and failed.

- Refactor landingPage to conditionally emit client script, mount, and script-src CSP for GET /
- Keep /install byte-identical to shared landing copy without client script or mount
- Add contract styles for home relic list using existing tokens
- Repin tests for mount placement, single script tag, CSP boundary, and copy parity
- Add /assets/home.js and smoke assertions to deploy workflow
The comment above landingPage claimed the missing connect-src makes the
list structurally unsendable. It refuses the request APIs; it does not
govern navigation or same-origin images, so say that. home.js now calls
the renderer directly, guarded against the partial document stub the
viewer tests install. /install keeps its blank line.
@jwaldrip
jwaldrip merged commit 64eaf77 into main Oct 7, 2026
1 check passed
@jwaldrip
jwaldrip deleted the feat/home-relics-list branch October 7, 2026 03:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant