Commit Graph
10 Commits
Author SHA1 Message Date
kevinveenbirkenbachandClaude Opus 5 304144ce67 fix(matrix): give each bridge declared tokens and a rendered registration, drop the swarm service removal
Every mautrix bridge generated its own registration with -g on start,
with fresh random tokens. Synapse had to read that file from a shared
directory, be restarted afterwards, and on swarm the services had to be
removed first so the registrations could be wiped (93735fd533). The
tokens changed on every redeploy and the deploy depended on a wait for
files a container writes.

Each bridge now has as_token and hs_token declared in meta/secrets.yml
and a fixed appservice_port in its addon. The bridge config reads the
tokens through lookup('config'), and registration.yaml.j2 renders the
registration from that config in the upstream -g format, so both sides
are known before anything starts and stay identical across redeploys.

The files reach the containers as secrets: matrix_bridge_mounts offers a
stack view (top-level definitions, content-hash named by compose_volumes),
a synapse view (one registration per bridge under /registrations, mode
0444) and a bridge view (the config at /config/config.yaml). The bridges
run as the synapse uid with an explicit --config and --no-update, keep
their scratch /data on a 16m tmpfs so hlth-volumes finds no anonymous
volume, and depend on a healthy synapse. Their healthcheck is
/_matrix/mau/ready, which the bridge only reports after whoami with the
as_token and an appservice ping carrying the hs_token, so a healthy
bridge proves the token pair.

The compose tasks lose the compose down, the swarm service rm block, the
registration wait, the permission fixes and both synapse restarts; this
reverts the workaround of 93735fd533.

Verified on the local compose stack: four bridges ready, synapse pings
answered, registration and config sha256 unchanged across a redeploy,
test-bridge-roster and 12 matrix Playwright specs green. A token
rotation (new pair in both files, compose up --force-recreate as the
handler runs it) came back healthy; a mismatched pair left the bridge
unhealthy. The swarm-shaped stack file passes docker stack config with
the 0444 secret modes, the 16 MiB tmpfs and content-hash secret names.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-11 11:43:16 +02:00
kevinveenbirkenbachandClaude Opus 5 5137465c6f style: delete every comment the comments-valid policy rejects
3529 invalid comments in 528 files, grandfathered in while the linter
only saw the working-tree diff. Removal ran through the linter's own
collectors, so detection and deletion cannot drift: a comment whose
opener line is blank before it and whose closer line is blank after it
loses all its lines, anything else loses only the comment region, and a
line left holding a bare delimiter goes with it.

Five files keep their comments behind a nocheck marker instead. Four are
upstream configs whose inline docs are upstream's own (mautrix, systemd
journald, nextcloud-oidc-login, MediaWiki). In littlejs the
commented-out entries ARE the deactivation mechanism for an example
tile, so deleting them would delete configuration.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-18 03:22:02 +02:00
kevinveenbirkenbachandClaude Opus 5 f713ba8350 refactor(credentials): address them at secrets.credentials, like every other meta file
meta/schema.yml became meta/secrets.yml, but its content still landed at
applications.<app>.credentials — the one meta file whose payload did not sit
under a root named after the file. Every other topic obeys that rule:
meta/volumes.yml goes to volumes, meta/rbac.yml to rbac. Credentials now go to
secrets.credentials.

The path exists exactly once, in utils/manager/credential_key.py, and
OVERRIDE_SECTION is derived from its two segments rather than spelled beside
them, so the dotted form used by --set and the nested form used by the
inventory cannot drift. Six consumers read it: the loader, the generator, the
CLI override parser, the two emit helpers, and the four lookup validators.

--set now accepts exactly one key shape,
applications.<app>.secrets.credentials.<key>. The three short forms are
rejected instead of guessed at: the same short key names different credentials
on different apps. That also retired primary_app_id, which only existed to
disambiguate them.

Three defects surfaced while wiring this up. _apply_one_role_schema still wrote
to the old root, so an app's own credentials would have moved while its
providers' stayed behind. web-app-pgadmin reads through
lookup('applications', ...).credentials.<key>, an access path no regex over
lookup('config', ...) could see, and would have deployed with an empty postgres
password. And _META_NON_GENERIC_TOPICS still listed 'schema' — a bare topic
name, invisible to any path search — so the generic file-root loop assigned the
raw schema under secrets and the follow-up write landed inside the cached
document, truncating keycloak's four credentials to one for every later
reader.

