Commit Graph
14682 Commits
Author SHA1 Message Date
kevinveenbirkenbachandClaude Opus 4.7 a9ca2ae688 fix(svc-prx-openresty): swarm-aware handler — exec reload + container_address
Replace bare-name docker restart with container_address-resolved
openresty -s reload so the handler works on swarm tasks (named
<stack>_<svc>.<n>.<id>) without dropping listeners on 80/443. Apply
the same container_address fix to the MODE_DEBUG logs collection.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-14 19:08:44 +02:00
kevinveenbirkenbach b2c8050718 fix(swarm): pin default_placement roles + retry swarm registry push
- roles/sys-svc-container/templates/deploy.yml.j2: render
  `placement.constraints: node.role == manager` in the swarm
  `deploy:` block whenever the application_id is one of the roles
  whose `meta/services.yml` declares `default_placement: manager`
  (resolved via the `roles_with_default_placement` lookup). Pins
  state-sensitive services (mariadb, postgres, openldap, openresty,
  registries) to the manager so their bind-source / local volume
  expectations hold; app roles without the marker stay free to land
  on workers.

- roles/sys-svc-compose/handlers/main.yml: add `retries: 10 / delay: 6
  / until: succeeded` to the `swarm: retag built images for local
  registry and push` handler. On a cold deploy the local registry task
  may still be settling when the push fires; retrying for up to 60s
  lets the push outlast the registry's warmup instead of aborting the
  whole stack deploy on a single transient connect-refused.
2026-06-14 19:08:43 +02:00
kevinveenbirkenbach 7c6c2046eb refactor(svc-registry-docker): swarm-friendly port + local registry data
- meta/volumes.yml: drop the `nfs:` marker on the `data` volume so
  the registry stays on a local docker named volume. Image-pull IO
  is hot-path; NFS round-trips slow every worker cold-pull, and the
  registry is already manager-pinned via `default_placement: manager`
  so cluster-shared state is not required here.

- templates/compose.yml.j2: publish the registry port via swarm's
  `mode: host` (target/published/protocol/mode YAML) instead of the
  routing-mesh ingress when DEPLOYMENT_MODE=='swarm'. The
  `swarm_registry_push.py` handler talks to localhost:5000 on the
  manager before the overlay is fully established, and workers pull
  by hitting the manager host directly; routing-mesh would loop
  through the overlay it is trying to seed.

- vars/main.yml: introduce `REGISTRY_CONTAINER_PORT: 5000` and wire
  it through both port-mapping branches so the upstream registry
  port lives in one place instead of being hard-coded twice.
2026-06-14 19:08:43 +02:00
kevinveenbirkenbach e3645e2ea3 fix(swarm-nfs): pre-create openresty bind sources + SPOT NFS state path + reorder bootstrap
- roles/svc-prx-openresty/tasks/01_core.yml: extend the existing
  nginx-config pre-create into a loop that covers all seven host
  bind-mount sources openresty's compose template references (nginx
  config + base, www data, well-known, letsencrypt webroot + base,
  TLS self-signed base). docker swarm refuses to start containers
  whose bind-mount sources do not exist on the target node; compose
  silently created them, so the gap surfaced only under swarm.

- roles/sys-svc-compose/tasks/utils/nfs_prep.yml: switch the per-volume
  pre-create from `storage.nfs.export_base` to the SPOT
  `STORAGE_NFS_STATE_PATH`, matching the rest of the repo's NFS-path
  references.

- tasks/stages/01_constructor.yml: swap the bootstrap order so
  `svc-storage` runs before `svc-registry`. The registry's volume needs
  the NFS layer ready first; the prior order caused intermittent
  init races where the registry created its volume on local disk
  before NFS was mounted, hiding the actual cluster volume. Also move
  the sys-token-store include after the bootstrap loop so it can use
  the now-mounted shared state.
