- Imported tracks are created directly under their final name with
O_EXCL (never overwriting; " (2)" on conflict), fsync'ed and checked
against SHA-256 — no hidden .pipu-tmp file next to them any more.
- On any failure the agent removes its own partial file and the empty
directories it created for it.
- The write check uses access(W_OK) and the read-only mount flag instead
of creating and deleting .pipu-write-test in the storage root.
- Tests assert the storage tree holds only audio files after successful
and failed writes and after the write check.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- storage-agent: POST /internal/files copies a staged file into a source
(temp file, fsync, SHA-256 check, hard link so nothing is overwritten,
" (2)" suffix on conflict, owner PIPU_FILE_UID/GID) and
POST /internal/files/test. NFS sources that accept uploads are mounted
rw in an agent-only /library-rw and bind-mounted read-only for others;
local sources are written through /library-host-rw.
- meta: write templates ({track:02}, {disc:1-}, [optional]) with name
sanitising and length limits, suggestion from the source's regexes
and a reversibility check against 50 catalogue tracks.
- API and admin UI: accepts_uploads / write_template on sources,
write-template suggest and check endpoints, write test result.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- schema for sources, path patterns, media files, artists, albums,
tracks, genres (migration 00003)
- path patterns (RE2 with named groups, skip), merge with tags by
source priority, fallbacks from the file name and folder, name
normalization and multi-artist split
- scanner: walk, skip unchanged files by size and mtime, ffprobe in 8
workers with a single catalog writer, renames by content hash keep
the track, duplicates fold into one track with the best file as
primary, missing files are kept 30 days
- storage-agent mounts NFS with validated server, export and an option
whitelist, no shell; test mount counts audio files
- admin API: sources CRUD, connection test, patterns with preview,
scan, library status, file errors, host folder picker; scheduled
scans and health checks; mounts are restored on start
- catalog API: artist, artist albums, album with tracks, track
- api image is alpine with ffmpeg for ffprobe
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ci / docker (образы собираются — их сервер собирает сам при обновлении) (services/api/Dockerfile, storage-agent, storage-agent) (push) Canceled after 0s
Security and robustness:
- storage-agent rejects an empty INTERNAL_TOKEN (config notEmpty + middleware)
- api takes the client IP only from X-Real-IP (chi RealIP trusted True-Client-IP)
- /readyz reports ok/fail and logs the cause; checks run in parallel with a timeout
- storage-agent shuts down gracefully; shared httpx.NewServer/Serve with IdleTimeout
- goose migrations take a Postgres advisory lock
- recommender readyz: per-check timeout, redis socket timeouts
Clients:
- Android release allows cleartext HTTP for LAN servers (expo-build-properties)
- checkServer times out after 10s; connect button is disabled while checking
Deploy and CI:
- install.sh hands cache/uploads/updates/models to the non-root service users
- update.sh refuses downgrades unless PIPU_ALLOW_DOWNGRADE=1
- recommender/analyzer get only the env they need; required secrets fail fast
- nginx security headers and CSP; strip True-Client-IP
- dev compose binds ports to 127.0.0.1; postgres/redis images pinned
- version.mjs fails on a malformed explicit tag instead of releasing 0.0.0-dev
- contract check catches untracked generated files
- final releases fail without a release keystore; changelog skips rc tags
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>