Commit Graph
14682 Commits
Author SHA1 Message Date
evangelostsak bf9dc12ac7 refactor(wireguard): remove the generated mesh contract
It was rewritten from whatever declarations a run used, so a local run left
the working tree dirty.
2026-10-14 15:03:40 +02:00
evangelostsak 0fc5c3695d refactor(mesh): remove the contract writer
The per-host state is a list now, which the inventory validator does not
walk, so no role contract has to name the deployment's meshes.
2026-10-14 15:03:40 +02:00
evangelostsak 081260fc45 feat(mesh): drop --group-vars-file and the generated contract
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.
2026-10-14 15:03:40 +02:00
evangelostsak 91bcbe904c refactor(mesh): read the transport address through the mesh reader 2026-10-14 15:03:40 +02:00
evangelostsak 66608aa2d2 refactor(mesh): store a host's interfaces as a list
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.
2026-10-14 15:03:40 +02:00
evangelostsak 4641a7f341 feat(mesh): read the meshes and their members from host_vars
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.
2026-10-14 15:03:40 +02:00
evangelostsak be55cebab8 refactor(mesh): plan against the resolved topology
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.
2026-10-14 15:03:40 +02:00
evangelostsak d12c024e45 refactor(mesh): a spec is a mesh, a topology is who joins it
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.
2026-10-14 15:03:40 +02:00
evangelostsak 870b62b74e feat(networks): one reader for the WireGuard meshes of an inventory
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.
2026-10-14 15:03:40 +02:00
kevinveenbirkenbachandClaude Opus 5.5 46373c1ba3 fix(act): remove only the own worktree's act containers
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>
2026-10-14 13:50:07 +02:00
kevinveenbirkenbach 5ca454d75c Merge remote-tracking branch 'origin/main' into fix/web-app-bluesky-pds-lxm 2026-10-14 13:37:42 +02:00
kevinveenbirkenbach 44a4e05f7c Merge branch 'main' into fix/web-app-n8n/sso-hook-logger/852 2026-10-14 13:35:46 +02:00
kevinveenbirkenbachandClaude Opus 5.5 45732329b6 fix(lookup): keep the onion apex from redirecting to a clearnet homepage
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>
2026-10-14 13:35:29 +02:00
kevinveenbirkenbach efb816d93b Merge branch 'main' of github.com:infinito-nexus/core 2026-10-14 13:35:17 +02:00
kevinveenbirkenbachandClaude Opus 5.5 4e5c01676c fix(web-app-bridgy-fed): build on python 3.14 until cbor2 ships cp315
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>
2026-10-14 13:35:15 +02:00
kevinveenbirkenbachandClaude Opus 5.5 bfaa834f29 fix(web-app-nextcloud): write onlyoffice's server URL last and recheck it
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>
2026-10-14 13:34:52 +02:00
kevinveenbirkenbach a3c8f31d07 Merge remote-tracking branch 'origin/main' into HEAD
# Conflicts:
#	roles/web-app-stalwart/README.md
2026-10-14 13:34:01 +02:00
kevinveenbirkenbach ce1200d80c Merge pull request #922 from infinito-nexus/fix/web-app-stalwart/ban-period/916
fix(web-app-stalwart): let address bans expire
2026-10-14 13:28:36 +02:00
kevinveenbirkenbach 88d0cd8308 Merge pull request #923 from infinito-nexus/fix/web-app-n8n/manual-run-trigger
fix(web-app-n8n): start the gateway probe from its trigger under n8n 2
2026-10-14 12:04:27 +02:00
kevinveenbirkenbachandClaude Opus 5.5 8c0a670627 fix(web-app-n8n): start the gateway probe from its trigger under n8n 2
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>
2026-10-14 12:01:49 +02:00
kevinveenbirkenbachandClaude Opus 5.5 f92b7000af fix(web-app-stalwart): let address bans expire
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>
2026-10-14 12:01:36 +02:00
evangelostsak 216aff6245 test(ci): a retrigger carries the meshes input 2026-10-14 11:23:23 +02:00
evangelostsak 2f96b52580 test(swarm-test): extras bind swarm and NFS to the primary mesh 2026-10-14 11:23:23 +02:00
evangelostsak af74e70ac0 test(swarm-test): the round deploys the declarations it is pointed at
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.
2026-10-14 11:23:23 +02:00
evangelostsak 4856bcd131 test(swarm): the advertise address follows the primary mesh 2026-10-14 11:23:23 +02:00
evangelostsak e9dbab5835 test(mesh): the contract shape carries the primary flag 2026-10-14 11:23:23 +02:00
evangelostsak 05dc4af041 test(mesh): the controller joins the first declared mesh by default 2026-10-14 11:23:23 +02:00
evangelostsak a521937794 test(mesh): no two interfaces of a host route one prefix
Covers the swarm lab under the three-mesh example, where the NFS server is a
spoke of two meshes, and generated topologies of one to eight meshes.
2026-10-14 11:23:23 +02:00
evangelostsak 06cc872c1c test(mesh): pin how the primary mesh is chosen 2026-10-14 11:23:23 +02:00
evangelostsak 762513ec32 test(lookup): hold mesh_ranges to a rendered pool
The declared template must come back rendered, and one that stays a template
must fail rather than reach the export list.
2026-10-14 11:23:23 +02:00
evangelostsak eb90249d93 docs(requirements): add the multi-mesh and example criteria to 037
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.
2026-10-14 11:23:23 +02:00
evangelostsak 1f48a6e8cd docs(wireguard): describe declared meshes instead of two fixed ones
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.
2026-10-14 11:23:23 +02:00
evangelostsak a992b5d1a2 docs(examples): explain the primary mesh and how to test the example
Covers declaration order, the local and CI commands, and that the lab is
stricter than this inventory: its NFS server is a spoke of beta and gamma.
2026-10-14 11:23:22 +02:00
evangelostsak 8cb3939939 docs(ci): document the vpn axis and the meshes input
Adds the vpn row to the rotation table, as rotated since swarm rows spread
over both arms, and how an example is deployed in CI.
2026-10-14 11:23:22 +02:00
evangelostsak dab66459d0 ci(manual): add the meshes input
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.
2026-10-14 11:23:22 +02:00
evangelostsak a518cf9576 ci(orchestrator): forward the meshes input to every deploy chunk 2026-10-14 11:23:22 +02:00
evangelostsak 13f8a98faa ci(deploy): accept the mesh declarations as an input
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.
2026-10-14 11:23:22 +02:00
evangelostsak e122ef501d feat(make): swarm-zombie takes meshes=
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.
2026-10-14 11:23:22 +02:00
evangelostsak 00156e6955 feat(swarm-test): deploy the declarations INFINITO_SWARM_MESHES names
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.
2026-10-14 11:23:22 +02:00
evangelostsak 999f4933f3 chore(env): pass INFINITO_SWARM_MESHES through to .env 2026-10-14 11:23:22 +02:00
evangelostsak 309d0b03c2 feat(swarm-test): INFINITO_SWARM_MESHES selects the declarations
A meshed round deploys the declarations this names, the development lab's by
default, so an example under inventories/examples can be deployed instead.
2026-10-14 11:23:22 +02:00
evangelostsak 5ef948bb3f fix(swarm-test): read manager and NFS addresses off the primary mesh
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.
2026-10-14 11:23:22 +02:00
evangelostsak 99a50c033a fix(swarm): advertise on the manager's primary mesh
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.
2026-10-14 11:23:22 +02:00
evangelostsak ca69d865b2 fix(mesh): default the controller mesh to the first declared
--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.
2026-10-14 11:23:22 +02:00
evangelostsak da35a34bba chore(wireguard): regenerate the contract with the primary flag 2026-10-14 11:23:22 +02:00
evangelostsak e0b46e0882 feat(mesh): declare the primary flag in the role contract
The writer now stores 'primary' in every mesh entry, and the inventory
validator refuses a key the role meta does not declare.
2026-10-14 11:23:22 +02:00
evangelostsak f126dd4aec fix(mesh): route the pool through one interface per host
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.
2026-10-14 11:23:22 +02:00
evangelostsak 4321d3a342 feat(mesh): resolve each host's primary mesh
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.
2026-10-14 11:23:22 +02:00
evangelostsak 2431ee2ba0 fix(lookup): render the mesh pool before returning it
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.
2026-10-14 11:23:22 +02:00
Prageeth Panicker 06f0b42f12 Merge remote-tracking branch 'origin/main' into feature/web-app-wazuh/add-role 2026-10-14 09:00:39 +00:00