diff --git a/00-META/glossary.md b/00-META/glossary.md index 789f750c..b9662d74 100644 --- a/00-META/glossary.md +++ b/00-META/glossary.md @@ -265,6 +265,10 @@ and the operator act. **Recorded by** the controller's `identity` and `licences` is the two ends of one credential, the consumer's and the provider's. - **secret** — a credential or other value the vault keeps sealed and the node-engine unseals into a file at apply ([ADR 0085](../02-DECISIONS/0085-a-secret-is-a-provision.md)). +- **secret family** — a module's own secrets declared as one name ending in `-*`; each member is the prefix + and a name (`smb-password-`, one per source the module's settings name), given by its full name + and never made by the mesh + ([ADR 0283](../02-DECISIONS/0283-a-part-that-waits-for-the-operators-secret-or-setting-is-said-as-waiting-passes-the-first-node-gate-and-names-the-act.md)). - **vault** — the module that owns every secret (`mesh-vault`, [24 — The secrets vault](../03-DESIGN/01-to-be/24-the-secrets-vault.md)). - **grant** — what an identity may call or reach, bounded by the provisions it requires @@ -387,7 +391,7 @@ planner, the walk). **Downstream of** Module, Core, Health and repair and Data. **Purpose.** To notice what is wrong with the mesh, say it once, repair what may be repaired unattended, and record what was done by hand. **Recorded by** the controller's condition store. **Upstream of** Change and delivery (the first-node gate) and Operator and conversation (a condition -becomes a message). **Decided by** ADR 0227, 0231, 0240, 0252. +becomes a message). **Decided by** ADR 0227, 0231, 0240, 0252, 0283. **Uses:** node, module, node-engine (Core). - **health** — a module's or a core component's statement of how it is alive and ready, which the @@ -395,6 +399,11 @@ becomes a message). **Decided by** ADR 0227, 0231, 0240, 0252. - **relogin needed** — an account's health when the group database lists it in an account group and its running session, started before, does not have that group yet. Said by the node-engine as the module's resource of kind `account`; it clears at the first look after a new login (ADR 0252). +- **waiting** — a resource's health when its module's tool check says it waits for the operator: a named own + secret or setting of the module, not given yet, without which a part cannot work. Not a fault: the + controller checks it and says it as the condition `needs-operator`, naming the act, and the first-node gate + reads it as *waits for a person* + ([ADR 0283](../02-DECISIONS/0283-a-part-that-waits-for-the-operators-secret-or-setting-is-said-as-waiting-passes-the-first-node-gate-and-names-the-act.md)). - **probe** — one look at one thing's health, run by whoever owns the verdict; what a probe returns is a **finding**, before it becomes a condition. - **signal** and **watchdog** — something that must keep happening (a heartbeat, a report after a send), diff --git a/02-DECISIONS/0240-a-module-says-how-it-is-healthy-and-the-node-engine-judges-it.md b/02-DECISIONS/0240-a-module-says-how-it-is-healthy-and-the-node-engine-judges-it.md index 5b1ab8be..c4fd56b0 100644 --- a/02-DECISIONS/0240-a-module-says-how-it-is-healthy-and-the-node-engine-judges-it.md +++ b/02-DECISIONS/0240-a-module-says-how-it-is-healthy-and-the-node-engine-judges-it.md @@ -172,6 +172,13 @@ send* holds the module on this condition. Every start begins in `starting`, so a send clears at the new build's start and anything after it is the new build's. At the gate's bound the build is put back, as ADR 0236 §3 says. +> **The mechanism changed — 2026-10-10, by [ADR 0283](0283-a-part-that-waits-for-the-operators-secret-or-setting-is-said-as-waiting-passes-the-first-node-gate-and-names-the-act.md).** The four states, the +> condition and the gate's reading stand. A fifth state, `waiting`, now sits beside them: a tool check +> (rule 2) may answer *waits* as well as healthy or not, naming an own secret or a setting the operator has +> yet to give, and one look that says so is enough, inside the grace too. The controller checks the wait, +> says it as `needs-operator` rather than `unhealthy`, and the gate reads it as ADR 0254's *waits for a +> person*: a pass. + **5. A provider down is said once, at the provider.** A check names the provision it exercises. While that provision's provider for this consumer is unhealthy on the record, the consumer's finding is held under the provider's condition — listed there as waiting on it, raised as nothing of its own — and the consumer's diff --git a/02-DECISIONS/0254-a-wait-for-a-persons-new-login-is-not-a-gates-failure-and-each-module-of-a-send-keeps-its-own-pass.md b/02-DECISIONS/0254-a-wait-for-a-persons-new-login-is-not-a-gates-failure-and-each-module-of-a-send-keeps-its-own-pass.md index ed9da10f..bd6e2502 100644 --- a/02-DECISIONS/0254-a-wait-for-a-persons-new-login-is-not-a-gates-failure-and-each-module-of-a-send-keeps-its-own-pass.md +++ b/02-DECISIONS/0254-a-wait-for-a-persons-new-login-is-not-a-gates-failure-and-each-module-of-a-send-keeps-its-own-pass.md @@ -101,6 +101,12 @@ anyway, since the unit that cannot run is what the gate names first. > **Narrowed — 2026-10-08.** Read with the dated correction under "What the gate cannot tell" in the > consequences: only a move whose build puts an account in a group its previous build did not is > excused, and what that costs while a login stays owed. + + > **The mechanism changed — 2026-10-10, by [ADR 0283](0283-a-part-that-waits-for-the-operators-secret-or-setting-is-said-as-waiting-passes-the-first-node-gate-and-names-the-act.md).** The reading + > *waits for a person* now has a second source: a resource whose tool check says it waits for an own + > secret or a setting the operator has yet to give, checked by the controller. Unlike a new login, such a + > wait is excused for any build of the module, since every build owes it alike; the narrowing above still + > holds for the login. 2. **The gate passes a module that waits for a person, and carries the wait.** The reading counts as a pass. The verdict says the wait ("… and it waits for a person: relogin needed on the laptop: …"), in the plan, in the gate's record and in each carried build's verdict. Nothing is put back for it, so the group diff --git a/02-DECISIONS/0263-a-share-is-a-provision-a-node-mounts-it-on-demand-and-only-nodes-that-ask-get-it.md b/02-DECISIONS/0263-a-share-is-a-provision-a-node-mounts-it-on-demand-and-only-nodes-that-ask-get-it.md index 5f578fab..72ffaf4f 100644 --- a/02-DECISIONS/0263-a-share-is-a-provision-a-node-mounts-it-on-demand-and-only-nodes-that-ask-get-it.md +++ b/02-DECISIONS/0263-a-share-is-a-provision-a-node-mounts-it-on-demand-and-only-nodes-that-ask-get-it.md @@ -111,6 +111,13 @@ Rejected: vault and unsealed on the node as a file (ADR 0086). The module writes one credentials file per source from it, readable by root only, which the mount unit names. No password is written in a repository, a manifest or `/etc/fstab`. + + > **The mechanism changed — 2026-10-10, by ADR 0283.** That a share's credential is an operator-delivered + > secret, issued outside the mesh and unsealed on the node as a file, stands. The one secret + > `smb-credentials` holding every login is replaced: the username is the setting `smb-users`, and each SMB + > source's password is its own one-line secret `smb-password-`, a member of a secret family the + > mesh never makes a value for, so it can be typed at the desk prompt one source at a time + > ([ADR 0283](0283-a-part-that-waits-for-the-operators-secret-or-setting-is-said-as-waiting-passes-the-first-node-gate-and-names-the-act.md)). 8. **Health:** a share that should always be reachable and is not, for 5 minutes, raises one plain condition, naming the share, the node and since when. It is the `mounts` module's health (ADR 0240), judged by its tool `mounts_health`, whose finding is *share unreachable*; no new kind of condition is diff --git a/02-DECISIONS/0283-a-part-that-waits-for-the-operators-secret-or-setting-is-said-as-waiting-passes-the-first-node-gate-and-names-the-act.md b/02-DECISIONS/0283-a-part-that-waits-for-the-operators-secret-or-setting-is-said-as-waiting-passes-the-first-node-gate-and-names-the-act.md new file mode 100644 index 00000000..c1f286de --- /dev/null +++ b/02-DECISIONS/0283-a-part-that-waits-for-the-operators-secret-or-setting-is-said-as-waiting-passes-the-first-node-gate-and-names-the-act.md @@ -0,0 +1,192 @@ +--- +topic: what runs on it +status: accepted +date: 2026-10-10 +deciders: jochen +reconstructed: false +extends: 02-DECISIONS/0240-a-module-says-how-it-is-healthy-and-the-node-engine-judges-it.md +--- + +# 283. A part that waits for the operator's secret or setting is said as waiting, passes the first-node gate, and names the act + +## Context + +On 2026-10-10 the mounts module ([ADR 0263](0263-a-share-is-a-provision-a-node-mounts-it-on-demand-and-only-nodes-that-ask-get-it.md)) +was given an occasional source on the workstation: the gaming device's SMB share, approved by the operator on +their phone at 15:06 through a settings proposal ([ADR 0277](0277-a-trusted-setting-is-proposed-through-a-verb-and-set-on-the-operators-warrant-and-a-modules-own-secret-is-asked-for-and-typed-at-the-desk.md)). +Its login was never given. What followed is issue 386, with issue 379 behind it: + +- **The module said the wait, and the mesh read it as a fault.** Its health tool answered *not healthy*: + "the source … cannot be mounted: its login is not given". The node-engine has two answers from a tool + check, healthy or not (ADR 0240 rule 2), so it stated the resource `unhealthy`. The controller raised + `module.mounts..unhealthy`, urgent after four hours, whose plain words said "Nothing for you to + do … It clears as soon as it runs again". Both halves were false: it waited for the operator, and nothing + but the operator would clear it. +- **The first-node gate failed a build for it.** A fix to mounts sat in its gate for ten minutes at 0 of 3 + passes and was put back (`build.mounts..rolled-back`), because the gate reads a module's stated + health and the resource was unhealthy. Every later walk waited behind it, since one walk runs at a time. + The fault was not the build's: any build would have said the same until the login was given. +- **The intended path could not give what the module wanted.** The module took one own secret, + `smb-credentials`, holding a `[source]` section with `username=` and `password=`. The desk prompt that + `secret-ask` opens (ADR 0277 §5) takes one hidden line. A later build of the module read a one-line form, + but a login typed blind as `[name] username=… password=…` is a line the operator gets wrong without seeing + it, and several sources would need several lines the prompt cannot take. +- **The mesh puts a value of its own in a secret nobody gave.** For an own secret not given, the controller + makes a random value and places it, even for one whose manifest says `"issued-by": "outside"`. That is how + the module came to read "line 1 is not key=value inside a [section]": the file held forty random + characters. ADR 0228 already says no value of the mesh's works in place of an outside party's; making one + anyway hides "not given" behind "given and wrong". +- **One precedent exists.** ADR 0254 made a wait for a person's new login a gate reading of its own, + *waits for a person*, which passes and is carried in the verdict, and a condition of its own, + `relogin-needed`, the operator's, never escalated by age. Issue 339 did the same for a directory used as + found. Both are recognised by the controller from what the node-engine says of a kind of resource. Neither + lets a module say it waits. + +**Checked against GENESIS.** *Failure must be loud*, and a wait is not a failure: a gate that undoes a build +for the operator's missing step says something false, loudly, and a condition that says "nothing for you to +do" about the one thing only the operator can do is the quiet half of the same fault. *Evidence over +assertion*: a module saying it waits is an assertion, so the controller checks it against what it holds +before it excuses anything. *When something needs a decision, you are asked, with the context*: the act is +named exactly, once. Nothing here conflicts with GENESIS. + +## Considered Options + +**How the wait is said.** + +1. *Keep it unhealthy, and fix only the words of the condition.* The gate still fails every build sent + while a secret is owed. Rejected. +2. *A reason prefix on an unhealthy resource, as `relogin needed` and `used as found` are.* Works with no + new state, but a module's tool says free text, and a prefix on free text is a word anyone can type. + Rejected for a module's own check. +3. **A third answer of a health tool, *waits*, carried as a state of its own, `waiting`, naming each thing + it waits for: an own secret or a setting of the module, by name, and what the operator gives, in + words.** Chosen. Structured, so the controller can check it; a state, so nothing that reads `unhealthy` + counts it as a fault. + +**What the gate does with it.** + +4. *Read it as waiting*, as a provider down is read: no pass, no failure. The walk then waits for the + operator, possibly for days, and every walk behind it with it. That is issue 386 without its rollback. + Rejected. +5. **The reading *waits for a person* of ADR 0254, carried in the verdict: a pass.** Chosen, under a check: + the controller excuses a wait only when what it names is the module's and is not given (decision 3). + +**Whether only the move that brought the wait is excused**, as ADR 0254 narrowed its own. + +6. *Yes.* A new login is owed because one build added a group, so a later build cannot have brought it. A + secret not given is owed by every build alike; excusing only the first would fail every fix sent while + the operator is away, which is the stall this record removes. Rejected. +7. **No: any build is excused while what it waits for is, on the record, not given.** Chosen. The cost is in + the consequences. + +**How a module with several sources takes one hidden line per password.** + +8. *One secret holding every login, typed in a set form.* Issue 386's own path. Rejected above. +9. *One fixed secret per module for the password, with the username as a setting.* One SMB source per + machine, and a second needs a change to the manifest. Rejected. +10. **An own secret may be declared as a family, `-*`: one member per part the settings name, each + one line, given by its full name.** Chosen. The username is an ordinary setting, since it is not a + secret, and it travels through a settings proposal the operator approves on their phone. + +## Decision + +1. **A health tool answers one of three: healthy, unhealthy, or waits.** The answer keeps `healthy` and + `why`, and gains `state` (`healthy`, `unhealthy`, `waits`) and `waits`: a list, each naming exactly one + own secret (`secret`) or one setting (`setting`) of the module by name, the part that waits (`part`), + and what the operator gives, in words with no path, address or value (`what`). `waits` means nothing + else of the module is wrong. A module with a fault beside a wait answers `unhealthy` and says both. + An answer that says `waits` with no wait, or a wait naming neither or both, is read as unhealthy with + that reason. The type, its constructors and its reader are mesh-sdk's (`go/health`), so the module and + the node-engine read one shape; an answer without `state` is read from `healthy` as before. + +2. **The node-engine states such a resource `waiting`**, a fifth state beside healthy, unhealthy, + starting, held and unknown (ADR 0240 §4), with its waits. One look that says it is enough, inside the + grace too: it is a statement, not a flake that a second look might not repeat. The next look that says + anything else replaces it. + +3. **The controller checks a wait before it excuses it.** A wait for a secret is excused only when the + module's manifest declares that own secret (or the family it belongs to, decision 6) as + `"issued-by": "outside"`, and the store holds no value given for it on that machine. A wait for a + setting is excused only when the manifest declares that setting. A wait that fails the check is judged + as unhealthy, saying why ("says it waits for … , which was given at …"). + +4. **The first-node gate reads an excused wait as *waits for a person* (ADR 0254), a pass**, and the + verdict carries it: "… and it waits for the operator: on ". Nothing is put back for it. + Any build of the module is excused while the wait checks out; a module with anything else unhealthy is + judged as before. + +5. **The wait is said to the operator as the module's condition `module...needs-operator`**, + in place of `unhealthy`: a warning, the operator's, never escalated by age, raised on two statements in a + row and cleared on the first that no longer says it, as every module condition is. Its plain words + (ADR 0253) name the act and never say "nothing for you to do": for a secret, "Needs you: type at + 's desk prompt. An agent opens the prompt for you when asked"; for a setting, "Needs you: approve + of on when an agent proposes it". Its summary names the secret or setting and + the line (`nox secret ask `), for whoever looks closer. A wait that fails the + check raises the module's `unhealthy` condition instead. + +6. **An own secret may be a family.** A name ending in `-*` in `own-secrets` declares one: its path holds + exactly one `*`, and it must say `"issued-by": "outside"`. A member is the prefix followed by a name's + characters — a lower-case letter or digit, then lower-case letters, digits and hyphens, so never a `/`, a + `.` or anything else a path could be steered by (`smb-password-games`); any other name is refused, given by that full name through `secret accept` or `secret-ask`, and + placed at the path with `*` replaced by what follows the prefix. **The mesh never makes a member**: one + not given is no file, and the module reads its absence as not given. `module check` refuses a family + without `issued-by: outside`, or whose path does not hold exactly one `*`. + +7. **The mounts module takes a source's login as a setting and a secret.** The username is the setting + `smb-users` (`name=username`, space-separated). The password is the family `smb-password-*`, one member + per SMB source, one hidden line each. A source whose username or password is not given is not mounted + and is said as waiting for that setting or secret, never as unhealthy. The `smb-credentials` secret and + its form are dropped: nothing was ever given for it. + +## Consequences + +- **Issue 386's walk passes.** A build of mounts sent while the password is owed passes its gate with the + wait carried, and later walks go on. +- **The operator reads one sentence that names the act**, "Needs you: type the password of the source … + at 's desk prompt", which clears once the password is given and the source is said healthy or + absent. +- **What the gate cannot tell.** While a wait checks out, a build that also breaks the waiting part itself + (a source whose mount would fail for a reason of its own once the password came) passes. That fault is + said after the password is given, as the module's own `unhealthy` condition, and a newer build or a + person's push is the way, as ADR 0254 already accepts for its wait. +- **A setting's wait is checked by name only.** The controller cannot read whether a free-form setting + covers a part. A module that says it waits for a setting it declares is believed; the needs-operator + condition says it in front of the operator, which is where a false one shows. Since that condition is + never escalated by age, a module that wrongly says it waits passes its gates for as long as the operator + leaves the condition standing; that is accepted, because the operator sees it every time the conditions + are read. +- **The state is new on the wire.** A controller older than this reads `waiting` as not healthy and raises + nothing, so the gate fails at the bound as before. A node-engine older than this reads `waits` as not + healthy, as before. Order of delivery: mesh-sdk, then the controller, then the node-engine, then the module. +- **An outside secret not given stops being made only for a family.** A plain own secret marked + `issued-by: outside` is still made when not given; changing that touches every module that declares one, + and is tracked by issue 401. +- **The workstation's fstab line for the device** is adopted only once the source is mounted by the module + (issue 379). + +## How it is checked + +| Rule | Checked by | +|---|---| +| 1 Three answers | mesh-sdk `go/health` tests: each constructor's JSON; the reader on an answer with no state (healthy and not), on `waits` with no wait, a wait naming neither or both, a `what` with a path or an address — each read as unhealthy with its reason | +| 2 The state `waiting` | node-engine (mesh-host) `internal/liveness` tests: a tool check answering `waits` is stated `waiting` with its waits, in grace and after; the next healthy answer is `healthy`; an engine reading an answer without `state` judges as before | +| 3 The check | controller tests: a wait for an outside secret not given is excused; for one given, for one not declared, for one not `issued-by: outside`, and for a setting not declared, it is not, each with its reason | +| 4 The gate | controller tests: a module whose only resource not healthy is an excused wait passes with the wait in the verdict; the same with a second resource unhealthy is not yet; a later build is excused too | +| 5 The condition | controller tests: two statements of an excused wait raise `needs-operator`, a warning, resolver operator, its needs naming the desk prompt for a secret and the approval for a setting, never "nothing for you to do"; a statement without it clears it; a wait that fails the check raises `unhealthy` | +| 6 Families | controller tests: `module check` refuses each malformed family; a member is accepted and asked for at the desk by its full name, a name outside the family is refused, and so is a member whose suffix holds a `/`, a `.` or an upper-case letter; a member given is placed at its path; a member not given places nothing and composes | +| 7 mounts | the module's tests: a source without username waits for `smb-users`, without password waits for `smb-password-`, with both is written; a password file is read as one line; health answers `waits` and not `unhealthy`; nothing logs or answers a password | +| live | on the workstation, before the password: `conditions` shows `module.mounts..needs-operator` naming the desk prompt, no `unhealthy`, and the next mounts walk passes its gate. After `secret-ask` and the operator's typing: the condition clears, and the source mounts when the device is on | + +## References + +- Issue 386 (the gate), issue 379 (the workstation's two hand-written mounts). +- [ADR 0240](0240-a-module-says-how-it-is-healthy-and-the-node-engine-judges-it.md) (the states, the tool + check), [ADR 0254](0254-a-wait-for-a-persons-new-login-is-not-a-gates-failure-and-each-module-of-a-send-keeps-its-own-pass.md) + (*waits for a person*), [ADR 0253](0253-a-condition-says-itself-to-the-operator-in-plain-words-beside-its-summary.md) + (plain words), [ADR 0277](0277-a-trusted-setting-is-proposed-through-a-verb-and-set-on-the-operators-warrant-and-a-modules-own-secret-is-asked-for-and-typed-at-the-desk.md) + (the proposal and the desk prompt), [ADR 0228](0228-a-value-given-by-hand-lives-only-until-its-modules-first-good-start.md) + (issued outside), [ADR 0263](0263-a-share-is-a-provision-a-node-mounts-it-on-demand-and-only-nodes-that-ask-get-it.md) + rule 7. +- [To-be 48](../03-DESIGN/01-to-be/48-a-module-says-how-it-is-healthy.md) §4 and §5, and + [to-be 52](../03-DESIGN/01-to-be/52-shares-and-mounts.md): the designs this amends. +- mesh-sdk, mesh-controller, mesh-host and mesh-catalog: the branches `feat/386-a-wait-for-the-operator`. diff --git a/03-DESIGN/01-to-be/48-a-module-says-how-it-is-healthy.md b/03-DESIGN/01-to-be/48-a-module-says-how-it-is-healthy.md index 8985b1df..842735e1 100644 --- a/03-DESIGN/01-to-be/48-a-module-says-how-it-is-healthy.md +++ b/03-DESIGN/01-to-be/48-a-module-says-how-it-is-healthy.md @@ -1,9 +1,10 @@ --- layer: to-be status: in-progress -code: [mesh-host, mesh-controller, mesh-lab, mesh-catalog] -updated: 2026-10-08 +code: [mesh-host, mesh-controller, mesh-lab, mesh-catalog, mesh-sdk] +updated: 2026-10-10 decisions: + - 02-DECISIONS/0283-a-part-that-waits-for-the-operators-secret-or-setting-is-said-as-waiting-passes-the-first-node-gate-and-names-the-act.md - 02-DECISIONS/0254-a-wait-for-a-persons-new-login-is-not-a-gates-failure-and-each-module-of-a-send-keeps-its-own-pass.md - 02-DECISIONS/0240-a-module-says-how-it-is-healthy-and-the-node-engine-judges-it.md - 02-DECISIONS/0241-a-machine-says-how-its-network-is-and-an-outside-writer-of-a-mesh-file-is-a-finding.md @@ -121,8 +122,16 @@ period do the timing, without an execution per look from outside. **Per module and long-running resource, the node-engine keeps a state:** `healthy`, `unhealthy` (with the reason: down, restarting, or the check's failure in words), `starting` (inside grace), `held` (§1) or -`unknown` (nothing could be read). With it: since when, the failing streak, and the restarts counted. Every -start of a resource — a new build, a recreate, a restart — begins in `starting`. +`unknown` (nothing could be read), or `waiting` (its tool check says it waits for the operator, below). +With it: since when, the failing streak, and the restarts counted. Every start of a resource — a new build, +a recreate, a restart — begins in `starting`. + +**A tool check answers one of three: healthy, unhealthy, or waits** +([ADR 0283](../../02-DECISIONS/0283-a-part-that-waits-for-the-operators-secret-or-setting-is-said-as-waiting-passes-the-first-node-gate-and-names-the-act.md)). +*Waits* says nothing of the module is wrong but a part that cannot work until the operator gives something: +each wait names one own secret or one setting of the module, the part, and what the operator gives, in words +with no path, address or value. The answer's shape is mesh-sdk's, read by the module and the node-engine +alike. One look that says it states the resource `waiting`, inside the grace too, with its waits. **Said three ways**, as the core already says its own state: @@ -146,6 +155,14 @@ place of `unhealthy` ([ADR 0254](../../02-DECISIONS/0254-a-wait-for-a-persons-ne cleared as above, its summary one sentence that says to log out and in again. When the login has come and a unit still fails, it clears and `unhealthy` is raised. +**A wait for the operator's secret or setting is its own condition**, `module...needs-operator`, +in place of `unhealthy` (ADR 0283): a warning, the operator's, never escalated by age, raised and cleared as +above. The controller first checks the wait: a secret it names must be the module's own, declared issued +outside the mesh, and hold no value given on that machine; a setting must be one the module declares. Its +plain words name the act — "Needs you: type the password of the source … at 's desk prompt", or +approve the setting when an agent proposes it — and never say there is nothing to do. A wait that fails the +check is said as `unhealthy`, with why. + **`status`** lists it with the other conditions; **`node show`** shows each module's resources with their state and since when, so "is it working" has an answer without opening a terminal on the machine. @@ -174,6 +191,11 @@ fails at the bound until the operator logs in again. The node-engine names the account on every resource that runs in an account's own manager, which is how the controller tells such a unit from one that is broken. +**A wait for the operator's secret or setting is a wait for a person too** (ADR 0283, issue 386). A module +whose only resources not healthy are `waiting`, each wait checked as in §4, reads **waits for a person**: a +pass, carried in the verdict, nothing put back. Unlike a new login, a secret not given is owed by every +build alike, so any build of the module is excused while the wait checks out. + **Each module of a send keeps its own pass** (ADR 0254). A send is still judged as one, with one verdict. The gate also counts, per module, the judgings in a row that found it healthy or waiting for a person; when the send fails, a module whose count reached the passes asked keeps a pass, said "on its own", and is diff --git a/03-DESIGN/01-to-be/52-shares-and-mounts.md b/03-DESIGN/01-to-be/52-shares-and-mounts.md index 138af67c..8d9422ef 100644 --- a/03-DESIGN/01-to-be/52-shares-and-mounts.md +++ b/03-DESIGN/01-to-be/52-shares-and-mounts.md @@ -7,6 +7,7 @@ code: - mesh-catalog modules/mounts updated: 2026-10-10 decisions: + - 02-DECISIONS/0283-a-part-that-waits-for-the-operators-secret-or-setting-is-said-as-waiting-passes-the-first-node-gate-and-names-the-act.md - 02-DECISIONS/0277-a-trusted-setting-is-proposed-through-a-verb-and-set-on-the-operators-warrant-and-a-modules-own-secret-is-asked-for-and-typed-at-the-desk.md - 02-DECISIONS/0263-a-share-is-a-provision-a-node-mounts-it-on-demand-and-only-nodes-that-ask-get-it.md - 02-DECISIONS/0051-shared-data-is-the-operators.md @@ -55,7 +56,7 @@ The glossary holds the three words. | `nfs-server` | the catalogue | holds `node-nfs-server`, provides the provision, writes its export file, runs the service, opens 2049 to the private network | | `mounts` | the catalogue | holds `node-mounts`, writes a unit pair per share, keeps each share's state, serves the tools | | the binding | the controller | gives a consumer the server's name; the consumer asks the server's seat for each share's path and access over the bus | -| the credential | the vault | the `mounts` module's own secret `smb-credentials`, issued outside the mesh and given with `secret accept`, unsealed on the node as a file | +| the credential | the vault | per SMB source, the `mounts` module's own secret `smb-password-` (a member of the family `smb-password-*`), issued outside the mesh and given with `secret accept` or typed at the desk prompt, unsealed on the node as a file; the username is the setting `smb-users` | ## 1. The server: `nfs-server` @@ -102,12 +103,18 @@ The glossary holds the three words. this node mounts, by the server's name for them. `none` (the default) mounts none. - **Its setting `sources`.** Occasional sources: `name=smb:///@` or `name=disk:@`, each optionally `:ro`. `none` (the default) is none. -- **Its secret `smb-credentials`.** One section per SMB source, `[name]` with `username=` and - `password=`, issued outside the mesh and given with `secret accept mounts smb-credentials - --from ` at the controller's terminal — or asked for by an agent with the controller's `secret-ask` - verb, on which a hidden prompt opens at that machine's desk naming the module, the secret and who asked, - and the operator types it there ([ADR 0277](../../02-DECISIONS/0277-a-trusted-setting-is-proposed-through-a-verb-and-set-on-the-operators-warrant-and-a-modules-own-secret-is-asked-for-and-typed-at-the-desk.md)). The process writes one credentials file per source, readable by root only. A source - with no section is said with that remedy and is not mounted. +- **Its setting `smb-users`.** The username of each SMB source, `name=username`, space-separated; `none` (the + default) is none. Not a secret, so it is a setting. +- **Its secret family `smb-password-*`.** One member per SMB source, `smb-password-`, holding that + source's password as one line, issued outside the mesh and asked for by an agent with the controller's + `secret-ask` verb, on which a hidden prompt opens at that machine's desk naming the module, the secret and + who asked, and the operator types it there ([ADR 0277](../../02-DECISIONS/0277-a-trusted-setting-is-proposed-through-a-verb-and-set-on-the-operators-warrant-and-a-modules-own-secret-is-asked-for-and-typed-at-the-desk.md)); + or given with `secret accept mounts smb-password- --from ` at the controller's + terminal. The mesh never makes a member, so one not given is no file ([ADR 0283](../../02-DECISIONS/0283-a-part-that-waits-for-the-operators-secret-or-setting-is-said-as-waiting-passes-the-first-node-gate-and-names-the-act.md)). + The process writes one credentials file per source from the username and the password, readable by root + only. A source whose username or password is not given is not mounted, and its health answers *waits*, + naming the setting or the secret and what the operator gives: the controller says it as + `needs-operator`, and the first-node gate does not fail a build for it (ADR 0283). - **Its settings are trusted settings** (a trusted file asks for them), so a verb may not set them outright: an agent proposes them (`settings propose mounts … --node `) and the operator approves on their phone, where the ask shows each source in its shape ("recalbox=‹address›@‹path›") and the layer's @@ -213,9 +220,9 @@ folders, none refused for a reason not understood. export goes; the mesh's stays. *Undo:* `zfs set sharenfs=` by a person, recorded as a hand-act. 7. **The downloads**, the same way: read-only first, then read-write. -8. **The gaming device**: an agent asks for `smb-credentials` with `secret-ask`, and the operator types it at - that machine's desk (or gives it with `secret accept` at the terminal); the agent proposes the source in - `sources`, and the operator approves it on their phone; `node-mounts.list` says it `absent` while the +8. **The gaming device**: the agent proposes the source in `sources` and its username in `smb-users`, and the + operator approves each on their phone; an agent asks for `smb-password-` with `secret-ask`, and the + operator types it at that machine's desk (or gives it with `secret accept` at the terminal); `node-mounts.list` says it `absent` while the device is off, and it mounts on access when it is on. Then its fstab line is adopted. 9. **The laptop**, read-only, granted read-only on the home server.