diff --git a/modules/builder/module.json b/modules/builder/module.json index 9d38701..68b3e9e 100644 --- a/modules/builder/module.json +++ b/modules/builder/module.json @@ -6,7 +6,7 @@ ], "claims": [ { - "name": "the-build-machine", + "name": "mesh-build-machine", "scope": "node" } ], diff --git a/modules/distribution/module.json b/modules/distribution/module.json index da0cfef..dfa7c9c 100644 --- a/modules/distribution/module.json +++ b/modules/distribution/module.json @@ -9,7 +9,7 @@ ], "claims": [ { - "name": "the-artifact-store", + "name": "mesh-artifact-store", "scope": "mesh" } ], diff --git a/modules/dnsmasq/module.json b/modules/dnsmasq/module.json index 3442b37..3e3ae78 100644 --- a/modules/dnsmasq/module.json +++ b/modules/dnsmasq/module.json @@ -16,7 +16,7 @@ }, "claims": [ { - "name": "the-dns-port", + "name": "mesh-dns-port", "scope": "node" } ], @@ -25,7 +25,7 @@ "port": 53, "protocol": "udp", "from": "mesh", - "why": "every name for this machine and what it runs — the mesh's own answered here, the rest forwarded", + "why": "every name for this machine and what it runs \u2014 the mesh's own answered here, the rest forwarded", "fixed": true } ], @@ -46,7 +46,7 @@ "type": "file", "path": "/etc/dnsmasq.conf", "mode": "0644", - "content": "# Managed by the mesh. dnsmasq's own defaults are replaced whole rather than\n# patched, because this module owns the file and a patch would leave whatever\n# was there before to be discovered later.\n\n# What the mesh computed: one wildcard per machine — its name and everything\n# under it — and the mesh's own suffix as a local domain, so a name under it is\n# answered here or not at all and is never asked upstream. Rewritten whenever a\n# machine joins or leaves, which is why the service below restarts on it: a\n# reload makes dnsmasq re-read hosts files, not its configuration, and a\n# wildcard is configuration.\nconf-file=/etc/mesh-resolver/nodes.conf\n\n# Where it answers. Both are names the mesh chose, so this file needs to know\n# nothing about this particular machine:\n#\n# mesh0 the private network, so anything on it can ask — including\n# this machine's containers. This module writes the runtime's\n# `dns` key into its own configuration file, beside whatever the\n# machine had there (novox/hq ADR 0102), naming this address: a\n# container cannot reach the machine's loopback, and a runtime\n# whose host resolves at loopback falls back to a public resolver\n# and never sees a mesh name. The runtime reads that key when it\n# starts and not on a reload, and a restart stops every container\n# on the machine, so this module orders neither: the key holds for\n# every container created after the runtime next starts. On the\n# machine this replaces the predecessor wrote the same value, so\n# nothing there is waiting on it.\n# 127.0.0.1 this machine's own use. The predecessor's resolver answered\n# here, and the resolv.conf it wrote on every machine says so;\n# that file stays in force on an adopted machine until the mesh's\n# module for it is taken, so the resolver has to answer where the\n# machine already asks or the machine loses DNS the moment this\n# module is taken. Not .53 or .54: systemd-resolved holds BOTH —\n# .53 is its stub and .54 its proxy stub — and neither is .1, so\n# the two coexist on a machine that runs it. This module used to\n# answer on 127.0.0.55 instead: a convention of its own, beside\n# the one every machine already followed. One address, this one,\n# and the modules that point a machine at the mesh name the same.\n#\n# Whatever address it listens on, it takes the machine's DNS port.\n# That is why this module claims `the-dns-port`.\n#\n# bind-dynamic rather than bind-interfaces: mesh0 does not exist until the\n# machine is on the private network, and binding an interface that is not there\n# yet fails to start rather than waiting for it.\nbind-dynamic\ninterface=mesh0\nlisten-address=127.0.0.1\n\n# **It must never read resolv.conf to find out where to forward.** Whatever\n# points this machine at the mesh writes this resolver's own address there — so\n# a resolver that read it for upstreams would find itself, and every query it\n# could not answer locally would loop until its receive queue filled. That is\n# not theoretical: it filled with 15KB of queries and every lookup on the\n# machine hung. no-resolv is what makes that loop impossible: the upstreams are\n# the two lines below, and nothing on the machine can redirect them.\n#\n# It forwards, because it is now asked for everything. The module that points\n# this machine at the mesh names this resolver alone — as the predecessor's\n# did — so the host and every container resolve the world through it. The\n# upstreams are the ones the predecessor's module shipped as its defaults. The\n# mesh's own names never reach them: the local= line in the file above stops\n# them here, answered or refused.\nno-resolv\nserver=1.1.1.1\nserver=8.8.8.8\n\n# A name without a dot is never forwarded — a bare hostname is answered from\n# /etc/hosts or not at all — and reverse lookups of private ranges are answered\n# here rather than asking the world who 10.x is.\ndomain-needed\nbogus-priv\n" + "content": "# Managed by the mesh. dnsmasq's own defaults are replaced whole rather than\n# patched, because this module owns the file and a patch would leave whatever\n# was there before to be discovered later.\n\n# What the mesh computed: one wildcard per machine \u2014 its name and everything\n# under it \u2014 and the mesh's own suffix as a local domain, so a name under it is\n# answered here or not at all and is never asked upstream. Rewritten whenever a\n# machine joins or leaves, which is why the service below restarts on it: a\n# reload makes dnsmasq re-read hosts files, not its configuration, and a\n# wildcard is configuration.\nconf-file=/etc/mesh-resolver/nodes.conf\n\n# Where it answers. Both are names the mesh chose, so this file needs to know\n# nothing about this particular machine:\n#\n# mesh0 the private network, so anything on it can ask \u2014 including\n# this machine's containers. This module writes the runtime's\n# `dns` key into its own configuration file, beside whatever the\n# machine had there (novox/hq ADR 0102), naming this address: a\n# container cannot reach the machine's loopback, and a runtime\n# whose host resolves at loopback falls back to a public resolver\n# and never sees a mesh name. The runtime reads that key when it\n# starts and not on a reload, and a restart stops every container\n# on the machine, so this module orders neither: the key holds for\n# every container created after the runtime next starts. On the\n# machine this replaces the predecessor wrote the same value, so\n# nothing there is waiting on it.\n# 127.0.0.1 this machine's own use. The predecessor's resolver answered\n# here, and the resolv.conf it wrote on every machine says so;\n# that file stays in force on an adopted machine until the mesh's\n# module for it is taken, so the resolver has to answer where the\n# machine already asks or the machine loses DNS the moment this\n# module is taken. Not .53 or .54: systemd-resolved holds BOTH \u2014\n# .53 is its stub and .54 its proxy stub \u2014 and neither is .1, so\n# the two coexist on a machine that runs it. This module used to\n# answer on 127.0.0.55 instead: a convention of its own, beside\n# the one every machine already followed. One address, this one,\n# and the modules that point a machine at the mesh name the same.\n#\n# Whatever address it listens on, it takes the machine's DNS port.\n# That is why this module claims `the-dns-port`.\n#\n# bind-dynamic rather than bind-interfaces: mesh0 does not exist until the\n# machine is on the private network, and binding an interface that is not there\n# yet fails to start rather than waiting for it.\nbind-dynamic\ninterface=mesh0\nlisten-address=127.0.0.1\n\n# **It must never read resolv.conf to find out where to forward.** Whatever\n# points this machine at the mesh writes this resolver's own address there \u2014 so\n# a resolver that read it for upstreams would find itself, and every query it\n# could not answer locally would loop until its receive queue filled. That is\n# not theoretical: it filled with 15KB of queries and every lookup on the\n# machine hung. no-resolv is what makes that loop impossible: the upstreams are\n# the two lines below, and nothing on the machine can redirect them.\n#\n# It forwards, because it is now asked for everything. The module that points\n# this machine at the mesh names this resolver alone \u2014 as the predecessor's\n# did \u2014 so the host and every container resolve the world through it. The\n# upstreams are the ones the predecessor's module shipped as its defaults. The\n# mesh's own names never reach them: the local= line in the file above stops\n# them here, answered or refused.\nno-resolv\nserver=1.1.1.1\nserver=8.8.8.8\n\n# A name without a dot is never forwarded \u2014 a bare hostname is answered from\n# /etc/hosts or not at all \u2014 and reverse lookups of private ranges are answered\n# here rather than asking the world who 10.x is.\ndomain-needed\nbogus-priv\n" }, { "id": "runtime-dns", diff --git a/modules/fail2ban/module.json b/modules/fail2ban/module.json index 8b94092..208e204 100644 --- a/modules/fail2ban/module.json +++ b/modules/fail2ban/module.json @@ -6,7 +6,7 @@ ], "claims": [ { - "name": "the-intrusion-prevention", + "name": "mesh-intrusion-prevention", "scope": "node" } ], diff --git a/modules/gitea/module.json b/modules/gitea/module.json index d80f2ee..a5b9843 100644 --- a/modules/gitea/module.json +++ b/modules/gitea/module.json @@ -75,11 +75,11 @@ }, "claims": [ { - "name": "npm-package-registry", + "name": "mesh-npm-package-registry", "scope": "mesh" }, { - "name": "git", + "name": "mesh-git", "scope": "mesh" } ], diff --git a/modules/mesh-catalog/module.json b/modules/mesh-catalog/module.json index 097368a..4c52d90 100644 --- a/modules/mesh-catalog/module.json +++ b/modules/mesh-catalog/module.json @@ -7,7 +7,7 @@ ], "claims": [ { - "name": "the-catalogue", + "name": "mesh-catalog", "scope": "mesh" } ], diff --git a/modules/nftables/module.json b/modules/nftables/module.json index 11a6be6..2eadabb 100644 --- a/modules/nftables/module.json +++ b/modules/nftables/module.json @@ -6,7 +6,7 @@ ], "claims": [ { - "name": "the-packet-filter", + "name": "mesh-packet-filter", "scope": "node" } ], @@ -30,7 +30,7 @@ "id": "stock-unit-stop", "type": "file", "path": "/etc/systemd/system/nftables.service.d/mesh.conf", - "content": "# The mesh: stopping the stock unit deletes only the mesh's table, never the whole ruleset\n# (novox/hq ADR 0100) — a flush would take the container runtime's rules and any firewall with it.\n[Service]\nExecStop=\nExecStop=nft delete table inet mesh\n", + "content": "# The mesh: stopping the stock unit deletes only the mesh's table, never the whole ruleset\n# (novox/hq ADR 0100) \u2014 a flush would take the container runtime's rules and any firewall with it.\n[Service]\nExecStop=\nExecStop=nft delete table inet mesh\n", "mode": "0644" }, { diff --git a/modules/resolv-conf/module.json b/modules/resolv-conf/module.json index 520e864..4a4a7e9 100644 --- a/modules/resolv-conf/module.json +++ b/modules/resolv-conf/module.json @@ -2,12 +2,22 @@ "module": "resolv-conf", "version": "1", "slug": "resolv", - - "requires": ["wildcard-resolution"], - "claims": [{"name": "the-resolver-configuration", "scope": "node"}], - + "requires": [ + "wildcard-resolution" + ], + "claims": [ + { + "name": "mesh-resolver-configuration", + "scope": "node" + } + ], "resources": [ - {"id": "resolv", "type": "file", "path": "/etc/resolv.conf", "mode": "0644", - "content": "# Managed by the mesh.\n#\n# For a machine where nothing else owns this file. On one where systemd-resolved\n# or NetworkManager does, assign that module instead — this one and those claim\n# the same thing, so the mesh refuses the pair rather than letting them take\n# turns overwriting each other, which is the failure this claim exists to stop.\n#\n# The mesh's resolver, and only it — the one line the predecessor wrote on every\n# machine it set up. It answers the mesh's names itself and forwards everything\n# else to upstreams named in its own configuration, never read from this file.\n# This file used to carry a second nameserver as a placeholder for \"whatever\n# this machine used before\"; that was never a fallback for names the mesh does\n# not know — a resolver's second line is asked only when the first does not\n# answer at all — and now that the first answers everything it would be a line\n# nothing ever reached.\nnameserver 127.0.0.1\noptions edns0\n"} + { + "id": "resolv", + "type": "file", + "path": "/etc/resolv.conf", + "mode": "0644", + "content": "# Managed by the mesh.\n#\n# For a machine where nothing else owns this file. On one where systemd-resolved\n# or NetworkManager does, assign that module instead \u2014 this one and those claim\n# the same thing, so the mesh refuses the pair rather than letting them take\n# turns overwriting each other, which is the failure this claim exists to stop.\n#\n# The mesh's resolver, and only it \u2014 the one line the predecessor wrote on every\n# machine it set up. It answers the mesh's names itself and forwards everything\n# else to upstreams named in its own configuration, never read from this file.\n# This file used to carry a second nameserver as a placeholder for \"whatever\n# this machine used before\"; that was never a fallback for names the mesh does\n# not know \u2014 a resolver's second line is asked only when the first does not\n# answer at all \u2014 and now that the first answers everything it would be a line\n# nothing ever reached.\nnameserver 127.0.0.1\noptions edns0\n" + } ] } diff --git a/modules/resolved-split-dns/module.json b/modules/resolved-split-dns/module.json index 246ba83..b8efa28 100644 --- a/modules/resolved-split-dns/module.json +++ b/modules/resolved-split-dns/module.json @@ -2,18 +2,38 @@ "module": "resolved-split-dns", "version": "1", "slug": "splitdns", - - "requires": ["wildcard-resolution"], - "claims": [{"name": "the-resolver-configuration", "scope": "node"}], - + "requires": [ + "wildcard-resolution" + ], + "claims": [ + { + "name": "mesh-resolver-configuration", + "scope": "node" + } + ], "resources": [ - {"id": "drop-in", "type": "directory", "path": "/etc/systemd/resolved.conf.d", "mode": "0755"}, - - {"id": "route", "type": "file", - "path": "/etc/systemd/resolved.conf.d/mesh.conf", "mode": "0644", - "content": "# Managed by the mesh.\n#\n# **Only the mesh's names.** The tilde makes this a routing domain rather than a\n# search domain: queries under it go to the resolver below, and everything else\n# keeps going wherever this machine already sent it. A resolver that took over\n# all of DNS would be this module claiming the machine's whole network, which\n# is not what it says it claims. The mesh's resolver can forward the rest too;\n# this module is for a machine that wants systemd-resolved to stay in charge of\n# that, and only lends it the mesh's suffix.\n#\n# 127.0.0.1 is where the mesh's resolver answers on every machine — a fixed\n# address, so this file needs to know nothing about this particular machine.\n# systemd-resolved holds .53 and .54 itself, which is why the resolver is on\n# neither, and why the two coexist here.\n[Resolve]\nDNS=127.0.0.1\nDomains=~internal\n"}, - - {"id": "resolved", "type": "service", "unit": "systemd-resolved.service", - "state": "running", "boot": "enabled", "restart-on": ["route"]} + { + "id": "drop-in", + "type": "directory", + "path": "/etc/systemd/resolved.conf.d", + "mode": "0755" + }, + { + "id": "route", + "type": "file", + "path": "/etc/systemd/resolved.conf.d/mesh.conf", + "mode": "0644", + "content": "# Managed by the mesh.\n#\n# **Only the mesh's names.** The tilde makes this a routing domain rather than a\n# search domain: queries under it go to the resolver below, and everything else\n# keeps going wherever this machine already sent it. A resolver that took over\n# all of DNS would be this module claiming the machine's whole network, which\n# is not what it says it claims. The mesh's resolver can forward the rest too;\n# this module is for a machine that wants systemd-resolved to stay in charge of\n# that, and only lends it the mesh's suffix.\n#\n# 127.0.0.1 is where the mesh's resolver answers on every machine \u2014 a fixed\n# address, so this file needs to know nothing about this particular machine.\n# systemd-resolved holds .53 and .54 itself, which is why the resolver is on\n# neither, and why the two coexist here.\n[Resolve]\nDNS=127.0.0.1\nDomains=~internal\n" + }, + { + "id": "resolved", + "type": "service", + "unit": "systemd-resolved.service", + "state": "running", + "boot": "enabled", + "restart-on": [ + "route" + ] + } ] } diff --git a/modules/showcase/module.json b/modules/showcase/module.json index 289cf76..a72c2e3 100644 --- a/modules/showcase/module.json +++ b/modules/showcase/module.json @@ -2,72 +2,189 @@ "module": "showcase", "version": "1", "slug": "show", - - "capabilities": ["container-runtime"], - - "provides": [{ "name": "greeting", "scope": "mesh" }], - "serves": { "greeting": { "path": "/greeting" } }, - "requires": ["postgres-database"], - "binds": { "postgres-database": "/var/lib/showcase/database.json" }, - "secrets": { "postgres-database": "/var/lib/showcase/database.secret" }, - "own-secrets": { "broker": "/var/lib/mesh/showcase/broker" }, - - "claims": [{ "name": "the-showcase", "scope": "node" }], - - "emits": ["module.showcase.acknowledged"], - "consumes": ["module.showcase.greeted"], - + "capabilities": [ + "container-runtime" + ], + "provides": [ + { + "name": "greeting", + "scope": "mesh" + } + ], + "serves": { + "greeting": { + "path": "/greeting" + } + }, + "requires": [ + "postgres-database" + ], + "binds": { + "postgres-database": "/var/lib/showcase/database.json" + }, + "secrets": { + "postgres-database": "/var/lib/showcase/database.secret" + }, + "own-secrets": { + "broker": "/var/lib/mesh/showcase/broker" + }, + "claims": [ + { + "name": "mesh-showcase", + "scope": "node" + } + ], + "emits": [ + "module.showcase.acknowledged" + ], + "consumes": [ + "module.showcase.greeted" + ], "listens": [ - { "port": 8080, "protocol": "tcp", "from": "mesh", - "why": "the port the daemon itself listens on. The mesh assigns the machine-side number and tells consumers that one (ADR 0038)" } + { + "port": 8080, + "protocol": "tcp", + "from": "mesh", + "why": "the port the daemon itself listens on. The mesh assigns the machine-side number and tells consumers that one (ADR 0038)" + } ], - "build": { "artifacts": [ - { "name": "code", "kind": "bundle", "language": "typescript", - "entrypoints": ["index.js", "tools/index.js", "provisioner/index.js", - "daemon/index.js", "step/index.js", "report/index.js"] }, - { "name": "files", "kind": "archive", "from": "files" }, - { "name": "helper", "kind": "upstream", - "from": "alpine@sha256:28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b" } + { + "name": "code", + "kind": "bundle", + "language": "typescript", + "entrypoints": [ + "index.js", + "tools/index.js", + "provisioner/index.js", + "daemon/index.js", + "step/index.js", + "report/index.js" + ] + }, + { + "name": "files", + "kind": "archive", + "from": "files" + }, + { + "name": "helper", + "kind": "upstream", + "from": "alpine@sha256:28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b" + } ] }, - "resources": [ - { "id": "account", "type": "user", "name": "showcase", "shell": "/usr/bin/nologin", - "home": "/var/lib/showcase" }, - - { "id": "logs", "type": "access", "path": "/var/log", "mode": "0755" }, - - { "id": "mesh-state", "type": "directory", "path": "/var/lib/mesh/showcase", "mode": "0700" }, - { "id": "state", "type": "directory", "path": "/var/lib/showcase", "mode": "0755" }, - - { "id": "settings", "type": "file", "path": "/var/lib/showcase/showcase.env", "mode": "0600", - "content": "SHOWCASE_GREETING=hello\nSHOWCASE_EVERY_SECONDS=30\nSHOWCASE_STATE=/var/lib/showcase\nSHOWCASE_DATABASE=${bound:postgres-database:at}\nSHOWCASE_LISTEN=${port:8080}\n" }, - - { "id": "packed", "type": "archive", "path": "/opt/showcase", "artifact": "files" }, - - { "id": "net", "type": "network", "name": "showcase" }, - - { "id": "tooling", "type": "package", "package": "jq" }, - - { "id": "migrate", "type": "process", "name": "showcase-migrate", "artifact": "code", - "run": ["node", "step/index.js"], "run-once": true, - "env-file": ["/var/lib/showcase/showcase.env"] }, - - { "id": "server", "type": "process", "name": "showcase", "artifact": "code", - "run": ["node", "daemon/index.js"], "user": "showcase", - "env-file": ["/var/lib/showcase/showcase.env"], - "restart-on": ["settings"] }, - - { "id": "reporting", "type": "process", "name": "showcase-report", "artifact": "code", - "run": ["node", "report/index.js"], "schedule": "0 3 * * *", - "env-file": ["/var/lib/showcase/showcase.env"] }, - - { "id": "tools", "type": "container", "name": "mesh-showcase", "artifact": "helper", + { + "id": "account", + "type": "user", + "name": "showcase", + "shell": "/usr/bin/nologin", + "home": "/var/lib/showcase" + }, + { + "id": "logs", + "type": "access", + "path": "/var/log", + "mode": "0755" + }, + { + "id": "mesh-state", + "type": "directory", + "path": "/var/lib/mesh/showcase", + "mode": "0700" + }, + { + "id": "state", + "type": "directory", + "path": "/var/lib/showcase", + "mode": "0755" + }, + { + "id": "settings", + "type": "file", + "path": "/var/lib/showcase/showcase.env", + "mode": "0600", + "content": "SHOWCASE_GREETING=hello\nSHOWCASE_EVERY_SECONDS=30\nSHOWCASE_STATE=/var/lib/showcase\nSHOWCASE_DATABASE=${bound:postgres-database:at}\nSHOWCASE_LISTEN=${port:8080}\n" + }, + { + "id": "packed", + "type": "archive", + "path": "/opt/showcase", + "artifact": "files" + }, + { + "id": "net", + "type": "network", + "name": "showcase" + }, + { + "id": "tooling", + "type": "package", + "package": "jq" + }, + { + "id": "migrate", + "type": "process", + "name": "showcase-migrate", + "artifact": "code", + "run": [ + "node", + "step/index.js" + ], + "run-once": true, + "env-file": [ + "/var/lib/showcase/showcase.env" + ] + }, + { + "id": "server", + "type": "process", + "name": "showcase", + "artifact": "code", + "run": [ + "node", + "daemon/index.js" + ], + "user": "showcase", + "env-file": [ + "/var/lib/showcase/showcase.env" + ], + "restart-on": [ + "settings" + ] + }, + { + "id": "reporting", + "type": "process", + "name": "showcase-report", + "artifact": "code", + "run": [ + "node", + "report/index.js" + ], + "schedule": "0 3 * * *", + "env-file": [ + "/var/lib/showcase/showcase.env" + ] + }, + { + "id": "tools", + "type": "container", + "name": "mesh-showcase", + "artifact": "helper", "network": "showcase", - "volumes": ["/var/lib/mesh/showcase/broker:/run/secrets/broker:ro"], - "env": { "MESH_BROKER_FILE": "/run/secrets/broker" }, - "args": ["sleep", "infinity"] } + "volumes": [ + "/var/lib/mesh/showcase/broker:/run/secrets/broker:ro" + ], + "env": { + "MESH_BROKER_FILE": "/run/secrets/broker" + }, + "args": [ + "sleep", + "infinity" + ] + } ] }