tests/integration/ansible/lookups/config/test_credentials_roundtrip.py pins both
directions of the seam and the cache invariant that last defect broke.

Existing inventories still carry the pre-rename root; migrating them is
deliberately out of scope here.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-12 16:51:56 +02:00
kevinveenbirkenbachandClaude Opus 4.8 6122213ed2 feat(web-app-matrix): migrate dead mautrix-facebook/instagram to mautrix-meta + gate bridge specs by deployed addon
Legacy mautrix-python facebook/instagram bridges are EOL and fail at runtime (appservice bot masquerade 403). Replace both with the bridgev2 mautrix-meta addon: config, schema credential, variant-2 wiring, and a profile spec with a positive @metabot probe plus an un-bridged-MXID negative control.

playwright.env.j2 now emits MAUTRIX_<bridge>_ADDON_ENABLED only for bridges that are actually deployed, so each bridge spec runs iff its bridge is up (closes the skip-as-green gap) and is telegram-drop aware.

Verified: compose variant-2 deploy failed=0; @metabot registers on the live Synapse; bridge suite green (meta/signal/whatsapp/slack), telegram skips (no API creds).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 18:45:58 +02:00
kevinveenbirkenbachandClaude Opus 4.8 674b58fdfe fix(matrix): load bridge registrations in synapse + url-encode legacy bridge db passwords
Two bugs kept every mautrix bridge from working: (1) homeserver.yaml.j2 gated app_service_config_files on '{% if MATRIX_BRIDGES | bool %}', but MATRIX_BRIDGES is a list and Ansible's bool filter on a list is always False, so synapse loaded zero appservices and rejected every bridge's as_token (401). Use '| length > 0'. (2) the legacy facebook/instagram bridges interpolated the DB password into the postgres:// URL unencoded; a password with a reserved char crashed mautrix-python with 'Invalid URL: port can't be converted to integer'. URL-encode it like the bridgev2 bridges. Verified by a clean redeploy that recreated the bridge containers and regenerated registrations from the templates: all 5 bridges + synapse healthy.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 07:08:02 +02:00
kevinveenbirkenbachandClaude Opus 4.8 f57dda89db fix(matrix): null the mautrix-whatsapp segment.io analytics url (test-external)
The bridgev2 config migration carried over the upstream example-config's analytics block, whose url pointed at https://api.segment.io/v1/track. The token is already null (telemetry off), but the live external URL made the external-URL-reachability test fail (HTTP 400 from the segment tracking endpoint). Null the url so no external telemetry endpoint is referenced.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 04:00:20 +02:00
kevinveenbirkenbachandClaude Opus 4.8 7eaebbe895 fix(matrix): make the migrated bridgev2 bridges actually deploy
Follow-ups after the bridgev2 config migration, each verified in the live DiD stack: (1) URL-encode the bridge DB password in the postgres uri (the credential contains '/', which the stricter bridgev2 Go URL parser rejected as an invalid port) so signal/whatsapp/slack connect to postgres and generate their registration; (2) drop the telegram bridge from MATRIX_BRIDGES when its Telegram api_id/api_hash credentials are absent (telegram requires them at startup and crash-loops without — unlike the other bridges — blocking synapse); (3) give the bridge healthcheck a 5m start_period so the registration generation has grace before it is marked unhealthy.

(4) Two-phase synapse start: drop the synapse->bridges service_healthy depends_on (a single 'compose up -d' did not respect the start_period and aborted with 'dependency ... unhealthy' before the bridges generated registration) and instead restart synapse after the Ansible 'wait for registration files' step so it loads the freshly written appservice registrations. The bridges generate registration independently of synapse, so synapse no longer needs to block on their health.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 03:25:58 +02:00
kevinveenbirkenbachandClaude Opus 4.8 0eee711159 fix(matrix): migrate signal/telegram/whatsapp/slack bridges to bridgev2 config
The mautrix bridge images (dock.mau.dev/mautrix/<name>:latest) moved to the bridgev2/megabridge architecture, but the role's config templates were still the legacy mautrix-go format. On boot the bridgev2 binary aborts with 'Legacy bridge config detected, but hacky network config migrator is not set', crash-loops, never writes registration.yaml, stays unhealthy, and holds synapse in Created (depends_on service_healthy) — failing the matrix deploy in CI run 28044348006.

