Resolves the runner Dockerfile to the lint-standard Acquire::Retries=3
and keeps the collabora TODO wording; hardens the merged tree for the
stricter lints (apt retries in the tor Dockerfile, shell-or-true
markers, comment cleanup).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The role self-gates on tls.enabled and books itself as executed either
way; the TLS guard at the include suppressed that bookkeeping, so the
required_by verifier failed onion deployments with 'did not execute'.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The server.yml split hoisted csp to the top level; the filter kept
reading server.csp.whitelist and silently returned no mirrorable asset
hosts, leaving the mirror vhost whitelist and body rewrite empty.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Workers have no tor egress resolver (onion probes NXDOMAIN, exit 12)
and serve no clearnet vhosts (ingress answers 404); the check belongs
to OpenResty, which is placed on the manager.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Nodes outside the mesh (nfs, backup) can neither resolve the onion
mail host (no tor resolver) nor reach a locally published SMTP port;
hlth-msmtp died with status=68/NOHOST. The manager's ingress publishes
the mail ports on every node IP.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A cold onion connect alone measured 15.7s and tor holds SOCKS streams
up to 120s while fetching the descriptor; the 30s caps cut cold
circuits mid-handshake across all three forwarders and DB clients.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Pin node hostnames to their lab-net IPs (dual-homed nodes let Docker
DNS hand workers the cache-net IP while the registry binds only the
lab IP), quiesce the CI compose tor before bootstrap (it publishes the
same onion identity as the cluster), and reap wedged nodes with a
retried SIGKILL so leftovers cannot starve later phases.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
test.sh spawns its SOCKS forwarders with the bare python3 from PATH -
the baked /opt/venvs/infinito venv on swarm nodes - which neither
sys-pip-install (targets ansible_python_interpreter) nor the system pip
reaches. python3 -m pip hits exactly the interpreter test.sh sees;
--break-system-packages keeps the compose CI system python accepting it.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Cold onion probes need curl to outlive tor's internal 120s descriptor
wait; the recreate path must force-update the tor_tor service in swarm
instead of spawning a competing compose publisher; and a degraded Tor
circuit fails single suites with pure timeouts, so failed roles get one
retry on rotated circuits.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The compose-only guard left swarm sso-proxies resolving auth.<onion>
through the embedded DNS, which races container startup and
intermittently hands back a loopback that dead-ends in the task netns
(token exchange refused). The static extra_hosts entry wins in both
modes.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Pin tor to the manager (host-published SOCKS/DNS live on the stack
host), forward HiddenServicePorts and the dnsmasq onion fast path to
the node address instead of loopback (dead-ends in task netns),
tolerate an empty clearnet-upstream capture, detect foreign dnsmasq
drop-ins before claiming the listener, and transfer onion_self_reach
to remote targets via script.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
In swarm mode NEXTCLOUD_CONTAINER_ADDRESS is a subshell expression that resolves the live container id, but the additional-config loader task called container exec via command/argv, which passes the expression literally and fails with 'No such container'. Run it through the shell like every occ task does, and convert the encrypted-appconfig task that carried the same latent argv pattern.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The infinito config loader was appended to the occ-managed config.php with a shell printf while the image entrypoint was still in its post-install write phase; the entrypoint read config.php before the append and wrote it back after, clobbering the loader, so /var/www/infinito extras (oidc_login_provider_url among them) never reached Nextcloud and /apps/oidc_login/oidc answered 500 in the compose e2e job. Install the loader as config/zzz-infinito.config.php after occ reports installed instead: Nextcloud merges config/*.config.php on every read, occ never rewrites those files, and the entrypoint rsync excludes /config/, so no clobber path remains. A file mount was rejected because a non-empty config/ suppresses the entrypoint's base-config copy on first install. The loader now declares $CONFIG directly (additional config files load after unset($CONFIG)), cron gets the /var/www/infinito bind it silently lacked, and the dead include-path vars and host render go away.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
On the DinD swarm nodes glibc resolves localhost to ::1 first and the IPv6 connect to the ingress-published port hangs until timeout, so the mattermost HTTP gate burned 30 attempts while 127.0.0.1 through the ingress DNAT answers fine (measured on an identically shaped node). Point nextcloud_mm_local_url at DOCKER_REACH_HOST, the repo's established reach pattern (gitea, shopware, nextcloud's own config wait), which the DinD nodes override to the bridge gateway; compose keeps 127.0.0.1.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The hlth-webserver health service HEAD-requests the deploy's *.infinito.localhost vhosts, but the guide boot container has no resolver mapping for them (no CoreDNS sidecar, no dev-flow dns setup), so the service failed every start and burned 25x60s retries. Install libnss-myhostname on the apt branch and ensure the myhostname NSS module is listed in nsswitch.conf on every distro; it resolves any *.localhost name to loopback where the openresty vhosts answer.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The ACL include was gated on declared intent (storage.backend != 'nfs'), but the host-mode CI deploy NFSv4-mounts /var/lib/infinito via the nfs-server controller mount while storage.backend stays at its 'local' default, so setfacl ran against nfs4 and failed with EOPNOTSUPP (the Linux NFSv4 client exposes no POSIX ACL xattrs). Probe the fstype of the backups root with findmnt and skip ACLs on any nfs*; the existing chown backup:backup + 0700 already enforce the same posture there.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Every readiness signal before the OAuth provisioning (mmctl local mode, the container healthcheck) probes Mattermost's local-mode unix socket, never the HTTP listener, and the provisioning curl was the deploy's first request against :8065, one-shot and unretried. On swarm the ingress-published port can transiently swallow that single request after a concurrent stack deploy reprograms the mesh (observed as curl 28, 30s, 0 bytes). Wait for /api/v4/system/ping on the delegate node before provisioning and retry the provisioning itself.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ganesha FSAL_VFS cannot initialise export roots on overlayfs (name_to_handle_at returns EOPNOTSUPP in init_export_root) and cannot claim tmpfs either (resolve_posix_filesystem ENOENT), so on CI/DinD nodes whose /srv/nfs sits on overlay every client mount fails with 'No such file or directory'. On overlay hosts inside the dev/act/github runtime, mount a 2G ext4 loopback image on the export base before rendering exports; refuse to mount over a non-empty base. Real hosts (RUNTIME=host) are never touched.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The provisioned become password doubles as the Keycloak administrator realm password (user-administrator/meta/users.yml) and the guide flow generated it 64-char alphanumeric, so kcadm set-password failed the realm passwordPolicy specialChars(1) on every fresh single-app deploy. Delegate generate_random_password to ValueGenerator.generate_strong_password, the same class-guaranteeing generator every other realm user already gets via the strong_password filter; the realm policy stays strict. Also drop the commented-out Trusted Hosts block from the realm template.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The catthehacker Act runner image lacks the project's pyyaml plus gettext-base/jq that GitHub's ubuntu-latest ships, so guide_select and the compose deploy step fail under act. Install them in an ACT-gated step (no-op on real CI).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Invert the .claude ignore from a per-file denylist to an allowlist so new tool state never leaks into the repo; only settings.json and .gitignore stay tracked.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Guide-provisioned single SSO apps left OIDC.CLIENT.SECRET undefined, so the Keycloak realm client render raised "object of type 'dict' has no attribute 'SECRET'" under ansible-core 2.21. Add an oidc_client_secret credential to the Keycloak schema (auto-generated like administrator_password) and default defaults_oidc.CLIENT.SECRET to it via lookup('config', 'web-app-keycloak', 'credentials.oidc_client_secret', ''); an inventory OIDC.CLIENT.SECRET still overrides through the recursive combine at tasks/stages/01_constructor.yml.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Standardize the private inventory dir on the fully spelled "production" (matching inventories/development). Update the role README template (SPOT) and regenerate 153 role READMEs plus the admin deploy guide; add inventories/production/ to .gitignore, propagated to .dockerignore via make fix-dockerignore.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Read INFINITO_LIFECYCLES from default.env via a new tested_lifecycles() helper; derive TESTED_LIFECYCLES, the README overview envelope, the guide-select role filter, and the invokable-lifecycle lint from it. Add "maintained" to the envelope and drop the redundant hardcoded "alpha beta rc stable" strings from workflows, CLI help, and docs.
Guide selection now skips pre-alpha/eol roles (153 -> 117 testable).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The cached YAML loader crashed on the !vault tag with a ConstructorError,
so any lint test that walks the tree and parses every .yml (yaml-syntax,
role/group-vars usage, ...) failed as soon as a vault-encrypted inventory
was present. Register a !vault constructor on SafeLoader that loads the
ciphertext as a plain scalar, mirroring the existing !override/!reset
handling.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Removing the baked .env forces cli.meta.env to regenerate inside the
container; with GITHUB_ACTIONS=true its on-GHA guard aborts (exit 2) unless
GITHUB_REPOSITORY_OWNER / GITHUB_REPOSITORY is set, so .env is never written
and compose-up dies with KeyError: INFINITO_CONTAINER. Forward the owner and
repository (already resolved on the runner) into each per-distro container.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Move the operator cheatsheet's copy-paste prompts into per-workflow i8- skills
that each route to their authoritative procedure doc, and reduce the cheatsheet
to a table index plus a usage paragraph, enforced by a new lint. Add skills for
develop, test, quality, refactor, the debug and requirement flows, and
commit / pull-request / push-trigger-pull; rename the iteration skills to
i8-iterate-* and rename the index to i8-help. Wire the active-listening,
dialectic, and robot skills into the flows for clarification, uncertain root
causes, and autonomous execution. Drop the Infinito-specific agent aliases
(alias.md, INFINITO_ALIAS_MD, the make alias agent section, and the two alias
tests); terminal aliases stay.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The prebuilt CI image ships a .env generated at build time with the debian
default, and load.sh only regenerates when it is missing, so every distro's
DinD ran with debian's INFINITO_CONTAINER and config regardless of the image.
Remove the baked .env before make compose-up so it is regenerated from the
container's INFINITO_DISTRO, restoring the per-distro behaviour the old
matrix flow had on a fresh checkout.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The host-mode guide booted the pkgmgr distro image with --entrypoint
/sbin/init, but that image ships no init at all (it is a package-manager CLI
tool), so runc rejected the container with exit 127. Install systemd in a
throwaway container, commit an image that carries /sbin/init, and boot that
instead. Also read the post-loop readiness state into a variable so a
degraded systemd is not masked by pipefail.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The readiness check piped systemctl is-system-running into grep. Because
is-system-running exits non-zero on a degraded system (only running returns
0) and the script runs under set -o pipefail, the pipeline propagated that
non-zero even when grep matched, so the loop never broke and centos, which
settles at degraded with one failed unit, always exited 1 regardless of the
timeout. Read the state into a variable and match it, so a degraded system
is accepted.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The db:migrate and settings tasks, and the nextcloud to openproject OAuth
provisioning, ran container exec into the openproject web container from the
manager. In swarm the web replica is scheduled on a worker, so the
manager-resolved container id is not local and the exec fails. Resolve the
web service's hosting node and node-local container id via resolve_host_cid
and delegate every exec to that node, matching the pattern the role's own
03_admin.yml and the mattermost integration already use. The nextcloud
running probe is dropped in favour of the convergence wait.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The guide job had no failure diagnostics, so an unhealthy container such as
decidim failed the deploy with no logs to inspect. Run the recursive
container.py rescue collector on failure and upload the snapshot, mirroring
the deploy-swarm and deploy-compose jobs. In compose mode the collector
descends into the persisted INFINITO_CONTAINER and captures the nested app
containers' logs, inspect and health status.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The per-distro DNS Docker-in-Docker container runs on an overlay rootfs, so
the inner daemon's default overlayfs snapshotter fails to mount overlay on
overlay ("invalid argument") and docker compose up never brings coredns and
infinito up. Install fuse-overlayfs and write /etc/docker/daemon.json with
storage-driver fuse-overlayfs plus containerd-snapshotter disabled before
starting docker, mirroring the swarm node daemon.json that already runs this
way in CI.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The OAuth-app provisioning runs host-side on the mattermost node (delegated),
reaching the REST API via the published localhost port, not inside the
container. Fix the now-inaccurate header comment.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The Mattermost <-> Nextcloud OAuth provisioning ran its container probe and
mmctl steps on the manager, but in swarm the mattermost task runs on a
worker, so the probe found no container and the whole coupling was skipped.
The connect button then stayed disabled and the integration e2e timed out.
Resolve the mattermost hosting node and node-local container id via
resolve_host_cid and delegate every mmctl step, and the OAuth-app
provisioning, to that node, mirroring the pattern web-app-mattermost's own
role uses. The OAuth-app step keeps running bash on the host because the
image has no shell, but now on the mattermost node so its localhost URL
resolves. Drop the now-unused running probe and the
NEXTCLOUD_MATTERMOST_CONTAINER_ADDRESS var.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
parallel.sh already passes INFINITO_PULL_POLICY=always into the DinD
container, so the shell-side "${INFINITO_PULL_POLICY:-always}" default is
redundant and a second source of truth that contradicts default.env's
INFINITO_PULL_POLICY=never. Read the value from the environment; default.env
stays the single default.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The nfs-false-data-loss lint requires every nfs:false to argue why no data
is lost on a node-local volume. The pgdata dummy holds nothing and is
re-provisioned on redeploy.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Quote ETHERPAD_DB_PASSWORD with dotenv_quote so the generated env file is
mode-safe (the passwords lint requires it; compose strips the outer quotes,
swarm passes the value verbatim). Switch the etherpad entrypoint wrapper
mount from a single-file bind to a docker config, which swarm can place on
any node; the meta-volume lint rejects single-file binds as swarm-fragile.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
After a rolling swarm redeploy xwiki reboots for roughly 356 seconds while
Solr and the extensions initialise. The superadmin auth probe only retried
for about 75 seconds, expiring mid-boot, so the downstream fail block
tripped and the job failed. Retry for up to 600 seconds and gate the loop on
a 200, so the probe waits out the reboot; a genuinely dead superadmin still
fails loudly through the existing fail block.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The pgdata dummy mount used a host-path bind whose source directory is only
created on the manager. In swarm a worker scheduling the proxy replica finds
no such directory and rejects the task, so the chaos routine sees no worker
task and openproject fails. Use a named docker volume instead; it is
provisioned on whichever node runs the service. nfs: false keeps the
throwaway path off NFS backing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
storage.nfs.server defaults to an empty string, so on a single-node or
self-mount deploy nfs_client_src builds the source as ":/srv/nfs/..." and
mount.nfs4 fails with "Failed to resolve server :". Default the server to
this node's own address when unset; the exporter is also the mount target
in that topology. The guard fires only on an empty server, so an explicitly
configured server is never overridden.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The host-deploy base image ships systemd-logind masked, so loginctl
enable-linger cannot activate org.freedesktop.login1 over D-Bus and the
guide fails after the image pull with "Could not activate remote peer".
Unmask and start logind before enabling linger; it is a no-op where logind
is already active.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Etherpad's ueberdb backend was pinned to the bbb redis service. During a
deploy that redis churns (RestartCount=2), taking etherpad's DB with it, so
etherpad crashes (RestartCount=1) and the health-container job fails. bbb's
own postgres stays stable (RestartCount=0) through the same deploy.
A boot wrapper rewrites settings.json dbType redis to postgres before the
upstream entrypoint runs; env carries the connection values; the etherpad
database is provisioned in the postgres password-sync task; compose adds a
postgres dependency. The ep_redis_publisher pub/sub client stays on redis
(its failures are non-fatal).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Move the inline fork-prerequisites result assertion out of the
fork-prereqs-ready job into scripts/github/gate/fork_prereqs.sh (result
passed via the RESULT env var; the job now checks out the repo).
The staged diff also drops the code-quality-gate and test-install-gate
aggregator jobs and wires their members (lint, test, security-codeql,
test-install-make, test-install-pkgmgr) directly into the done job's needs,
which still hard-fails on any upstream failure or cancellation.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>