Nocturne workstation deployment and image-access repair¶
The owner requested deployment of the latest Nocturne application. The gallery
is healthy on tested source 7841ed0f57c5664dfc6ca86892f426e4a8b06d83, based on
merged main e3e679c6, with the image-permission fix in
PR #565. Production remains in
legacy mode with ten workers; NAS feed activation is unchanged.
Starting state and root causes¶
The existing PC container was already stopped. Docker Desktop could not resolve its old WSL startup-script bind. The canonical PC release uses an image-owned NFS entrypoint, removing that temporary host-script dependency.
The published e3e679c6 bundle passed checksums and its actual-PC read-only NFS
preflight, but application startup failed: /app/main.py was root-owned mode
0640 and unreadable after the entrypoint dropped to 1000:100. Backend tests
had run as root and the metadata-only preflight intentionally did not import the
application. The failing restart loop was stopped before continuing.
Fix and candidate evidence¶
- Normalize only image-owned
/apppermissions witha+rXduring the canonical Docker build. Keep existing state ownership and NAS permissions unchanged. - Add a mandatory non-root image check before release packaging. It reads the
packaged application/frontend files and imports
mainas1000:100, with network disabled, capabilities dropped and disposable state only. - The new check failed against the published image and passed after that single permission change. The source fix is committed and pushed in PR #565; it is not represented as an already-merged or registry-published release.
- The canonical replacement was built from the exact committed source with a deliberately restrictive checkout. The non-root check passed for 49 files.
- The actual replacement image passed 380 backend tests, 10 skipped; the release/probe unit suite passed 24 tests. Skips are not NAS acceptance.
- A private catalog-only migration copy passed integrity checking and preserved all tested annotation digests. It had no original media or live catalog mount.
The local candidate image is
ghcr.io/spadoople/nocturne-gallery:sha-7841ed0f57c5664dfc6ca86892f426e4a8b06d83,
with Docker image ID
sha256:572e222b1fb66d4261b5a8675cdb2ed2551834d91aa26c50bb8c08afc6810196.
It was packaged and checksum-verified locally using the versioned release tool,
then deployed without rebuilding or pulling another image. The earlier published
bundle is retained for diagnosis; do not confuse it with the working candidate.
Backup and live installation¶
Before replacement, a private stopped-state backup of 442,083 catalog records was restored to a separate directory. SQLite integrity passed; media annotation, tag, album and action digests matched. The existing password was preserved. The old image was tagged for recovery and its configuration/entrypoint capture retained. No backups or prior artifacts were removed.
The fixed candidate repeated the actual NFS preflight successfully: NFSv4.1,
read-only mount, expected media containment, UID/GID 1000:100, empty supplementary
groups and zero process capabilities. It mounted no catalog or password and did
not start the app. Since the old container was stopped, the isolated preflight
used its existing Compose network instead of an unavailable container namespace.
The new service reused the original local state, Compose project, login and
proxy settings. Only base, PC and release Compose files were used. No host-media
bind, entrypoint-script bind, old scanner bind or ownership reset was introduced.
Only one local container writes the catalog. The actual server process runs as
1000:100 with effective capabilities zero, and its NFS mount is read-only.
flowchart LR
A[Stopped old gallery] --> B[Private backup and restore test]
B --> C[Versioned permission fix]
C --> D[Canonical image and backend tests]
D --> E[Read-only actual NFS preflight]
E --> F[Single PC catalog writer]
F --> G[Login, assets, browse and progress checks]
Live verification¶
- Container healthy; live SQLite integrity passed and original annotation digests matched after startup. State UID/GID/mode were preserved.
- Existing login/session, frontend assets, media browsing, collections, search, status and an actual cached thumbnail were verified through the authenticated application. Verification credentials/session values were not logged.
- Public HTTPS returned the existing authentication redirect. This is not a claim of a fresh end-to-end SSO/browser session.
- Ten workers, 60-GiB cache and 300-second polling were verified.
- Scan progress advanced from 210,000 to 288,400 and then 354,800 items, with no scan errors. Completed jobs advanced from the backup's 523,031 to 523,800; failed jobs decreased from 8,034 to 8,030 at that checkpoint. The first full post-deployment scan subsequently completed successfully in about 494 seconds (eight minutes fourteen seconds), verified from persisted success/start times; the next scheduled scan began afterward. This is not a comparable-load speedup benchmark.
Raw logs, environment values, NAS source/media paths, catalog backups and account state remain private in the workstation release/backup directories. Direct and C&C-hop SSH checks of the former saltierpoop gallery were blocked by missing trusted host keys; no key was accepted and no SSH setting changed. This deployment did not start that gallery; its current remote state was not independently refreshed.
Remaining work and rollback¶
The running candidate is deployed; merging PR #565 and publishing a new official release are separate actions. The fix prevents future root-only build tests from allowing this image-permission defect through packaging.
NAS helper/transport installation, bounded live discovery acceptance, full parity, scraper impact and feed activation remain separate. Existing media-job failures are retained for diagnosis; no blanket retry/reset was performed.
Keep the current local state and additive schema. Prefer legacy scheduling on the generation-aware image for functional rollback; an old image may misinterpret new versioned cache metadata. Preserve the tested backup and archived original configuration. Do not restore an old catalog over newer annotations as a routine rollback, change exports, add source binds or reset permissions.