Add a `route` contribution (requires/contributes/binds) to every web app so
each gets a Host-routed public name via route-proxy, mirroring the
de-spiegel/only-office pattern:
- novox: gitea, keycloak, nextcloud, umami, invoicing, verdaccio, registry,
and mailu (single mail.novox.be -> 7080; admin/webmail/api ride that port).
- ace: grafana, sonarr, radarr, lidarr, bazarr, ombi, tautulli, jackett,
nodered, searxng, home-assistant, bookshelf, baserow.
Split photos so its three sites each get a name: photos keeps server +
admin-client (photos.novox.be), and new photos-eef (eef.novox.be) and
photos-filip (filip.novox.be) modules carry the client sites.
The production FQDN stays the literal default; a per-node .incus name is a
settings override applied where the mesh runs, not a manifest hardcoding.
Claude-Session: https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF
04-ISSUES/036: eight media modules each declared the shared library and
download directories under /services/media/* as their own `directory`
resources. Six of them owning one path is the collision the resolver
refuses — so the stack's only sensible assignment, all on one machine
sharing one filesystem, would be refused the first time two landed
together.
The media library is the operator's, owned by no module (novox/hq
ADR 0051). Move every /services/media/* path from an owned `directory`
resource to an `accesses` entry: the mesh mounts it and owns nothing —
does not create, chown, reconcile or remove it — and several modules may
access one path with no conflict. Each module's own config and mesh-state
directories stay owned resources.
Modes are least-privilege: plex reads the libraries it streams; the
managers and download clients get read-write on what they import and
write; bazarr writes subtitles into the libraries (read-write) and only
reads the download spool. The container volume mounts are unchanged.
Claude-Session: https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF
Mirrors the proven catalog patterns field-for-field:
- lidarr -> the Servarr twin of radarr/sonarr (API v1, artist content); no
provisioner (it is a consumer app).
- mongodb -> postgres shape: mongodb-database provider, provisioner mints a
per-consumer db+user (ADR 0053), client shells to mongosh (no npm driver,
the psql convention).
- mssql -> postgres shape: mssql-database provider, sqlcmd client.
- mosquitto -> redis shape: mqtt-topic provider via the Dynamic Security
plugin, deliberately avoiding hal's password_file (that file is nox issue
011 exactly); provisioner mints a per-consumer MQTT client+role.
All four typecheck (strict, NodeNext) against the built @novox/mesh-sdk, and
their service images are digest-pinned to resolved registry digests. The
mesh-runtime-<mod> images keep the all-zeros placeholder the pipeline pins,
as postgres/redis do, and must bundle each module's CLI (mongosh/sqlcmd/
mosquitto_ctrl) as mesh-runtime-postgres bundles psql.
Not yet lab-verified: each module lists in-code what an integration test must
prove (auth model, provisioner reconcile, mosquitto dynsec bootstrap ordering).
Claude-Session: https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF