jschoubben 8148678af8 bazarr: reach sonarr and radarr through the mesh; placed dirs; the image ace runs
bazarr reached sonarr and radarr as `sonarr:8989` and `radarr:7878`,
container names on HAL's shared network, which the mesh does not have.
It now requires sonarr-api and radarr-api (provided since #156) and a
run-once step writes host, port, TLS, base path and key into bazarr
through bazarr's own POST /api/system/settings - the call its settings
screen makes - only for the fields that differ, and reads them back.
bazarr's config.yaml is not written by the mesh: bazarr holds it in
memory and rewrites it, so the two would overwrite each other. The key is
tried against the app first; a refused key (a minted pair credential
before the operator accepts the app's own) is never written, and the step
fails naming the `secret accept` that fixes it. Declared last so its
failing gates nothing else (ADR 0136); restart-on its four inputs.

The api-key own-secret is gone. bazarr makes its own key and nothing lets
the mesh set it, so a minted one could never work; the tools and the step
read auth.apikey from bazarr's own config/config.yaml (mounted read-only),
which also stays right if the key is regenerated. Nothing to accept.

The config dir is a pathless ${dir:config}; config.json and the bindings
live in a placed state dir; /var/lib/mesh/bazarr keeps only the broker.
The image is pinned to the digest ace runs (v1.6.1-ls364); the old pin was
v1.6.0-ls361, older than ace's database.

Based on feat/servarr-api-provision (#156); this branch contains it.

Verified: catalogue tests with MESH_CATALOGUE pass (not skipped); a
resolve of sonarr+radarr+bazarr on a fake ace renders both bindings and
sealed credentials into the state dir with every ${} filled, and bazarr
alone is refused naming sonarr and radarr; strict tsc passes and the
Dockerfile's non-strict compile builds; 8 node tests pass; the compiled
step against the pinned image in a throwaway container with fake
sonarr/radarr wrote sonarr (ip, port, apikey), refused radarr's minted
key and wrote nothing for it, wrote radarr once the key was right, and
changed nothing on a third run - the values landed in config.yaml.
2026-09-30 11:58:52 +02:00

mesh-catalog

The Novox Mesh catalogue. The modules the mesh builds, provisions and runs — as manifests, one per module under modules/.

This is data, not a control-plane concern. The manifests describe what a module is: what it provides, what it requires, the seats it claims, the resources the host applies for it. The engine that reads them — parsing, eligibility resolution, sealing, declaration emission — lives in the control plane (novox/mesh-controller, internal/catalogue), which consumes this repository as a build source. The host (novox/mesh-host) applies the declarations the control plane emits. Neither is here.

What a module is, and is not

A module is one thing the mesh can run, named once, described completely by its manifest. A manifest names its image (pinned by digest), the resources the host owns for it (directories, files, the container, the private network it joins), what it requires from a provider and what it provides to consumers, and the sealed secrets it needs filled on the machine.

  • Core mesh components are not modules. The node host, the foundation, the control-plane contexts and the surfaces are the mesh itself; they ship as their own repositories (mesh-host, mesh-foundation, mesh-controller, mesh-surfaces, mesh-sdk), not from here.
  • Standalone applications are not here either. A larger application lives in its own repository with its manifest at the root, registered with the mesh as a build source (novox/hq ADR 0010). This repository holds the modules the mesh maintains as its shared catalogue; an application the mesh merely hosts keeps its manifest beside its own code.

So there is one home for the catalogue the mesh owns, and every application that runs on the mesh rather than being of it carries its own — both reach the pipeline the same way, as a registered source.

Layout

modules/<name>.json      one manifest per module

Flat, because the catalogue's shape carries no meaning: a module is found by its name and described by its manifest, and what relates two modules — a shared seat, a claim, a provider/consumer edge — is data inside the manifests, not a directory the tree encodes (novox/hq, the domain-grouping question closed in favour of seats, claims and tags).

The manifest contract

The shape a manifest must satisfy is owned by the control plane's catalogue engine and is what validates a manifest before a machine ever sees it — a stray key, a consumer contributing the wrong provision field, an image that nothing builds. That validation belongs with this repository and is being re-homed here from mesh-controller; until it is, the pipeline is the gate — it builds each module and refuses a manifest it cannot resolve.

Where the reasoning lives

Design and decisions are in novox/hq:

  • 02-DECISIONS/0002-everything-is-a-module.md — one unit, no second mechanism
  • 02-DECISIONS/0010-applications-live-in-their-own-repository.md — why applications are not here
  • 02-DECISIONS/0030-the-repository-structure.md — the repositories, and the open tier-4 question this repository answers
  • 03-DESIGN/00-as-is/10-module-catalogue.md — the catalogue's shape, and what it records
S
Description
Novox Mesh — the catalogue. Module manifests the mesh builds, provisions and runs. Data, not a control-plane concern.
Readme
2.5 MiB
Languages
TypeScript 91.2%
Dockerfile 8%
Shell 0.8%