Accept ADR 0263 with the operator's choices: one share per folder so backups are never exported, mesh machines only
This commit is contained in:
+8
-1
@@ -219,7 +219,7 @@ and Health and repair. **Decided by** ADR 0100, 0126, 0176, 0181, 0207, 0221, 02
|
||||
|
||||
**Purpose.** To resolve which module serves a provision for which consumer, and to wire the two.
|
||||
**Recorded by** the controller. **Upstream of** Data; downstream of Module, Placement and Identity.
|
||||
**Decided by** ADR 0027, 0084, 0138, 0225, 0230.
|
||||
**Decided by** ADR 0027, 0084, 0138, 0225, 0230, 0263.
|
||||
**Uses:** module, seat, credential (Identity and access).
|
||||
|
||||
- **provision** — a service one module `provides` and others `require`; the mesh resolves a provider
|
||||
@@ -234,6 +234,13 @@ and Health and repair. **Decided by** ADR 0100, 0126, 0176, 0181, 0207, 0221, 02
|
||||
reaches ([ADR 0138](../02-DECISIONS/0138-an-assignment-binds-an-endpoint-and-says-how-far-it-reaches.md)).
|
||||
- **retire** — a provider stops serving a consumer the mesh no longer asks for; what it kept is deleted
|
||||
only by a person ([ADR 0230](../02-DECISIONS/0230-a-consumer-the-mesh-stops-asking-for-is-retired-and-deleted-only-by-a-person.md)).
|
||||
- **share** — a folder one node offers to the mesh's other nodes over the private network: the provision
|
||||
`nfs-share`, provided by the holder of the node seat `node-nfs-server`, one share per folder
|
||||
([ADR 0263](../02-DECISIONS/0263-a-share-is-a-provision-a-node-mounts-it-on-demand-and-only-nodes-that-ask-get-it.md)).
|
||||
- **mount** — a share, or an occasional source, made visible at a path on one node by the holder of the
|
||||
node seat `node-mounts`, as a mount unit and an automount unit, mounted on access (ADR 0263).
|
||||
- **occasional source** — a share on a device that is not a node and comes and goes, such as a USB disk;
|
||||
its absence is a state, *not present*, never a fault (ADR 0263).
|
||||
|
||||
## Identity and access — who may do what
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
status: active
|
||||
status: graduated
|
||||
initiated: 2026-10-08
|
||||
touches:
|
||||
- 04-ISSUES/169-a-machine-shares-its-files-and-the-mesh-does-not-know/00-report.md
|
||||
@@ -12,7 +12,9 @@ touches:
|
||||
- 02-DECISIONS/0092-an-operator-delivers-a-pair-credential.md
|
||||
- the catalogue's module manifests
|
||||
- the controller's seat set
|
||||
became: []
|
||||
became:
|
||||
- 03-DESIGN/01-to-be/52-shares-and-mounts.md
|
||||
- 02-DECISIONS/0263-a-share-is-a-provision-a-node-mounts-it-on-demand-and-only-nodes-that-ask-get-it.md
|
||||
---
|
||||
|
||||
# 038 — Shares and mounts the mesh manages
|
||||
@@ -71,8 +73,10 @@ The detail is in [01-evidence.md](01-evidence.md). In short:
|
||||
|
||||
## Where it goes
|
||||
|
||||
[02-options.md](02-options.md) weighs the options. The proposed decision is
|
||||
[02-options.md](02-options.md) weighs the options. The decision is
|
||||
[ADR 0263](../../02-DECISIONS/0263-a-share-is-a-provision-a-node-mounts-it-on-demand-and-only-nodes-that-ask-get-it.md),
|
||||
and the to-be design is
|
||||
[52 — Shares and mounts](../../03-DESIGN/01-to-be/52-shares-and-mounts.md). Both wait for the
|
||||
operator's word. The open questions are listed at the end of the options.
|
||||
and the design is [52 — Shares and mounts](../../03-DESIGN/01-to-be/52-shares-and-mounts.md).
|
||||
|
||||
On 2026-10-08 the operator chose **one share per folder**, so the backups are never exported, and
|
||||
**mesh machines only**, over the private network. Which nodes mount a share, and whether the laptop
|
||||
mounts read-only, is decided at assignment.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# 038 — Options
|
||||
|
||||
Each question below lists the options weighed and the one recommended. The recommendations together
|
||||
are [ADR 0263](../../02-DECISIONS/0263-a-share-is-a-provision-a-node-mounts-it-on-demand-and-only-nodes-that-ask-get-it.md).
|
||||
became [ADR 0263](../../02-DECISIONS/0263-a-share-is-a-provision-a-node-mounts-it-on-demand-and-only-nodes-that-ask-get-it.md).
|
||||
|
||||
## Q1. Who writes the export
|
||||
|
||||
@@ -137,14 +137,10 @@ Every exchange between the modules is over NATS (research 036):
|
||||
- `mounts` keeps each share's state in its bucket; a view or the controller watches it;
|
||||
- no module reads another's files (ADR 0260).
|
||||
|
||||
## Open questions for the operator
|
||||
## The operator's answers (2026-10-08)
|
||||
|
||||
1. **The pool's root, or one share per folder?** Recommended: one per folder (Q2 option C), so the
|
||||
backup repository is never exported.
|
||||
2. **Every node, or the nodes that ask?** Recommended: only the workstation and the laptop, with a
|
||||
mesh-wide setting available (Q4).
|
||||
3. **Underlay clients.** The private network only is recommended. Does anything on the home network
|
||||
that is not a node (a TV, a media player) still need the NFS or SMB share? Today the filter closes
|
||||
both, so nothing has been able to use them since 2026-10-04.
|
||||
4. **Read-write from the laptop.** Should the laptop mount the library read-only? Recommended: yes,
|
||||
read-only, since it travels.
|
||||
1. **The pool's root, or one share per folder?** One share per folder (Q2 option C), so the backup
|
||||
repository is never exported.
|
||||
2. **Underlay clients?** Mesh machines only, over the private network.
|
||||
3. **Every node, or the nodes that ask? Read-write from the laptop?** Decided at assignment, which is the
|
||||
operator's; the recommendation (Q4 option C; the laptop read-only) stands in the design.
|
||||
|
||||
+37
-23
@@ -1,6 +1,6 @@
|
||||
---
|
||||
topic: what runs on it
|
||||
status: proposed
|
||||
status: accepted
|
||||
date: 2026-10-08
|
||||
deciders: jochen
|
||||
reconstructed: false
|
||||
@@ -56,47 +56,61 @@ Rejected:
|
||||
into the library for no use. The laptop, behind a VPN client, would try a mount whenever something
|
||||
looks at the path. Kept as a setting, not as the default.
|
||||
7. **One `file-share` seat for NFS and SMB.** The two share an intent, not a contract (issue 169).
|
||||
8. **The pool's root as one share**, with `crossmnt` and every client mapped to the library's owner. That
|
||||
keeps the `0700` backup repository closed to clients, but it still exports it, and every other folder
|
||||
at the root becomes writable from every node. The operator chose one share per folder on 2026-10-08,
|
||||
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.
|
||||
|
||||
## Decision
|
||||
|
||||
1. **A node's share is a provision.** The module `nfs-server` holds the node seat `nfs-share` and
|
||||
provides the provision `nfs-share`. Its assignment's settings list the shares: a name, the path
|
||||
(an `access`, ADR 0051: the module never creates, chowns or removes it), and read-only or
|
||||
read-write. It writes its own file in `/etc/exports.d`, never `/etc/exports`, and listens on 2049/tcp
|
||||
1. **A node's share is a provision.** The module `nfs-server` holds the node seat `node-nfs-server` and
|
||||
provides the provision `nfs-share`. Its assignment's setting `shares` lists the shares: a name, the
|
||||
path, and read-only or read-write. The path is the operator's data (ADR 0051): the module never
|
||||
creates, chowns, chmods or removes anything under it. It writes its own file in `/etc/exports.d`, never `/etc/exports`, and listens on 2049/tcp
|
||||
from the private network. It serves NFS version 4 only, so no other port opens.
|
||||
2. **Every export squashes every client to the share's owner.** `all_squash`, with `anonuid` and
|
||||
`anongid` read from the exported path's owner on the server (`${access:<id>:uid}`), never written as
|
||||
`anongid` read by the module from the exported path's owner on the server, never written as
|
||||
numbers. The clients are `${machine:mesh-range}` and nothing else. A file written from any node lands
|
||||
owned by the library's owner, and no client acts as any other account on the server.
|
||||
3. **The home server exports one share per folder, not the pool's root**: the library and the
|
||||
downloads. The backup repository and the archive are not exported. (If the operator wants the pool's
|
||||
root as one share, it is exported with `crossmnt` and the same squashing, which keeps the `0700`
|
||||
backup repository closed. That is the alternative this record names, and the operator's to choose.)
|
||||
3. **One share per folder, never a pool's root** (the operator's choice, 2026-10-08): on the home server,
|
||||
the library and the downloads. The backup repository and the archive are not exported. The module
|
||||
refuses a share that is `/` or that holds a backup repository at its path or one level below, so a
|
||||
setting cannot export one by mistake.
|
||||
4. **A node mounts through one module.** The module `mounts` holds the node seat `node-mounts`. For each
|
||||
share it writes a `.mount` unit and an `.automount` unit, never an fstab line. Only the automount is
|
||||
enabled, so nothing mounts at boot and nothing can fail the boot. Each mount has an idle timeout
|
||||
(default 10 minutes) and a mount timeout (default 30 s; 10 s for an occasional source). A read-write
|
||||
NFS share is mounted `hard`. A mount point is always an autofs mount, so nothing writes into the
|
||||
empty directory under it (issue 169's data-loss case).
|
||||
5. **Only the nodes that ask get a share.** A node mounts what its `mounts` assignment lists, plus every
|
||||
share a module on that node requires. The list may be set once at the mesh-wide settings layer, which
|
||||
is how "every node" is said, and a node's own layer can leave a share out. The proposal is to assign
|
||||
`mounts` to the workstation and the laptop, and not to the control-node.
|
||||
5. **Only the nodes that ask get a share.** A node mounts what its `mounts` assignment's setting `shares`
|
||||
lists. The list may be set once at the mesh-wide settings layer, which is how "every node" is said,
|
||||
and a node's own layer can leave a share out. Which nodes are assigned, and whether the laptop mounts
|
||||
read-only, is decided at assignment, which is the operator's; the recommendation is the workstation
|
||||
and the laptop (read-only), not the control-node.
|
||||
6. **An occasional source is a share with presence `occasional`.** A device that is not a node, such as
|
||||
the gaming device or a USB disk, is described in the `mounts` assignment's settings. Its absence is a
|
||||
the gaming device or a USB disk, is described in the `mounts` assignment's setting `sources`. Its absence is a
|
||||
state, `absent`, not a fault. It is mounted on access when it is there, and it never fails the boot or
|
||||
leaves a failed unit. Mounting when a device appears on the network waits for issue 154 (mDNS is
|
||||
dropped by the packet filter today).
|
||||
7. **A credential for a share is an operator-delivered secret** (ADR 0092), accepted into the vault and
|
||||
unsealed on the node as a file (ADR 0086), which the mount unit names. No password is written in a
|
||||
7. **A credential for a share is an operator-delivered secret** (ADR 0092): the `mounts` module's own
|
||||
secret `smb-credentials`, issued outside the mesh (ADR 0228), given with `secret accept`, kept by the
|
||||
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`.
|
||||
8. **Health:** a share that should always be reachable and is not, for 5 minutes, raises one plain
|
||||
condition, `share-unreachable`, naming the share, the node and since when. An occasional source that
|
||||
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
|
||||
added to the controller. An occasional source that
|
||||
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. `nfs-server` keeps its exports
|
||||
and clients, and `mounts` keeps each share's state, in their own key-value buckets (ADR 0201). They
|
||||
ask each other by request and reply. Neither reads the other's files (ADR 0260).
|
||||
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).
|
||||
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
|
||||
@@ -106,8 +120,8 @@ Rejected:
|
||||
|
||||
- The workstation's library comes back over the private network, and the two failed units stop
|
||||
appearing at boot.
|
||||
- Two node seats join the mesh's closed set, `nfs-share` and `node-mounts`. That is a controller
|
||||
change.
|
||||
- Two node seats join the mesh's closed set, `node-nfs-server` and `node-mounts`. That is a controller
|
||||
change. The provision `nfs-share` needs none.
|
||||
- What got harder: every client writes to a share as one account, so there is no per-person
|
||||
permission on it. Traffic to the library crosses the private network's encryption even on the home
|
||||
network; the workstation is on Wi-Fi, so the tunnel is not the limit there, and this is measured at
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
---
|
||||
layer: to-be
|
||||
status: proposed
|
||||
code: []
|
||||
status: in-progress
|
||||
code:
|
||||
- mesh-controller internal/catalogue (the seats node-nfs-server and node-mounts)
|
||||
- mesh-catalog modules/nfs-server
|
||||
- mesh-catalog modules/mounts
|
||||
updated: 2026-10-08
|
||||
decisions:
|
||||
- 02-DECISIONS/0263-a-share-is-a-provision-a-node-mounts-it-on-demand-and-only-nodes-that-ask-get-it.md
|
||||
@@ -21,17 +24,17 @@ from [research 038](../../01-RESEARCH/038-shares-and-mounts-the-mesh-manages/00-
|
||||
|
||||
A **share** is a directory one machine offers to others over the network. A **mount** is a share made
|
||||
visible at a path on one node. An **occasional source** is a share on a device that comes and goes.
|
||||
These three words are proposed for the glossary with this design.
|
||||
The glossary holds the three words.
|
||||
|
||||
## The parts
|
||||
|
||||
```
|
||||
home server workstation, laptop (the nodes that ask)
|
||||
|
||||
nfs-server (seat nfs-share, node) mounts (seat node-mounts, node)
|
||||
nfs-server (seat node-nfs-server) mounts (seat node-mounts, node)
|
||||
├ /etc/exports.d/<its file> ├ <path>.automount enabled, idle 10 min
|
||||
│ one line per share, to the ├ <path>.mount not enabled, timeout 30 s
|
||||
│ private network's range only, │ What = binding at:path, options
|
||||
│ private network's range only, │ What = <server>:<path>, options
|
||||
│ all_squash to the share's owner ├ credentials file (vault, ADR 0086),
|
||||
├ nfs-server.service, NFS v4 only │ for an SMB source only
|
||||
├ listens 2049/tcp from mesh ├ its process: probes each source,
|
||||
@@ -39,7 +42,7 @@ These three words are proposed for the glossary with this design.
|
||||
▲ └ tools: mounts_list, mounts_exports,
|
||||
│ provision nfs-share mount, unmount, test, adopt
|
||||
│ (the controller wires it; ▲
|
||||
│ binding: at, path, options) │ settings: shares wanted, occasional
|
||||
│ binding: the server's name) │ settings: shares wanted, occasional
|
||||
└──────────────── request/reply on the bus ─────────┘ sources (mesh-wide and node layers)
|
||||
|
||||
gaming device (not a node) ── SMB share ──► an occasional source in the workstation's mounts settings
|
||||
@@ -47,61 +50,73 @@ These three words are proposed for the glossary with this design.
|
||||
|
||||
| part | where | does |
|
||||
|---|---|---|
|
||||
| seats `nfs-share` and `node-mounts` | the controller's closed set | node seats, capacity 1, with their verbs |
|
||||
| `nfs-server` | the catalogue | holds `nfs-share`, provides the provision, writes its export file, runs the service, opens 2049 to the private network |
|
||||
| seats `node-nfs-server` and `node-mounts` | the controller's closed set | node seats, capacity 1, with their verbs |
|
||||
| `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 private-network name, the path and the options |
|
||||
| the credential | the vault | an operator-delivered secret per SMB source, unsealed on the node as a file |
|
||||
| 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 |
|
||||
|
||||
## 1. The server: `nfs-server`
|
||||
|
||||
- **Settings.** A list of shares, each with a name, the path as an `access` (the operator's data; the
|
||||
module never creates, chowns or removes it), and `read-write` or `read-only`.
|
||||
- **Its file.** One file in `/etc/exports.d`, written by the node-engine. One line per share:
|
||||
the path, to `${machine:mesh-range}`, with `rw` or `ro`, `sync`, `secure`, `no_subtree_check`,
|
||||
`all_squash`, and `anonuid` and `anongid` taken from the path's owner on the server
|
||||
(`${access:<id>:uid}` and `:gid`). The pool's root is exported only if the operator asks, and then
|
||||
with `crossmnt` as well.
|
||||
- **The service.** `nfs-server.service`, running and enabled, restarted when the file changes.
|
||||
NFS version 4 only, set in the module's drop-in for `nfs.conf`. So only 2049/tcp is needed, declared in
|
||||
`listens` with `from: mesh`.
|
||||
- **The provision.** `nfs-share`, one per share, with `path` and `options` in what it serves.
|
||||
- **Health.** The service is active, and the kernel's export table holds every line of the module's
|
||||
file.
|
||||
- **Its bucket.** The exports as the kernel holds them, and the clients that have each mounted, with
|
||||
since when.
|
||||
- **Tools.** `exports` (r), `clients` (r), `reload` (a), and `test` (r): does this server export this
|
||||
path to this node.
|
||||
- **Taking over.** An act, `adopt`, clears a dataset's `sharenfs` property for a path the module now
|
||||
exports, on a person's word, and says what it did.
|
||||
- **Its setting `shares`.** A list of `name=path`, each optionally `:ro`, separated by spaces: on the
|
||||
home server the library and the downloads. A share's path is the operator's data (ADR 0051); the
|
||||
module never creates, chowns, chmods or removes anything under it.
|
||||
- **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`.
|
||||
- **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.
|
||||
- **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 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.
|
||||
|
||||
## 2. The client: `mounts`
|
||||
|
||||
- **Settings.** `shares`: the names of the `nfs-share` provisions this node mounts, each with a mount
|
||||
point and optional `read-only`. `sources`: occasional sources, each with a protocol (`smb` or `disk`),
|
||||
a name or a disk's UUID, a share name, a mount point, and the name of its credential. Both may be set
|
||||
at the mesh-wide layer, which is how "every node" is said, and changed at a node's own layer.
|
||||
- **A share a module requires.** When a module on the node requires `nfs-share`, the controller adds that
|
||||
share to the node's `mounts` set, with the mount point that module's assignment gives.
|
||||
- **The unit pair.** For each share, a `.mount` and an `.automount`, named by the escaped mount point and
|
||||
written in `/etc/systemd/system` by the node-engine:
|
||||
- the `.automount` is enabled under `remote-fs.target`, with `TimeoutIdleSec=10min`;
|
||||
- the `.mount` is not enabled; it has `TimeoutSec=30s` (10 s for an occasional source), `_netdev`,
|
||||
and an order after the private network's link;
|
||||
- for NFS: `vers=4.2`, `hard,timeo=150,retrans=3` when read-write, `soft` allowed when read-only;
|
||||
- for SMB: `credentials=<the unsealed file>`, `uid` and `gid` of the node's account that uses it;
|
||||
- for a disk: `BindsTo=` the disk's device unit, so the mount goes with the disk.
|
||||
- **States**, kept in its bucket per share, with since when: `armed` (automount waiting), `mounted`,
|
||||
`absent` (an occasional source not there), `unreachable` (should be there and is not), `failed`
|
||||
(the mount was tried and refused, with the error).
|
||||
- **Its process.** Probes each source's port every 30 s, and the device unit for a disk. It never
|
||||
touches a mount point, so a hung server cannot hang it.
|
||||
- **Health.** Every automount is armed, and every share that is not occasional is `armed` or `mounted`.
|
||||
An occasional source that is `absent` is healthy.
|
||||
- **Tools.** `mounts_list` (r), `mounts_exports` (r), `test` (r), `mount` and `unmount` (a), `adopt` (a).
|
||||
- **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.
|
||||
- **Its setting `sources`.** Occasional sources: `name=smb://<host>/<share>@<mountpoint>` or
|
||||
`name=disk:<uuid>@<mountpoint>`, each optionally `:ro`.
|
||||
- **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.
|
||||
- **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
|
||||
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
|
||||
after the network is online;
|
||||
- NFS: version 4.2, `hard,timeo=150,retrans=3` when read-write; `ro,soft` when read-only;
|
||||
- SMB: `credentials=<its file>`, `iocharset=utf8`, `nofail`;
|
||||
- a disk: bound to the disk's device unit, so the mount goes with the disk.
|
||||
- **States**, per share, with since when, in its bucket `shares` (one entry per machine): `armed`,
|
||||
`mounted`, `absent` (an occasional source not there), `unreachable` (should be there and is not),
|
||||
`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
|
||||
*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.
|
||||
|
||||
## 3. Which nodes get which share
|
||||
|
||||
Which nodes are assigned `mounts`, and with which settings, is decided at assignment, which is the
|
||||
operator's. The recommendation:
|
||||
|
||||
| node | gets | why |
|
||||
|---|---|---|
|
||||
| home server | none; it holds `nfs-server` | the files are local; the media modules keep their local bind mounts |
|
||||
@@ -113,8 +128,11 @@ These three words are proposed for the glossary with this design.
|
||||
|
||||
| condition | raised when | cleared when |
|
||||
|---|---|---|
|
||||
| `share-unreachable` (warning) | a share that is not occasional has been `unreachable` or `failed` for 5 minutes | the next probe reaches it |
|
||||
| the server's health | the export table lacks a declared line, or the service is down | the next look finds both right |
|
||||
| the `mounts` module's health, finding *share unreachable* | a share that is not occasional has been `unreachable` or `failed` for 5 minutes, or an automount is not armed | the next look finds it reached and armed |
|
||||
| the `nfs-server` module's health | a share is missing from the kernel's export table, a share was refused, or the service is down | the next look finds all three right |
|
||||
|
||||
Both are the module's health under ADR 0240, judged by its tool; the controller raises and clears the
|
||||
condition as for any module, and adds no kind of its own.
|
||||
|
||||
Each says the share, the node and since when: *"The home server's share `library` is not reachable
|
||||
from the workstation since 19:21."* An absent occasional source raises nothing; `mounts_list` says
|
||||
@@ -122,7 +140,7 @@ from the workstation since 19:21."* An absent occasional source raises nothing;
|
||||
|
||||
## 5. Migration
|
||||
|
||||
1. Build and deliver `mounts` (read-only first, already proposed) and `nfs-server`, and add the two seats.
|
||||
1. Build and deliver the two seats, `nfs-server` and `mounts`.
|
||||
2. Assign `nfs-server` to the home server with the library and the downloads. Check `exports` and
|
||||
`test` from the workstation.
|
||||
3. `adopt` on the home server clears the library's `sharenfs` property. The underlay range leaves the
|
||||
|
||||
@@ -51,7 +51,7 @@ document is written and this one's status becomes `implemented`.
|
||||
| [`49-the-mesh-in-domains.md`](49-the-mesh-in-domains.md) | **In progress.** Every concept belongs to one of ten domains, which owns its one word; the glossary is organised by them and is the authority, a retired word is named on its replacement's line with its scope, and two checks hold this repository's documents and the catalogue's tool descriptions to it | [ADR 0244](../../02-DECISIONS/0244-the-mesh-is-described-in-domains-and-one-word-names-one-thing.md), [ADR 0006](../../02-DECISIONS/0006-the-substrate-and-the-control-plane.md), [ADR 0008](../../02-DECISIONS/0008-a-context-owns-its-store.md) |
|
||||
| [`50-split-dns-on-a-machine-with-a-vpn-client.md`](50-split-dns-on-a-machine-with-a-vpn-client.md) | **In progress.** A machine whose VPN client pushes servers of its own runs a resolver of its own, on a node seat, that routes the VPN's domains over its link and every other name to the mesh's resolvers; the VPN client's module carries the adapter | [ADR 0247](../../02-DECISIONS/0247-a-machine-with-a-vpn-client-routes-names-by-domain-through-a-resolver-of-its-own.md), [ADR 0223](../../02-DECISIONS/0223-the-mesh-has-two-resolvers-and-a-machine-lists-only-them.md) |
|
||||
| [`51-the-registries-keep-what-is-named.md`](51-the-registries-keep-what-is-named.md) | **In progress.** The artifact store, the package registry and each machine's container runtime get tools that say what they hold, what names each thing and what could go; every removal is a dry run unless a person asks and says why, and what decides is always a record | [ADR 0251](../../02-DECISIONS/0251-the-registries-say-what-they-hold-and-keep-what-is-named-and-a-machine-keeps-the-image-it-runs-and-the-one-before.md), [ADR 0189](../../02-DECISIONS/0189-the-store-keeps-what-the-records-name.md) |
|
||||
| [`52-shares-and-mounts.md`](52-shares-and-mounts.md) | **Proposed.** A node shares a directory through `nfs-server`, which provides it as a provision to the private network only, squashing every client to the share's owner; a node that asks mounts it through `mounts`, a mount unit and an automount per share, so nothing mounts or fails at boot; a device that comes and goes is an occasional source whose absence is a state | [ADR 0263](../../02-DECISIONS/0263-a-share-is-a-provision-a-node-mounts-it-on-demand-and-only-nodes-that-ask-get-it.md), [ADR 0051](../../02-DECISIONS/0051-shared-data-is-the-operators.md), [ADR 0092](../../02-DECISIONS/0092-an-operator-delivers-a-pair-credential.md) |
|
||||
| [`52-shares-and-mounts.md`](52-shares-and-mounts.md) | **In progress.** A node shares one folder at a time through `nfs-server`, which provides it as a provision to the private network only, mapping every client to the folder's owner; a node that asks mounts it through `mounts`, a mount unit and an automount per share, so nothing mounts or fails at boot; a device that comes and goes is an occasional source whose absence is a state | [ADR 0263](../../02-DECISIONS/0263-a-share-is-a-provision-a-node-mounts-it-on-demand-and-only-nodes-that-ask-get-it.md), [ADR 0051](../../02-DECISIONS/0051-shared-data-is-the-operators.md), [ADR 0092](../../02-DECISIONS/0092-an-operator-delivers-a-pair-credential.md) |
|
||||
|
||||
## Not yet written
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
---
|
||||
status: open
|
||||
status: located
|
||||
opened: 2026-09-30
|
||||
located-in:
|
||||
- mesh-catalog (no module shares a path over the network)
|
||||
- hq 02-DECISIONS (a file-share seat, per ADR 0126, is a module's own to define)
|
||||
fixed-by:
|
||||
amended-design:
|
||||
amended-design: [03-DESIGN/01-to-be/52-shares-and-mounts.md]
|
||||
---
|
||||
|
||||
# 169 — A machine shares its files, and the mesh does not know
|
||||
@@ -129,3 +129,8 @@ the mounted tree, answers it on the consumer's side too.
|
||||
|
||||
Re-checked 2026-10-08: still holds. No module in either catalogue shares a path over the network, and no
|
||||
`nfs-share` or `smb-share` seat exists.
|
||||
|
||||
**Decided 2026-10-08:** [ADR 0263](../../02-DECISIONS/0263-a-share-is-a-provision-a-node-mounts-it-on-demand-and-only-nodes-that-ask-get-it.md),
|
||||
from [research 038](../../01-RESEARCH/038-shares-and-mounts-the-mesh-manages/00-overview.md), which measured
|
||||
the closure this report predicted: the workstation's library mount has timed out at every boot since
|
||||
2026-10-04. The seats are `node-nfs-server` and `node-mounts`, the provision `nfs-share`; Samba waits.
|
||||
|
||||
Reference in New Issue
Block a user