Skip to content

feat(matrix): add Matrix channel integration#6

Open
theredspoon wants to merge 17 commits intosimple10:mainfrom
theredspoon:feat/matrix-channel
Open

feat(matrix): add Matrix channel integration#6
theredspoon wants to merge 17 commits intosimple10:mainfrom
theredspoon:feat/matrix-channel

Conversation

@theredspoon
Copy link
Contributor

Summary

  • Add Matrix channel support via the bundled @openclaw/matrix plugin — configured through stack.yml, deployed via pre-deploy, with env var coercion and channel block stripping in resolve-config-vars.mjs
  • Add telegram.enabled / matrix.enabled toggles that gate credential emission in docker-compose and strip disabled channel blocks from the deployed config (so they don't appear in the Control UI)
  • Add entrypoint patches for Matrix plugin dependencies and config var boolean coercion

What's included

  • stack.yml.example — Matrix defaults (homeserver, enabled flag)
  • pre-deploy.mjs — validation (homeserver + access_token required when enabled), derived matrix_enabled / telegram_enabled booleans
  • docker-compose.yml.hbs — conditional env var emission for both channels
  • resolve-config-vars.mjs${VAR} → native boolean coercion, disabled channel block stripping
  • entrypoint.sh — conditional Matrix plugin dependency install
  • openclaw/default/openclaw.jsonc — Matrix channel config template
  • docs/MATRIX.md — setup guide
  • docs/TELEGRAM.md — updated with toggle docs
  • Playbook updates (onboarding, verification, pairing, maintenance, backup)

Test plan

  • npm run pre-deploy:dry with matrix.enabled: false — Matrix channel block stripped, secrets omitted
  • npm run pre-deploy:dry with matrix.enabled: true — channel block preserved, secrets emitted
  • Telegram toggle follows same pattern
  • Deployed to VPS with both channels disabled — Control UI shows no configured channels
  • Deployed with Matrix enabled — Control UI shows Matrix channel

Scopes Matrix messaging integration as an alternative to Telegram.
Covers plugin vs. sidecar approaches, Conduit self-hosted option,
idempotency, open questions on OpenClaw plugin API and E2E encryption.
Wires in the @openclaw/matrix plugin as a first-class deploy concern:

- stack.yml.example: matrix: defaults block with all config options;
  commented per-claw example (access_token env var pattern)
- docker-compose.yml.hbs: MATRIX_ENABLED always emitted; homeserver and
  access_token emitted only when enabled (sensitive var guard)
- entrypoint.sh: idempotent plugin install at startup (filesystem check
  before install, runs as node uid 1000 for correct ownership)
- openclaw/default/openclaw.jsonc: matrix channel block with env var
  substitution, disabled by default via MATRIX_ENABLED=false
- build/pre-deploy.mjs: validateClaw checks homeserver + access_token
  when matrix.enabled; matrix_enabled derived boolean for Handlebars;
  MATRIX_HOMESERVER and MATRIX_ACCESS_TOKEN added to potentiallyEmptyVars
  so openclaw.jsonc ${VAR} resolves cleanly when Matrix is disabled
- docs/MATRIX.md: operator setup guide (account, token, pairing, rooms,
  E2EE, token rotation, troubleshooting)
- docs/PROPOSAL-MATRIX-CHANNEL.md: updated from initial draft to reflect
  confirmed native plugin support and full implementation scope
Matrix sync state and E2EE crypto keys live in .openclaw/matrix/.
Without this, a restore would lose the bot's device identity and
require re-verification in all encrypted rooms.

matrix_dir is conditionally populated (ls -d 2>/dev/null) so the
tar command is unchanged for claws without Matrix enabled.

Also updates the 06-backup.md "What Gets Backed Up" table.
Critical:
- pre-deploy: always include MATRIX_HOMESERVER and MATRIX_ACCESS_TOKEN in
  empty-env-vars regardless of any claw's config. Previously computed against
  only the first claw, breaking multi-claw stacks with heterogeneous Matrix
  config (enabled on one claw, disabled on another).
- openclaw.jsonc: add @openclaw/matrix to plugins.allow and plugins.entries
  (enabled via ${MATRIX_ENABLED}). Without this, a sync-deploy that rewrites
  openclaw.json removes Matrix plugin allowance even when the plugin is
  installed — and the entrypoint's directory-existence check prevents reinstall.

Moderate:
- entrypoint: change matrix plugin install failure from warning to hard error
  (exit 1). Booting with MATRIX_ENABLED=true and a non-functional Matrix
  channel is misleading; a clear failure is better than a silent degradation.
- MATRIX.md: fix deploy command (service name is <project>-openclaw-<claw>,
  not just the claw key); fix E2EE section (encryption not rendered from
  stack.yml — must edit per-claw openclaw.jsonc directly); update room config
  section to clarify groups require openclaw.jsonc edit or Control UI.
- stack.yml.example: annotate which matrix fields are rendered into
  docker-compose.yml vs which require direct openclaw.jsonc configuration.
…external

Confirmed against a live OpenClaw installation: @openclaw/matrix ships bundled
with OpenClaw (extensions dir is empty; matrix/ state dir exists). No install
step is needed. Plugin key in config is "matrix" (unscoped), not "@openclaw/matrix".

- entrypoint: remove the `openclaw plugins install @openclaw/matrix` block entirely
- openclaw.jsonc: remove "@openclaw/matrix" from plugins.allow (not needed for
  bundled plugins); rename plugins.entries key from "@openclaw/matrix" to "matrix"
- MATRIX.md: update §5 Deploy and Plugin install troubleshooting to reflect
  that the plugin is bundled and auto-loaded when MATRIX_ENABLED=true
Confirmed from live install: @openclaw/matrix is bundled with OpenClaw,
not an external extension. Update all sections that assumed an install step
was needed, that the plugin lives in ~/.openclaw/extensions/, or that the
config key is "@openclaw/matrix". Update status to Implemented.
…ot bundled

Reverts the incorrect inference from 40cae9b and e831726 that @openclaw/matrix
is bundled with OpenClaw. The empty ~/.openclaw/extensions/ on the reference
server was because OpenClaw was installed from a git checkout (local path
./extensions/matrix), not because the plugin ships with the binary.

Source of truth: extensions/matrix/package.json blurb says "install the plugin
to enable" and openclaw.install.npmSpec = "@openclaw/matrix" with defaultChoice
= "npm". The plugin key "matrix" (unscoped) from openclaw.plugin.json is kept —
that part was correctly identified.

- Restore entrypoint.sh matrix plugin install block (exit 1 on failure,
  gosu node, directory-existence idempotency check)
- Restore plugins.allow "matrix" entry in openclaw/default/openclaw.jsonc
- Fix plugin comments in openclaw.jsonc (external, not bundled)
- Fix docs/MATRIX.md §5 and "Plugin not loading" section
- Fix docs/PROPOSAL-MATRIX-CHANNEL.md plugin model description throughout
Set deviceName to "OPENCLAW_GATEWAY" statically in openclaw.jsonc.
userId is auto-generated by OpenClaw from the claw name — not configured here.
- §4: remove groups-in-stack.yml example (groups not rendered from stack.yml);
  point to openclaw.jsonc via cross-reference to Rooms section
- Rooms section: fix duplicate 1. list items → 1/2/1 (linter requires 1/1/1)
- Token rotation: fix service name to match correct pattern
614a31c incorrectly concluded the plugin was external based on
package.json metadata. Live testing confirms it ships inside the Docker
image at /app/extensions/matrix/ and loads without an install step.

The entrypoint install block was creating a second copy at
~/.openclaw/extensions/@openclaw/matrix/, causing a "duplicate plugin id"
warning and a load failure (wrong import paths in the installed copy).

- Remove entrypoint matrix plugin install block
- Remove "matrix" from plugins.allow (not needed for bundled plugins)
- Update docs and proposal to reflect bundled model
…t template updates

Entrypoint: patch keyed-async-queue subpath regression (openclaw#32772),
install matrix plugin deps on first boot (openclaw#16031).
resolve-config-vars: coerce "${VAR}" whole-value patterns to native
booleans/numbers so MATRIX_ENABLED=true resolves to true, not "true".
Default template: set groupPolicy to "open", always load matrix plugin,
keep encryption off as safe default.
Gate Telegram channel on stack.yml telegram.enabled (default: true).
When disabled, the channel block is stripped from the deployed
openclaw.json so it doesn't appear in the Control UI. Same mechanism
as matrix.enabled — resolve-config-vars strips disabled channels
after resolving env vars.
When a channel ID env var (e.g. ADMIN_TELEGRAM_ID) is unset, ${VAR}
resolves to "", producing [""] in allowFrom arrays. Filter these out
so optional channels don't leave phantom empty entries.
The default template uses groupPolicy: open (respond in any joined
room when mentioned), not allowlist. Updated docs to match and
explain how to switch to allowlist if needed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant