Security fix, critical.mesh_store_reader — the login mesh-store.query and postgres_query run as — was granted pg_read_all_data, so any caller of the store seat's query verb could read every column of every database: the controller's Ed25519 signing key (identity.signing_key.private, which signs every declaration a node-engine applies as root), the inventory's sealed secrets and operator keys, the bus users' password hashes, and every consumer's own secrets (gitea, keycloak, nextcloud…). Confirmed live on 2026-10-08 by privileges only (has_column_privilege, pg_has_role); no value was selected.
What changes
The reader holds no role.ReaderStatements no longer grants pg_read_all_data; every pass revokes every role the reader is a member of — so an existing install loses pg_read_all_data at the first pass after this build starts, and at the first query even before that.
Deny by default, per column. A consumer declares what the reader may read in its contribution: "contributes": {"postgres-database": {"readable": ["agents", "steps(id, created_at)"]}}. A list, so the controller's reading of contributes cannot take it for the several-instance shape; contribution values are passed through as they are, so the controller's module check needs nothing new. A database nobody declares — the controller's identity, inventory and licences among them — gives the reader nothing.
A secret is never granted, declared or not: a column or table whose name says private/secret/seal/token/password/credential/salt/otp/key is refused and reported.
Reconciled every minute on every database (RunReader): table-level grants revoked, undeclared column grants revoked, declared ones granted, schema usage granted where needed; one implicit transaction per database. postgres_reader_grants (r) answers the last pass: readable, refused, missing, revoked, exposed — names only.
Fail closed where it is used. Before a caller's statement runs, the reader's own session lists the secret-named columns it can read (a grant to PUBLIC or by hand shows there); if any, the statement is refused naming them. A statement naming a known secret table of the controller's or a secret-sounding name is refused before it is sent — defence in depth; the privileges are the guard.
Tests (each fails on main)
TestTheReaderIsMadeAsTheAdminWithEveryAttributeOnce now requires no GRANT pg_read_all_data and a REVOKE.
TestLiveAnInstallsReaderReadsNoSecret (real server, MESH_POSTGRES_LIVE), using only what the module had before: an install's reader as found (pg_read_all_data plus a hand grant) — on main it reads the fixture signing key; here it is refused and loses the role.
TestLiveTheReaderReadsWhatIsDeclaredAndNoSecret: across every database, no secret-named column readable after the reconcile; declared columns readable; SELECT * on a table with a refused column fails; a second pass changes nothing; a narrowed declaration takes back; a GRANT … TO PUBLIC is found as exposed and refuses queries.
Unit tests for the parser, the secret names, the grant plan, the statement guard, the probe refusal, the contributions file, and that every catalogue manifest's readable parses.
Run here against a throwaway PostgreSQL 18; the live store runs 17.
After merge
The module's upgrade policy is record: a person takes the build. Until it is taken, the hole stays open on the live store. After it, mesh-store.query reads nothing in any database until modules declare readable (none does yet) — diagnosis that read the controller's inventory through it loses that until the controller declares what of its own is readable (a follow-up; the controller is no consumer of the provision, so it has no contribution to declare it in).
hq issue 337 holds the report and the diagnosis, including the recommendation on the signing key.
**Security fix, critical.** `mesh_store_reader` — the login `mesh-store.query` and `postgres_query` run as — was granted `pg_read_all_data`, so any caller of the store seat's `query` verb could read every column of every database: the controller's Ed25519 signing key (`identity.signing_key.private`, which signs every declaration a node-engine applies as root), the inventory's sealed secrets and operator keys, the bus users' password hashes, and every consumer's own secrets (gitea, keycloak, nextcloud…). Confirmed live on 2026-10-08 by privileges only (`has_column_privilege`, `pg_has_role`); no value was selected.
## What changes
- **The reader holds no role.** `ReaderStatements` no longer grants `pg_read_all_data`; every pass revokes every role the reader is a member of — so an existing install loses `pg_read_all_data` at the first pass after this build starts, and at the first query even before that.
- **Deny by default, per column.** A consumer declares what the reader may read in its contribution: `"contributes": {"postgres-database": {"readable": ["agents", "steps(id, created_at)"]}}`. A list, so the controller's reading of `contributes` cannot take it for the several-instance shape; contribution values are passed through as they are, so the controller's module check needs nothing new. A database nobody declares — the controller's `identity`, `inventory` and `licences` among them — gives the reader nothing.
- **A secret is never granted, declared or not:** a column or table whose name says private/secret/seal/token/password/credential/salt/otp/key is refused and reported.
- **Reconciled every minute on every database** (`RunReader`): table-level grants revoked, undeclared column grants revoked, declared ones granted, schema usage granted where needed; one implicit transaction per database. `postgres_reader_grants` (r) answers the last pass: readable, refused, missing, revoked, exposed — names only.
- **Fail closed where it is used.** Before a caller's statement runs, the reader's own session lists the secret-named columns it can read (a grant to PUBLIC or by hand shows there); if any, the statement is refused naming them. A statement naming a known secret table of the controller's or a secret-sounding name is refused before it is sent — defence in depth; the privileges are the guard.
## Tests (each fails on main)
- `TestTheReaderIsMadeAsTheAdminWithEveryAttributeOnce` now requires no `GRANT pg_read_all_data` and a `REVOKE`.
- `TestLiveAnInstallsReaderReadsNoSecret` (real server, `MESH_POSTGRES_LIVE`), using only what the module had before: an install's reader as found (pg_read_all_data plus a hand grant) — on main it **reads the fixture signing key**; here it is refused and loses the role.
- `TestLiveTheReaderReadsWhatIsDeclaredAndNoSecret`: across every database, no secret-named column readable after the reconcile; declared columns readable; `SELECT *` on a table with a refused column fails; a second pass changes nothing; a narrowed declaration takes back; a `GRANT … TO PUBLIC` is found as exposed and refuses queries.
- Unit tests for the parser, the secret names, the grant plan, the statement guard, the probe refusal, the contributions file, and that every catalogue manifest's `readable` parses.
Run here against a throwaway PostgreSQL 18; the live store runs 17.
## After merge
The module's upgrade policy is `record`: a person takes the build. Until it is taken, the hole stays open on the live store. After it, `mesh-store.query` reads nothing in any database until modules declare `readable` (none does yet) — diagnosis that read the controller's inventory through it loses that until the controller declares what of its own is readable (a follow-up; the controller is no consumer of the provision, so it has no contribution to declare it in).
hq issue 337 holds the report and the diagnosis, including the recommendation on the signing key.
mesh/merge-gate pass: builds postgres, sent nowhere; no bus step; 2 wait(s) for a person; every machine composes with the change as it did without (4 of 4 …
mesh_store_reader held pg_read_all_data, so every caller of mesh-store.query
and postgres_query could read every column the store holds: the signing key
nodes trust, sealed secrets, password hashes. The reader now holds no role;
each minute, on every database, its memberships are revoked and its grants
brought to what the consumer's contribution lists under readable, minus any
column or table whose name says it holds a secret. Before a statement runs,
the reader's own session is asked which secret columns it can read and the
statement is refused if any; a statement naming a known secret table is
refused before it is sent.
Deliverynovox/mesh-catalog@e0e97a96c401 — delivered since 2026-10-08T20:17:14Z
Delivery plan — builds postgres, sent nowhere; no bus step; 2 wait(s) for a person
tier 0: postgres
ace: waits for a person: postgres
novox: waits for a person: postgres
postgres waits for a person: its policy records (module)
postgres provides postgres-database: its consumers are sent again after it
postgres keeps data (ADR 0233): what it holds is backed up before it moves
Transitions
2026-10-08 20:11 (new) → proposed (announced): novox/mesh-catalog#143's head announced
2026-10-08 20:13 proposed → checked (checked): the verdict names this commit
2026-10-08 20:13 checked → ready (accepted): the gate passed or warned, and the repository's own check did not fail
2026-10-08 20:16 ready → published (merged): on the trunk its modules follow: merged there, and its walk opened — or nothing for a walk to move
2026-10-08 20:16 published → delivering (go): its walk started: let go by this owner in its group's order, by a person, or on the controller's own path
2026-10-08 20:17 delivering → delivered (done): its walk is done: every machine of its deploy plan passed, or was left as its policy says
The commit's note under refs/notes/mesh-plan keeps every transition: git log --notes=mesh-plan.
<!-- mesh-delivery:view -->
**Delivery** `novox/mesh-catalog@e0e97a96c401` — **delivered** since 2026-10-08T20:17:14Z
**Delivery plan** — builds postgres, sent nowhere; no bus step; 2 wait(s) for a person
- tier 0: postgres
- ace: waits for a person: postgres
- novox: waits for a person: postgres
- postgres waits for a person: its policy records (module)
- postgres provides postgres-database: its consumers are sent again after it
- postgres keeps data (ADR 0233): what it holds is backed up before it moves
**Transitions**
- 2026-10-08 20:11 (new) → proposed (announced): novox/mesh-catalog#143's head announced
- 2026-10-08 20:13 proposed → checked (checked): the verdict names this commit
- 2026-10-08 20:13 checked → ready (accepted): the gate passed or warned, and the repository's own check did not fail
- 2026-10-08 20:16 ready → published (merged): on the trunk its modules follow: merged there, and its walk opened — or nothing for a walk to move
- 2026-10-08 20:16 published → delivering (go): its walk started: let go by this owner in its group's order, by a person, or on the controller's own path
- 2026-10-08 20:17 delivering → delivered (done): its walk is done: every machine of its deploy plan passed, or was left as its policy says
The commit's note under `refs/notes/mesh-plan` keeps every transition: `git log --notes=mesh-plan`.
mesh/merge-gate pass: builds postgres, sent nowhere; no bus step; 2 wait(s) for a person; every machine composes with the change as it did without (4 of 4 …
mesh/merge-gate: PASS — every machine composes with the change as it did without (4 of 4 compose) (modules: postgres)
mesh/repo-check: PASS — its merge-check.sh passed
Delivery plan — builds postgres, sent nowhere; no bus step; 2 wait(s) for a person
tier 0: postgres
ace: waits for a person: postgres
novox: waits for a person: postgres
postgres waits for a person: its policy records (module)
postgres provides postgres-database: its consumers are sent again after it
postgres keeps data (ADR 0233): what it holds is backed up before it moves
Run by the build seat on g14 as build-1791488629803571254 (builds --log build-1791488629803571254).
--- the gate: postgres
postgres: ok, keeps store (valuable), dumps (rebuildable), ready: server by tcp on database every 30s
long-running resources without health: 0
1 manifest(s) checked. Judged against the seats this binary carries; a claim on one of the mesh's own seats is judged fully at registration, and a seat declared by a module not given here reads as unknown. Identities judged on a 6-character machine name, against the bounds of the providers given here
judging 0 module(s) changed against 4 machine(s), as the snapshot of 2026-10-08T19:43:11Z says they run
composing every machine as the mesh is
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
composing every machine with the change
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
merge gate: PASS — every machine composes with the change as it did without (4 of 4 compose)
judged by controller development build against the facts of 2026-10-08T19:43:11Z
machines:
- a machine (f35): composes
- holds mesh-dns-resolver (idt): composes
- holds git, holds mesh-artifact-store, holds mesh-broker, holds mesh-catalog, holds mesh-delivery, holds mesh-dns-resolver, holds mesh-store, holds mesh-vault, holds npm-package-registry, the control node, the hub (ygcyr): composes
- a machine (yvbpsm): composes
a merge would rebuild 1 module(s) in 1 tier(s): postgres
--- its merge-check.sh (go toolchain)
ok git.novox.be/novox/mesh-catalog/checks/words 0.002s
words: 12 retired words, none in the 32619 strings of 585 files
ok git.novox.be/novox/mesh-catalog/checks/paths 0.005s
paths: no module names another's state on its machine (3 known, listed to go)
long-running resources without health: 70, as kept
testing modules/postgres
ok postgres/cmd/postgres-provider 1.290s
**Merge check** at `4f44bf1e`
- `mesh/merge-gate`: **PASS** — every machine composes with the change as it did without (4 of 4 compose) (modules: postgres)
- `mesh/repo-check`: **PASS** — its merge-check.sh passed
**Delivery plan** — builds postgres, sent nowhere; no bus step; 2 wait(s) for a person
- tier 0: postgres
- ace: waits for a person: postgres
- novox: waits for a person: postgres
- postgres waits for a person: its policy records (module)
- postgres provides postgres-database: its consumers are sent again after it
- postgres keeps data (ADR 0233): what it holds is backed up before it moves
Run by the build seat on g14 as `build-1791488629803571254` (`builds --log build-1791488629803571254`).
```
--- the gate: postgres
postgres: ok, keeps store (valuable), dumps (rebuildable), ready: server by tcp on database every 30s
long-running resources without health: 0
1 manifest(s) checked. Judged against the seats this binary carries; a claim on one of the mesh's own seats is judged fully at registration, and a seat declared by a module not given here reads as unknown. Identities judged on a 6-character machine name, against the bounds of the providers given here
judging 0 module(s) changed against 4 machine(s), as the snapshot of 2026-10-08T19:43:11Z says they run
composing every machine as the mesh is
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
composing every machine with the change
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
merge gate: PASS — every machine composes with the change as it did without (4 of 4 compose)
judged by controller development build against the facts of 2026-10-08T19:43:11Z
machines:
- a machine (f35): composes
- holds mesh-dns-resolver (idt): composes
- holds git, holds mesh-artifact-store, holds mesh-broker, holds mesh-catalog, holds mesh-delivery, holds mesh-dns-resolver, holds mesh-store, holds mesh-vault, holds npm-package-registry, the control node, the hub (ygcyr): composes
- a machine (yvbpsm): composes
a merge would rebuild 1 module(s) in 1 tier(s): postgres
--- its merge-check.sh (go toolchain)
ok git.novox.be/novox/mesh-catalog/checks/words 0.002s
words: 12 retired words, none in the 32619 strings of 585 files
ok git.novox.be/novox/mesh-catalog/checks/paths 0.005s
paths: no module names another's state on its machine (3 known, listed to go)
long-running resources without health: 70, as kept
testing modules/postgres
ok postgres/cmd/postgres-provider 1.290s
```
mesh/merge-gate: PASS — every machine composes with the change as it did without (4 of 4 compose) (modules: postgres)
mesh/repo-check: PASS — its merge-check.sh passed
Delivery plan — builds postgres, sent nowhere; no bus step; 2 wait(s) for a person
tier 0: postgres
ace: waits for a person: postgres
novox: waits for a person: postgres
postgres waits for a person: its policy records (module)
postgres provides postgres-database: its consumers are sent again after it
postgres keeps data (ADR 0233): what it holds is backed up before it moves
Run by the build seat on novox as build-1791488690293758040 (builds --log build-1791488690293758040).
--- the gate: postgres
postgres: ok, keeps store (valuable), dumps (rebuildable), ready: server by tcp on database every 30s
long-running resources without health: 0
1 manifest(s) checked. Judged against the seats this binary carries; a claim on one of the mesh's own seats is judged fully at registration, and a seat declared by a module not given here reads as unknown. Identities judged on a 6-character machine name, against the bounds of the providers given here
judging 0 module(s) changed against 4 machine(s), as the snapshot of 2026-10-08T19:43:11Z says they run
composing every machine as the mesh is
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
composing every machine with the change
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
merge gate: PASS — every machine composes with the change as it did without (4 of 4 compose)
judged by controller development build against the facts of 2026-10-08T19:43:11Z
machines:
- a machine (f35): composes
- holds mesh-dns-resolver (idt): composes
- holds git, holds mesh-artifact-store, holds mesh-broker, holds mesh-catalog, holds mesh-delivery, holds mesh-dns-resolver, holds mesh-store, holds mesh-vault, holds npm-package-registry, the control node, the hub (ygcyr): composes
- a machine (yvbpsm): composes
a merge would rebuild 1 module(s) in 1 tier(s): postgres
--- its merge-check.sh (go toolchain)
ok git.novox.be/novox/mesh-catalog/checks/words 0.006s
words: 12 retired words, none in the 32619 strings of 585 files
ok git.novox.be/novox/mesh-catalog/checks/paths 0.006s
paths: no module names another's state on its machine (3 known, listed to go)
long-running resources without health: 70, as kept
testing modules/postgres
ok postgres/cmd/postgres-provider 1.466s
**Merge check** at `fb07d552`
- `mesh/merge-gate`: **PASS** — every machine composes with the change as it did without (4 of 4 compose) (modules: postgres)
- `mesh/repo-check`: **PASS** — its merge-check.sh passed
**Delivery plan** — builds postgres, sent nowhere; no bus step; 2 wait(s) for a person
- tier 0: postgres
- ace: waits for a person: postgres
- novox: waits for a person: postgres
- postgres waits for a person: its policy records (module)
- postgres provides postgres-database: its consumers are sent again after it
- postgres keeps data (ADR 0233): what it holds is backed up before it moves
Run by the build seat on novox as `build-1791488690293758040` (`builds --log build-1791488690293758040`).
```
--- the gate: postgres
postgres: ok, keeps store (valuable), dumps (rebuildable), ready: server by tcp on database every 30s
long-running resources without health: 0
1 manifest(s) checked. Judged against the seats this binary carries; a claim on one of the mesh's own seats is judged fully at registration, and a seat declared by a module not given here reads as unknown. Identities judged on a 6-character machine name, against the bounds of the providers given here
judging 0 module(s) changed against 4 machine(s), as the snapshot of 2026-10-08T19:43:11Z says they run
composing every machine as the mesh is
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
composing every machine with the change
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
merge gate: PASS — every machine composes with the change as it did without (4 of 4 compose)
judged by controller development build against the facts of 2026-10-08T19:43:11Z
machines:
- a machine (f35): composes
- holds mesh-dns-resolver (idt): composes
- holds git, holds mesh-artifact-store, holds mesh-broker, holds mesh-catalog, holds mesh-delivery, holds mesh-dns-resolver, holds mesh-store, holds mesh-vault, holds npm-package-registry, the control node, the hub (ygcyr): composes
- a machine (yvbpsm): composes
a merge would rebuild 1 module(s) in 1 tier(s): postgres
--- its merge-check.sh (go toolchain)
ok git.novox.be/novox/mesh-catalog/checks/words 0.006s
words: 12 retired words, none in the 32619 strings of 585 files
ok git.novox.be/novox/mesh-catalog/checks/paths 0.006s
paths: no module names another's state on its machine (3 known, listed to go)
long-running resources without health: 70, as kept
testing modules/postgres
ok postgres/cmd/postgres-provider 1.466s
```
mesh/merge-gate pass: builds postgres, sent nowhere; no bus step; 2 wait(s) for a person; every machine composes with the change as it did without (4 of 4 …
A superuser's REVOKE acts as the table owner, so a grant to the reader made
by a role holding GRANT OPTION survived it while the report said revoked.
Each grant is now revoked under SET ROLE to its grantor, the grants are read
back, and anything still held is reported as a failure. The role was
altered every minute with its password in the statement, which a server
logging DDL would record and which could collide with a first query; it is
now altered only when an attribute, a setting or the password has drifted,
one ensure at a time. SECURITY DEFINER functions and views the reader may
use reach data without a grant on it, so every pass lists them. The README
says the name filter is a backstop and that the read-only session default
can be turned off by a statement.
mesh/merge-gate: PASS — every machine composes with the change as it did without (4 of 4 compose) (modules: postgres)
mesh/repo-check: PASS — its merge-check.sh passed
Delivery plan — builds postgres, sent nowhere; no bus step; 2 wait(s) for a person
tier 0: postgres
ace: waits for a person: postgres
novox: waits for a person: postgres
postgres waits for a person: its policy records (module)
postgres provides postgres-database: its consumers are sent again after it
postgres keeps data (ADR 0233): what it holds is backed up before it moves
Run by the build seat on shanks as build-1791489726678529021 (builds --log build-1791489726678529021).
--- the gate: postgres
postgres: ok, keeps store (valuable), dumps (rebuildable), ready: server by tcp on database every 30s
long-running resources without health: 0
1 manifest(s) checked. Judged against the seats this binary carries; a claim on one of the mesh's own seats is judged fully at registration, and a seat declared by a module not given here reads as unknown. Identities judged on a 6-character machine name, against the bounds of the providers given here
judging 0 module(s) changed against 4 machine(s), as the snapshot of 2026-10-08T19:59:18Z says they run
composing every machine as the mesh is
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
composing every machine with the change
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
merge gate: PASS — every machine composes with the change as it did without (4 of 4 compose)
judged by controller development build against the facts of 2026-10-08T19:59:18Z
machines:
- a machine (f35): composes
- holds mesh-dns-resolver (idt): composes
- holds git, holds mesh-artifact-store, holds mesh-broker, holds mesh-catalog, holds mesh-delivery, holds mesh-dns-resolver, holds mesh-store, holds mesh-vault, holds npm-package-registry, the control node, the hub (ygcyr): composes
- a machine (yvbpsm): composes
a merge would rebuild 1 module(s) in 1 tier(s): postgres
--- its merge-check.sh (go toolchain)
ok git.novox.be/novox/mesh-catalog/checks/words 0.002s
words: 12 retired words, none in the 32652 strings of 585 files
ok git.novox.be/novox/mesh-catalog/checks/paths 0.006s
paths: no module names another's state on its machine (3 known, listed to go)
long-running resources without health: 70, as kept
testing modules/postgres
ok postgres/cmd/postgres-provider 1.375s
**Merge check** at `8bf50f75`
- `mesh/merge-gate`: **PASS** — every machine composes with the change as it did without (4 of 4 compose) (modules: postgres)
- `mesh/repo-check`: **PASS** — its merge-check.sh passed
**Delivery plan** — builds postgres, sent nowhere; no bus step; 2 wait(s) for a person
- tier 0: postgres
- ace: waits for a person: postgres
- novox: waits for a person: postgres
- postgres waits for a person: its policy records (module)
- postgres provides postgres-database: its consumers are sent again after it
- postgres keeps data (ADR 0233): what it holds is backed up before it moves
Run by the build seat on shanks as `build-1791489726678529021` (`builds --log build-1791489726678529021`).
```
--- the gate: postgres
postgres: ok, keeps store (valuable), dumps (rebuildable), ready: server by tcp on database every 30s
long-running resources without health: 0
1 manifest(s) checked. Judged against the seats this binary carries; a claim on one of the mesh's own seats is judged fully at registration, and a seat declared by a module not given here reads as unknown. Identities judged on a 6-character machine name, against the bounds of the providers given here
judging 0 module(s) changed against 4 machine(s), as the snapshot of 2026-10-08T19:59:18Z says they run
composing every machine as the mesh is
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
composing every machine with the change
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
merge gate: PASS — every machine composes with the change as it did without (4 of 4 compose)
judged by controller development build against the facts of 2026-10-08T19:59:18Z
machines:
- a machine (f35): composes
- holds mesh-dns-resolver (idt): composes
- holds git, holds mesh-artifact-store, holds mesh-broker, holds mesh-catalog, holds mesh-delivery, holds mesh-dns-resolver, holds mesh-store, holds mesh-vault, holds npm-package-registry, the control node, the hub (ygcyr): composes
- a machine (yvbpsm): composes
a merge would rebuild 1 module(s) in 1 tier(s): postgres
--- its merge-check.sh (go toolchain)
ok git.novox.be/novox/mesh-catalog/checks/words 0.002s
words: 12 retired words, none in the 32652 strings of 585 files
ok git.novox.be/novox/mesh-catalog/checks/paths 0.006s
paths: no module names another's state on its machine (3 known, listed to go)
long-running resources without health: 70, as kept
testing modules/postgres
ok postgres/cmd/postgres-provider 1.375s
```
mesh/merge-gate pass: builds postgres, sent nowhere; no bus step; 2 wait(s) for a person; every machine composes with the change as it did without (4 of 4 …
A reader given a privilege WITH GRANT OPTION that passed it on made the
plain revoke fail with dependent privileges exist; the pass still listed the
table as revoked and logged it every minute. The pass now retries such a
change with CASCADE, which reaches only grants that came from the reader,
reads the grants back, reports as revoked only what is gone, and says what
is still held and why when anything is.
mesh/merge-gate: PASS — every machine composes with the change as it did without (4 of 4 compose) (modules: postgres)
mesh/repo-check: PASS — its merge-check.sh passed
Delivery plan — builds postgres, sent nowhere; no bus step; 2 wait(s) for a person
tier 0: postgres
ace: waits for a person: postgres
novox: waits for a person: postgres
postgres waits for a person: its policy records (module)
postgres provides postgres-database: its consumers are sent again after it
postgres keeps data (ADR 0233): what it holds is backed up before it moves
Run by the build seat on novox as build-1791490270411226578 (builds --log build-1791490270411226578).
--- the gate: postgres
postgres: ok, keeps store (valuable), dumps (rebuildable), ready: server by tcp on database every 30s
long-running resources without health: 0
1 manifest(s) checked. Judged against the seats this binary carries; a claim on one of the mesh's own seats is judged fully at registration, and a seat declared by a module not given here reads as unknown. Identities judged on a 6-character machine name, against the bounds of the providers given here
judging 0 module(s) changed against 4 machine(s), as the snapshot of 2026-10-08T20:09:18Z says they run
composing every machine as the mesh is
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
composing every machine with the change
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
merge gate: PASS — every machine composes with the change as it did without (4 of 4 compose)
judged by controller development build against the facts of 2026-10-08T20:09:18Z
machines:
- a machine (f35): composes
- holds mesh-dns-resolver (idt): composes
- holds git, holds mesh-artifact-store, holds mesh-broker, holds mesh-catalog, holds mesh-delivery, holds mesh-dns-resolver, holds mesh-store, holds mesh-vault, holds npm-package-registry, the control node, the hub (ygcyr): composes
- a machine (yvbpsm): composes
a merge would rebuild 1 module(s) in 1 tier(s): postgres
--- its merge-check.sh (go toolchain)
ok git.novox.be/novox/mesh-catalog/checks/words 0.002s
words: 12 retired words, none in the 32662 strings of 585 files
ok git.novox.be/novox/mesh-catalog/checks/paths 0.007s
paths: no module names another's state on its machine (3 known, listed to go)
long-running resources without health: 70, as kept
testing modules/postgres
ok postgres/cmd/postgres-provider 1.409s
**Merge check** at `e0e97a96`
- `mesh/merge-gate`: **PASS** — every machine composes with the change as it did without (4 of 4 compose) (modules: postgres)
- `mesh/repo-check`: **PASS** — its merge-check.sh passed
**Delivery plan** — builds postgres, sent nowhere; no bus step; 2 wait(s) for a person
- tier 0: postgres
- ace: waits for a person: postgres
- novox: waits for a person: postgres
- postgres waits for a person: its policy records (module)
- postgres provides postgres-database: its consumers are sent again after it
- postgres keeps data (ADR 0233): what it holds is backed up before it moves
Run by the build seat on novox as `build-1791490270411226578` (`builds --log build-1791490270411226578`).
```
--- the gate: postgres
postgres: ok, keeps store (valuable), dumps (rebuildable), ready: server by tcp on database every 30s
long-running resources without health: 0
1 manifest(s) checked. Judged against the seats this binary carries; a claim on one of the mesh's own seats is judged fully at registration, and a seat declared by a module not given here reads as unknown. Identities judged on a 6-character machine name, against the bounds of the providers given here
judging 0 module(s) changed against 4 machine(s), as the snapshot of 2026-10-08T20:09:18Z says they run
composing every machine as the mesh is
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
composing every machine with the change
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
merge gate: PASS — every machine composes with the change as it did without (4 of 4 compose)
judged by controller development build against the facts of 2026-10-08T20:09:18Z
machines:
- a machine (f35): composes
- holds mesh-dns-resolver (idt): composes
- holds git, holds mesh-artifact-store, holds mesh-broker, holds mesh-catalog, holds mesh-delivery, holds mesh-dns-resolver, holds mesh-store, holds mesh-vault, holds npm-package-registry, the control node, the hub (ygcyr): composes
- a machine (yvbpsm): composes
a merge would rebuild 1 module(s) in 1 tier(s): postgres
--- its merge-check.sh (go toolchain)
ok git.novox.be/novox/mesh-catalog/checks/words 0.002s
words: 12 retired words, none in the 32662 strings of 585 files
ok git.novox.be/novox/mesh-catalog/checks/paths 0.007s
paths: no module names another's state on its machine (3 known, listed to go)
long-running resources without health: 70, as kept
testing modules/postgres
ok postgres/cmd/postgres-provider 1.409s
```
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Security fix, critical.
mesh_store_reader— the loginmesh-store.queryandpostgres_queryrun as — was grantedpg_read_all_data, so any caller of the store seat'squeryverb could read every column of every database: the controller's Ed25519 signing key (identity.signing_key.private, which signs every declaration a node-engine applies as root), the inventory's sealed secrets and operator keys, the bus users' password hashes, and every consumer's own secrets (gitea, keycloak, nextcloud…). Confirmed live on 2026-10-08 by privileges only (has_column_privilege,pg_has_role); no value was selected.What changes
ReaderStatementsno longer grantspg_read_all_data; every pass revokes every role the reader is a member of — so an existing install losespg_read_all_dataat the first pass after this build starts, and at the first query even before that."contributes": {"postgres-database": {"readable": ["agents", "steps(id, created_at)"]}}. A list, so the controller's reading ofcontributescannot take it for the several-instance shape; contribution values are passed through as they are, so the controller's module check needs nothing new. A database nobody declares — the controller'sidentity,inventoryandlicencesamong them — gives the reader nothing.RunReader): table-level grants revoked, undeclared column grants revoked, declared ones granted, schema usage granted where needed; one implicit transaction per database.postgres_reader_grants(r) answers the last pass: readable, refused, missing, revoked, exposed — names only.Tests (each fails on main)
TestTheReaderIsMadeAsTheAdminWithEveryAttributeOncenow requires noGRANT pg_read_all_dataand aREVOKE.TestLiveAnInstallsReaderReadsNoSecret(real server,MESH_POSTGRES_LIVE), using only what the module had before: an install's reader as found (pg_read_all_data plus a hand grant) — on main it reads the fixture signing key; here it is refused and loses the role.TestLiveTheReaderReadsWhatIsDeclaredAndNoSecret: across every database, no secret-named column readable after the reconcile; declared columns readable;SELECT *on a table with a refused column fails; a second pass changes nothing; a narrowed declaration takes back; aGRANT … TO PUBLICis found as exposed and refuses queries.readableparses.Run here against a throwaway PostgreSQL 18; the live store runs 17.
After merge
The module's upgrade policy is
record: a person takes the build. Until it is taken, the hole stays open on the live store. After it,mesh-store.queryreads nothing in any database until modules declarereadable(none does yet) — diagnosis that read the controller's inventory through it loses that until the controller declares what of its own is readable (a follow-up; the controller is no consumer of the provision, so it has no contribution to declare it in).hq issue 337 holds the report and the diagnosis, including the recommendation on the signing key.
Delivery
novox/mesh-catalog@e0e97a96c401— delivered since 2026-10-08T20:17:14ZDelivery plan — builds postgres, sent nowhere; no bus step; 2 wait(s) for a person
Transitions
The commit's note under
refs/notes/mesh-plankeeps every transition:git log --notes=mesh-plan.Merge check at
4f44bf1emesh/merge-gate: PASS — every machine composes with the change as it did without (4 of 4 compose) (modules: postgres)mesh/repo-check: PASS — its merge-check.sh passedDelivery plan — builds postgres, sent nowhere; no bus step; 2 wait(s) for a person
Run by the build seat on g14 as
build-1791488629803571254(builds --log build-1791488629803571254).Merge check at
fb07d552mesh/merge-gate: PASS — every machine composes with the change as it did without (4 of 4 compose) (modules: postgres)mesh/repo-check: PASS — its merge-check.sh passedDelivery plan — builds postgres, sent nowhere; no bus step; 2 wait(s) for a person
Run by the build seat on novox as
build-1791488690293758040(builds --log build-1791488690293758040).a643e28083to8bf50f7555Merge check at
8bf50f75mesh/merge-gate: PASS — every machine composes with the change as it did without (4 of 4 compose) (modules: postgres)mesh/repo-check: PASS — its merge-check.sh passedDelivery plan — builds postgres, sent nowhere; no bus step; 2 wait(s) for a person
Run by the build seat on shanks as
build-1791489726678529021(builds --log build-1791489726678529021).Merge check at
e0e97a96mesh/merge-gate: PASS — every machine composes with the change as it did without (4 of 4 compose) (modules: postgres)mesh/repo-check: PASS — its merge-check.sh passedDelivery plan — builds postgres, sent nowhere; no bus step; 2 wait(s) for a person
Run by the build seat on novox as
build-1791490270411226578(builds --log build-1791490270411226578).