Files
hq/02-DECISIONS/0176-the-login-shell-is-a-node-seat-and-execute-is-its-contract.md
T
jochen 0bf70ee8b4 Graduate research 025: the environment and the shell's contributions
ADR 0203: the account's environment is one module's (seat node-environment);
every module contributes variables and PATH entries, rendered by the
controller as a POSIX file and as environment.d.
ADR 0204: shell code is contributed to the login shell in named slots, and
login-shell becomes the mesh's node-login-shell.
ADR 0205: software the distribution does not package ships as a pinned
archive of the module.
Issue 225: undeclaring a user stops a node applying; the shell is never
given back or checked.
To-be 41 carries the work packages; to-be 38 WP5 points to it.
2026-10-04 10:30:23 +02:00

84 lines
5.0 KiB
Markdown

---
topic: what runs on it
status: accepted
date: 2026-10-02
deciders: jochen
reconstructed: false
extends: 02-DECISIONS/0132-a-seat-carries-the-tools-its-holder-must-serve.md
---
# 176. The login shell is a node seat held by one shell module, and `execute` is its contract
> **The mechanism changed — 2026-10-04, by [ADR 0204](0204-a-module-contributes-shell-code-to-the-login-shell-in-named-slots.md).** The seat is no longer declared by the shell modules (§1). It is `node-login-shell`, in the mesh's own seat set, which a shell module claims. Its holder also places the shell code other modules contribute, and sources the account's environment ([ADR 0203](0203-the-accounts-environment-is-one-modules-and-every-module-contributes-to-it.md)). What stands: one holder per node, the login shell set by the `user` shape and given back, `execute` as the contract, and any node may call it.
## Context
[ADR 0040](0040-what-a-module-is.md) names the shell as its example of a *shared* seat: bash, zsh
and fish all join `shell`, and one may be default. The operator's reading is sharper, and it
matches [ADR 0126](0126-a-module-declares-its-own-seats.md) better: *installing* a shell is
installing software, and several may be installed; *holding* the seat is being the login shell,
which a node has exactly one of. A definition says which seats a module can hold; the assignment
says which it does.
A seat carries the tools its holder must serve ([ADR 0132](0132-a-seat-carries-the-tools-its-holder-must-serve.md)),
and [to-be 33](../03-DESIGN/01-to-be/33-the-tools-the-mesh-answers.md) leaves which verbs each
seat serves as a decision per seat, taken slowly. This is the first seat of the operator's
environment, and the one every node has.
## Considered Options
1. **A shared `shell` seat with a default**, as 0040's example reads. Rejected: *default* is a
second concept beside *holder* for the same fact, and the `user` shape already makes the
login shell declared state ([to-be 05](../03-DESIGN/01-to-be/05-the-node-host.md)).
2. **No seat; each shell module sets the login shell for itself.** Rejected: two assigned shell
modules would fight over `chsh`, and nothing would say which won.
3. **An exclusive node-scoped seat, `login-shell`, declared by the shell modules, held by one
per node.** Chosen.
## Decision
**1. `login-shell` is a node-scoped seat declared by the shell modules.** zsh, fish and bash each
declare that they can hold it; a node's assignment says which does; the controller refuses a
second holder by name as for every seat. A shell module that is assigned without holding the seat
is installed and nothing more.
**2. Holding the seat sets the account's login shell.** The holder's declaration carries the
`user` shape with the shell it provides, so the login shell is declared state the host applies and
gives back when the holding moves — `chsh` stops being a hook.
**3. The seat's contract is `execute`.** One verb, one argument, the command, run on the node the
seat is scoped to as the operator account, answering with what it printed and how it exited.
Every holder serves it; a holder may serve its own tools beside it
([ADR 0170](0170-the-firewall-seat-serves-its-verbs.md) §2) — show the rendered configuration, list
the plugins, set a prompt value.
**4. Any node may call it on any node.** The grant is the node tools runtime's
([ADR 0175](0175-one-tool-runtime-per-node-serves-every-modules-tools-on-the-host-side.md) §5):
*run `uptime` on every node* is five calls to one verb.
## Consequences
- The first environment module is a shell: a package, files under the home owned by the account,
a seat declaration and claim, a `user` shape, and one tool. It proves the whole pattern on every
node, servers included, before anything graphical is written.
- ADR 0040's shell example is read as *installed is not holding*; a dated note in that record says
so. Its decision is untouched.
- `execute` is a shell on every machine, addressed over the bus. That is the point, and it is
the widest verb the mesh serves; it exists because the operator decided every node may call
every tool, and this record does not narrow that.
## How it is checked
| Rule | Checked by |
|---|---|
| Two shell modules assigned to one node, one holding: one `user` shape in the declaration, naming the holder's shell | the controller's composition tests |
| A second claimant is refused by name | the catalogue's seat tests |
| `execute` runs as the account and answers output and exit status | the module's tool tests over a fake runner, and live on every node |
| The seat's verb appears with its scope and machine in the node tools listing | the runtime's tests (to-be 33 §4) |
## References
- [Research 018](../01-RESEARCH/018-the-operators-machine-as-modules/04-the-seats-of-the-environment.md)
- [ADR 0040](0040-what-a-module-is.md), [ADR 0126](0126-a-module-declares-its-own-seats.md),
[ADR 0132](0132-a-seat-carries-the-tools-its-holder-must-serve.md), [ADR 0170](0170-the-firewall-seat-serves-its-verbs.md)