A node-local volume does not follow a swarm reschedule, so its data is lost on drain. Add a meta/volumes.yml lint that fails when a 'nfs: false' opt-out lacks both '# nocheck: nfs-false-data-loss' and a '# Reason:' arguing why that loss is acceptable. Justify the 10 existing entries (checkmk OMD site; gitlab repositories, logs, runtime state).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The web-app role-iteration prompt only covered the compose loop; update it to iterate both deploy modes (compose then swarm) per the swarm and roundtrip loop docs, including the swarm-exec/shell/down/clean live-cluster steps and the compose-AND-swarm green gate.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The swarm matrix deploy runs under `sudo -E env` with an explicit variable whitelist that dropped the disable var. Add it so `make swarm-zombie disable=...` reaches the provisioner (empty stays a no-op, so CI is unaffected).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The dev-environment routine reconciles every app's full dependency closure in both compose and swarm and needs ~290min, but the job timeout was 270, so all five distro jobs were cancelled ~4min into the last phase. Raise to 360 (GitHub's per-job maximum). Also drop three narration comments un-grandfathered by the change.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Penpot is content-addressable and re-encodes uploads server-side, so the fixed fixture image (plus bytes appended after IEND, which the re-encode strips) deduped to a single object; on a second variant deploy the bucket already held it, so 'objects after > before' failed. Add a uniqueImagePng(seed) generator whose decoded pixels vary per run, so each upload produces a genuinely new bucket object.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The stock image lays the app under /var/www/html from /usr/src at runtime, so the whole webroot landed on the shared NFS volume; on kernel-nfsd the www-data Apache workers on a non-populating swarm node get EPERM serving that root-owned code (403), and the stack never converges. Bake the code into the image at build time so /var/www/html is node-local on every replica, and share only data/, custom/ and client/custom over NFS. Move the boot-lock and ready-marker into the shared data/ so replicas still serialize the one-time install, keep the ready-marker check ahead of the lock (a serving replica must not re-enter the image entrypoint and rebuild the shared cache, which aborts 255), and chown the three shared dirs. Verified in compose (deploy + hard-restart + e2e); swarm eliminates cross-node code access by construction.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
03_keycloak_client.yml force-restarts opentalk_controller and blocks on swarm convergence, but it ran before the embedded object-store bucket was created. On a fresh deploy the missing 'opentalk' bucket crash-loops the controller (exit 255) so the update never converges. Move the bucket-provisioning block ahead of the Keycloak-client include.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
url.base ends with '/', so Laravel builds redirects as APP_URL then 'setup'; the trailing slash yields the double-slash '//setup', which 404s and the readiness probe never converges. Strip it with regex_replace. Also tag the upstream Snipe-IT .env.example template with a comments-valid nocheck so its intentional section banners are not flagged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
port-ui prefetches every card icon over HTTPS at boot before it binds, so the 30s tcp-healthcheck default reaps it mid-prefetch on loaded swarm workers and the stack never converges. Set container_healthcheck_start_period to 10m (matching the heavier siblings).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The no-auth variant does not stage the trusted-header hook file, but env.j2 emitted EXTERNAL_HOOK_FILES unconditionally, so n8n crash-looped on a missing /home/node/hooks.js ("Cannot find module"). Wrap it in the N8N_SSO_ENABLED block alongside the other SSO-only vars.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Fider's SSRF guard (validate.WebhookURL) 500s the OAuth callback when the Keycloak host resolves to an internal/overlay IP, so login never completes. Set ALLOW_PRIVATE_NETWORK_TARGETS=true so it can reach the trusted internal Keycloak.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The OMD site config store uses fcntl.flock, which returns EBADF over NFS, so in swarm the checkmk site never serves and oauth2-proxy 502s. Opt checkmk_data out of the swarm NFS rewrite so it stays node-local.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The override renders with lstrip_blocks=False (only compose.yml.j2 gets lstrip via the stack_host_template plugin), so an indented set line left dangling whitespace under trim_blocks, collapsing each service key to the same indent as its body, which fails with 'services.image/volumes must be a mapping'. Move the set statements to column 0 so they render correctly under both lstrip settings.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
n8n (192.168.106.16/28) and svc-runner (192.168.106.0/28) sat inside mailu's 192.168.106.0/24, so co-deploying either with mailu aborted with "Pool overlaps with other one on this address space". Reassign both to free /24s (218, 219). The overlap test read meta/server.yml, which has no networks key, so it passed vacuously; point it at meta/networks.yml so it actually gates duplicate/overlapping subnets.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The swarm render-distribution slurps a template rendered on the stack host and copies it to the peers, but the peer's parent directory only exists on the stack host, so the copy failed ("Destination directory does not exist") on wrk nodes. Add an idempotent peer-side dir-ensure, gated on swarm mode and non-stack-host, right before the copy.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
compose_networks/container_networks read DEPLOYMENT_MODE directly, so a node-local compose-in-swarm role (compose_mode_force=compose, e.g. the matrix ansible flavor) rendered a swarm overlay a standalone compose container cannot attach to ("network matrix not found"). Honor compose_mode_force like the other mode-aware lookups.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
n8n was merged (from main) serving a canonical domain without a tor service block, and its local subnet 192.168.106.16/28 collided with svc-net-tor. Declare the complete tor block (meta/services.yml with the playwright-service-flag nocheck, meta/variants.yml pinning services.tor.enabled true in variant 0 and false in the no-auth variant) and move svc-net-tor's nominal (host-networked) subnet to 192.168.106.32/28. Fixes the tor-contract, variant-coverage, bond-baseline, playwright-env and subnet-overlap/duplicate test failures.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
New lint test_no_raw_goto.py forbids raw `page.goto(...)` in role playwright specs + persona flows (the gotoOnion wrapper in personas/utils is exempt) so navigation retries transient Tor-transport errors over onion. Migrates every raw call across ~75 roles (343 sites) to `gotoOnion(page, ...)` and teaches gotoOnion to detect relative onion URLs (`isOnionCanonical`). gotoOnion is a behaviour-identical drop-in on clearnet (single attempt, same return). Verified: no-raw-goto lint green, eslint 0 errors, make test green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
make autoformat normalised inline-comment spacing (` #` -> ` #`) in role meta/services.yml + meta/variants.yml and re-wrapped a few Python files (ruff line-length). Pure formatting — verified via git diff -w (whitespace-only) and diff inspection (ruff line-wraps only, no logic change).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Over an onion-exclusive deployment the OIDC issuer is plaintext http (.onion has no TLS), so provider_ca_files pointed oauth2-proxy at a CA file that was never mounted into the sidecar — crashing it at startup (connection refused on the auth gate, no redirect to Keycloak). Gate provider_ca_files on an https issuer and mount the root CA into the sso-proxy container for the self-signed https case (the file was previously never mounted, a latent crash there too). Verified over onion: prometheus admin/biber/guest specs 5 passed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
New to_onion_url filter swaps a clearnet host suffix for the tor_node and forces http (no-op without tor_node or off-primary hosts). body_filter.lua uses it in onionize() so that .onion requests receive the onion CDN origin for injected head/body snippets (e.g. universal-logout.js), which the Tor browser cannot reach on clearnet. Covered by 7 unit tests.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
keycloak.js/logout.js wrap every wait in resolveTimeout so Tor's higher latency does not abort the OIDC adapter/logout waits. biber.js/admin.js drop the onion skip (personas now execute over onion instead of being skipped) and add a sharpen guard: a completed OIDC round-trip that still shows a Login control fails explicitly as a login failure, not a misleading 'no logout control'.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>