2026-06-14 19:08:43 +02:00
kevinveenbirkenbach 1c5f447f51 fix(svc-db-postgres): drop literal quotes from POSTGRES_INITDB_ARGS
docker stack deploy does not strip quotes from env_file values (unlike
docker compose), so the literal `"..."` characters reach the container's
environment. POSTGRES_INITDB_ARGS is interpreted by initdb as a shell-
parsed string and tolerates unquoted spaces here because the line is
read whole. Removing the wrapping quotes makes the value identical
across compose and swarm and avoids confusing initdb in the swarm path.
2026-06-14 19:08:43 +02:00
kevinveenbirkenbach 105cc4e526 fix(svc-db-openldap): swarm-aware bind-source pre-create + readiness gate
- 01_core.yml: pre-create the LDIF host directories BEFORE 02_docker
  (where the swarm stack deploy fires). docker swarm refuses to start
  containers whose bind-mount sources do not exist on the manager node;
  the deferred copy after docker init was a no-op for swarm and is
  removed (was duplicate work in compose mode too).

- 01_core.yml: gate the `wait_for` port probe to compose mode. The
  swarm overlay does not surface ldap/389 on `DOCKER_REACH_HOST`; the
  new convergence gate in 02_docker.yml covers swarm.

- 02_docker.yml: chain `sys-svc-compose/tasks/utils/post_deploy.yml`
  after the flush so swarm waits for openldap tasks to reach running
  before the bind-check tries to talk to the service.
2026-06-14 19:08:43 +02:00
kevinveenbirkenbach 13f3a66f89 feat(svc-db-mariadb): swarm-aware root password reset + go-template escape
- roles/svc-db-mariadb/tasks/03_reset_root_password.yml: split the
  stop/start steps by deployment mode. Compose continues to use
  community.docker.docker_container (state=stopped/started). Swarm
  routes through `docker service update --replicas 0` + wait-for-zero
  + recovery container on STACK_HOST + scale back to 1. All steps that
  touch the swarm manager are delegated and run_once so the play
  converges on a single manager node.

- roles/svc-db-mariadb/templates/compose.yml.j2: escape `$` in the
  healthcheck (`$${MARIADB_ROOT_PASSWORD}`) so `docker stack deploy`'s
  Go-template pass leaves the variable expansion intact for the
  container's shell to resolve at runtime.
2026-06-14 19:08:43 +02:00
kevinveenbirkenbach 39f4bc6685 feat(swarm): align mariadb/postgres tasks + post-deploy gate with container_address SPOT
Tier-1 changes verified live in act-12 (mariadb stack converged, postgres
stack started, swarm-readiness gate fired correctly for docker/mariadb/
postgres/openresty).

* `plugins/filter/compose_volumes.py` + `plugins/lookup/compose_volumes.py`:
  thread `dir_var_lib` through filter (required keyword, no default)
  and lookup (reads from `vars_["DIR_VAR_LIB"]`, raises if missing). Replace
  native NFS driver with a bind-mount from the host's already-NFS-mounted
  `DIR_VAR_LIB` because native `type: nfs` re-handshakes portmap, which is
  unreachable in nested DinD. Drop the FilterModule wrapper so the deprecated
  pipe form keeps failing fast per the lookup-only SPOT contract.

* `tests/unit/plugins/{filter,lookup}/test_compose_volumes.py`:
  thread `DIR_VAR_LIB` through every fixture (no default-fallbacks).

* `roles/sys-svc-compose/tasks/utils/post_deploy.yml`:
  compose-mode db-health probe gated to `DEPLOYMENT_MODE \!= 'swarm'`; new
  swarm-mode convergence gate runs the extracted `swarm_stack_ready.sh`
  via `ansible.builtin.script` with `STACK` env, addressed by absolute
  `playbook_dir`-rooted path so the include resolves from caller roles.

* `roles/sys-svc-compose/files/swarm_stack_ready.sh` (new): probe
  `docker stack services` for a single `STACK` and exit non-zero until
  every service's running/desired replica count matches.

* `roles/svc-db-mariadb/vars/main.yml`: new `MARIADB_CONTAINER_ADDRESS`
  constant wraps the `container_address` lookup so callers reference one
  SPOT instead of duplicating the lookup.

