From 068f7ee36deeaae3fd38ec4c92dc4948443af526 Mon Sep 17 00:00:00 2001 From: KodeStar Date: Fri, 13 Nov 2026 22:50:19 +0000 Subject: [PATCH 01/10] Install on a NAS: a Synology page, the data folder first, permission fixes A new user on a Synology NAS hit folder permissions twice (the data folder, then the books) and found nothing in the docs about either. Now: - Install on a Synology NAS: Container Manager, the data folder created first, PUID/PGID from the DSM user (usually 1026/100), the share permissions for that user (not only a group), the log, updates. - The Docker quickstart creates the data folder before the first start, says the server needs read access to the books, has a docker run variant with PUID/PGID, and links Get the apps. - Troubleshooting has a permission-denied entry quoting the container's log lines, the start error and the share timeout line verbatim. - Backups: a backups folder on another disk, owned by the server's user, never a share mounted at /data/backups. - Set up your server: backups/ in the data folder list, NAS log pointers, and only a plain /admin visit opens the flow (a link opens its page). - Developer docs: the entrypoint's chown and checks, the launcher's start check, a backup's permission_denied detail, the unlistable library root, and the setup redirect's first-page rule. Part of #277. --- .../architecture/release-pipeline.md | 4 +- docs-developers/contributing/documentation.md | 1 + .../server/backups-and-notifications.md | 7 +- docs-developers/server/configuration.md | 39 ++++++- docs-developers/server/scanner.md | 8 +- docs-developers/server/web-ui.md | 18 ++-- docs-users/admin/backups.md | 24 +++++ docs-users/features.md | 2 +- docs-users/getting-started/first-run.md | 9 +- .../getting-started/install-synology.md | 102 ++++++++++++++++++ .../getting-started/quickstart-docker.md | 32 ++++-- docs-users/index.md | 3 +- docs-users/troubleshooting.md | 28 ++++- sidebars-users.ts | 1 + 14 files changed, 251 insertions(+), 27 deletions(-) create mode 100644 docs-users/getting-started/install-synology.md diff --git a/docs-developers/architecture/release-pipeline.md b/docs-developers/architecture/release-pipeline.md index 94e5051e..36175046 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 under Docker](../server/configuration.md#folder-permissions-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..de5bcf17 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-under-docker) + `/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/server/backups-and-notifications.md b/docs-developers/server/backups-and-notifications.md index 5bd839bd..533a5766 100644 --- a/docs-developers/server/backups-and-notifications.md +++ b/docs-developers/server/backups-and-notifications.md @@ -142,6 +142,11 @@ push out the copy of the database as the older server left it. - whether a backup is being made; - `last`: the newest attempt since the server started, with a short `error` (`disk_full`, `permission_denied` or `failed`). The cause is in the log. + A backup checks its folder with a probe file of its own before `VACUUM INTO` + (`fsaccess.CheckWrite`), since SQLite words a refused folder as "unable to + open database file". A refusal at any step (the check, the chmod, the rename) + is `permission_denied`, and `Result.Detail` (not on the wire) holds the + sentence naming the folder, the server's user, the owner and the fix. - `latest`: the newest backup in the folder that isn't a before-restore or before-upgrade copy, whenever it was made; - `next`: `null` when the schedule is off. @@ -324,7 +329,7 @@ subscribed to its kind. A nil `*Service` does nothing, so callers don't check. | `new_device` | every pairing exchange (`POST /auth/exchange`), and a password sign-in (`POST /auth/login`) unless an earlier session of the same person already carried its `device_id` (`auth.IssueSession`). A sign-in without a `device_id` always counts. Never for demo accounts. | `user`, `device` (the name the client sent, one plain line of at most 100 characters), `app` (from `X-AudioSilo-Client`, may be empty) | | `invite_redeemed` | a pairing exchange whose token came from an invite code | `user` (whose invite), `device` | | `update_available` | `updates.Checker.OnAvailable` after a check that finds a newer release. Announced **once per version** (`dedup_key` is the version). | `version`, `name`, `url` (the release page) | -| `backup_failed` | `backup.Service.OnFailure` (scheduled or manual) | `trigger`, `error` (`disk_full`, `permission_denied`, `failed`) | +| `backup_failed` | `backup.Service.OnFailure` (scheduled or manual) | `trigger`, `error` (`disk_full`, `permission_denied`, `failed`), and for `permission_denied` a `detail` sentence (the folder, the server's user, its owner and the fix) that the notification's text uses | | `bridge_job_finished` | `bridge.Service.OnTransition` (wired by `API.SetBridge`): a [Bridge](../bridge/index.md) job moved **into** `done` | `job_id`, `job_kind`, `state`, `library`, `library_id`, `path`, `title`, plus per-kind fields. See [Bridge event data](#bridge-event-data). | | `bridge_job_failed` | the same hook, for a job that moved **into** `failed` or into a notable `needs_attention`. See [Bridge event data](#bridge-event-data). | as `bridge_job_finished`, plus `code` and `detail` | | `book_released` | the [upcoming releases](api/reference.md#upcoming-releases) job (`upcoming.Runner`, wired to `API.announceReleases`). See [`book_released`](#book_released). | `count`, `titles` (the first five, each `Title (Series #position)`, or `Title (Series)` without a position) | diff --git a/docs-developers/server/configuration.md b/docs-developers/server/configuration.md index a8358308..6c5d816c 100644 --- a/docs-developers/server/configuration.md +++ b/docs-developers/server/configuration.md @@ -886,7 +886,7 @@ by the settings table in `internal/config/settings.go` (`fields`: each entry's | `AUDIOSILO_SESSION_DAYS` | `activity.session_days` | integer, 30-3650 | | `AUDIOSILO_BACKUP_SCHEDULE` | `backups.schedule` | `""`, `daily:HH:MM` or `weekly:DAY:HH:MM` | | `AUDIOSILO_BACKUP_KEEP` | `backups.keep` | integer, 1-365 | -| `AUDIOSILO_BACKUP_DIR` | `backups.dir` | absolute path | +| `AUDIOSILO_BACKUP_DIR` | `backups.dir` | absolute path; the Docker entrypoint also gives the folder to `PUID`/`PGID` (see [Folder permissions under Docker](#folder-permissions-under-docker)) | | `AUDIOSILO_MODULES_PODCASTS_ENABLED` | `modules.podcasts.enabled` | `strconv.ParseBool` (`true`/`1`/…); locks the console toggle when set | | `AUDIOSILO_MODULES_CONTRIBUTIONS_ENABLED` | `modules.contributions.enabled` | `strconv.ParseBool` (`true`/`1`/…); each module's toggle is `AUDIOSILO_MODULES__ENABLED` | | `AUDIOSILO_PODCAST_DISCOVERY_ENABLED` | `podcasts.discovery.enabled` | `strconv.ParseBool` (`false`/`0`/… turns it off); read at start | @@ -1252,3 +1252,40 @@ tools. | `/restore.json` | A restore waiting for the next start (removed when it is applied or refused) | | `/restore-result.json` | How the last restore went | | `/audiosilo.db.before-restore-