Commit Graph
3 Commits
Author SHA1 Message Date
jschoubben a5b11fd6b2 A build machine is told what to check the broker against
The credential was a URL and nothing else, so the builder verified the broker
the ordinary way — against public roots. A mesh's broker presents a certificate
of the mesh's own, which is in no trust store anywhere, so the connection could
only ever succeed against a broker somebody else vouches for. It failed at TLS
with an error about an unknown authority rather than about a missing pin, and
the container sat there running: up, credential on disk, connected to nothing.

So the sealed credential now carries the URL and the broker's fingerprint —
the same two facts a node's token carries, for the same reason, delivered out
of band relative to the thing being trusted. The builder pins it: the standard
chain check is replaced rather than removed, and what replaces it is stricter,
accepting one certificate instead of every certificate a public authority
would sign.

A file holding only a URL still works, for a builder somebody runs by hand
against a broker with an ordinary certificate.
2026-08-31 00:55:33 +02:00
jschoubben 0262873254 status --json, so a board has something to read
A board reads through interfaces and holds nothing. Everything it needs
is already answered — as text, for people, which is not something a page
can read.

`--json` rather than a serving API, because nothing needs one yet:
whatever serves a board runs the command, and the constraint holds either
way — the board never touches a context's store. An API is the larger
thing and should wait until something asks for it.

Both forms are gathered from the same reads before either says anything,
so they answer the same questions rather than being two implementations
that can drift. That was not true of the first version: the JSON printed
after the text, because the branch was too late.

Four properties, each asserted and each confirmed to fail when removed:

- refused and failed stay distinct all the way out. They are fixed in
  different places, so one word for both sends half a page's readers to
  the wrong one — and how much DID apply is carried, since "three of
  eight" and "none of eight" are different machines
- a machine that never spoke carries no time at all, rather than a zero
  one that any page would format as a date in 1970
- nothing is null. A page distinguishing "no machines are wrong" from
  "this field is missing" has to handle both, and null is the one that
  gets forgotten
- no field is named like a secret. Everything here comes from records
  that hold no readable one, but a shape a page is built against is
  exactly where one would eventually be added for convenience
2026-08-30 20:22:04 +02:00
jschoubben 79d6ade4c8 push --behind, and a builder told where to publish
`status` says which machines are not doing what they were told, and
nothing acted on it: a machine that refused or failed stayed wrong until
somebody ran push again naming it.

`push --behind` sends only to machines whose last report was not a clean
apply. A command rather than a timer, deliberately: a scheduler is then a
scheduler over this, where building the scheduler first would have meant
two paths to one act with nothing to compare them against.

Naming a machine and asking which machines need one are different
requests, so `push <node> --behind` is refused rather than guessed. With
nothing behind it says so, because "nothing needed one" and "this did not
run" must never look the same. A machine failing the same way for six
hours is pushed to anyway and said about — refusing would leave no way to
retry after fixing the cause, and this is a command somebody ran.

Proven in the lab: a machine is broken with a package that does not
exist, `push --behind` names it and not the machine that is fine, the
module is corrected, and the machine recovers without anybody naming it.

And the builder can be told where to publish rather than configured. A
builder that is a module requires an artifact store, and the mesh writes
it the same binding any consumer of any provision gets. A binding with no
address is refused rather than falling back to anything — that would
publish to a store on the wrong machine and be found out much later. The
variable remains for a builder run by a person, which is how it is still
run while being developed.
2026-08-30 19:47:02 +02:00