Every directory says how it treats files it does not know, and the catalogue checks it (issue 1024) #349

Merged
mesh-tools merged 2 commits from feat/owned-paths into main 2026-10-17 18:14:12 +00:00
211 changed files with 1785 additions and 1339 deletions
+3
View File
@@ -0,0 +1,3 @@
module git.novox.be/novox/mesh-catalog/checks/owned
go 1.22
+236
View File
@@ -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:<id>} 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 . <catalogue root>
//
// 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:<id>} 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")
}
+132
View File
@@ -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)
}
}
+8 -3
View File
@@ -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=""
+6
View File
@@ -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).
+2 -1
View File
@@ -26,7 +26,8 @@
"id": "state",
"type": "directory",
"mode": "0700",
"place": "."
"place": ".",
"unknown": "program"
},
{
"id": "store-names",
+12
View File
@@ -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 `# <module>` 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).
+14 -7
View File
@@ -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",
+11
View File
@@ -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).
+6 -3
View File
@@ -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": [
+11
View File
@@ -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).
+6 -3
View File
@@ -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",
+11
View File
@@ -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).
+13 -6
View File
@@ -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",
-26
View File
@@ -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"]
-34
View File
@@ -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.
@@ -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}
-3
View File
@@ -1,3 +0,0 @@
module build-agent
go 1.22
-174
View File
@@ -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)
}
}
-139
View File
@@ -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"
}
]
}
}
-64
View File
@@ -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)
}
}
+9
View File
@@ -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).
+2 -1
View File
@@ -25,7 +25,8 @@
"id": "state",
"type": "directory",
"mode": "0700",
"place": "."
"place": ".",
"unknown": "program"
},
{
"id": "anchor",
+15 -4
View File
@@ -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).
+6 -3
View File
@@ -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",
+6
View File
@@ -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).
+2 -1
View File
@@ -118,7 +118,8 @@
"id": "state",
"type": "directory",
"mode": "0700",
"place": "."
"place": ".",
"unknown": "program"
},
{
"id": "database-url",
+6
View File
@@ -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 `# <module>` 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).
+2 -8
View File
@@ -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",
+9
View File
@@ -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).
+2 -1
View File
@@ -24,7 +24,8 @@
"id": "state",
"type": "directory",
"mode": "0700",
"place": "."
"place": ".",
"unknown": "program"
},
{
"id": "config",
+6
View File
@@ -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).
+2 -1
View File
@@ -66,7 +66,8 @@
"type": "directory",
"mode": "0700",
"owner": "desk-channel",
"place": "."
"place": ".",
"unknown": "program"
},
{
"id": "settings",
+10
View File
@@ -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).
+4 -2
View File
@@ -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",
+11
View File
@@ -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).
+6 -3
View File
@@ -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",
+6
View File
@@ -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).
+2 -1
View File
@@ -83,7 +83,8 @@
"id": "state",
"type": "directory",
"mode": "0700",
"place": "mesh"
"place": "mesh",
"unknown": "program"
},
{
"id": "log-rotation",
+14 -5
View File
@@ -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).
+5 -2
View File
@@ -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",
+13
View File
@@ -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).
+10 -5
View File
@@ -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",
+6
View File
@@ -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 `# <module>` 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).
+2 -1
View File
@@ -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",
+11
View File
@@ -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).
+12 -6
View File
@@ -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",
+9
View File
@@ -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).
+2 -1
View File
@@ -17,7 +17,8 @@
"id": "state",
"type": "directory",
"mode": "0700",
"place": "."
"place": ".",
"unknown": "program"
},
{
"id": "config",
+13
View File
@@ -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).
+10 -5
View File
@@ -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",
+9
View File
@@ -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).
+2 -1
View File
@@ -31,7 +31,8 @@
"id": "state",
"type": "directory",
"mode": "0700",
"place": "."
"place": ".",
"unknown": "program"
},
{
"id": "page",
+12
View File
@@ -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).
+8 -4
View File
@@ -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",
+24 -14
View File
@@ -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/<NN>-<module>.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).
+6 -2
View File
@@ -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",
+11 -2
View File
@@ -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 `# <module>` 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).
+4 -2
View File
@@ -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",
+11
View File
@@ -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).
+6 -3
View File
@@ -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",
+13
View File
@@ -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).
+10 -5
View File
@@ -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",
+10
View File
@@ -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).
+4 -2
View File
@@ -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",
+9
View File
@@ -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).
+2 -1
View File
@@ -23,7 +23,8 @@
"id": "state",
"type": "directory",
"mode": "0700",
"place": "."
"place": ".",
"unknown": "program"
},
{
"id": "config",
+6
View File
@@ -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).
+2 -1
View File
@@ -20,7 +20,8 @@
"id": "state",
"type": "directory",
"mode": "0700",
"place": "."
"place": ".",
"unknown": "program"
},
{
"id": "huge-pages",
+8
View File
@@ -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).
+6 -3
View File
@@ -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",
+10
View File
@@ -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).
+4 -2
View File
@@ -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",
+7
View File
@@ -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).
+4 -2
View File
@@ -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",
+10
View File
@@ -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).
+4 -2
View File
@@ -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",
+10
View File
@@ -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).
+4 -2
View File
@@ -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",
+25
View File
@@ -15,3 +15,28 @@ installation's are settings on the assignment, `settings set mailu <file>`:
`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).
+40 -20
View File
@@ -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",
+9
View File
@@ -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).
+2 -1
View File
@@ -18,7 +18,8 @@
"id": "mesh-state",
"type": "directory",
"mode": "0700",
"place": "mesh"
"place": "mesh",
"unknown": "program"
},
{
"id": "server",
+10
View File
@@ -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).
+4 -2
View File
@@ -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",
+9
View File
@@ -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).
+8 -4
View File
@@ -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",
+9
View File
@@ -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).
+2 -1
View File
@@ -41,7 +41,8 @@
"id": "state",
"type": "directory",
"mode": "0700",
"place": "."
"place": ".",
"unknown": "program"
},
{
"id": "database-url",
+6
View File
@@ -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).
+2 -1
View File
@@ -7,7 +7,8 @@
"id": "program",
"type": "archive",
"artifact": "program",
"path": "/usr/local/bin"
"path": "/usr/local/bin",
"unknown": "program"
}
],
"items": [
+6
View File
@@ -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).
+2 -1
View File
@@ -52,7 +52,8 @@
"type": "directory",
"mode": "0700",
"owner": "mesh-config",
"place": "."
"place": ".",
"unknown": "program"
}
],
"build": {
+9
View File
@@ -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).
+2 -1
View File
@@ -76,7 +76,8 @@
"id": "state",
"type": "directory",
"mode": "0700",
"place": "."
"place": ".",
"unknown": "program"
}
],
"build": {
+6
View File
@@ -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 `<node>:/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).
+2 -1
View File
@@ -32,7 +32,8 @@
"id": "state",
"type": "directory",
"mode": "0700",
"place": "."
"place": ".",
"unknown": "program"
}
],
"build": {
+6
View File
@@ -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).
+2 -2
View File
@@ -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",
+4 -3
View File
@@ -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",

Some files were not shown because too many files have changed in this diff Show More