Commit Graph
16 Commits
Author SHA1 Message Date
jschoubben 50b639ff52 The env-file references to files the conversion removed go with them 2026-09-21 12:43:41 +02:00
jschoubben 1a28e5aec6 Six modules take their secrets from files; the rest say precisely why not
From the survey of every env-file secret (ADR 0086, issue 041): amqp-ping,
minio, mongodb and grafana use the _FILE twin their software honours;
mesh-catalog and model-usage read DATABASE_URL_FILE (a file the mesh
templates, mounted where only the runtime reads it); grafana's secret files
belong to its own account. Two dead deliveries removed: a line nothing read
in amqp-email-forwarder, and mailu's secret.env on four containers that
never read it. The 25 exceptions that remain carry the surveyed reason —
convertible and awaiting a bed, convertible through a generated config file,
the application's own code, or not convertible.
2026-09-21 12:29:25 +02:00
jschoubben 32dec5f0c2 The controller reads its credentials from files; every other env-file secret says why
ADR 0086. mesh-controller mounts its six own secrets and names them with
_FILE twins, so no credential of its own reaches its environment. The 35
containers that still read a secret through an env-file carry
secrets-in-environment with the reason; converting each where its software
accepts a path is the per-module work of issue 041.
2026-09-21 10:10:33 +02:00
jschoubben d2dce34716 The catalogue asks on start, and registers a replay as history
A replayed build is registered exactly as any other and announced to nobody. A
module that moved months ago is not something anything should act on now:
emitting `upgraded` would have the control plane decide about a rollout, and
`rebuild-needed` would ask for builds of things already current.

Asked on every start rather than only the first, because a catalogue cannot tell
whether it has a gap — and the answer is idempotent, so asking when there is none
costs a message. Asked after subscribing, so a build arriving during the replay
is not lost between the two.

Closes novox/hq 04-ISSUES/050 with mesh-control.

Claude-Session: https://claude.ai/code/session_01D6qtiYU3P9jk3pnAXyAFyx
2026-09-15 01:26:58 +02:00
jschoubben cf116a4932 Modules compile in the toolchain and ship on the runtime 2026-09-14 01:57:06 +02:00
jschoubben 5ea88b149b The three modules name their base rather than pinning a copy of it
Each named a digest produced inside a lab that no longer exists, so none of them
could be built anywhere else. They say which module they stand on now, and there
is deliberately no default — a build nobody told stops at the declaration rather
than at a reference that resolves to nothing.
2026-09-13 23:53:22 +02:00
jschoubben 21ad008879 Stale means the artifact moved, not the commit
A comment changed in a build recipe is a new commit and a byte-identical image.
Comparing commits called every module standing on it stale, so the mesh would
have rebuilt itself entirely to arrive back exactly where it started — and
listed each dependent once per commit that had produced the same image.
2026-09-13 02:48:21 +02:00
jschoubben 87243bc524 Every module stands on a base the mesh built
The base was a digest typed in by hand, for an image nothing in the mesh could
produce — so the graph held edges pointing at it with no version on the far end,
and the one change that reaches every module at once could never be noticed.
It is a module now, and these edges resolve.
2026-09-13 02:45:37 +02:00
jschoubben c80d9d0f7c The graph holds what a module declares, not only what it was built on
Build edges are discovered by building; requires and provides are stated by the
module about itself. Both belong in the graph and answer different questions —
and "what provides postgres-database" needed a sweep over every manifest, which
only something holding all of them can do.
2026-09-13 01:44:56 +02:00
jschoubben 2d2ca80e3b Index the edge after the column that carries it exists
On a store with the old shape the table is not re-created, so an index declared
beside it is built on a column the migration has not added yet.
2026-09-13 01:41:23 +02:00
jschoubben f15c814145 A build edge names an artifact, because that is what the builder can see
The catalogue expected each edge to name a module and a commit. The builder
sends a pinned image reference — it cannot know which module produced it, that
is a fact about the graph. So every build that had been built on top of anything
was rejected, and only the modules built against nothing ever registered.

Versions now record what they published, and an edge resolves through that. An
edge to an artifact no module here produced is kept: it resolves by itself when
that module is registered, which is the ordinary case while a mesh fills in.
2026-09-13 01:37:51 +02:00
jschoubben 9387f8b040 A module that listens is served, not run
`run` imports an entrypoint without binding a broker — it exists for a step that
works offline and exits. Both the catalogue and amqp-ping subscribe on import,
so both died on the first on() with no broker bound.
2026-09-13 01:30:39 +02:00
jschoubben 594295f009 The catalogue restarts when its database credentials change
Without it the container keeps whatever the env file said when it was created.
Nothing reports that: it runs, and it is wrong.
2026-09-13 01:22:38 +02:00
jschoubben f7d57e9556 The catalogue brings its own postgres driver
The runtime base carries what every module needs, and a database driver is not
that. Installed into an empty directory because the module's package.json also
names the sdk, which lives in the base rather than on a registry.
2026-09-13 01:16:23 +02:00
jschoubben e742b6a569 The catalogue builds its own runtime, and postgres serves its tools
Both modules keep their tools in an entrypoint of their own, so an image that
named only the consumer would serve none of them.
2026-09-13 01:13:43 +02:00
jschoubben a73cb8a2f8 The catalogue, as a module that owns the module graph
It links module-versions to each other and knows nothing about nodes; which
machine runs what stays the control plane's (novox/hq ADR 0070, 0072). Keeping
them apart is what lets the control plane carry on composing declarations while
this is down.

The builder announces what it built, this places it in the graph and announces
what that means, and the control plane hooks the meaning rather than the build
output. A rebuild producing the commit already current is registered and is not
an upgrade — announcing it would ripple outward forever through modules that did
not change.

Ordering is not computed. Modules stale and waiting on nothing that is itself
stale are announced as buildable; the rest stay stale and appear once whatever
they were waiting for is registered, so a chain and a diamond need no special
handling and nothing holds a plan.

Four tools over the graph: what this mesh holds, one module in full, what a
change to a module reaches, and what must be rebuilt and why. The edges are
derived from builds rather than declared, so they cannot drift from what the code
actually uses.

Claude-Session: https://claude.ai/code/session_01D6qtiYU3P9jk3pnAXyAFyx
2026-09-12 23:16:05 +02:00