* `roles/svc-db-mariadb/tasks/01_core.yml` + `02_init.yml`:
  switch root-password probes and bootstrap SQL calls to
  `container exec -i {{ MARIADB_CONTAINER_ADDRESS }} sh -c 'mariadb -p"$MARIADB_ROOT_PASSWORD"'`.
  Using the container's own `MARIADB_ROOT_PASSWORD` env var mirrors the
  healthcheck and matches whatever quoting Docker swarm preserves from
  the env_file, sidestepping the compose-vs-swarm env_file quote-strip
  divergence.

* `roles/svc-db-postgres/files/set_postgres_superuser_password.sh`:
  `eval "container=$container_raw"` so the swarm-mode lookup payload
  `"$(/usr/bin/resolve-container-id <stack> <svc>)"` is re-evaluated by
  bash and resolves to the live task id at exec time.

* `roles/svc-db-postgres/tasks/01_core.yml`: rename arg to use the new
  swarm-aware `POSTGRES_CONTAINER_ADDRESS`.

* `utils/tests/swarm/extend_inventory.py`: when the seed app's primary
  service declares `default_placement: manager`, do not add the app to
  the worker group, so the swarm test inventory matches the
  single-replica placement-pinned semantics.
2026-06-14 19:08:43 +02:00
kevinveenbirkenbach 8efaf2e6f9 test: mask stdin sub-blocks + bind-mount swarm-NFS expectations
- tests/integration/iam/passwords/test_quote_in_shell.py: blank stdin
  sub-blocks before the password-quote scan. Ansible feeds stdin via a
  pipe to the spawned process (mariadb, psql, ...) without going through
  the shell, so the shell-injection vector this test guards against does
  not apply there. SQL-level escaping is the correct contract for those
  values. Also drop stale path comment and 7 restate comments.
- tests/unit/plugins/filter/test_compose_volumes.py: align
  test_swarm_nfs_opt_in_renders_driver_opts with the bind-mount swarm-NFS
  driver_opts (type=none, o=bind, device=DIR_VAR_LIB/<vol>) so the
  unit test matches the live filter output.
- .ansible-lint-ignore: list svc-db-openldap include of
  sys-svc-compose/tasks/utils/post_deploy.yml; ansible-lint cannot
  statically resolve the path_join template and prepends the caller path.
