Proves the bridge starts on the configuration this role rendered: the
systemd unit is active and has not been restarting, the appservice port is
open, config.yaml and registration.yaml (parsed, not grepped) carry the
scenario's homeserver address, bot username, tokens, command prefix,
avatar-proxy key, public address and log level, the sqlite database was
created under the role's data path as the role's uid, and the running
container is the version defaults/main.yml pins.
It does not bridge anything. Discord is behind an account, which is where a
scenario stops being a test of this repository.
Two things are specific to this role rather than copied from the
mautrix-whatsapp scenario:
- mautrix-discord is the only bridge here whose validate_config.yml requires
a public address. It is composed from hostname + scheme + path prefix, and
those same three feed the Traefik avatar-proxy labels, so the scenario
picks a non-`/` path prefix and a non-default scheme and asserts the
composed router rule and strip-prefix middleware - both in the label file
and, read back off the running container, as labels Docker accepted.
Traefik labels are left enabled here for that reason, unlike in the
mautrix-whatsapp scenario which asserts their absence.
- matrix_bridge_mautrix_discord_bridge_double_puppet_server_map_default in
the role's defaults references matrix_bridge_beeper_linkedin_homeserver_domain
and matrix_bridge_beeper_linkedin_homeserver_address - variables belonging
to a different role, evidently copy-pasted from
matrix-bridge-beeper-linkedin, and unique to this role among the bridges.
A playbook run has every role's defaults in scope so it resolves silently;
a role scenario has only this role loaded and the template fails on the
undefined name. The scenario neutralises it in its own group_vars rather
than touching the role.
Falsified the crash-loop assertion: pointing the in-container sqlite path at
a directory that does not exist makes the bridge exit on startup, and the run
then fails at "Assert the service is active and has not been restarting" with
"activating after 5 automatic restart(s)". Reverted, and green again since -
including idempotence.
The third shape: a non-mautrix bridge. It confirms the scaffolding is not
mautrix-specific, and it is laid out differently enough to be worth having -
everything sits directly under the base path rather than in config/ and
data/ subdirectories, the owner and homeserver URL are passed on the command
line rather than through a config file, so the unit is where they can be
checked, and identd optionally binds host port 113.
matrix-base's matrix_server_fqn_* family moved into the shared context;
18 of the roles here read one of them.
Three roles now, three shapes - HTTP receiver, mautrix bridge, non-mautrix
bridge - and the scaffolding took no per-shape special-casing beyond each
role's own variables.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The second scenario, chosen to be a bridge rather than another HTTP
component: bridges are roughly 40 of the 70 roles here, and validating the
shape on role two is cheaper than discovering it on role forty. It found
two things the first role could not.
Bridges read a whole family of variables from outside themselves -
matrix_bridges_relay_enabled, _encryption_enabled, _encryption_default,
_msc4190_enabled, _self_sign_enabled, plus matrix_admin - all defined in
matrix-base. Since every bridge reads them, they belong in shared context
rather than in each bridge's scenario: molecule-shared/playbook-context.yml
now carries them along with the identity and path variables the first
scenario had inline. Note it is loaded through vars_files, which outranks
inventory group_vars, so it is authoritative; a scenario that wants to prove
one of these reaches the rendered configuration should say so explicitly.
Appservices call /whoami while starting and refuse to run if the id returned
is not the bot user they were configured as - mautrix-whatsapp exits 17 with
"Unexpected user ID in whoami call". The shared stub now takes the id it
should claim to be. Expect every bridge and bot to need this.
What the scenario proves: the bridge starts and stays up, opens its
appservice port, its rendered config and appservice registration carry the
scenario's tokens and bot user, it created its sqlite database under the
role's data path as the role's uid, and it runs the version the role pins.
It does not bridge anything and never will.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>