Commit Graph
100 Commits
Author SHA1 Message Date
kevinveenbirkenbachandClaude Opus 5.5 81e2be2204 fix(dev-python): hand requires-python to the remote script with a valid backreference
76adaeb9e8 wrote the regex_search group reference as '\1' inside the folded
task scalar. Jinja unescapes string literals, so the filter received \x01,
rejected it as an unknown argument and failed the dev-python task on every
remote host. The literal is now '\\1', and the unit test renders the task's
environment expression the way Ansible does instead of only checking that
the variable is named.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 15:55:22 +01:00
kevinveenbirkenbachandClaude Opus 5.5 76adaeb9e8 fix(dev-python): take the supported Python range from requires-python
bf9c5be173 capped the bootstrap at 3.14 with constants inside install.sh, a
second copy of the range pyproject.toml already declares. requires-python is
now the single source (>=3.11,<3.15): install.sh derives its floor and cap
from it, in the repository or, on a remote host where the script runs alone,
from INFINITO_PYTHON_REQUIRES that the dev-python role reads from the same
file; any other form fails loudly. The Dockerfile copies pyproject.toml
before the bootstrap, and pip now refuses 3.15 for the package as well.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 15:42:34 +01:00
kevinveenbirkenbachandClaude Opus 5.5 bf9c5be173 fix(dev-python): cap the bootstrap Python at the newest minor ansible-core supports
Fedora 45 defaults to Python 3.15. ansible-core 2.21, which the unpinned
`ansible` extra still resolves to (ansible 14.5.0 pins ~=2.21.5), calls
dataclasses._is_type, which 3.15 removed, so the Workspace (fedora:latest)
job died before the first playbook (run 41491308729).

