A local registry holds locally built, rebuildable images and a pull-through cache holds re-fetchable upstream content, so neither volume is backup-worthy: backup.disabled excludes the volumes from baudolo, the container_backup consumer is statically off (replacing the CI-variant override, which is deleted), and the volumes.yml opt-outs document the exemption for the backup-service-flag lint. derive_includes proves the backup legs leave both roles' include closures in every variant.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Picks up the restore fix that drops overloaded postgres functions by identity signature, unblocking the svc-bkp-remote-2-local restore drill.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Drop i8manfail (duplicate of i8cifail), rename i8manfails to i8pufail
to satisfy the consonant-cluster rule, and strip the comment from the
make-backed i8cire alias.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
infinito administration deploy ci trigger --failed now dispatches the
failed roles via the priority field and omits the whitelist, so the
priority line reruns them first and the regular line covers the rest.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
test-deploy-single-node.yml bundles the compose and host deploy tests
behind one reusable workflow; the orchestrator gains a priority input
that deploys focus roles first per mode and runs the remaining roles
only after every priority job succeeded, excluded via the new
INFINITO_BLACKLIST discovery filter in apps.sh. entry-manual exposes
the priority field and treats priority-only dispatches as roles-only.
Workflow dependency diagram added under .github/workflows/README.md.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The LDAP variant's login page renders an inline event-handler attribute that script-src-attr (unsafe-eval only) blocked, failing the hlth-csp gate; the SSO and plain variants never render it. unsafe-inline joins the directive, matching the keycloak/moodle pattern, with script-src-elem already carrying it.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The seed include re-passed the caller's loop vars as database_name/username/password: '{{ database_name }}' etc., shadowing each var with a template that resolves to itself; ansible-core 2.19 detects the recursion and dies on first access inside the seed assert. include_tasks inherits the enclosing scope, so the bridge loop's vars flow through without the re-pass; the seed's assert still sees them defined and non-empty.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Variant 1 (OIDC off) died on the LDAP uidNumber task: ansible-core 2.19 templates delegate_to before evaluating when, so the undefined MATTERMOST_LDAP_HOST_NODE exploded on a fully skipped path. The OIDC admin wiring now lives in 03_oidc_admin.yml behind a single OIDC-gated include (the file is never loaded when OIDC is off) with the authservice!=gitlab branch as one inner block, and the delegate reads the resolved node via default(omit) for the remaining skip path. Each condition is declared exactly once.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A fresh registry stores nothing until the first push, so the volume-backup drill on the registry variant has no payload to verify and fails its non-empty assertion. Variant 0 pins the container_backup consumer off; the backup drill stays covered by the app-role variants. Verified via derive_includes: the backup legs drop out of the variant's include closure.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
categories.yml now declares modes defaults per category (deepest match wins, parents apply as fallback): update, drv and svc-net disable compose and swarm (host-hardware and host-networking categories), svc-registry requires both enabled (CI bootstrap infrastructure). role_modes_defaults resolves a role's policy; the new category-modes lint requires every deploy-bearing service entry to declare exactly those modes.<mode>.enabled values explicitly, exempt via '# nocheck: category-modes' on the service key. update, the four drv roles and the three wireguard roles declare their values explicitly; the dict-valued modes meta key is excluded from the category-tree walkers.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ci trigger --run now takes a bare run id and resolves it against the current branch's tracking-remote repo; full URLs keep using the owner from the URL. The aliases no longer build run URLs from INFINITO_FORK_REPOSITORY_URL (stale values produced 404s against the wrong owner): a single i8cifail base passes the id or URL through, and i8cire, i8puci, i8manfail and i8manfails compose it (quality gate / sign-push / scope variants).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
make cosmos regenerated the backup-role READMEs against the current meta state: cardinality labels, the symbol legend and gear-marked Ansible dependencies from the newer generator, without the parked full-backup orchestrator edges.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The include-gated header is the formal task_gate directive the compose-verb and compose-chdir lints read; deleting it as narration broke both. The install task name drops below the 80-char limit.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
dedicated.sh streams rescue diagnostics via docker exec tar since docker cp cannot see the systemd container's mount namespace and silently extracted nothing. recover_drill.sh guards on docker availability: non-stack role deploys now drill the nfs and secrets legs instead of hard-failing on the impossible volume leg; stack jobs still drill all three. 04_stack_rm_wait tolerates an already-removed stack and re-issues the rm once services are gone.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
10x60s reaped services whose healthcheck start_period legitimately exceeds ten minutes (suitecrm first boot declares twenty); 25x60s covers the longest declared start_period with margin. The loop still exits early on success and fails loudly after the budget.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Every exited container triggered a whole-project compose restart, including init/migration one-shots that exit 0 by design, which restarted projects mid-first-boot. The collector now reads name and status and skips containers whose status starts with Exited (0); every other exit code and the unhealthy set still repair. Mirrors the hlth-container gate.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Same class as the mattermost bump: the 60x5s override lost against slow first boots under CI load (keycloak gated on health=healthy); 120x5s stays well under the resolve util's 600x2s default and still fails hard after the budget.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The 60x5s override lost against mattermost's plugin-prepackaging first boot and keycloak's healthy-wait under CI load; 120x5s stays well under the resolve util's own 600x2s default and the probe still fails hard after the budget. The LDAP host-node delegate reads the resolved var directly since resolve and consumer share the same when-gate.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
AKAUNTING_SETUP=true in the rendered env re-ran the installer on every container recreation between deploys (restore drill, repair restart) and crash-looped against the installed database. The flag is gone from the env entirely: compose now installs like swarm through an ephemeral one-shot (stop, compose run --rm -e AKAUNTING_SETUP=true akaunting --no-start, up --force-recreate), included mode-agnostically from tasks/setup/{{ DEPLOYMENT_MODE }}.yml with the swarm job renamed into the same folder. The first-run gate reads the marker stat directly instead of a set_fact indirection.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
workhorse now waits for sidekiq and sidekiq for webservice (service_started), closing the first-boot race where workhorse came up against a webservice that was not accepting connections yet. Swarm ignores depends_on, so the change only orders the compose boot; the graph stays acyclic (migrations -> webservice -> sidekiq -> workhorse).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
scssphp (pscss) is a require-dev tool, so dev packages must survive the root install until the theme scss compile; a --no-dev re-install afterwards prunes them, dropping the Symfony web profiler whose /_wdt XHRs violate the CSP. APP_ENV is pinned to prod because the pruned image no longer ships the dev-scoped bundles from bundles.php and would crash the kernel in dev. The legacy composer install never needed dev packages and installs --no-dev directly.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The admin exec targeted the container_address, which under swarm resolves through a manager-only subshell while the web task runs on another node. resolve_host_cid now supplies the hosting node and node-local container id; both rails runner tasks delegate there. Compose behavior is unchanged: the util passes the previous container address through and delegates to the same host.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
wait_converged checked only the first task line, so a rolling update with a still-running old task reported false convergence. The probe now lives in files/swarm/service_converged.sh (stack_ready.sh style: script module, SERVICE env, timeout 15 per engine call, container wrapper): it requires no update in flight (UpdateStatus completed/rollback_completed, or absent) and every desired-state=running task to be Running. Covered by a seven-scenario stub test during extraction; a never-updated service maps the engine's nil-template error to the no-update case.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Declare the proxy_resolvable overlay (swarm mode only) with the app domain as alias, following the committed keycloak/seaweedfs pattern: the front proxy harvests the alias and resolves the app cross-node over the overlay instead of 502ing on an unreachable node-local network. Compose deployments are unaffected since the overlay is gated to modes: [swarm].
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The ansible/MDAD flavor runs matrix as a node-local compose stack, so its local network declares force_bridge when the flavor is ansible; swarm components (network create, prometheus native metrics) then treat it as node-local bridge instead of demanding an overlay. The bridge-enable vars are renamed to the matrix_bridge_* names the pinned MDAD ref (master) expects; the old unprefixed names were silently ignored. All bridge flags and the flavor are read without defaults since the role config defines every key, so a missing key fails loudly.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A native-metrics app on a node-local force_bridge network cannot be joined or resolved by a swarm-wide prometheus: the stack deploy fails with the network scope error and the scrape job would flap as permanently down. native_metrics_apps now drops those apps at the source (DEPLOYMENT_MODE swarm + networks.local.force_bridge), covering all four call sites (network precreate, compose service networks, external-networks block, scrape include) without per-template guards. DEPLOYMENT_MODE is templated without a default so a missing value fails loudly.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Per-volume nfs uid/gid/mode (erpnext/frappe 1000, jenkins 1000, moodle/www-data 33, ejabberd 9000) so NFS-backed named volumes get the container runtime owner. nfs_prep falls back to default(omit) instead of re-forcing root:0755, which reverted the docker copy-up ownership for non-root images on every redeploy. compose_volumes test asserts the x-infinito-nfs marker.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
roles/categories.yml now declares the ordered 'stages' and a per-category 'stage' (plus run_after and bootstrap). utils/roles/stage.py resolves a role to its stage and exposes stage_groups(); the new stage_groups lookup feeds the 01_constructor / 02_server / 02_workstation / 03_destructor group loops in place of their hardcoded lists. A static invariant test proves the lookup reproduces the exact previous loops, so the rendered plays (and deploy behavior) are unchanged.
cli.meta.roles.order.run orders its main phase by stage as a run_after tiebreak. plugins/filter/invokable_paths tolerates the new sibling 'stages' key, and the invokable-roles wiring test recognizes stage_groups-driven wiring.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Roles declared under meta/main.yml dependencies now appear on both the
Dependencies and Dependents side, labelled with the glossary gear
symbol and connected by a fixed 1:1 edge; the legend explains it. All
role READMEs regenerated.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Edges now carry their relation: solid 1:1 for fixed consumption, dashed
0..1 for conditional (group-membership gated) consumption. Below every
diagram the generator renders a reading key for the edge kinds and the
node markers, with the glyphs drawn from the shared symbol glossary.
All role READMEs regenerated.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add cli/build/readme: derives each role README's managed sections from
templates/roles/README.md.j2.tmpl (the Cosmos mermaid diagram from
meta/services.yml, an invokable-gated Quick Setup, and Credits).
The Cosmos diagram shows dependencies, every declared service, and
dependents (roles that consume this one, matched by role name and by
provides provision-name in both directions). Role nodes carry deploy-mode
emoji markers (compose/swarm/host); disabled and shared services get a
stop mark; dependents are capped at 12.
test_readme enforces the Quick Setup body is byte-exact with the template
render. `make readme-generate` and `make cosmos` regenerate; `make cosmos`
also runs inside `make quality`.
Refactor: utils/symbol_glossary.py is the single source of truth for the
word/emoji mapping (unique project-wide), consumed by the complexity
matrix and the Cosmos builder. utils/roles/deploy.py holds role_has_stack
and role_deploy_modes, shared by both so the logic never drifts.
Add `make lint-mermaid` and its CI workflow, rendering every diagram via
mermaid-cli; the render step provisions chrome-headless-shell and unzip
on demand so it works in the minimal container.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A single 'Error: 503:' from registry.ollama.ai killed the whole
flowise closure deploy: the pre-pull task had no retry budget, so one
transient upstream error was instantly fatal (the blob downloaded at
full clearnet speed to 100%, then the manifest request 503ed — pure
infra flake, not Tor-related).
Give the sync path 5x15s retries mirroring the existing failed_when
semantics (rc 0 or 'up to date'); the async fire-and-forget path keeps
0 retries since until and async do not compose.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Playwright onion gate probed the app onion over Tor and silently
SKIPPED the whole suite when it did not answer — a broken onion could
go green. Remove the gate: the probe now waits hard (40x15s, covering
normal 2-5 min descriptor publication) and an unreachable onion fails
the deploy loudly. Reachability is part of what is under test.
The readiness probe treated transport-level failures (status: -1) as
'< 500', satisfying its until condition on attempt 1 and never using
its retries (nextcloud [0,1] died to TLSV1_UNRECOGNIZED_NAME this way).
Require a real HTTP status >= 100 so transport errors retry.
Tor pins one long-lived entry guard; its own log showed ours degraded
('failing more circuits than usual ... 38 collapsed, 21 timed out'),
which throttles every flow of the node at once — the most plausible
cause of the 89-minute descriptor-publication outage and the
intermittent ERR_SOCKS_CONNECTION_FAILED in E2E. New config key
services.tor.use_entry_guards (default true keeps the Tor default and
production anonymity posture); the matrix variant pins it false so test
deployments render UseEntryGuards 0 and a single bad relay cannot stall
the node. Anonymity is not under test there, only reachability.
Verified: full svc-net-tor closure deploy over onion with the gateless
semantics, failed=0, every onion suite executed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
OpenCloud's proxy mounts an unconditional https-redirect middleware: any
request carrying X-Forwarded-Proto other than https gets a 308, with no
config toggle. Onion vhosts are plaintext, so the app 308-looped on
onion nodes and the webserver health gate aborted the deploy.
Make X-Forwarded-Proto overridable in the shared proxy location
templates (default $scheme, byte-identical for every other role) and
have opencloud assert https towards its backend.
Verified on a fresh onion node: opencloud deploys, the health gate
passes, the SPA loads over the onion, and its well-known, OIDC env and
CSP all resolve to http://auth.<onion>. The SSO E2E still flakes on
transient Tor SOCKS errors mid-flow (client-side multi-hop OIDC), which
is a Tor path property, not an app defect.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The injected universal-logout control navigates to Keycloak's
end-session endpoint without id_token_hint, so modern Keycloak renders
a logout-confirmation page. Nothing clicked it: the SSO session and the
oauth2-proxy cookie survived, and the prometheus spec's strict
post-logout assertion (redirect back to Keycloak auth) timed out on
onion nodes across five CI jobs (svc-net-tor, discourse, svc-bkp,
fedora/manjaro env).
inAppLogout now confirms the Keycloak prompt (#kc-logout /
logout-confirm form) after clicking the logout control, completing the
real user journey for every persona flow. The prometheus spec drops its
bespoke fire-and-forget /logout goto in favour of the shared
inAppLogout plus a networkidle settle; the strict step-6 redirect
assertion is unchanged.
Verified live over onion: prometheus suite 5 passed / 2 gated skips.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Rails boot (including db:migrate) dies with OpenIDConnect::Discovery::
DiscoveryFailed / TLS alert 112 on onion nodes: the CSP initializer's
sso_host performs OIDC discovery, and the swd/webfinger gems force
URI::HTTPS regardless of the issuer scheme, dialing the plaintext-only
onion issuer on :443.
Mount a 00_-prefixed initializer override into web and sidekiq (HTTP-only
deployments, same guard as the force_ssl override) that pins the
SWD/WebFinger url_builder to URI::HTTP for http issuers. The 00_ name is
load-bearing: initializers load alphabetically and the pin must precede
content_security_policy.rb, which triggers the first discovery at boot.
Verified: mastodon onion closure deploy runs migrations and E2E green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
decidim 0.31.6 dropped the /health_check route (rails routes now serves
the Rails 7.1 /up endpoint), so the container healthcheck 404-curled
forever and the pre-migration healthy-gate deadlocked against the
pending-migration 500. Point the healthcheck at /up and reorder setup
to running -> migrate -> healthy.
OIDC discovery failed on onion nodes because the swd gem forces
URI::HTTPS regardless of the issuer scheme, dialing the plaintext-only
onion issuer on :443 (TLS alert 112). Pin SWD/WebFinger url_builder to
URI::HTTP when the issuer is http; https issuers are untouched.
First-time OIDC registration 500ed on needs_tos_accepted because a
fresh organization has no terms-of-service static page; ensure the
default pages idempotently before setting the organization host.
The spec now logs in through the shared performKeycloakLoginForm
helper and derives the Keycloak host from OIDC_ISSUER_URL instead of a
hardcoded clearnet domain.
Verified end to end: full onion closure deploy failed=0, decidim suite
6 passed / 3 gated skips.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
meta/services.yml modes states where a role RUNS; a new meta/tests.yml
skip list deactivates TESTING a mode without touching that capability.
The table gains test_compose/test_swarm/test_host columns (base column
minus skip), --filter/--sort and the JSON/YAML payloads know the new
fields, and CI discovery (apps.sh) now filters on test_<mode> == true.
svc-storage-nfs-client is the first consumer and skips all three modes.
Also replaces the nine VS16-based header/lifecycle emojis (like the
old host/jobs/weight symbols) with East-Asian-Wide ones: terminals
render VS16 sequences 2 cells wide while the width model said 1,
shifting every header right of them off its column. A guard test pins
all table symbols to wide, VS16-free codepoints.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Moves web-app-checkmk, web-app-jellyfin, web-app-n8n,
svc-registry-cache and svc-registry-docker from alpha to beta, pulling
them into the default CI test-deploy lifecycle envelope.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The runner command joins its siblings (dedicated, development) under
cli/administration/deploy; cli/deploy is gone. Makefile, docs and the
requirement links now point at the new path, PROJECT_ROOT climbs one
level further, and the unit tests moved along.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
One module per concern, mirroring the inventory/ subpackage layout:
run.py (single deploy pass + env forwarding SPOT), drill.py (recover
drill, host mode exempt, paths from CONTAINER_REPO_ROOT instead of a
hardcoded /opt/src/infinito), purge.py (inter-round cleanup) and
cli.py (arg parsing + round/pass loop, PASS 1/2 deduplicated).
Also extend the comment linter to flag bare string-expression
statements outside docstring position in Python: a triple-quoted
literal floating in a body is a comment in disguise and now runs
through the same validity rules; docstrings and PEP 224 attribute
docstrings stay allowed. Test narration comments moved into method
docstrings accordingly, and the design docs now link deploy/cli.py.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>