Commit Graph
430 Commits
Author SHA1 Message Date
jochen 2000ec3f48 The seven say what the runtime loads from their tools bundle (hq ADR 0192)
None declares a tools list, so the composer had nothing saying the runtime loads from the bundle,
and delivered it nowhere: built, recorded, never sent. loads names tools/index.js.
2026-10-03 15:58:19 +02:00
jochen 19511012f8 Seven tool containers move to bundles the node's runtime serves, given their words (hq ADR 0192)
baserow, confluence, gitlab, jira, letta, searxng and unifi: each runtime container's
environment becomes its tools bundle's env, mount targets folded back into the host paths they
came from; baserow and letta reach their service on the published port rather than a container
network name. The container, base images, Dockerfile and the module's own bus credential go,
and the state directory where only that credential lived. Tool code is unchanged: every client
is built from the environment the contributor is handed.
2026-10-03 15:35:00 +02:00
jschoubben b43e405947 dnsmasq: restart on the mesh's hosts region, which it reads only at start
The names region of /etc/hosts is written by mesh-wireguard and dnsmasq answers from it, but read
it once at start: after the mesh stopped publishing public names (hq ADR 0191) every machine's
hosts file was right and every resolver still answered mail.novox.be with a tunnel address. The
config comment that says so changes the file, which restarts dnsmasq once everywhere.
2026-10-03 15:29:28 +02:00
jochen aa5bf7d5ef fail2ban's tools are a bundle the node's runtime serves; its container goes (hq to-be 38 WP4)
The second holder follows the packet filter: the container, its base images, the Dockerfile,
and the bus credential and state directory only the container read are gone; the tools are a
TypeScript bundle node-tools loads. The daemon's socket answers only to root, so the client
runs through sudo without a prompt where the runtime's account is not root, naming sudo's
absence or refusal by how it failed; client and daemon are the one package the module declares.
2026-10-03 15:07:21 +02:00
mesh-admin 510de183b1 Merge pull request 'The packet filter's tools are a bundle the node's runtime serves; its container goes (hq to-be 38 WP4)' (#239) from feat/wp4-the-packet-filter-moves into main 2026-10-03 11:14:33 +00:00
jochen db5e7c80cf The packet filter's tools are a bundle the node's runtime serves; its container goes (hq to-be 38 WP4)
nftables drops its container, NET_ADMIN, the container-runtime capability, the runtime base
images, the Dockerfile, and the bus credential and state directory only the container read;
its tools are declared as a TypeScript bundle the toolchain compiles and node-tools loads on
every node, and the iptables package the image used to carry is declared on the host. The
runtime runs as the operator's account, so the tool runs the filter's commands through sudo
without a prompt when it is not root (ADR 0175 §4, to-be 38 WP4), naming sudo's absence or
refusal by how it failed; the filter file is the path the manifest's filtering names, held to
it by a test; a found firewall that is present but will not answer stops a removal rather
than passing for inactive; a legacy tool that is present but fails is said, not swallowed.
2026-10-03 12:59:07 +02:00
jschoubben a19638d112 ssh-client: own ~/.ssh, not the openssh package
A package has one owning module, and sshd already owns openssh on every node — assigning
ssh-client was refused for both declaring it, and the refusal left every node unresolvable
until it was unassigned. Owning it was wrong besides: unassigning ssh-client would have
removed the package sshd serves from. The client binary ships in the package sshd holds.
2026-10-03 12:43:35 +02:00
jschoubben 301aeda5f3 ssh-client module: the mesh owns ~/.ssh, config from the hub (to-be 29)
Requires openssh; creates ~/.ssh (0700, owned by the operator account via
${machine:account}); writes every other node's Host block (HostName + User
<account>) into a marked region of ~/.ssh/config (home-scoped, into:block), so
`ssh <node>` reaches each peer as the right account and the operator's own
config is kept. Universal-tier: assigned wherever a person logs in; a node with
no account gets no config.
2026-10-03 11:58:05 +02:00
jochen 8ccc6762d4 Retire builder: the build machine is the build-agent on every machine (hq ADR 0190)
build-agent holds node-build-agent on all four machines and the controller asks that seat; the one-holder
builder is unassigned and forgotten. Proven live before this: a catalogue module built on a workstation's
agent (step-ca, 2026-10-03 09:35).
2026-10-03 11:43:01 +02:00
jochen 02463ba55d build-agent: a short slug, so its identifier on a machine fits a backend's 20-character key
"mesh_novox_build_agent" is 22 characters; the mesh refused to send the control node its declaration
for it. "agent" keeps the identifier within what an S3 access key allows on every machine.
2026-10-03 11:09:24 +02:00
jschoubben 0778f8f0ae step-ca: stop offering acme-ca, so public names are certified by public-acme
step-ca and public-acme both offered acme-ca on novox, and route-proxy's pin names a node, not
a module — so which one certified the public names depended on provider order. After the
controller restart on 2026-10-03 it came out as step-ca, and every public site served a
certificate no browser trusts. step-ca's own names are already certified through
internal-acme-ca; acme-ca is the public authority's alone.
2026-10-03 10:54:43 +02:00
mesh-admin 6afc1160b6 Merge pull request 'build-agent: the build machine as a node role every machine can hold (hq ADR 0190)' (#231) from feat/build-agent into main 2026-10-03 01:52:04 +00:00
mesh-admin c32edcfc6f Merge pull request 'Retire mesh-console: the console is the node-tools runtime's serving mode (hq ADR 0175 §6, to-be 38 WP3)' (#230) from feat/retire-mesh-console into main 2026-10-03 00:40:37 +00:00
jschoubben a309deb479 Remove portainer: deprecated, and unassigned everywhere
It held the docker socket behind a public name. Nothing depends on it;
it is off both machines that ran it, with its data.
2026-10-02 23:25:45 +02:00
jschoubben 2e6cc71f7a gitea: the jail also bans what gitea's sshd refuses
The jail read gitea's container journal, which carries its sshd's lines,
but matched only the web login. 167 ssh attempts an hour from the
internet went unbanned. Two patterns, one per attempt: an unknown user,
and a user sshd refuses; tested against a day of the real log, 946
matches and none on an accepted login.
2026-10-02 23:23:35 +02:00
jochen 84403aec0c build-agent: the build machine as a node role every machine can hold (hq ADR 0190)
The builder's manifest with one change that matters: it claims node-build-agent, a node seat, so it
is assignable to every machine with a container runtime, and every holder pulls one build at a time
from the role's one work queue. A tier of many images is then built by as many machines as hold the
seat and are online. The builder module stays until this is assigned where it was; then it goes.
2026-10-02 22:28:48 +02:00
jochen b28b1b9f24 Retire mesh-console: the console is the node-tools runtime's serving mode (hq ADR 0175 §6, to-be 38 WP3)
The console was a container per node built on the runtime image, calling everything and serving
nothing. node-tools — the runtime as a module, in the mesh-tools repository — answers MCP on the
same loopback port from the same process that serves every module's tools, so the module that was
only that is gone. Merged once node-tools is assigned where mesh-console was, on every machine.
2026-10-02 21:58:21 +02:00
jschoubben 304044da40 A ban list never holds a neighbour (hq ADR 0186)
The home server banned the house's own router within an hour of the first public jail: the router
reflects local traffic, so every client in the building arrives as the gateway's address. Every
private range joins the mesh's own in the never-ban list.
2026-10-02 18:42:16 +02:00
jschoubben 23112b111c The proxy's jail reads both refusals, each pattern naming the host once
fail2ban expands <HOST> to a named group, so two in one pattern is a duplicate group name and
the daemon refuses to start at all -- every jail on the machine, not just this one. Two patterns,
one <HOST> each: the certificate refused for an unserved name, and the request refused for one.
Caught live on the control node (hq ADR 0179).
2026-10-02 17:23:42 +02:00
jschoubben b547308e05 The proxy's jail reads a refused name as well as a refused certificate
The pattern ended at the line's end, which only the certificate refusal does; a request for
an unserved name carries trailing text and never matched. Caught against the live lines
before the jail counted anything (hq ADR 0179).
2026-10-02 17:20:54 +02:00
jschoubben 1601d5a335 fail2ban holds the intrusion seat's verbs and composes the jails; mail, forge and proxy declare theirs (hq ADR 0179, to-be 31)
The module gains a runtime carrying only the fail2ban client with the daemon's socket shared in,
serving status/banned/ban/unban and its own fail2ban_settings. It declares jailing, so the
controller's composition lands in jail.d/mesh.conf and filter.d; mailu, route-proxy and gitea log to
the journal and declare a jail reading it by container name. The base is strict: three in a day for
a day, twice banned in two weeks for four; the mesh's range stays never banned.
2026-10-02 17:02:49 +02:00
jschoubben 3dfbad6f03 nftables declares the ufw front end absent once its filter is loaded (hq ADR 0175) 2026-10-02 16:27:34 +02:00
jschoubben 6dfd2401c9 lab: the image carries what the builds and the lab call: python3, file, iproute2, sudo 2026-10-02 15:23:52 +02:00
jschoubben 8cd4f199f1 lab: a run resolves the @novox scope from the forge's package registry 2026-10-02 15:13:22 +02:00
jschoubben bce7b3a551 lab: compile from the module's root, so the runtime finds its tools
Both sources sit in tools/, so tsc took tools/ as the root and wrote
dist/index.js, while the runtime loads dist/tools/index.js: the module
started and served nothing.
2026-10-02 15:08:01 +02:00
mesh-admin 17d3d3e63a Merge pull request 'lab: declares the virtualisation capability (hq ADR 0172)' (#218) from jschoubben/the-lab-is-a-module-2 into main 2026-10-02 12:53:43 +00:00
jschoubben b77582f6a6 Cite hq ADR 0170, not 0169: the firewall seat's record was renumbered after a collision on hq main 2026-10-02 14:52:26 +02:00
jschoubben b3865d240f lab: declares the virtualisation capability, which grants its daemon's socket 2026-10-02 14:48:10 +02:00
jschoubben 67d1a400e8 lab: the lab as a module, running beds when the mesh asks
Five tools on the machine the lab runs on: check, run beds against
branches on the forge, a run's status, its log, and stop. A run checks
out every repository the lab builds, side by side, and runs the suite;
one at a time, answered at once with an id (novox/hq ADR 0172).
2026-10-02 14:15:42 +02:00
jschoubben 663e8143d4 nftables holds the node-packet-filter seat: rules, reload and remove, from a runtime with NET_ADMIN (hq ADR 0169)
The seat's three verbs over the machine's own tools: the filter as enforced
(nftables and the legacy filter), the mesh's own table reloaded from its file,
and one rule set the mesh did not write removed by the name the host reports
it under (ADR 0168) — a predecessor's chain loses its jumps and goes, the
runtime's user chain is emptied back to its return, a table of the machine's
own goes whole; the mesh's tables, the runtime's chains, a built-in chain and
an active found firewall's chains are refused. Tested over the shapes two
machines of the first mesh reported live. The module's own tool stays.
2026-10-02 13:28:33 +02:00
jschoubben d1f8ab86d1 unifi: list networks and set the DNS their DHCP hands out
Which DNS server the home network's DHCP hands out could be changed only
in the controller's own interface or by hand against its API (novox/hq
issue 198).
2026-10-02 11:53:16 +02:00
jschoubben b72213261a dnsmasq: listen addresses are a setting, and docker's file takes none
The addresses dnsmasq listens on beside the machine's are a setting, so a
machine that answers its own LAN can say so (novox/hq issue 198). Docker's
daemon.json no longer merges the module's settings: it needs none, and a
setting reaching it is a key dockerd refuses. The host still merges it
into the existing file.
2026-10-02 11:49:57 +02:00
jschoubben 67f5236b01 Revert dnsmasq's listen addresses as a setting
A module's settings merge into every mergeable file it owns, so the
setting reached docker's daemon.json beside dnsmasq's config, where
dockerd would refuse it (novox/hq issue 198). Back to the fixed
loopback line until settings can be kept out of files they are not for.
2026-10-02 11:48:37 +02:00
jschoubben 56a22847f5 dnsmasq: the addresses it listens on beside the machine's are a setting
Loopback by default, as before. A machine that answers its own LAN adds
its LAN address, and its DNS endpoints' reach opens the filter (novox/hq
issue 198). The mesh-wide default must be set before this lands.
2026-10-02 11:42:41 +02:00
jschoubben 4f952ce771 route-proxy: a bus account, to read its membership
The proxy reads its routes and the mesh's addresses from its membership
on the bus rather than from a file alone (novox/hq ADR 0167, issue 191).
2026-10-02 01:46:18 +02:00
mesh-admin fec6d76fb3 Merge pull request 'mssql: the query runs as a read-only login, one line, no variables; sqlcmd is installed (hq #193)' (#210) from fix/193-mssql-reads-as-a-reader into main 2026-10-01 22:30:02 +00:00
jschoubben 400b2f9696 mssql: the query runs as a read-only login, one line, no variables; sqlcmd is installed (hq #193)
Proven on a throwaway server: as the administrator a caller's $(SQLCMDPASSWORD) returned
the sa password, and a line beginning ':!!' ran a program in the tools container. The
statement now runs as mesh_mssql_reader (CONNECT ANY DATABASE, SELECT ALL USER SECURABLES),
with substitution off (-x), after the module's own text on the first line, and a line break
is refused. go-sqlcmd v1.10.0 is installed at a pinned digest: the image never had sqlcmd,
so every mssql tool failed with spawn sqlcmd ENOENT.
2026-10-02 00:25:22 +02:00
jschoubben 160b5ad65a postgres: the store's query runs as a read-only login, never as the admin (hq #193)
The verb wrapped the caller's text in BEGIN READ ONLY ... ROLLBACK as the superuser, so
'COMMIT; ...' left the transaction and, proven on a throwaway server, COPY TO PROGRAM ran a
shell command on the database host. The statement now runs as mesh_store_reader:
pg_read_all_data, no other grant, read-only transactions by role and session, its password
an own-secret the mesh mints. Without that password the call is refused. -q drops the
command tags that came back as rows keyed by BEGIN.
2026-10-02 00:09:09 +02:00
jschoubben 0651b63926 route-proxy README: the node that runs a proxy carries public-acme (hq #258) 2026-10-01 17:31:58 +02:00
jschoubben 01d68bda88 And the uplink holders' capabilities return
The same split lost them the other way round: the merge base held both changes, each branch had reset
the other's files, and the three-way merge kept neither. Both halves of hq ADR 0161 are on main again
with this.
2026-10-01 17:19:41 +02:00
jschoubben 89e0dde9e0 The vault's claim and its own event names return
The uplink branch was split from the vault's with the vault's files reset to a main that did not yet
hold #205; merging it afterwards took the older vault definition along (no claim, the refused event
names), and the vault could not be built. Restored to #205's state.
2026-10-01 17:19:05 +02:00
mesh-admin 5ebc89d89d Merge pull request 'Each uplink holder declares the manager it speaks for (hq ADR 0161)' (#206) from feat/each-uplink-holder-declares-the-manager-it-speaks-for into main 2026-10-01 15:11:31 +00:00
jschoubben 2e96d2f67d This branch carries the uplink capabilities alone (hq ADR 0161 rule 3); merges once every machine running a holder has reported uplink-<manager> 2026-10-01 16:52:47 +02:00
jschoubben 932efb5186 This branch carries the vault's claim alone; the uplink capabilities wait for every machine to report its profile 2026-10-01 16:52:46 +02:00
jschoubben 6ba61a8b4b The provisioner announces the vault's events by their new names 2026-10-01 16:51:13 +02:00
jschoubben 8368697744 The vault's events are its own: provisioned, rotated, deprovisioned
A module publishes under its own name only; secret.provisioned read as another module's event and the
builder refused the vault's definition today, so the seat claim could not be built. The three events lose
the prefix; nothing outside the vault listens for the old names.
2026-10-01 16:50:56 +02:00
jschoubben 966fed1829 The vault claims mesh-vault, and each uplink holder declares the manager it speaks for (hq ADR 0161)
The vault's claim makes a second provider of secret a second claimant, refused by name. The three
uplink definitions declare uplink-networkmanager, uplink-systemd-networkd and uplink-dhcpcd, which the
host reports for the manager it finds active, so the holder for a manager the machine does not run
is refused the way any missing capability is. Merges after the controller holds the seat and the host
reports the capability.
2026-10-01 15:58:05 +02:00
jschoubben c00dd04494 postgres implements the store seat's verbs under the seat's name, and its claim says so (hq ADR 0160)
The store's databases and query are registered under mesh-store, so the runtime serves them on the
seat's subjects wherever postgres holds the seat and never lists them as postgres's own; the claim
names them, so the mesh can judge the holder without postgres listing the seat's verbs among its
tools. Scoped to what the store enables: creating a database stays postgres's tool.
2026-10-01 15:19:35 +02:00
jschoubben abf5859415 Merge remote-tracking branch 'origin/main' into feat/postgres-serves-the-stores-verbs 2026-10-01 15:19:08 +02:00
jschoubben 738415710c The media chain's stale copies leave the catalogue
bazarr, bookshelf, lidarr, nzbget, ombi, plex, qbittorrent, radarr,
sonarr and tautulli live in novox/mesh-media-catalog (#195 moved jackett
and left these behind). A build of this repository at a commit today
registered plex and nzbget from these copies, which carry no provides,
and ace's plan stopped resolving. Nothing here depends on the directories:
home-assistant consumes their provisions by name.
2026-10-01 14:53:35 +02:00