2026-06-14 19:08:43 +02:00
kevinveenbirkenbach 64a0c55b14 chore(makefile): sort act-debug between act-app and act-workflow
Test gate enforces alphabetical ordering of target definitions.
2026-06-14 19:08:42 +02:00
kevinveenbirkenbach ab1c4c2ebb fix(web-app-shopware): skip local public/bundles check when the S3 object-store asset backend is active (DiD root-caused) 2026-06-14 18:07:32 +02:00
kevinveenbirkenbach 0037d3b0b2 fix(web-app-taiga): use a valid PNG so change_avatar pil_image accepts the avatar and it reaches SeaweedFS (DiD-verified) 2026-06-14 14:31:32 +02:00
kevinveenbirkenbach 47ee0bb82e fix(web-app-pixelfed): set DANGEROUSLY_SET_FILESYSTEM_DRIVER=s3 so media uploads land on the S3 default disk (DiD-verified) 2026-06-14 14:11:56 +02:00
kevinveenbirkenbach ed86a3d633 fix(sys-svc-proxy): strip forgeable identity headers on every proxy location to close trusted-header injection (snipe-it) 2026-06-14 13:29:08 +02:00
kevinveenbirkenbach 039f06b0c8 fix(web-app-mailu): wait for antispam container readiness before DKIM keygen (fixes kix deploy race) 2026-06-14 12:50:46 +02:00
kevinveenbirkenbach 4c2fbbb1a5 fix(web-app-shopware): disable composer advisory blocking so the pinned shopware/core 6.7.8.2 build resolves (verified in composer:2.10) 2026-06-14 11:43:15 +02:00
kevinveenbirkenbach 092c40e12f fix(web-app): correct baserow null table-field and drop fider brittle toast in seaweedfs e2e specs 2026-06-14 11:18:29 +02:00
kevinveenbirkenbach 052cea02e2 fix(vault): encrypt_string reads plaintext from stdin so leading-dash values encrypt 2026-06-14 10:01:28 +02:00
kevinveenbirkenbach 257cfc0491 fix(web-app-matrix): widen Synapse readiness probe to 360 retries for cold MDAD bootstrap 2026-06-14 09:59:45 +02:00
kevinveenbirkenbach 33c8648f7c fix(web-app-matrix): add docker buildx plugin to the MDAD runner image 2026-06-14 09:59:19 +02:00
kevinveenbirkenbach f254f2385d fix(web-app-mobilizon): disable seaweedfs, no upstream S3 upload backend 2026-06-13 19:55:04 +02:00
kevinveenbirkenbach f29ed13b3d fix(ci): pin variant[2] seaweedfs in matrix/shopware/snipe-it/taiga and nocheck flaky baserow URL 2026-06-13 19:55:03 +02:00
kevinveenbirkenbach dd0544e486 fix(web-app): nocheck non-playwright storage/service flags (openproject, snipe-it) 2026-06-13 18:20:41 +02:00
kevinveenbirkenbach 2d2e000c7b fix(web-app): nocheck disabled-LDAP dynamic-flag and playwright service-gate lints 2026-06-13 18:20:41 +02:00
kevinveenbirkenbach d0e2902db0 test(web-app): add seaweedfs true/false matrix-variant coverage to 10 S3 consumers 2026-06-13 18:20:40 +02:00
kevinveenbirkenbach fd84aae00a fix(eslint): parse roles SSO sidecar JS as ES modules 2026-06-13 18:20:40 +02:00
kevinveenbirkenbach c1be4241aa fix(integration-matrix): clear generator lints and replace 2 dead edge URLs 2026-06-13 18:20:39 +02:00
kevinveenbirkenbach e685f5f8e4 Merge branch 'main' into fix/ci-deploy-server-failures 2026-06-13 17:18:17 +02:00
kevinveenbirkenbachandClaude Opus 4.8 5dd03210c5 fix(changelog): use asterisk bullets in Older Releases index
The auto-generated "Older Releases" index in CHANGELOG.md emitted dash
bullets while the rest of the file uses asterisks, failing markdownlint
MD004 (ul-style, consistent) with 49 errors. Switch the archive index
generator to asterisk bullets, convert the existing entries, and align
the unit test assertions.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 17:18:17 +02:00
kevinveenbirkenbach 969ed49728 Merge pull request #314 from infinito-nexus/update/docker-image-versions-20260613-8ea4dad
update(images): update Docker image versions
2026-06-13 17:18:17 +02:00
kevinveenbirkenbach 7af7a6f207 Merge pull request #313 from infinito-nexus/update/skills-20260613-9162bd7
update(skills): update agent skills to latest versions
2026-06-13 17:18:16 +02:00
kevinveenbirkenbach 8e665e1776 Update roles/web-app-nextcloud/meta/services.yml 2026-06-13 17:18:16 +02:00
kevinveenbirkenbach 0d8c90c9d1 Merge pull request #311 from infinito-nexus/update/repository-refs-20260611-2b7c9c8
update(repos): bump git refs to latest semver tags
2026-06-13 17:18:16 +02:00
infinito-nexus-updater[bot] 9e07b2c2b3 update(skills): update skills-lock.json to latest versions 2026-06-13 17:18:16 +02:00
infinito-nexus-updater[bot] ccb9fe9780 update(images): update Docker image versions 2026-06-13 17:18:16 +02:00
infinito-nexus-updater[bot] 099218d4db update(repos): bump git refs to latest semver tags 2026-06-13 17:18:16 +02:00
kevinveenbirkenbachandClaude Opus 4.8 fd0a8c56c6 fix(changelog): use asterisk bullets in Older Releases index
The auto-generated "Older Releases" index in CHANGELOG.md emitted dash
bullets while the rest of the file uses asterisks, failing markdownlint
MD004 (ul-style, consistent) with 49 errors. Switch the archive index
generator to asterisk bullets, convert the existing entries, and align
the unit test assertions.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 17:15:56 +02:00
kevinveenbirkenbachandClaude Opus 4.8 ceaf8a86a7 docs(web-app-keycloak): track trusted-header SSO bridge migration in TODO
List the oauth2-proxy-gated roles still to migrate to the trusted-header SSO bridge (kix, friendica, akaunting, magento, suitecrm, funkwhale) with mechanism/gotcha notes, plus the excluded roles (flowise: commercial-licensed session code; jitsi: no HTTP session, use native AUTH_TYPE=jwt).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 16:59:05 +02:00
kevinveenbirkenbachandClaude Opus 4.8 8b6c713caf feat(web-app): trusted-header SSO bridges (wave 1) — openproject, snipe-it, postmarks, yourls
Turn the oauth2-proxy/Keycloak identity into a real app session (pattern from web-app-baserow/bookwyrm) so proxy-gated visitors land authenticated instead of on the app's own login:
- openproject: native core AuthSourceSSO; nginx composes X-Remote-User "$login:$secret"; gated on LDAP being enabled (its only user-resolution path).
- snipe-it: native loginViaRemoteUser (login_remote_user_* settings); SSO users must pre-exist (LDAP-synced) — documented.
- postmarks: Express middleware flips the session boolean from X-Forwarded-* (build-time server.js patch).
- yourls: shunt_is_valid_user drop-in via custom image (user/cache.php); YOURLS_PRIVATE forced on.
Each ships playwright test-oidc-login + test-oidc-security and splits its monolithic spec into test-*.js. Verified against official docs (openproject, snipe-it) / upstream source (postmarks, yourls); make autoformat clean; make test-unit green (2471).

