Research 027/03: ~/.ssh as one module's, scripts on every machine, the keyring, mail as events

This commit is contained in:
jochen
2026-10-04 11:59:23 +02:00
parent 8712d666bf
commit 27b2d30441
2 changed files with 129 additions and 0 deletions
@@ -51,4 +51,5 @@ matters on their own.
- [01 — What the machines run](01-what-the-machines-run.md): evidence.
- [02 — Candidates and questions](02-candidates-and-questions.md)
- [03 — The account's own tools](03-the-accounts-own-tools.md): `~/.ssh` as one module's, scripts on every machine, the keyring, mail as events
- The predecessor's lessons, shared with research 026: [026/03](../026-the-graphical-session-as-modules/03-what-the-predecessor-taught.md)
@@ -0,0 +1,128 @@
# 03 — The account's own tools: ssh, scripts, mail
Three further directions from the operator on 2026-10-04. Each is account-level, like the shell
([to-be 41](../../03-DESIGN/01-to-be/41-the-shell-and-the-accounts-environment.md)).
## `~/.ssh` is one module's
*"A module owns `~/.ssh`, so it is its responsibility that every folder is set up consistently and
correctly."*
**Measured:**
- The catalogue's `ssh-client` module owns the directory (mode 0700) and one region of
`~/.ssh/config`: a `Host` block per machine of the mesh. It owns nothing else.
- On one workstation, a predecessor's header, `Include` and hand-written host block sat **above** the
mesh's region. ssh takes the first match, so the predecessor's entries were the ones in force, for
the same machines. Removed on 2026-10-04.
- On the control machine, two keys of a retired CI system were still in the operator's
`authorized_keys`, able to log in as the operator. Removed the same day.
- Permissions differ by file and by machine. Backups of the configuration lie beside it.
**Starting position:** `ssh-client` becomes the holder of everything under `~/.ssh`, classified as
ADR 0182 asks:
| path | class | how |
|---|---|---|
| `~/.ssh/`, its mode, every file's mode | owned | the directory resource, plus a check verb that reports a file with the wrong mode |
| `~/.ssh/config` | written into, the mesh's block **at the start** | the mesh's hosts win; the operator's lines after it are kept; an `Include config.d/*` line in the block |
| `~/.ssh/config.d/<module>` | owned by the contributing module | ssh's own drop-in: a work module adds its forge's host there (research 026 C1) |
| `~/.ssh/authorized_keys` | written into, the mesh's block | the operator's keys as the mesh records them, and nothing a retired system left. The operator's own lines are kept below the block |
| `~/.ssh/known_hosts` | written into, the mesh's block | every mesh machine's host key, so the first connection never asks |
| private keys | found | never read and never written by the mesh; a key the mesh should hand out comes from the vault, through the module's own process (ADR 0182, third class) |
The sshd module is the other half: the machine's side. It is already in the catalogue.
## Scripts on every machine, shared and machine-specific
*"All nodes should get some custom scripts, both node-specific and mesh-specific (shared)."*
**Measured:** the operator's script folder holds 64 entries plus 33 in its `bin/`. It is under no
version control, and exists only where it was copied. It mixes three kinds:
1. scripts belonging to a module (the desktop's watchers, lock, menus; a laptop model's brightness);
2. the operator's own tools;
3. installers that modules have replaced.
**Starting position:**
- **The operator's scripts live in a repository of their own,** registered as any application is
([ADR 0015](../../02-DECISIONS/0015-applications-live-in-their-own-repository.md)), built as archives,
unpacked into a directory the module owns under the home. `bin/` goes on `PATH` through an
environment contribution ([ADR 0203](../../02-DECISIONS/0203-the-accounts-environment-is-one-modules-and-every-module-contributes-to-it.md)),
and small functions go into the shell through a `shell` contribution
([ADR 0204](../../02-DECISIONS/0204-a-module-contributes-shell-code-to-the-login-shell-in-named-slots.md)).
- **"Machine-specific" is said by assignment, never by naming a machine**
([ADR 0112](../../02-DECISIONS/0112-a-module-definition-names-no-node-mesh-or-path.md)). One repository
holds several modules:
- `scripts` (shared, on every machine);
- `scripts-workstation`;
- `scripts-media`;
- and so on, each assigned where it applies.
A script that belongs to a piece of software or hardware moves into that module instead. A flavor
inside one module is what [research 026/03](../026-the-graphical-session-as-modules/03-what-the-predecessor-taught.md)
says not to repeat.
- **A script can also be a tool.** A script with a one-line description is served by the node's
runtime, so it can be called through the mesh on any machine that has it.
- A script that needs a secret gets it through question 2's mechanism, never from a file of
environment secrets.
## The keyring
*"A keyring is also a good thing to create a module for."*
**Measured on the two workstations, which both run GNOME Keyring:**
- **On one, the keyring unlocks at login.** The login manager's PAM service includes `login`, which
carries `pam_gnome_keyring`.
- **On the other, it does not.** The PAM line is only in the screensaver's service, so at session
start the window manager runs a script that asks for the password a second time and unlocks the
keyring with it.
- **On both, the session's start script starts the daemon again** with the ssh and gpg components,
and exports the ssh agent's socket. The keyring's current release serves the ssh agent through a
separate per-user socket unit instead.
**Starting position:** a `gnome-keyring` module that holds a node seat, `node-secret-service` (the
holder of the desktop's secret service; a password manager could hold it instead). It declares:
- the package;
- its lines in the login manager's PAM file, written into, never over (ADR 0102), so login unlocks it
on every machine;
- the ssh agent's user socket, once user-scoped units ship;
- the agent's socket path as an environment contribution, which needs a machine fact for the
account's runtime directory. ADR 0203 forbids `$` in values, so `$XDG_RUNTIME_DIR` cannot be
written in one.
The second unlock prompt and the second daemon start go away.
## Mail as events
*"Ideally a mail consumer with all my mail accounts configured, so my mail is recorded in the bus."*
**Measured:**
- The predecessor polled one work mailbox every minute. It **read an access token out of the mail
client's process memory**, called a mail API with it, and raised a desktop notification per unread
message. It worked only while the mail client ran, and stopped silently when the predecessor's units
were retired.
- Two further predecessor modules served mail tools, for one provider and for IMAP.
- The mesh runs a mail server of its own for its domains.
**Not decided here; it needs an effort of its own.** The questions it would have to answer:
- **Accounts and how each authenticates:**
- IMAP with an app password;
- a provider's OAuth with a registered application;
- the mesh's own mail server, which can publish delivery itself.
An employer's tenant may forbid registering an application at all.
- **What the bus records:**
- headers and a summary as events;
- bodies and attachments in an object store the event points at;
- retention, since mail is the most personal data the mesh would hold.
- **What consumes it:** a notifier bridge to the desktop (the predecessor's notifications), search,
an agent's context.
- **Where it runs:** one long-running module, not per machine (ADR 0198).
The obvious first step is the mail server the mesh already runs.