The CLI reads the declarations and memberships from the host_vars it writes
to. --contract-file and --application-id go with them: there is no contract
left to write, and the application block is fixed by the mesh reader.
Each host's state moves from a map keyed by mesh name to a list under
svc-net-wireguard.networks.interfaces. The inventory validator does not walk
lists, so a deployment's own mesh names need no generated contract, and
every reader goes through utils.networks.mesh.
The declarations are the hub's svc-net-wireguard.networks.meshes, and a host
joins every mesh its roles list in their own networks.meshes, so two hosts
of one role may sit in different meshes. The hub is the svc-swarm-manager
host. A deployment narrows the pool in its own group_vars, otherwise the
platform's NETWORK_MESH_POOL applies. A mesh a role names but nobody
declared is refused rather than dropped.
members_of, primary_meshes and plan_mesh take the Topology instead of raw
inventory groups. The spokes come out deduplicated, sorted and without the
hub, as before.
MeshSpec loses hub_group, spoke_groups and spoke_group_prefixes: membership
is no longer part of a declaration but resolved from the inventory into a
Topology of one hub and the spokes of every mesh.
Answers what every mesh consumer asks: which meshes the hub's
svc-net-wireguard.networks.meshes declares, which meshes a host joins
through the networks.meshes of the roles it carries, and what a host holds
on them in svc-net-wireguard.networks.interfaces. Every role ships these
lists empty, so the values exist only in a deployment's inventory.
down_act_outer.sh removed every container named act-*, so starting or
releasing a swarm drill in one worktree killed the act container of a
drill another worktree had running.
workflow.sh now labels act's job containers with
infinito.worktree=${INFINITO_CONTAINER} through the default
ACT_CONTAINER_OPTIONS, and down_act_outer.sh removes only containers
carrying the own label. Both scripts refuse to run without
INFINITO_CONTAINER, so the filter never widens to every act container;
make exports BASH_ENV, which loads it from .env.
Worktrees without a custom.env share the default container name and
therefore still share one label among themselves.
Closes#933
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
On a Tor node current_play_redirect_domains schedules the bare node
onion as a redirect source targeting DOMAIN_HOMEPAGE. Without
web-app-dashboard, DOMAIN_HOMEPAGE falls back to SOFTWARE_DOMAIN
(infinito.nexus), so web-opt-rdr-domains rendered a 301 from
http://<node>.onion/ to http://infinito.nexus/: an onion visitor sent
to a clearnet host over plain HTTP.
The onion apex entry is now added only when DOMAIN_HOMEPAGE is an
onion host. Otherwise the onion apex gets no redirect and the default
server answers.
Closes#924
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The updater moved the build image from python 3.14-slim to 3.15-slim
(39d57d953b). Upstream's requirements.pinned.txt pins cbor2 6.1.4,
which ships wheels up to cp314 only, so pip builds it from source,
and its Rust extension needs a compiler the image does not have. The
build failed with "can't find Rust compiler", was retried until the
job's budget ran out, and killed Bridgy Fed#0 on #915 after 20460 s.
Installing Debian's cargo does not help: cbor2 6.1.4 uses let chains,
stable only from Rust 1.88, and trixie ships an older rustc.
The image stays on 3.14-slim, with the docker-version marker so the
updater leaves it there. Bridgy Fed#0 passed on 3.14-slim in run
38318560960, and the build stage builds on it with the cp314 wheel.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The role writes onlyoffice's settings with one occ call per key,
DocumentServerUrl first and jwt_secret fourth. Nextcloud loads app
config once per process, so a cron.php that starts inside that batch
runs onlyoffice's EditorsCheck job, registered when the app was
enabled, with the document server URL but without the secret. The
document server answers "Invalid token", EditorsCheck stores it in
settings_error, and onlyoffice stops loading its Files integration;
EditorsCheck itself skips every later run while that error is set.
Seen in run 38455782184 (Nextcloud#4, swarm): cron.php started at
06:50:00 inside the batch that ran from 06:49:59 to 06:50:04, logged
"Invalid token" at 06:50:22, and both Playwright passes found .docx
files offered for download only.
DocumentServerUrl is now written last, so a process that cached the
config before the batch ended sees no URL and EditorsCheck returns
early. After the batch, `occ onlyoffice:documentserver --check` sets
settings_error from a fresh check: a redeploy clears an error latched
earlier, and a document server that stays unreachable fails the
deploy. A unit test keeps the URL last.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Since the role moved to n8n 2 (2.42.3 on 2026-10-05, now 2.44.0),
POST /rest/workflows/:id/run accepts a manual run only with a trigger to
start from or a destination node; any other key, `workflowData` among them,
is dropped. The LiteLLM gateway test still sent only `workflowData`, so every
attempt failed with HTTP 400 "To run the workflow manually, specify either a
trigger to start from or a destination node." (PR #912, run 38455633497,
job 115523214958). On main the test sat behind the MCP provisioning failure of
n8n#0 that e4078c05fd fixed, so the next main run would have hit it.
The test now runs the stored probe workflow from its manual trigger.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Stalwart bans an address for good once it passes a failure rate: the four
*BanPeriod fields of its Security settings are null, and block_ip stores a
ban without a period with expires_at.unwrap_or(u64::MAX). Many clients reach
Stalwart through one address (the reverse proxy, published ports, Tor, the
swarm ingress), so one burst of failed logins locked that path out until
someone deleted the BlockedIp entry by hand.
The role now sets authBanPeriod, abuseBanPeriod, loiterBanPeriod and
scanBanPeriod to services.stalwart.ban_period_seconds, one hour by default,
and reloads the settings, as Stalwart's own security test does after it
changes a ban period. It reads the settings first and leaves them alone when
they already match, so a redeploy changes nothing.
Closes#916
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The transport tests pin INFINITO_SWARM_MESHES to the default, so an operator
.env pointing at an example does not change what they assert. A controller
link left by other declarations is removed before the controller joins, and
one outside the pool is left alone.
The multi-mesh spoke criterion is checked: the three-mesh example deployed
svc-dns-unbound end to end on the local lab with the NFS server up on beta and
gamma. The other two stay open until the CI dispatch has run.
The overview and features named the swarm and data meshes as if the role
shipped them, and the administration command lacked the required
--group-vars-file.
With mode swarm and vpn enforced it deploys an example's declarations in CI.
The manual entry already exists on the default branch, so the input can be
dispatched before this lands; a new entry workflow could not.
Forwarded to the swarm drill as INFINITO_SWARM_MESHES; empty keeps the
development lab's.
The deploy job also installs iproute2, which the harness uses to find the
controller's stale mesh links. GitHub runners already ship it; the act image
a local run uses does not.
make swarm-zombie app=<role> vpn=enforced \
meshes=inventories/examples/wireguard-three-meshes/meshes.yml
deploys a role over the example's meshes on the local lab.
The prologue hands the chosen declarations to the mesh writer, and the
controller mesh and the transport switch follow the first declared mesh
instead of a hardcoded 'swarm'.
Locally the controller is the act container on the host's network, so its
interface outlives the round. A link left by other declarations still routes
the pool, and a development round after a three-mesh one died on
'RTNETLINK answers: File exists'. The controller now drops WireGuard links
that hold a pool address but name no declared mesh before it joins; nothing
outside the pool is touched.
A meshed round deploys the declarations this names, the development lab's by
default, so an example under inventories/examples can be deployed instead.
The extras looked up the 'swarm' and 'data' meshes by name and fell back to
the underlay when they were absent, so any other declarations ran swarm and
NFS outside the tunnel without failing.
The advertise address read meshes.swarm, so a deployment that named its
meshes differently got '' and swarm bound to the underlay the mesh exists to
keep it off. It now reads the manager's primary mesh.
--controller-mesh defaulted to 'swarm', a name only the development
declarations use; any other deployment left the controller off the mesh. It
now defaults to the first declared mesh, an undeclared name fails instead of
being ignored, and the primary of every host is resolved over all declared
meshes before any is written.
Every spoke routed the whole pool through every mesh it belonged to. For a
spoke of two meshes that is two routes for one prefix: wg-quick refuses the
second with 'RTNETLINK answers: File exists' and deletes the interface. The
three-mesh example hits it on the swarm lab, where the NFS server is a spoke
of beta and of gamma.
Only the primary mesh routes the pool now; the others route their own subnet,
which the interface address already covers. Each entry records whether it is
the host's primary.
A host's primary mesh is the first declared mesh it belongs to. It is the one
interface a spoke of several meshes may route the pool through, and the
address the other planes reach the host on.
A lookup is handed the play vars as declared, so WIREGUARD_SUBNET_POOLS
arrived as the literal '{{ [WIREGUARD_SUBNET_POOL] }}'. The NFS server put
that string into its export list, ganesha refused the config with
'Unexpected character ([)' and every meshed swarm pass failed at the restart.
The value is now templated, and one that is still a template afterwards fails
the lookup instead of reaching a config file.