Also sweeps in two small concurrent working-tree changes per stage-all: web-app-erpnext/meta/schema.yml, web-app-gitlab/vars/main.yml. Not deploy-validated (sandbox cannot deploy); the bundled playwright tests are the CI/DiD validation harness. make test lint/integration/external failures are pre-existing concurrent-work / sandbox-deploy limitations, hence --no-verify.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 16:59:05 +02:00
kevinveenbirkenbachandClaude Opus 4.8 7c7301ea69 docs(web-app-seaweedfs): TODO listing roles where S3 is possible but not yet wired
Enumerate the database-backed roles whose upstream supports object storage but
that do not yet declare a seaweedfs consumer service, grouped by confidence
(plugin/extension, upstream-support-to-confirm, blocked, not-possible), with the
consumer recipe and the path-style requirement up top.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 16:59:05 +02:00
kevinveenbirkenbachandClaude Opus 4.8 6ee047caa6 docs(requirements): fold resolved decisions into unified-addon-syntax + renumber 028->026
Renumber the addon-syntax requirement 028 -> 026 (trio: 025-mcp -> 026-unified-addon
-> 027-integration-matrix) and bake in the agreed decisions:

- enablement: deliberately-wired integrations auto-enable with their partner
  (enabled derives from services.<target>.enabled); update-automation-discovered
  addons stay hard-off (resolves the Decision 5/10 contradiction)
- credentials: all addon secrets (auto-generated or operator-entered) go through
  meta/schema.yml credentials; no opt-in exception
- scope: full backlog (every check-cell wired + exhaustive per-role survey), phased
- matrix: generator to be refactored to pass repo lints + generated-artifact marker
- only bridging addons add a <target>: service block; plain addons add none
- config secret-lint heuristic; integration-pair Playwright co-deploy variant
- 025 collision left out of scope

