diff --git a/00-META/glossary.md b/00-META/glossary.md index e177d8f2..bd9eae52 100644 --- a/00-META/glossary.md +++ b/00-META/glossary.md @@ -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 diff --git a/01-RESEARCH/038-shares-and-mounts-the-mesh-manages/00-overview.md b/01-RESEARCH/038-shares-and-mounts-the-mesh-manages/00-overview.md index 48025484..0298b153 100644 --- a/01-RESEARCH/038-shares-and-mounts-the-mesh-manages/00-overview.md +++ b/01-RESEARCH/038-shares-and-mounts-the-mesh-manages/00-overview.md @@ -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. diff --git a/01-RESEARCH/038-shares-and-mounts-the-mesh-manages/02-options.md b/01-RESEARCH/038-shares-and-mounts-the-mesh-manages/02-options.md index 25294f03..df6fb9c5 100644 --- a/01-RESEARCH/038-shares-and-mounts-the-mesh-manages/02-options.md +++ b/01-RESEARCH/038-shares-and-mounts-the-mesh-manages/02-options.md @@ -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. diff --git a/02-DECISIONS/0263-a-share-is-a-provision-a-node-mounts-it-on-demand-and-only-nodes-that-ask-get-it.md b/02-DECISIONS/0263-a-share-is-a-provision-a-node-mounts-it-on-demand-and-only-nodes-that-ask-get-it.md index 163d8344..e4d9298f 100644 --- a/02-DECISIONS/0263-a-share-is-a-provision-a-node-mounts-it-on-demand-and-only-nodes-that-ask-get-it.md +++ b/02-DECISIONS/0263-a-share-is-a-provision-a-node-mounts-it-on-demand-and-only-nodes-that-ask-get-it.md @@ -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::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 diff --git a/03-DESIGN/01-to-be/52-shares-and-mounts.md b/03-DESIGN/01-to-be/52-shares-and-mounts.md index 20d5f098..6251e2a3 100644 --- a/03-DESIGN/01-to-be/52-shares-and-mounts.md +++ b/03-DESIGN/01-to-be/52-shares-and-mounts.md @@ -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/ ├ .automount enabled, idle 10 min │ one line per share, to the ├ .mount not enabled, timeout 30 s - │ private network's range only, │ What = binding at:path, options + │ private network's range only, │ What = :, 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::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=`, `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:///@` or + `name=disk:@`, 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 mounts smb-credentials + --from `. 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=`, `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 diff --git a/03-DESIGN/01-to-be/README.md b/03-DESIGN/01-to-be/README.md index 47a585da..0ad621dc 100644 --- a/03-DESIGN/01-to-be/README.md +++ b/03-DESIGN/01-to-be/README.md @@ -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 diff --git a/04-ISSUES/169-a-machine-shares-its-files-and-the-mesh-does-not-know/00-report.md b/04-ISSUES/169-a-machine-shares-its-files-and-the-mesh-does-not-know/00-report.md index 45580f51..a6bc7ac4 100644 --- a/04-ISSUES/169-a-machine-shares-its-files-and-the-mesh-does-not-know/00-report.md +++ b/04-ISSUES/169-a-machine-shares-its-files-and-the-mesh-does-not-know/00-report.md @@ -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.