Merge pull request 'ADR 0283: the username lives in its sources entry, and the needs-operator words as built (issue 386)' (#297) from fix/386-username-in-sources into main

This commit was merged in pull request #297.
This commit is contained in:
2026-10-10 20:25:24 +00:00
4 changed files with 28 additions and 10 deletions
@@ -114,7 +114,7 @@ Rejected:
> **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
> `smb-credentials` holding every login is replaced: the username is the user part of the source's entry in the setting `sources`, and each SMB
> source's password is its own one-line secret `smb-password-<source>`, 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)).
@@ -124,6 +124,13 @@ named exactly, once. Nothing here conflicts with GENESIS.
the line (`nox secret ask <machine> <module> <secret>`), for whoever looks closer. A wait that fails the
check raises the module's `unhealthy` condition instead.
> **Progressive insight — 2026-10-10.** The words quoted above said "An agent opens the prompt for you when
> asked" and "when an agent proposes it". The controller's plain-words rule (ADR 0253) refuses the word
> agent in a condition's needs and explanation, and allows one needs sentence of at most 120 characters. The
> words as built are "Needs you: type <what> at <machine>'s desk prompt." and "approve <setting> of
> <module> on <machine> when it is proposed to you."; the explanation says a hidden prompt opens at the
> desk when the secret is asked for. The act is still named, and "nothing for you to do" is still never said.
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
@@ -138,6 +145,17 @@ named exactly, once. Nothing here conflicts with GENESIS.
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.
> **Progressive insight — 2026-10-10.** This decision said the username is "the setting `smb-users`
> (`name=username`, space-separated)". The catalogue's module check refused that setting at its first
> merge check: a key naming a user is the operator's own value and may have no default (ADR 0262), and a
> key with no default refuses to compose the module on every node until a layer sets it. The username is
> therefore carried in the source's own entry of the setting `sources`, as the user part of its address
> (`name=smb://<user>@<host>/<share>@<mountpoint>`), which is already the operator's value and already
> travels through a settings proposal. A source whose entry names no user waits for the setting `sources`.
> Row 7 of the table below said "a source without username waits for `smb-users`"; it now says the
> source waits for `sources`, and that a password written in the address is refused and never said.
> What was decided stands: the username is an ordinary setting, the password a one-line secret per source.
## Consequences
- **Issue 386's walk passes.** A build of mounts sent while the password is owed passes its gate with the
@@ -174,7 +192,7 @@ named exactly, once. Nothing here conflicts with GENESIS.
| 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-<source>`, with both is written; a password file is read as one line; health answers `waits` and not `unhealthy`; nothing logs or answers a password |
| 7 mounts | the module's tests: a source with no user in its entry waits for `sources`, a password in the address is refused and never said, without password it waits for `smb-password-<source>`, 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.<machine>.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
@@ -160,7 +160,7 @@ in place of `unhealthy` (ADR 0283): a warning, the operator's, never escalated b
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 <machine>'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
approve the setting when it is proposed to you — 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
+7 -7
View File
@@ -56,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 | per SMB source, the `mounts` module's own secret `smb-password-<source>` (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` |
| the credential | the vault | per SMB source, the `mounts` module's own secret `smb-password-<source>` (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 user part of the source's entry in the setting `sources` |
## 1. The server: `nfs-server`
@@ -101,10 +101,10 @@ The glossary holds the three words.
- **Its setting `shares`.** A list of `name=mountpoint`, each optionally `:ro`: the `nfs-share` shares
this node mounts, by the server's name for them. `none` (the default) mounts none.
- **Its setting `sources`.** Occasional sources: `name=smb://<host>/<share>@<mountpoint>` or
`name=disk:<uuid>@<mountpoint>`, each optionally `:ro`. `none` (the default) is none.
- **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 setting `sources`.** Occasional sources: `name=smb://<user>@<host>/<share>@<mountpoint>` or
`name=disk:<uuid>@<mountpoint>`, each optionally `:ro`. `none` (the default) is none. An SMB source's
username is the user part of its address (percent-encoded where it holds a `\` or an `@`); a password
there is refused. A source with no user waits for this setting.
- **Its secret family `smb-password-*`.** One member per SMB source, `smb-password-<source>`, 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
@@ -220,8 +220,8 @@ folders, none refused for a reason not understood.
export goes; the mesh's stays. *Undo:* `zfs set sharenfs=<the old value>` by a person, recorded as a
hand-act.
7. **The downloads**, the same way: read-only first, then read-write.
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-<source>` with `secret-ask`, and the
8. **The gaming device**: the agent proposes the source in `sources`, its username in its address, and the
operator approves it on their phone; an agent asks for `smb-password-<source>` 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.