Commit Graph
80 Commits
Author SHA1 Message Date
jschoubben 196c5b6af2 Compose a node's placed data items from the bus and move the readings layout into the sdk, so disk-load measures without a composed file and restic, mounts, nfs-server and the self-check read one layout (issue 1955, ADR 0571 clause 3) 2026-10-29 23:16:56 +01:00
jschoubben dedd988fc9 Export a bus in memory and a mesh whose plan hands a module with a provision, so mesh-provisioner's whole test composes a real plan (issue 1954 H5) 2026-10-29 22:44:49 +01:00
jschoubben 233b6be5dd Compose each node's provisioning plans from the bus alone, in a Planning mode that reads no secret, so mesh-provisioner composes the plans the controller did (issue 1954 L6-P1, ADR 0570 clause 2) 2026-10-29 22:31:02 +01:00
jschoubben 22c9ddd32b A runtime that already dropped a carried module's tools serves them again, read from what runs and its build (issue 1126, review)
rev-held2: ToolsRunning took a carried module's bundles from the runtime's MESH_TOOL_MODULES alone, and shanks had
already applied the drop of systemd-resolved, so its runs stayed empty and the drop permanent. Now every bundle
archive that runs at its module's BundlePath is a running bundle; one the runtime loads nothing of takes its loads and
words from the module's build (its built bundles, else its build's artifacts, by catalogue.BundleLoads, the rule a
build's resolution keeps, now one function). The runtime process and each archive are matched by their owning module
too, not only by id and path. A row added: the runtime already dropped it.
2026-10-29 21:03:16 +01:00
jschoubben 52056010fe A module composed as what runs of it serves its tools as they run, so the runtime no longer drops it (issue 1126)
After the take-over of placement on 2026-10-29, shanks's runtime stopped serving the folded systemd-resolved (the
node-resolver seat's verbs answer "nothing serves") and novox's the folded nats. Neither has a want: each runs inside
its node's own composition at a build of the old form (Scope.InOwn), and is composed as Carried of the indexer's
manifest. That manifest is the catalogue's, which no build resolved, so it has no bundles (catalogue/build.go: Bundles
are set only by a build's resolution), and the runtime's MESH_TOOL_MODULES and MESH_TOOL_ENV are made from bundles
(catalogue/runtime.go). The controller composed them from the resolved manifest its store kept.

The kind: a module composed as what runs of it (Stub, Carried: held, or folded at an old build) contributes to the
others from its manifest, not from what runs. For a held module with a new build it is the other way round: its new
bundles are served, which were never delivered.

- The quick fix: read the old-form build's resolved manifest for a folded module. One instance; a held module still
  hands its new bundles.
- The thorough fix, chosen: compose.ToolsRunning reads what the node's runtime serves as it runs (its process's
  MESH_TOOL_MODULES and MESH_TOOL_ENV, each bundle's archive), frombus gives it as Node.RunsTools, and every stubbed
  module of the machine's runtime (held or carried) contributes those bundles, whatever its manifest says.

