The shell fallback was assigned locally but never exported, so
sync_watcher.py and the Python config layer never saw the resolved
value. They happened to land on the same /var/lib/romm/sync default by
coincidence; export it so the shell and Python defaults stay linked
through a single source of truth.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The prod Dockerfile creates /var/lib/romm/sync at build time, but if a
user overrides SYNC_BASE_PATH to a path that doesn't exist (or runs the
dev entrypoint, which never created the default), watchfiles fails to
start because its target directory is missing. Have both entrypoints
mkdir -p the resolved path before handing it to watchfiles.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Move the sync staging folder out of ROMM_BASE_PATH so it lives on a
dedicated writable mount. This lets the container run with a read-only
root filesystem without losing in-flight save uploads, and keeps
app-owned state separate from the user-curated library volume.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The init script ran `sed -i` against /etc/gunicorn/logging.conf, which
fails both on read-only root filesystems and when the container runs
as a non-root UID (since /etc/gunicorn is not chmod'd writable). Copy
the config to /tmp/gunicorn/logging.conf at startup and edit/use that
copy instead, leaving the image file untouched.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The gunicorn logging config hard-coded level=INFO, ignoring LOGLEVEL.
Patch it at startup via sed. Also pass --logging_level to rq worker
and rqscheduler so their framework logging respects LOGLEVEL.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When OTEL_SDK_DISABLED=true (set automatically when no OTEL_ env vars
are present), the opentelemetry-instrument wrapper does not properly
pass through the WATCHFILES_CHANGES environment variable to watcher.py.
This causes the filesystem watcher to silently fail - watchfiles detects
changes but watcher.py receives an empty WATCHFILES_CHANGES and exits
immediately without scheduling any rescans.
The fix skips the opentelemetry-instrument wrapper when OTEL is disabled,
allowing watchfiles to pass WATCHFILES_CHANGES directly to watcher.py.
Fixes automatic rescan on filesystem change for users who don't configure
OpenTelemetry (the majority of self-hosted deployments).
Related `mod_zip` issue https://github.com/evanmiller/mod_zip/issues/90
has been fixed in commit
288d66541c
By upgrading `mod_zip` to include this fix, we can remove the workaround
that involved using a separate internal location and server to serve
files for zipping.
The COEP (Cross-Origin Embedder Policy) and COOP (Cross-Origin Opener
Policy) headers are needed by EmulatorJS to use the `SharedArrayBuffer`
feature, and enable multi-threaded cores.
These headers are only being set by Nginx for responses to requests
made to the EmulatorJS player path. This is because cross-origin
isolation breaks other features in the application (such as YouTube
embeds), so we only want to enable it for the EmulatorJS player.
It's important to mention that this change does not work when `DEV_MODE`
is set, as we would need Vite server to also set these headers in that
case. This could be implemented separately, if needed.
These changes are not enough to enable multi-threaded cores in the
frontend. Because our application is a SPA, we also need to ensure
that navigation to the EmulatorJS player path results in a full page
reload, so that the cross-origin isolation can be applied. This will be
handled in a separate PR.