Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 2 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ docs-users/ User Guide pages (getting-started, admin, listening, comm
docs-developers/ Developer pages (architecture, server{,/api}, frontend, bridge, meta, contributing)
sidebars-*.ts canonical page lists (hand-ordered)
screenshots/ capture pipeline: manifest.mjs (source of truth), run.sh,
capture-web.mjs, capture-admin.mjs, placeholders.mjs, .cache/ (gitignored)
capture-web.mjs, capture-admin.mjs, capture-setup.mjs, capture-meta.mjs,
placeholders.mjs, .cache/ (gitignored)
static/img/screenshots/ generated images (committed)
src/pages/index.tsx landing page; src/css/custom.css theme
.github/workflows/ ci.yml (build gate), deploy.yml (GitHub Pages on main)
Expand Down
4 changes: 2 additions & 2 deletions docs-developers/architecture/invariants.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,13 +160,13 @@ audiobooks" into "remote file read on your NAS".

- **No default creds**: first run either prints admin credentials + an auth code
**once** (the banner is the only place plaintext secrets ever appear) or, with
`--setup`, enables a one-time-token setup wizard at `/setup`
`--setup`, enables a one-time-token setup page at `/setup`
(`internal/api/handlers_setup.go`) that self-closes the moment an admin exists.
- **Hashed secrets**: tokens and auth codes are stored only as SHA-256 hashes;
passwords use argon2id (`internal/auth/hash.go`). `auth.ResolveRequest` resolves
opaque bearer tokens against the hashes.
- **Fragment-carried codes**: invite links are `<base>/connect#code=…` and the
setup wizard is `/setup#token=…` - fragments never reach the server or its
setup page is `/setup#token=…` - fragments never reach the server or its
access logs. The setup POST verifies its token in constant time.
- **Path safety**: `library.SafeJoin` rejects traversal outside the library root;
every filesystem access derived from user input goes through it. Share scoping
Expand Down
7 changes: 4 additions & 3 deletions docs-developers/contributing/documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ mappings:
| 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` |
| First-run setup: the `/setup` page (`handlers_setup.go`, `assets/setup.*`), the console's Set up your server flow and Overview card (`features/setup/`, `finish-setup-card.tsx`), `catalog/setup.go`, `library/preview.go`, `library/mounts.go`, `handlers_admin_setup.go` | `/users/getting-started/first-run` (a section and shot per step) + the quickstarts' setup step (`/users/getting-started/quickstart-docker`, `install-unraid`, `install-binary`) + `/users/admin/console-tour` (the card) + [web-ui.md](../server/web-ui.md#the-first-run-setup-flow) (the flow and the `/setup` page) + the [reference](../server/api/reference.md#admin-first-run-setup) (and `POST /setup`) + [api/index.md](../server/api/index.md#error-envelope-and-status-conventions) (the codes) + [data-model.md](../server/data-model.md#server-state) (`setup_state`, `0053`) + [configuration.md](../server/configuration.md) (`--setup`) + [backups-and-notifications.md](../server/backups-and-notifications.md#audit-log) (`settings.setup`) + the `server/setup-wizard*.png` and `setup/*.png` screenshots (`capture-setup.mjs`: `SHOTS_ONLY=setup/,server/setup-wizard screenshots/run.sh`) |
| 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) |
| Auth, invites, shares | [server/auth-and-security.md](../server/auth-and-security.md) + `/users/admin/users-and-invites`, `/users/admin/sharing` |
Expand Down Expand Up @@ -114,9 +115,9 @@ the feature name - several pages deliberately mark these as not-yet-shipped.
Every embedded image is listed in `screenshots/manifest.mjs` and lives under
`static/img/screenshots/`. The pipeline (`screenshots/run.sh`) rebuilds the
server, seeds a small public-domain LibriVox library, runs a demo-mode server,
and captures the web player, admin console, connect page, and setup wizard
with Playwright. Anything it can't reach gets a labelled placeholder so the
build never breaks.
and captures the web player, admin console, connect page, and the first-run
setup page and console flow (on a second, `--setup` server) with Playwright.
Anything it can't reach gets a labelled placeholder so the build never breaks.

Rules that keep this maintainable:

Expand Down
2 changes: 1 addition & 1 deletion docs-developers/contributing/workspace.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ npm --prefix admin-ui run dev # admin console dev server (proxies
```

Useful flags: `--data` (config/db/certs dir), `--ffprobe`/`--ffmpeg` (`""`
disables), `--setup` (first-run browser wizard instead of the printed creds).
disables), `--setup` (choose the first admin's password on a browser page instead of the printed creds; the console's setup flow follows either way).

**audiosilo-frontend**

Expand Down
11 changes: 6 additions & 5 deletions docs-developers/server/api/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ A few routes deliberately live *outside* the prefix:
| Route | Why |
|---|---|
| `GET /healthz` (also served at `GET /api/v1/healthz`) | container/orchestrator healthchecks expect a root-level probe |
| `GET /setup`, `POST /setup` | the first-run setup wizard is a browser page, not an API surface (and is off unless the launcher enables it) |
| `GET /setup`, `POST /setup` | the first-run setup page is a browser page, not an API surface (and is off unless the launcher enables it); the console's first-run flow uses `/api/v1/admin/setup` |
| `GET /.well-known/apple-app-site-association`, `GET /.well-known/assetlinks.json` | the well-known location is mandated by iOS/Android |
| `/`, `/connect`, `/admin`, `/assets/…`, `/web/…` | the baked-in admin/connect UI and the web player - static pages over the API, not part of it (see [Web UI](../web-ui.md)) |

Expand Down Expand Up @@ -183,8 +183,9 @@ Failures a person can fix also carry a machine-readable **`code`** next to
| `admin_needs_password` | `400` | an admin without a password (creating one, promoting a password-less account, or clearing an admin's password) |
| `password_too_short` | `400` | a password under the minimum length |
| `cannot_delete_self` | `400` | an admin deleting their own account |
| `path_not_absolute` | `400` | `GET /admin/fs/dirs` with a relative `path` |
| `folder_unreadable` | `404` | `GET /admin/fs/dirs` on a missing or unreadable folder |
| `path_not_absolute` | `400` | `GET /admin/fs/dirs` or `POST /admin/setup/preview` with a relative path |
| `folder_unreadable` | `404` | `GET /admin/fs/dirs` or `POST /admin/setup/preview` on a missing or unreadable folder |
| `invalid_setup_step` | `400` | `PUT /admin/setup` naming a step id the flow doesn't have, or an outcome other than `done` / `skipped` |
| `book_not_found` | `404` | an admin catalog call on a path that is not an indexed book (book page, edit, bulk edit, match, cover upload, community cover); an admin progress edit on a path with no progress and no indexed book; `POST /admin/bridge/jobs` and `GET /admin/bridge/estimate` on a path that is not an indexed book |
| `invalid_override` | `400` | a metadata edit the server refuses; the body also carries a `field` key naming the offending field (`PATCH /admin/libraries/{id}/book`, `POST /admin/books/bulk`) |
| `module_off` | `404` | any route a [managed module](../modules.md) mounts, while that module is off (Settings > Modules), for a caller the route's auth check admits (it runs first). The route exists either way, so turning the module on needs no restart. The `podcasts` module mounts `POST`/`DELETE /podcasts/follow` and `POST /podcasts/check`. Also `POST /admin/bridge/jobs` for a job the module owns (Phase 2b: `sidecars.contribute`, `meta.correct`, `sidecars.generate` with `contribute: "issue"`, the `contributions` module), checked before anything else about the job |
Expand All @@ -193,7 +194,7 @@ Failures a person can fix also carry a machine-readable **`code`** next to
| `unsupported_image` | `415` | a custom cover (uploaded or community) that is not a JPEG, PNG or WebP image |
| `cover_unavailable` | `502` | `PUT /admin/libraries/{id}/cover/community` or `GET /libraries/{id}/meta/cover` when the server couldn't fetch the image (the latter also for a minute after a failed fetch, without asking again) |
| `invalid_schedule` | `400` | a library `scan_schedule` that isn't `""`, `every:<N>h` (1, 3, 6, 12, 24) or `daily:HH:MM` (`POST`/`PATCH /admin/libraries`) |
| `invalid_pattern` | `400` | a library `ignore_patterns` list the server refuses: more than 100 patterns, one over 200 bytes, one that matches nothing, or a malformed wildcard; the message names the line (`POST`/`PATCH /admin/libraries`) |
| `invalid_pattern` | `400` | a library `ignore_patterns` list the server refuses: more than 100 patterns, one over 200 bytes, one that matches nothing, or a malformed wildcard; the message names the line (`POST`/`PATCH /admin/libraries`, `POST /admin/setup/preview`) |
| `invalid_metadata_source` | `400` | a library `metadata_source` other than `"tags"` or `"path"` (`POST`/`PATCH /admin/libraries`) |
| `not_indexable` | `404` | `POST /admin/libraries/{id}/book/rescan` on a path with no book any more (gone, not a book, or skipped by the library's ignore rules); the Bridge's `POST /bridge/rescan` on a path with nothing indexable (with `mode: "attachments"`, no indexed book there) |
| `current_device` | `409` | `DELETE /admin/devices/{id}` on the token making the request (sign out instead) |
Expand Down Expand Up @@ -248,7 +249,7 @@ Podcast actions add `invalid_request` and `invalid_feed_url` (`400`),
`no_library_access` (`403`), `show_not_found` (`404`), `not_podcast_library`,
`bridge_unavailable` and `library_not_writable` (`409`), and
`too_many_requests` / `checked_recently` (`429`). Library kinds add
`invalid_kind` / `kind_immutable` (`400`) and `root_overlaps` (`409`). An
`invalid_kind` / `kind_immutable` (`400`) and `root_overlaps` (`409`, also from `POST /admin/setup/preview`). An
undownloaded episode streams as `404 not_downloaded`. Full shapes and async
feed failures: [Podcasts](reference.md#podcasts).

Expand Down
Loading
Loading