Design 52 says how the modules were built: the server's bucket carries each share's path, two root processes, settings default to none
mesh/merge-gate pass: the change touches no module of the mesh's graph
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery-group group feat/mounts-module checking: 2 of 3 member(s) ready
mesh/delivery superseded: a newer head of the same pull request

This commit is contained in:
jochen
2026-10-08 18:37:15 +02:00
parent 34899385ec
commit f4b79ba7c2
2 changed files with 31 additions and 21 deletions
@@ -62,7 +62,8 @@ Rejected:
so the backups are never exported.
9. **A provider's paths written into its binding.** A binding carries what the provider's manifest
`serves`, which is the same for every machine; a share's path is a setting of one assignment. The
consumer asks the server for it over the bus instead, so no controller change is needed for it.
consumer reads it from the server's bucket on the bus instead, so no controller change is needed for
it.
## Decision
@@ -107,10 +108,10 @@ Rejected:
is absent raises nothing. The server's health is its service and its export table holding every
declared export.
9. **Over the bus.** The controller wires the two through the provision: the binding gives the consumer
the server's name. The consumer then asks the server's seat, `node-nfs-server.exports`, by request and
reply for each share's path and access, so a share's path is said once, on the server. `nfs-server`
keeps its exports, and `mounts` keeps each share's state, in their own key-value buckets (ADR 0201).
Neither reads the other's files (ADR 0260).
the server's name. `nfs-server` keeps its exports in its key-value bucket (ADR 0201), and `mounts`
reads it (`reads`) for each share's path and access, so a share's path is said once, on the server.
`mounts` keeps each share's state in its own bucket. The seat's `exports` verb answers the same by
request and reply. Neither reads the other's files (ADR 0260).
10. **Taking over a hand-written line is an act.** The `mounts` module's `adopt` comments out one fstab
line, keeping a copy, only after its own unit pair for that mount point is armed, and only on a
person's word. The ZFS `sharenfs` property is cleared the same way, by `nfs-server`, once its own
+25 -16
View File
@@ -64,17 +64,21 @@ The glossary holds the three words.
- **What it refuses.** A path that does not exist or is not a directory, `/`, and a path that holds a
backup repository at its root or one level below. A refused share is said by the tools and makes the
module unhealthy; the others are exported.
- **Its file.** `/etc/exports.d/mesh.exports`, written by the module's own process, which runs as root.
One line per share: the path, to `${machine:mesh-range}` and nothing else, with `rw` or `ro`, `sync`,
`secure`, `no_subtree_check`, `all_squash`, a stable `fsid`, and `anonuid` and `anongid` read from the
path's owner. `exportfs -ra` runs when the file changes.
- **The service.** `nfs-server.service`, running and enabled. NFS version 3 is turned off in the
module's drop-in for `nfs.conf`, so only 2049/tcp is needed, declared in `listens` with `from: mesh`.
- **Its file.** `/etc/exports.d/mesh.exports`, written by the module's own process, which runs as root
and looks every 30 s. One line per share: the path, to `${machine:mesh-range}` and nothing else, with
`rw` or `ro`, `sync`, `secure`, `no_subtree_check`, `all_squash`, and `anonuid` and `anongid` read from
the path's owner. `exportfs -ra` runs when the file changes or a share is missing from the kernel's
table. The filesystem's own id is kept; none is forced.
- **The service.** `nfs-server.service`, running and enabled. NFS versions 2, 3 and 4.0 are turned off
in the module's drop-in in `/etc/nfs.conf.d`, so only 2049/tcp is needed, declared in `listens` with
`from: mesh`.
- **The provision.** `nfs-share`, at the mesh's scope, with no identity: the binding gives the
consumer the server's name, and the consumer asks for the rest.
consumer the server's name, and the consumer reads the rest from the server's bucket. Its consumers'
data class is `none`: the folders are the operator's, and the mesh backs up nothing through it.
- **Health.** Its tool `nfs_health`: the service is active, every share is in the kernel's export table,
and no share was refused.
- **Its bucket.** `exports`, one entry per machine: the shares as the kernel holds them.
- **Its bucket.** `exports`, one entry per machine, keyed by the node's name: the answer of `exports`,
put by the module's tools every 30 s. The root process does not reach the bus; its tools do.
- **Its seat's verbs.** `exports` (r), `clients` (r), `test` (r), `reload` (a) and `adopt` (a). `adopt`
clears a ZFS dataset's `sharenfs` property for a path the module now exports, only once the mesh's
export of it is in the kernel's table, and only with a person's confirmation and reason.
@@ -82,18 +86,22 @@ The glossary holds the three words.
## 2. The client: `mounts`
- **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.
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`.
`name=disk:<uuid>@<mountpoint>`, 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 <node> mounts smb-credentials
--from <file>`. 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.
- **Asking the server.** The process asks `node-nfs-server.exports` on the server named by the binding,
over the bus, for each share's path and access. A share the server does not export is said, not
guessed.
- **Reading the server.** The module reads the server's bucket `exports` (its manifest's `reads`) for
each share's path and access: its tools, which reach the bus, write what they read to a file of the
module's, and the root process takes only a name, an absolute path and the access from it. A share
the server does not export is said, not guessed, and a silent server removes nothing.
- **Two processes, both root.** `apply` writes the unit pairs and the SMB credentials files and arms
the automounts; `watch` probes each source and keeps the states. The tools act as root through
`sudo -n` for `mount`, `unmount` and `adopt`, and say so when it is refused.
- **The unit pair.** For each share and source, a `.mount` and an `.automount`, named by the escaped
mount point, written in `/etc/systemd/system` by the module's own process with a marker line. It never
mount point, written in `/etc/systemd/system` by `apply` with a marker line. It never
touches a unit without its marker, and it removes its own pairs that are no longer asked for:
- the `.automount` is enabled under `remote-fs.target`, with an idle time of 10 minutes;
- the `.mount` is not enabled; its mount timeout is 30 s, 10 s for an occasional source; it starts
@@ -106,8 +114,9 @@ The glossary holds the three words.
`failed` (the mount was tried and refused, with the error).
- **Its probes.** Every 30 s, a connection to the server's port (2049, or 445 for SMB) with a short
timeout, and the device for a disk. It never touches a mount point, so a hung server cannot hang it.
- **Health.** Its tool `mounts_health`: every automount is armed, and no share that is not occasional
has been `unreachable` or `failed` for 5 minutes. An absent occasional source is healthy and said as
- **Health.** `apply` by its unit; `watch` by the tool `mounts_health`: every automount is armed, every
share the settings name could be set up, `watch` looked in the last 2 minutes, and no share that is not
occasional has been `unreachable` or `failed` for 5 minutes. An absent occasional source is healthy and said as
*not present since 18:02*.
- **Its seat's verbs.** `list` (r), `test` (r), `mount` (a), `unmount` (a), `adopt` (a); its tools
`mounts_list` and `mounts_exports` (r) stay.