Your decision, ratified: the media library (and shared/pre-existing data) is operator-owned and external; a module declares access, not ownership; the host mounts but owns nothing (no create/chown/reconcile/remove); an absent accessed path is refused clearly; several accessors co-resolve. Status proposed->accepted; index regenerated (records + index checks pass). Implementation lives on the code branches (mesh-control/catalog/host), held for merge after the convergence fix. Claude-Session: https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF
167 lines
11 KiB
Markdown
167 lines
11 KiB
Markdown
---
|
|
topic: what runs on it
|
|
status: accepted
|
|
date: 2026-09-05
|
|
deciders: jochen
|
|
reconstructed: false
|
|
extends: 0030-data-outlives-the-mesh-that-declared-it.md
|
|
---
|
|
|
|
# 51. Shared data is the operator's, and a module is granted access to it
|
|
|
|
## Context
|
|
|
|
**Eight modules declared one filesystem as eight private ones.** The media stack — a library
|
|
server, the acquisition managers for films, series, music and books, a subtitle fetcher and two
|
|
download clients — shares directories on one machine: the download clients write into
|
|
`/services/media/downloads` and the managers read it; the managers write into the libraries and
|
|
the library server reads them. That sharing is the entire point of the stack. Yet each module
|
|
declared every shared directory it touched as its own `directory` resource, with an owner and a
|
|
mode. `/services/media/downloads` was written seven times, as seven private directories that
|
|
happen to be the same path.
|
|
|
|
**The resolver refuses exactly that, and is right to.** Two modules declaring one path on one node
|
|
are refused by name, with no exemption for identical content and no merge — because two owners of
|
|
one path is the class of fault this repository keeps recording ([04-ISSUES/036](../04-ISSUES/036-six-modules-own-what-they-must-share/00-report.md)).
|
|
So the stack as written refuses its own only sensible assignment: all of it on one machine,
|
|
sharing one filesystem. It passed today only because no test co-resolves any two of the eight. The
|
|
first machine assigned two of them together is where the refusal would have surfaced.
|
|
|
|
**The vocabulary had one word for two intentions, and this was already seen.**
|
|
[04-ISSUES/026](../04-ISSUES/026-the-data-directories-are-not-declared/00-report.md) found the
|
|
same gap from the other side and named it precisely: two kinds of mount are spelled identically —
|
|
*the directory my data lives in*, which the mesh creates and owns, and *a facility I was granted*,
|
|
which already exists and the mesh only reaches. That issue deferred inventing a field to tell them
|
|
apart, because doing so is a design decision and it declined to make one to get a check green. This
|
|
is that decision.
|
|
|
|
**A `directory` resource is owned, on every axis.** The host creates it, sets its owner and mode,
|
|
and removes it when it is empty and no longer declared — it *removes what it made and leaves what
|
|
it merely configured* ([ADR 0005](0005-the-node-host.md), [ADR 0030](0030-data-outlives-the-mesh-that-declared-it.md)).
|
|
A shared media library is none of that. It existed before the mesh, several modules read and write
|
|
it at once, and losing it is the one failure that does not recover. It is not any module's
|
|
resource; it is the operator's, and a module only needs to be let at it.
|
|
|
|
## Considered Options
|
|
|
|
1. **Make the stack one module with several containers**, the way the mail system already is. The
|
|
issue raises it directly: is a set of modules that must share a filesystem really one module?
|
|
**Rejected.** The eight are independently assignable and independently useful — a person may run
|
|
the download client without the library server, or the film manager without the music one — and
|
|
folding them into a single module to express a shared directory would make *what a module is*
|
|
turn on an incidental filesystem contract. It also does not generalise: the next pipeline of
|
|
modules handing files to each other on one machine (an ingest folder, a spool, a drop directory)
|
|
would face the same wall and the same wrong remedy.
|
|
|
|
2. **One module owns the directories and the rest `require` them.** **Rejected**, and this is the
|
|
heart of the decision. Nobody owns shared operator data. The library predates the mesh and
|
|
outlives any one module, so making the library server or a manager its owner means unassigning
|
|
that module orphans everyone else's access — and the owner would set the owner and mode of a
|
|
tree it did not create. Ownership is the wrong relationship to model, because the true owner is
|
|
not a module at all.
|
|
|
|
3. **A flag on a directory resource** — `external: true`, or an owner of `operator`. **Rejected.**
|
|
It overloads one shape with a boolean that inverts every one of its semantics: created becomes
|
|
*must already exist*, owned becomes *touch nothing*, removed-when-empty becomes *never removed*.
|
|
That is the *two-kinds-spelled-identically* trap of 04-ISSUES/026 reintroduced with a single
|
|
quiet field — a reviewer reading `type: directory` would have to check one boolean elsewhere to
|
|
know whether the mesh owns the thing at all.
|
|
|
|
4. **A distinct `accesses` declaration, separate from resources.** **Adopted.**
|
|
|
|
## Decision
|
|
|
|
**Shared, pre-existing data is operator-owned and external. The mesh does not create it, does not
|
|
set its owner or mode, does not reconcile it and does not remove it.** A media library, a download
|
|
spool, an ingest directory is the operator's, and the mesh is a guest in it.
|
|
|
|
**A module declares that it needs *access* to such a path, not that it owns a resource there.** The
|
|
manifest field is `accesses`: a list of `{path, mode}`, where mode is `read` or `read-write` and
|
|
absent narrows to `read` — the safe default, because the danger with an access is being given more
|
|
than was meant, not less. A module's own configuration and state directories stay owned
|
|
`directory` resources; only the shared, pre-existing paths become accesses.
|
|
|
|
**The host mounts an accessed path and owns nothing about it.** It reaches the machine as a new
|
|
declaration shape, `access`, distinct from `directory`. The host confirms the path is present and
|
|
does nothing else — no create, no chown, no mode, no removal.
|
|
|
|
**An accessed path absent at apply time is refused, clearly, not created.** The mesh does not own
|
|
it, so conjuring it would be a lie the host then acts on — and specifically the lie 04-ISSUES/026
|
|
records, where a bind mount whose source does not exist is made by the container runtime as root
|
|
with the wrong ownership. The host says the operator must provide the path instead.
|
|
|
|
**Several modules accessing one path is normal, and never refused.** The duplicate-path refusal is
|
|
about *ownership*, not *use*: it applies to resources a module owns and to those alone. An access
|
|
is not a resource and never enters the check, so the eight-module stack co-resolves. What stays
|
|
refused is genuine rivalry — two modules owning one path — and the new contradiction it exposes: a
|
|
path one module owns while another merely accesses it, because that asserts both that the mesh owns
|
|
the directory and that the operator does.
|
|
|
|
This is a decision and not a patch because it settles *what a module may say about a path it did
|
|
not make*, which every co-located file-handoff in the catalogue now and later depends on — and
|
|
because it draws the ownership line [ADR 0030](0030-data-outlives-the-mesh-that-declared-it.md)
|
|
started: the host owns what it made and keeps what it merely configured, and this adds the third
|
|
case it did not have a word for — what it neither made nor configured, and must not touch.
|
|
|
|
### How each claim is checked
|
|
|
|
- **The stack co-resolves.** A control-plane unit test assigns two modules that declare access to
|
|
one path on one node and asserts no refusal — the exact case the resolver refuses when the same
|
|
path is owned. The mirror test, two modules *owning* one path, still refuses, so the sharing
|
|
vocabulary does not weaken the rule it sits beside.
|
|
- **Ownership and access cannot both be claimed of one path.** A unit test asserts the resolver
|
|
refuses a path one module owns and another accesses, naming both.
|
|
- **Absent is refused, not created.** A host unit test applies an access to a path that does not
|
|
exist and asserts a clear refusal that names the operator, and that nothing was created.
|
|
- **Present is confirmed and nothing moves.** A host unit test applies an access to an existing
|
|
directory and asserts the apply reports no change and disturbs nothing.
|
|
- **Undeclaring never removes.** A host unit test drops a previously declared access and asserts
|
|
the operator's directory and its contents are left exactly as they were — the data-loss failure
|
|
[ADR 0030](0030-data-outlives-the-mesh-that-declared-it.md) exists to prevent, on a directory the
|
|
mesh never made.
|
|
- **Every media manifest is corrected.** No `/services/media/*` path is an owned `directory`
|
|
resource in any of the eight; each is an `accesses` entry, and each module's own config and state
|
|
directories remain owned. Checked by the control-plane manifest parser, which now understands
|
|
`accesses` and refuses a malformed one.
|
|
|
|
## Consequences
|
|
|
|
**A shared filesystem between co-located modules now has a vocabulary**, and it is not the media
|
|
stack's alone: any pipeline handing files to a neighbour on one machine — an ingest directory, a
|
|
spool, a drop folder — says *I access this operator path* rather than *I own this directory*, and
|
|
several of them may say it of one path.
|
|
|
|
**Unassigning a module that reached shared data leaves the data.** Correct, and the same trade
|
|
[ADR 0030](0030-data-outlives-the-mesh-that-declared-it.md) made for owned directories: removing
|
|
data is a person's act, done knowingly, not a side effect of unassignment.
|
|
|
|
**The operator must provision the shared paths before the stack is applied**, and a machine that
|
|
lacks one is told plainly which. That is a real new obligation, and it is the right one: the mesh
|
|
cannot own what predates it, so it cannot create it either, and saying so at apply time beats a
|
|
directory conjured as root and a service that half-works.
|
|
|
|
**The host vocabulary grew by one shape**, which is a cost — every added shape widens what a
|
|
compromised control plane can express ([ADR 0005](0005-the-node-host.md)). It is a narrow one: an
|
|
`access` is confirmed by a stat and grants the host no new action. It earns its place by letting
|
|
the host refuse to create what it must not own, which no existing shape could say.
|
|
|
|
**A path can be both owned and accessed only by refusal.** If a future manifest declares one path
|
|
as an owned directory in one module and an access in another, the resolver refuses it rather than
|
|
guessing which is meant — the two assertions about who owns the data cannot both hold.
|
|
|
|
## References
|
|
|
|
- [04-ISSUES/036](../04-ISSUES/036-six-modules-own-what-they-must-share/00-report.md) — six (in
|
|
fact eight) modules own what they must share; the problem this resolves
|
|
- [04-ISSUES/026](../04-ISSUES/026-the-data-directories-are-not-declared/00-report.md) — the two
|
|
kinds of mount spelled identically, which deferred this field to a decision
|
|
- [ADR 0030](0030-data-outlives-the-mesh-that-declared-it.md) — data outlives the mesh; the host
|
|
keeps what it did not make. This record adds the case it had no word for
|
|
- [ADR 0005](0005-the-node-host.md) — the host removes what it made and leaves what it merely
|
|
configured; the vocabulary is finite and every shape is a security decision
|
|
- [ADR 0040](0040-what-a-module-is.md) — what a module is; an access is a new thing a module may
|
|
say about the machine it lands on
|
|
- mesh-control `feat/shared-data-access`, mesh-catalog `feat/media-access-not-ownership`,
|
|
mesh-host `feat/mount-operator-owned` — the mechanism, the corrected manifests, and the host
|
|
shape
|