diff --git a/docs-developers/architecture/release-pipeline.md b/docs-developers/architecture/release-pipeline.md index 94e5051e..bf796f92 100644 --- a/docs-developers/architecture/release-pipeline.md +++ b/docs-developers/architecture/release-pipeline.md @@ -107,7 +107,9 @@ The multi-stage `Dockerfile`: with `ARG WEB_IMAGE=ghcr.io/kodestar/audiosilo-web:latest`; - uses `alpine:3.24` for the final stage, with **ffmpeg installed from apk**. Unlike the native binaries, the Docker image does bundle ffmpeg. The stage also - has a `PUID` and `PGID` entrypoint and presets `AUDIOSILO_WEB_DIR=/app/web`. + has a `PUID` and `PGID` entrypoint, which gives that user its folders and checks + them (see [Folder permissions](../server/configuration.md#under-docker)), + and presets `AUDIOSILO_WEB_DIR=/app/web`. Because the image pins one specific web build, the server and its bundled player always ship as a **known-compatible pair**. Native apps talking to any server diff --git a/docs-developers/contributing/documentation.md b/docs-developers/contributing/documentation.md index d689718a..bdc8377a 100644 --- a/docs-developers/contributing/documentation.md +++ b/docs-developers/contributing/documentation.md @@ -120,6 +120,7 @@ mappings: | `pkg/query`, `pkg/query/querytest`, `pkg/release` in audiosilo-meta, or the artifact schema | [meta/overview.md](../meta/overview.md#public-go-packages) + [meta/data-model.md](../meta/data-model.md#the-compiled-artifact-and-schema-versioning) + the [cross-repo contract](../architecture/cross-repo-contract.md#13-community-metadata-a-three-repo-seam) (mirror-mode servers read the artifact directly) | | Meta schemas, `metaserve` API, or intake tooling (`route.yml`, the intake denylist included) | [meta developer pages](../meta/overview.md) (data model, API, contributing) - and the [cross-repo contract](../architecture/cross-repo-contract.md) when the server's `/meta` envelope is affected, and `/users/community/contributing-from-your-server` when the limits that hold a Bridge's submission change | | meta.audiosilo.app site UI | `/users/community/meta-site` + the `meta/` screenshots (+ [meta/overview.md](../meta/overview.md) when it is a site-architecture change) | +| The Docker entrypoint (`docker-entrypoint.sh`: `PUID`/`PGID`, the chown and the folder checks) or the folder permission wording (`internal/fsaccess`, a backup's `permission_denied`) | [configuration.md](../server/configuration.md#folder-permissions) + `/users/troubleshooting#permission-denied` (it quotes the log lines verbatim) + the install pages (`/users/getting-started/quickstart-docker`, `install-synology`, `install-unraid`) | | Build / release / distribution | [release-pipeline.md](../architecture/release-pipeline.md) + [releasing.md](./releasing.md) (+ `/users/getting-started/install-unraid` when the native archives or the LinuxServer.io image's template, paths or defaults change) | | A capability flag | [server/api/index.md](../server/api/index.md) + the feature's user page | | The shared design tokens (`audiosilo-server/admin-ui/src/styles/globals.css`: colours, font stacks, shadows, motion) | Section 2 (shared tokens) of this site's `src/css/custom.css`, copied across verbatim (the dark block stays inside its `@media screen` wrapper), then a look at the built site in light and dark | diff --git a/docs-developers/frontend/chromecast.md b/docs-developers/frontend/chromecast.md index 21b33024..bf8dd6ee 100644 --- a/docs-developers/frontend/chromecast.md +++ b/docs-developers/frontend/chromecast.md @@ -141,6 +141,7 @@ The pure rules are `cast-media.ts`: | `codec` | The book needs the transcoder and the server has none. | | `no-connection` | The book's server isn't signed in. | | `episode` | A podcast episode, which never casts. | + | `proxy` | The address in use is the origin of the connection's [proxy headers](state-and-data.md#proxy-headers) (`behindProxy`). The receiver fetches by itself and can't send them, so the proxy would turn it away. | - **`castNeedsTranscode`** is true for the server's `direct_playable: false`, or for a codec the Default Media Receiver doesn't play (`CAST_UNPLAYABLE_CODECS`: ALAC, the diff --git a/docs-developers/frontend/connect-and-addresses.md b/docs-developers/frontend/connect-and-addresses.md index 461f61b0..77cb1702 100644 --- a/docs-developers/frontend/connect-and-addresses.md +++ b/docs-developers/frontend/connect-and-addresses.md @@ -32,6 +32,22 @@ counted by `StepDots` (`CONNECT_STEPS`): Start, Sign in and Ready. [`lan_url`](../server/configuration.md#home-address-lan_url)). - The server's error. +#### Proxy headers on the connect screen + +Off the served player, a **Proxy headers** toggle opens a `ProxyHeadersEditor` +for the address as the listener typed it (`typedUrl`, never the field after the +flow rewrites it to a plain-http fallback). The set is held in memory as the +session's `pendingProxyHeaders`, and only for an https origin. The flow's bare +clients are `connectClient(base)` (`src/api/connect-client.ts`) and +`probeServerId`, through `connectProxyHeaders(url)`: the pending set for that +origin, else a signed-in connection's set for it (a second person on the same +server). A reconnect's sign-in (`ownId`) takes only the pending set or that +connection's own set, never another connection's. The scanner hands a code back +to a new `ConnectStart`, which takes up the pending set, so a QR code pairs with +the headers when its address is the field's. The flow's layout drops the pending +set when the flow is left, and a sign-in stores it with the connection (see +[Proxy headers](state-and-data.md#proxy-headers)). + #### Pairing links, QR codes and invite links Native offers **Scan a QR code** (`/connect/scan`, `expo-camera`). Web offers **I have a diff --git a/docs-developers/frontend/state-and-data.md b/docs-developers/frontend/state-and-data.md index 2f4ad1cc..24995ed6 100644 --- a/docs-developers/frontend/state-and-data.md +++ b/docs-developers/frontend/state-and-data.md @@ -39,6 +39,10 @@ endpoints themselves. server keeps the stored app when a request has no header. See [Client identification](../server/api/index.md#client-identification-x-audiosilo-client). +- **Proxy headers.** A connection's [proxy headers](#proxy-headers) are the + constructor's sixth argument. `request()` and every hand-built request send + them, `authHeaders()` hands them to the media layers with the token, and + `proxyHeadersFor(url)` gives them alone for a URL that carries `?token=`. - **Path-addressed everything.** Content calls are `GET /libraries/{id}/{item,chapters,cover,stream}?path=…` and the like. The path rides as a query parameter, never a URL segment, because encoded slashes are a @@ -1546,6 +1550,51 @@ Tokens are stripped before the metadata is persisted. `serverId` on a connection persisted before that field existed. It runs after [the launch reset](#the-launch-reset), so it never reads wrongly-keyed records. +#### Proxy headers + +A connection to a server behind a sign-in proxy (Cloudflare Access, Authentik, +Authelia) can carry custom headers, `Connection.proxyHeaders` (`{ origin, +headers }`). The rules are in `src/lib/proxy-headers.ts` and the storage in +`src/lib/proxy-headers-store.ts`. They are credentials, kept like the token: + +- **Storage.** They live in memory and in secure-store under + `audiosilo.proxy-headers.` (see [Storage layers](#storage-layers)). `persist` + strips them and keeps only the non-secret `hasProxyHeaders` marker, and + `hydrate` reads them beside the token for a marked connection only. They never + reach AsyncStorage, the query cache, a URL or a log line. +- **Lifetime.** They go wherever the token goes: sign-out, a retired identity, + the auth reset, a connection `hydrate` drops. The identity migration copies them + in its token step (`copyProxyHeaders`). `removeConnection` deletes the token + first, so a failed delete stops the removal with the headers still kept. A new + connection to a server another connection here reaches with headers takes a + copy of that set under its own id. +- **One https origin.** A set names the origin (scheme, host and port) it was + entered for. `proxyHeaderRecord` is the one rule: a request to that origin + carries the set, and any other URL gets none: a plain-http URL, another port, + or the home address unless the set was entered for it (an https home address + is one of the Account card's choices). A set for an http origin is refused at entry and dropped + at load (`cleanProxyHeaders`). Reserved names (`Authorization`, `Cookie`, + `Host`, `Range`, `Content-Type`, `Content-Length`, `Connection`, + `Transfer-Encoding`, `X-AudioSilo-Client`) and values with control characters + are refused the same way. +- **Web.** Only a request same-origin with the page carries them. The server's + CORS lists a fixed set of request headers, so a custom one cross-origin would + fail its preflight. `` and `