89 lines
8.0 KiB
Markdown
89 lines
8.0 KiB
Markdown
# mesh-provisioner-client
|
|
|
|
Holds the `node-provisioner` seat on every node: the provisioner's client (novox/hq ADR 0349 clauses 3 to 5, design 57
|
|
P5). Go, one binary (`cmd/mesh-provisioner-client`), run as a service of its own under the account
|
|
`mesh-provisioner-client`, outside the tool runner, on a bus account of its own, `provisioner.<node>`, delivered as
|
|
its own secret `provisioner-broker` (mesh-controller `internal/broker/provisioner.go`). The bus grants that account
|
|
its own machine's keys and, to read alone, the whole assignments mirror, where a provider's consumers on any
|
|
machine are bound to it (ADR 0373); nothing else.
|
|
|
|
## What it does
|
|
|
|
For each module the controller handed to the provisioner on its node — an assignment in
|
|
`mesh-controller_assignments` (`<node>.<module>`) whose value carries a `provisioning` plan: the templates archive
|
|
by reference and digest, the files it fills (path, template, `owner-at-runtime`, how its program takes a change)
|
|
and the placement values its templates name — the client:
|
|
|
|
- fetches the templates archive and uses it only when it hashes to the plan's digest, keeping it by digest;
|
|
- records each placeholder as a need in `mesh-provisioner_needs`, `<node>.<module>.<kind>.<name>` (a character a
|
|
key cannot hold written `=<hex>`);
|
|
- watches each owner's keys for its node: placement in the plan, config in `mesh-config_resolved`
|
|
(`<node>.<module>.<key>`), both named by `mesh-provisioner_registry`;
|
|
- asks the vault (`mesh-vault.get`, named by the registry) for each of the module's own secrets its plan names
|
|
(`provisioning.secrets`), in its own name on the subject naming its account, with the public half of a key it
|
|
made at start and keeps in memory alone; the vault answers the secret sealed to that key (ADR 0206's box), so only
|
|
the box crosses the bus. A secret is asked once per run, and again when the vault says `secret-changed` for it,
|
|
so a rotation reaches its file without a new declaration. A secret with a `path` is written to that file alone,
|
|
as an `owned` file. A secret the vault does not hold yet, or a vault that does not answer, is waited for; a
|
|
refusal fails the module with the vault's reason and is asked again after ten minutes. A value is never written
|
|
to a bucket, a report or a log;
|
|
- asks the vault the same way for each credential of what the module requires that its plan names
|
|
(a `provisioning.provisions` entry with a `name`, issue 757; every entry names the provider serving that
|
|
provision, issue 760, and its offer's credential form, issue 768, which the vault acts on): the value a provider's software issued for this module on this machine,
|
|
`provision/<provider>/<provision>/<node>/<module>` (the forge's read token for claude-code, issue 737), filled
|
|
wherever `${secret:<name>}` names it and written to its `path` alone, and asked again on its `secret-changed`;
|
|
- fills each item the module contributes to a collection (`provisioning.items`: the collection and its template)
|
|
from the same owners as a file, and records it in `mesh-provisioner_collect`, `<node>.<collection>.<contributor>`
|
|
(a character a key's token cannot hold written `=<hex>`: `shell:zsh:normal` is `shell=3Azsh=3Anormal`). An item
|
|
naming a secret fails: an item is kept in a bucket, and no secret is;
|
|
- fills a collector's `${collected:<collection>}` from every item of that collection recorded for its node, in the
|
|
contributors' order, each introduced by a line naming its contributor in the collection's comment
|
|
(`provisioning.collections`: the collections the module collects, each with its comment). The three old
|
|
namespaces read as collections of the same name: `${shell:zsh:normal}` is the collection `shell:zsh:normal`,
|
|
`${environment:posix}` is `environment:posix`, `${contribution:node-hotkeys:trigger}` is
|
|
`contribution:node-hotkeys:trigger`. A collector naming a collection its plan does not fails. A collection with no
|
|
item is filled empty, and filled again whenever an item of it is recorded or goes;
|
|
- fills a module's files only once every value is there; otherwise it writes nothing and reports `waiting` with what
|
|
it waits for;
|
|
- has the node-engine write each changed file through its local write socket (`/run/nox-mesh-host/fill.sock`,
|
|
mesh-host `internal/filling`), which writes it as the declaration says (an `owned` file whole, a `seed` only when
|
|
absent, a `managed` file's own keys or block) and restarts or reloads its program as the declaration says;
|
|
- writes every file again every minute, so the node-engine puts back an `owned` file changed on disk;
|
|
- reports `prov.applied.<node>.<module>` and keeps the same report in `mesh-provisioner_applied` (`<node>.<module>`):
|
|
`applied` with each file's action and digest, `waiting`, `failed` with the reason, or `removed`;
|
|
- when a module is taken off the node, removes its needs, its items and its report (the node-engine removes the files its
|
|
declaration no longer names); and when the bus comes back, or the client starts, fills from the current values;
|
|
- fills a provider's `${consumers:<provision>}` (novox/hq ADR 0373, issue 679) with one entry per consumer bound to
|
|
it for that provision, in the order of the assignments' keys and, within a plan, of its `provisions` entries: a
|
|
JSON list, each entry `{consumer, module, local, credential, value}`, the credential its own,
|
|
`pair/<provider>/<provision>/<node>/<module>[/<local>]`, asked of the vault in the client's own name as a secret
|
|
is. Who binds a provider is read from every machine's plans, so the client watches the whole assignments mirror
|
|
(it carries no secret's value) and reads only its own machine's plans as its own. A consumer whose credential the
|
|
vault does not hold yet, or will not hand, is left out and said in the log, so the others are served; the
|
|
provider's helper waits for it (mesh-sdk `go/provider` ConsumersFile). It is filled again when a consumer binds or
|
|
unbinds and when the vault says one of the credentials changed. The catalogue holds the file to mode 0600 and
|
|
owned, and refuses the placeholder in a module not providing that provision (mesh-controller internal/catalogue
|
|
consumers.go); an item naming it fails;
|
|
- fills a consumer's `${bound:<provision>:<key>}` from its provider's entry (mesh-sdk `go/provider`, issue 678): the
|
|
bucket `<provider module>_bound` of the provider its plan names, key `<node>.<module>.<provision>[.<local>]`, as
|
|
the registry says, watched for its own machine's keys once a plan names it; `as`, `user` or one of the served
|
|
values. An entry not written yet, or a bucket not there yet, is waited for; one the provider refused fails the
|
|
module with the provider's reason.
|
|
|
|
It fills `config`, `secret` (design 57 V4, issue 647), the collections (`collected`, and `shell`, `environment`
|
|
and `contribution` read as collections: design 57 L1, issue 684), `consumers` and `bound` (issue 679) and the
|
|
placement kinds (`dir`, `access`, `port`, `seat`, `machine`). Any other kind fails with the reason.
|
|
|
|
## A module's code asks at runtime
|
|
|
|
A module whose code runs in its machine's tool runner reads its config values through the SDK (ADR 0349 clause 14
|
|
as ADR 0370 changes it, issue 685). The tool runner asks this client on `prov.ask.<node>`, which only the same
|
|
machine's tool runner may publish, with `{module, kind, key}`; the module is the one whose bundle asks, named by the
|
|
tool runner and never by the module's code. The client answers `{value}`, the value as `mesh-config_resolved` holds
|
|
it, only for a module assigned to its machine and a key mesh-config resolved for it (what its manifest declares), or
|
|
`{error}` with the reason. It never answers a secret: the vault hands a secret only to a caller the bus proves, so a
|
|
module in the tool runner reads its secrets from its files. A binding's non-secret values (`bound`) are filled
|
|
into files (issue 679) and not yet answered on an ask. It says on `prov.changed.<node>.<module>` which value moved (`{kind, key}`, every
|
|
key of the kind when `key` is absent, never the value), when the client starts and on every change, so the tool
|
|
runner asks again for the watches it holds.
|