go/outward: the content rule in one place, for the operator channel and node-reads (hq ADR 0310 §4, issue 418) #23

Merged
mesh-admin merged 2 commits from fix/418-node-reads into main 2026-10-12 13:20:23 +00:00
Contributor

What. A new Go package go/outward: the content rule the operator channel applies (messenger's content.go), with the secret class split out as mesh-catalog #178 (issue 383) splits it:

  • Check(text): what may leave on a channel (roles and words; no address, path or secret's shape).
  • CheckSecret(text): a secret's shape alone; a path is judged piece by piece between its slashes.
  • Refusal{Class, What}: never the text itself.

Why. ADR 0310 §4: the operator channel and every node-reads answer apply one rule, kept in the SDK (ADR 0039 §1). mesh-reads (mesh-catalog, same branch name) uses CheckSecret on every line it answers.

Not in this group. The messenger still has its own copy. Switching it to this package changes modules/messenger/cmd/messenger/content.go, which mesh-catalog #178 (issue 383) also changes. I left it out so as not to cut into that work. It is a follow-up once #178 has merged, and I am asking for an issue for it.

Tests. outward_test.go carries messenger's cases unchanged (roles and words pass; every address, path and secret class is refused as before), plus TestCheckSecretRefusesASecretsShapeAndNothingElse. sh go/merge-check.sh passes (gofmt, vet, every Go package's tests).

Order. Merge this first. mesh-catalog's mesh-reads pins it as v0.1.14-0.20261012115257-4dc5f29d3e23.

Part of issue 418 (with mesh-controller and mesh-catalog on fix/418-node-reads). Not merged.

**What.** A new Go package `go/outward`: the content rule the operator channel applies (messenger's `content.go`), with the secret class split out as mesh-catalog #178 (issue 383) splits it: - `Check(text)`: what may leave on a channel (roles and words; no address, path or secret's shape). - `CheckSecret(text)`: a secret's shape alone; a path is judged piece by piece between its slashes. - `Refusal{Class, What}`: never the text itself. **Why.** ADR 0310 §4: the operator channel and every `node-reads` answer apply one rule, kept in the SDK (ADR 0039 §1). `mesh-reads` (mesh-catalog, same branch name) uses `CheckSecret` on every line it answers. **Not in this group.** The messenger still has its own copy. Switching it to this package changes `modules/messenger/cmd/messenger/content.go`, which mesh-catalog #178 (issue 383) also changes. I left it out so as not to cut into that work. It is a follow-up once #178 has merged, and I am asking for an issue for it. **Tests.** `outward_test.go` carries messenger's cases unchanged (roles and words pass; every address, path and secret class is refused as before), plus `TestCheckSecretRefusesASecretsShapeAndNothingElse`. `sh go/merge-check.sh` passes (gofmt, vet, every Go package's tests). **Order.** Merge this first. mesh-catalog's `mesh-reads` pins it as `v0.1.14-0.20261012115257-4dc5f29d3e23`. Part of issue 418 (with mesh-controller and mesh-catalog on `fix/418-node-reads`). Not merged.
mesh-admin added 1 commit 2026-10-12 12:04:56 +00:00
go/outward: the content rule in one place, so the operator channel and node-reads apply the same one (hq ADR 0310 §4, issue 418)
mesh/delivery-group group fix/418-node-reads rejected: a member's own check failed
mesh/merge-gate pass: builds mesh-sdk, sent nowhere; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (typescript), go/merge-check.sh (go)
mesh/delivery superseded: a newer head of the same pull request
4dc5f29d3e
The operator channel's rule (messenger's content.go, with the secret class
split out as in issue 383's branch) moves here as package outward: Check for
what may leave on a channel, CheckSecret for a secret's shape alone, which is
what a node-reads answer is held to. The messenger switches to it once
mesh-catalog #178, which changes that file, has merged.
Author
Contributor

Delivery novox/mesh-sdk@67bfa22132f2 — delivered since 2026-10-12T13:29:20Z

Delivery plan — builds mesh-sdk, sent nowhere; no bus step

  • tier 0: mesh-sdk

Group fix/418-node-reads, in order: novox/mesh-controller@63a99d4323 → novox/mesh-sdk@67bfa22132

Machines

  • mesh-issues on novox: passed — healthy 1 times over 21s
  • mesh-sdk on every machine running it: sent
  • mesh-issues on the rest running it: sent

Transitions

  • 2026-10-12 12:27 (new) → proposed (announced): novox/mesh-sdk#23's head announced
  • 2026-10-12 12:30 proposed → checked (checked): the verdict names this commit
  • 2026-10-12 12:30 checked → ready (accepted): the gate passed or warned, and the repository's own check did not fail
  • 2026-10-12 13:27 ready → published (merged): on the trunk its modules follow: merged there, and its walk opened — or nothing for a walk to move
  • 2026-10-12 13:27 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 — not a failed walk that carried a later commit for it, after which it waits for a walk of its own commit
  • 2026-10-12 13:29 delivering → delivered (done): its walk is done, carrying its own commit: 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-sdk@67bfa22132f2` — **delivered** since 2026-10-12T13:29:20Z **Delivery plan** — builds mesh-sdk, sent nowhere; no bus step - tier 0: mesh-sdk **Group** `fix/418-node-reads`, in order: novox/mesh-controller@63a99d43237f → novox/mesh-sdk@67bfa22132f2 - novox/mesh-controller@63a99d43237f before novox/mesh-sdk@67bfa22132f2: built by - composed together: pass — every machine composes with the change as it did without (4 of 4 compose) **Machines** - mesh-issues on novox: passed — healthy 1 times over 21s - mesh-sdk on every machine running it: sent - mesh-issues on the rest running it: sent **Transitions** - 2026-10-12 12:27 (new) → proposed (announced): novox/mesh-sdk#23's head announced - 2026-10-12 12:30 proposed → checked (checked): the verdict names this commit - 2026-10-12 12:30 checked → ready (accepted): the gate passed or warned, and the repository's own check did not fail - 2026-10-12 13:27 ready → published (merged): on the trunk its modules follow: merged there, and its walk opened — or nothing for a walk to move - 2026-10-12 13:27 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 — not a failed walk that carried a later commit for it, after which it waits for a walk of its own commit - 2026-10-12 13:29 delivering → delivered (done): its walk is done, carrying its own commit: 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`.
Author
Contributor

Merge check at 4dc5f29d

  • mesh/merge-gate: PASS — every machine composes with the change as it did without (4 of 4 compose) (modules: mesh-sdk)
  • mesh/repo-check: PASS — its merge-check.sh passed, each part in its toolchain: merge-check.sh (typescript), go/merge-check.sh (go)

Delivery plan — builds mesh-sdk, sent nowhere; no bus step

  • tier 0: mesh-sdk

Run by the build seat on ace as build-1791806709621577837 (builds --log build-1791806709621577837).

  duration_ms: 64.89474
  type: 'test'
  ...
# Subtest: delete refuses an active consumer and deletes only the named retired one
ok 12 - delete refuses an active consumer and deletes only the named retired one
  ---
  duration_ms: 58.886762
  type: 'test'
  ...
# Subtest: an addition acts on the first pass
ok 13 - an addition acts on the first pass
  ---
  duration_ms: 11.936628
  type: 'test'
  ...
# Subtest: what the backend holds at start is retired by the same rules
ok 14 - what the backend holds at start is retired by the same rules
  ---
  duration_ms: 46.0531
  type: 'test'
  ...
# Subtest: the four tools are registered for the module and answer
ok 15 - the four tools are registered for the module and answer
  ---
  duration_ms: 0.862041
  type: 'test'
  ...
# [tools] broken: contributor failed, exposing none: Error: no token
# Subtest: semver orders releases
ok 16 - semver orders releases
  ---
  duration_ms: 0.885165
  type: 'test'
  ...
# Subtest: module tools register and collect, a thrower is skipped not fatal
ok 17 - module tools register and collect, a thrower is skipped not fatal
  ---
  duration_ms: 0.589719
  type: 'test'
  ...
# [provisioner:analytics] webapp-anchor: no longer in /tmp/prov-0GMZtV/analytics.json; RETIRED, MARK ONLY — this provider cannot disable a consumer (its adapter has no retire), so webapp-anchor KEEPS ITS ACCESS and its data until a person deletes it with `cleanup delete`. Nothing was changed in the backend (novox/hq ADR 0230): the mesh stopped asking for it: the same answer in 5 passes over 0s, from 2026-10-12T12:08:19.759Z
# [provisioner:analytics] webapp-anchor: DELETING, asked by someone: gone for good
# Subtest: provisioner creates each consumer with the mesh's login and password; remove waits for a person's delete
ok 18 - provisioner creates each consumer with the mesh's login and password; remove waits for a person's delete
  ---
  duration_ms: 216.896339
  type: 'test'
  ...
# [provisioner:s3-bucket] mesh_anchor_ncloud: no longer in /tmp/prov-derived-LxT7J8/store.json; RETIRED, MARK ONLY — this provider cannot disable a consumer (its adapter has no retire), so mesh_anchor_ncloud KEEPS ITS ACCESS and its data until a person deletes it with `cleanup delete`. Nothing was changed in the backend (novox/hq ADR 0230): the mesh stopped asking for it: the same answer in 5 passes over 0s, from 2026-10-12T12:08:19.993Z
# [provisioner:s3-bucket] mesh_anchor_ncloud: DELETING, asked by someone: unassigned
# Subtest: provisioner hands the adapter what the mesh derived, and still has it at withdrawal
ok 19 - provisioner hands the adapter what the mesh derived, and still has it at withdrawal
  ---
  duration_ms: 134.075528
  type: 'test'
  ...
# Subtest: provisioner stops calling an unreadable secret a race, and says what to look at
ok 20 - provisioner stops calling an unreadable secret a race, and says what to look at
  ---
  duration_ms: 72.008027
  type: 'test'
  ...
# [provisioner:postgres-database] contributions file unreadable (/tmp/prov-WegL2H/postgres-database.json): ENOENT: no such file or directory, open '/tmp/prov-WegL2H/postgres-database.json'; nothing applied or removed until it can be read
# [provisioner:postgres-database] contributions file readable again
# [provisioner:postgres-database] contributions file is not JSON (/tmp/prov-WegL2H/postgres-database.json): SyntaxError: Expected property name or '}' in JSON at position 2 (line 1 column 3); nothing applied or removed until it can be read
# [provisioner:postgres-database] contributions file readable again
# [provisioner:postgres-database] /tmp/prov-WegL2H/postgres-database.json is for s3-bucket, not postgres-database; nothing applied or removed until it can be read
# [provisioner:postgres-database] contributions file readable again
# [provisioner:postgres-database] /tmp/prov-WegL2H/postgres-database.json has no given list; nothing applied or removed until it can be read
# [provisioner:postgres-database] contributions file is not JSON (/tmp/prov-WegL2H/postgres-database.json): SyntaxError: Unexpected end of JSON input; nothing applied or removed until it can be read
# [provisioner:postgres-database] contributions file readable again
# [provisioner:postgres-database] RETIREMENT WAITS FOR A PERSON: the mesh no longer asks for 2 of the 2 consumer(s) this provider holds (mesh_anchor_forge, mesh_anchor_mail), more than 3, or more than 50% of the 2 held. Nothing is retired until `retire approve` or `retire reject` (novox/hq ADR 0230)
# [provisioner:postgres-database] retirement of mesh_anchor_forge, mesh_anchor_mail approved by test: both unassigned on purpose
# [provisioner:postgres-database] mesh_anchor_forge: no longer in /tmp/prov-WegL2H/postgres-database.json; RETIRED, MARK ONLY — this provider cannot disable a consumer (its adapter has no retire), so mesh_anchor_forge KEEPS ITS ACCESS and its data until a person deletes it with `cleanup delete`. Nothing was changed in the backend (novox/hq ADR 0230): approved by test: both unassigned on purpose
# [provisioner:postgres-database] mesh_anchor_mail: no longer in /tmp/prov-WegL2H/postgres-database.json; RETIRED, MARK ONLY — this provider cannot disable a consumer (its adapter has no retire), so mesh_anchor_mail KEEPS ITS ACCESS and its data until a person deletes it with `cleanup delete`. Nothing was changed in the backend (novox/hq ADR 0230): approved by test: both unassigned on purpose
# Subtest: provisioner removes nothing while its contributions file cannot be used (novox/hq issue 241)
ok 21 - provisioner removes nothing while its contributions file cannot be used (novox/hq issue 241)
  ---
  duration_ms: 1306.684282
  type: 'test'
  ...
# [provisioner:cache] webapp-anchor: the backend no longer holds it; applying again
# Subtest: provisioner applies again what the backend no longer holds, and trusts memory without holds
ok 22 - provisioner applies again what the backend no longer holds, and trusts memory without holds
  ---
  duration_ms: 212.539567
  type: 'test'
  ...
# [provisioner:cache] webapp-anchor: could not check the backend, will ask again: Error: connection refused
# [provisioner:cache] webapp-anchor: could not check the backend, will ask again: Error: connection refused
# [provisioner:cache] webapp-anchor: could not check the backend, will ask again: Error: connection refused
# Subtest: provisioner keeps a consumer applied when the backend cannot be asked
ok 23 - provisioner keeps a consumer applied when the backend cannot be asked
  ---
  duration_ms: 71.4734
  type: 'test'
  ...
# Subtest: provisioner backs off when create does not satisfy holds, and says so
ok 24 - provisioner backs off when create does not satisfy holds, and says so
  ---
  duration_ms: 402.256375
  type: 'test'
  ...
# Subtest: provisioner treats a failed or hung holds as could-not-ask, and never logs the password
ok 25 - provisioner treats a failed or hung holds as could-not-ask, and never logs the password
  ---
  duration_ms: 403.027537
  type: 'test'
  ...
# Subtest: provisioner stops a verify pass at a timeout, and retries a failed re-apply at the next check
ok 26 - provisioner stops a verify pass at a timeout, and retries a failed re-apply at the next check
  ---
  duration_ms: 302.029577
  type: 'test'
  ...
# Subtest: modules can SERVE: a real async tool, loaded and invoked over the broker
ok 27 - modules can SERVE: a real async tool, loaded and invoked over the broker
  ---
  duration_ms: 26.554996
  type: 'test'
  ...
# Subtest: serving refuses one module exposing two tools of the same name
ok 28 - serving refuses one module exposing two tools of the same name
  ---
  duration_ms: 0.252024
  type: 'test'
  ...
# Subtest: events: a module emits, a listener and the audit sink (\#) both receive it, with metadata
ok 29 - events: a module emits, a listener and the audit sink (\#) both receive it, with metadata
  ---
  duration_ms: 0.567147
  type: 'test'
  ...
# Subtest: each registration is given the environment asked for it, and sees no other's (ADR 0192)
ok 30 - each registration is given the environment asked for it, and sees no other's (ADR 0192)
  ---
  duration_ms: 0.193815
  type: 'test'
  ...
# Subtest: a bundle on stdio lists its tools and answers a call, a seat's verb among them
ok 31 - a bundle on stdio lists its tools and answers a call, a seat's verb among them
  ---
  duration_ms: 124.336998
  type: 'test'
  ...
# Subtest: a bundle served as its module lists the seat's verbs as the seat's, whichever registered first (ADR 0193)
ok 32 - a bundle served as its module lists the seat's verbs as the seat's, whichever registered first (ADR 0193)
  ---
  duration_ms: 82.953138
  type: 'test'
  ...
# Subtest: a launched tool's emit is asked of the runtime, and the call answers once the runtime has published (ADR 0193)
ok 33 - a launched tool's emit is asked of the runtime, and the call answers once the runtime has published (ADR 0193)
  ---
  duration_ms: 84.670397
  type: 'test'
  ...
# Subtest: a launched bundle subscribes as it is imported, handles events the runtime hands it, and asks through it (ADR 0198)
ok 34 - a launched bundle subscribes as it is imported, handles events the runtime hands it, and asks through it (ADR 0198)
  ---
  duration_ms: 3198.443269
  type: 'test'
  ...
# Subtest: a subscribe the runtime refused leaves no handler behind, so one event is handled once (novox/hq issue 276)
ok 35 - a subscribe the runtime refused leaves no handler behind, so one event is handled once (novox/hq issue 276)
  ---
  duration_ms: 83.059367
  type: 'test'
  ...
# Subtest: a launched bundle watches its state as it is imported, has the current values before it goes on, and keeps state through the runtime (ADR 0201)
ok 36 - a launched bundle watches its state as it is imported, has the current values before it goes on, and keeps state through the runtime (ADR 0201)
  ---
  duration_ms: 283.915073
  type: 'test'
  ...
# Subtest: topics match as the runtime's do: * one segment, \# and ** any number
ok 37 - topics match as the runtime's do: * one segment, \# and ** any number
  ---
  duration_ms: 2.346133
  type: 'test'
  ...
1..37
# tests 37
# suites 0
# pass 37
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 3934.417378
NOT TESTED HERE: go/ (the Go binding) — the Go toolchain runs it from go/merge-check.sh
NOT TESTED HERE: python/, rust/, c/ — no toolchain of theirs runs a merge check
--- its go/merge-check.sh (go toolchain)
ok  	git.novox.be/novox/mesh-sdk/go	0.121s
ok  	git.novox.be/novox/mesh-sdk/go/asker	0.002s
ok  	git.novox.be/novox/mesh-sdk/go/asks	0.002s
ok  	git.novox.be/novox/mesh-sdk/go/cli	1.035s
?   	git.novox.be/novox/mesh-sdk/go/example	[no test files]
ok  	git.novox.be/novox/mesh-sdk/go/health	0.002s
ok  	git.novox.be/novox/mesh-sdk/go/outward	0.003s
**Merge check** at `4dc5f29d` - `mesh/merge-gate`: **PASS** — every machine composes with the change as it did without (4 of 4 compose) (modules: mesh-sdk) - `mesh/repo-check`: **PASS** — its merge-check.sh passed, each part in its toolchain: merge-check.sh (typescript), go/merge-check.sh (go) **Delivery plan** — builds mesh-sdk, sent nowhere; no bus step - tier 0: mesh-sdk Run by the build seat on ace as `build-1791806709621577837` (`builds --log build-1791806709621577837`). ``` duration_ms: 64.89474 type: 'test' ... # Subtest: delete refuses an active consumer and deletes only the named retired one ok 12 - delete refuses an active consumer and deletes only the named retired one --- duration_ms: 58.886762 type: 'test' ... # Subtest: an addition acts on the first pass ok 13 - an addition acts on the first pass --- duration_ms: 11.936628 type: 'test' ... # Subtest: what the backend holds at start is retired by the same rules ok 14 - what the backend holds at start is retired by the same rules --- duration_ms: 46.0531 type: 'test' ... # Subtest: the four tools are registered for the module and answer ok 15 - the four tools are registered for the module and answer --- duration_ms: 0.862041 type: 'test' ... # [tools] broken: contributor failed, exposing none: Error: no token # Subtest: semver orders releases ok 16 - semver orders releases --- duration_ms: 0.885165 type: 'test' ... # Subtest: module tools register and collect, a thrower is skipped not fatal ok 17 - module tools register and collect, a thrower is skipped not fatal --- duration_ms: 0.589719 type: 'test' ... # [provisioner:analytics] webapp-anchor: no longer in /tmp/prov-0GMZtV/analytics.json; RETIRED, MARK ONLY — this provider cannot disable a consumer (its adapter has no retire), so webapp-anchor KEEPS ITS ACCESS and its data until a person deletes it with `cleanup delete`. Nothing was changed in the backend (novox/hq ADR 0230): the mesh stopped asking for it: the same answer in 5 passes over 0s, from 2026-10-12T12:08:19.759Z # [provisioner:analytics] webapp-anchor: DELETING, asked by someone: gone for good # Subtest: provisioner creates each consumer with the mesh's login and password; remove waits for a person's delete ok 18 - provisioner creates each consumer with the mesh's login and password; remove waits for a person's delete --- duration_ms: 216.896339 type: 'test' ... # [provisioner:s3-bucket] mesh_anchor_ncloud: no longer in /tmp/prov-derived-LxT7J8/store.json; RETIRED, MARK ONLY — this provider cannot disable a consumer (its adapter has no retire), so mesh_anchor_ncloud KEEPS ITS ACCESS and its data until a person deletes it with `cleanup delete`. Nothing was changed in the backend (novox/hq ADR 0230): the mesh stopped asking for it: the same answer in 5 passes over 0s, from 2026-10-12T12:08:19.993Z # [provisioner:s3-bucket] mesh_anchor_ncloud: DELETING, asked by someone: unassigned # Subtest: provisioner hands the adapter what the mesh derived, and still has it at withdrawal ok 19 - provisioner hands the adapter what the mesh derived, and still has it at withdrawal --- duration_ms: 134.075528 type: 'test' ... # Subtest: provisioner stops calling an unreadable secret a race, and says what to look at ok 20 - provisioner stops calling an unreadable secret a race, and says what to look at --- duration_ms: 72.008027 type: 'test' ... # [provisioner:postgres-database] contributions file unreadable (/tmp/prov-WegL2H/postgres-database.json): ENOENT: no such file or directory, open '/tmp/prov-WegL2H/postgres-database.json'; nothing applied or removed until it can be read # [provisioner:postgres-database] contributions file readable again # [provisioner:postgres-database] contributions file is not JSON (/tmp/prov-WegL2H/postgres-database.json): SyntaxError: Expected property name or '}' in JSON at position 2 (line 1 column 3); nothing applied or removed until it can be read # [provisioner:postgres-database] contributions file readable again # [provisioner:postgres-database] /tmp/prov-WegL2H/postgres-database.json is for s3-bucket, not postgres-database; nothing applied or removed until it can be read # [provisioner:postgres-database] contributions file readable again # [provisioner:postgres-database] /tmp/prov-WegL2H/postgres-database.json has no given list; nothing applied or removed until it can be read # [provisioner:postgres-database] contributions file is not JSON (/tmp/prov-WegL2H/postgres-database.json): SyntaxError: Unexpected end of JSON input; nothing applied or removed until it can be read # [provisioner:postgres-database] contributions file readable again # [provisioner:postgres-database] RETIREMENT WAITS FOR A PERSON: the mesh no longer asks for 2 of the 2 consumer(s) this provider holds (mesh_anchor_forge, mesh_anchor_mail), more than 3, or more than 50% of the 2 held. Nothing is retired until `retire approve` or `retire reject` (novox/hq ADR 0230) # [provisioner:postgres-database] retirement of mesh_anchor_forge, mesh_anchor_mail approved by test: both unassigned on purpose # [provisioner:postgres-database] mesh_anchor_forge: no longer in /tmp/prov-WegL2H/postgres-database.json; RETIRED, MARK ONLY — this provider cannot disable a consumer (its adapter has no retire), so mesh_anchor_forge KEEPS ITS ACCESS and its data until a person deletes it with `cleanup delete`. Nothing was changed in the backend (novox/hq ADR 0230): approved by test: both unassigned on purpose # [provisioner:postgres-database] mesh_anchor_mail: no longer in /tmp/prov-WegL2H/postgres-database.json; RETIRED, MARK ONLY — this provider cannot disable a consumer (its adapter has no retire), so mesh_anchor_mail KEEPS ITS ACCESS and its data until a person deletes it with `cleanup delete`. Nothing was changed in the backend (novox/hq ADR 0230): approved by test: both unassigned on purpose # Subtest: provisioner removes nothing while its contributions file cannot be used (novox/hq issue 241) ok 21 - provisioner removes nothing while its contributions file cannot be used (novox/hq issue 241) --- duration_ms: 1306.684282 type: 'test' ... # [provisioner:cache] webapp-anchor: the backend no longer holds it; applying again # Subtest: provisioner applies again what the backend no longer holds, and trusts memory without holds ok 22 - provisioner applies again what the backend no longer holds, and trusts memory without holds --- duration_ms: 212.539567 type: 'test' ... # [provisioner:cache] webapp-anchor: could not check the backend, will ask again: Error: connection refused # [provisioner:cache] webapp-anchor: could not check the backend, will ask again: Error: connection refused # [provisioner:cache] webapp-anchor: could not check the backend, will ask again: Error: connection refused # Subtest: provisioner keeps a consumer applied when the backend cannot be asked ok 23 - provisioner keeps a consumer applied when the backend cannot be asked --- duration_ms: 71.4734 type: 'test' ... # Subtest: provisioner backs off when create does not satisfy holds, and says so ok 24 - provisioner backs off when create does not satisfy holds, and says so --- duration_ms: 402.256375 type: 'test' ... # Subtest: provisioner treats a failed or hung holds as could-not-ask, and never logs the password ok 25 - provisioner treats a failed or hung holds as could-not-ask, and never logs the password --- duration_ms: 403.027537 type: 'test' ... # Subtest: provisioner stops a verify pass at a timeout, and retries a failed re-apply at the next check ok 26 - provisioner stops a verify pass at a timeout, and retries a failed re-apply at the next check --- duration_ms: 302.029577 type: 'test' ... # Subtest: modules can SERVE: a real async tool, loaded and invoked over the broker ok 27 - modules can SERVE: a real async tool, loaded and invoked over the broker --- duration_ms: 26.554996 type: 'test' ... # Subtest: serving refuses one module exposing two tools of the same name ok 28 - serving refuses one module exposing two tools of the same name --- duration_ms: 0.252024 type: 'test' ... # Subtest: events: a module emits, a listener and the audit sink (\#) both receive it, with metadata ok 29 - events: a module emits, a listener and the audit sink (\#) both receive it, with metadata --- duration_ms: 0.567147 type: 'test' ... # Subtest: each registration is given the environment asked for it, and sees no other's (ADR 0192) ok 30 - each registration is given the environment asked for it, and sees no other's (ADR 0192) --- duration_ms: 0.193815 type: 'test' ... # Subtest: a bundle on stdio lists its tools and answers a call, a seat's verb among them ok 31 - a bundle on stdio lists its tools and answers a call, a seat's verb among them --- duration_ms: 124.336998 type: 'test' ... # Subtest: a bundle served as its module lists the seat's verbs as the seat's, whichever registered first (ADR 0193) ok 32 - a bundle served as its module lists the seat's verbs as the seat's, whichever registered first (ADR 0193) --- duration_ms: 82.953138 type: 'test' ... # Subtest: a launched tool's emit is asked of the runtime, and the call answers once the runtime has published (ADR 0193) ok 33 - a launched tool's emit is asked of the runtime, and the call answers once the runtime has published (ADR 0193) --- duration_ms: 84.670397 type: 'test' ... # Subtest: a launched bundle subscribes as it is imported, handles events the runtime hands it, and asks through it (ADR 0198) ok 34 - a launched bundle subscribes as it is imported, handles events the runtime hands it, and asks through it (ADR 0198) --- duration_ms: 3198.443269 type: 'test' ... # Subtest: a subscribe the runtime refused leaves no handler behind, so one event is handled once (novox/hq issue 276) ok 35 - a subscribe the runtime refused leaves no handler behind, so one event is handled once (novox/hq issue 276) --- duration_ms: 83.059367 type: 'test' ... # Subtest: a launched bundle watches its state as it is imported, has the current values before it goes on, and keeps state through the runtime (ADR 0201) ok 36 - a launched bundle watches its state as it is imported, has the current values before it goes on, and keeps state through the runtime (ADR 0201) --- duration_ms: 283.915073 type: 'test' ... # Subtest: topics match as the runtime's do: * one segment, \# and ** any number ok 37 - topics match as the runtime's do: * one segment, \# and ** any number --- duration_ms: 2.346133 type: 'test' ... 1..37 # tests 37 # suites 0 # pass 37 # fail 0 # cancelled 0 # skipped 0 # todo 0 # duration_ms 3934.417378 NOT TESTED HERE: go/ (the Go binding) — the Go toolchain runs it from go/merge-check.sh NOT TESTED HERE: python/, rust/, c/ — no toolchain of theirs runs a merge check --- its go/merge-check.sh (go toolchain) ok git.novox.be/novox/mesh-sdk/go 0.121s ok git.novox.be/novox/mesh-sdk/go/asker 0.002s ok git.novox.be/novox/mesh-sdk/go/asks 0.002s ok git.novox.be/novox/mesh-sdk/go/cli 1.035s ? git.novox.be/novox/mesh-sdk/go/example [no test files] ok git.novox.be/novox/mesh-sdk/go/health 0.002s ok git.novox.be/novox/mesh-sdk/go/outward 0.003s ```
mesh-admin left a comment
Author
Contributor

Verdict: request changes at 4dc5f29d3e

Independent review for issue 418 (ADR 0310 §4). go vet ./... is clean and go test -race ./... passes in go/. The package is new and additive, so nothing that imports the SDK today changes. Check keeps the operator channel's cases.

Required: CheckSecret misses the shapes that node-reads will meet most often. node-reads applies CheckSecret to every line of env files, unit files, configs and history files. I fed it shapes from those files, and these pass as clean:

  • A credential inside a URL: nats://user:pa55word@host:4222. Any scheme://user:pass@ should be refused by its shape, whatever the password looks like.
  • An environment-style name that ends in a secret word: MESH_TOKEN=s3cr3t-v4lue, SUDO_PASSWORD hunter2. The \b in reAssigned does not fire after _, so DB_PASS=, *_TOKEN=, *_SECRET= and *_PASSWORD= are only caught when the value happens to look random.
  • A name and a value with spaces around =: aws_secret_access_key = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY. The spaces break the run, and the slashes split the value into pieces shorter than 20 characters.
  • Base64 values: of 1000 random 32-byte base64 values, 24 pass, and of 1000 random 16-byte base64url values, 246 pass. Splitting a run at / (needed for paths) lets base64 through when the slashes fall evenly.

Suggested fix:

  • Add a named shape for URL userinfo ([a-z][a-z0-9+.-]*://[^/\s:@]+:[^/\s@]+@).
  • Let reAssigned match a secret word at the end of a name with _ or - before it ((?i)(^|[^a-z0-9])[a-z0-9_]*(password|passwd|pass|pwd|secret|token|api_?key|private_?key|psk|credential)s?\s*[=: ]\s*\S, tuned against the existing cases).
  • Split a run at / only when the run starts with /, ~ or ./, or contains //, so base64 is judged whole.
  • Add each of the shapes above as a test case.

Follow-up, not blocking here: mesh-controller still keeps its own copy in internal/outward ("mirrored, not imported"). Once the messenger moves onto this package, the controller should import it too, so ADR 0310 §4's "one rule" holds. The PR already names the messenger move as a follow-up under issue 418.

Merge order for the group: this PR first (after the fix), then mesh-controller #257, then the catalogue module.

Verdict: request changes at 4dc5f29d3e23a06d58fd1dcc7e2c7ea96a16917b Independent review for issue 418 (ADR 0310 §4). `go vet ./...` is clean and `go test -race ./...` passes in `go/`. The package is new and additive, so nothing that imports the SDK today changes. `Check` keeps the operator channel's cases. **Required: `CheckSecret` misses the shapes that node-reads will meet most often.** node-reads applies `CheckSecret` to every line of env files, unit files, configs and history files. I fed it shapes from those files, and these pass as clean: - A credential inside a URL: `nats://user:pa55word@host:4222`. Any `scheme://user:pass@` should be refused by its shape, whatever the password looks like. - An environment-style name that ends in a secret word: `MESH_TOKEN=s3cr3t-v4lue`, `SUDO_PASSWORD hunter2`. The `\b` in `reAssigned` does not fire after `_`, so `DB_PASS=`, `*_TOKEN=`, `*_SECRET=` and `*_PASSWORD=` are only caught when the value happens to look random. - A name and a value with spaces around `=`: `aws_secret_access_key = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY`. The spaces break the run, and the slashes split the value into pieces shorter than 20 characters. - Base64 values: of 1000 random 32-byte base64 values, 24 pass, and of 1000 random 16-byte base64url values, 246 pass. Splitting a run at `/` (needed for paths) lets base64 through when the slashes fall evenly. Suggested fix: - Add a named shape for URL userinfo (`[a-z][a-z0-9+.-]*://[^/\s:@]+:[^/\s@]+@`). - Let `reAssigned` match a secret word at the end of a name with `_` or `-` before it (`(?i)(^|[^a-z0-9])[a-z0-9_]*(password|passwd|pass|pwd|secret|token|api_?key|private_?key|psk|credential)s?\s*[=: ]\s*\S`, tuned against the existing cases). - Split a run at `/` only when the run starts with `/`, `~` or `./`, or contains `//`, so base64 is judged whole. - Add each of the shapes above as a test case. **Follow-up, not blocking here:** mesh-controller still keeps its own copy in `internal/outward` ("mirrored, not imported"). Once the messenger moves onto this package, the controller should import it too, so ADR 0310 §4's "one rule" holds. The PR already names the messenger move as a follow-up under issue 418. Merge order for the group: this PR first (after the fix), then mesh-controller #257, then the catalogue module.
jschoubben added 1 commit 2026-10-12 12:27:53 +00:00
go/outward: refuse a password in an address, a value given to an env-style secret name, and base64 wherever its slashes fall (review 53, issue 418)
mesh/merge-gate pass: builds mesh-sdk, sent nowhere; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (typescript), go/merge-check.sh (go)
mesh/delivery delivered
mesh/delivery-group group fix/418-node-reads delivered: every member is delivered
67bfa22132
node-reads applies CheckSecret to env, unit and configuration files, where
user:pass@ in a URL, DB_PASS=, MESH_TOKEN= and name = value with spaces passed,
and a random base64 value passed when its slashes or dashes cut it into short
pieces. A run is now judged in pieces only where it is a path, base64 padding
marks a value, and letters alone are random when the case changes as a random
string's does, so a CamelCase name still passes.
Author
Contributor

Merge check at 67bfa221

  • mesh/merge-gate: PASS — every machine composes with the change as it did without (4 of 4 compose) (modules: mesh-sdk)
  • mesh/repo-check: PASS — its merge-check.sh passed, each part in its toolchain: merge-check.sh (typescript), go/merge-check.sh (go)

Delivery plan — builds mesh-sdk, sent nowhere; no bus step

  • tier 0: mesh-sdk

Run by the build seat on g14 as build-1791808075767291231 (builds --log build-1791808075767291231).

  ---
  duration_ms: 68.105048
  type: 'test'
  ...
# Subtest: delete refuses an active consumer and deletes only the named retired one
ok 12 - delete refuses an active consumer and deletes only the named retired one
  ---
  duration_ms: 60.240992
  type: 'test'
  ...
# Subtest: an addition acts on the first pass
ok 13 - an addition acts on the first pass
  ---
  duration_ms: 16.95916
  type: 'test'
  ...
# Subtest: what the backend holds at start is retired by the same rules
ok 14 - what the backend holds at start is retired by the same rules
  ---
  duration_ms: 47.675033
  type: 'test'
  ...
# Subtest: the four tools are registered for the module and answer
ok 15 - the four tools are registered for the module and answer
  ---
  duration_ms: 1.389165
  type: 'test'
  ...
# [tools] broken: contributor failed, exposing none: Error: no token
# Subtest: semver orders releases
ok 16 - semver orders releases
  ---
  duration_ms: 0.787072
  type: 'test'
  ...
# Subtest: module tools register and collect, a thrower is skipped not fatal
ok 17 - module tools register and collect, a thrower is skipped not fatal
  ---
  duration_ms: 0.497968
  type: 'test'
  ...
# [provisioner:analytics] webapp-anchor: no longer in /tmp/prov-Kp26WK/analytics.json; RETIRED, MARK ONLY — this provider cannot disable a consumer (its adapter has no retire), so webapp-anchor KEEPS ITS ACCESS and its data until a person deletes it with `cleanup delete`. Nothing was changed in the backend (novox/hq ADR 0230): the mesh stopped asking for it: the same answer in 5 passes over 0s, from 2026-10-12T12:30:23.919Z
# [provisioner:analytics] webapp-anchor: DELETING, asked by someone: gone for good
# Subtest: provisioner creates each consumer with the mesh's login and password; remove waits for a person's delete
ok 18 - provisioner creates each consumer with the mesh's login and password; remove waits for a person's delete
  ---
  duration_ms: 211.145413
  type: 'test'
  ...
# [provisioner:s3-bucket] mesh_anchor_ncloud: no longer in /tmp/prov-derived-SBNKyO/store.json; RETIRED, MARK ONLY — this provider cannot disable a consumer (its adapter has no retire), so mesh_anchor_ncloud KEEPS ITS ACCESS and its data until a person deletes it with `cleanup delete`. Nothing was changed in the backend (novox/hq ADR 0230): the mesh stopped asking for it: the same answer in 5 passes over 0s, from 2026-10-12T12:30:24.146Z
# [provisioner:s3-bucket] mesh_anchor_ncloud: DELETING, asked by someone: unassigned
# Subtest: provisioner hands the adapter what the mesh derived, and still has it at withdrawal
ok 19 - provisioner hands the adapter what the mesh derived, and still has it at withdrawal
  ---
  duration_ms: 125.530489
  type: 'test'
  ...
# Subtest: provisioner stops calling an unreadable secret a race, and says what to look at
ok 20 - provisioner stops calling an unreadable secret a race, and says what to look at
  ---
  duration_ms: 71.217959
  type: 'test'
  ...
# [provisioner:postgres-database] contributions file unreadable (/tmp/prov-PXxpRn/postgres-database.json): ENOENT: no such file or directory, open '/tmp/prov-PXxpRn/postgres-database.json'; nothing applied or removed until it can be read
# [provisioner:postgres-database] contributions file readable again
# [provisioner:postgres-database] contributions file is not JSON (/tmp/prov-PXxpRn/postgres-database.json): SyntaxError: Expected property name or '}' in JSON at position 2 (line 1 column 3); nothing applied or removed until it can be read
# [provisioner:postgres-database] contributions file readable again
# [provisioner:postgres-database] /tmp/prov-PXxpRn/postgres-database.json is for s3-bucket, not postgres-database; nothing applied or removed until it can be read
# [provisioner:postgres-database] contributions file readable again
# [provisioner:postgres-database] /tmp/prov-PXxpRn/postgres-database.json has no given list; nothing applied or removed until it can be read
# [provisioner:postgres-database] contributions file readable again
# [provisioner:postgres-database] RETIREMENT WAITS FOR A PERSON: the mesh no longer asks for 2 of the 2 consumer(s) this provider holds (mesh_anchor_forge, mesh_anchor_mail), more than 3, or more than 50% of the 2 held. Nothing is retired until `retire approve` or `retire reject` (novox/hq ADR 0230)
# [provisioner:postgres-database] retirement of mesh_anchor_forge, mesh_anchor_mail approved by test: both unassigned on purpose
# [provisioner:postgres-database] mesh_anchor_forge: no longer in /tmp/prov-PXxpRn/postgres-database.json; RETIRED, MARK ONLY — this provider cannot disable a consumer (its adapter has no retire), so mesh_anchor_forge KEEPS ITS ACCESS and its data until a person deletes it with `cleanup delete`. Nothing was changed in the backend (novox/hq ADR 0230): approved by test: both unassigned on purpose
# [provisioner:postgres-database] mesh_anchor_mail: no longer in /tmp/prov-PXxpRn/postgres-database.json; RETIRED, MARK ONLY — this provider cannot disable a consumer (its adapter has no retire), so mesh_anchor_mail KEEPS ITS ACCESS and its data until a person deletes it with `cleanup delete`. Nothing was changed in the backend (novox/hq ADR 0230): approved by test: both unassigned on purpose
# Subtest: provisioner removes nothing while its contributions file cannot be used (novox/hq issue 241)
ok 21 - provisioner removes nothing while its contributions file cannot be used (novox/hq issue 241)
  ---
  duration_ms: 1308.584045
  type: 'test'
  ...
# [provisioner:cache] webapp-anchor: the backend no longer holds it; applying again
# Subtest: provisioner applies again what the backend no longer holds, and trusts memory without holds
ok 22 - provisioner applies again what the backend no longer holds, and trusts memory without holds
  ---
  duration_ms: 212.495164
  type: 'test'
  ...
# [provisioner:cache] webapp-anchor: could not check the backend, will ask again: Error: connection refused
# [provisioner:cache] webapp-anchor: could not check the backend, will ask again: Error: connection refused
# [provisioner:cache] webapp-anchor: could not check the backend, will ask again: Error: connection refused
# Subtest: provisioner keeps a consumer applied when the backend cannot be asked
ok 23 - provisioner keeps a consumer applied when the backend cannot be asked
  ---
  duration_ms: 71.523213
  type: 'test'
  ...
# Subtest: provisioner backs off when create does not satisfy holds, and says so
ok 24 - provisioner backs off when create does not satisfy holds, and says so
  ---
  duration_ms: 404.301677
  type: 'test'
  ...
# Subtest: provisioner treats a failed or hung holds as could-not-ask, and never logs the password
ok 25 - provisioner treats a failed or hung holds as could-not-ask, and never logs the password
  ---
  duration_ms: 402.253872
  type: 'test'
  ...
# Subtest: provisioner stops a verify pass at a timeout, and retries a failed re-apply at the next check
ok 26 - provisioner stops a verify pass at a timeout, and retries a failed re-apply at the next check
  ---
  duration_ms: 302.519432
  type: 'test'
  ...
# Subtest: modules can SERVE: a real async tool, loaded and invoked over the broker
ok 27 - modules can SERVE: a real async tool, loaded and invoked over the broker
  ---
  duration_ms: 60.207829
  type: 'test'
  ...
# Subtest: serving refuses one module exposing two tools of the same name
ok 28 - serving refuses one module exposing two tools of the same name
  ---
  duration_ms: 0.252866
  type: 'test'
  ...
# Subtest: events: a module emits, a listener and the audit sink (\#) both receive it, with metadata
ok 29 - events: a module emits, a listener and the audit sink (\#) both receive it, with metadata
  ---
  duration_ms: 0.570144
  type: 'test'
  ...
# Subtest: each registration is given the environment asked for it, and sees no other's (ADR 0192)
ok 30 - each registration is given the environment asked for it, and sees no other's (ADR 0192)
  ---
  duration_ms: 0.165211
  type: 'test'
  ...
# Subtest: a bundle on stdio lists its tools and answers a call, a seat's verb among them
ok 31 - a bundle on stdio lists its tools and answers a call, a seat's verb among them
  ---
  duration_ms: 125.877461
  type: 'test'
  ...
# Subtest: a bundle served as its module lists the seat's verbs as the seat's, whichever registered first (ADR 0193)
ok 32 - a bundle served as its module lists the seat's verbs as the seat's, whichever registered first (ADR 0193)
  ---
  duration_ms: 83.441353
  type: 'test'
  ...
# Subtest: a launched tool's emit is asked of the runtime, and the call answers once the runtime has published (ADR 0193)
ok 33 - a launched tool's emit is asked of the runtime, and the call answers once the runtime has published (ADR 0193)
  ---
  duration_ms: 84.991932
  type: 'test'
  ...
# Subtest: a launched bundle subscribes as it is imported, handles events the runtime hands it, and asks through it (ADR 0198)
ok 34 - a launched bundle subscribes as it is imported, handles events the runtime hands it, and asks through it (ADR 0198)
  ---
  duration_ms: 3221.341964
  type: 'test'
  ...
# Subtest: a subscribe the runtime refused leaves no handler behind, so one event is handled once (novox/hq issue 276)
ok 35 - a subscribe the runtime refused leaves no handler behind, so one event is handled once (novox/hq issue 276)
  ---
  duration_ms: 82.868584
  type: 'test'
  ...
# Subtest: a launched bundle watches its state as it is imported, has the current values before it goes on, and keeps state through the runtime (ADR 0201)
ok 36 - a launched bundle watches its state as it is imported, has the current values before it goes on, and keeps state through the runtime (ADR 0201)
  ---
  duration_ms: 271.001279
  type: 'test'
  ...
# Subtest: topics match as the runtime's do: * one segment, \# and ** any number
ok 37 - topics match as the runtime's do: * one segment, \# and ** any number
  ---
  duration_ms: 2.368209
  type: 'test'
  ...
1..37
# tests 37
# suites 0
# pass 37
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 3963.915328
NOT TESTED HERE: go/ (the Go binding) — the Go toolchain runs it from go/merge-check.sh
NOT TESTED HERE: python/, rust/, c/ — no toolchain of theirs runs a merge check
--- its go/merge-check.sh (go toolchain)
ok  	git.novox.be/novox/mesh-sdk/go	0.182s
ok  	git.novox.be/novox/mesh-sdk/go/asker	0.009s
ok  	git.novox.be/novox/mesh-sdk/go/asks	0.008s
ok  	git.novox.be/novox/mesh-sdk/go/cli	1.377s
?   	git.novox.be/novox/mesh-sdk/go/example	[no test files]
ok  	git.novox.be/novox/mesh-sdk/go/health	0.009s
ok  	git.novox.be/novox/mesh-sdk/go/outward	0.114s
**Merge check** at `67bfa221` - `mesh/merge-gate`: **PASS** — every machine composes with the change as it did without (4 of 4 compose) (modules: mesh-sdk) - `mesh/repo-check`: **PASS** — its merge-check.sh passed, each part in its toolchain: merge-check.sh (typescript), go/merge-check.sh (go) **Delivery plan** — builds mesh-sdk, sent nowhere; no bus step - tier 0: mesh-sdk Run by the build seat on g14 as `build-1791808075767291231` (`builds --log build-1791808075767291231`). ``` --- duration_ms: 68.105048 type: 'test' ... # Subtest: delete refuses an active consumer and deletes only the named retired one ok 12 - delete refuses an active consumer and deletes only the named retired one --- duration_ms: 60.240992 type: 'test' ... # Subtest: an addition acts on the first pass ok 13 - an addition acts on the first pass --- duration_ms: 16.95916 type: 'test' ... # Subtest: what the backend holds at start is retired by the same rules ok 14 - what the backend holds at start is retired by the same rules --- duration_ms: 47.675033 type: 'test' ... # Subtest: the four tools are registered for the module and answer ok 15 - the four tools are registered for the module and answer --- duration_ms: 1.389165 type: 'test' ... # [tools] broken: contributor failed, exposing none: Error: no token # Subtest: semver orders releases ok 16 - semver orders releases --- duration_ms: 0.787072 type: 'test' ... # Subtest: module tools register and collect, a thrower is skipped not fatal ok 17 - module tools register and collect, a thrower is skipped not fatal --- duration_ms: 0.497968 type: 'test' ... # [provisioner:analytics] webapp-anchor: no longer in /tmp/prov-Kp26WK/analytics.json; RETIRED, MARK ONLY — this provider cannot disable a consumer (its adapter has no retire), so webapp-anchor KEEPS ITS ACCESS and its data until a person deletes it with `cleanup delete`. Nothing was changed in the backend (novox/hq ADR 0230): the mesh stopped asking for it: the same answer in 5 passes over 0s, from 2026-10-12T12:30:23.919Z # [provisioner:analytics] webapp-anchor: DELETING, asked by someone: gone for good # Subtest: provisioner creates each consumer with the mesh's login and password; remove waits for a person's delete ok 18 - provisioner creates each consumer with the mesh's login and password; remove waits for a person's delete --- duration_ms: 211.145413 type: 'test' ... # [provisioner:s3-bucket] mesh_anchor_ncloud: no longer in /tmp/prov-derived-SBNKyO/store.json; RETIRED, MARK ONLY — this provider cannot disable a consumer (its adapter has no retire), so mesh_anchor_ncloud KEEPS ITS ACCESS and its data until a person deletes it with `cleanup delete`. Nothing was changed in the backend (novox/hq ADR 0230): the mesh stopped asking for it: the same answer in 5 passes over 0s, from 2026-10-12T12:30:24.146Z # [provisioner:s3-bucket] mesh_anchor_ncloud: DELETING, asked by someone: unassigned # Subtest: provisioner hands the adapter what the mesh derived, and still has it at withdrawal ok 19 - provisioner hands the adapter what the mesh derived, and still has it at withdrawal --- duration_ms: 125.530489 type: 'test' ... # Subtest: provisioner stops calling an unreadable secret a race, and says what to look at ok 20 - provisioner stops calling an unreadable secret a race, and says what to look at --- duration_ms: 71.217959 type: 'test' ... # [provisioner:postgres-database] contributions file unreadable (/tmp/prov-PXxpRn/postgres-database.json): ENOENT: no such file or directory, open '/tmp/prov-PXxpRn/postgres-database.json'; nothing applied or removed until it can be read # [provisioner:postgres-database] contributions file readable again # [provisioner:postgres-database] contributions file is not JSON (/tmp/prov-PXxpRn/postgres-database.json): SyntaxError: Expected property name or '}' in JSON at position 2 (line 1 column 3); nothing applied or removed until it can be read # [provisioner:postgres-database] contributions file readable again # [provisioner:postgres-database] /tmp/prov-PXxpRn/postgres-database.json is for s3-bucket, not postgres-database; nothing applied or removed until it can be read # [provisioner:postgres-database] contributions file readable again # [provisioner:postgres-database] /tmp/prov-PXxpRn/postgres-database.json has no given list; nothing applied or removed until it can be read # [provisioner:postgres-database] contributions file readable again # [provisioner:postgres-database] RETIREMENT WAITS FOR A PERSON: the mesh no longer asks for 2 of the 2 consumer(s) this provider holds (mesh_anchor_forge, mesh_anchor_mail), more than 3, or more than 50% of the 2 held. Nothing is retired until `retire approve` or `retire reject` (novox/hq ADR 0230) # [provisioner:postgres-database] retirement of mesh_anchor_forge, mesh_anchor_mail approved by test: both unassigned on purpose # [provisioner:postgres-database] mesh_anchor_forge: no longer in /tmp/prov-PXxpRn/postgres-database.json; RETIRED, MARK ONLY — this provider cannot disable a consumer (its adapter has no retire), so mesh_anchor_forge KEEPS ITS ACCESS and its data until a person deletes it with `cleanup delete`. Nothing was changed in the backend (novox/hq ADR 0230): approved by test: both unassigned on purpose # [provisioner:postgres-database] mesh_anchor_mail: no longer in /tmp/prov-PXxpRn/postgres-database.json; RETIRED, MARK ONLY — this provider cannot disable a consumer (its adapter has no retire), so mesh_anchor_mail KEEPS ITS ACCESS and its data until a person deletes it with `cleanup delete`. Nothing was changed in the backend (novox/hq ADR 0230): approved by test: both unassigned on purpose # Subtest: provisioner removes nothing while its contributions file cannot be used (novox/hq issue 241) ok 21 - provisioner removes nothing while its contributions file cannot be used (novox/hq issue 241) --- duration_ms: 1308.584045 type: 'test' ... # [provisioner:cache] webapp-anchor: the backend no longer holds it; applying again # Subtest: provisioner applies again what the backend no longer holds, and trusts memory without holds ok 22 - provisioner applies again what the backend no longer holds, and trusts memory without holds --- duration_ms: 212.495164 type: 'test' ... # [provisioner:cache] webapp-anchor: could not check the backend, will ask again: Error: connection refused # [provisioner:cache] webapp-anchor: could not check the backend, will ask again: Error: connection refused # [provisioner:cache] webapp-anchor: could not check the backend, will ask again: Error: connection refused # Subtest: provisioner keeps a consumer applied when the backend cannot be asked ok 23 - provisioner keeps a consumer applied when the backend cannot be asked --- duration_ms: 71.523213 type: 'test' ... # Subtest: provisioner backs off when create does not satisfy holds, and says so ok 24 - provisioner backs off when create does not satisfy holds, and says so --- duration_ms: 404.301677 type: 'test' ... # Subtest: provisioner treats a failed or hung holds as could-not-ask, and never logs the password ok 25 - provisioner treats a failed or hung holds as could-not-ask, and never logs the password --- duration_ms: 402.253872 type: 'test' ... # Subtest: provisioner stops a verify pass at a timeout, and retries a failed re-apply at the next check ok 26 - provisioner stops a verify pass at a timeout, and retries a failed re-apply at the next check --- duration_ms: 302.519432 type: 'test' ... # Subtest: modules can SERVE: a real async tool, loaded and invoked over the broker ok 27 - modules can SERVE: a real async tool, loaded and invoked over the broker --- duration_ms: 60.207829 type: 'test' ... # Subtest: serving refuses one module exposing two tools of the same name ok 28 - serving refuses one module exposing two tools of the same name --- duration_ms: 0.252866 type: 'test' ... # Subtest: events: a module emits, a listener and the audit sink (\#) both receive it, with metadata ok 29 - events: a module emits, a listener and the audit sink (\#) both receive it, with metadata --- duration_ms: 0.570144 type: 'test' ... # Subtest: each registration is given the environment asked for it, and sees no other's (ADR 0192) ok 30 - each registration is given the environment asked for it, and sees no other's (ADR 0192) --- duration_ms: 0.165211 type: 'test' ... # Subtest: a bundle on stdio lists its tools and answers a call, a seat's verb among them ok 31 - a bundle on stdio lists its tools and answers a call, a seat's verb among them --- duration_ms: 125.877461 type: 'test' ... # Subtest: a bundle served as its module lists the seat's verbs as the seat's, whichever registered first (ADR 0193) ok 32 - a bundle served as its module lists the seat's verbs as the seat's, whichever registered first (ADR 0193) --- duration_ms: 83.441353 type: 'test' ... # Subtest: a launched tool's emit is asked of the runtime, and the call answers once the runtime has published (ADR 0193) ok 33 - a launched tool's emit is asked of the runtime, and the call answers once the runtime has published (ADR 0193) --- duration_ms: 84.991932 type: 'test' ... # Subtest: a launched bundle subscribes as it is imported, handles events the runtime hands it, and asks through it (ADR 0198) ok 34 - a launched bundle subscribes as it is imported, handles events the runtime hands it, and asks through it (ADR 0198) --- duration_ms: 3221.341964 type: 'test' ... # Subtest: a subscribe the runtime refused leaves no handler behind, so one event is handled once (novox/hq issue 276) ok 35 - a subscribe the runtime refused leaves no handler behind, so one event is handled once (novox/hq issue 276) --- duration_ms: 82.868584 type: 'test' ... # Subtest: a launched bundle watches its state as it is imported, has the current values before it goes on, and keeps state through the runtime (ADR 0201) ok 36 - a launched bundle watches its state as it is imported, has the current values before it goes on, and keeps state through the runtime (ADR 0201) --- duration_ms: 271.001279 type: 'test' ... # Subtest: topics match as the runtime's do: * one segment, \# and ** any number ok 37 - topics match as the runtime's do: * one segment, \# and ** any number --- duration_ms: 2.368209 type: 'test' ... 1..37 # tests 37 # suites 0 # pass 37 # fail 0 # cancelled 0 # skipped 0 # todo 0 # duration_ms 3963.915328 NOT TESTED HERE: go/ (the Go binding) — the Go toolchain runs it from go/merge-check.sh NOT TESTED HERE: python/, rust/, c/ — no toolchain of theirs runs a merge check --- its go/merge-check.sh (go toolchain) ok git.novox.be/novox/mesh-sdk/go 0.182s ok git.novox.be/novox/mesh-sdk/go/asker 0.009s ok git.novox.be/novox/mesh-sdk/go/asks 0.008s ok git.novox.be/novox/mesh-sdk/go/cli 1.377s ? git.novox.be/novox/mesh-sdk/go/example [no test files] ok git.novox.be/novox/mesh-sdk/go/health 0.009s ok git.novox.be/novox/mesh-sdk/go/outward 0.114s ```
mesh-admin left a comment
Author
Contributor

Verdict: approve at 67bfa22132

Re-review against review 53. go vet ./... is clean, go test -race -count=1 ./... passes, and the forge's mesh/merge-gate (4 of 4 compose) and mesh/repo-check (go and typescript) are green at this head.

I reran my probes against CheckSecret. Every shape I named in review 53 is now refused:

  • nats://user:pa55word@host:4222 and postgres://app:x@db/app
  • MESH_TOKEN=s3cr3t-v4lue, DB_PASS=correcthorse, SUDO_PASSWORD hunter2, export GITHUB_TOKEN=abc, client_secret: abc
  • aws_secret_access_key = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY

Random base64 values passing, out of 2000 each, standard / url-safe: 16 bytes 1 / 1 (was 246 of 1000 for url-safe), 24 bytes 3 / 0, 32 bytes 0 / 0 (was 24 of 1000), 48 bytes 0 / 0.

Ordinary config lines still pass:

  • sshd's PasswordAuthentication no and ChallengeResponseAuthentication no
  • Environment=MESH_BACKUP_PASSWORD_FILE=/var/lib/restic/repository.secret, token_file = /etc/app/token, password_file: /run/secrets/db, auth_token_lifetime = 3600
  • fstab UUID=… and PARTUUID=…
  • /proc/mdstat lines, a passwd line, ExecStart= and Include lines, docker overlay mount options and an https URL

Not blocking, for the record:

  • Two harmless lines are withheld: an ssh public key line (ssh-ed25519 AAAA…) and a /dev/disk/by-id/nvme-…_S6B0NL0W123456A-part1 path, whose serial number looks random. This loses some usefulness and is no risk.
  • Three shapes still pass, because no name or shape marks them: password "hunter2" (a quoted value after a space), mysql -pHunter2Hunter2 and Authorization: Bearer <short>. File modes and mesh-reads' name refusals are the bounds there.
  • One base64 line of a PEM private key still passes this rule on its own. mesh-catalog #247 now withholds the whole BEGIN…END block, which covers it.

Merge order: this PR first, then mesh-controller #257, then mesh-catalog #247 once #257's rollout says done.

Verdict: approve at 67bfa22132f264c0d844d2034e5ae605a4c5c4b9 Re-review against review 53. `go vet ./...` is clean, `go test -race -count=1 ./...` passes, and the forge's `mesh/merge-gate` (4 of 4 compose) and `mesh/repo-check` (go and typescript) are green at this head. I reran my probes against `CheckSecret`. Every shape I named in review 53 is now refused: - `nats://user:pa55word@host:4222` and `postgres://app:x@db/app` - `MESH_TOKEN=s3cr3t-v4lue`, `DB_PASS=correcthorse`, `SUDO_PASSWORD hunter2`, `export GITHUB_TOKEN=abc`, `client_secret: abc` - `aws_secret_access_key = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY` Random base64 values passing, out of 2000 each, standard / url-safe: 16 bytes 1 / 1 (was 246 of 1000 for url-safe), 24 bytes 3 / 0, 32 bytes 0 / 0 (was 24 of 1000), 48 bytes 0 / 0. Ordinary config lines still pass: - sshd's `PasswordAuthentication no` and `ChallengeResponseAuthentication no` - `Environment=MESH_BACKUP_PASSWORD_FILE=/var/lib/restic/repository.secret`, `token_file = /etc/app/token`, `password_file: /run/secrets/db`, `auth_token_lifetime = 3600` - fstab `UUID=…` and `PARTUUID=…` - `/proc/mdstat` lines, a passwd line, `ExecStart=` and `Include` lines, docker overlay mount options and an https URL Not blocking, for the record: - Two harmless lines are withheld: an ssh public key line (`ssh-ed25519 AAAA…`) and a `/dev/disk/by-id/nvme-…_S6B0NL0W123456A-part1` path, whose serial number looks random. This loses some usefulness and is no risk. - Three shapes still pass, because no name or shape marks them: `password "hunter2"` (a quoted value after a space), `mysql -pHunter2Hunter2` and `Authorization: Bearer <short>`. File modes and mesh-reads' name refusals are the bounds there. - One base64 line of a PEM private key still passes this rule on its own. mesh-catalog #247 now withholds the whole BEGIN…END block, which covers it. Merge order: this PR first, then mesh-controller #257, then mesh-catalog #247 once #257's rollout says done.
mesh-admin merged commit c1ae30b0a2 into main 2026-10-12 13:20:23 +00:00
mesh-admin deleted branch fix/418-node-reads 2026-10-12 13:20:23 +00:00
Sign in to join this conversation.
No Reviewers
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: novox/mesh-sdk#23