14 Commits
Author SHA1 Message Date
jschoubben 7b06a7a408 Event names are local now, in the manifests and in the code
Every module named its events the way the old bus spelled a routing key —
`module.<module>.<verb>`. Design 29 says a module names an event locally and the
mesh works out where it lands, so all 37 were stale against a rule already
decided. On the new bus that derives into a namespace belonging to a module
called "module", so no cross-module subscription in the mesh matched anything:
nothing failed, nothing reacted (novox/hq 04-ISSUES/127).

36 manifests converted, and 43 files of module code with them. The code mattered
as much as the manifests: the runtime builds the subject from what `emit()` is
handed, so a converted manifest with unconverted code would have had the
permission and the subject disagree.

Three things the new check found on the way:

- `photos` emitted an event its manifest never declared, which the new bus refuses
  outright. Declared.
- `showcase` waited for an event nothing emits, so its demo could never be
  triggered — only `showcase` may publish under its own name. It emits both halves
  now.
- `distribution` declared an event named after a different module. It emits
  `image.pushed` under its own name. An event about a *role* belongs on the seat,
  where the name outlives whoever holds it, but the sdk has no way to publish on a
  seat yet, so that stays recorded rather than declared.

The audit logger's "everything" pattern is `**` rather than the old bus's `#`.
2026-09-27 14:42:28 +02:00
jochen 3d271f72ea redis: say whether it still holds a consumer's ACL user
The server keeps ACL users in memory only, so a restart forgets every
consumer while the provisioner keeps running (hq issue 120). holds()
checks ACL GETUSER for the user, enabled, with the mesh's password, so
the harness makes a forgotten user again. Needs mesh-sdk 0.1.1.
2026-09-26 00:53:13 +02:00
jschoubben c71bbd497f redis: INFO is allowed back — client libraries ask it at connect, and it reads nothing a consumer keeps 2026-09-22 02:03:23 +02:00
jschoubben 421f4d8577 redis: a consumer's ACL loses the dangerous category — a key pattern does not confine FLUSHALL (novox/hq issue 080) 2026-09-22 01:39:29 +02:00
jschoubben 82e513a360 Add the mesh-vault module; redis takes its password from it
mesh-vault provides `secret` (novox/hq ADR 0085, design 24). The value is
the pair credential the controller mints — the vault holds no copy, only a
ledger of who holds one, its fingerprint and every rotation, and two tools that
answer by fingerprint and never by value. Rotation is `rotate secret`,
unchanged machinery pointed at a secret with an owner (design 13). Named in the
mesh's own namespace, beside mesh-controller and mesh-catalog, because it is
the mesh's own code rather than wrapped software.

redis is the first consumer: its own password stops being an own-secret nothing
could rotate and becomes a `secret` it requires, read from the same file into
the same hole. The server now restarts on its config, or it would keep the
password it started with through every rotation (playbook 06).
2026-09-21 00:48:13 +02:00
jschoubben 591b26f712 Ten migration-critical modules become mesh-buildable (issue 060)
keycloak, mailu, minio, mongodb, mssql, nextcloud, portainer, redis,
umami and verdaccio get the Dockerfile + build section the eight
buildable modules already had; their runtime containers name the
artifact instead of a placeholder digest.

One convention, settled (060's open question, informed by 061): the
runtime container runs serve mode with every serve-time entrypoint in
MESH_TOOL_MODULES — tools serve, events flow, and a provider's
provisioner reconciles in the same process with the broker connected.
postgres, gitea and lavinmq are retrofitted from args-run provisioners,
which served no tools and emitted lifecycle events nowhere.

route-proxy is deferred: its build context is the mesh-controller
repository, a cross-repo shape the build section cannot yet express.
2026-09-18 02:02:21 +02:00
jschoubben 1f0c3ac69e fix: db/cache/store providers must serve their port; drop baserow's empty redis contribution
The breadth install (catalogue-broad) surfaced two latent resolution bugs that
single-module compile checks never caught (compile != resolve):

1. postgres/redis/mongodb/minio served no "port", yet seven consumers
   (baserow, letta, invoicing, gitea, umami, keycloak, nextcloud) reference
   ${bound:<provision>:port}. A binding auto-carries at/from/as; the port is the
   provider's half of the answer and must be declared in `serves` (manifest.go:
   "Serves is what a consumer needs to know ... a port, a path, a realm"). Added
   port to each: postgres 5432, redis 6379, mongodb 27017, minio 9000. Without it
   no DB consumer could resolve, let alone deploy.