Tests: two rows (folded, held) added to TestCompositionsAreTheDeclarationSplitByModule; no new test function.
2026-10-29 21:03:16 +01:00
jschoubben dbcaa29a1a Never hold a module because a module it reads from is held: it composes with what the held one last offered (issue 1960)
mesh/check pass dbcaa29a1a38 on 394fec8a63c1 | unit 3318 run, 1951 kept, 38 skipped | integration 3994 run, 2125 kept, 38 skipped | over bound: unit, …
mesh/delivery merged: source.main moved to novox/mesh-sdk@307b6234e6bb
mesh/delivery-group group fix/1960-no-hold-spread merged: every member is merged
On novox on 2026-10-29 de-spiegel, held for an unset config value, held mailu, route-proxy and nftables ("it reads
what de-spiegel contributes or listens on"), and mailu held postgres. mesh-sdk #128 (issue 1126) stopped the hold
spreading to consumers and collectors but kept dependents(): the provider of what a held module contributes to, and
the filter where it listens, were held behind it, transitively.

- Quick fix: drop dependents() alone. The filter and the proxy would then read a held module's new manifest, which
  may not be what runs (review of the apply, round 2).
- Thorough fix, chosen: no module is held behind another, in the apply and in the answer (dependents, heldBehind and
  the held-behind path of heldAsToday removed). A held module offers what it last offered: its contributions and
  listens from its manifest at the build its own composition runs at (ranOf), else the manifest it is composed from.
  Only the part of its offer that does not compose is left out, by provision (culprit): de-spiegel's routes leave
  route-proxy without them, while mailu keeps its smtp account; the hold names it.

Tests: rows changed in the existing answer test (de-spiegel held, mailu and proxy composed; site held, the filter
composed with what runs; leave-out by provision). No new test function.
2026-10-29 15:54:21 +01:00
jschoubben c2999a4e13 A held module is no dependence of its neighbours: its hold stops spreading (issue 1126)
mesh/check pass c2999a4e13cc on d4c61ee8ffd0 | unit 3054 run, 234 kept, 36 skipped | integration 3075 run, 266 kept, 29 skipped | over bound: unit (sl…
mesh/delivery merged: source.main moved to novox/mesh-sdk@394fec8a63c1
mesh/delivery-group group fix/1126-held-as-runs merged: every member is merged
novox's seed p-20261028t231141-ec84b4 refused 11 modules "what it composes of ..., held, is not what runs of them":
de-spiegel's hold spread through mailu to postgres, nftables and route-proxy, and from postgres to every consumer, and
gitea, held so, refused the node: "its credential of postgres-database is handed to the provisioner and the module is
not: nothing would fill it". Three rounds each moved the error. The kind: a held module still took part in the
composition as a dependence, so its hold spread.

- The quick fix: let heldAsToday also follow dependsOnHeld's words, and skip the handed refusal for gitea. It moves the
  error once more and leaves every node's applying path holding consumers.
- The thorough fix, chosen: a held module composes only as what runs of it. composeEach no longer compares the
  composition with each held module as its stub (dependsOnHeld removed): a consumer or collector composes against what
  runs of the held module like any other module, and a real difference is still a named difference in the answer, a
  module's own refusal still its refusal. A stub (compose.Stub, catalogue.Manifest.Stubbed) leaves the credentials and
  own secrets it would hand the provisioner out of its plan as they run, never refused for want of a plan. Only what a
  held module's new manifest says to others in their own files is still held behind it (dependents: the provider of
  what it contributes to, the filter where it listens), which heldAsToday already follows.

Tests: rows added to existing tests (a held provider with a binding consumer, which the old code held in the seed's
words; a held feeder of a collector; a held module with a handed credential and own secret). No new test function.
2026-10-29 00:49:00 +01:00
jschoubben 76f7c4d223 A stub keeps its items and their templates, so a held module still contributes to other modules' collections; it drops only the templates for its own files (issue 1126)
mesh/check pass 76f7c4d2238f on d747c1128e42 | unit 3018 run, 270 kept, 36 skipped | integration 3039 run, 302 kept, 29 skipped | over bound: unit (sl…
mesh/delivery merged: source.main moved to novox/mesh-sdk@d4c61ee8ffd0
mesh/delivery-group group fix/1126-seed-oldbuild merged: every member is merged
2026-10-28 23:56:33 +01:00
jschoubben f609be7864 Keep as today what a node keeps today and what is held only behind it, saying how; a stub hands and collects nothing of its own (issue 1126) 2026-10-28 23:49:22 +01:00
mesh-tools d747c1128e Merge pull request 'L3: the vault mints bus accounts, the grants part and the node seat node-engine (issue 774, ADR 0561), rebased onto L2' (#126) from feat/774-vault-mints-sdk into main 2026-10-28 22:27:36 +00:00
jschoubben 77b48a4f1a Name in the seed's brief the first differing field's value as it runs and as composed, so the operator sees what changes (issue 1126)
mesh/check being carried to 77b48a4f1aff on f9afe328881f | from 53f26493697e
mesh/delivery-group group fix/1126-seed-held rejected: composed together: the checker's verdict at the members' heads (63905bd4b284): the composition
mesh/delivery merged: source.main moved to novox/mesh-sdk@f9afe328881f
2026-10-28 22:26:05 +01:00
jschoubben e84a121b8a Let the seed carry the differences its nodes found, each named in its brief for the operator to approve knowingly; a refusal or silence still fails (issue 1126) 2026-10-28 22:24:57 +01:00
jschoubben 06b13ec1cc Show in a proposal's brief what stays held as each node holds it today (issue 1126)
mesh/check failed: the composition: the manifest of mesh-delivery-assigner uses what mesh-controller at ba4d15657343d3116bd6657f85e53630a7dbabf2 (mesh…
mesh/delivery-group group fix/1126-seed-held rejected: composed together: the checker's verdict at the members' heads (2b8e3078c117): the composition
mesh/delivery closed: a newer head of the same pull request
2026-10-28 22:17:57 +01:00
jschoubben 0830fcb25e Hold that a refused module not held today keeps the answer refused (issue 1126) 2026-10-28 22:17:40 +01:00
jschoubben b03bf4c118 A node's answer holds as today what it holds today, so a seed that reproduces today is the same (issue 1126) 2026-10-28 22:13:37 +01:00
jschoubben d6db3f38e6 The vault signs: the contract of its state signers, its verb sign and its event signed, a node's grants to ask it and to read what it trusts, and the authority composed as a bundle (issue 774, ADR 0550 §2) 2026-10-28 21:54:04 +01:00
jschoubben 5228a5782d Name the modules the mesh provides itself in one predicate, so the seed's check takes the private network as the composer and the grants part do (issue 1126)
mesh/check checking 5228a5782d5c on d7c790d2422c | novox/mesh-sdk#120 in group fix/1126-provided
mesh/delivery merged: source.main moved to novox/mesh-sdk@6b43afa2ab38
mesh/delivery-group group fix/1126-provided merged: every member is merged
2026-10-28 20:12:51 +01:00
jschoubben a2dbcf670b Bound a proposal's answers and ask by pulse keys whose expiry the holder hears, and name a hand-back proposal, so no timer waits (ADR 0567 §4, §7, issue 1126) 2026-10-28 18:43:39 +01:00
jschoubben 7a82384e21 Grant a node's agent the seat's placement and candidates, so it composes from the seat's state (ADR 0567 §3, §9, issue 1126) 2026-10-28 18:12:09 +01:00
jschoubben 7c565f2aa5 Move Intended and JudgeAnswers into mesh-sdk for a proposal: a difference is shown, only a refusal or silence fails (ADR 0567 §4, issue 1126) 2026-10-28 18:11:18 +01:00
jschoubben edc6f269f3 Name the seat's states, candidates by proposal and the proposal itself, so readers and the holder share one contract (ADR 0567 §1-§6, issue 1126) 2026-10-28 18:09:38 +01:00
jschoubben 0de3006415 A module refused alone is held, composed as what runs of it, on the node that applies as in its answer: one rule for both paths (issue 1126)
mesh/check pass 0de30064156a on 7aedd28f01dc | unit 2667 run, 590 kept, 36 skipped | integration 2676 run, 633 kept, 29 skipped | over bound: unit (sl…
mesh/delivery merged: source.main moved to novox/mesh-sdk@98f2f2406921
mesh/delivery-group group fix/1126-apply-culprit merged: every member is merged
The applying path (frombus.Compose) composed with no culprit, so one module's
refusal came back as the whole node's: novox's apply would have refused all its
modules for de-spiegel's unset route names. composeEach is now the one rule
Compose and Answer both follow:
- a module whose stub changes the refusal is held (compose.Node.Held); in the
  applying path a stand-in for a module placed here holds that module; only a
  refusal of no single module refuses the node;
- a held module is composed from what runs of it (compose.Carried of its own
  composition in Running) and carried in the node's own wherever that runs it,
  folded or leaving the fold, so the node's own apply never removes it (issue
  1945 by a new path) and what the others read of it is what runs;
- none of a held module's own composition is in what is applied;
- a module whose composition differs when the held ones are composed as stubs
  is held too; the node's own differing refuses the node;
- since a held module is composed from its new manifest, what reads that
  manifest is held with it: the providers of what it contributes to (the
  proxy its routes) and, when it listens, the filter's holder.

Quick fix weighed: a culprit loop in Compose alone. Thorough, chosen: one
shared function, since the kind met twice (seed-8 shadow, now the apply), and
review rounds 1 and 2 found its leaks.
2026-10-28 17:22:35 +01:00
jschoubben f387581b6b A module's own refusal refuses that module, not the node: a stub whose contributions still fail the same is tried bare (issue 1126, seed-8: novox refused the whole candidate for de-spiegel's unset route names)
mesh/check pass f387581b6bfb on 3d021445f743 | unit 3018 run, 234 kept, 36 skipped | integration 3038 run, 266 kept, 29 skipped | over bound: unit (sl…
mesh/delivery merged: source.main moved to novox/mesh-sdk@7aedd28f01dc
mesh/delivery-group group fix/1126-seed-8 merged: every member is merged
2026-10-28 14:07:59 +01:00
jschoubben eda82b1598 A folded module owns its manifest's paths, as the controller tells node-files them, not every path it runs at (issue 1126, seed-8: f1-circuits, snake and txt-game shifted node-tools' owned paths on ace) 2026-10-28 14:07:59 +01:00
jschoubben 16b8d8a1ee Compose as the controller does, so a node's shadow can answer same (issue 1126, C1 C2 C3 C5 C6, T1)
Seals are reused and compared by slot (resource, field), never across resources;
one rule says which resolved config values are laid; a seat path that steps back
is owned by the seat it steps back for, whatever the seat order; a folded module
of an old-form build is composed from what runs of it; the operator export's file
is carried as it runs; a differing resource names its differing JSON paths, found
before its bodies are cut, and an answer is fitted to the node's key.
2026-10-28 09:36:36 +01:00
jschoubben 362a3f1378 A refusal for want of a provider names the modules another node cannot run in this composition, and a refusal naming no placed module is answered at once (issue 1126, probe #532)
mesh/delivery merged: source.main moved to novox/mesh-sdk@be77f6656326
mesh/delivery-group group fix/1126-shadow-bodies merged: every member is merged
mesh/check pass 362a3f1378aa on 03da552cb331 | unit 2860 run, 1141 kept, 36 skipped | integration 2887 run, 1241 kept, 29 skipped | over bound: unit (…
ace and g14 answered 'nothing in this mesh provides artifact-store … assign
distribution to a node' while novox runs it: a module another node cannot run
in this composition (catalogue Resolution.Unhostable) offers nothing, silently.
The refusal now names each such module and what it lacks, and each node left
out. And the culprit search composed the whole mesh once per placed module for a
refusal no stub changes (ace's answer took 7m46s): only the modules the refusal
names are tried, at most 8.
2026-10-28 01:39:07 +01:00
jschoubben 4e0c57771e Bound the bodies an answer shows, naming the rest (issue 1126)
mesh/delivery merged: source.main moved to novox/mesh-sdk@03da552cb331
mesh/delivery-group group fix/1126-shadow-shows merged: every member is merged
mesh/check pass 4e0c57771e8c on 47eb1009921d | unit 2980 run, 270 kept, 36 skipped | integration 2999 run, 302 kept, 29 skipped | over bound: unit (sl…
2026-10-28 01:09:02 +01:00
jschoubben b31c4580aa A candidate answer shows every differing resource, masked; a module folded in the node's own is judged there; the node's own carries its bus membership as it runs it (issue 1126, seed #531)
novox's answer to seed #531 named 15 resources by id alone, so why each differed
was guessed. ModuleDiffers.Bodies keeps every differing resource with both
bodies, each value under a key a secret could be in masked (the same, another
seal of the same value, another value). The five old-form modules read
'removed' because the agent also lists them by module from the declaration kept
before the switch; they are judged inside the node's own. And the node's own
lacked bus-membership, which only the controller composes from its store: the
membership the node runs is carried, until the grants part hands it sealed.
2026-10-28 01:08:49 +01:00
jschoubben 7359d187db A stub keeps its module's directories, so the places its own secrets and data name still resolve (issue 1126: novox refused nats's ${dir:mesh-state})
mesh/delivery merged: source.main moved to novox/mesh-sdk@47eb1009921d
mesh/delivery-group group fix/1126-switch-safe merged: every member is merged
mesh/check pass 7359d187db8d on 47b1dcf08f85 | unit 2992 run, 1008 kept, 36 skipped | integration 3025 run, 1102 kept, 29 skipped | over bound: unit (…
2026-10-28 00:21:45 +01:00
jschoubben e32d777530 A deleting head's answer compares the controller's compositions with what runs, and is refused only without returning; fold the shadow's tests into TestInput (issue 1126, review of the shadow, round 1)
mesh/delivery merged: source.main moved to novox/mesh-sdk@47b1dcf08f85
mesh/delivery-group group fix/1126-shadow-all merged: every member is merged
mesh/check checking e32d77753024 on d48365aed010 | novox/mesh-sdk#110 in group fix/1126-shadow-all
2026-10-27 23:32:31 +01:00
jschoubben 2fd5ffdd62 A node's answer names every module it cannot compose, a folded module of an old-form build is left to the node as it runs, and no store reached says why (issue 1126, the seed's shadow)
The seed's shadow answers named the first refusal only (ace: baserow's archive,
novox: de-spiegel), so each round showed one more. A build that cannot be read or
taken in, and a module the composition refuses, are now each composed as a stub
and named. A module folded into the node's own composition whose manifest names a
build (de-spiegel, nats, photos, systemd-resolved: haves naming a build-... id)
runs a build only the controller holds: it is composed as a stub and its
resources are carried as the node runs them.
2026-10-27 23:17:05 +01:00
jschoubben 7a8e9f9706 Wait for a done key whose artifact names no reference, as the controller's take-in does, and hold the fold guard with a row (issue 1126, review round 1)
mesh/delivery merged: source.main moved to novox/mesh-sdk@d48365aed010
mesh/delivery-group group fix/1126-node-creds merged: every member is merged
mesh/check checking 7a8e9f970648 on d48365aed010 | novox/mesh-sdk#109 in group fix/1126-node-creds
2026-10-27 21:12:13 +01:00
jschoubben d7a758894a Leave a module the node runs inside its own composition to its manifest, as the fold composes it, so taking builds in moves no folded module (issue 1126) 2026-10-27 21:11:20 +01:00
jschoubben 04de3e5da2 Compose a node's own modules from their builds and read what providers elsewhere serve, so a self-composing node hands moved modules and settles served settings as the controller does (issue 1126)
frombus composed every module from the indexer's raw manifest: no moved module
was handed, so the node stood in for own secrets the vault leaves to the
provisioner (the seed's refusals on all four nodes), and a provider elsewhere
had no config values, so mailu's served domain refused shanks.

- takein: the controller's take-in of a done key (artifacts, launchers, the
  templates archive's texts, Resolve) as one SDK rule; catalogue.Launchers holds
  the launcher rule buildstep now delegates to.
- frombus: each module placed on the composing node, and each kept at another
  build (Scope.KeptAt, a fingerprint), composes from its built manifest; another
  node's modules give the keys their served values fill (Reader.Served).
- buscompose: Declared.Served and Principal.ServedReads grant every node the read
  of exactly those keys of mesh-config_resolved.
- compose.ArtifactStoreAddress: the store as the node's composition routes to it.
2026-10-27 21:11:20 +01:00
jschoubben 05b6783103 Fold a module no new-form build names into the node's own composition, as the controller does, so a self-composing node keeps the tunnel and the bus (issue 1126)
mesh/delivery merged: source.main moved to novox/mesh-sdk@a262c7dbe0ff
mesh/delivery-group group fix/1126-fold merged: every member is merged
mesh/check checking 05b678310366 on 56de5ba84010 | novox/mesh-sdk#107 in group fix/1126-fold
mesh-wireguard (built by nobody) and every module a node runs inside its own
composition (Node.InOwn, from frombus.InOwn of what it runs) were cut out as
skipped: every node's shadow answered differs to the placement seed, and a node
composing itself after it would drop their resources. compositions now folds
them in place, module taken off, filled paths with them, as mesh-controller's
compositionsOf does; a want naming a new-form build still moves one out.
2026-10-27 08:50:26 +01:00
jschoubben cadd7b1b2f Ask the vault in a candidate answer only for credentials of modules the node runs, and refuse a stand-in of a running module, so a placement that adds a module is answered as added (issue 1126, ADR 0559 §4)
mesh/delivery merged: source.main moved to novox/mesh-sdk@56de5ba84010
mesh/delivery-group group feat/1126-l2b merged: every member is merged
mesh/check being carried to cadd7b1b2f8f on 43eef46c4121 | from b0cbc4e6aca8, mesh-catalog 64530a8dcb08 | novox/mesh-sdk#106 in group feat/1126-l2b
2026-10-27 02:43:10 +01:00
jschoubben 062c29c3a2 Judge a node's own composition in its candidate answer only where it runs one, as its agent applies it only on a want (issue 1126) 2026-10-27 02:27:04 +01:00
jschoubben ba398e2460 Answer a placement candidate as ADR 0559 has it: added and removed modules, per-module refusals, both digests of a difference, and a node's read of every candidate key (issue 1126) 2026-10-27 02:19:27 +01:00
jschoubben 82f10c3bc9 Let frombus and the composer compose with a given placement and compare a node's compositions with what it runs, so a placement candidate can be answered and checked (issue 1126) 2026-10-27 02:12:11 +01:00
jschoubben 3968849b1b Review of L2: the export is a placement file at its version with its modules (S1); a node asks the vault only when a module placed on it declares an own secret, so one with none composes whether or not the vault answers (H8's failure) (issue 1126)
mesh/delivery closed: a newer head of the same pull request
mesh/check checking 3968849b1b36 on a7a1fbd34e1d | novox/mesh-sdk#98 in group feat/1126-nodes-compose
2026-10-26 20:08:32 +01:00
jschoubben 9713c3023a Placement carries a settings-form module's config values, which mesh-config does not hold, and the export writes them (issue 1126, ADR 0555 §1): the media modules on one node would compose without them
mesh/check failed: ace: novox/mesh-catalog#505 does not merge into the trunk: files in conflict: .gitignore | 9713c3023a18 on a7a1fbd34e1d | unit 1245…
mesh/delivery closed: a newer head of the same pull request
2026-10-26 19:38:37 +01:00
jschoubben 944a35d200 A node's record carries the mesh's own certificate as the machine holds it, and frombus composes with the composing node's (issue 1126, ADR 0555 §3)
mesh/delivery closed: a newer head of the same pull request
mesh/check failed: shanks: novox/mesh-catalog#505: checks/placement/go.mod names mesh-sdk 91413de33516, which is neither on mesh-sdk's trunk nor the g…
2026-10-26 19:23:06 +01:00
jschoubben 7d81e8326f frombus gives the composing node's core credentials as the vault's boxes sealed to it, from the same own secrets (issue 1126, ADR 0349 §16, ADR 0550 §5) 2026-10-26 19:22:58 +01:00
jschoubben 91413de335 compose names the pair credentials a node's composition reads as consumer and provider, and frombus asks them sealed before it composes (issue 1126, ADR 0550 §5)
mesh/delivery closed: a newer head of the same pull request
mesh/check failed: g14: novox/mesh-catalog#505: checks/placement/go.mod names mesh-sdk f1d7c7377dc4, which is neither on mesh-sdk's trunk nor the grou…
2026-10-26 19:18:46 +01:00
jschoubben dfe3ba9475 frombus exports a node's placement from a composition input, Input's inverse for what a person decides, and the controller's seat declares placement-export (issue 1126, ADR 0555 §6)
mesh/delivery closed: a newer head of the same pull request
mesh/check checking dfe3ba94750c on a7a1fbd34e1d | novox/mesh-sdk#98 in group feat/1126-nodes-compose
2026-10-26 19:11:34 +01:00
jschoubben f1d7c7377d frombus reads the composing node's own secrets sealed to it, and node principals may ask the vault's own in their own name (issue 1126, ADR 0550 §5)
mesh/delivery-group group feat/1126-nodes-compose checking: 4 of 5 member(s) ready
mesh/delivery closed: a newer head of the same pull request
mesh/check checking f1d7c7377dc4 on a7a1fbd34e1d | novox/mesh-sdk#98 in group feat/1126-nodes-compose
2026-10-26 19:03:00 +01:00
jschoubben b72de2b75a A node applying what it composed refuses a composition that stands in for anything its input lacks, the bus's user list alone held out (issue 1126, ADR 0547 §2) 2026-10-26 18:59:05 +01:00
jschoubben 2b0689f654 A node's record rides in its own have key, <node>.node: no bucket, writer or grant of its own, and its key's change is what readers hear (issue 1126, ADR 0555 §3) 2026-10-26 18:29:45 +01:00
jschoubben 5b1e990eb4 The tunnel found at adoption and the names of the hub's peers are kept in the placement; whether the hub took it over and who enrolled under a peer's key are read from placement and records, as the controller reads them (issue 1126, ADR 0555) 2026-10-26 18:26:11 +01:00
jschoubben a431746c0e What a person decides of a node lives in its placement file and what the machine observes in its record; two nodes on one address or two hubs are refused (issue 1126, K2) 2026-10-26 18:17:59 +01:00