Rewrite the 4 cutover bridges' configs to the bridgev2 layout (top-level database/network/matrix/double_puppet/encryption), preserving every templated value: homeserver address/domain, the postgres DB uri with its credential lookup, the appservice id/port/bot, the permissions map, and the raw go-template guards. The default entrypoint still generates registration.yaml, so no compose/registration changes are needed. facebook/instagram/imessage stay on the legacy format (their images are legacy-frozen and deploy healthy). Render + YAML validated; actual bridge boot is verified by CI.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 01:22:50 +02:00
kevinveenbirkenbachandClaude Opus 4.7 2f4ef3cfad refactor(web-app-matrix): central postgres + shared MATRIX_POSTGRES_*
Both flavors integrate with Infinito's central postgres (no more MDAD
bundled DB inside the DiD container). The connection vars live in a
single set of MATRIX_POSTGRES_* constants in vars/main.yml and are
referenced by both flavors' templates so the source-of-truth is
explicit and identical.

- vars/main.yml: MATRIX_MDAD_POSTGRES_* renamed to MATRIX_POSTGRES_*
  (drop MDAD prefix — these are flavor-agnostic) + add
  MATRIX_POSTGRES_DATABASE.
- templates/flavor/ansible/vars.yml.j2 (was mdad-vars.yml.j2):
  postgres_enabled: false; wire postgres_connection_*/synapse
  database_* to MATRIX_POSTGRES_* + matrix_synapse_container_network:
  host so the inner Synapse inherits matrix-mdad's outer networks and
  resolves `database` / `openldap` by hostname. valkey_enabled: false
  (Synapse single-process; central redis path not used here).
- templates/flavor/ansible/hosts.j2 (was mdad-hosts.j2): renamed, no
  content change. mdad- prefix dropped since the file is already
  under flavor/ansible/.
- templates/flavor/ansible/services.yml.j2: matrix-mdad joins the
  central `postgres` network (conditional on DB shared) and
  `openldap` network (conditional on MATRIX_LDAP_ENABLED).
- templates/flavor/compose/synapse/homeserver.yaml.j2 and the six
  templates/flavor/compose/mautrix/*.config.yml.j2 templates: switch
  inline `lookup('database', application_id, 'host'/'username'/...)`
  calls to MATRIX_POSTGRES_* (same constants the ansible flavor uses).
- tasks/flavor/ansible/: renumber 01-05 cleanly
  - 03_database.yml (was 02a_database.yml) — sys-svc-rdbms include
  - 04_run_playbook.yml (was 03_run_playbook.yml)
  - 05_proxy_wiring.yml (was 04_proxy_wiring.yml)
- tasks/flavor/ansible/main.yml + 02_render_inventory.yml + README.md:
  update references to the renamed files.
- tasks/flavor/compose/02_create-and-seed-database.yml: switch host/port
  lookups to MATRIX_POSTGRES_* too.

Tests: 186/186 green.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-05 19:00:46 +02:00
kevinveenbirkenbachandClaude Opus 4.7 70c1a03f13 feat(web-app-matrix): add ansible flavor via unified compose.yml.j2
Implements requirement 025 (docs/requirements/025-web-app-matrix-ansible-flavor.md).

Architecture:
- services.matrix.flavor: ansible | compose
- Single templates/compose.yml.j2 branches on matrix_flavor
- ansible flavor: defines matrix-mdad-runner one-shot compose service that
  DiD-executes upstream matrix-docker-ansible-deploy (pinned ref + migration
  validated version). MDAD writes /etc/systemd/system/matrix-*.service units
  on the host; post-up Ansible task container-waits the runner, then probes
  loopback ports + wires sys-stk-front-proxy.
- compose flavor: legacy in-repo stack (synapse/element/coturn/bridges) kept
  for regression coverage, marked deprecated.

Variant-switch ansible -> compose:
- 00_cleanup_mdad_residue.yml tears down MDAD-installed systemd units +
  leftover matrix-* containers before compose-flavor brings up its own
  matrix-synapse (else: name conflict, compose down doesn't touch
  systemd-managed state).

Reorganization:
- roles/web-app-matrix/{tasks,templates,files}/flavor/{ansible,compose}/
- Renamed legacy compose-flavor task files into flavor/compose/
- New runner Dockerfile (multi-stage, ARG-parameterized FROM via build-args)

Verified: 3 variants x 2 passes (sync+async) full_cycle green, all 6
PLAY RECAPs failed=0. V1 (ansible all-on), V2 (ansible no-auth), V3
(compose deprecated all-on). Playwright matrix tests pass per variant.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-05 10:21:41 +02:00