2. baserow declared an empty contribution `contributes: {"redis-cache": {}}`.
   redis's serves spec for the cache is empty (a cache takes no per-consumer
   payload), so a consumer only `requires` it; an empty contribution is refused.
   Dropped it (requires/binds/secrets unchanged).

Found by mesh-lab catalogue-broad; the same fixes are mirrored in that bed.

Claude-Session: https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF
2026-09-06 02:38:22 +02:00
jschoubben 6e8c18afff Resync hq ADR references 0044-0054 -> 0039-0049 after the hq record reconciliation 2026-09-05 12:49:16 +02:00
jschoubben 08bdd0e456 Providers: broker-bound runtime container replaces the old provisioner (ADR 0052/0053)
Each provider's separate provisioner container becomes a runtime container that
serves the module's tools and runs its provisioner under the module's scoped broker
account: mesh-runtime-<module>, on the backend's own network (reaching the backend
by name and the broker by NAT), with MESH_BROKER_FILE + MESH_RECEIVES replacing
GRANTS. umami gains the broker own-secret it lacked. cloudflare-dns's adapter is
re-pointed at the ADR 0053 contract (a data provision, like umami — its record
return is the scoped-out concern).

Proven: provider-on-backend-network green — redis's runtime, on the private redis
network, binds the broker and provisions a consumer with the mesh's credential.

Claude-Session: https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF
2026-09-05 00:52:09 +02:00
jschoubben 3b03fcd8f7 Providers create the credential the mesh minted, sealing nothing (ADR 0053)
redis, postgres and minio adapters drop generatePassword + the returned credential:
each creates the resource under the login the mesh derived (`as`) with the password
the mesh minted (`p.password`). minio's client gains a secret-key argument so it sets
the mesh's secret rather than generating one. umami (analytics) is re-pointed at the
new contract too; its siteId return is a data-provision concern ADR 0053 scopes out.

Proven: mesh-lab provider-uses-mesh-credential green — redis creates the consumer's
login with the mesh's password, the consumer authenticates (PONG), no seal key set.

Claude-Session: https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF
2026-09-05 00:27:37 +02:00
jschoubben 6615b5e3b3 plex, redis: module runtime containers + declare self-consumed events (ADR 0052/0046)
plex gains a broker-bound tools/events runtime container (mesh-runtime-plex)
alongside its server, and a never-throwing plex_reachable health probe. redis and
postgres subscribe to their own lifecycle events in index.ts but declared no
consumes — so the substrate never made the queue the runtime binds and it crashed
on start (404). Declare the consume, as ADR 0046 requires. Proven end-to-end in the
mesh-lab: assigned-plex and assigned-redis both green.

Claude-Session: https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF
2026-09-04 22:29:41 +02:00
jschoubben 67d8d0dcbd catalogue: one mesh-state namespace — /var/lib/mesh/<module>/ (consistency)
The review found broker own-secret paths drifting: mostly /var/lib/<module>/
broker, but grafana/redis/icecast/nextcloud carried a '-module' suffix to dodge
a collision with the service's own /var/lib/<name> data, and photos sat under
/etc. Normalized to one collision-free namespace a service never owns:
/var/lib/mesh/<module>/broker, with a mesh-state directory resource for the
parent, across 24 modules. audit-logger is grandfathered (lab-proven, referenced
by the assigned test, and it has no service to collide with). All 33 manifests
parse; no client hardcoded a path, so nothing in code moved.

Claude-Session: https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF
2026-09-04 21:28:55 +02:00
jschoubben 4d04585831 redis, postgres: full nox provider modules — client, tools, provisioner, events
redis provides redis-cache: a real admin client speaking RESP over a raw socket
(node:net, no deps); provisioner makes a keyspace-scoped ACL user per grant.
postgres provides postgres-database: admin client executing through psql (the
consistent shell-out port, like minio's mc), full DDL for create/drop database+
role, a CSV row parser, read-only query tool. Both emit
module.<x>.<thing>.provisioned/.deprovisioned from the provisioner. Both
typecheck; manifests parse.
2026-09-04 02:39:03 +02:00
jschoubben c5efbd53ca A module is a package, not a bare manifest
Each module becomes modules/<name>/ holding module.json, with room for
the rest of what a module is — its tools, health checks, provisioning,
lifecycle — which the conversion from hal still has to bring across.
The flat <name>.json was only the resource-declaration half.

Claude-Session: https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF
2026-09-03 00:47:14 +02:00