install.sh now picks and installs the highest Python between
PYTHON_MIN_MINOR and PYTHON_MAX_MINOR (3.11 to 3.14) on apt, rpm and
pick_python_bin, and fails loudly when only newer interpreters exist. On
Fedora 45 it installs python3.14. Raise PYTHON_MAX_MINOR once ansible ships
ansible-core 2.22.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 15:06:41 +01:00
kevinveenbirkenbach d807d1b6b4 Merge pull request #1343 from infinito-nexus/update/docker-image-versions-web-app-matrix-20261102-0d5ec80
update(images): update Docker image versions (web-app-matrix)
2026-11-02 18:27:01 +01:00
kevinveenbirkenbach 9d92726412 Merge pull request #1344 from infinito-nexus/update/docker-image-versions-web-app-seaweedfs-20261102-1736cb5
update(images): update Docker image versions (web-app-seaweedfs)
2026-11-02 18:26:43 +01:00
kevinveenbirkenbach ca21cdd2fd Merge pull request #1339 from infinito-nexus/update/docker-image-versions-web-app-socialhome-20261102-feb553b
update(images): update Docker image versions (web-app-socialhome)
2026-11-02 17:51:46 +01:00
kevinveenbirkenbach f96b5aabba Merge pull request #1342 from infinito-nexus/update/version-sources-web-app-zammad-20261102-2de8475
update(versions): update pins with a declared source (web-app-zammad)
2026-11-02 15:02:47 +01:00
kevinveenbirkenbach 9e49c1d67e Merge pull request #1341 from infinito-nexus/update/version-sources-web-app-stalwart-20261102-6da48bb
update(versions): update pins with a declared source (web-app-stalwart)
2026-11-02 15:02:20 +01:00
kevinveenbirkenbach f6a378b35b Merge branch 'main' into update/version-sources-web-app-stalwart-20261102-6da48bb 2026-11-02 15:02:07 +01:00
kevinveenbirkenbach fd3e0b9a33 Merge pull request #1340 from infinito-nexus/update/docker-image-versions-web-app-stalwart-20261102-48eb899
update(images): update Docker image versions (web-app-stalwart)
2026-11-02 15:01:19 +01:00
kevinveenbirkenbach f707036771 Merge pull request #1338 from infinito-nexus/update/docker-image-versions-web-app-gitea-20261102-96944f8
update(images): update Docker image versions (web-app-gitea)
2026-11-02 15:00:53 +01:00
kevinveenbirkenbach cb905638a2 Merge pull request #1337 from infinito-nexus/update/repository-refs-pkgmgr-20261102-da915bf
update(repos): bump git refs to latest semver tags (pkgmgr)
2026-11-02 15:00:36 +01:00
kevinveenbirkenbach 562052a032 Merge pull request #1330 from infinito-nexus/fix/ci/runner-disk-nvme/1329
fix(ci): put containerd, Docker and swap on the runner's empty spare disk
2026-11-02 14:46:11 +01:00
kevinveenbirkenbachandClaude Opus 5.5 2d7bf740b0 fix(ci): put containerd, Docker and swap on the runner's empty spare disk
The small GitHub runner shape (AWS, 72G root) ran out of disk in swarm and compose rows (#1329: WordPress#2 of run 41261531965, Nextcloud#6 and #7 of run 41262286962) while an empty instance-store disk nvme1n1 sat unused. mount_spare_disk.sh formats and mounts it at /mnt, but only on a GitHub-hosted runner outside act, and only a whole unpartitioned disk without any signature, mount or swap use of at least 75G; every decline leaves a notice. relocate_docker_dataroot.sh then bind-mounts /var/lib/containerd from the spare disk and moves the Docker data-root to /mnt/docker-host (not /mnt/docker, which is the nested daemon's volume that dedicated.sh wipes). It refuses before stopping anything when images, volumes, build cache, foreign containerd namespaces or shims exist or daemon.json is unreadable, and rolls back the bind mount and daemon.json on any later failure or cancel. Swap goes on / once containerd lives on /mnt, dump_state.sh logs disk size, model and free space per storage root, and the swarm watchdog samples / and /var/lib/containerd every 10 s.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 12:31:02 +01:00
kevinveenbirkenbach 076eea636b Merge pull request #1328 from infinito-nexus/update/repository-refs-pkgmgr-20261101-99cab09
update(repos): bump git refs to latest semver tags (pkgmgr)
2026-11-01 02:43:47 +01:00
kevinveenbirkenbach 35fd939957 Merge pull request #1327 from infinito-nexus/fix/dotenv-smoke/real-env-untouched/1188
fix(tests): keep the dotenv smoke test out of the repository .env
2026-11-01 02:34:16 +01:00
kevinveenbirkenbachandClaude Opus 5.5 16de8ed3a0 fix(tests): keep the dotenv smoke test out of the repository .env
TestDotenvSmoke ran `python -m cli.meta.env` with cwd=PROJECT_ROOT and no
--output, so every make test rewrote the real .env with defaults: as root
inside the container, with a fresh INFINITO_CACHE_PACKAGE_ADMIN_PASSWORD
and possibly another INFINITO_DOMAIN.

The test now writes to --output <tempdir>/.env and asserts that the
repository .env keeps its inode and mtime. A marker line in a worktree .env
vanished on every run of the old test and survives the new one; without
--output the new assertion fails with "The smoke run replaced the
repository .env".

Fixes #1188

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 02:26:03 +01:00
kevinveenbirkenbach bc4f6a4106 Merge pull request #1323 from infinito-nexus/update/repository-refs-pkgmgr-20261031-82d6480
update(repos): bump git refs to latest semver tags (pkgmgr)
2026-10-31 17:28:47 +01:00
kevinveenbirkenbach 3a72ef96cc Merge pull request #1322 from infinito-nexus/fix/sys-svc-webserver-core/access-log-no-tokens/1192
fix(sys-svc-webserver-core): keep URL tokens out of the openresty access log
2026-10-31 17:23:27 +01:00
kevinveenbirkenbach 1cd9fef208 Merge remote-tracking branch 'origin/main' into fix/sys-svc-webserver-core/access-log-no-tokens/1192 2026-10-31 14:33:11 +01:00
kevinveenbirkenbach df5689d70d Merge pull request #1320 from infinito-nexus/update/docker-image-versions-web-app-socialhome-20261031-b84d86f
update(images): update Docker image versions (web-app-socialhome)
2026-10-31 14:29:26 +01:00
kevinveenbirkenbachandClaude Opus 5.5 8d6040948c fix(sys-svc-webserver-core): match token routes on the decoded path
The access-log map read $request_uri, which keeps percent-encoding and
repeated slashes. A request such as /%70assword/reset/<token>,
/password%2Freset/<token> or //password//reset/<token> missed the
token-route expression, fell through to the plain-path branch and was
logged with the token in plain text, while the backend still decoded it
to its reset handler.

The map now reads $uri: decoded, slash-merged (merge_slashes is not
overridden anywhere) and without the query string, so the existing
expressions match every spelling of a token route. Trade-off: after an
internal redirect the log shows the internal URI; with the generic
vhost's error_page (ws_generic.conf.j2) a 5xx line now carries
/500.html instead of the client's path, with the status unchanged.
Nothing in the repository parses the proxy access log.

The unit test resolves the map against the variable it reads, modelling
$uri as nginx builds it, and adds the three encoded spellings above.
Negative control: with the map back on $request_uri, exactly those three
cases fail (3 failed, 8 passed).

Reported by CodeRabbit on #1322.

Part of #1192

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 12:46:31 +01:00
kevinveenbirkenbach dbcc78e376 Merge remote-tracking branch 'origin/main' into fix/sys-svc-webserver-core/access-log-no-tokens/1192 2026-10-31 10:40:44 +01:00
kevinveenbirkenbachandClaude Opus 5.5 10d2b11cc9 fix(sys-svc-webserver-core): keep URL tokens out of the openresty access log
The front proxy logged the full request line: production had no
access_log, so nginx fell back to `combined` ($request, Referer,
$remote_user), and the debug format logged $request and the Referer.
Query-string tokens (?access_token=, ?key=, ?token=) and token routes
such as /password/reset/{token} and /i/confirm-email/{user}/{random}
ended up in plain text in the container log.

nginx.conf.j2 now declares a map from $request_uri to
$infinito_access_log_path:

- the path is written without the query string;
- the segments after a known token route (password/reset,
  password-reset, reset-password, confirm-email,
  lostpassword/reset/form, email-login, activate-account; matched
  case-insensitively at any depth) are written as [redacted], so the
  request, its status and timings stay visible without the token.

Both modes write an explicit access_log to /dev/stdout:

- production uses the new `redacted` format, laid out like `combined`
  (remote address, time, "method path protocol", status, bytes,
  user agent) with Referer and basic-auth user written as "-", so
  combined-style parsers keep working;
- debug keeps its fields (host, timings, upstream time, lengths,
  X-Forwarded-For, scheme, server name) but logs
  "method path protocol" instead of $request and drops the Referer.

The path comes from $request_uri instead of $uri so internal rewrites
(error_page, try_files) do not replace the path the client requested.
Nothing in the repository parses the proxy access log (Wazuh, the
health checks and the Prometheus log_by_lua metrics do not read it),
so no consumer needs to change. The error log still carries
`request:` and `referrer:` on error-level entries; that is outside
this change.

A unit test renders nginx.conf.j2 in both modes and asserts that every
access_log uses a format declared there, that no format references
$request, $request_uri, $args, $query_string, $is_args, $arg_*,
$http_referer or $remote_user, and, by resolving the map as nginx does,
that query strings are dropped, token routes are redacted and
lookalike paths stay unchanged.

Verified live with web-svc-html in production mode (deploy failed=0, so
openresty accepted the config). A probe request to
/password/reset/<probe>?token=<probe> was logged as
"GET /password/reset/[redacted] HTTP/1.1" 404 62 "-" "curl/8.14.1",
and /plain/page as "GET /plain/page HTTP/1.1". Negative control: the
same request against the previous config logged the probe path token
and the query token in plain text. The debug format with the fix is
covered by the unit test only.

Closes #1192

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 10:40:40 +01:00
kevinveenbirkenbach 9008b4435e Merge pull request #1321 from infinito-nexus/update/docker-image-versions-web-svc-logout-20261031-b634bf4
update(images): update Docker image versions (web-svc-logout)
2026-10-31 10:39:45 +01:00
kevinveenbirkenbach 0a4fd11e7e Merge pull request #1319 from infinito-nexus/fix/svc-prx-openresty/worker-connections/1300
fix(svc-prx-openresty): size worker_connections independently of pids_limit
2026-10-31 10:09:54 +01:00
kevinveenbirkenbach c37190495b Merge remote-tracking branch 'origin/main' into fix/svc-prx-openresty/worker-connections/1300 2026-10-31 03:00:09 +01:00
kevinveenbirkenbachandClaude Opus 5.5 f52da1c875 fix(svc-prx-openresty): size worker_connections independently of pids_limit
The shared proxy's nginx.conf derived worker_connections from the
openresty container's pids_limit. pids_limit caps processes and threads,
worker_connections caps simultaneous sockets per worker (client and
upstream), so with pids_limit 256 and one worker the whole host's proxy
held about 256 sockets, roughly 128 proxied requests in flight across
every vhost. Raising pids_limit to buy connections would loosen the
fork-bomb guard.

svc-prx-openresty now declares services.openresty.worker_connections
(4096 per worker, the common nginx production range, enough for several
chunk-heavy apps such as OpenWebUI at ~150-170 requests per page load).
WEBSERVER_PROXY_WORKER_CONNECTIONS reads it through lookup('config'),
without a default, so a missing key fails loudly. pids_limit stays 256.

WEBSERVER_PROXY_WORKER_RLIMIT_NOFILE is derived as 2x worker_connections
(8192), one descriptor for each client and upstream socket of a proxied
request, and nginx.conf renders it as worker_rlimit_nofile. The openresty
container pins its nofile ulimit (soft and hard) to the same value:
workers call setrlimit as root without CAP_SYS_RESOURCE, so they can only
lower the hard limit, and the inherited hard limit depends on the host
daemon (Docker >= 25 defaults to 1024:524288, DiD nests whatever the
outer container has). An explicit ulimit makes the rlimit succeed on
every host instead of logging a setrlimit alert.

The unit test pins worker_connections 4096, worker_rlimit_nofile 8192 and
the container ulimit, and fails when worker_connections follows
pids_limit again.

Live proof (compose deploy of web-svc-html, both plays failed=0): the
deployed nginx.conf renders worker_rlimit_nofile 8192 and
worker_connections 4096; openresty's master, worker and cache manager
run with "Max open files 8192 8192"; the openresty log carries no
setrlimit or "worker_connections are not enough" line.

Closes #1300

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 03:00:03 +01:00
kevinveenbirkenbach 73b0eceed8 Merge pull request #1315 from infinito-nexus/update/docker-image-versions-web-app-pihole-20261031-d252fb3
update(images): update Docker image versions (web-app-pihole)
2026-10-31 02:20:41 +01:00
kevinveenbirkenbach ee503bbbeb Merge pull request #1317 from infinito-nexus/fix/tests/worker-limits-carrier-isolation
test(lookup): restore sys.modules after loading the sso plugins fresh
2026-10-31 02:20:16 +01:00
kevinveenbirkenbach fe04755517 Merge pull request #1316 from infinito-nexus/update/docker-image-versions-web-svc-logout-20261031-63df524
update(images): update Docker image versions (web-svc-logout)
2026-10-31 02:20:03 +01:00
kevinveenbirkenbachandClaude Opus 5.5 9aa56b71e9 test(lookup): restore sys.modules after loading the sso plugins fresh
test_sso.py, test_sso_oidc_plugin.py and test_sso_proxy_consumers.py
evict utils, utils.cache, utils.cache.applications and the roles
packages from sys.modules to import their plugin against fresh modules,
and never put the originals back. Ansible's plugin loader keeps the
applications lookup it loaded earlier in sys.modules, so that lookup
stayed bound to the evicted utils.cache.applications, whose
_MERGED_APPLICATIONS_CACHE utils.cache._reset_cache_for_tests() no
longer reaches.

On main this turned test_webserver_worker_limits red whenever one xdist
worker ran a test that loads the applications lookup through Ansible,
then one of these sso tests, then the worker-limits test: the stale
cache served the cpus 0.5 payload of test_a_sub_cpu_proxy_still_runs_one_worker,
so the later methods rendered worker_processes 1 instead of 4 (main run
41139196554, Workspace centos and fedora, second make test on gw1).

_load_module now runs inside mock.patch.dict(sys.modules): the plugin
keeps the fresh modules it was built against, and sys.modules returns to
its prior state afterwards. This is the change store-i18n carries since
ec0fde6561, ported unchanged so both lines stay identical.

Evidence:
- Before: pytest -n 0 test_tor_ports.py test_sso_oidc_plugin.py
  test_webserver_worker_limits.py -> "3 failed, 33 passed", the CI
  failure set ('1' != '4' twice, worker_processes 1 vs 4).
- After: test_tor_ports.py, the four sso test files and
  test_webserver_worker_limits.py -> "68 passed".

(cherry picked from commit ec0fde6561)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 02:17:21 +01:00
kevinveenbirkenbach 8ad0a0e434 Merge pull request #1314 from infinito-nexus/fix/web-app-shopware/php-8.3.36
fix(web-app-shopware): move PHP base to docker-base 8.3.36-caddy
2026-10-31 00:53:21 +01:00
kevinveenbirkenbach f59484b710 Merge remote-tracking branch 'origin/main' into fix/web-app-shopware/php-8.3.36 2026-10-31 00:45:21 +01:00
kevinveenbirkenbachandClaude Opus 5.5 2348d48110 fix(web-app-shopware): move PHP base to docker-base 8.3.36-caddy
The bare ghcr.io/shopware/docker-base:8.3 tag is frozen. Its digest
sha256:14826eb9 is the same as 8.3.25, built 2025-09-05, and carries
PHP_VERSION=8.3.25. Upstream has published only flavoured tags since
8.3.26. 8.3.36-caddy (sha256:b1fdca6e, built 2026-10-30, also the
current 8.3-caddy) carries PHP_VERSION=8.3.36. It has the same
supervisord entrypoint, www-data user and exposed ports 8000/9000 as
the bare tag. That clears the 8.3.36 security floor that hlth-runtime
reports for shopware-web, -worker and -scheduler.

The flavoured tag needs two follow-ups:

- SHOPWARE_COMPOSER_PLATFORM_PHP drops the "-<flavor>" suffix before it
  pads major.minor. Without that, composer gets platform.php
  "8.3.36-caddy". It now renders 8.3.36, and 8.3 still renders 8.3.0.
- shopware/docker 6ae1717 ("feat: remove default env vars") removed
  SHOPWARE_ES_ENABLED=0, SHOPWARE_ES_INDEXING_ENABLED=0 and
  SHOPWARE_ES_INDEX_PREFIX= from the image. Shopware 6.7.13.0 reads
  them through %env()% with no container default, so env.j2 now sets
  the values the old image shipped. SHOPWARE_HTTP_DEFAULT_TTL keeps its
  7200 default from core services.xml. Nothing in Shopware reads
  DATABASE_PORT, and the role uses DATABASE_URL.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 00:45:19 +01:00
kevinveenbirkenbach ba4c06b943 Merge pull request #1309 from infinito-nexus/update/docker-image-versions-web-svc-logout-20261030-c8885e1
update(images): update Docker image versions (web-svc-logout)
2026-10-30 23:35:58 +01:00
kevinveenbirkenbach 2729a56125 Merge pull request #1308 from infinito-nexus/fix/web-app-fusiondirectory/lifecycle-eol
fix(web-app-fusiondirectory): set the role's lifecycle to eol
2026-10-30 23:33:44 +01:00
kevinveenbirkenbach 6f12dcc147 Merge remote-tracking branch 'origin/main' into fix/web-app-fusiondirectory/lifecycle-eol 2026-10-30 23:24:37 +01:00
kevinveenbirkenbachandClaude Opus 5.5 727cce33ec fix(web-app-fusiondirectory): set the role's lifecycle to eol
FusionDirectory's image runs PHP 7.4, which has been out of security
support since 2022, and upstream ships no PHP 8 build. The runtime
health check of #1265 therefore reports every deployment of it, and in
CI a red FusionDirectory job makes the matrix's fail-fast cancel every
other job of the run (seen on #1265's run 41101611445: one red job, 79
cancelled). There is nothing to fix on our side short of upstream
shipping a supported runtime.

- meta/services.yml: lifecycle beta -> eol. Default CI (tested envelope
  alpha..maintenance) no longer deploys the role, the updater and the
  image mirror skip it (mirrored image set 188 -> 187).
- README.md: end-of-life banner as lifecycle.md requires for eol: the
  project neither maintains nor tests the role, operators use it at
  their own risk, upstream FusionDirectory for support.
- Root README.md: make docs drops the role from the generated table of
  the tested lifecycle envelope.

Production deploys of the role keep running the runtime check, so an
operator who still deploys it sees the PHP 7.4 finding.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 23:24:31 +01:00
kevinveenbirkenbach b97f38ca49 Merge pull request #1261 from infinito-nexus/fix/web-app-keycloak/admin-network-allowlist/1121
feat(web-app-keycloak): serve /admin only to declared source networks
2026-10-30 23:15:41 +01:00
kevinveenbirkenbach d95319c116 Merge pull request #1307 from infinito-nexus/fix/web-app-stalwart/swarm-smtp-listener
fix(storage): keep the NFS client off the ports the platform publishes
2026-10-30 23:09:32 +01:00
kevinveenbirkenbach b0bacd072f Merge remote-tracking branch 'origin/main' into fix/web-app-stalwart/swarm-smtp-listener 2026-10-30 23:02:44 +01:00
kevinveenbirkenbachandClaude Opus 5.5 eb7056dafd fix(storage): keep the NFS client off the ports the platform publishes
Main CI run 40976399687, socialhome#0 (swarm, tor, centos): the CLI tests
of svc-net-tor (onion:25 "socks CONNECT failed with reply 0x01") and
web-app-stalwart (probe_mail relay: ConnectionRefusedError on port 25)
failed. dockerd on the manager had aborted the whole ingress program of
the mail stack:

  Failed to add ingress: failed to program ingress ports:
  failed to bind host port 0.0.0.0:993/tcp: address already in use

/proc/net/tcp named the squatter: 192.168.244.10:993 -> :2049, the
node's own NFS client. The mount carries no noresvport, so the kernel
drew a source port from the 665-1023 reserved window and landed on
imaps. None of 25/465/993/995/4190/8095 got a DNAT rule or a
docker-proxy listener while every other published port did, and
stalwart still reported healthy because its healthcheck runs inside the
container. 993 and 995 are the only published ports inside that window,
so this is luck of the draw on any NFS-backed swarm host that runs the
mail role.

mount_opts now emits noresvport. insecure on the kernel-flavor export is
the other half: kernel nfsd defaults to secure and answers a mount from
an unprivileged source port with EACCES. It reaches the kernel flavor
only; ganesha already accepts unprivileged clients.

What it costs: the next deploy of every existing NFS client sees a
pending opts change and remounts the shared state; a host-side job
holding that path at that instant fails loudly. A plain remount may keep
a running mount on its old reserved port until the next real mount.

Port of f46c4d0bab from feature/store-i18n, applied identically so the
later store-i18n -> main merge stays conflict-free.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 23:02:41 +01:00
kevinveenbirkenbach 8a19b024db Merge pull request #1265 from infinito-nexus/feature/sys-ctl-hlth-runtime/vulnerable-runtimes/1127
feat(sys-ctl-hlth-runtime): alert on EOL or unpatched PHP and missing PHP hardening
2026-10-30 22:45:25 +01:00
kevinveenbirkenbach 4f8661ec19 Merge pull request #1306 from infinito-nexus/update/version-sources-web-app-gitea-20261030-01d3535
update(versions): update pins with a declared source (web-app-gitea)
2026-10-30 22:30:58 +01:00
kevinveenbirkenbach f8da96b01e Merge pull request #1305 from infinito-nexus/fix/tests/codeql-unused-project-root
fix(tests): resolve CodeQL unused PROJECT_ROOT in two test packages
2026-10-30 21:15:39 +01:00
kevinveenbirkenbach 2507f7584b Merge remote-tracking branch 'origin/main' into fix/tests/codeql-unused-project-root 2026-10-30 21:10:23 +01:00
kevinveenbirkenbachandClaude Opus 5.5 cb18214760 fix(tests): resolve CodeQL unused PROJECT_ROOT in two test packages
CodeQL py/unused-global-variable flagged two test package inits
(code-scanning alerts #1068 and #1072):

- tests/unit/python/roles/web-app-fider/__init__.py exports PROJECT_ROOT
  to test_tenant_bootstrap_gate.py through `from . import PROJECT_ROOT`
  but declared no __all__. Declare it, as every other test package
  that re-exports PROJECT_ROOT does.
- tests/unit/python/roles/sys-front-tls/__init__.py defined a
  PROJECT_ROOT nothing imports: the only test lives in templates/, whose
  own __init__.py provides it. Leave the role-level package init empty,
  like the other role-level test packages.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 21:10:21 +01:00
kevinveenbirkenbach 76a60065fc Merge pull request #1295 from infinito-nexus/fix/ci/deploy-disable-union
fix(ci): merge the matrix row's disable tokens with the caller's list
2026-10-30 20:48:00 +01:00
kevinveenbirkenbach 91e444dcca Merge pull request #1304 from infinito-nexus/fix/web-app-mailu/lifecycle-eol
fix(web-app-mailu): set the role's lifecycle to eol
2026-10-30 20:17:22 +01:00
kevinveenbirkenbachandClaude Opus 5.5 193e7d4463 fix(web-app-mailu): set the role's lifecycle to eol
Mailu is superseded by web-app-stalwart and the project no longer
maintains it. Its OIDC flavor runs the archived heviat/Mailu-OIDC images
(the webmail ships Roundcube 1.6.9 and PHP 8.3.15), and its unbound
sidecar fails DNS since the root KSK rollover (#1303). Rather than fix
a role on its way out, mark it end of life:

- meta/services.yml: lifecycle deprecated -> eol. The updater and the
  image mirror now skip the role (the mirrored image set drops from
  189 to 188 entries), and default CI keeps excluding it as before.
- README.md: the banner now states that the project neither maintains
  nor tests the role, that operators use it at their own risk, and
  links upstream Mailu for support, as lifecycle.md requires for eol.
  The migration pointer to web-app-stalwart stays.
- web-app-stalwart README: "deprecated" becomes "end-of-life".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 20:10:49 +01:00
kevinveenbirkenbach bad992a537 Merge remote-tracking branch 'origin/main' into feature/sys-ctl-hlth-runtime/vulnerable-runtimes/1127 2026-10-30 18:47:55 +01:00
kevinveenbirkenbachandClaude Opus 5.5 a2e91a6158 fix(sys-ctl-hlth-runtime): run the check after every app container exists
hlth-runtime set system_service_force_flush_final: false. In debug mode
(CI) that makes sys-service start the unit as soon as the role is
included, so whether the check saw the app containers depended on where
the role landed in the play: in CI run 41023277637 SuiteCRM#0 and
Nextcloud#4 had their containers up first (suitecrm started 11:46:28,
check 11:49:02), while FusionDirectory#0 in the run before checked
before its containers existed and passed without inspecting anything.
In production (SYS_SERVICE_ALL_ENABLED false) the unit never ran during
a deploy at all, only from its timer.

Put it on the final run list like every other health check that
inspects containers (hlth-container, hlth-volumes, hlth-csp,
hlth-webserver, hlth-disc-space, hlth-btrfs): sys-service-terminator
starts it once all apps are deployed, so a runtime below its floor
fails the deploy deterministically.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 18:47:50 +01:00
kevinveenbirkenbach 85febdf3d7 Merge pull request #1297 from infinito-nexus/fix/sys-svc-webserver-core/proxy-worker-limits
fix(sys-svc-webserver-core): size proxy workers from the proxy alone
2026-10-30 17:40:49 +01:00
kevinveenbirkenbach df98ebbd36 Merge pull request #1302 from infinito-nexus/fix/web-app-openproject/swarm-seeder-wait
fix(web-app-openproject): wait for the swarm seeder and detect dead DB sockets
2026-10-30 17:39:44 +01:00
kevinveenbirkenbach 35f7505dc9 Merge pull request #1301 from infinito-nexus/fix/svc-bkp-volume-2-local/restore-into-resolved-mountpoint
fix(svc-bkp-volume-2-local): restore into the resolved volume mountpoint
2026-10-30 17:38:59 +01:00
kevinveenbirkenbach bc0af0e9dc Merge pull request #1299 from infinito-nexus/update/docker-image-versions-web-app-funkwhale-20261030-863dbb9
update(images): update Docker image versions (web-app-funkwhale)
2026-10-30 17:31:59 +01:00
kevinveenbirkenbach c5ee33def3 Merge remote-tracking branch 'origin/main' into fix/web-app-keycloak/admin-network-allowlist/1121 2026-10-30 16:52:08 +01:00
kevinveenbirkenbachandClaude Opus 5.5 2393e8ddba fix(web-app-keycloak): reject scoped IPv6 admin networks, drop module docstring
Addresses two CodeRabbit findings on PR #1261.

1. Scoped IPv6 (thread 4421596356): ipaddress.ip_network accepts a scope
   ID, so `fe80::1%eth0` resolved to `fe80::1%eth0/128` and the vhost
   would carry `allow fe80::1%eth0/128;`, which nginx refuses to load.
   kc_admin_networks now raises AnsibleFilterError for any entry whose
   network address carries a scope ID. test_admin_allowlist.py covers a
   scoped address and a scoped CIDR; both fail against the previous
   filter.
2. Module docstring (thread 4421596351): removed; it described the
   module instead of documenting parameters or a trip-wire.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 16:52:06 +01:00
kevinveenbirkenbach e995d9c008 Merge remote-tracking branch 'origin/main' into fix/web-app-openproject/swarm-seeder-wait 2026-10-30 16:51:58 +01:00
kevinveenbirkenbachandClaude Opus 5.5 2d80ad5f1b fix(web-app-openproject): wait for the swarm seeder and detect dead DB sockets
Compose gates web, worker and cron on the seeder via depends_on
service_completed_successfully. Swarm ignores depends_on, so nothing
waited for the one-shot seeder there. In CI (store-i18n run 40979252521,
OpenProject#0 swarm) an overlay flap reset the seeder's Postgres
connection; Postgres rolled back RootSeeder's transaction, but the
seeder never noticed and sat idle on the dead socket forever
("Executing database seed..."). No UserCustomFieldSection rows were
committed, so /my/account rendered without fieldsets and the deploy
still reported green.

- 01_manager_ops.yml: in swarm, after the handler flush deploys the
  stack, poll job_state.sh on the seeder service until its newest task
  is Complete (fail on Shutdown, give up after 20 minutes), mirroring
  web-app-zammad's init-job wait. Compose keeps relying on depends_on.
- env.j2: add libpq TCP keepalives (idle 60s, interval 10s, count 6)
  and tcp_user_timeout=120000 to DATABASE_URL, so a connection whose
  peer vanished errors out within about two minutes instead of hanging.
  The seeder then exits non-zero and swarm's on-failure restart policy
  reruns the seed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 16:51:56 +01:00
kevinveenbirkenbach ed1fbbed76 Merge remote-tracking branch 'origin/main' into fix/svc-bkp-volume-2-local/restore-into-resolved-mountpoint 2026-10-30 16:51:47 +01:00
kevinveenbirkenbachandClaude Opus 5.5 b894db0a75 fix(svc-bkp-volume-2-local): restore into the resolved volume mountpoint
rsync 3.5.0 (Debian security update 3.5.0+ds1-0+deb13u1) refuses a
destination symlink owned by neither root nor the calling user.
svc-opt-ssd-hdd turns a volume's `_data` into a symlink to /mnt/hdd or
/mnt/ssd. The restore drill empties the volume and runs `compose create`;
docker repopulates it and lchowns the `_data` symlink to the image
directory's owner (e.g. www-data). The restore then failed with
"refusing to follow a symlink owned by an untrusted user" and
"change_dir#1 .../_data/ failed: Too many levels of symbolic links",
the drill left its subjects down, and the dependent checks timed out
(CI run 40979252521, ssd-hdd#0, on feature/store-i18n; recover.py is
identical on main).

VolumeRecovery now resolves the mountpoint before handing it to rsync:
os.path.realpath locally, `ssh <host> realpath -e -- <mountpoint>` for
--docker-host, so rsync writes into the real directory and never meets
the symlink. `--insecure-links` was not used because older rsync builds
on other distributions lack it. Two new unit tests (local symlinked
mountpoint, remote resolution); both fail against the previous code.

Root cause and fix by the i18n session (feature/store-i18n triage of
ssd-hdd#0), ported to main.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 16:51:44 +01:00
kevinveenbirkenbach 143873a54e Merge pull request #1296 from infinito-nexus/update/docker-image-versions-web-app-funkwhale-20261030-f4a2d75
update(images): update Docker image versions (web-app-funkwhale)
2026-10-30 15:20:00 +01:00
kevinveenbirkenbach 8c2ee35911 Merge remote-tracking branch 'origin/main' into fix/sys-svc-webserver-core/proxy-worker-limits 2026-10-30 14:53:59 +01:00
kevinveenbirkenbachandClaude Opus 5.5 69a0dfa9b9 fix(sys-svc-webserver-core): size proxy workers from the proxy alone
The shared OpenResty nginx.conf took worker_processes and worker_connections
from WEBSERVER_WORKER_*, which are the min() of svc-prx-openresty's limits and
the current play's application_id. The file renders once, in the scope of
whichever role loads the proxy first, so that role's limits capped every vhost:
on feature/store-i18n web-svc-mirror (pids_limit 64) loaded it first, every
vhost got worker_connections 64, and openresty raised 555 "64
worker_connections are not enough while connecting to upstream" alerts while a
single openwebui page fires ~150-170 chunk requests (CI run 40979252521,
homeassistant#1). Main carries the same wiring.

Add WEBSERVER_PROXY_WORKER_PROCESSES and WEBSERVER_PROXY_WORKER_CONNECTIONS,
computed from svc-prx-openresty's own openresty service only (same
resource_filter fallbacks, at least one worker), and render nginx.conf from
them. WEBSERVER_WORKER_* keep pairing the proxy with the play for app-internal
nginx such as Nextcloud's. The values can only stay equal or rise.

Found by the i18n session's triage of homeassistant#1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 14:53:54 +01:00
kevinveenbirkenbach 22ff50c9b1 Merge pull request #1294 from infinito-nexus/fix/web-app-akaunting/stale-php81-todo/1119
docs(web-app-akaunting): drop the stale PHP 8.1 to-do
2026-10-30 14:20:30 +01:00
kevinveenbirkenbach bfeeb948ac Merge pull request #1289 from infinito-nexus/update/docker-image-versions-web-app-socialhome-20261030-76d61fd
update(images): update Docker image versions (web-app-socialhome)
2026-10-30 12:00:43 +01:00
kevinveenbirkenbachandClaude Opus 5.5 204aa37278 fix(ci): merge the matrix row's disable tokens with the caller's list
Both deploy steps set `disable: ${{ matrix.disable || inputs.disable }}`.
A row carries tokens of its own whenever its network mode needs them (a
clearnet row disables Tor), so the caller's list never reached the
deploy. On feature/store-i18n (CI run 40979252521) the workspace swarm
zombie for svc-db-mariadb/postgres deployed 19 apps instead of 7 on its
clearnet row (fedora: 4h06 for one step, job cancelled at 345 min). main
carries the same expression; it bites as soon as a caller's disable list
meets a clearnet row.

The steps now pass format('{0} {1}', matrix.disable, inputs.disable);
every reader of `disable` splits on spaces and commas. Both input
descriptions follow. test_deploy_disable_merge.py reads the live
expressions; against the old fallback it fails two subtests.

Ported from feature/store-i18n 57e574ac2d (store-i18n's extra `network:`
step env stays there).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 11:13:55 +01:00
kevinveenbirkenbachandClaude Opus 5.5 d3f3800ed8 docs(web-app-akaunting): drop the stale PHP 8.1 to-do
The role builds akaunting/docker at master. Upstream moved that
Dockerfile to `FROM php:8.2.35-apache` (akaunting/docker#123, merged
2026-10-26), a release with the fixes for CVE-2026-109618 and
CVE-2026-109619, so the to-do that asked to move off PHP 8.1.34 no
longer describes the image the role builds.

Closes #1119

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 10:27:34 +01:00
kevinveenbirkenbach 13a7753e09 Merge pull request #1293 from infinito-nexus/fix/web-app-matrix/element-force-verification-variant-1
fix(web-app-matrix): turn off Element's forced verification in variant 1
2026-10-30 09:19:32 +01:00
kevinveenbirkenbach 9da596aab8 Merge remote-tracking branch 'origin/main' into fix/web-app-matrix/element-force-verification-variant-1 2026-10-30 09:17:08 +01:00
kevinveenbirkenbachandClaude Opus 5.5 df00ff0abf fix(web-app-matrix): turn off Element's forced verification in variant 1
4a56b4c19c set services.element.force_verification to false only for
the SSO variants 0 and 2, on the reasoning that only the SSO specs log
the same account in twice. The deploy's second pass logs the variant 1
administrator (ansible flavor, SSO off, password login) in again. PASS 1
bootstraps the account's cross-signing identity, so in PASS 2 Element
stops on "Confirm your digital identity" ("Use another device" / "Can't
confirm?" / "Remove this device", no skip), and the password-login spec
times out at _shared.js:436 (fork run 40979252521, job 123823820263,
Discourse#1 with web-app-matrix variant 1, all three retries).

Variant 1 now sets force_verification to false like the other two. The
services.yml default stays true, so production keeps forced
verification. No variant exercises true end to end any more; the
follow-up issue restores one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 09:17:06 +01:00
kevinveenbirkenbach a81967fb66 Merge pull request #1290 from infinito-nexus/fix/web-app-bigbluebutton/apps-akka-transaction-isolation
fix(web-app-bigbluebutton): pin apps-akka's transaction isolation
2026-10-30 09:16:42 +01:00
kevinveenbirkenbach 1f87c98eca Merge pull request #1291 from infinito-nexus/fix/web-app-fusiondirectory/php-memory-unlimited
fix(web-app-fusiondirectory): lift the PHP memory_limit in the FPM pool
2026-10-30 09:14:00 +01:00
kevinveenbirkenbach facb27e86e Merge remote-tracking branch 'origin/main' into fix/web-app-fusiondirectory/php-memory-unlimited 2026-10-30 08:57:49 +01:00
kevinveenbirkenbachandClaude Opus 5.5 46fe011796 fix(web-app-fusiondirectory): lift the PHP memory_limit in the FPM pool
#1259 moved the interim CVE-2026-109618 mitigation (memory_limit = -1)
out of the shared hardening.ini into memory-unlimited.ini and mounted it
only in the roles whose images it had inventoried. Its branch predates
#1263, which gave FusionDirectory the php_hardening mount, so after both
merged FusionDirectory ran with the image default of 128M again.

A conf.d mount would not have fixed it: the nfrastack image writes
php_admin_value[memory_limit] into its FPM pool
(/etc/php7/pools.d/www/www-settings.conf) from PHP_MEMORY_LIMIT, and a
pool php_admin_value outranks every conf.d ini. Measured on main: CLI
128M, FPM 128M. So even before #1259 the hardening ini only ever lifted
the CLI limit.

nfrastack/fusiondirectory:1.5 runs PHP 7.4.33 on Alpine 3.15, end of
life and never patched, so the role carries the mitigation until it
moves to a PHP 8 image.

- Set PHP_MEMORY_LIMIT=-1 in templates/env.j2. The image seds it into
  php.ini and the pool, so CLI and FPM both report -1.
- The root TODO lists FusionDirectory with the image and the reason it
  uses the variable instead of the mount; the role TODO names which
  setting carries which CVE mitigation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 08:57:44 +01:00
kevinveenbirkenbach 6bca206c16 Merge remote-tracking branch 'origin/main' into fix/web-app-bigbluebutton/apps-akka-transaction-isolation 2026-10-30 08:57:36 +01:00
kevinveenbirkenbachandClaude Opus 5.5 c1ff8e5d11 fix(web-app-bigbluebutton): pin apps-akka's transaction isolation
At BBB boot, bbb-graphql-server's start.sh runs pg_terminate_backend and
drops bbb_graphql. When that kills apps-akka's first HikariCP connection
while the pool detects the driver's default transaction isolation,
HikariCP 4.0.3 swallows the 08xxx error, marks detection as done and
keeps isolation -1 for the pool's lifetime. Every later connection setup
then calls setTransactionIsolation(-1), which pgjdbc rejects with
"Transaction isolation level -1 not supported", so meetings never reach
the database and the client shows "Oops" (about 1 in 8 boots).

Pin the isolation explicitly so HikariCP never falls back to the
detected value: slick-hikaricp reads postgres.transactionIsolation,
prepends TRANSACTION_ and hands it to HikariCP, hence the value must be
exactly READ_COMMITTED (the PostgreSQL default, so normal boots behave
as before). The flag goes into apps-akka's own compose environment as
JAVA_OPTS, which the sbt-native-packager start script honours, instead
of env.j2, because compose_mods pins env.j2 onto every bbb service and
would hand the flag to bbb-web and fsesl-akka as well.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 08:57:32 +01:00
kevinveenbirkenbach 85f47fdbf8 Merge remote-tracking branch 'origin/main' into feature/sys-ctl-hlth-runtime/vulnerable-runtimes/1127 2026-10-30 08:57:26 +01:00
kevinveenbirkenbachandClaude Opus 5.5 284b0873dd fix(sys-ctl-hlth-runtime): harden LAM CLI, catch apk options, drop vars comment
Addresses three CodeRabbit findings on PR #1265.

1. LAM hardening invisible to the probe (thread 4413764929): the runtime
   check probes PHP with `php -r`, the CLI SAPI. web-app-lam mounted the
   hardening ini only into /etc/php/8.4/apache2/conf.d, which the Debian
   CLI does not scan, so the probe read unhardened CLI settings and the
   check failed for LAM. The php_hardening entry now also mounts the ini
   into /etc/php/8.4/cli/conf.d. No other role mounts it into a Debian
   per-SAPI directory; the rest use the shared scan dir of the official
   image or of Alpine. The TODO entry now names the remaining gap: the
   probe reads only the CLI copy.

2. Explanatory comment above HEALTH_RUNTIME_FLOORS (thread 4413764949):
   removed. The README already states the end-of-life rule and now also
   defines a floor as the lowest patched release of each line.

3. `apk --no-cache add` missed (thread 4415018662): PACKAGE_INSTALL_PATTERN
   required `add` right after `apk`, so `RUN apk --no-cache add php7`
   passed as an image without PHP. The apk branch now accepts options
   before `add`, like the apt branch. New tests cover the option form and
   guard that `apk` in a path or a comment is no package install.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 08:57:21 +01:00
kevinveenbirkenbach bbe75cf4ed Merge remote-tracking branch 'origin/main' into fix/web-app-keycloak/admin-network-allowlist/1121 2026-10-30 08:57:15 +01:00
kevinveenbirkenbachandClaude Opus 5.5 b358a8bb00 fix(web-app-keycloak): append the admin allowlist only when networks are declared
tasks/00_core.yml built webserver_extra_configuration by concatenating the
helper iframes, the admin allowlist and the logout panel template lookups
with `~`. With the shipped empty `services.keycloak.admin.allowed_networks`
the allowlist template rendered no text, Ansible's template lookup returned
None for it, and `~` wrote the word "None" into the auth vhost. OpenResty
then refused `unknown directive "Nonelocation"` in front of the logout
panel location and every deploy with Keycloak went red.

The role now resolves the declared networks once in KEYCLOAK_ADMIN_NETWORKS
and the task appends the allowlist template only when that list is not
empty, the same guard the logout panel uses. The template renders the
restricted /admin location unconditionally from KEYCLOAK_ADMIN_NETWORKS.

With a list that did not contain the deploy host, the deploy still went
red: Keycloak answers `/` with a redirect to `/admin/`, so the CSP health
check and the front proxy probe, which request `/` from the host, got 403
and hlth-csp failed. While the allowlist is active, `location = /` now
redirects to the account console of the platform realm instead, and the
README no longer claims the deploy needs no entry for any reason.

test_admin_allowlist.py rendered the template with plain Jinja, which
yields '' for an empty template, so it could not see the None. It now
evaluates the task's own webserver_extra_configuration expression through
Ansible's Templar and real template lookup, which reproduces
"HELPER_IFRAMES\nNoneLOGOUT_PANEL" on the old task, and covers the root
redirect.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 08:57:11 +01:00
kevinveenbirkenbach 2b88c3f433 Merge pull request #1287 from infinito-nexus/dependabot/pip/ruff-0.17.3
build(deps-dev): Bump ruff from 0.17.2 to 0.17.3
2026-10-30 08:33:51 +01:00
kevinveenbirkenbach 314a343a8a Merge pull request #1286 from infinito-nexus/update/docker-image-versions-web-app-shopware-20261030-64bd454
update(images): update Docker image versions (web-app-shopware)
2026-10-30 08:20:08 +01:00
kevinveenbirkenbach 37cade8df6 Merge pull request #1255 from infinito-nexus/fix/mail/dmarc-rua/1125
feat(mail): publish a DMARC aggregate report address (rua)
2026-10-30 08:19:06 +01:00
kevinveenbirkenbach 102fb47dbf Merge pull request #1277 from infinito-nexus/update/docker-image-versions-web-app-pretix-20261029-df38fa2
update(images): update Docker image versions (web-app-pretix)
2026-10-30 08:15:28 +01:00
kevinveenbirkenbach 8ef7f32914 Merge pull request #1284 from infinito-nexus/update/version-sources-web-app-stalwart-20261029-dd5e824
update(versions): update pins with a declared source (web-app-stalwart)
2026-10-30 00:55:52 +01:00
kevinveenbirkenbach 5995766f58 Merge pull request #1283 from infinito-nexus/fix/web-app-fider/tenant-without-sso
fix(web-app-fider): bootstrap the tenant without SSO
2026-10-30 00:01:10 +01:00
kevinveenbirkenbach 022abe155e Merge pull request #1259 from infinito-nexus/fix/sys-svc-compose-php/remove-interim-php-mitigations/1126
fix(sys-svc-compose-php): scope memory_limit -1 to unpatched images
2026-10-29 23:59:23 +01:00
kevinveenbirkenbach ab2dd4979e Merge branch 'main' into update/docker-image-versions-web-app-pretix-20261029-df38fa2 2026-10-29 23:55:16 +01:00
kevinveenbirkenbach 87404d62be Merge remote-tracking branch 'origin/main' into feature/sys-ctl-hlth-runtime/vulnerable-runtimes/1127 2026-10-29 23:55:08 +01:00
kevinveenbirkenbach 7404aa4e6a Merge remote-tracking branch 'origin/main' into fix/web-app-fider/tenant-without-sso 2026-10-29 23:55:04 +01:00
kevinveenbirkenbachandClaude Opus 5.5 b5d4391272 style(lint): apply ruff format to the worktree swarm lab subnet lint
tests/lint/filesystem/env/test_worktree_swarm_lab_subnet.py landed on
main with lines ruff format wraps, so every `make autoformat` rewrites it
and leaves an unstaged change behind. Formatter output only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 23:55:02 +01:00
kevinveenbirkenbachandClaude Opus 5.5 73b21eef98 fix(web-app-fider): bootstrap the tenant without SSO
00_core.yml included 01_setup_oidc.yml only when services.sso.enabled
was true, but that file carried the whole tenant bootstrap: wait for
HTTP, count tenants, POST /_api/tenants, create the admin user and
activate the tenant. Only its last task configured the Keycloak
provider. With SSO off no tenant was ever created.

Fider's RequireTenant middleware then answers every tenant route,
/api/v1/tags included, with 307 to /signup, and /signup is open to any
visitor, who can claim the fresh installation and become its
administrator. The MCP read fider_list_tags got that 307 in the swarm
SSO-off matrix row.

The tenant bootstrap moves unchanged into tasks/01_tenant.yml, which
00_core.yml includes on the stack host regardless of SSO. The OIDC file
becomes tasks/02_setup_oidc.yml and keeps only the provider upsert,
still gated on services.sso.enabled. The README states which bootstrap
steps run without SSO.

A unit test evaluates the include and task guards for every SSO,
stack-host and deploy-mode state: a fresh stack host posts exactly one
tenant with and without SSO, an existing tenant is never posted again,
the Keycloak provider is written only with SSO, and a non-stack host
runs none of it. The compose matrix row with SSO off stayed green
because nothing there read a tenant route.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 23:55:01 +01:00
kevinveenbirkenbach 48b571a094 Merge remote-tracking branch 'origin/main' into review/dmarc-rua-1125 2026-10-29 22:24:56 +01:00
kevinveenbirkenbach 065b33c646 Merge pull request #1282 from infinito-nexus/fix/ci/validation-main-merged-base-sha
fix(ci): check the PR head against the main it was tested with
2026-10-29 22:15:04 +01:00
kevinveenbirkenbachandClaude Opus 5.5 7a3f27fc63 style(lint): apply ruff format to the worktree swarm lab subnet lint
tests/lint/filesystem/env/test_worktree_swarm_lab_subnet.py landed on
main with lines ruff format wraps, so every `make autoformat` rewrites it
and leaves an unstaged change behind. Formatter output only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 22:10:46 +01:00
kevinveenbirkenbachandClaude Opus 5.5 6d44e1e28a fix(ci): check the PR head against the main it was tested with
The Validation step "Assert the base branch is fully merged into the PR
head" ran main_merged.sh, which fetches the live tip of main when the job
starts. PR runs wait in the queue for hours while image-update PRs land
on main several times an hour, so nearly every queued run failed
Validation on start and had to be re-pushed after another main merge
(PR #1265: behind_by=0 at push, behind_by=9 while its run still queued).

main_merged.sh takes a new INFINITO_MAIN_COMMIT override: when set, it
fetches that full commit SHA from the remote and requires HEAD to
contain it instead of the live tip; a set but malformed or empty value
exits 2. The Validation step passes github.event.pull_request.base.sha,
the main tip at event time, which the step's pull_request-only guard
always provides. The run tests the merge against that base, so the gate
still guarantees the tested tree contains it.

The local pre-push hook (make test-main-merged) leaves the override
unset and keeps checking the live tip: a push happens right now, so
lagging the current main is the real defect there.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 22:10:45 +01:00
kevinveenbirkenbach be28751879 Merge pull request #1281 from infinito-nexus/update/repository-refs-web-app-bookwyrm-20261029-7fb75db
update(repos): bump git refs to latest semver tags (web-app-bookwyrm)
2026-10-29 21:58:35 +01:00