diff --git a/checks/owned/go.mod b/checks/owned/go.mod new file mode 100644 index 00000000..d1c4739a --- /dev/null +++ b/checks/owned/go.mod @@ -0,0 +1,3 @@ +module git.novox.be/novox/mesh-catalog/checks/owned + +go 1.22 diff --git a/checks/owned/main.go b/checks/owned/main.go new file mode 100644 index 00000000..09512d5d --- /dev/null +++ b/checks/owned/main.go @@ -0,0 +1,236 @@ +// Command owned judges how the catalogue's directories and archives treat files they did not write (novox/hq +// ADR 0422 §8, issue 1024). It refuses, naming the module, the resource and the reason: +// +// 1. a directory or archive without `unknown`, or with a value but move-out, module or program; +// +// 2. `move-out` on a path not under ${machine:account-home} or ${machine:agent-home}; +// +// 3. `knows` with an empty, absolute, `.`/`..`/empty-component or repeated path; +// +// 4. a path one module declares (a resource's path, or a fact with "home": true) lying inside a directory of +// another module, after ${dir:} is resolved within each module and both homes are read as one root. +// Archives are not containers for this rule. This holds for ~/.ssh what checks/sshdir held (ADR 0415 §6). +// +// go run . +// +// Exits 1 on a finding. +package main + +import ( + "encoding/json" + "fmt" + "os" + "path" + "path/filepath" + "regexp" + "sort" + "strings" +) + +// Home is the one root both machine homes are read as. +const Home = "${home}" + +var ( + ofDir = regexp.MustCompile(`\$\{dir:([a-z0-9][a-z0-9_-]*)\}`) + homeVar = strings.NewReplacer("${machine:account-home}", Home, "${machine:agent-home}", Home) + values = map[string]bool{"move-out": true, "module": true, "program": true} +) + +// Finding is one refusal. +type Finding struct { + Rule int + Module, ID, Why string +} + +type resource struct { + ID, Type, Path, Unknown string + Knows []string + HasKnows bool +} + +type parsed struct { + Module string + Resources []resource + Facts []string // paths of facts with "home": true, under the home +} + +func load(file string) (*parsed, error) { + raw, err := os.ReadFile(file) + if err != nil { + return nil, err + } + var m struct { + Module string `json:"module"` + Resources []struct { + ID string `json:"id"` + Type string `json:"type"` + Path string `json:"path"` + Unknown *string `json:"unknown"` + Knows []string `json:"knows"` + } `json:"resources"` + Facts map[string]struct { + Path string `json:"path"` + Home bool `json:"home"` + } `json:"facts"` + } + if err := json.Unmarshal(raw, &m); err != nil { + return nil, fmt.Errorf("%s: %w", file, err) + } + out := &parsed{Module: m.Module} + if out.Module == "" { + out.Module = filepath.Base(filepath.Dir(file)) + } + for _, r := range m.Resources { + x := resource{ID: r.ID, Type: r.Type, Path: r.Path, Knows: r.Knows, HasKnows: r.Knows != nil} + if r.Unknown != nil { + x.Unknown = *r.Unknown + if x.Unknown == "" { + x.Unknown = "(empty)" + } + } + out.Resources = append(out.Resources, x) + } + keys := make([]string, 0, len(m.Facts)) + for k := range m.Facts { + keys = append(keys, k) + } + sort.Strings(keys) + for _, k := range keys { + if f := m.Facts[k]; f.Home && f.Path != "" { + out.Facts = append(out.Facts, f.Path) + } + } + return out, nil +} + +// norm resolves ${dir:} from the module's own resources, reads both homes as one root and cleans the path. +func norm(p string, dirs map[string]string) string { + for i := 0; i < 8 && ofDir.MatchString(p); i++ { + p = ofDir.ReplaceAllStringFunc(p, func(w string) string { + if to, ok := dirs[ofDir.FindStringSubmatch(w)[1]]; ok { + return to + } + return w + }) + } + p = homeVar.Replace(p) + if p == "" { + return "" + } + return path.Clean(p) +} + +func underHome(p string) bool { return strings.HasPrefix(p, Home+"/") } + +// badKnows says why a knows entry is refused, or "". +func badKnows(k string) string { + if k == "" { + return "is empty" + } + if strings.HasPrefix(k, "/") { + return "is absolute" + } + for _, c := range strings.Split(strings.TrimSuffix(k, "/"), "/") { + if c == "" || c == "." || c == ".." { + return "has an empty, . or .. component" + } + } + return "" +} + +type declared struct { + module, id, path string +} + +// Check reads every manifest under root/modules and answers what it refuses. +func Check(root string) ([]Finding, error) { + files, err := filepath.Glob(filepath.Join(root, "modules", "*", "module.json")) + if err != nil { + return nil, err + } + sort.Strings(files) + var out []Finding + var paths []declared + type container struct{ module, path string } + var containers []container + for _, file := range files { + m, err := load(file) + if err != nil { + return nil, err + } + dirs := map[string]string{} + for _, r := range m.Resources { + if r.ID != "" && r.Path != "" { + dirs[r.ID] = r.Path + } + } + for _, r := range m.Resources { + p := norm(r.Path, dirs) + if p != "" { + paths = append(paths, declared{m.Module, r.ID, p}) + } + if r.Type != "directory" && r.Type != "archive" { + continue + } + if r.Type == "directory" && p != "" { + containers = append(containers, container{m.Module, p}) + } + add := func(rule int, why string) { out = append(out, Finding{rule, m.Module, r.ID, why}) } + switch { + case r.Unknown == "": + add(1, "a "+r.Type+" without `unknown` (move-out, module or program)") + case !values[r.Unknown]: + add(1, fmt.Sprintf("`unknown` is %q, not move-out, module or program", r.Unknown)) + case r.Unknown == "move-out" && !underHome(p): + add(2, fmt.Sprintf("`move-out` on %q, which is not under ${machine:account-home} or ${machine:agent-home}", r.Path)) + } + seen := map[string]bool{} + for _, k := range r.Knows { + if why := badKnows(k); why != "" { + add(3, fmt.Sprintf("`knows` entry %q %s", k, why)) + continue + } + c := strings.TrimSuffix(k, "/") + if seen[c] { + add(3, fmt.Sprintf("`knows` entry %q is repeated", k)) + } + seen[c] = true + } + if r.HasKnows && len(r.Knows) == 0 { + add(3, "`knows` is empty") + } + } + for _, f := range m.Facts { + if p := norm(Home+"/"+f, nil); p != "" { + paths = append(paths, declared{m.Module, "fact " + f, p}) + } + } + } + for _, d := range paths { + for _, c := range containers { + if c.module != d.module && (d.path == c.path || strings.HasPrefix(d.path, c.path+"/")) { + out = append(out, Finding{4, d.module, d.id, fmt.Sprintf("declares %q, inside a directory of %s (%q)", d.path, c.module, c.path)}) + } + } + } + return out, nil +} + +func main() { + root := "." + if len(os.Args) > 1 { + root = os.Args[1] + } + found, err := Check(root) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + for _, f := range found { + fmt.Printf("%s: %s: %s (rule %d, novox/hq ADR 0422 §8, issue 1024)\n", f.Module, f.ID, f.Why, f.Rule) + } + if len(found) > 0 { + os.Exit(1) + } + fmt.Println("owned paths: every directory and archive says how it treats unknown files; no module declares inside another's directory") +} diff --git a/checks/owned/main_test.go b/checks/owned/main_test.go new file mode 100644 index 00000000..0882ba47 --- /dev/null +++ b/checks/owned/main_test.go @@ -0,0 +1,132 @@ +package main + +import ( + "os" + "path/filepath" + "strings" + "testing" +) + +func manifest(t *testing.T, root, module, body string) { + t.Helper() + dir := filepath.Join(root, "modules", module) + if err := os.MkdirAll(dir, 0o755); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(filepath.Join(dir, "module.json"), []byte(body), 0o644); err != nil { + t.Fatal(err) + } +} + +func check(t *testing.T, root string) []Finding { + t.Helper() + found, err := Check(root) + if err != nil { + t.Fatal(err) + } + return found +} + +// only asserts exactly one finding, of the rule, naming the module and the resource (issue 1024). +func only(t *testing.T, found []Finding, rule int, module, id string) { + t.Helper() + if len(found) != 1 || found[0].Rule != rule || found[0].Module != module || found[0].ID != id || found[0].Why == "" { + t.Fatalf("found %+v; want one finding of rule %d for %s/%s with a reason", found, rule, module, id) + } +} + +func dir(id, path, rest string) string { + return `{"id":"` + id + `","type":"directory","path":"` + path + `"` + rest + `}` +} + +func mod(name string, resources ...string) string { + return `{"module":"` + name + `","resources":[` + strings.Join(resources, ",") + `]}` +} + +func TestMissingOrUnknownValueIsRefused(t *testing.T) { + root := t.TempDir() + manifest(t, root, "a", mod("a", dir("d", "/var/lib/a", ""))) + only(t, check(t, root), 1, "a", "d") + + root = t.TempDir() + manifest(t, root, "a", mod("a", `{"id":"z","type":"archive","path":"/opt/a","unknown":"delete"}`)) + only(t, check(t, root), 1, "a", "z") +} + +func TestMoveOutOutsideAHomeIsRefused(t *testing.T) { + root := t.TempDir() + manifest(t, root, "a", mod("a", dir("d", "/etc/a.d", `,"unknown":"move-out"`))) + only(t, check(t, root), 2, "a", "d") +} + +func TestMoveOutUnderAHomeIsAllowed(t *testing.T) { + root := t.TempDir() + manifest(t, root, "a", mod("a", + dir("d", "${machine:account-home}/.config/a", `,"unknown":"move-out"`), + dir("e", "${dir:d}/sub", `,"unknown":"move-out"`), + dir("f", "${machine:agent-home}/x", `,"unknown":"move-out"`), + dir("g", "/var/lib/a", `,"unknown":"program"`), + dir("h", "${machine:account-home}/.ssh", `,"unknown":"module"`))) + if found := check(t, root); len(found) != 0 { + t.Fatalf("refused %+v", found) + } +} + +func TestBadKnowsIsRefused(t *testing.T) { + for _, knows := range []string{`[""]`, `["/abs"]`, `["a/../b"]`, `["./a"]`, `["a//b"]`, `["a","a"]`, `["a","a/"]`} { + root := t.TempDir() + manifest(t, root, "a", mod("a", dir("d", "${machine:account-home}/.config/a", `,"unknown":"move-out","knows":`+knows))) + only(t, check(t, root), 3, "a", "d") + } + root := t.TempDir() + manifest(t, root, "a", mod("a", dir("d", "${machine:account-home}/.config/a", `,"unknown":"move-out","knows":["forge.credentials","kept/"]`))) + if found := check(t, root); len(found) != 0 { + t.Fatalf("refused %+v", found) + } +} + +func TestAnotherModulesPathInsideADirectoryIsRefused(t *testing.T) { + root := t.TempDir() + manifest(t, root, "owner", mod("owner", dir("ssh", "${machine:account-home}/.ssh", `,"unknown":"module"`))) + manifest(t, root, "intruder", mod("intruder", `{"id":"f","type":"file","path":"${machine:account-home}/.ssh/config.d/20"}`)) + manifest(t, root, "viahome", mod("viahome", `{"id":"g","type":"file","path":"${machine:agent-home}/.ssh/x"}`)) + manifest(t, root, "facty", `{"module":"facty","facts":{"hosts":{"path":".ssh/config.d/00","home":true}}}`) + manifest(t, root, "outside", mod("outside", `{"id":"o","type":"file","path":"${machine:account-home}/.sshrc"}`)) + got := map[string]int{} + for _, f := range check(t, root) { + if f.Rule != 4 || f.Why == "" { + t.Fatalf("finding %+v", f) + } + got[f.Module]++ + } + if len(got) != 3 || got["intruder"] != 1 || got["viahome"] != 1 || got["facty"] != 1 { + t.Fatalf("refused %v; want intruder, viahome, facty", got) + } +} + +func TestDirReferencesAreResolvedPerModule(t *testing.T) { + root := t.TempDir() + manifest(t, root, "owner", mod("owner", dir("base", "${machine:account-home}/.config/o", `,"unknown":"move-out"`))) + manifest(t, root, "other", mod("other", + dir("base", "${machine:account-home}/.config/p", `,"unknown":"move-out"`), + `{"id":"f","type":"file","path":"${dir:base}/x"}`)) + if found := check(t, root); len(found) != 0 { + t.Fatalf("refused %+v", found) + } + manifest(t, root, "third", mod("third", + dir("d", "${machine:account-home}/.config/t", `,"unknown":"move-out"`), + `{"id":"f","type":"file","path":"${dir:d}/../o/x"}`)) + only(t, check(t, root), 4, "third", "f") +} + +func TestAnArchiveIsNotAContainerAndSameModuleIsFine(t *testing.T) { + root := t.TempDir() + manifest(t, root, "a", mod("a", + `{"id":"arc","type":"archive","path":"/opt/a","unknown":"program"}`, + dir("d", "/opt/a2", `,"unknown":"program"`), + `{"id":"f","type":"file","path":"/opt/a2/x"}`)) + manifest(t, root, "b", mod("b", `{"id":"f","type":"file","path":"/opt/a/x"}`)) + if found := check(t, root); len(found) != 0 { + t.Fatalf("refused %+v", found) + } +} diff --git a/merge-check.sh b/merge-check.sh index 8afe1cc1..a466457d 100755 --- a/merge-check.sh +++ b/merge-check.sh @@ -45,8 +45,10 @@ # unless checks/templates/inline lists it as not yet moved; each names its template in files/. That list # only shrinks, judged against the trunk's copy at the merge base. # 10. the mail-store seat (novox/hq ADR 0378 §3, issue 800): no manifest but modules/mesh-mail claims it. -# 11. ~/.ssh (novox/hq ADR 0415 §6, issue 1020): no manifest but modules/ssh-client names a path under it — -# ssh-client owns it and sweeps out what it does not know. +# 11. owned paths (novox/hq ADR 0422 §8, issue 1024; it holds ~/.ssh for ssh-client as ADR 0415 §6 asked): +# every directory and archive says `unknown` (move-out, module or program) and a valid `knows`; `move-out` +# only under a home; and no module declares a path inside a directory of another module. +# checks/sshdir (ADR 0415 §6) stays while main runs it; it goes under issue 1035 once main runs checks/owned. set -eu (cd checks/words && go test -count=1 . && go run . ../..) @@ -54,8 +56,11 @@ set -eu (cd checks/agenthome && go test -count=1 . && go run . ../..) # The mail-store seat (novox/hq ADR 0378 §3): only mesh-mail may claim it. (cd checks/mailstore && go test -count=1 . && go run . ../..) -# ~/.ssh (novox/hq ADR 0415 §6): only ssh-client may name a path under it. +# ~/.ssh (novox/hq ADR 0415 §6): only ssh-client may name a path under it. Main's merge check runs this line, so +# checks/sshdir stays until main runs checks/owned; it goes under issue 1035. (cd checks/sshdir && go test -count=1 . && go run . ../..) +# Owned paths (novox/hq ADR 0422 §8): unknown files named per directory; no path inside another module's directory. +(cd checks/owned && go test -count=1 . && go run . ../..) # The secrets (novox/hq issue 342): a password is not a setting, so no setting can replace the vault's. The # known list only shrinks: judged against the trunk's copy at the merge base, a line it does not hold is refused. secrets_base="" diff --git a/modules/artifact-store-tools/README.md b/modules/artifact-store-tools/README.md index c337eefb..1b747f8f 100644 --- a/modules/artifact-store-tools/README.md +++ b/modules/artifact-store-tools/README.md @@ -109,3 +109,9 @@ store's current or an older name, a definition not read said first, escalation t tools served are exactly the manifest's `tools` and its `invokes` exactly the two controller verbs and the catalogue's two tools. Any command other than the two read-only scripts fails the test. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/artifact-store-tools/module.json b/modules/artifact-store-tools/module.json index fb6b34bb..208329cd 100644 --- a/modules/artifact-store-tools/module.json +++ b/modules/artifact-store-tools/module.json @@ -26,7 +26,8 @@ "id": "state", "type": "directory", "mode": "0700", - "place": "." + "place": ".", + "unknown": "program" }, { "id": "store-names", diff --git a/modules/asus-zephyrus-g14/README.md b/modules/asus-zephyrus-g14/README.md index 05efa63c..aec3828d 100644 --- a/modules/asus-zephyrus-g14/README.md +++ b/modules/asus-zephyrus-g14/README.md @@ -385,3 +385,15 @@ The module no longer writes a file into i3's `config.d`. Its window-manager line under `files/i3/` where it had one) are a contribution to `node-display-session`. The i3 module places them in its own configuration under a `# ` line, so this module depends on a window manager being assigned beside it. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `/usr/local/lib/asus-zephyrus-g14`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `/etc/systemd/system/systemd-suspend.service.d`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `/etc/systemd/system/systemd-hibernate.service.d`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `/etc/systemd/system/systemd-suspend-then-hibernate.service.d`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `/etc/systemd/system/nvidia-powerd.service.d`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `/etc/UPower/UPower.conf.d`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `/etc/X11/xorg.conf.d`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/asus-zephyrus-g14/module.json b/modules/asus-zephyrus-g14/module.json index 77869927..d1ee8d3b 100644 --- a/modules/asus-zephyrus-g14/module.json +++ b/modules/asus-zephyrus-g14/module.json @@ -54,7 +54,8 @@ "id": "scripts", "type": "archive", "path": "/usr/local/lib/asus-zephyrus-g14", - "artifact": "scripts" + "artifact": "scripts", + "unknown": "program" }, { "id": "nvidia-options", @@ -74,7 +75,8 @@ "id": "suspend-drop-ins", "type": "directory", "path": "/etc/systemd/system/systemd-suspend.service.d", - "mode": "0755" + "mode": "0755", + "unknown": "program" }, { "id": "nvidia-on-suspend", @@ -87,7 +89,8 @@ "id": "hibernate-drop-ins", "type": "directory", "path": "/etc/systemd/system/systemd-hibernate.service.d", - "mode": "0755" + "mode": "0755", + "unknown": "program" }, { "id": "nvidia-on-hibernate", @@ -100,7 +103,8 @@ "id": "suspend-then-hibernate-drop-ins", "type": "directory", "path": "/etc/systemd/system/systemd-suspend-then-hibernate.service.d", - "mode": "0755" + "mode": "0755", + "unknown": "program" }, { "id": "nvidia-on-suspend-then-hibernate", @@ -113,7 +117,8 @@ "id": "powerd-drop-ins", "type": "directory", "path": "/etc/systemd/system/nvidia-powerd.service.d", - "mode": "0755" + "mode": "0755", + "unknown": "program" }, { "id": "powerd-opt-in", @@ -146,7 +151,8 @@ "id": "upower-drop-ins", "type": "directory", "path": "/etc/UPower/UPower.conf.d", - "mode": "0755" + "mode": "0755", + "unknown": "program" }, { "id": "low-battery", @@ -169,7 +175,8 @@ "id": "xorg-drop-ins", "type": "directory", "path": "/etc/X11/xorg.conf.d", - "mode": "0755" + "mode": "0755", + "unknown": "program" }, { "id": "touchpad", diff --git a/modules/audit-logger/README.md b/modules/audit-logger/README.md new file mode 100644 index 00000000..76c1f690 --- /dev/null +++ b/modules/audit-logger/README.md @@ -0,0 +1,11 @@ +# audit-logger + +The `audit-logger` module of the mesh catalogue: declares directories on the node. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:trail}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:spool}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/audit-logger/module.json b/modules/audit-logger/module.json index 9f225050..122b94e7 100644 --- a/modules/audit-logger/module.json +++ b/modules/audit-logger/module.json @@ -47,17 +47,20 @@ "id": "state", "type": "directory", "mode": "0700", - "place": "." + "place": ".", + "unknown": "program" }, { "id": "trail", "type": "directory", - "mode": "0700" + "mode": "0700", + "unknown": "program" }, { "id": "spool", "type": "directory", - "mode": "0700" + "mode": "0700", + "unknown": "program" } ], "capabilities": [ diff --git a/modules/baserow/README.md b/modules/baserow/README.md new file mode 100644 index 00000000..a410bb65 --- /dev/null +++ b/modules/baserow/README.md @@ -0,0 +1,11 @@ +# baserow + +The `baserow` module of the mesh catalogue: runs the container image `baserow/baserow`. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:mesh-state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:data}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/baserow/module.json b/modules/baserow/module.json index e19568b6..81196218 100644 --- a/modules/baserow/module.json +++ b/modules/baserow/module.json @@ -71,19 +71,22 @@ "id": "mesh-state", "type": "directory", "mode": "0700", - "place": "mesh" + "place": "mesh", + "unknown": "program" }, { "id": "state", "type": "directory", "mode": "0700", - "place": "." + "place": ".", + "unknown": "program" }, { "id": "data", "type": "directory", "mode": "0755", - "owner": "9999:9999" + "owner": "9999:9999", + "unknown": "program" }, { "id": "server-env", diff --git a/modules/betterbird/README.md b/modules/betterbird/README.md index 628f32e3..bbc081d1 100644 --- a/modules/betterbird/README.md +++ b/modules/betterbird/README.md @@ -347,3 +347,14 @@ outside the mesh. With this version, a push to a node (each waits for the operat - The controller's `settings` verb, for every proposal and to read a layer as it stands; its `nodes` and `node` verbs, for which machines hold the module; and the module's own `betterbird_derive` on those machines (`invokes`). + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `~/.config/betterbird`: holds only what this module declares and its own tools' records: `accounts-record.json` (`cmd/betterbird-tools/adoption.go:36`), `configuration-record.json` (`owned.go:33`) and the `kept/` folder of replaced filter files (`filters.go:261`); anything else is moved out by the node-engine to `~/.local/state/mesh-moved/betterbird/` with a list, never deleted (ADR 0422 §4). +- `/opt/betterbird`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `/opt/betterbird/owl`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `/opt/betterbird/distribution`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `/opt/betterbird/defaults/pref`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `/usr/local/lib/mesh-betterbird`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/betterbird/module.json b/modules/betterbird/module.json index 19319ef4..891f8832 100644 --- a/modules/betterbird/module.json +++ b/modules/betterbird/module.json @@ -83,7 +83,9 @@ "type": "directory", "path": "${machine:account-home}/.config/betterbird", "owner": "${machine:account}", - "mode": "0755" + "mode": "0755", + "unknown": "move-out", + "knows": ["accounts-record.json", "configuration-record.json", "kept/"] }, { "id": "prefs", @@ -150,31 +152,36 @@ "id": "installation", "type": "archive", "path": "/opt/betterbird", - "artifact": "archive" + "artifact": "archive", + "unknown": "program" }, { "id": "owl", "type": "archive", "path": "/opt/betterbird/owl", - "artifact": "owl" + "artifact": "owl", + "unknown": "program" }, { "id": "policies", "type": "archive", "path": "/opt/betterbird/distribution", - "artifact": "distribution" + "artifact": "distribution", + "unknown": "program" }, { "id": "spelling", "type": "archive", "path": "/opt/betterbird/defaults/pref", - "artifact": "defaults" + "artifact": "defaults", + "unknown": "program" }, { "id": "preparation", "type": "archive", "path": "/usr/local/lib/mesh-betterbird", - "artifact": "tools" + "artifact": "tools", + "unknown": "program" }, { "id": "start", diff --git a/modules/build-agent/Dockerfile b/modules/build-agent/Dockerfile deleted file mode 100644 index 55f388d9..00000000 --- a/modules/build-agent/Dockerfile +++ /dev/null @@ -1,26 +0,0 @@ -ARG GO_BASE -ARG ALPINE_BASE -# build-agent's image: the build machine itself, compiled into a container (novox/hq ADR 0190). -# -# **The source is not vendored here.** builder's actual code — cmd/mesh-builder, internal/builder, -# internal/catalogue — lives in the mesh-controller repository, the same control plane it is one -# half of. This module ships the packaging, not a second copy of the source, so the build context -# is the mesh-controller repository (declared under build.artifacts[].context), and this -# Dockerfile compiles ./cmd/mesh-builder from it — the same shape route-proxy already uses for the -# same reason. The build seat hands it only that program's build source (novox/hq ADR 0267, the -# artifact's `compiles`): a merge elsewhere in that repository does not move this image. -FROM ${GO_BASE} AS build -WORKDIR /src -COPY go.mod go.sum ./ -RUN go mod download -COPY . . -RUN CGO_ENABLED=0 GOOS=linux go build -trimpath -buildvcs=false -ldflags=-buildid= -o /mesh-builder ./cmd/mesh-builder - -# Unlike mesh-controller's own FROM scratch (ADR 0006: nothing to audit but one binary), the build -# machine's whole job is shelling out to git and docker — it needs a real userland to do that in, -# not a second copy of either tool vendored into this image. apk installs both from the base's own -# packages, not fetched on its own at build time. -FROM ${ALPINE_BASE} -RUN apk add --no-cache docker-cli git -COPY --from=build /mesh-builder /usr/local/bin/mesh-builder -ENTRYPOINT ["/usr/local/bin/mesh-builder"] diff --git a/modules/build-agent/README.md b/modules/build-agent/README.md deleted file mode 100644 index 7f253b8e..00000000 --- a/modules/build-agent/README.md +++ /dev/null @@ -1,34 +0,0 @@ -# build-agent - -The mesh's build machine as a role every machine can hold (novox/hq ADR 0190). It holds the node seat -`node-build-agent`: every holder pulls one build at a time from the role's one work queue when it is -idle, so a tier of many images is built by as many machines as hold the seat and are online, and a -machine that is off builds nothing and blocks nothing. The controller asks the role, never a machine; -the outcome names the machine that built it. - -What a holding machine needs is what the builder always needed, said here once: a container runtime -(the socket is mounted), the artifact store as a provision, a workspace, and the bus credential. The code is `cmd/mesh-builder` in the mesh-controller repository, compiled from -that repository's main (`build.artifacts[].context`); this module ships the packaging. - -Assign it to every machine with a container runtime. It replaces `builder`, the one-holder form of the -same thing; retire that once this is assigned where it was. - -A send that replaces the agent's container does not throw away the build it runs: the container declares -`busy` as `/usr/local/bin/mesh-builder busy` (novox/hq ADR 0335). The node-engine asks it before replacing it; from that -ask the agent takes no new build, the node-engine leaves it running while it says a build is in hand, and -replaces it once it says idle, or after 15 minutes at the latest. - -## The package registry (novox/hq ADR 0336) - -The package registry (`npm-package-registry`, provided by the gitea module) is an optional requirement. -Genesis assigns the build agent before anything provides it, so the agent is assignable on a mesh with no -registry, and `mesh-controller.plan` and `node` list the requirement as unbound until the gitea module is -assigned; it then binds with no further act. - -Until it binds, the agent on genesis's node reads the `genesis-package-registry` own-secret, which the -installer seals to it at the packages step: the forge it raised, the port, the npm path, the account -`mesh-builder` and its password. Issued outside the mesh, so no other machine is given one. A real binding -wins over it, and each build's log says which the build resolved from. A build that needs the registry (a -`package` artifact, an image whose Dockerfile names `.npmrc`, a TypeScript artifact with an `@novox/` -dependency) on an agent with neither fails before building, naming the machine. Go artifacts and images -that do not name `.npmrc` build without it. `manifest_test.go` holds the manifest to this. diff --git a/modules/build-agent/files/build-agent.env b/modules/build-agent/files/build-agent.env deleted file mode 100644 index 15ee1bbb..00000000 --- a/modules/build-agent/files/build-agent.env +++ /dev/null @@ -1,8 +0,0 @@ -MESH_BROKER_FILE=/run/mesh/broker -MESH_NODE=${machine:name} -MESH_REGISTRY=${bound:artifact-store:at}:${bound:artifact-store:port} -MESH_PACKAGE_BINDING=/run/mesh/package-registry.json -MESH_NPM_TOKEN_FILE=/run/mesh/package-registry.secret -MESH_GENESIS_PACKAGE_REGISTRY=/run/mesh/genesis-package-registry.json -MESH_WORKSPACE=${dir:workspace} -MESH_JUDGE_CACHE=${dir:judge-cache} diff --git a/modules/build-agent/go.mod b/modules/build-agent/go.mod deleted file mode 100644 index 8d3d54b0..00000000 --- a/modules/build-agent/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module build-agent - -go 1.22 diff --git a/modules/build-agent/manifest_test.go b/modules/build-agent/manifest_test.go deleted file mode 100644 index 59e041d9..00000000 --- a/modules/build-agent/manifest_test.go +++ /dev/null @@ -1,174 +0,0 @@ -// The build agent's manifest (novox/hq ADR 0336, issue 121). The build agent is assigned at genesis's -// step 13, before anything provides the package registry, so that requirement is optional -// (ADR 0330); until it binds, the agent on genesis's node reads the genesis document the installer -// seals to it at the packages step. The program is mesh-controller's cmd/mesh-builder; only the -// packaging lives here, and this is its test. -package buildagent - -import ( - "encoding/json" - "os" - "strings" - "testing" -) - -type manifest struct { - Requires []json.RawMessage `json:"requires"` - Binds map[string]string `json:"binds"` - Secrets map[string]string `json:"secrets"` - OwnSecrets map[string]json.RawMessage `json:"own-secrets"` - Resources []map[string]any `json:"resources"` -} - -func read(t *testing.T) manifest { - t.Helper() - raw, err := os.ReadFile("module.json") - if err != nil { - t.Fatal(err) - } - var m manifest - if err := json.Unmarshal(raw, &m); err != nil { - t.Fatal(err) - } - return m -} - -// requirement is one entry of `requires`: its name, and whether it is optional. -func requirements(t *testing.T, m manifest) map[string]bool { - t.Helper() - out := map[string]bool{} - for _, raw := range m.Requires { - var name string - if json.Unmarshal(raw, &name) == nil { - out[name] = false - continue - } - var long struct { - Name string `json:"name"` - Optional bool `json:"optional"` - } - if err := json.Unmarshal(raw, &long); err != nil { - t.Fatalf("requires: %s: %v", raw, err) - } - out[long.Name] = long.Optional - } - return out -} - -func TestThePackageRegistryIsOptionalAndTheArtifactStoreStaysHard(t *testing.T) { - r := requirements(t, read(t)) - optional, has := r["npm-package-registry"] - if !has || !optional { - t.Errorf("npm-package-registry is not an optional requirement (ADR 0336 §1): %v", r) - } - if optional, has := r["artifact-store"]; !has || optional { - t.Errorf("artifact-store is not a hard requirement: %v", r) - } -} - -// The binding and the credential of the registry are delivered only while it is bound: ADR 0330 §6 -// composes neither for an absent requirement, so no file of the module may name them through a -// placeholder unless it is itself only-when-bound. -func TestTheRegistrysBindsAndSecretsAreOnlyWhenBound(t *testing.T) { - m := read(t) - if m.Binds["npm-package-registry"] == "" || m.Secrets["npm-package-registry"] == "" { - t.Fatalf("the agent is not told where the registry is once it binds: binds %v, secrets %v", m.Binds, m.Secrets) - } - for _, r := range m.Resources { - if r["type"] != "file" { - continue - } - content := templateText(t, r) - if strings.Contains(content, "${bound:npm-package-registry:") || strings.Contains(content, "${secret:npm-package-registry}") { - if r["only-when-bound"] != "npm-package-registry" { - t.Errorf("%v names the registry's binding and is delivered while it is absent", r["id"]) - } - } - } -} - -// The genesis document is an own-secret issued outside the mesh — by the installer at genesis's packages -// step (ADR 0336 §3) — so the mesh never makes one of its own on any other machine, and the agent is -// told where it lands. -func TestTheGenesisDocumentIsDeclaredAndIssuedOutside(t *testing.T) { - m := read(t) - raw, has := m.OwnSecrets["genesis-package-registry"] - if !has { - t.Fatalf("the build agent declares no genesis-package-registry own-secret: %v", m.OwnSecrets) - } - var own struct { - Path string `json:"path"` - IssuedBy string `json:"issued-by"` - } - if err := json.Unmarshal(raw, &own); err != nil || own.IssuedBy != "outside" { - t.Fatalf("the genesis document is not issued outside the mesh: %s (%v)", raw, err) - } - if !strings.HasPrefix(own.Path, "${dir:mesh-state}/") { - t.Fatalf("the genesis document is not where the container reads /run/mesh: %s", own.Path) - } - inside := "/run/mesh/" + strings.TrimPrefix(own.Path, "${dir:mesh-state}/") - for _, r := range m.Resources { - if r["id"] == "agent-env" { - content := templateText(t, r) - if !strings.Contains(content, "MESH_GENESIS_PACKAGE_REGISTRY="+inside+"\n") { - t.Errorf("the agent is not told where the genesis document is (%s):\n%s", inside, content) - } - return - } - } - t.Fatal("the build agent has no environment file") -} - -// **The judge cache is the agent's own, beside its workspace and never in it** (novox/hq ADR 0358 §6, issue 704): a -// directory resource of its own, kept as the agent's data, mounted into the agent at the same path as on the host — -// the container runtime resolves a judge build's mounts on the host — and handed to the agent as MESH_JUDGE_CACHE. -func TestTheJudgeCacheIsADirectoryOfItsOwnMountedAtTheSamePath(t *testing.T) { - raw, err := os.ReadFile("module.json") - if err != nil { - t.Fatal(err) - } - var m struct { - Data struct { - Own []struct { - ID string `json:"id"` - Path string `json:"path"` - Class string `json:"class"` - } `json:"own"` - } `json:"data"` - Resources []struct { - ID string `json:"id"` - Type string `json:"type"` - Template string `json:"template"` - Volumes []string `json:"volumes"` - } `json:"resources"` - } - if err := json.Unmarshal(raw, &m); err != nil { - t.Fatal(err) - } - dir, data, env, mounted := false, false, false, false - for _, d := range m.Data.Own { - if d.ID == "judge-cache" && d.Path == "${dir:judge-cache}" && d.Class == "cache" { - data = true - } - } - for _, r := range m.Resources { - switch { - case r.ID == "judge-cache" && r.Type == "directory": - dir = true - case r.ID == "agent-env": - env = strings.Contains(templateText(t, map[string]any{"id": r.ID, "template": r.Template}), "MESH_JUDGE_CACHE=${dir:judge-cache}\n") - case r.ID == "server": - for _, v := range r.Volumes { - if v == "${dir:judge-cache}:${dir:judge-cache}" { - mounted = true - } - if strings.HasPrefix(v, "${dir:workspace}") && strings.Contains(v, "judge-cache") { - t.Errorf("the judge cache is mounted inside the workspace: %s", v) - } - } - } - } - if !dir || !data || !env || !mounted { - t.Fatalf("the judge cache: directory %v, data %v, env %v, mounted at the same path %v", dir, data, env, mounted) - } -} diff --git a/modules/build-agent/module.json b/modules/build-agent/module.json deleted file mode 100644 index f9b789af..00000000 --- a/modules/build-agent/module.json +++ /dev/null @@ -1,139 +0,0 @@ -{ - "module": "build-agent", - "version": "1", - "slug": "agent", - "capabilities": [ - "container-runtime" - ], - "claims": [ - { - "name": "node-build-agent", - "scope": "node", - "serves": ["current", "kill", "pause", "resume"] - } - ], - "reads": [ - "mesh-delivery.pulls" - ], - "requires": [ - "artifact-store", - { - "name": "npm-package-registry", - "optional": true - } - ], - "binds": { - "npm-package-registry": "${dir:mesh-state}/package-registry.json" - }, - "secrets": { - "npm-package-registry": "${dir:mesh-state}/package-registry.secret" - }, - "own-secrets": { - "broker": "${dir:mesh-state}/broker", - "genesis-package-registry": { - "path": "${dir:mesh-state}/genesis-package-registry.json", - "issued-by": "outside" - } - }, - "data": { - "own": [ - { - "id": "workspace", - "path": "${dir:workspace}", - "class": "cache", - "why": "a build's working copy, cloned again for every build" - }, - { - "id": "judge-cache", - "path": "${dir:judge-cache}", - "class": "cache", - "why": "the Go cache every merge check's judge is built with and the release-branch judges kept per head; mounted into judge builds only, never into a check's containers (novox/hq ADR 0358 §6)" - } - ] - }, - "resources": [ - { - "id": "mesh-state", - "type": "directory", - "mode": "0700", - "place": "mesh" - }, - { - "id": "workspace", - "type": "directory", - "mode": "0700" - }, - { - "id": "judge-cache", - "type": "directory", - "mode": "0700" - }, - { - "id": "agent-env", - "type": "file", - "path": "${dir:mesh-state}/build-agent.env", - "mode": "0600", - "template": "files/build-agent.env" - }, - { - "id": "server", - "type": "container", - "name": "mesh-build-agent", - "artifact": "server", - "env-file": [ - "${dir:mesh-state}/build-agent.env" - ], - "volumes": [ - "${dir:mesh-state}:/run/mesh:ro", - "${dir:workspace}:${dir:workspace}", - "${dir:judge-cache}:${dir:judge-cache}", - "/var/run/docker.sock:/var/run/docker.sock" - ], - "restart-on": [ - "agent-env" - ], - "network": "host", - "health": { - "kind": "exec", - "command": "/usr/local/bin/mesh-builder ready", - "interval": "10s", - "timeout": "5s", - "looks": 3, - "grace": "60s" - }, - "busy": { - "command": "/usr/local/bin/mesh-builder busy" - } - } - ], - "build": { - "artifacts": [ - { - "name": "server", - "kind": "image", - "from": "Dockerfile", - "compiles": "cmd/mesh-builder", - "context": { - "seat": "git", - "repository": "novox/mesh-controller", - "ref": "main" - } - }, - { - "name": "files", - "kind": "archive", - "from": "files" - } - ], - "on": [ - { - "arg": "GO_BASE", - "image": "golang@sha256:8ac98ca534ac3f51e1f420a1dd2c15e74c75cfa0f23f3ad27eb5d7236c349a0c" - }, - { - "arg": "ALPINE_BASE", - "image": "alpine@sha256:d9e853e87e55526f6b2917df91a2115c36dd7c696a35be12163d44e6e2a4b6bc" - } - ] - } -} diff --git a/modules/build-agent/templates_test.go b/modules/build-agent/templates_test.go deleted file mode 100644 index 896a9770..00000000 --- a/modules/build-agent/templates_test.go +++ /dev/null @@ -1,64 +0,0 @@ -package buildagent - -import ( - "encoding/json" - "os" - "path/filepath" - "testing" -) - -// The module's directory, where its manifest and its files/ are. -const moduleDir = "." - -// templateText is the text a file resource places on the machine: its template's, in the module's files/ -// (novox/hq ADR 0349 §2). A resource that names no template, or a template that is not there, fails the test. -func templateText(t *testing.T, r map[string]any) string { - t.Helper() - name, _ := r["template"].(string) - if name == "" { - t.Fatalf("resource %v names no template", r["id"]) - } - raw, err := os.ReadFile(filepath.Join(moduleDir, filepath.FromSlash(name))) - if err != nil { - t.Fatalf("resource %v names %s: %v", r["id"], name, err) - } - return string(raw) -} - -// No file resource carries its text inline: each names its template in files/, and the build packs files/ -// into the one archive the controller reads the templates back from (novox/hq ADR 0349 §2). -func TestEveryFileTakesItsTextFromATemplate(t *testing.T) { - raw, err := os.ReadFile(filepath.Join(moduleDir, "module.json")) - if err != nil { - t.Fatal(err) - } - var m struct { - Resources []map[string]any `json:"resources"` - Build struct { - Artifacts []map[string]any `json:"artifacts"` - } `json:"build"` - } - if err := json.Unmarshal(raw, &m); err != nil { - t.Fatal(err) - } - for _, r := range m.Resources { - if _, inline := r["content"]; inline { - t.Errorf("resource %v keeps its text inline: keep it in files/ and name it as the template", r["id"]) - } - if r["type"] == "file" { - templateText(t, r) - } - } - packs := 0 - for _, a := range m.Build.Artifacts { - if a["kind"] == "archive" && a["from"] == "files" { - packs++ - if a["name"] != "files" { - t.Errorf("the templates archive is named %v, not files", a["name"]) - } - } - } - if packs != 1 { - t.Errorf("the build packs files/ into %d archives, not one", packs) - } -} diff --git a/modules/ca-trust/README.md b/modules/ca-trust/README.md new file mode 100644 index 00000000..ea0b821a --- /dev/null +++ b/modules/ca-trust/README.md @@ -0,0 +1,9 @@ +# ca-trust + +The `ca-trust` module of the mesh catalogue: claims the seat `the-mesh-trust-anchor`. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/ca-trust/module.json b/modules/ca-trust/module.json index 94e64fe4..b3c9dc93 100644 --- a/modules/ca-trust/module.json +++ b/modules/ca-trust/module.json @@ -25,7 +25,8 @@ "id": "state", "type": "directory", "mode": "0700", - "place": "." + "place": ".", + "unknown": "program" }, { "id": "anchor", diff --git a/modules/claude-code/README.md b/modules/claude-code/README.md index c0423f3d..a02e88f9 100644 --- a/modules/claude-code/README.md +++ b/modules/claude-code/README.md @@ -10,10 +10,13 @@ Two directories, declared, so the mesh refuses a second module owning either: - `/etc/claude-code`, the agent's machine-wide managed directory, root's, `0755`. - `~/.claude` under the home of the account agents run as (the operator's, unless the machine names an agent account of its own), that account's, `0700`. The module owns the directory — - that it exists, who owns it, its mode — and of what is inside only what it writes. Everything else - in it (history, projects, local settings, a person's own rules and skills) is the person's - and is never read or written (hq ADR 0182) — but for each project's memory folder, which the module - carries between nodes (hq ADR 0343, below). Unassigned, the module leaves the directory: the host + that it exists, who owns it, its mode — and of what is inside only what it writes. What else is in + it (history, projects, local settings, a person's own rules and skills) is the person's, and the + directory is `unknown: module` (ADR 0422 §7): the module's own process decides what to do with a file + it did not write. Today that process carries each project's memory folder between nodes (hq ADR 0343, + below) and reads the home's items for its status tool; the reactions ADR 0273 decides (drift raised as a + condition, `home-copies`, unlisted entries raised) are not built yet (issue 1030), and until then nothing + else in it is moved. Unassigned, the module leaves the directory: the host removes a directory only when it is empty. And, for where the agent runs as an account of its own (hq ADR 0266): @@ -353,3 +356,11 @@ is written. The same sudo starts the helper as the agent's account, where there Go, one binary (`cmd/claude-code`) the node's runtime launches. Tested with `go test ./...`; the managed instruction file is held to the TypeScript renderer it replaced (`testdata/rendered-by-typescript.json`), and the sealed box is the licence manager's own format. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `/etc/claude-code`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `~/.claude`: value `module` (ADR 0422 §7). The module's process carries each project's memory folder (ADR 0343) and reads the home's items for its status tool; nothing else in it is moved. The reactions ADR 0273 decides (drift raised as a condition, `home-copies`, unlisted entries raised) are not built yet (issue 1030). +- `${dir:state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/claude-code/module.json b/modules/claude-code/module.json index d6de675d..b869d012 100644 --- a/modules/claude-code/module.json +++ b/modules/claude-code/module.json @@ -154,7 +154,8 @@ "id": "managed", "type": "directory", "path": "/etc/claude-code", - "mode": "0755" + "mode": "0755", + "unknown": "program" }, { "id": "start", @@ -182,14 +183,16 @@ "type": "directory", "path": "${machine:agent-home}/.claude", "mode": "0700", - "owner": "${machine:agent-account}" + "owner": "${machine:agent-account}", + "unknown": "module" }, { "id": "state", "type": "directory", "mode": "0700", "owner": "${machine:account}", - "place": "." + "place": ".", + "unknown": "program" }, { "id": "facts", diff --git a/modules/claude-licence-manager/README.md b/modules/claude-licence-manager/README.md index eb1f4064..58a984b3 100644 --- a/modules/claude-licence-manager/README.md +++ b/modules/claude-licence-manager/README.md @@ -53,3 +53,9 @@ Go one replaced, so the format is the one already on the machines. # the store against a real postgres: docker run -d --rm --name licmgr-pg -e POSTGRES_PASSWORD=t -p 15498:5432 postgres:16-alpine MESH_TEST_POSTGRES=postgres://postgres:t@127.0.0.1:15498/postgres go test ./... + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/claude-licence-manager/module.json b/modules/claude-licence-manager/module.json index a78a86fd..a55da31e 100644 --- a/modules/claude-licence-manager/module.json +++ b/modules/claude-licence-manager/module.json @@ -118,7 +118,8 @@ "id": "state", "type": "directory", "mode": "0700", - "place": "." + "place": ".", + "unknown": "program" }, { "id": "database-url", diff --git a/modules/clipmenu/README.md b/modules/clipmenu/README.md index b9f504c9..9ae52091 100644 --- a/modules/clipmenu/README.md +++ b/modules/clipmenu/README.md @@ -93,3 +93,9 @@ The module no longer writes a file into i3's `config.d`. Its window-manager line under `files/i3/` where it had one) are a contribution to `node-display-session`. The i3 module places them in its own configuration under a `# ` line, so this module depends on a window manager being assigned beside it. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `~/.config/systemd/user/clipmenud.service.d`: holds only what this module declares; anything else is moved out by the node-engine to `~/.local/state/mesh-moved/clipmenu/` with a list, never deleted (ADR 0422 §4). diff --git a/modules/clipmenu/module.json b/modules/clipmenu/module.json index f2d328f2..30ec9c0c 100644 --- a/modules/clipmenu/module.json +++ b/modules/clipmenu/module.json @@ -76,19 +76,13 @@ "mode": "0755", "template": "files/bin/xsel" }, - { - "id": "user-units-dir", - "type": "directory", - "path": "${machine:account-home}/.config/systemd/user", - "owner": "${machine:account}", - "mode": "0755" - }, { "id": "daemon-dropins", "type": "directory", "path": "${machine:account-home}/.config/systemd/user/clipmenud.service.d", "owner": "${machine:account}", - "mode": "0755" + "mode": "0755", + "unknown": "move-out" }, { "id": "daemon-dropin", diff --git a/modules/confluence/README.md b/modules/confluence/README.md new file mode 100644 index 00000000..b318407f --- /dev/null +++ b/modules/confluence/README.md @@ -0,0 +1,9 @@ +# confluence + +The `confluence` module of the mesh catalogue: declares directories on the node. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/confluence/module.json b/modules/confluence/module.json index 18fb5e5e..caa0d1be 100644 --- a/modules/confluence/module.json +++ b/modules/confluence/module.json @@ -24,7 +24,8 @@ "id": "state", "type": "directory", "mode": "0700", - "place": "." + "place": ".", + "unknown": "program" }, { "id": "config", diff --git a/modules/desk-channel/README.md b/modules/desk-channel/README.md index 860ef98a..4579d995 100644 --- a/modules/desk-channel/README.md +++ b/modules/desk-channel/README.md @@ -55,3 +55,9 @@ each machine's notifier through the mesh: nothing new runs on the machines. | tool | does | |---|---| | `desk_channel_status` | whether it can show messages, the machines and each one's last answer, and its rules | + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/desk-channel/module.json b/modules/desk-channel/module.json index 3ee2dcc2..115ce620 100644 --- a/modules/desk-channel/module.json +++ b/modules/desk-channel/module.json @@ -66,7 +66,8 @@ "type": "directory", "mode": "0700", "owner": "desk-channel", - "place": "." + "place": ".", + "unknown": "program" }, { "id": "settings", diff --git a/modules/distribution/README.md b/modules/distribution/README.md new file mode 100644 index 00000000..a7a90a38 --- /dev/null +++ b/modules/distribution/README.md @@ -0,0 +1,10 @@ +# distribution + +The `distribution` module of the mesh catalogue: runs the container image `registry`; claims the seat `mesh-artifact-store`. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `/var/lib/mesh/registry`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `/var/lib/mesh-registry`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/distribution/module.json b/modules/distribution/module.json index e368b824..eb9f7c24 100644 --- a/modules/distribution/module.json +++ b/modules/distribution/module.json @@ -50,13 +50,15 @@ "id": "state", "type": "directory", "path": "/var/lib/mesh/registry", - "mode": "0700" + "mode": "0700", + "unknown": "program" }, { "id": "registry-data", "type": "directory", "path": "/var/lib/mesh-registry", - "mode": "0700" + "mode": "0700", + "unknown": "program" }, { "id": "store", diff --git a/modules/dnsmasq/README.md b/modules/dnsmasq/README.md new file mode 100644 index 00000000..1fa0c2ce --- /dev/null +++ b/modules/dnsmasq/README.md @@ -0,0 +1,11 @@ +# dnsmasq + +The `dnsmasq` module of the mesh catalogue: installs the package `dnsmasq`; claims the seat `mesh-dns-resolver`. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:mesh-state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `/etc/systemd/system/dnsmasq.service.d`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `/var/lib/dnsmasq-watch`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/dnsmasq/module.json b/modules/dnsmasq/module.json index 1865d9b7..58cc0da5 100644 --- a/modules/dnsmasq/module.json +++ b/modules/dnsmasq/module.json @@ -58,7 +58,8 @@ "id": "mesh-state", "type": "directory", "mode": "0700", - "place": "mesh" + "place": "mesh", + "unknown": "program" }, { "id": "package", @@ -76,7 +77,8 @@ "id": "drop-ins", "type": "directory", "path": "/etc/systemd/system/dnsmasq.service.d", - "mode": "0755" + "mode": "0755", + "unknown": "program" }, { "id": "wait-for-address", @@ -105,7 +107,8 @@ "id": "watch-dir", "type": "directory", "path": "/var/lib/dnsmasq-watch", - "mode": "0755" + "mode": "0755", + "unknown": "program" }, { "id": "watch", diff --git a/modules/docker/README.md b/modules/docker/README.md index bc33c109..459fab7d 100644 --- a/modules/docker/README.md +++ b/modules/docker/README.md @@ -268,3 +268,9 @@ The tests run against a fake runner and cover: - event filtering; - volume ownership; - that the tools served are exactly the manifest's `tools`. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/docker/module.json b/modules/docker/module.json index 80f0bed3..a3a75e09 100644 --- a/modules/docker/module.json +++ b/modules/docker/module.json @@ -83,7 +83,8 @@ "id": "state", "type": "directory", "mode": "0700", - "place": "mesh" + "place": "mesh", + "unknown": "program" }, { "id": "log-rotation", diff --git a/modules/dunst/README.md b/modules/dunst/README.md index c990dc4d..908d7cc5 100644 --- a/modules/dunst/README.md +++ b/modules/dunst/README.md @@ -6,8 +6,10 @@ The notifier as a module (novox/hq ADR 0208, research 026/05). The context menu its `dunstrc` calls is `rofi`, which the `rofi` module installs: two modules on one machine may not declare the same package, so a machine dunst serves also has `rofi` assigned. - Claims the mesh's `node-notifier` seat and serves its verbs `send` and `history`. -- Owns `~/.config/dunst/dunstrc` and the directory `~/.config/dunst/dunstrc.d/`. Another module's - rule is that module's own file in the directory (ADR 0208 §4). dunst reads the directory after +- Owns `~/.config/dunst/dunstrc` and the directory `~/.config/dunst/dunstrc.d/`. It holds what this + module declares and `50-slack.conf` (named in `knows`); any other file is moved out to + `~/.local/state/mesh-moved/dunst/` with a list, never deleted (ADR 0422). Another module's rule is that + module's own declared file in the directory (ADR 0208 §4). dunst reads the directory after `dunstrc`, so a drop-in outranks it. - **Starts nothing.** The package registers dunst with D-Bus, which starts it on the first notification, inside the account's service manager. There is no autostart line and no @@ -97,10 +99,10 @@ count. The file keeps only the settings that differ from dunst's defaults. - `icon_path`, which named two directories of an icon theme that is not installed, is gone. The icon theme is looked up recursively. -## What it leaves as found +## What it keeps -- `~/.config/dunst/dunstrc.d/50-slack.conf`, the Slack rule. It becomes the Slack module's own drop-in - when there is one, and until then it is the operator's file in a directory this module owns. +- `~/.config/dunst/dunstrc.d/50-slack.conf`, the Slack rule, named in `knows`: kept until its owner exists. + It becomes the Slack module's own drop-in when there is one. Any other file in the folder is moved out. ## Migration (ADR 0182) @@ -115,3 +117,10 @@ count. The file keeps only the settings that differ from dunst's defaults. unknown. - `dunst_rules` and the counts ask the running notifier. When none runs, the bus starts one, which needs a session to draw on. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `~/.config/dunst`: holds only what this module declares; anything else is moved out by the node-engine to `~/.local/state/mesh-moved/dunst/` with a list, never deleted (ADR 0422 §4). +- `~/.config/dunst/dunstrc.d`: holds only what this module declares and `50-slack.conf` (the Slack rule, kept until its owner exists); anything else is moved out by the node-engine to `~/.local/state/mesh-moved/dunst/` with a list, never deleted (ADR 0422 §4). diff --git a/modules/dunst/module.json b/modules/dunst/module.json index fa4923db..0c6983b4 100644 --- a/modules/dunst/module.json +++ b/modules/dunst/module.json @@ -56,14 +56,17 @@ "type": "directory", "path": "${machine:account-home}/.config/dunst", "owner": "${machine:account}", - "mode": "0755" + "mode": "0755", + "unknown": "move-out" }, { "id": "dropins", "type": "directory", "path": "${machine:account-home}/.config/dunst/dunstrc.d", "owner": "${machine:account}", - "mode": "0755" + "mode": "0755", + "unknown": "move-out", + "knows": ["50-slack.conf"] }, { "id": "configuration", diff --git a/modules/fail2ban/README.md b/modules/fail2ban/README.md new file mode 100644 index 00000000..87fb505a --- /dev/null +++ b/modules/fail2ban/README.md @@ -0,0 +1,13 @@ +# fail2ban + +The `fail2ban` module of the mesh catalogue: installs the package `fail2ban`; claims the seat `node-intrusion-prevention`. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `/var/lib/mesh-fail2ban`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `/etc/fail2ban/jail.d`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `/etc/fail2ban/action.d`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `/etc/fail2ban/filter.d`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `/var/run/fail2ban`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/fail2ban/module.json b/modules/fail2ban/module.json index d2aa2a65..446395f2 100644 --- a/modules/fail2ban/module.json +++ b/modules/fail2ban/module.json @@ -42,7 +42,8 @@ "id": "state-dir", "type": "directory", "path": "/var/lib/mesh-fail2ban", - "mode": "0755" + "mode": "0755", + "unknown": "program" }, { "id": "own-addresses-list", @@ -56,25 +57,29 @@ "id": "jail-d", "type": "directory", "path": "/etc/fail2ban/jail.d", - "mode": "0755" + "mode": "0755", + "unknown": "program" }, { "id": "action-d", "type": "directory", "path": "/etc/fail2ban/action.d", - "mode": "0755" + "mode": "0755", + "unknown": "program" }, { "id": "filter-d", "type": "directory", "path": "/etc/fail2ban/filter.d", - "mode": "0755" + "mode": "0755", + "unknown": "program" }, { "id": "run-dir", "type": "directory", "path": "/var/run/fail2ban", - "mode": "0755" + "mode": "0755", + "unknown": "program" }, { "id": "jail-local", diff --git a/modules/feh/README.md b/modules/feh/README.md index ea3140e3..72a19302 100644 --- a/modules/feh/README.md +++ b/modules/feh/README.md @@ -52,3 +52,9 @@ The module no longer writes a file into i3's `config.d`. Its window-manager line under `files/i3/` where it had one) are a contribution to `node-display-session`. The i3 module places them in its own configuration under a `# ` line, so this module depends on a window manager being assigned beside it. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `~/.local/share/feh/wallpapers`: holds only what this module declares; anything else is moved out by the node-engine to `~/.local/state/mesh-moved/feh/` with a list, never deleted (ADR 0422 §4). diff --git a/modules/feh/module.json b/modules/feh/module.json index eb2c69bd..d6e44073 100644 --- a/modules/feh/module.json +++ b/modules/feh/module.json @@ -35,7 +35,8 @@ "type": "archive", "path": "${machine:account-home}/.local/share/feh/wallpapers", "owner": "${machine:account}", - "artifact": "wallpapers" + "artifact": "wallpapers", + "unknown": "move-out" }, { "id": "fehbg", diff --git a/modules/gitea/README.md b/modules/gitea/README.md index dca279c0..5592f0d6 100644 --- a/modules/gitea/README.md +++ b/modules/gitea/README.md @@ -233,3 +233,14 @@ and serves the entry values `user`, `ssh_host` (the forge machine's mesh name, f reported no key gets no entry, and its client says it waits. Keys titled `mesh:` that are no longer wanted are removed, a key added by hand never is. Every consumer is made again every ten minutes and at once when a key changes. `go test ./cmd/forge-account-provider ./internal/forgeaccount` runs it against a fake forge. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:mesh-state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:mesh-state}/state`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `/var/lib/mesh/forge-patches`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:grants}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:data}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/gitea/module.json b/modules/gitea/module.json index 324684eb..e8c73163 100644 --- a/modules/gitea/module.json +++ b/modules/gitea/module.json @@ -188,13 +188,15 @@ "id": "mesh-state", "type": "directory", "mode": "0700", - "place": "mesh" + "place": "mesh", + "unknown": "program" }, { "id": "runtime-state", "type": "directory", "path": "${dir:mesh-state}/state", - "mode": "0700" + "mode": "0700", + "unknown": "program" }, { "id": "ready-authors", @@ -209,18 +211,21 @@ "type": "directory", "path": "/var/lib/mesh/forge-patches", "mode": "0733", - "owner": "${machine:account}" + "owner": "${machine:account}", + "unknown": "program" }, { "id": "state", "type": "directory", "mode": "0700", - "place": "." + "place": ".", + "unknown": "program" }, { "id": "grants", "type": "directory", - "mode": "0700" + "mode": "0700", + "unknown": "program" }, { "id": "server-env", @@ -233,7 +238,8 @@ "id": "data", "type": "directory", "mode": "0700", - "owner": "1000:1000" + "owner": "1000:1000", + "unknown": "program" }, { "id": "server", diff --git a/modules/gitlab/README.md b/modules/gitlab/README.md new file mode 100644 index 00000000..0d9db43d --- /dev/null +++ b/modules/gitlab/README.md @@ -0,0 +1,9 @@ +# gitlab + +The `gitlab` module of the mesh catalogue: declares directories on the node. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/gitlab/module.json b/modules/gitlab/module.json index 38be2014..3cb33fb2 100644 --- a/modules/gitlab/module.json +++ b/modules/gitlab/module.json @@ -17,7 +17,8 @@ "id": "state", "type": "directory", "mode": "0700", - "place": "." + "place": ".", + "unknown": "program" }, { "id": "config", diff --git a/modules/grafana/README.md b/modules/grafana/README.md new file mode 100644 index 00000000..42877662 --- /dev/null +++ b/modules/grafana/README.md @@ -0,0 +1,13 @@ +# grafana + +The `grafana` module of the mesh catalogue: runs the container image `grafana/grafana`. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:mesh-state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:data}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:state}/datasources`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:state}/influxdb`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/grafana/module.json b/modules/grafana/module.json index 9d8ceebf..1cbf995f 100644 --- a/modules/grafana/module.json +++ b/modules/grafana/module.json @@ -48,19 +48,22 @@ "id": "mesh-state", "type": "directory", "mode": "0700", - "place": "mesh" + "place": "mesh", + "unknown": "program" }, { "id": "state", "type": "directory", "mode": "0700", - "place": "." + "place": ".", + "unknown": "program" }, { "id": "data", "type": "directory", "mode": "0700", - "owner": "472:472" + "owner": "472:472", + "unknown": "program" }, { "id": "admin-secret", @@ -90,14 +93,16 @@ "type": "directory", "path": "${dir:state}/datasources", "mode": "0755", - "owner": "472:472" + "owner": "472:472", + "unknown": "program" }, { "id": "influxdb-credential", "type": "directory", "path": "${dir:state}/influxdb", "mode": "0700", - "owner": "472:472" + "owner": "472:472", + "unknown": "program" }, { "id": "influxdb-secret", diff --git a/modules/hello-web/README.md b/modules/hello-web/README.md new file mode 100644 index 00000000..9b5f4499 --- /dev/null +++ b/modules/hello-web/README.md @@ -0,0 +1,9 @@ +# hello-web + +The `hello-web` module of the mesh catalogue: declares directories on the node. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/hello-web/module.json b/modules/hello-web/module.json index 44dfc471..005578cd 100644 --- a/modules/hello-web/module.json +++ b/modules/hello-web/module.json @@ -31,7 +31,8 @@ "id": "state", "type": "directory", "mode": "0700", - "place": "." + "place": ".", + "unknown": "program" }, { "id": "page", diff --git a/modules/home-assistant/README.md b/modules/home-assistant/README.md new file mode 100644 index 00000000..34c2957f --- /dev/null +++ b/modules/home-assistant/README.md @@ -0,0 +1,12 @@ +# home-assistant + +The `home-assistant` module of the mesh catalogue: runs the container image `ghcr.io/home-assistant/home-assistant`. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:mesh-state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:config}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:written}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/home-assistant/module.json b/modules/home-assistant/module.json index 8aff932f..9c4f99db 100644 --- a/modules/home-assistant/module.json +++ b/modules/home-assistant/module.json @@ -58,23 +58,27 @@ "id": "mesh-state", "type": "directory", "mode": "0700", - "place": "mesh" + "place": "mesh", + "unknown": "program" }, { "id": "state", "type": "directory", "mode": "0700", - "place": "." + "place": ".", + "unknown": "program" }, { "id": "config", "type": "directory", - "mode": "0700" + "mode": "0700", + "unknown": "program" }, { "id": "written", "type": "directory", - "mode": "0700" + "mode": "0700", + "unknown": "program" }, { "id": "server", diff --git a/modules/i3/README.md b/modules/i3/README.md index 5903d847..ec5defdd 100644 --- a/modules/i3/README.md +++ b/modules/i3/README.md @@ -16,14 +16,14 @@ The window manager as a module (novox/hq ADR 0208, research 026, to-be 42 phase | path | class (ADR 0182) | what | |---|---|---| -| `~/.config/i3/` | owned directory | its contents other than `config` are found and kept | -| `~/.config/i3/config.d/` | owned directory | other modules' drop-ins, and yours | +| `~/.config/i3/` | owned directory (`move-out`) | what the module declares, and `scripts/` (named in `knows`: the screenshot binding in the main file runs `scripts/screenshot.sh`, kept until its owner exists); any other file is moved out by the node-engine to `~/.local/state/mesh-moved/i3/` with a list, never deleted (ADR 0422 §4) | +| `~/.config/i3/config.d/` | owned directory (`move-out`) | what the module declares, and `10-asus.conf` and `20-g14.conf` (named in `knows`, kept until the laptop's hardware module exists); any other file is moved out by the node-engine to `~/.local/state/mesh-moved/i3/` with a list, never deleted (ADR 0422 §4) | | `~/.config/i3/config` | owned (the found file kept once) | the main configuration, from [`files/config`](files/config) | | `/etc/lemurs/wms/i3` | owned | the login manager's session entry: `exec systemd-cat -t x-session /bin/sh "$HOME/.xinitrc"`, the session's output in the journal (`journalctl -t x-session`) because the login manager's pipe has no reader | -**Drop-ins.** Another module adds to i3 with its own `~/.config/i3/config.d/-.conf`. The -`include` is the last line of the main file, so every variable set there (`$mod`, `$ws1`…`$ws10`) is -in scope. Files are read in name order. A file of yours there is yours. +**Drop-ins.** `~/.config/i3/config.d/` holds what this module declares and the files named in `knows`; a +file of yours put there is moved out (ADR 0422). The `include` is the last line of the main file, so every +variable set there (`$mod`, `$ws1`…`$ws10`) is in scope. Files are read in name order. ## The reload watcher @@ -103,10 +103,13 @@ key. volume, the sessions launcher and the screenshot binding. Each leaves when the module that owns it is written. The peripherals' tray applet left it for the `polychromatic` module. -## What it leaves found +## What it keeps, and what it moves out -`~/.config/i3/scripts/`, `~/.config/i3/unlock-keyring.sh`, every file in `config.d/`, the -`/usr/share/xsessions` entries (the package's, no longer offered by `lemurs`), and i3's restart +Kept, because they work for the operator today: `~/.config/i3/scripts/` (the screenshot binding runs +`scripts/screenshot.sh`), and `config.d/10-asus.conf` and `20-g14.conf`, each until its owner exists. Both are +named in `knows`. Anything else in `~/.config/i3/` (such as `unlock-keyring.sh`) or in `config.d/` is moved +out by the node-engine to `~/.local/state/mesh-moved/i3/` with a list, never deleted (ADR 0422 §4). Left +alone, outside the folders: the `/usr/share/xsessions` entries (the package's, no longer offered by `lemurs`), and i3's restart state and logs. ## The one-off migration (ADR 0182) @@ -120,11 +123,11 @@ state and logs. module decides. 3. **Delete `/etc/lemurs/wms/i3wm`** (see `lemurs`). 4. **`config.d/` fragments:** - - `10-asus.conf` and `20-g14.conf` (the laptop) belong to that machine model's hardware module. - Keep them until it exists. The desktop no longer carries them. - - `50-slack.conf` (both) is yours, or a future `slack` module's. Keep it. - - `99-tmp-wine-focus.conf` (the desktop), a test of a predecessor change by its own comment: - **delete** it, or keep it as yours. + - `10-asus.conf` and `20-g14.conf` (the laptop) belong to that machine model's hardware module. They + are named in `knows`, so they stay until it exists. The desktop no longer carries them. + - `50-slack.conf` is not an i3 file: the Slack rule is dunst's (`dunstrc.d`, see `dunst`). + - `99-tmp-wine-focus.conf` (the desktop), a test of a predecessor change by its own comment, is moved + out at the first look with a list; nothing is lost. 5. **The main file's predecessor copy** is kept once by the host and written over. Nothing to do. ## What changes when it is assigned @@ -156,6 +159,13 @@ about to end, or accept it: a reload keeps every window and workspace. `node-display-session` receives `config` contributions, and this module places them with `${contribution:node-display-session:config}` near the end of its configuration, each module's -under a line naming it. `~/.config/i3/config.d/` is now the operator's alone: the include at the very +under a line naming it. `~/.config/i3/config.d/` holds only what is declared and named in `knows`: the include at the very end reads it after the mesh's lines. The catalogue-wide test composes every module's contribution as the controller does and checks the result with `i3 -C`. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `~/.config/i3`: holds only what this module declares and `scripts/` (the main file's screenshot binding runs `scripts/screenshot.sh`; kept until its owner exists); anything else is moved out by the node-engine to `~/.local/state/mesh-moved/i3/` with a list, never deleted (ADR 0422 §4). +- `~/.config/i3/config.d`: holds only what this module declares and `10-asus.conf`, `20-g14.conf` (the laptop's, kept until its hardware module exists); anything else is moved out by the node-engine to `~/.local/state/mesh-moved/i3/` with a list, never deleted (ADR 0422 §4). diff --git a/modules/i3/module.json b/modules/i3/module.json index 29fc3e85..1ec92426 100644 --- a/modules/i3/module.json +++ b/modules/i3/module.json @@ -72,14 +72,18 @@ "type": "directory", "path": "${machine:account-home}/.config/i3", "owner": "${machine:account}", - "mode": "0755" + "mode": "0755", + "unknown": "move-out", + "knows": ["scripts/"] }, { "id": "drop-ins", "type": "directory", "path": "${machine:account-home}/.config/i3/config.d", "owner": "${machine:account}", - "mode": "0755" + "mode": "0755", + "unknown": "move-out", + "knows": ["10-asus.conf", "20-g14.conf"] }, { "id": "config", diff --git a/modules/i3status-rust/README.md b/modules/i3status-rust/README.md index 8d3c9dda..77ca3cf8 100644 --- a/modules/i3status-rust/README.md +++ b/modules/i3status-rust/README.md @@ -80,9 +80,11 @@ a machine without the hardware, was rejected: the knowledge would stay in the ba other. It runs on both, once per session, and ends with the session. - The docker block's click, which ran a program installed on neither machine, is gone. -## What it leaves as found +## What it leaves, and what it moves out -- `~/.config/i3status-rust/scripts/` (a mouse battery script for one device). +- `~/.config/i3status-rust/scripts/` (a mouse battery script for one device) is not named in `knows`: the + bars do not use it, so the node-engine moves it out to `~/.local/state/mesh-moved/i3status-rust/` with a + list, never deleted (ADR 0422 §4). - `~/scripts/pkg-updates` and `~/scripts/i3-bar-watchdog`, replaced here. - The user unit `~/.config/systemd/user/i3-bar-watchdog.service` and its enablement link. @@ -112,3 +114,10 @@ The module no longer writes a file into i3's `config.d`. Its window-manager line under `files/i3/` where it had one) are a contribution to `node-display-session`. The i3 module places them in its own configuration under a `# ` line, so this module depends on a window manager being assigned beside it. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `~/.config/i3status-rust`: holds only what this module declares; anything else is moved out by the node-engine to `~/.local/state/mesh-moved/i3status-rust/` with a list, never deleted (ADR 0422 §4). +- `~/.config/i3status-rust/icons`: holds only what this module declares; anything else is moved out by the node-engine to `~/.local/state/mesh-moved/i3status-rust/` with a list, never deleted (ADR 0422 §4). diff --git a/modules/i3status-rust/module.json b/modules/i3status-rust/module.json index 6704d0dc..1e07556b 100644 --- a/modules/i3status-rust/module.json +++ b/modules/i3status-rust/module.json @@ -43,14 +43,16 @@ "type": "directory", "path": "${machine:account-home}/.config/i3status-rust", "owner": "${machine:account}", - "mode": "0755" + "mode": "0755", + "unknown": "move-out" }, { "id": "icons-dir", "type": "directory", "path": "${machine:account-home}/.config/i3status-rust/icons", "owner": "${machine:account}", - "mode": "0755" + "mode": "0755", + "unknown": "move-out" }, { "id": "top-bar", diff --git a/modules/icecast/README.md b/modules/icecast/README.md new file mode 100644 index 00000000..1d2eabc4 --- /dev/null +++ b/modules/icecast/README.md @@ -0,0 +1,11 @@ +# icecast + +The `icecast` module of the mesh catalogue: runs the container image `infiniteproject/icecast`. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:mesh-state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:logs}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/icecast/module.json b/modules/icecast/module.json index f8288be7..6f9ab549 100644 --- a/modules/icecast/module.json +++ b/modules/icecast/module.json @@ -66,19 +66,22 @@ "id": "mesh-state", "type": "directory", "mode": "0700", - "place": "mesh" + "place": "mesh", + "unknown": "program" }, { "id": "state", "type": "directory", "mode": "0700", - "place": "." + "place": ".", + "unknown": "program" }, { "id": "logs", "type": "directory", "mode": "0700", - "owner": "100:101" + "owner": "100:101", + "unknown": "program" }, { "id": "server-conf", diff --git a/modules/influxdb/README.md b/modules/influxdb/README.md new file mode 100644 index 00000000..f021b6a5 --- /dev/null +++ b/modules/influxdb/README.md @@ -0,0 +1,13 @@ +# influxdb + +The `influxdb` module of the mesh catalogue: runs the container image `influxdb`. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:mesh-state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:data}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:config}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:grants}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/influxdb/module.json b/modules/influxdb/module.json index 2d73b8d7..53389a5f 100644 --- a/modules/influxdb/module.json +++ b/modules/influxdb/module.json @@ -88,30 +88,35 @@ "id": "mesh-state", "type": "directory", "mode": "0700", - "place": "mesh" + "place": "mesh", + "unknown": "program" }, { "id": "state", "type": "directory", "mode": "0700", - "place": "." + "place": ".", + "unknown": "program" }, { "id": "data", "type": "directory", "mode": "0700", - "owner": "1000:1000" + "owner": "1000:1000", + "unknown": "program" }, { "id": "config", "type": "directory", "mode": "0700", - "owner": "1000:1000" + "owner": "1000:1000", + "unknown": "program" }, { "id": "grants", "type": "directory", - "mode": "0700" + "mode": "0700", + "unknown": "program" }, { "id": "server-env", diff --git a/modules/invoicing/README.md b/modules/invoicing/README.md new file mode 100644 index 00000000..b86e33c1 --- /dev/null +++ b/modules/invoicing/README.md @@ -0,0 +1,10 @@ +# invoicing + +The `invoicing` module of the mesh catalogue: runs the container image `registry-api.novox.be/novox/invoicing-app`; runs the container image `registry-api.novox.be/novox/invoicing-api`. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:mesh-state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/invoicing/module.json b/modules/invoicing/module.json index 5b846d1c..29670a3e 100644 --- a/modules/invoicing/module.json +++ b/modules/invoicing/module.json @@ -55,13 +55,15 @@ "id": "mesh-state", "type": "directory", "mode": "0700", - "place": "mesh" + "place": "mesh", + "unknown": "program" }, { "id": "state", "type": "directory", "mode": "0700", - "place": "." + "place": ".", + "unknown": "program" }, { "id": "api-env", diff --git a/modules/jira/README.md b/modules/jira/README.md new file mode 100644 index 00000000..21f19b2a --- /dev/null +++ b/modules/jira/README.md @@ -0,0 +1,9 @@ +# jira + +The `jira` module of the mesh catalogue: declares directories on the node. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/jira/module.json b/modules/jira/module.json index eceb279d..9f98dd83 100644 --- a/modules/jira/module.json +++ b/modules/jira/module.json @@ -23,7 +23,8 @@ "id": "state", "type": "directory", "mode": "0700", - "place": "." + "place": ".", + "unknown": "program" }, { "id": "config", diff --git a/modules/kernel/README.md b/modules/kernel/README.md index 09405354..6acb6a37 100644 --- a/modules/kernel/README.md +++ b/modules/kernel/README.md @@ -50,3 +50,9 @@ rotates the journal once and not for `always` nor while the machine starts, goes reload without rewriting what it found, gives back what it found and then keeps nothing, refuses a value the kernel does not take before writing anything, fails when the write does not take, and says when the kernel has no transparent huge pages. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/kernel/module.json b/modules/kernel/module.json index 7305c945..67d32156 100644 --- a/modules/kernel/module.json +++ b/modules/kernel/module.json @@ -20,7 +20,8 @@ "id": "state", "type": "directory", "mode": "0700", - "place": "." + "place": ".", + "unknown": "program" }, { "id": "huge-pages", diff --git a/modules/keycloak/README.md b/modules/keycloak/README.md index e786fc09..925e00de 100644 --- a/modules/keycloak/README.md +++ b/modules/keycloak/README.md @@ -83,3 +83,11 @@ capability. `live_test.go` runs the repair against a real, throwaway Keycloak 26 container whose admin keeps an older password. The file's comment has the commands. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:mesh-state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:grants}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/keycloak/module.json b/modules/keycloak/module.json index 03749aa6..847d5f4f 100644 --- a/modules/keycloak/module.json +++ b/modules/keycloak/module.json @@ -116,18 +116,21 @@ "id": "mesh-state", "type": "directory", "mode": "0700", - "place": "mesh" + "place": "mesh", + "unknown": "program" }, { "id": "state", "type": "directory", "mode": "0700", - "place": "." + "place": ".", + "unknown": "program" }, { "id": "grants", "type": "directory", - "mode": "0700" + "mode": "0700", + "unknown": "program" }, { "id": "consumers", diff --git a/modules/lab/README.md b/modules/lab/README.md new file mode 100644 index 00000000..17db440e --- /dev/null +++ b/modules/lab/README.md @@ -0,0 +1,10 @@ +# lab + +The `lab` module of the mesh catalogue: installs the package `git`; installs the package `make`; installs the package `python`; installs the package `file`; installs the package `iproute2`; installs the package `npm`; installs the package `go`; installs the package `incus`. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `/var/lib/mesh-lab-runs`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/lab/module.json b/modules/lab/module.json index 66bb4b43..e23963df 100644 --- a/modules/lab/module.json +++ b/modules/lab/module.json @@ -42,13 +42,15 @@ "id": "state", "type": "directory", "mode": "0700", - "place": "." + "place": ".", + "unknown": "program" }, { "id": "work", "type": "directory", "path": "/var/lib/mesh-lab-runs", - "mode": "0700" + "mode": "0700", + "unknown": "program" }, { "id": "runtime-env", diff --git a/modules/lemurs/README.md b/modules/lemurs/README.md index ebe732b3..17e2e34a 100644 --- a/modules/lemurs/README.md +++ b/modules/lemurs/README.md @@ -109,3 +109,10 @@ merges the X resources before any long-lived client connects. On the first login the server reset twice within four seconds: the X log showed three server generations. xterm fell back to the bitmap `fixed` font, without the mesh's face and colours. lemurs reads its configuration when it starts, so the change applies after the next boot. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `/etc/lemurs/xsessions`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `/etc/lemurs/wayland-sessions`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/lemurs/module.json b/modules/lemurs/module.json index abaf8b11..702a9596 100644 --- a/modules/lemurs/module.json +++ b/modules/lemurs/module.json @@ -43,13 +43,15 @@ "id": "xsessions", "type": "directory", "path": "/etc/lemurs/xsessions", - "mode": "0755" + "mode": "0755", + "unknown": "program" }, { "id": "wayland-sessions", "type": "directory", "path": "/etc/lemurs/wayland-sessions", - "mode": "0755" + "mode": "0755", + "unknown": "program" }, { "id": "service", diff --git a/modules/letta/README.md b/modules/letta/README.md new file mode 100644 index 00000000..ee4af0ff --- /dev/null +++ b/modules/letta/README.md @@ -0,0 +1,10 @@ +# letta + +The `letta` module of the mesh catalogue: runs the container image `letta/letta`. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:mesh-state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/letta/module.json b/modules/letta/module.json index dbcc5590..de7baf64 100644 --- a/modules/letta/module.json +++ b/modules/letta/module.json @@ -60,13 +60,15 @@ "id": "mesh-state", "type": "directory", "mode": "0700", - "place": "mesh" + "place": "mesh", + "unknown": "program" }, { "id": "state", "type": "directory", "mode": "0700", - "place": "." + "place": ".", + "unknown": "program" }, { "id": "server-env", diff --git a/modules/local-model-consumer/README.md b/modules/local-model-consumer/README.md new file mode 100644 index 00000000..38255e61 --- /dev/null +++ b/modules/local-model-consumer/README.md @@ -0,0 +1,10 @@ +# local-model-consumer + +The `local-model-consumer` module of the mesh catalogue: declares directories on the node. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:config}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/local-model-consumer/module.json b/modules/local-model-consumer/module.json index 6fdb90b5..73002092 100644 --- a/modules/local-model-consumer/module.json +++ b/modules/local-model-consumer/module.json @@ -13,12 +13,14 @@ "id": "state", "type": "directory", "mode": "0700", - "place": "." + "place": ".", + "unknown": "program" }, { "id": "config", "type": "directory", - "mode": "0700" + "mode": "0700", + "unknown": "program" }, { "id": "openai-env", diff --git a/modules/mailu/README.md b/modules/mailu/README.md index ba5d3443..e87c4f61 100644 --- a/modules/mailu/README.md +++ b/modules/mailu/README.md @@ -15,3 +15,28 @@ installation's are settings on the assignment, `settings set mailu `: `website` are shown by the web front; `proxy-address` is what `REAL_IP_FROM` trusts a real-IP header from — the address the proxy forwards with. The front's own hostname is the name of its `web` route, told to it by the mesh. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:mesh-state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:grants}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:data-automx}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:data-certs}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:data-data}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:data-dkim}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:data-mail}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:data-mailqueue}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:data-filter}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:data-clamav}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:data-redis}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:data-webmail}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:data-dav}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:data-fetchmail}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:data-overrides-nginx}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:data-overrides-dovecot}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:data-overrides-postfix}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:data-overrides-rspamd}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:data-overrides-roundcube}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/mailu/module.json b/modules/mailu/module.json index 52750c98..568be145 100644 --- a/modules/mailu/module.json +++ b/modules/mailu/module.json @@ -291,23 +291,27 @@ "id": "mesh-state", "type": "directory", "mode": "0700", - "place": "mesh" + "place": "mesh", + "unknown": "program" }, { "id": "state", "type": "directory", "mode": "0700", - "place": "." + "place": ".", + "unknown": "program" }, { "id": "grants", "type": "directory", - "mode": "0700" + "mode": "0700", + "unknown": "program" }, { "id": "data-automx", "type": "directory", - "mode": "0700" + "mode": "0700", + "unknown": "program" }, { "id": "config-env", @@ -340,82 +344,98 @@ { "id": "data-certs", "type": "directory", - "mode": "0700" + "mode": "0700", + "unknown": "program" }, { "id": "data-data", "type": "directory", - "mode": "0700" + "mode": "0700", + "unknown": "program" }, { "id": "data-dkim", "type": "directory", - "mode": "0700" + "mode": "0700", + "unknown": "program" }, { "id": "data-mail", "type": "directory", - "mode": "0700" + "mode": "0700", + "unknown": "program" }, { "id": "data-mailqueue", "type": "directory", - "mode": "0755" + "mode": "0755", + "unknown": "program" }, { "id": "data-filter", "type": "directory", - "mode": "0700" + "mode": "0700", + "unknown": "program" }, { "id": "data-clamav", "type": "directory", - "mode": "0700" + "mode": "0700", + "unknown": "program" }, { "id": "data-redis", "type": "directory", - "mode": "0700" + "mode": "0700", + "unknown": "program" }, { "id": "data-webmail", "type": "directory", - "mode": "0700" + "mode": "0700", + "unknown": "program" }, { "id": "data-dav", "type": "directory", - "mode": "0700" + "mode": "0700", + "unknown": "program" }, { "id": "data-fetchmail", "type": "directory", - "mode": "0700" + "mode": "0700", + "unknown": "program" }, { "id": "data-overrides-nginx", "type": "directory", - "mode": "0700" + "mode": "0700", + "unknown": "program" }, { "id": "data-overrides-dovecot", "type": "directory", - "mode": "0700" + "mode": "0700", + "unknown": "program" }, { "id": "data-overrides-postfix", "type": "directory", - "mode": "0700" + "mode": "0700", + "unknown": "program" }, { "id": "data-overrides-rspamd", "type": "directory", - "mode": "0700" + "mode": "0700", + "unknown": "program" }, { "id": "data-overrides-roundcube", "type": "directory", - "mode": "0700" + "mode": "0700", + "unknown": "program" }, { "id": "net", diff --git a/modules/marrytts/README.md b/modules/marrytts/README.md new file mode 100644 index 00000000..1bf579d0 --- /dev/null +++ b/modules/marrytts/README.md @@ -0,0 +1,9 @@ +# marrytts + +The `marrytts` module of the mesh catalogue: runs the container image `synesthesiam/marytts`. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:mesh-state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/marrytts/module.json b/modules/marrytts/module.json index bd12abfa..ed242aa3 100644 --- a/modules/marrytts/module.json +++ b/modules/marrytts/module.json @@ -18,7 +18,8 @@ "id": "mesh-state", "type": "directory", "mode": "0700", - "place": "mesh" + "place": "mesh", + "unknown": "program" }, { "id": "server", diff --git a/modules/matrix/README.md b/modules/matrix/README.md new file mode 100644 index 00000000..7e513bb1 --- /dev/null +++ b/modules/matrix/README.md @@ -0,0 +1,10 @@ +# matrix + +The `matrix` module of the mesh catalogue: runs the container image `matrixconduit/matrix-conduit`; runs the container image `vectorim/element-web`. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:db}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/matrix/module.json b/modules/matrix/module.json index 2c044576..170719cc 100644 --- a/modules/matrix/module.json +++ b/modules/matrix/module.json @@ -160,12 +160,14 @@ "id": "state", "type": "directory", "mode": "0700", - "place": "." + "place": ".", + "unknown": "program" }, { "id": "db", "type": "directory", - "mode": "0700" + "mode": "0700", + "unknown": "program" }, { "id": "conduit-conf", diff --git a/modules/memory-pressure/README.md b/modules/memory-pressure/README.md index 8c4d0c75..ee76aecc 100644 --- a/modules/memory-pressure/README.md +++ b/modules/memory-pressure/README.md @@ -118,3 +118,12 @@ zram statistics and PSI lines. They cover: - `since` refused when it is an option; - *no match* read as no kills; - the manifest: tools listed equal tools served, events, no machine named, triggers exist. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `/etc/sysctl.d`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `/etc/systemd/oomd.conf.d`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `/etc/systemd/system/-.slice.d`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `/etc/systemd/system/user@.service.d`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/memory-pressure/module.json b/modules/memory-pressure/module.json index 76ae53be..1af96e30 100644 --- a/modules/memory-pressure/module.json +++ b/modules/memory-pressure/module.json @@ -34,7 +34,8 @@ "id": "sysctl-drop-ins", "type": "directory", "path": "/etc/sysctl.d", - "mode": "0755" + "mode": "0755", + "unknown": "program" }, { "id": "swap-tunables", @@ -55,7 +56,8 @@ "id": "oomd-drop-ins", "type": "directory", "path": "/etc/systemd/oomd.conf.d", - "mode": "0755" + "mode": "0755", + "unknown": "program" }, { "id": "oomd-limits", @@ -68,7 +70,8 @@ "id": "root-slice-drop-ins", "type": "directory", "path": "/etc/systemd/system/-.slice.d", - "mode": "0755" + "mode": "0755", + "unknown": "program" }, { "id": "root-slice", @@ -81,7 +84,8 @@ "id": "user-manager-drop-ins", "type": "directory", "path": "/etc/systemd/system/user@.service.d", - "mode": "0755" + "mode": "0755", + "unknown": "program" }, { "id": "user-manager", diff --git a/modules/mesh-catalog/README.md b/modules/mesh-catalog/README.md new file mode 100644 index 00000000..32e34d2f --- /dev/null +++ b/modules/mesh-catalog/README.md @@ -0,0 +1,9 @@ +# mesh-catalog + +The `mesh-catalog` module of the mesh catalogue: claims the seat `mesh-catalog`. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/mesh-catalog/module.json b/modules/mesh-catalog/module.json index 5a263800..8d8c6599 100644 --- a/modules/mesh-catalog/module.json +++ b/modules/mesh-catalog/module.json @@ -41,7 +41,8 @@ "id": "state", "type": "directory", "mode": "0700", - "place": "." + "place": ".", + "unknown": "program" }, { "id": "database-url", diff --git a/modules/mesh-cli/README.md b/modules/mesh-cli/README.md index 6dfb4cab..74f2387f 100644 --- a/modules/mesh-cli/README.md +++ b/modules/mesh-cli/README.md @@ -62,3 +62,9 @@ operator. alone); the line is asked as given and the answer printed with its exit; a reason, a refusal, a cut and an outcome not known are said and a refusal never exits 0; a verb's process is refused before asking; an engine that does not answer is said. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `/usr/local/bin`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/mesh-cli/module.json b/modules/mesh-cli/module.json index a72f91ac..681a3e55 100644 --- a/modules/mesh-cli/module.json +++ b/modules/mesh-cli/module.json @@ -7,7 +7,8 @@ "id": "program", "type": "archive", "artifact": "program", - "path": "/usr/local/bin" + "path": "/usr/local/bin", + "unknown": "program" } ], "items": [ diff --git a/modules/mesh-config/README.md b/modules/mesh-config/README.md index 7a9ab019..b8ac192f 100644 --- a/modules/mesh-config/README.md +++ b/modules/mesh-config/README.md @@ -50,3 +50,9 @@ and sets nothing, when the proposal or the value stored at the level changed sin value as a set does, keeps who approved it beside the value (`approved`), and asks the controller to record the warrant in its hand-act log. A Decline, an expiry or the router's refusal sets nothing. `action` `proposals` lists them, newest first; with `proposal`, one read whole, with whether the router's ask is the one composed from it. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/mesh-config/module.json b/modules/mesh-config/module.json index 9665b54c..e03e891b 100644 --- a/modules/mesh-config/module.json +++ b/modules/mesh-config/module.json @@ -52,7 +52,8 @@ "type": "directory", "mode": "0700", "owner": "mesh-config", - "place": "." + "place": ".", + "unknown": "program" } ], "build": { diff --git a/modules/mesh-delivery/README.md b/modules/mesh-delivery/README.md new file mode 100644 index 00000000..18b78dcb --- /dev/null +++ b/modules/mesh-delivery/README.md @@ -0,0 +1,9 @@ +# mesh-delivery + +The `mesh-delivery` module of the mesh catalogue: claims the seat `mesh-delivery`. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/mesh-delivery/module.json b/modules/mesh-delivery/module.json index 80f1cc49..7a445a3a 100644 --- a/modules/mesh-delivery/module.json +++ b/modules/mesh-delivery/module.json @@ -76,7 +76,8 @@ "id": "state", "type": "directory", "mode": "0700", - "place": "." + "place": ".", + "unknown": "program" } ], "build": { diff --git a/modules/mesh-files/README.md b/modules/mesh-files/README.md index f844592b..b2e49215 100644 --- a/modules/mesh-files/README.md +++ b/modules/mesh-files/README.md @@ -61,3 +61,9 @@ an override the override and the owned paths it went through. The caller is the name for `read`, `list` and `search`. Copying or moving such a place is allowed: nothing is printed. - A copy or move to or from `:/path` is refused: copying between nodes comes in step 2 (issue 903). - A write without a reason; a path that is not absolute; a mode that is not a mode. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/mesh-files/module.json b/modules/mesh-files/module.json index 867222a3..cc1df162 100644 --- a/modules/mesh-files/module.json +++ b/modules/mesh-files/module.json @@ -32,7 +32,8 @@ "id": "state", "type": "directory", "mode": "0700", - "place": "." + "place": ".", + "unknown": "program" } ], "build": { diff --git a/modules/mesh-issues/README.md b/modules/mesh-issues/README.md index 33287bf0..865dda67 100644 --- a/modules/mesh-issues/README.md +++ b/modules/mesh-issues/README.md @@ -421,3 +421,9 @@ the carry carrying each value kept, oldest first, once, passing over a deletion write to an issue the bucket kept before the switch carries its three kept values first, read through the runtime's history. Over the bus, the stream the controller makes holds one operation per change of `TestAnIssueIsOpenedNotedLinkedMovedAndListedOverTheBus`. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/mesh-issues/files/settings.json b/modules/mesh-issues/files/settings.json index 2adad465..f4b7ba1a 100644 --- a/modules/mesh-issues/files/settings.json +++ b/modules/mesh-issues/files/settings.json @@ -12,7 +12,6 @@ "mesh-catalog modules/betterbird", "mesh-catalog modules/blueman", "mesh-catalog modules/bluetooth", - "mesh-catalog modules/build-agent", "mesh-catalog modules/ca-trust", "mesh-catalog modules/claude-code", "mesh-catalog modules/claude-licence-manager", @@ -98,7 +97,6 @@ "mesh-catalog modules/restic", "mesh-catalog modules/rofi", "mesh-catalog modules/route-adapter", - "mesh-catalog modules/route-proxy", "mesh-catalog modules/screen-lock", "mesh-catalog modules/searxng", "mesh-catalog modules/sensors", @@ -128,6 +126,8 @@ "mesh-catalog modules/zsh-autosuggestions", "mesh-catalog modules/zsh-syntax-highlighting", "mesh-controller", + "mesh-controller modules/build-agent", + "mesh-controller modules/route-proxy", "mesh-host", "mesh-lab", "mesh-media-catalog", diff --git a/modules/mesh-issues/module.json b/modules/mesh-issues/module.json index e911c2b1..9d5f9d94 100644 --- a/modules/mesh-issues/module.json +++ b/modules/mesh-issues/module.json @@ -105,7 +105,6 @@ "mesh-catalog modules/betterbird", "mesh-catalog modules/blueman", "mesh-catalog modules/bluetooth", - "mesh-catalog modules/build-agent", "mesh-catalog modules/ca-trust", "mesh-catalog modules/claude-code", "mesh-catalog modules/claude-licence-manager", @@ -191,7 +190,6 @@ "mesh-catalog modules/restic", "mesh-catalog modules/rofi", "mesh-catalog modules/route-adapter", - "mesh-catalog modules/route-proxy", "mesh-catalog modules/screen-lock", "mesh-catalog modules/searxng", "mesh-catalog modules/sensors", @@ -221,6 +219,8 @@ "mesh-catalog modules/zsh-autosuggestions", "mesh-catalog modules/zsh-syntax-highlighting", "mesh-controller", + "mesh-controller modules/build-agent", + "mesh-controller modules/route-proxy", "mesh-host", "mesh-lab", "mesh-media-catalog", @@ -287,7 +287,8 @@ "id": "state", "type": "directory", "mode": "0700", - "place": "." + "place": ".", + "unknown": "program" }, { "id": "settings", diff --git a/modules/mesh-knowledge/README.md b/modules/mesh-knowledge/README.md index 8bef489d..7cb938b1 100644 --- a/modules/mesh-knowledge/README.md +++ b/modules/mesh-knowledge/README.md @@ -235,3 +235,9 @@ listening on no port. A machine with neither skips the postgres half and says so `testdata/hq` holds eleven records at the commit in its `COMMIT` file, and `testdata/hq-main.tar.gz` holds the whole of main at `testdata/hq-main.commit`. The bus test starts the bundle as the runtime does and plays the runtime over a real NATS server. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/mesh-knowledge/module.json b/modules/mesh-knowledge/module.json index 261662be..c57687b6 100644 --- a/modules/mesh-knowledge/module.json +++ b/modules/mesh-knowledge/module.json @@ -100,7 +100,8 @@ "id": "state", "type": "directory", "mode": "0700", - "place": "." + "place": ".", + "unknown": "program" }, { "id": "database-url", diff --git a/modules/mesh-mail-notifier/README.md b/modules/mesh-mail-notifier/README.md index 9891e96b..29f8a720 100644 --- a/modules/mesh-mail-notifier/README.md +++ b/modules/mesh-mail-notifier/README.md @@ -38,3 +38,9 @@ accounts it announces, in mesh-mail's trusted setting `grants` (`mail-store`'s s |---|---| | `mail-notifier.status` | every account it is granted, when it last showed each one a notice, and any notice still failing to send (its count, last error, since when). Read-only | | `mail_notifier_health` | how it is: unhealthy once a notice has failed to show for more than ten minutes, healthy otherwise, even mid-retry. Not (yet) the node-engine's own check — this is one process, judged only by its own tool (ADR 0227 rule 8); a watching process of its own is issue 841, not yet decided. Read-only | + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/mesh-mail-notifier/module.json b/modules/mesh-mail-notifier/module.json index dec4d77a..9afba466 100644 --- a/modules/mesh-mail-notifier/module.json +++ b/modules/mesh-mail-notifier/module.json @@ -46,7 +46,8 @@ "type": "directory", "mode": "0700", "owner": "mesh-mail-notifier", - "place": "." + "place": ".", + "unknown": "program" } ], "build": { diff --git a/modules/mesh-mail/README.md b/modules/mesh-mail/README.md index 51d133d9..f7b77338 100644 --- a/modules/mesh-mail/README.md +++ b/modules/mesh-mail/README.md @@ -79,3 +79,9 @@ above — the same grants, the same refusals, the same ask-then-Approve for a se `prepare` brings the index's schema to this version's (numbered migrations, and `schema.sql` for a fresh database). `keep` watches the accounts and is judged by its unit. `watch` judges every minute and is judged by `mail_health`. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/mesh-mail/module.json b/modules/mesh-mail/module.json index ae445990..38a5ccea 100644 --- a/modules/mesh-mail/module.json +++ b/modules/mesh-mail/module.json @@ -103,7 +103,8 @@ "id": "state", "type": "directory", "mode": "0700", - "place": "." + "place": ".", + "unknown": "program" }, { "id": "database-url", diff --git a/modules/mesh-provisioner-client/README.md b/modules/mesh-provisioner-client/README.md index 62c1ec4c..da3bae5b 100644 --- a/modules/mesh-provisioner-client/README.md +++ b/modules/mesh-provisioner-client/README.md @@ -86,3 +86,9 @@ module in the tool runner reads its secrets from its files. A binding's non-secr into files (issue 679) and not yet answered on an ask. It says on `prov.changed..` which value moved (`{kind, key}`, every key of the kind when `key` is absent, never the value), when the client starts and on every change, so the tool runner asks again for the watches it holds. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/mesh-provisioner-client/module.json b/modules/mesh-provisioner-client/module.json index af84e540..095128a8 100644 --- a/modules/mesh-provisioner-client/module.json +++ b/modules/mesh-provisioner-client/module.json @@ -24,7 +24,8 @@ "type": "directory", "mode": "0700", "owner": "mesh-provisioner-client", - "place": "." + "place": ".", + "unknown": "program" }, { "id": "client", diff --git a/modules/mesh-provisioner/README.md b/modules/mesh-provisioner/README.md index 88c246b3..383d216e 100644 --- a/modules/mesh-provisioner/README.md +++ b/modules/mesh-provisioner/README.md @@ -31,3 +31,9 @@ reads the needs without writing them. This module writes only the registry. `mesh-provisioner.provisioning` with `what` `registry` (the default), `needs`, `applied` or `collect`, narrowed by `node` and `module` (for `collect`, the contributor) and, for `collect`, `collection`. Reads only. A key out of its bucket's shape is said in the answer, never dropped quietly. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/mesh-provisioner/module.json b/modules/mesh-provisioner/module.json index 11133aef..650425b3 100644 --- a/modules/mesh-provisioner/module.json +++ b/modules/mesh-provisioner/module.json @@ -34,7 +34,8 @@ "type": "directory", "mode": "0700", "owner": "mesh-provisioner", - "place": "." + "place": ".", + "unknown": "program" } ], "build": { diff --git a/modules/mesh-reads/README.md b/modules/mesh-reads/README.md index 2981bdc3..5112a9b0 100644 --- a/modules/mesh-reads/README.md +++ b/modules/mesh-reads/README.md @@ -61,3 +61,9 @@ token. | `account` | `getent passwd`, `getent group`, `id`, `groups` | 2000 accounts and groups; never a password field | No answer is larger than 1 MiB. Nothing here writes a file. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/mesh-reads/module.json b/modules/mesh-reads/module.json index 4a664485..18062752 100644 --- a/modules/mesh-reads/module.json +++ b/modules/mesh-reads/module.json @@ -33,7 +33,8 @@ "type": "directory", "mode": "0700", "owner": "mesh-reads", - "place": "." + "place": ".", + "unknown": "program" } ], "build": { diff --git a/modules/mesh-vault/README.md b/modules/mesh-vault/README.md index d51bdd7a..429b861a 100644 --- a/modules/mesh-vault/README.md +++ b/modules/mesh-vault/README.md @@ -277,3 +277,13 @@ the vault answers it (recorded before the prompt, one open ask per secret and th the bus named it, the typed value opened only here), the issuing provider's test with a stand-in provider (ADR 0305 §13's checks that belong to the vault: nothing rewritten for a value held, `rotate` refused, no other way in), and accept and rotate. `followups_test.go` holds V1's review follow-ups. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:grants}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:ledger}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:root}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:store}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/mesh-vault/module.json b/modules/mesh-vault/module.json index 83ee6cd4..44944c95 100644 --- a/modules/mesh-vault/module.json +++ b/modules/mesh-vault/module.json @@ -86,31 +86,36 @@ "type": "directory", "mode": "0700", "owner": "mesh-vault", - "place": "." + "place": ".", + "unknown": "program" }, { "id": "grants", "type": "directory", "mode": "0700", - "owner": "mesh-vault" + "owner": "mesh-vault", + "unknown": "program" }, { "id": "ledger", "type": "directory", "mode": "0700", - "owner": "mesh-vault" + "owner": "mesh-vault", + "unknown": "program" }, { "id": "root", "type": "directory", "mode": "0700", - "owner": "mesh-vault" + "owner": "mesh-vault", + "unknown": "program" }, { "id": "store", "type": "directory", "mode": "0700", - "owner": "mesh-vault" + "owner": "mesh-vault", + "unknown": "program" } ], "build": { diff --git a/modules/mesh-watcher/README.md b/modules/mesh-watcher/README.md index d311fa82..f107520e 100644 --- a/modules/mesh-watcher/README.md +++ b/modules/mesh-watcher/README.md @@ -36,3 +36,9 @@ when the parts that would tell them are what failed. | `watcher_last_heard` | when each signal was last heard, and what it sent lately | | `watcher_test` | a test message to Telegram now, directly | | `watcher_ping` | the bus round trip's other end | + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/mesh-watcher/module.json b/modules/mesh-watcher/module.json index 798fcf20..e9543a5d 100644 --- a/modules/mesh-watcher/module.json +++ b/modules/mesh-watcher/module.json @@ -31,7 +31,8 @@ "id": "state", "type": "directory", "mode": "0700", - "place": "." + "place": ".", + "unknown": "program" }, { "id": "settings", diff --git a/modules/messenger/README.md b/modules/messenger/README.md index 10b86afd..5a5a26ba 100644 --- a/modules/messenger/README.md +++ b/modules/messenger/README.md @@ -253,3 +253,9 @@ runtime-carried module's are). It holds no secret. until the runtime takes a seat's queue for a bundle (mesh-tools). - **Channels are inside the holder**, not modules contributing to the seat: a seat a module declares cannot receive contributions yet (ADR 0212 kinds are compiled for the mesh's own seats). + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/messenger/module.json b/modules/messenger/module.json index 89d0b3a6..7af1c177 100644 --- a/modules/messenger/module.json +++ b/modules/messenger/module.json @@ -145,7 +145,8 @@ "type": "directory", "mode": "0700", "place": ".", - "owner": "messenger" + "owner": "messenger", + "unknown": "program" }, { "id": "settings", diff --git a/modules/minio/README.md b/modules/minio/README.md new file mode 100644 index 00000000..44447bd1 --- /dev/null +++ b/modules/minio/README.md @@ -0,0 +1,11 @@ +# minio + +The `minio` module of the mesh catalogue: runs the container image `docker.io/pgsty/minio`; installs the package `minio-client`. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:grants}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `/var/lib/minio-store`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/minio/module.json b/modules/minio/module.json index fd59501e..85b82530 100644 --- a/modules/minio/module.json +++ b/modules/minio/module.json @@ -92,12 +92,14 @@ "id": "state", "type": "directory", "mode": "0700", - "place": "." + "place": ".", + "unknown": "program" }, { "id": "grants", "type": "directory", - "mode": "0700" + "mode": "0700", + "unknown": "program" }, { "id": "root-env", @@ -110,7 +112,8 @@ "id": "data", "type": "directory", "path": "/var/lib/minio-store", - "mode": "0700" + "mode": "0700", + "unknown": "program" }, { "id": "net", diff --git a/modules/model-usage/README.md b/modules/model-usage/README.md new file mode 100644 index 00000000..732b149f --- /dev/null +++ b/modules/model-usage/README.md @@ -0,0 +1,10 @@ +# model-usage + +The `model-usage` module of the mesh catalogue: declares directories on the node. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:spool}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/model-usage/module.json b/modules/model-usage/module.json index 0ab5703d..50743a3c 100644 --- a/modules/model-usage/module.json +++ b/modules/model-usage/module.json @@ -43,7 +43,8 @@ "id": "state", "type": "directory", "mode": "0700", - "place": "." + "place": ".", + "unknown": "program" }, { "id": "database-url", @@ -55,7 +56,8 @@ { "id": "spool", "type": "directory", - "mode": "0700" + "mode": "0700", + "unknown": "program" }, { "id": "prepare", diff --git a/modules/mongodb/README.md b/modules/mongodb/README.md new file mode 100644 index 00000000..12d9e3ef --- /dev/null +++ b/modules/mongodb/README.md @@ -0,0 +1,12 @@ +# mongodb + +The `mongodb` module of the mesh catalogue: runs the container image `mongo`. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:grants}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:data}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:dumps}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/mongodb/module.json b/modules/mongodb/module.json index de9d9105..cbb12681 100644 --- a/modules/mongodb/module.json +++ b/modules/mongodb/module.json @@ -81,22 +81,26 @@ "id": "state", "type": "directory", "mode": "0700", - "place": "." + "place": ".", + "unknown": "program" }, { "id": "grants", "type": "directory", - "mode": "0700" + "mode": "0700", + "unknown": "program" }, { "id": "data", "type": "directory", - "mode": "0700" + "mode": "0700", + "unknown": "program" }, { "id": "dumps", "type": "directory", - "mode": "0700" + "mode": "0700", + "unknown": "program" }, { "id": "net", diff --git a/modules/mosquitto/README.md b/modules/mosquitto/README.md new file mode 100644 index 00000000..9b7c4682 --- /dev/null +++ b/modules/mosquitto/README.md @@ -0,0 +1,12 @@ +# mosquitto + +The `mosquitto` module of the mesh catalogue: runs the container image `eclipse-mosquitto`. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:mesh-state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:grants}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:data}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/mosquitto/module.json b/modules/mosquitto/module.json index e9cb913c..21058e0a 100644 --- a/modules/mosquitto/module.json +++ b/modules/mosquitto/module.json @@ -78,24 +78,28 @@ "id": "mesh-state", "type": "directory", "mode": "0700", - "place": "mesh" + "place": "mesh", + "unknown": "program" }, { "id": "state", "type": "directory", "mode": "0700", - "place": "." + "place": ".", + "unknown": "program" }, { "id": "grants", "type": "directory", - "mode": "0700" + "mode": "0700", + "unknown": "program" }, { "id": "data", "type": "directory", "mode": "0700", - "owner": "1883:1883" + "owner": "1883:1883", + "unknown": "program" }, { "id": "server-conf", diff --git a/modules/mounts/README.md b/modules/mounts/README.md index 65cac223..83f72dd8 100644 --- a/modules/mounts/README.md +++ b/modules/mounts/README.md @@ -210,3 +210,12 @@ The tools run as the operator account and act as root through `sudo -n`. The `su account every command, so **this module's code is the only line** between a tool call and root: each act checks again what it may touch before it runs a command, and a test holds each check. Narrowing what sudo grants the account to the commands these modules need is a separate decision, not made here. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `/etc/mesh-mounts`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `/var/lib/mesh-mounts`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `/var/lib/mesh-mounts/from-bus`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/mounts/module.json b/modules/mounts/module.json index 9424dc1c..488ebb97 100644 --- a/modules/mounts/module.json +++ b/modules/mounts/module.json @@ -73,13 +73,15 @@ "id": "state", "type": "directory", "mode": "0700", - "place": "." + "place": ".", + "unknown": "program" }, { "id": "config-dir", "type": "directory", "path": "/etc/mesh-mounts", - "mode": "0755" + "mode": "0755", + "unknown": "program" }, { "id": "config", @@ -92,14 +94,16 @@ "id": "bus-dir-parent", "type": "directory", "path": "/var/lib/mesh-mounts", - "mode": "0755" + "mode": "0755", + "unknown": "program" }, { "id": "bus-dir", "type": "directory", "path": "/var/lib/mesh-mounts/from-bus", "mode": "0755", - "owner": "${machine:account}" + "owner": "${machine:account}", + "unknown": "program" }, { "id": "apply", diff --git a/modules/mssql/README.md b/modules/mssql/README.md new file mode 100644 index 00000000..899cac99 --- /dev/null +++ b/modules/mssql/README.md @@ -0,0 +1,12 @@ +# mssql + +The `mssql` module of the mesh catalogue: runs the container image `mcr.microsoft.com/mssql/server`. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:grants}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:data}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:data}/backup`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/mssql/module.json b/modules/mssql/module.json index e30276c9..ed4e4488 100644 --- a/modules/mssql/module.json +++ b/modules/mssql/module.json @@ -80,12 +80,14 @@ "id": "state", "type": "directory", "mode": "0700", - "place": "." + "place": ".", + "unknown": "program" }, { "id": "grants", "type": "directory", - "mode": "0700" + "mode": "0700", + "unknown": "program" }, { "id": "sa-env", @@ -98,14 +100,16 @@ "id": "data", "type": "directory", "mode": "0700", - "owner": "10001:0" + "owner": "10001:0", + "unknown": "program" }, { "id": "dumps", "type": "directory", "path": "${dir:data}/backup", "mode": "0700", - "owner": "10001:0" + "owner": "10001:0", + "unknown": "program" }, { "id": "net", diff --git a/modules/n8n/README.md b/modules/n8n/README.md new file mode 100644 index 00000000..c911c864 --- /dev/null +++ b/modules/n8n/README.md @@ -0,0 +1,11 @@ +# n8n + +The `n8n` module of the mesh catalogue: runs the container image `redis`; runs the container image `selenium/standalone-chrome`. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:data}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:cache}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/n8n/module.json b/modules/n8n/module.json index 23874b61..975247fa 100644 --- a/modules/n8n/module.json +++ b/modules/n8n/module.json @@ -60,19 +60,22 @@ "id": "state", "type": "directory", "mode": "0700", - "place": "." + "place": ".", + "unknown": "program" }, { "id": "data", "type": "directory", "mode": "0700", - "owner": "1000:1000" + "owner": "1000:1000", + "unknown": "program" }, { "id": "cache", "type": "directory", "mode": "0700", - "owner": "999:999" + "owner": "999:999", + "unknown": "program" }, { "id": "database-secret", diff --git a/modules/nats/README.md b/modules/nats/README.md index f07245fa..c9d3c046 100644 --- a/modules/nats/README.md +++ b/modules/nats/README.md @@ -85,3 +85,12 @@ reads (`nats stream restore ` on an unpacked `streams//`). `MESH_TEST_NATS_IMAGE=` also runs the manifest's declared dump, exactly, against the module's image and configuration. - `cmd/nats-tools`: `go test ./...`. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `/var/lib/mesh-broker-nats`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `/var/lib/nats-module/conf`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:snapshots}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:mesh-state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/nats/module.json b/modules/nats/module.json index 3c633473..3ad66bc3 100644 --- a/modules/nats/module.json +++ b/modules/nats/module.json @@ -75,24 +75,28 @@ "id": "jetstream-data", "type": "directory", "path": "/var/lib/mesh-broker-nats", - "mode": "0700" + "mode": "0700", + "unknown": "program" }, { "id": "conf-dir", "type": "directory", "path": "/var/lib/nats-module/conf", - "mode": "0700" + "mode": "0700", + "unknown": "program" }, { "id": "snapshots", "type": "directory", - "mode": "0700" + "mode": "0700", + "unknown": "program" }, { "id": "mesh-state", "type": "directory", "mode": "0700", - "place": "mesh" + "place": "mesh", + "unknown": "program" }, { "id": "server-conf", diff --git a/modules/nextcloud/README.md b/modules/nextcloud/README.md new file mode 100644 index 00000000..366d7d89 --- /dev/null +++ b/modules/nextcloud/README.md @@ -0,0 +1,11 @@ +# nextcloud + +The `nextcloud` module of the mesh catalogue: runs the container image `nextcloud`. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:mesh-state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:html}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/nextcloud/module.json b/modules/nextcloud/module.json index c7d32627..2854a160 100644 --- a/modules/nextcloud/module.json +++ b/modules/nextcloud/module.json @@ -79,13 +79,15 @@ "id": "mesh-state", "type": "directory", "mode": "0700", - "place": "mesh" + "place": "mesh", + "unknown": "program" }, { "id": "state", "type": "directory", "mode": "0700", - "place": "." + "place": ".", + "unknown": "program" }, { "id": "server-env", @@ -98,7 +100,8 @@ "id": "html", "type": "directory", "mode": "0750", - "owner": "33:33" + "owner": "33:33", + "unknown": "program" }, { "id": "server", diff --git a/modules/nfs-server/README.md b/modules/nfs-server/README.md index 3c17f47a..ab6488d7 100644 --- a/modules/nfs-server/README.md +++ b/modules/nfs-server/README.md @@ -153,3 +153,12 @@ The tools run as the operator account and act as root through `sudo -n`. The `su account every command, so **this module's code is the only line** between a tool call and root: each act checks again what it may touch before it runs a command, and a test holds each check. Narrowing what sudo grants the account to the commands these modules need is a separate decision, not made here. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `/run/nfs-server`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `/var/lib/nfs-server`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `/var/lib/nfs-server/from-bus`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `/etc/nfs-server`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/nfs-server/module.json b/modules/nfs-server/module.json index 70b7f924..f9994d16 100644 --- a/modules/nfs-server/module.json +++ b/modules/nfs-server/module.json @@ -101,7 +101,8 @@ "id": "run-dir", "type": "directory", "path": "/run/nfs-server", - "mode": "0755" + "mode": "0755", + "unknown": "program" }, { "id": "server", @@ -120,14 +121,16 @@ "id": "wants-dir-parent", "type": "directory", "path": "/var/lib/nfs-server", - "mode": "0755" + "mode": "0755", + "unknown": "program" }, { "id": "wants-dir", "type": "directory", "path": "/var/lib/nfs-server/from-bus", "mode": "0755", - "owner": "${machine:account}" + "owner": "${machine:account}", + "unknown": "program" }, { "id": "exports", @@ -154,7 +157,8 @@ "id": "config-dir", "type": "directory", "path": "/etc/nfs-server", - "mode": "0755" + "mode": "0755", + "unknown": "program" }, { "id": "config", diff --git a/modules/node-env/README.md b/modules/node-env/README.md new file mode 100644 index 00000000..4a6bce13 --- /dev/null +++ b/modules/node-env/README.md @@ -0,0 +1,10 @@ +# node-env + +The `node-env` module of the mesh catalogue: claims the seat `node-environment`. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `~/.config/mesh`: holds only what this module declares and `forge.credentials`, which claude-code's process places in the agent's home (issue 1033); anything else is moved out by the node-engine to `~/.local/state/mesh-moved/node-env/` with a list, never deleted (ADR 0422 §4). +- `~/.config/environment.d`: holds only what this module declares; anything else is moved out by the node-engine to `~/.local/state/mesh-moved/node-env/` with a list, never deleted (ADR 0422 §4). diff --git a/modules/node-env/module.json b/modules/node-env/module.json index a933578c..85fd7e57 100644 --- a/modules/node-env/module.json +++ b/modules/node-env/module.json @@ -14,14 +14,17 @@ "type": "directory", "path": "${machine:account-home}/.config/mesh", "owner": "${machine:account}", - "mode": "0755" + "mode": "0755", + "unknown": "move-out", + "knows": ["forge.credentials"] }, { "id": "environment-d", "type": "directory", "path": "${machine:account-home}/.config/environment.d", "owner": "${machine:account}", - "mode": "0755" + "mode": "0755", + "unknown": "move-out" }, { "id": "posix", diff --git a/modules/nodered/README.md b/modules/nodered/README.md new file mode 100644 index 00000000..763aa722 --- /dev/null +++ b/modules/nodered/README.md @@ -0,0 +1,12 @@ +# nodered + +The `nodered` module of the mesh catalogue: runs the container image `nodered/node-red`. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:mesh-state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:data}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:written}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/nodered/module.json b/modules/nodered/module.json index 48c65dd0..f8068dd1 100644 --- a/modules/nodered/module.json +++ b/modules/nodered/module.json @@ -143,24 +143,28 @@ "id": "mesh-state", "type": "directory", "mode": "0700", - "place": "mesh" + "place": "mesh", + "unknown": "program" }, { "id": "state", "type": "directory", "mode": "0700", - "place": "." + "place": ".", + "unknown": "program" }, { "id": "data", "type": "directory", "mode": "0700", - "owner": "1000:1000" + "owner": "1000:1000", + "unknown": "program" }, { "id": "written", "type": "directory", - "mode": "0700" + "mode": "0700", + "unknown": "program" }, { "id": "settings-code", diff --git a/modules/ollama/README.md b/modules/ollama/README.md new file mode 100644 index 00000000..efe7b645 --- /dev/null +++ b/modules/ollama/README.md @@ -0,0 +1,9 @@ +# ollama + +The `ollama` module of the mesh catalogue: runs the container image `ollama/ollama`. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `/services/ollama`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/ollama/module.json b/modules/ollama/module.json index 2291c632..2d5fb48c 100644 --- a/modules/ollama/module.json +++ b/modules/ollama/module.json @@ -42,7 +42,8 @@ "id": "state", "type": "directory", "path": "/services/ollama", - "mode": "0700" + "mode": "0700", + "unknown": "program" }, { "id": "server", diff --git a/modules/only-office/README.md b/modules/only-office/README.md new file mode 100644 index 00000000..3129fc4b --- /dev/null +++ b/modules/only-office/README.md @@ -0,0 +1,16 @@ +# only-office + +The `only-office` module of the mesh catalogue: runs the container image `onlyoffice/documentserver`. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:logs}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:data}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:lib}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:db}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:rabbitmq}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:redis}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:fonts}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/only-office/module.json b/modules/only-office/module.json index ac0eed75..4af651f4 100644 --- a/modules/only-office/module.json +++ b/modules/only-office/module.json @@ -80,7 +80,8 @@ "id": "state", "type": "directory", "mode": "0700", - "place": "." + "place": ".", + "unknown": "program" }, { "id": "server-env", @@ -92,37 +93,44 @@ { "id": "logs", "type": "directory", - "mode": "0700" + "mode": "0700", + "unknown": "program" }, { "id": "data", "type": "directory", - "mode": "0700" + "mode": "0700", + "unknown": "program" }, { "id": "lib", "type": "directory", - "mode": "0700" + "mode": "0700", + "unknown": "program" }, { "id": "db", "type": "directory", - "mode": "0700" + "mode": "0700", + "unknown": "program" }, { "id": "rabbitmq", "type": "directory", - "mode": "0700" + "mode": "0700", + "unknown": "program" }, { "id": "redis", "type": "directory", - "mode": "0700" + "mode": "0700", + "unknown": "program" }, { "id": "fonts", "type": "directory", - "mode": "0700" + "mode": "0700", + "unknown": "program" }, { "id": "net", diff --git a/modules/openai-consumer/README.md b/modules/openai-consumer/README.md new file mode 100644 index 00000000..45272519 --- /dev/null +++ b/modules/openai-consumer/README.md @@ -0,0 +1,10 @@ +# openai-consumer + +The `openai-consumer` module of the mesh catalogue: declares directories on the node. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:config}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/openai-consumer/module.json b/modules/openai-consumer/module.json index 90457d98..2e98414e 100644 --- a/modules/openai-consumer/module.json +++ b/modules/openai-consumer/module.json @@ -19,12 +19,14 @@ "id": "state", "type": "directory", "mode": "0700", - "place": "." + "place": ".", + "unknown": "program" }, { "id": "config", "type": "directory", - "mode": "0700" + "mode": "0700", + "unknown": "program" }, { "id": "apply", diff --git a/modules/photos-eef/README.md b/modules/photos-eef/README.md new file mode 100644 index 00000000..d27db20a --- /dev/null +++ b/modules/photos-eef/README.md @@ -0,0 +1,9 @@ +# photos-eef + +The `photos-eef` module of the mesh catalogue: runs the container image `registry-api.novox.be/novox/photos-client`. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/photos-eef/module.json b/modules/photos-eef/module.json index 0ab336b8..101bdc27 100644 --- a/modules/photos-eef/module.json +++ b/modules/photos-eef/module.json @@ -31,7 +31,8 @@ "id": "state", "type": "directory", "mode": "0700", - "place": "." + "place": ".", + "unknown": "program" }, { "id": "net", diff --git a/modules/photos-filip/README.md b/modules/photos-filip/README.md new file mode 100644 index 00000000..7d8bd12d --- /dev/null +++ b/modules/photos-filip/README.md @@ -0,0 +1,9 @@ +# photos-filip + +The `photos-filip` module of the mesh catalogue: runs the container image `registry-api.novox.be/novox/photos-client`. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/photos-filip/module.json b/modules/photos-filip/module.json index 518012f8..9c1c83fa 100644 --- a/modules/photos-filip/module.json +++ b/modules/photos-filip/module.json @@ -31,7 +31,8 @@ "id": "state", "type": "directory", "mode": "0700", - "place": "." + "place": ".", + "unknown": "program" }, { "id": "net", diff --git a/modules/picom/README.md b/modules/picom/README.md index be9a79ba..54e3408b 100644 --- a/modules/picom/README.md +++ b/modules/picom/README.md @@ -59,7 +59,7 @@ A picom a tool starts runs under the account's own service manager (`systemd-run in place. It probably fails to start on that machine's GPU. `picom_restart` with `hard` after assignment answers with what happened. The next step is the `picom` user unit's journal. -## What it leaves as found +## What lies outside its directory The window manager's own `exec --no-startup-id picom` line belongs to the `i3` module's configuration, which no longer carries it. Nothing else of picom's lies outside the directory this module owns. @@ -78,3 +78,9 @@ which no longer carries it. Nothing else of picom's lies outside the directory t the module. - `xorg-xprop` is declared here because the `xorg` module does not install it. If `xorg` comes to declare it, it leaves this module, since a package is declared once per node. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `~/.config/picom`: holds only what this module declares; anything else is moved out by the node-engine to `~/.local/state/mesh-moved/picom/` with a list, never deleted (ADR 0422 §4). diff --git a/modules/picom/module.json b/modules/picom/module.json index 2f747784..cfdc3970 100644 --- a/modules/picom/module.json +++ b/modules/picom/module.json @@ -55,7 +55,8 @@ "type": "directory", "path": "${machine:account-home}/.config/picom", "owner": "${machine:account}", - "mode": "0755" + "mode": "0755", + "unknown": "move-out" }, { "id": "configuration", diff --git a/modules/postgres/README.md b/modules/postgres/README.md index 10a9ad2e..be777275 100644 --- a/modules/postgres/README.md +++ b/modules/postgres/README.md @@ -114,3 +114,12 @@ retired login locked out with its data kept and listed retired, enabled again as dropping only its own database and role. `live_reader_test.go` and `readable_live_test.go` need no extension: an install's reader as issue 337 found it reading no secret after its first query, and the reconcile granting exactly what is declared, refusing secrets, taking back what is no longer declared, and finding a grant to `PUBLIC`. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:grants}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `/var/lib/mesh-store`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:store-data}/dumps`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/postgres/module.json b/modules/postgres/module.json index 934c2ef4..919df421 100644 --- a/modules/postgres/module.json +++ b/modules/postgres/module.json @@ -96,12 +96,14 @@ "id": "state", "type": "directory", "mode": "0700", - "place": "." + "place": ".", + "unknown": "program" }, { "id": "grants", "type": "directory", - "mode": "0700" + "mode": "0700", + "unknown": "program" }, { "id": "consumers", @@ -115,14 +117,16 @@ "type": "directory", "path": "/var/lib/mesh-store", "mode": "0700", - "owner": "999:70" + "owner": "999:70", + "unknown": "program" }, { "id": "dumps", "type": "directory", "path": "${dir:store-data}/dumps", "mode": "0700", - "owner": "999:70" + "owner": "999:70", + "unknown": "program" }, { "id": "server", diff --git a/modules/power/README.md b/modules/power/README.md index e353aaa0..b23268f5 100644 --- a/modules/power/README.md +++ b/modules/power/README.md @@ -132,3 +132,18 @@ its three siblings) — the same way the laptop module asks for the NVIDIA drive **polkit is this module's package.** logind gives a delay lock to a process that is not root only through polkit. A server without polkit refused the watcher with "Permission denied", and its sleeps and shutdowns would have gone unsaid. The module installs it; nothing else needed it there. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `/usr/local/lib/mesh-power`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `/etc/mesh-power`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `/etc/mesh-power/moments`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `/etc/systemd/logind.conf.d`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `/etc/systemd/system/sleep.target.d`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `/etc/systemd/system/suspend.target.d`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `/etc/systemd/system/hibernate.target.d`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `/etc/systemd/system/hybrid-sleep.target.d`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `/etc/systemd/system/suspend-then-hibernate.target.d`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `/run/mesh-power`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/power/module.json b/modules/power/module.json index cea72ad7..a110d410 100644 --- a/modules/power/module.json +++ b/modules/power/module.json @@ -128,19 +128,22 @@ "id": "scripts", "type": "archive", "path": "/usr/local/lib/mesh-power", - "artifact": "scripts" + "artifact": "scripts", + "unknown": "program" }, { "id": "config-dir", "type": "directory", "path": "/etc/mesh-power", - "mode": "0755" + "mode": "0755", + "unknown": "program" }, { "id": "moments-dir", "type": "directory", "path": "/etc/mesh-power/moments", - "mode": "0755" + "mode": "0755", + "unknown": "program" }, { "id": "moment-after-boot", @@ -188,7 +191,8 @@ "id": "logind-drop-ins", "type": "directory", "path": "/etc/systemd/logind.conf.d", - "mode": "0755" + "mode": "0755", + "unknown": "program" }, { "id": "logind-power", @@ -244,7 +248,8 @@ "id": "sleep-drop-ins", "type": "directory", "path": "/etc/systemd/system/sleep.target.d", - "mode": "0755" + "mode": "0755", + "unknown": "program" }, { "id": "sleep-wants", @@ -257,7 +262,8 @@ "id": "suspend-drop-ins", "type": "directory", "path": "/etc/systemd/system/suspend.target.d", - "mode": "0755" + "mode": "0755", + "unknown": "program" }, { "id": "suspend-wants", @@ -270,7 +276,8 @@ "id": "hibernate-drop-ins", "type": "directory", "path": "/etc/systemd/system/hibernate.target.d", - "mode": "0755" + "mode": "0755", + "unknown": "program" }, { "id": "hibernate-wants", @@ -283,7 +290,8 @@ "id": "hybrid-sleep-drop-ins", "type": "directory", "path": "/etc/systemd/system/hybrid-sleep.target.d", - "mode": "0755" + "mode": "0755", + "unknown": "program" }, { "id": "hybrid-sleep-wants", @@ -296,7 +304,8 @@ "id": "suspend-then-hibernate-drop-ins", "type": "directory", "path": "/etc/systemd/system/suspend-then-hibernate.target.d", - "mode": "0755" + "mode": "0755", + "unknown": "program" }, { "id": "suspend-then-hibernate-wants", @@ -330,7 +339,8 @@ "id": "draw-dir", "type": "directory", "path": "/run/mesh-power", - "mode": "0755" + "mode": "0755", + "unknown": "program" }, { "id": "draw", diff --git a/modules/powerlevel10k/README.md b/modules/powerlevel10k/README.md index b6d8e07b..3a2d2a5d 100644 --- a/modules/powerlevel10k/README.md +++ b/modules/powerlevel10k/README.md @@ -21,6 +21,9 @@ release's contents, and update the version here and in the shell code's comment. | `~/.local/share/powerlevel10k/` | the theme, unpacked from the `theme` archive | owned, whole | | `~/.config/powerlevel10k/p10k.zsh` | the prompt's configuration, unpacked from the `configuration` archive | owned, whole | +Both directories are `unknown: move-out` (ADR 0422): they hold only what the archives declare, and anything +else found in them is moved out by the node-engine, never deleted. + The configuration is today's `~/.p10k.zsh`, byte for byte. It is the predecessor's file, and was identical on every machine. It ships as an archive of one file rather than as an inline file. At 86 KB, inline content would ride in every declaration the node receives, and would be unreadable @@ -44,3 +47,10 @@ operator's `.zshrc` has its cache line commented out today, and the configuratio - **Moving from the predecessor:** once this module is assigned, `~/.zsh/themes/powerlevel10k` and `~/.p10k.zsh` are no longer read, and the operator removes them, once (ADR 0182; the zsh module's README lists the lines to delete from `.zshrc`). + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `~/.local/share/powerlevel10k`: holds only what this module declares and the nine `.zwc` files the theme compiles into its own folder at every zsh start (`powerlevel9k.zsh-theme.zwc`, `powerlevel10k.zsh-theme.zwc`, `internal/{p10k,icons,configure,worker,parser}.zsh.zwc`, `gitstatus/gitstatus.plugin.zsh.zwc`, `gitstatus/install.zwc`; `powerlevel10k.zsh-theme` lines 62-70); anything else is moved out by the node-engine to `~/.local/state/mesh-moved/powerlevel10k/` with a list, never deleted (ADR 0422 §4). +- `~/.config/powerlevel10k`: holds only what this module declares; anything else is moved out by the node-engine to `~/.local/state/mesh-moved/powerlevel10k/` with a list, never deleted (ADR 0422 §4). diff --git a/modules/powerlevel10k/module.json b/modules/powerlevel10k/module.json index 39d2810d..da1bd4df 100644 --- a/modules/powerlevel10k/module.json +++ b/modules/powerlevel10k/module.json @@ -8,14 +8,17 @@ "type": "archive", "path": "${machine:account-home}/.local/share/powerlevel10k", "owner": "${machine:account}", - "artifact": "theme" + "artifact": "theme", + "unknown": "move-out", + "knows": ["powerlevel9k.zsh-theme.zwc", "powerlevel10k.zsh-theme.zwc", "internal/p10k.zsh.zwc", "internal/icons.zsh.zwc", "internal/configure.zsh.zwc", "internal/worker.zsh.zwc", "internal/parser.zsh.zwc", "gitstatus/gitstatus.plugin.zsh.zwc", "gitstatus/install.zwc"] }, { "id": "configuration", "type": "archive", "path": "${machine:account-home}/.config/powerlevel10k", "owner": "${machine:account}", - "artifact": "configuration" + "artifact": "configuration", + "unknown": "move-out" } ], "items": [ diff --git a/modules/powerlevel10k/test/module.test.ts b/modules/powerlevel10k/test/module.test.ts index e9bc8522..9f8d7829 100644 --- a/modules/powerlevel10k/test/module.test.ts +++ b/modules/powerlevel10k/test/module.test.ts @@ -22,8 +22,8 @@ test("the configuration archive holds the prompt's configuration and nothing els test("both are unpacked under the account's home, owned by the account", () => { const byId = Object.fromEntries(m.resources.map((r: { id: string }) => [r.id, r])); - assert.deepEqual(byId.theme, { id: "theme", type: "archive", path: "${machine:account-home}/.local/share/powerlevel10k", owner: "${machine:account}", artifact: "theme" }); - assert.deepEqual(byId.configuration, { id: "configuration", type: "archive", path: "${machine:account-home}/.config/powerlevel10k", owner: "${machine:account}", artifact: "configuration" }); + assert.deepEqual(byId.theme, { id: "theme", type: "archive", path: "${machine:account-home}/.local/share/powerlevel10k", owner: "${machine:account}", artifact: "theme", unknown: "move-out", knows: ["powerlevel9k.zsh-theme.zwc", "powerlevel10k.zsh-theme.zwc", "internal/p10k.zsh.zwc", "internal/icons.zsh.zwc", "internal/configure.zsh.zwc", "internal/worker.zsh.zwc", "internal/parser.zsh.zwc", "gitstatus/gitstatus.plugin.zsh.zwc", "gitstatus/install.zwc"] }); + assert.deepEqual(byId.configuration, { id: "configuration", type: "archive", path: "${machine:account-home}/.config/powerlevel10k", owner: "${machine:account}", artifact: "configuration", unknown: "move-out" }); }); // The zsh code is an item of the collection shell:zsh:normal, its text a template in files/ (ADR 0348, 0349). diff --git a/modules/records/README.md b/modules/records/README.md index 9854e316..6fb679cd 100644 --- a/modules/records/README.md +++ b/modules/records/README.md @@ -49,3 +49,10 @@ layout cloned into the given directory itself, from a container running as root, operator account cannot change; git then refused every sync as "dubious ownership" (hq issue 251). What that layout left is removed where it is the module's, and named in `records_status` (`leftBehind`, with the one command that deletes it) where it is not. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:mesh-state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:checkout}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/records/module.json b/modules/records/module.json index 9fcf22f5..db6f7e5a 100644 --- a/modules/records/module.json +++ b/modules/records/module.json @@ -43,13 +43,15 @@ "id": "mesh-state", "type": "directory", "mode": "0700", - "place": "mesh" + "place": "mesh", + "unknown": "program" }, { "id": "checkout", "type": "directory", "mode": "0700", - "place": "." + "place": ".", + "unknown": "program" }, { "id": "config", diff --git a/modules/redis/README.md b/modules/redis/README.md new file mode 100644 index 00000000..9a2c8af2 --- /dev/null +++ b/modules/redis/README.md @@ -0,0 +1,11 @@ +# redis + +The `redis` module of the mesh catalogue: runs the container image `redis`. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:grants}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:data}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/redis/module.json b/modules/redis/module.json index fc67a289..58e11064 100644 --- a/modules/redis/module.json +++ b/modules/redis/module.json @@ -68,18 +68,21 @@ "id": "state", "type": "directory", "mode": "0700", - "place": "." + "place": ".", + "unknown": "program" }, { "id": "grants", "type": "directory", - "mode": "0700" + "mode": "0700", + "unknown": "program" }, { "id": "data", "type": "directory", "mode": "0700", - "owner": "999:1000" + "owner": "999:1000", + "unknown": "program" }, { "id": "server-conf", diff --git a/modules/restic/README.md b/modules/restic/README.md index 9bdaedf0..1ec851b8 100644 --- a/modules/restic/README.md +++ b/modules/restic/README.md @@ -21,3 +21,10 @@ Against mistakes, not disasters: nothing leaves the machine. `node-backup.backed-up` lists each module's restore points taken by hand under `handTaken`: when each was taken, the time it is kept at least until, whether it still waits for a later good night, and a sentence saying so. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:repository}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/restic/module.json b/modules/restic/module.json index e7089278..18ee811d 100644 --- a/modules/restic/module.json +++ b/modules/restic/module.json @@ -77,12 +77,14 @@ "id": "state", "type": "directory", "mode": "0700", - "place": "." + "place": ".", + "unknown": "program" }, { "id": "repository", "type": "directory", - "mode": "0700" + "mode": "0700", + "unknown": "program" }, { "id": "declared", diff --git a/modules/rofi/README.md b/modules/rofi/README.md index b83197c3..f9bdab20 100644 --- a/modules/rofi/README.md +++ b/modules/rofi/README.md @@ -70,9 +70,10 @@ A Wayland session gets the same menu from whichever launcher holds the seat ther - `icon-theme` and `window-command` are gone. They named an icon theme and a program (`wmctrl`) that nothing installs. rofi's defaults serve. -## What it leaves as found +## What it moves out -- `~/.config/rofi/themes-repo/` (a cloned theme repository), the five symbolic links into it +- Moved out by the node-engine to `~/.local/state/mesh-moved/rofi/` with a list, never deleted (ADR 0422 §4), + because no `knows` names them: `~/.config/rofi/themes-repo/` (a cloned theme repository), the five symbolic links into it (`applets`, `images`, `launchers`, `powermenu`, `scripts`), `colors/`, `theme.rasi` and `powermenu.rasi`. - The predecessor's scripts in `~/scripts`: `rofi-launcher-normal`, `rofi-launcher-terminal`, @@ -83,8 +84,9 @@ A Wayland session gets the same menu from whichever launcher holds the seat ther Once the `i3` module carries the main i3 configuration: -1. Delete `~/.config/rofi/theme.rasi`, `powermenu.rasi`, `colors/`, the five links and `themes-repo/`. - Nothing reads them any more. +1. `~/.config/rofi/theme.rasi`, `powermenu.rasi`, `colors/`, the five links and `themes-repo/` are moved + out for you (with a list, in `~/.local/state/mesh-moved/rofi/`). Nothing reads them any more; delete the + moved copies when you like. 2. Delete the four scripts above from `~/scripts`. 3. Your scripts that call `rofi -dmenu` keep working. Calling `dmenu` instead lets them follow the seat, for example to a Wayland launcher later. @@ -102,3 +104,10 @@ The module no longer writes a file into i3's `config.d`. Its window-manager line under `files/i3/` where it had one) are a contribution to `node-display-session`. The i3 module places them in its own configuration under a `# ` line, so this module depends on a window manager being assigned beside it. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `~/.config/rofi`: holds only what this module declares; anything else is moved out by the node-engine to `~/.local/state/mesh-moved/rofi/` with a list, never deleted (ADR 0422 §4). +- `~/.config/rofi/themes`: holds only what this module declares; anything else is moved out by the node-engine to `~/.local/state/mesh-moved/rofi/` with a list, never deleted (ADR 0422 §4). diff --git a/modules/rofi/module.json b/modules/rofi/module.json index 1cc9b2ff..fbd061d0 100644 --- a/modules/rofi/module.json +++ b/modules/rofi/module.json @@ -34,14 +34,16 @@ "type": "directory", "path": "${machine:account-home}/.config/rofi", "owner": "${machine:account}", - "mode": "0755" + "mode": "0755", + "unknown": "move-out" }, { "id": "themes-dir", "type": "directory", "path": "${machine:account-home}/.config/rofi/themes", "owner": "${machine:account}", - "mode": "0755" + "mode": "0755", + "unknown": "move-out" }, { "id": "configuration", diff --git a/modules/route-adapter/README.md b/modules/route-adapter/README.md index d71d3975..d7cd2abc 100644 --- a/modules/route-adapter/README.md +++ b/modules/route-adapter/README.md @@ -179,3 +179,10 @@ provider helper for `route`, with no credential. A consumer of a route whose tem (a handed gitea's origins file) is filled by its machine's client from this module's entry for it. The entry's values are the names the controller composed for that consumer, carried in its plan and copied unchanged: the public domain is placement, so only the controller can join a label to it. The adapter still writes the predecessor proxy's files. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `/var/lib/route-adapter/routes`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/route-adapter/module.json b/modules/route-adapter/module.json index 0f2f37d2..5234c78b 100644 --- a/modules/route-adapter/module.json +++ b/modules/route-adapter/module.json @@ -58,13 +58,15 @@ "id": "state", "type": "directory", "mode": "0700", - "place": "." + "place": ".", + "unknown": "program" }, { "id": "routes-dir", "type": "directory", "path": "/var/lib/route-adapter/routes", - "mode": "0700" + "mode": "0700", + "unknown": "program" }, { "id": "config", diff --git a/modules/route-proxy/Dockerfile b/modules/route-proxy/Dockerfile deleted file mode 100644 index 7e7fe648..00000000 --- a/modules/route-proxy/Dockerfile +++ /dev/null @@ -1,30 +0,0 @@ -ARG ALPINE_BASE=alpine:3.20 -ARG GO_BASE=golang:1.25 -# The route-proxy module's runtime image: the reference reverse proxy compiled into a container. -# -# **The proxy source is not vendored here.** The canonical proxy — the contract written as something -# that runs — lives in the mesh-controller repository at examples/route-proxy (novox/hq 08-connectivity -# §3). This module ships the *packaging*, not a second copy of the contract, so the build context is -# the mesh-controller repository, and this Dockerfile compiles ./examples/route-proxy from it. The build seat -# hands it only that program's build source — its import closure, go.mod, go.sum and the vendored packages -# it uses (novox/hq ADR 0267, the artifact's `compiles`) — so a merge elsewhere in that repository does not -# move this image, and a COPY of anything outside it fails by name. -# -# docker build -f mesh-catalog/modules/route-proxy/Dockerfile \ -# -t mesh-route-proxy:development /mesh-controller -# -# The mesh pins the digest of what this produces; the committed module.json carries the placeholder -# digest every mesh-built image does, replaced at publish. -FROM ${GO_BASE} AS build -WORKDIR /src -COPY go.mod go.sum ./ -RUN go mod download -COPY . . -RUN CGO_ENABLED=0 GOOS=linux go build -trimpath -buildvcs=false -ldflags=-buildid= -o /mesh-route-proxy ./examples/route-proxy - -# A small runtime with the public CA roots the ACME client needs to reach a real authority, and run -# as root so it can bind :80 and :443 — the two privileged ports a public front door listens on. -FROM ${ALPINE_BASE} -RUN apk add --no-cache ca-certificates -COPY --from=build /mesh-route-proxy /usr/local/bin/mesh-route-proxy -ENTRYPOINT ["/usr/local/bin/mesh-route-proxy"] diff --git a/modules/route-proxy/README.md b/modules/route-proxy/README.md deleted file mode 100644 index 5b167dfe..00000000 --- a/modules/route-proxy/README.md +++ /dev/null @@ -1,93 +0,0 @@ -# route-proxy — the mesh's public ingress - -The shipping form of the reference reverse proxy (novox/hq ADR 0007, novox/hq 08-connectivity §3 -"Exposure"). It is the `traefik` replacement: the module that **provides `route`**. - -## What it is - -**A route is a grant.** A module that must be reachable declares `requires: ["route"]` and -contributes the name it wants and the port it listens on; this module provides `route`, receives -every consumer that asked as a file, and forwards by the `Host` header of each request to -`http://:` — where `at` is where the mesh says that consumer's machine is (empty means -this machine, reached over loopback). - -The control plane does not write the proxy's configuration. It delivers the facts — the -contributions file at `receives.route` — and the proxy turns them into whatever it runs. That -boundary is why swapping this proxy for another costs nothing to the modules that publish through -it. - -| field | value | why | -|---|---|---| -| `provides` | `route` (scope `mesh`) | a module on any node may require it | -| `serves.route` | `{}` | a route hands back a name, not a credential — the consumer already knows the name it asked for | -| `receives.route` | `…/routes/mesh.json` | where the mesh writes every contribution; the proxy reads it as `$ROUTES` and re-reads on change | -| `listens` | `80` and `443`, both `from: anywhere` | the one machine with a public opening; the firewall opens these for free (ADR 0045) | - -No provisioner: the proxy neither mints a credential for anybody nor answers a provision other -than `route`. It reads the file the mesh writes, and its own broker credential is for its tools. - -## Which issuer: Let's Encrypt, stated here (novox/hq ADR 0226) - -**The public issuer is this module's own fact, not a provision.** Until 2026-10-06 it came from a -second module, `public-acme`, that ran nothing and offered `acme-ca` pointing at Let's Encrypt; every -node running a proxy was assigned it too, and nothing else ever consumed it. It is retired: the proxy -names the production directory in its own `acme.env`, and the only issuer it is still bound to is the -mesh's own (`internal-acme-ca`, from `step-ca`). - -**`acme.env` is byte for byte what the binding rendered.** The proxy keeps each authority's account -and certificates in a directory under `/var/lib/route-proxy/acme` named after a digest of the -directory URL — `https://acme-v02.api.letsencrypt.org:443/directory`, port included, as the binding -spelled it — and of the root bundle the `trust` container copies in. Any change to either is a new -authority to the proxy: a new account, and every routed name ordered again against Let's Encrypt's -rate limits. So the URL keeps the `:443`, `ACME_ROOTS_PATH` stays empty, and the `trust` artifact's -pinned image is not moved without a decision; mesh-controller's -`TestRouteProxyKeepsItsPublicAccountDirectory` holds all three. - -## How it ships the Go proxy - -The proxy is a Go program, unlike the TypeScript tool-runtime modules. The canonical source is -**not vendored here** — it lives in the mesh-controller repository at `examples/route-proxy`, the -contract written as something that runs. This module ships only the packaging: a multi-stage -[`Dockerfile`](Dockerfile) whose build context is the mesh-controller repository root and which -compiles `./examples/route-proxy` into `mesh-route-proxy`. The committed `module.json` carries the -placeholder digest every mesh-built image does (`@sha256:0000…`); the mesh pins the real digest at -publish. - -In the lab, `mesh-lab/scripts/build-route-proxy-image.sh` builds this into the local daemon as -`mesh-route-proxy:development`, and a scenario stocks it and serves it by digest. - -## Certificates, and the endpoint (resolves 04-ISSUES/004) - -TLS is opt-in and terminates here for public names, via ACME (autocert). A proxy with no -`TLS_LISTEN` serves plain HTTP; with it, it obtains a publicly-trusted certificate for exactly the -names the mesh routes here (`HostPolicy` = only routed names, so a scan cannot spend the account's -issuance quota) and answers the HTTP-01 challenge on `:80` at the name being certified. - -**`ACME_DIRECTORY` defaults to Let's Encrypt *staging*, not production.** This closes -novox/hq 04-ISSUES/004 (certificate issuance targets production): -the issuer is a per-node choice, and the safe endpoint is the default. Production issuance is -rate-limited per domain and per account and does not replenish quickly; defaulting to production -would leave the safe path depending on somebody remembering to opt out of it, on exactly the work -most likely to iterate. A staging certificate is trusted by no browser, so the mistake announces -itself on the first request rather than a fortnight later at the rate limit. - -**The module states production.** The binary's staging default stands for anything that runs it -without the module — the lab, a developer's machine — and the module's `acme.env` names production -for every node it is assigned to, because a node assigned the public proxy is one that serves public -traffic. - -| env | default | meaning | -|---|---|---| -| `ROUTES` | `/routes/mesh.json` | the contributions file the mesh writes (`receives.route`) | -| `LISTEN` | `:80` | HTTP, and the ACME HTTP-01 challenge | -| `TLS_LISTEN` | `:443` | HTTPS; unset to serve plain HTTP only | -| `ACME_CACHE` | `/acme` | where issued certificates persist; required when `TLS_LISTEN` is set, so a restart does not re-order | -| `ACME_DIRECTORY` | Let's Encrypt **staging** in the binary; production in the module's `acme.env` | the issuer | -| `ACME_CA_BUNDLE` | *(unset)* | a file of roots to trust for the issuer's own API — set only for a private/lab authority whose API certificate the world does not yet trust | - -## Answered by the provider helper (issue 792) - -A small Go program beside the proxy (`cmd/route-provider`) runs the SDK's provider helper for `route`, with no credential, -and the offer says `"answered-by": "helper"`: a handed consumer (gitea's origins file) is filled by its machine's client -from this module's entry, which carries the public names the controller composed for it. The module is **not** marked -moved: its own files, `acme.env` and the broker secret, are composed as before, and the proxy itself is unchanged. diff --git a/modules/route-proxy/cmd/route-provider/main.go b/modules/route-proxy/cmd/route-provider/main.go deleted file mode 100644 index e70a6892..00000000 --- a/modules/route-proxy/cmd/route-provider/main.go +++ /dev/null @@ -1,37 +0,0 @@ -// route-provider: route-proxy's Go half, one binary the node's runtime launches and speaks MCP to over stdio -// through the Go SDK. It serves no tools; it runs the SDK's provider helper that answers the consumers of `route` -// (issue 792). The TypeScript adapter beside it keeps writing the proxy's files. -// -// stdout is the MCP channel; everything this module says, it says on stderr. -package main - -import ( - "context" - "fmt" - "os" - - stdio "git.novox.be/novox/mesh-sdk/go" - "git.novox.be/novox/mesh-sdk/go/provider" -) - -func say(format string, args ...any) { - fmt.Fprintf(os.Stderr, "[route-proxy] "+format+"\n", args...) -} - -func main() { - node := os.Getenv("MESH_NODE") - if node == "" { - say("MESH_NODE is not set; answering no consumers") - } else { - p := newProvider(node, provider.StateBus(ModuleName), say) - go func() { - if err := p.Run(context.Background()); err != nil { - say("the provider helper stopped: %v", err) - } - }() - } - if err := stdio.Serve("", nil); err != nil { - say("%v", err) - os.Exit(1) - } -} diff --git a/modules/route-proxy/cmd/route-provider/provider.go b/modules/route-proxy/cmd/route-provider/provider.go deleted file mode 100644 index 7eee671b..00000000 --- a/modules/route-proxy/cmd/route-provider/provider.go +++ /dev/null @@ -1,36 +0,0 @@ -package main - -// route-proxy handed to the provisioner (novox/hq ADR 0349 clause 12, issue 792): the module runs the SDK's provider -// helper for `route`, so a consumer of a route — a handed gitea reading `${bound:route:name-web}` — is told its public -// name from the entry in this module's bucket, the one its machine's client fills its files from. -// -// **It composes nothing.** A route's public name is the label its consumer chose joined to the node's public domain, -// which only the controller knows (the domain is placement), so the controller composes it and carries it in the -// consumer's plan entry (`names`); the helper copies it into the entry's values unchanged. There is no credential for -// a route and nothing to make: the traefik files stay the TypeScript adapter's, written from the contributions the -// mesh still gives it. - -import ( - "git.novox.be/novox/mesh-sdk/go/provider" -) - -// ProvisionName is what route-proxy provides, as its manifest says. -const ProvisionName = "route" - -// ModuleName is this module. -const ModuleName = "route-proxy" - -// newProvider is route-proxy answering its consumers over bus on node: the route, served as the manifest serves it -// (nothing of its own), with no credential, and nothing to make or retire for a consumer. -func newProvider(node string, bus provider.Bus, logf func(string, ...any)) *provider.Provider { - return provider.New(provider.Options{ - Node: node, - Module: ModuleName, - Offers: map[string]provider.Offer{ProvisionName: { - Credential: provider.NoCredential, - Serves: map[string]any{}, - }}, - Bus: bus, - Logf: logf, - }) -} diff --git a/modules/route-proxy/cmd/route-provider/provider_test.go b/modules/route-proxy/cmd/route-provider/provider_test.go deleted file mode 100644 index d7ccaffc..00000000 --- a/modules/route-proxy/cmd/route-provider/provider_test.go +++ /dev/null @@ -1,125 +0,0 @@ -package main - -import ( - "context" - "crypto/sha256" - "encoding/hex" - "encoding/json" - "os" - "sync" - "testing" - - "git.novox.be/novox/mesh-sdk/go/provider" -) - -// bucketBus is the helper's bus over in-memory buckets. -type bucketBus struct { - mu sync.Mutex - buckets map[string]map[string][]byte -} - -func (b *bucketBus) Watch(_ context.Context, bucket string, handle func(string, []byte)) error { - b.mu.Lock() - defer b.mu.Unlock() - for k, v := range b.buckets[bucket] { - handle(k, v) - } - return nil -} - -func (b *bucketBus) Put(_ context.Context, bucket, key string, value []byte) error { - b.mu.Lock() - defer b.mu.Unlock() - if b.buckets[bucket] == nil { - b.buckets[bucket] = map[string][]byte{} - } - b.buckets[bucket][key] = append([]byte(nil), value...) - return nil -} - -func (b *bucketBus) Delete(_ context.Context, bucket, key string) error { - b.mu.Lock() - defer b.mu.Unlock() - delete(b.buckets[bucket], key) - return nil -} - -// A consumer of a route is answered with the names its plan's entry carries, and nothing of a credential (issue 792): -// the entry's values are the controller's composition, copied unchanged, and no vault is asked. -func TestAConsumerOfARouteIsToldItsNamesFromItsPlanEntry(t *testing.T) { - bus := &bucketBus{buckets: map[string]map[string][]byte{}} - plan, _ := json.Marshal(map[string]any{"node": "g14", "module": "gitea", "provisioning": map[string]any{ - "provisions": []any{map[string]any{"provision": ProvisionName, "provider": "novox", - "provider-module": ModuleName, "at": "10.7.0.1", "from": "novox", "as": "mesh_g14_gitea", - "names": map[string]any{"name": "git.example.org", "internal-name": "git.novox.internal", - "name-web": "git.example.org"}}}}}) - if err := bus.Put(context.Background(), provider.AssignmentsBucket, "g14.gitea", plan); err != nil { - t.Fatal(err) - } - p := newProvider("novox", bus, t.Logf) - out, err := p.Pass(context.Background()) - if err != nil { - t.Fatal(err) - } - if len(out) != 1 || out[0].Status != provider.Answered { - t.Fatalf("the pass did %+v", out) - } - var e provider.Entry - if err := json.Unmarshal(bus.buckets[provider.BucketOf(ModuleName)]["g14.gitea.route"], &e); err != nil { - t.Fatal(err) - } - if e.Values["name-web"] != "git.example.org" || e.Values["name"] != "git.example.org" || - e.Values["internal-name"] != "git.novox.internal" || e.VaultName != "" || e.User != "" || e.Refused != "" { - t.Fatalf("the entry is %+v", e) - } -} - -// route-proxy's own files and resources stay as they were (issue 792): the module answers its route through the -// helper but is not marked moved, so its acme.env, its broker secret and its containers are composed as today and -// the public ingress does not change. Everything of the manifest but its build, what it provides and the state it -// keeps is pinned by this digest; changing the ingress means changing it here, on purpose. -func TestRouteProxyIsNotMovedAndKeepsItsOwnFiles(t *testing.T) { - raw, err := os.ReadFile("../../module.json") - if err != nil { - t.Fatal(err) - } - var m map[string]any - if err := json.Unmarshal(raw, &m); err != nil { - t.Fatal(err) - } - if _, moved := m["moved"]; moved { - t.Fatal("route-proxy is marked moved: its own files would be handed") - } - provides, _ := m["provides"].([]any) - if len(provides) != 1 || provides[0].(map[string]any)["answered-by"] != "helper" { - t.Fatalf("route is not said to be answered by the helper: %v", provides) - } - for _, k := range []string{"build", "provides", "state", "reads"} { - delete(m, k) - } - pinned, err := json.Marshal(m) // sorted keys, compact - if err != nil { - t.Fatal(err) - } - sum := sha256.Sum256(pinned) - if got := hex.EncodeToString(sum[:]); got != "57a39de0943d8b33b9db6cb998ce97f8793602cca6cdfa5849c1c392390454a3" { - t.Fatalf("route-proxy's own manifest changed: digest %s", got) - } - // The two acme templates are pinned the same way (issue 849): they are what the proxy's certificates are asked - // with, so a change to either is a change to the public ingress. - for file, want := range map[string]string{ - "acme.env": "d66eba145c893080e30e013329fc00e51395f0c31fc8621a20c7b26abc11b52f", - "internal-acme.env": "baf4da75379183758c8ae505be4dd8a79d5b089ae1e7c8ded4d232df40251df5", - } { - text, err := os.ReadFile("../../files/" + file) - if err != nil { - t.Fatal(err) - } - sum := sha256.Sum256(text) - if got := hex.EncodeToString(sum[:]); got != want { - t.Fatalf("route-proxy's files/%s changed: digest %s, pinned %s; if the change is meant, check that the "+ - "public and internal ingress still ask for the certificates they should, then pin the new digest here", - file, got, want) - } - } -} diff --git a/modules/route-proxy/files/acme.env b/modules/route-proxy/files/acme.env deleted file mode 100644 index 428aae2b..00000000 --- a/modules/route-proxy/files/acme.env +++ /dev/null @@ -1,3 +0,0 @@ -ACME_DIRECTORY=https://acme-v02.api.letsencrypt.org:443/directory -ACME_ROOTS=https://acme-v02.api.letsencrypt.org:443 -ACME_ROOTS_PATH= diff --git a/modules/route-proxy/files/internal-acme.env b/modules/route-proxy/files/internal-acme.env deleted file mode 100644 index 72b0f7bb..00000000 --- a/modules/route-proxy/files/internal-acme.env +++ /dev/null @@ -1,3 +0,0 @@ -INTERNAL_ACME_DIRECTORY=https://${bound:internal-acme-ca:at}:${bound:internal-acme-ca:port}${bound:internal-acme-ca:path} -INTERNAL_ACME_ROOTS=https://${bound:internal-acme-ca:at}:${bound:internal-acme-ca:port}${bound:internal-acme-ca:roots} -INTERNAL_ACME_ROOTS_PATH=${bound:internal-acme-ca:roots} diff --git a/modules/route-proxy/go.mod b/modules/route-proxy/go.mod deleted file mode 100644 index 54bca71c..00000000 --- a/modules/route-proxy/go.mod +++ /dev/null @@ -1,5 +0,0 @@ -module route-proxy - -go 1.25.0 - -require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261016174426-a4e7f480b3b0 diff --git a/modules/route-proxy/go.sum b/modules/route-proxy/go.sum deleted file mode 100644 index 63b4da41..00000000 --- a/modules/route-proxy/go.sum +++ /dev/null @@ -1,2 +0,0 @@ -git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261016174426-a4e7f480b3b0 h1:EK84so3Y8JdFT3BHrzj9cPQQrDUPa/d+IdOGnerlmEI= -git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261016174426-a4e7f480b3b0/go.mod h1:GFuZUElBZ9A++mxgIKo97aXXo+kV0uJ/UkbhQPPIbrY= diff --git a/modules/route-proxy/module.json b/modules/route-proxy/module.json deleted file mode 100644 index a2734f17..00000000 --- a/modules/route-proxy/module.json +++ /dev/null @@ -1,253 +0,0 @@ -{ - "module": "route-proxy", - "version": "1", - "slug": "rproxy", - "capabilities": [ - "container-runtime" - ], - "provides": [ - { - "name": "route", - "scope": "mesh", - "identity": false, - "answered-by": "helper" - } - ], - "serves": { - "route": {} - }, - "receives": { - "route": "${dir:routes-dir}/mesh.json" - }, - "requires": [ - "internal-acme-ca" - ], - "binds": { - "internal-acme-ca": "${dir:state}/internal-acme-ca.json" - }, - "own-secrets": { - "broker": "${dir:mesh-state}/broker" - }, - "listens": [ - { - "name": "http", - "port": 80, - "protocol": "tcp", - "from": "anywhere", - "why": "public HTTP, and the ACME HTTP-01 challenge answered at the name being certified" - }, - { - "name": "https", - "port": 443, - "protocol": "tcp", - "from": "anywhere", - "why": "public HTTPS for every name the mesh routes here" - } - ], - "data": { - "own": [ - { - "id": "acme", - "path": "${dir:acme-cache}", - "class": "rebuildable", - "why": "issued certificates, issued again" - }, - { - "id": "ca", - "path": "${dir:ca-dir}", - "class": "cache", - "why": "the authority's roots, fetched again at every start" - } - ] - }, - "resources": [ - { - "id": "state", - "type": "directory", - "mode": "0700", - "place": "." - }, - { - "id": "mesh-state", - "type": "directory", - "mode": "0700", - "place": "mesh" - }, - { - "id": "routes-dir", - "type": "directory", - "path": "/var/lib/route-proxy/routes", - "mode": "0700" - }, - { - "id": "acme-cache", - "type": "directory", - "path": "/var/lib/route-proxy/acme", - "mode": "0700" - }, - { - "id": "ca-dir", - "type": "directory", - "path": "/var/lib/route-proxy/ca", - "mode": "0755" - }, - { - "id": "acme-env", - "type": "file", - "path": "${dir:state}/acme.env", - "mode": "0600", - "template": "files/acme.env" - }, - { - "id": "internal-acme-env", - "type": "file", - "path": "${dir:state}/internal-acme.env", - "mode": "0600", - "template": "files/internal-acme.env" - }, - { - "id": "trust", - "type": "container", - "name": "route-proxy-trust", - "artifact": "trust", - "run-once": true, - "network": "host", - "env-file": [ - "${dir:state}/acme.env" - ], - "volumes": [ - "${dir:ca-dir}:/ca" - ], - "args": [ - "sh", - "-c", - "if [ -z \"$ACME_ROOTS_PATH\" ]; then cp /etc/ssl/certs/ca-certificates.crt /ca/root.crt; exit 0; fi; for i in $(seq 1 60); do wget -q -T 10 --no-check-certificate -O /ca/root.crt \"$ACME_ROOTS\" && grep -q 'BEGIN CERTIFICATE' /ca/root.crt && exit 0; sleep 2; done; echo \"the authority at $ACME_ROOTS did not serve its roots within two minutes\" >&2; exit 1" - ], - "restart-on": [ - "acme-env" - ] - }, - { - "id": "internal-trust", - "type": "container", - "name": "route-proxy-internal-trust", - "artifact": "trust", - "run-once": true, - "network": "host", - "env-file": [ - "${dir:state}/internal-acme.env" - ], - "volumes": [ - "${dir:ca-dir}:/ca" - ], - "args": [ - "sh", - "-c", - "if [ -z \"$INTERNAL_ACME_ROOTS_PATH\" ]; then cp /etc/ssl/certs/ca-certificates.crt /ca/internal-root.crt; exit 0; fi; for i in $(seq 1 60); do wget -q -T 10 --no-check-certificate -O /ca/internal-root.crt \"$INTERNAL_ACME_ROOTS\" && grep -q 'BEGIN CERTIFICATE' /ca/internal-root.crt && exit 0; sleep 2; done; echo \"the authority at $INTERNAL_ACME_ROOTS did not serve its roots within two minutes\" >&2; exit 1" - ], - "restart-on": [ - "internal-acme-env" - ] - }, - { - "id": "server", - "type": "container", - "name": "route-proxy", - "artifact": "server", - "network": "host", - "env-file": [ - "${dir:state}/acme.env", - "${dir:state}/internal-acme.env" - ], - "volumes": [ - "${dir:routes-dir}:/routes:ro", - "${dir:acme-cache}:/acme", - "${dir:ca-dir}:/ca:ro", - "${dir:mesh-state}/broker:/run/secrets/broker:ro" - ], - "env": { - "ROUTES": "/routes/mesh.json", - "LISTEN": ":80", - "TLS_LISTEN": ":443", - "ACME_CACHE": "/acme", - "ACME_CA_BUNDLE": "/ca/root.crt", - "INTERNAL_ACME_CA_BUNDLE": "/ca/internal-root.crt", - "MESH_BROKER_FILE": "/run/secrets/broker" - }, - "restart-on": [ - "trust", - "acme-env", - "internal-trust", - "internal-acme-env" - ], - "logging": "journald", - "health": { - "kind": "tcp", - "endpoint": "http", - "interval": "10s", - "timeout": "5s", - "looks": 3, - "grace": "60s" - } - } - ], - "build": { - "artifacts": [ - { - "name": "server", - "kind": "image", - "from": "Dockerfile", - "compiles": "examples/route-proxy", - "context": { - "seat": "git", - "repository": "novox/mesh-controller", - "ref": "main" - } - }, - { - "name": "trust", - "kind": "upstream", - "from": "alpine@sha256:28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b" - }, - { - "name": "provider", - "kind": "bundle", - "language": "go", - "system": "arch", - "from": "cmd/route-provider", - "binary": "route-provider", - "loads": [ - "route-provider" - ] - }, - { - "name": "files", - "kind": "archive", - "from": "files" - } - ], - "on": [ - { - "arg": "GO_BASE", - "image": "golang@sha256:6c2a5538f964f1c82f97ad14988bf05de100d922d159d0e398b54c7b0ca0c6c9" - }, - { - "arg": "ALPINE_BASE", - "image": "alpine@sha256:d9e853e87e55526f6b2917df91a2115c36dd7c696a35be12163d44e6e2a4b6bc" - } - ] - }, - "jails": [ - { - "name": "route-proxy", - "failregex": "^.*TLS handshake error from :\\d+: (?:no public route for|acme/autocert: missing server name)\n ^.*refused: no route for .*, asked from :\\d+$", - "jail": "backend = systemd\njournalmatch = CONTAINER_NAME=route-proxy\nport = http,https\nmaxretry = 10\nfindtime = 1d\nbantime = 1d" - } - ], - "state": [ - "bound" - ], - "reads": [ - "mesh-controller.assignments" - ] -} diff --git a/modules/searxng/README.md b/modules/searxng/README.md new file mode 100644 index 00000000..6b28bbca --- /dev/null +++ b/modules/searxng/README.md @@ -0,0 +1,11 @@ +# searxng + +The `searxng` module of the mesh catalogue: runs the container image `valkey/valkey`; runs the container image `searxng/searxng`. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:mesh-state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:valkey-data}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/searxng/module.json b/modules/searxng/module.json index 01d40126..cee18e4e 100644 --- a/modules/searxng/module.json +++ b/modules/searxng/module.json @@ -107,18 +107,21 @@ "id": "mesh-state", "type": "directory", "mode": "0700", - "place": "mesh" + "place": "mesh", + "unknown": "program" }, { "id": "state", "type": "directory", "mode": "0700", - "place": "." + "place": ".", + "unknown": "program" }, { "id": "valkey-data", "type": "directory", - "mode": "0700" + "mode": "0700", + "unknown": "program" }, { "id": "net", diff --git a/modules/showcase/README.md b/modules/showcase/README.md new file mode 100644 index 00000000..e37d5141 --- /dev/null +++ b/modules/showcase/README.md @@ -0,0 +1,11 @@ +# showcase + +The `showcase` module of the mesh catalogue: installs the package `jq`; claims the seat `the-showcase`. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:mesh-state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `/opt/showcase`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/showcase/module.json b/modules/showcase/module.json index 9f9e9812..e657e2ab 100644 --- a/modules/showcase/module.json +++ b/modules/showcase/module.json @@ -101,13 +101,15 @@ "id": "mesh-state", "type": "directory", "mode": "0700", - "place": "mesh" + "place": "mesh", + "unknown": "program" }, { "id": "state", "type": "directory", "mode": "0755", - "place": "." + "place": ".", + "unknown": "program" }, { "id": "settings", @@ -120,7 +122,8 @@ "id": "packed", "type": "archive", "path": "/opt/showcase", - "artifact": "files" + "artifact": "files", + "unknown": "program" }, { "id": "net", diff --git a/modules/ssh-client/README.md b/modules/ssh-client/README.md index 301cd579..c6a42861 100644 --- a/modules/ssh-client/README.md +++ b/modules/ssh-client/README.md @@ -83,7 +83,7 @@ ADR 0417 (issue 1020) adds: `ssh_client_check` reports a failed last sweep, and a listed move with nothing at its destination, as a problem. -No other module declares a path under `~/.ssh` (ADR 0415 §6): the catalogue's `checks/sshdir` fails one +No other module declares a path under `~/.ssh` (ADR 0415 §6): the catalogue's `checks/owned` (ADR 0422 §8) fails one that does. So the predecessor's `config.d/mesh`, `config.bak-*`, `known_hosts.old`, `removed-*` and @@ -132,3 +132,10 @@ The tests use a temporary home and a fake runner, with public keys made for the itself), what it moves, what it leaves and names, a file being written, another file system, and the list written before each move; - that the tools served are the manifest's `tools`. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `~/.ssh`: the module's own sweep moves everything it does not know into a kept folder, listing each move first and failing loudly when one cannot be made (ADR 0415, ADR 0417); value `module` (ADR 0422 §3). +- `~/.ssh/config.d`: the same sweep covers it; value `module` (ADR 0415, ADR 0417). diff --git a/modules/ssh-client/cmd/ssh-client-tools/templates_test.go b/modules/ssh-client/cmd/ssh-client-tools/templates_test.go index 4b8be920..ccadfd09 100644 --- a/modules/ssh-client/cmd/ssh-client-tools/templates_test.go +++ b/modules/ssh-client/cmd/ssh-client-tools/templates_test.go @@ -156,7 +156,6 @@ func TestForgeFilesRenderFromTheBoundValues(t *testing.T) { t.Errorf("10-forge = %q, want %q", got, want) } - resourceByPath(t, home+"/.config/git") // the directory g := resourceByPath(t, home+"/.config/git/config") if g["into"] != "block" { t.Errorf("git config is %v, want a block", g["into"]) diff --git a/modules/ssh-client/module.json b/modules/ssh-client/module.json index d4780c2f..a6965445 100644 --- a/modules/ssh-client/module.json +++ b/modules/ssh-client/module.json @@ -34,14 +34,16 @@ "type": "directory", "path": "${machine:account-home}/.ssh", "owner": "${machine:account}", - "mode": "0700" + "mode": "0700", + "unknown": "module" }, { "id": "config-d", "type": "directory", "path": "${machine:account-home}/.ssh/config.d", "owner": "${machine:account}", - "mode": "0700" + "mode": "0700", + "unknown": "module" }, { "id": "config", @@ -70,13 +72,6 @@ "mode": "0600", "template": "files/forge" }, - { - "id": "git-config-dir", - "type": "directory", - "path": "${machine:account-home}/.config/git", - "owner": "${machine:account}", - "mode": "0755" - }, { "id": "git-config", "type": "file", diff --git a/modules/step-ca/README.md b/modules/step-ca/README.md new file mode 100644 index 00000000..455ee1e7 --- /dev/null +++ b/modules/step-ca/README.md @@ -0,0 +1,10 @@ +# step-ca + +The `step-ca` module of the mesh catalogue: runs the container image `smallstep/step-ca`. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:mesh-state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:home}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/step-ca/module.json b/modules/step-ca/module.json index ebb587db..b11d2b99 100644 --- a/modules/step-ca/module.json +++ b/modules/step-ca/module.json @@ -43,14 +43,16 @@ "id": "mesh-state", "type": "directory", "mode": "0700", - "place": "mesh" + "place": "mesh", + "unknown": "program" }, { "id": "home", "type": "directory", "mode": "0700", "owner": "1000:1000", - "place": "." + "place": ".", + "unknown": "program" }, { "id": "config", diff --git a/modules/supabase/README.md b/modules/supabase/README.md new file mode 100644 index 00000000..eafb0cae --- /dev/null +++ b/modules/supabase/README.md @@ -0,0 +1,16 @@ +# supabase + +The `supabase` module of the mesh catalogue: runs the container image `timberio/vector`; runs the container image `supabase/postgres`; runs the container image `supabase/logflare`; runs the container image `kong`; runs the container image `supabase/gotrue`; runs the container image `postgrest/postgrest`; runs the container image `supabase/realtime`; runs the container image `darthsim/imgproxy`; runs the container image `supabase/storage-api`; runs the container image `supabase/postgres-meta`; runs the container image `supabase/edge-runtime`; runs the container image `supabase/studio`; runs the container image `supabase/supavisor`. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:state}/db-init`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:state}/functions-main`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:db-data}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:state}/dumps`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:db-config}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:storage}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:functions}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/supabase/module.json b/modules/supabase/module.json index 2ae3515c..14ddbcd5 100644 --- a/modules/supabase/module.json +++ b/modules/supabase/module.json @@ -113,47 +113,55 @@ "id": "state", "type": "directory", "mode": "0700", - "place": "." + "place": ".", + "unknown": "program" }, { "id": "db-init", "type": "directory", "path": "${dir:state}/db-init", - "mode": "0755" + "mode": "0755", + "unknown": "program" }, { "id": "functions-main-dir", "type": "directory", "path": "${dir:state}/functions-main", - "mode": "0755" + "mode": "0755", + "unknown": "program" }, { "id": "db-data", "type": "directory", "mode": "0700", - "owner": "105:106" + "owner": "105:106", + "unknown": "program" }, { "id": "dumps", "type": "directory", "path": "${dir:state}/dumps", - "mode": "0700" + "mode": "0700", + "unknown": "program" }, { "id": "db-config", "type": "directory", "mode": "0755", - "owner": "105:106" + "owner": "105:106", + "unknown": "program" }, { "id": "storage", "type": "directory", - "mode": "0755" + "mode": "0755", + "unknown": "program" }, { "id": "functions", "type": "directory", - "mode": "0755" + "mode": "0755", + "unknown": "program" }, { "id": "settings", diff --git a/modules/systemd/README.md b/modules/systemd/README.md new file mode 100644 index 00000000..cf68a0c1 --- /dev/null +++ b/modules/systemd/README.md @@ -0,0 +1,9 @@ +# systemd + +The `systemd` module of the mesh catalogue: installs the package `systemd`; claims the seat `node-service-manager`. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/systemd/module.json b/modules/systemd/module.json index 11ee4042..984ccf3a 100644 --- a/modules/systemd/module.json +++ b/modules/systemd/module.json @@ -56,7 +56,8 @@ "id": "state", "type": "directory", "mode": "0700", - "place": "." + "place": ".", + "unknown": "program" } ] } diff --git a/modules/telegram/README.md b/modules/telegram/README.md index f8821915..aeb1ea79 100644 --- a/modules/telegram/README.md +++ b/modules/telegram/README.md @@ -102,3 +102,9 @@ never inject one. | tool | does | |---|---| | `telegram_status` | whether it can send and why not (never the token), the bot's name, when updates last arrived, the last delivery and error, counts since start, and its rules | + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/telegram/module.json b/modules/telegram/module.json index 5534476a..95ae6f1b 100644 --- a/modules/telegram/module.json +++ b/modules/telegram/module.json @@ -54,7 +54,8 @@ "type": "directory", "mode": "0700", "owner": "telegram", - "place": "." + "place": ".", + "unknown": "program" } ], "build": { diff --git a/modules/triggerhappy/README.md b/modules/triggerhappy/README.md index a648fb12..7027cc34 100644 --- a/modules/triggerhappy/README.md +++ b/modules/triggerhappy/README.md @@ -45,3 +45,10 @@ model's module now contributes its keys. When it is pushed, the host gives back `asus-g14.conf` it kept, which still calls the predecessor's `as-user` scripts. Delete that file afterwards. The daemon reads only `mesh.conf` from then on, so the file would not fire, but `triggerhappy_check` flags any file besides the mesh's. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `/etc/systemd/system/triggerhappy.service.d`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `/etc/triggerhappy/triggers.d`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/triggerhappy/module.json b/modules/triggerhappy/module.json index fe8507c2..18faaf4b 100644 --- a/modules/triggerhappy/module.json +++ b/modules/triggerhappy/module.json @@ -22,7 +22,8 @@ "id": "drop-ins", "type": "directory", "path": "/etc/systemd/system/triggerhappy.service.d", - "mode": "0755" + "mode": "0755", + "unknown": "program" }, { "id": "as-account", @@ -35,7 +36,8 @@ "id": "triggers-dir", "type": "directory", "path": "/etc/triggerhappy/triggers.d", - "mode": "0755" + "mode": "0755", + "unknown": "program" }, { "id": "triggers", diff --git a/modules/umami/README.md b/modules/umami/README.md new file mode 100644 index 00000000..f0358716 --- /dev/null +++ b/modules/umami/README.md @@ -0,0 +1,10 @@ +# umami + +The `umami` module of the mesh catalogue: runs the container image `ghcr.io/umami-software/umami`. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:grants}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/umami/module.json b/modules/umami/module.json index a11f9b76..22ead419 100644 --- a/modules/umami/module.json +++ b/modules/umami/module.json @@ -67,12 +67,14 @@ "id": "state", "type": "directory", "mode": "0700", - "place": "." + "place": ".", + "unknown": "program" }, { "id": "grants", "type": "directory", - "mode": "0700" + "mode": "0700", + "unknown": "program" }, { "id": "server-env", diff --git a/modules/unifi/README.md b/modules/unifi/README.md new file mode 100644 index 00000000..12b95230 --- /dev/null +++ b/modules/unifi/README.md @@ -0,0 +1,11 @@ +# unifi + +The `unifi` module of the mesh catalogue: runs the container image `lscr.io/linuxserver/unifi-controller`. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:mesh-state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `${dir:data}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/unifi/module.json b/modules/unifi/module.json index 77510e53..2ee5cf49 100644 --- a/modules/unifi/module.json +++ b/modules/unifi/module.json @@ -104,19 +104,22 @@ "id": "mesh-state", "type": "directory", "mode": "0700", - "place": "mesh" + "place": "mesh", + "unknown": "program" }, { "id": "state", "type": "directory", "mode": "0700", - "place": "." + "place": ".", + "unknown": "program" }, { "id": "data", "type": "directory", "mode": "0700", - "owner": "1000:1000" + "owner": "1000:1000", + "unknown": "program" }, { "id": "server", diff --git a/modules/website/README.md b/modules/website/README.md new file mode 100644 index 00000000..6819d111 --- /dev/null +++ b/modules/website/README.md @@ -0,0 +1,9 @@ +# website + +The `website` module of the mesh catalogue: runs the container image `registry-api.novox.be/novox/www`. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/website/module.json b/modules/website/module.json index 8a30f764..481cfc66 100644 --- a/modules/website/module.json +++ b/modules/website/module.json @@ -30,7 +30,8 @@ "id": "state", "type": "directory", "mode": "0700", - "place": "." + "place": ".", + "unknown": "program" }, { "id": "net", diff --git a/modules/xorg/README.md b/modules/xorg/README.md index 65265569..5172b801 100644 --- a/modules/xorg/README.md +++ b/modules/xorg/README.md @@ -202,3 +202,9 @@ bindings. Those bindings belong to that machine's hardware module, not here. - **The `# ` naming lines in the `xresources` slots** are safe only because this module merges with `-nocpp`. If the controller rendered `!` for `xresources`, that would be the format's own comment. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `~/.config/xorg`: holds only what this module declares; anything else is moved out by the node-engine to `~/.local/state/mesh-moved/xorg/` with a list, never deleted (ADR 0422 §4). diff --git a/modules/xorg/module.json b/modules/xorg/module.json index 298ad877..7b0b6b1e 100644 --- a/modules/xorg/module.json +++ b/modules/xorg/module.json @@ -89,7 +89,8 @@ "type": "directory", "path": "${machine:account-home}/.config/xorg", "owner": "${machine:account}", - "mode": "0755" + "mode": "0755", + "unknown": "move-out" }, { "id": "xresources", diff --git a/modules/zfs/README.md b/modules/zfs/README.md index 6eac079f..ecb0b5dc 100644 --- a/modules/zfs/README.md +++ b/modules/zfs/README.md @@ -222,3 +222,12 @@ The tests cover: - the health verdicts, past against new errors; - a machine without ZFS; - the manifest against the tools the bundle serves. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `/var/lib/zfs-tools`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `/run/zfs-events`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `/etc/zfs-tools`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). +- `/var/lib/zfs-scrub`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/zfs/module.json b/modules/zfs/module.json index 06d97f96..c109320a 100644 --- a/modules/zfs/module.json +++ b/modules/zfs/module.json @@ -142,13 +142,15 @@ "type": "directory", "path": "/var/lib/zfs-tools", "mode": "0755", - "owner": "${machine:account}" + "owner": "${machine:account}", + "unknown": "program" }, { "id": "cursor-dir", "type": "directory", "path": "/run/zfs-events", - "mode": "0700" + "mode": "0700", + "unknown": "program" }, { "id": "events", @@ -167,7 +169,8 @@ "id": "config-dir", "type": "directory", "path": "/etc/zfs-tools", - "mode": "0755" + "mode": "0755", + "unknown": "program" }, { "id": "scrub-config", @@ -180,7 +183,8 @@ "id": "scrub-state-dir", "type": "directory", "path": "/var/lib/zfs-scrub", - "mode": "0700" + "mode": "0700", + "unknown": "program" }, { "id": "scrub-run", diff --git a/modules/zsh/README.md b/modules/zsh/README.md index a2e80e0f..0104778e 100644 --- a/modules/zsh/README.md +++ b/modules/zsh/README.md @@ -22,6 +22,9 @@ The login shell as a module (novox/hq ADR 0176, ADR 0204, to-be 41). and `~/.local/bin`, `~/scripts`, `~/scripts/bin` at the start of `PATH`. The holder of `node-environment` (`node-env`) writes them; this module writes no `export` of its own. +zsh owns no folder. It writes two blocks, one into `~/.zshrc` and one into `~/.zshenv`, and every line +outside them is the person's (ADR 0422, ADR 0416). + ## The two blocks Each block is the mesh's, between `# BEGIN mesh zsh.` and `# END mesh zsh.`. Every line @@ -91,3 +94,9 @@ them by hand once the module that replaces each is assigned: - `~/.zsh/plugins/zsh-autocomplete`: loaded by nothing today. Anything else under `~/.zsh/` is yours. + +## Files it does not know + +Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416). + +- `${dir:state}`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2). diff --git a/modules/zsh/module.json b/modules/zsh/module.json index adc1234e..da125af2 100644 --- a/modules/zsh/module.json +++ b/modules/zsh/module.json @@ -125,7 +125,8 @@ "type": "directory", "place": ".", "owner": "root", - "mode": "0755" + "mode": "0755", + "unknown": "program" }, { "id": "tools-conf", diff --git a/modules/zsh/test/manifest.test.ts b/modules/zsh/test/manifest.test.ts index 25ea3f78..019e94eb 100644 --- a/modules/zsh/test/manifest.test.ts +++ b/modules/zsh/test/manifest.test.ts @@ -104,7 +104,7 @@ test("execute is a per-machine config value, served unless a machine withholds i assert.equal(m.config.execute.default, "serve"); assert.match(m.config.execute.description, /withhold/); const dir = resource("state"); - assert.deepEqual(dir, { id: "state", type: "directory", place: ".", owner: "root", mode: "0755" }); + assert.deepEqual(dir, { id: "state", type: "directory", place: ".", owner: "root", mode: "0755", unknown: "program" }); const conf = resource("tools-conf"); assert.equal(conf.type, "file"); assert.equal(conf.path, "${dir:state}/tools.conf");