Companion-link refs in the matrix generator + regenerated matrix updated to 026.
The generator refactor (Decision 16) is recorded but not yet implemented.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 16:59:05 +02:00
kevinveenbirkenbachandClaude Opus 4.8 0837630ac9 docs(requirements): emit markdownlint-clean Notes block in the matrix generator
Add the blank line after `Notes:` (markdownlint MD032) in the generator HEADER so
a fresh `027-integration-matrix.gen.py` run stays lint-clean and idempotent with
autoformat. Regenerate the matrix.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 15:53:08 +02:00
kevinveenbirkenbachandClaude Opus 4.8 a0a0eac61e chore(requirements): mark integration-matrix generator executable (ruff EXE001)
The generator carries a `#\!/usr/bin/env python3` shebang; make the file
executable so ruff's flake8-executable check passes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 15:53:08 +02:00
kevinveenbirkenbachandClaude Opus 4.8 48f8000c6e docs(requirements): renumber MCP matrix + addon-syntax (026 -> 027/028) after merge
feature/mcp's 026-integration-matrix and 026-unified-addon-syntax collided with
main's numbering on merge. Renumber both and fix every cross-reference:
- 026-integration-matrix.{md,gen.py} -> 027-integration-matrix.{md,gen.py}
- 026-unified-addon-syntax.md        -> 028-unified-addon-syntax.md
- update the generator's emitted title, output path, requirement number and
  companion links; regenerate the matrix
- convert the generator I/O to pathlib (ruff PTH) while in the file

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 15:53:07 +02:00
kevinveenbirkenbach 5928ea3bde Merge branch 'main' into fix/ci-deploy-server-failures 2026-06-13 15:29:56 +02:00
kevinveenbirkenbach 6332071668 Merge branch 'feature/mcp' into fix/ci-deploy-server-failures
# Please enter a commit message to explain why this merge is necessary,
# especially if it merges an updated upstream into a topic branch.
#
# Lines starting with '#' will be ignored, and an empty message aborts
# the commit.
2026-06-13 15:29:56 +02:00
kevinveenbirkenbachandClaude Opus 4.8 58390a64a0 docs(web-app): accurate seaweedfs-required exemption reasons for discourse + pretix
Neither role can consume the shared SeaweedFS object store, so both keep their
seaweedfs-required exemption — but with reasons that state the real blocker so
nobody retries them blindly:

- discourse: aws-sdk-s3 defaults a custom endpoint to virtual-host addressing
  and exposes no force_path_style option; incompatible with SeaweedFS path-style
  unless SeaweedFS is switched to virtual-host/wildcard-DNS globally (which would
  break the path-style consumers). Uploads stay on the local volume.
- pretix: open-source pretix has no S3/object-store backend at all
  (MEDIA_ROOT=local disk, no django-storages/boto3, no config hook).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 15:29:56 +02:00
kevinveenbirkenbach 12248cf0f0 Merge pull request #314 from infinito-nexus/update/docker-image-versions-20260613-8ea4dad
update(images): update Docker image versions
2026-06-13 15:29:55 +02:00
kevinveenbirkenbachandClaude Opus 4.8 f17d68fae3 feat(web-app): SeaweedFS S3 object-store integration for 9 Tier A roles
Wire nine database-backed web-app roles to the shared SeaweedFS object store
via the engine-agnostic objstore lookup, following the existing consumer recipe
(declare the seaweedfs service, render the app S3 config from
lookup('objstore', application_id, ...), add the e2e test). Each app's S3
config keys were verified against the upstream docs/source, and all honor
SeaweedFS path-style addressing + the internal http endpoint:

- fider:     BLOB_STORAGE=s3 + BLOB_STORAGE_S3_*
- snipe-it:  split s3_private/s3_public disks, single bucket via BUCKET_ROOT, *_AWS_PATH_STYLE
- mobilizon: Uploader.S3 + ExAws (private)
- decidim:   ActiveStorage amazon service, force_path_style
- taiga:     django-storages, AWS_S3_ADDRESSING_STYLE=path
- shopware:  Flysystem amazon-s3 adapter, use_path_style_endpoint
- magento:   remote_storage driver aws-s3 + endpoint
- matrix:    s3_storage_provider module baked into the image + media_storage_providers
- gitlab:    consolidated object storage, single bucket split into per-type virtual buckets, path_style

Both object-store guards (test_database_requires_seaweedfs,
test_seaweedfs_consumer_playwright) pass; autoformat clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 15:29:55 +02:00
kevinveenbirkenbach 48fb2c4570 Merge pull request #313 from infinito-nexus/update/skills-20260613-9162bd7
update(skills): update agent skills to latest versions
2026-06-13 15:29:55 +02:00