Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
9269f70
API reference: listening in the house, cast tokens, capabilities hous…
KodeStar Oct 9, 2026
fc24c51
Server pages: cast tokens, household privacy, CSP Cast sender origin,…
KodeStar Oct 9, 2026
c747260
Cross-repo contract: listening in the house and casting (seam 15)
KodeStar Oct 9, 2026
fdefe34
State and data: connection id = server + user, the one-time key migra…
KodeStar Oct 9, 2026
23c53f3
State and data: the profiles TODO as an MDX comment
KodeStar Oct 9, 2026
54b4ce6
Developer docs: Chromecast and Listening in the house pages, the rout…
KodeStar Oct 9, 2026
842dc18
User Guide: Listening in the house and Chromecast; casting needs a tr…
KodeStar Oct 9, 2026
6580ed1
Capability readers: household and cast; older servers lack them
KodeStar Oct 9, 2026
96f255f
Profiles and kids mode: User Guide and developer pages, Settings > Ho…
KodeStar Oct 10, 2026
438eab5
Cast tokens are a tokens kind tied to the sender's session; CSP names…
KodeStar Oct 10, 2026
3b7bab4
Playback: the output button is Audio output; Android's media output p…
KodeStar Oct 10, 2026
732672b
CarPlay is on by default: drop the pending/opt-in wording
KodeStar Oct 12, 2026
943cf48
Screenshots: the household shots, and the web-player set retaken for …
KodeStar Oct 12, 2026
6583b19
Developer docs: Phase 8 as the code has it
KodeStar Oct 12, 2026
67ee115
One home per fact: cast tokens and the household rules
KodeStar Oct 12, 2026
5bdcd39
User Guide: Phase 8 wording, clock-format times, one home per fact
KodeStar Oct 12, 2026
24bc21a
Screenshots: tidy the household and Cast capture
KodeStar Oct 12, 2026
0e8aa71
Docs review (max): Phase 8 statements match the code
KodeStar Oct 13, 2026
983946f
Merge origin/main into docs/player-8
KodeStar Oct 13, 2026
047232b
Data model: household_sharing is 0042 and cast tokens 0043
KodeStar Oct 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 40 additions & 4 deletions docs-developers/architecture/cross-repo-contract.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,10 @@ client side in [State & data](../frontend/state-and-data.md#bookmarks-notes-and-
`<img>`/`<audio>`, so cover and stream GETs accept the session token as a
`?token=` query param on every platform.

**Server:** `internal/api/middleware.go` - only the cover, stream and community
cover (`/meta/cover`) routes use `requireMediaAuth`, which calls `bearerToken(r, true)`; all other routes are
**Server:** `internal/api/middleware.go` - only the cover and stream routes
(`requireBookMediaAuth`, which also takes a cast token) and the community cover route
(`/meta/cover`, `requireMediaAuth`) call `bearerToken(r, true)`, both through
`mediaAuth`; all other routes are
header-only (`bearerToken(r, false)`), so tokens never ride the query string where
they could leak into access logs or Referer headers.
**Frontend:** `client.ts` `mediaTokenQuery()` → `coverUrl()`/`streamUrl()`. Native
Expand All @@ -140,9 +142,15 @@ thumbnail of a community `cover_url` that the server fetched, so the web player,
whose CSP stays `img-src 'self' data: blob:`, never contacts a cover host. Details
in [§14](#14-community-metadata-a-three-repo-seam).

**Cast tokens** (capability `cast`) ride the same `?token=`, on a book's cover and
stream only: a player casting to a Chromecast mints one for the book
(`POST /cast/token`) and builds the receiver's URLs with it, never with its session
token. Details in [§15](#15-listening-in-the-house-and-casting).

**A change requires:** do **not** "tighten" the server to reject query-param
tokens on media routes without first removing the web player's dependency on
them - the web player cannot authenticate media any other way.
them - the web player cannot authenticate media any other way (and a Cast
receiver has no other way either).

## 4. Audio `Content-Type` must be real (byte-sniffed)

Expand Down Expand Up @@ -263,7 +271,7 @@ version.
**Server:** `handleServerInfo` advertises `admin_ui`, `web_player`, `upload`,
`transcode`, `websocket`, `api_keys`, `export`, `metadata`, `meta_bundle`,
`browse_people`, `cover_sizes`, `next_book` and the flags added after it (the
latest, `meta_covers`, gates the community cover route), plus the
latest, `household` and `cast`, gate [§15](#15-listening-in-the-house-and-casting)), plus the
server version
(`api.Version`, stamped from the release tag via ldflags). `transcode` reflects
ffmpeg availability; `web_player` reflects whether `/web` is populated; `api_keys`
Expand Down Expand Up @@ -518,6 +526,34 @@ additive, same as every other seam here. A change to `pkg/query` or
`pkg/release`'s exported API reaches the server through its `go.mod` (and the
desktop manager, which builds the server).

## 15. Listening in the house and casting

The player redesign's Phase 8, two additive features each behind its own flag
(seam 8), mirrored in `types.ts` (`HouseholdSettings`, `HouseListeningRow`,
`HouseListening`, `CastToken`), `client.ts` and `hooks.ts`.

**Listening in the house** (capability `household`): `GET`/`PUT /me/household`
(`{ share_listening }`) and `GET /household/listening` (`{ sharing, listening: [Row] }`);
shapes in [the reference](../server/api/reference.md#listening-in-the-house). Every
privacy rule is the server's alone
([what other people see](../server/auth-and-security.md#listening-in-the-house-what-other-people-see)):
the player only decides what to show of the answer and how often to ask. Client side:
[Listening in the house](../frontend/listening-in-the-house.md).

**Casting** (capability `cast`): `POST /cast/token` `{ library_id, path }` ->
`201 { token, expires_at }`, a credential for one book accepted only as the
`?token=` of that book's cover and stream
([Cast tokens as media auth](../server/api/reference.md#cast-tokens-as-media-auth);
its lifetime and policy: [Cast tokens](../server/auth-and-security.md#cast-tokens)).
The web player's CSP admits the Cast Web Sender's three scripts on
`https://www.gstatic.com` by path in `script-src`, and nothing else. The receiver
never sees the session token; no flag, no Cast button. Client side, including which
address the receiver's URLs use: [Chromecast](../frontend/chromecast.md).

**A change requires:** the household rules stay in the server's catalog (a player
must never filter someone in); any widening of where a cast token is accepted is a
security-critical change with allowed and denied tests on the server.

## The wire-change checklist

Every change to the wire format follows the same shape (the worked example in
Expand Down
3 changes: 2 additions & 1 deletion docs-developers/architecture/invariants.md
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,8 @@ audiobooks" into "remote file read on your NAS".
whose `script-src` carries a sha256 hash of that document's inline scripts
(`web.htmlCSP` in `internal/web/web.go`).
- **Confined token-in-URL**: the `?token=` media-auth fallback is accepted **only**
on cover/stream GETs (`bearerToken(r, true)` via `requireMediaAuth` in
on the cover, stream and community cover GETs (`bearerToken(r, true)` via
`requireBookMediaAuth` / `requireMediaAuth` in
`internal/api/middleware.go`); every other route is header-only, so session
tokens can't leak into logs or Referer headers elsewhere.
- **Tested both ways**: security-critical code (`SafeJoin`, scope checks, rate
Expand Down
5 changes: 5 additions & 0 deletions docs-developers/contributing/documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ mappings:
| Playback engines / native module | [frontend/playback.md](../frontend/playback.md) (+ `/users/listening/playback` for lock-screen and headphone behaviour) |
| Smart speed, Voice boost, time saved (`effects.ts`, `time-saved.ts`, `effects-settings.tsx`, the Android `effects/` processors, `VoiceBoostTap.swift`, `VoiceBoostDSP.swift`) | [frontend/audio-effects.md](../frontend/audio-effects.md) (+ [frontend/book-page.md](../frontend/book-page.md) for `smartSpeedSaved`) + `/users/listening/full-player` (Smart speed and Voice boost), the Playback settings in `/users/listening/settings`, the Your listening row in `/users/listening/book-page` + the `web-player/phone-speed-sheet.png`, `settings.png` and `phone-settings.png` screenshots |
| CarPlay, Android Auto (`src/car/`, the module's car code, `plugins/withCarPlay.js`), the after-first-unlock token store, the iOS widgets and Live Activity (`src/widgets/`) | [frontend/native-integrations.md](../frontend/native-integrations.md) (+ [testing.md](../frontend/testing.md#native-checks) for how to run them) + `/users/listening/in-the-car`, `/users/listening/mobile-apps` (Availability, In the car, widgets), `/users/listening/sleep-timer` (the Live Activity). The web can't show these surfaces: device screenshots, when there are any, are added by hand to the manifest |
| Connection ids, the identity migration (`src/lib/connection-id.ts`, `src/lib/identity-migration.ts`, a new document keyed by a connection id) | [frontend/state-and-data.md](../frontend/state-and-data.md#connection-identity-server_iduser_id) (+ `ID_DOCUMENTS` when a new document is keyed by a connection id) |
| Profiles, the PIN, Settings > Household (`src/stores/profiles.ts`, `src/lib/profile-pin.ts`, `src/components/profiles/`) | [frontend/profiles.md](../frontend/profiles.md) + `/users/listening/profiles`, the Household and sharing section of `/users/listening/settings` |
| Kids mode (`src/components/kids/`, the bedtime rule in `auto-sleep-controller.ts`) | [frontend/kids-mode.md](../frontend/kids-mode.md) + `/users/listening/kids-mode` |
| Listening in the house (`src/components/house/`, the household hooks; server `catalog/household.go`) | [frontend/listening-in-the-house.md](../frontend/listening-in-the-house.md) + the [reference](../server/api/reference.md#listening-in-the-house) + [auth-and-security.md](../server/auth-and-security.md#listening-in-the-house-what-other-people-see) + `/users/listening/in-the-house`, `/users/admin/activity` (what the server records) |
| Chromecast (`src/cast/`, `plugins/withCast.js`; server `auth/cast.go`, `handlers_cast.go`, the web CSP) | [frontend/chromecast.md](../frontend/chromecast.md) + the [reference](../server/api/reference.md#casting) + [auth-and-security.md](../server/auth-and-security.md#cast-tokens) + [server/web-ui.md](../server/web-ui.md#per-document-csp-htmlcsp) for the web CSP (`castSenderScripts`, the policy `TestHTMLCSPCastSender` pins) + `/users/listening/chromecast`, `/users/listening/playback` (other speakers), `/users/getting-started/remote-access` (the certificate note) |
| Downloads / PWA, the offline companion (`offline-meta.ts`) | [frontend/offline.md](../frontend/offline.md) + `/users/listening/offline-downloads` |
| Scanner, detection, metadata | [server/scanner.md](../server/scanner.md) + `/users/getting-started/organizing-your-library` |
| Community chapters (`internal/chapteralign`, `internal/chaptercheck`, `catalog/communitychapters.go`, the Chapters card's panel) | [server/community-chapters.md](../server/community-chapters.md) + the Community chapters section of `/users/admin/books` (and `/users/admin/health` for the Health rows) |
Expand Down
5 changes: 4 additions & 1 deletion docs-developers/frontend/book-page.md
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ few values (its state, and the bookmark glyph: a bookmark inside the book marks

## The aside

`BookAside` stacks three cards (a right-hand column from `BODY_COLUMNS_MIN`, see
`BookAside` stacks up to four cards (a right-hand column from `BODY_COLUMNS_MIN`, see
[Layout](#layout-by-the-measured-width), else between the hero
and the tabs):

Expand All @@ -226,3 +226,6 @@ and the tabs):
[time saved](audio-effects.md#time-saved-srcplaybacktime-savedts)). Null for a
book neither started nor finished; the history is only fetched then, and kept fresh
for ten minutes (`HISTORY_STALE_MS`: a span this device records refreshes it).
- **In the house** - `InTheHouseCard` (`src/components/house/`), last, only while the
listener shares and someone who shares is on this book; see
[Listening in the house](listening-in-the-house.md).
Loading
Loading