A BotFather for self-hosted Matrix (Synapse + MAS)
  • Rust 98.6%
  • Dockerfile 1.4%
Find a file
Julian Lindner 134a455a84
All checks were successful
CI / docker (push) Successful in 2m58s
CI / cargo (push) Successful in 42s
feat(commands): accept commands without a leading slash (Element compat)
Matrix clients like Element intercept a leading '/' as their own slash-command,
so '/newbot' never reaches the bot. Parse commands with OR without the slash;
a bare unknown word is plain text, a slashed unknown is a usage hint. Harden the
conversational flow so only 'cancel' interrupts (a bot named e.g. 'token' won't
hijack the flow). Update all user-facing hints to bare command words. +2 tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 16:14:59 +08:00
.forgejo/workflows feat: implement matrix-botfather (Rust) 2026-06-16 15:10:33 +08:00
src feat(commands): accept commands without a leading slash (Element compat) 2026-06-16 16:14:59 +08:00
.gitignore feat: implement matrix-botfather (Rust) 2026-06-16 15:10:33 +08:00
Cargo.lock feat: implement matrix-botfather (Rust) 2026-06-16 15:10:33 +08:00
Cargo.toml feat: implement matrix-botfather (Rust) 2026-06-16 15:10:33 +08:00
deny.toml feat: implement matrix-botfather (Rust) 2026-06-16 15:10:33 +08:00
DESIGN.md docs: sanitize DESIGN.md for public release 2026-06-16 15:17:47 +08:00
Dockerfile feat: implement matrix-botfather (Rust) 2026-06-16 15:10:33 +08:00
LICENSE feat: implement matrix-botfather (Rust) 2026-06-16 15:10:33 +08:00
README.md feat: implement matrix-botfather (Rust) 2026-06-16 15:10:33 +08:00
rustfmt.toml feat: implement matrix-botfather (Rust) 2026-06-16 15:10:33 +08:00

matrix-botfather

A BotFather for self-hosted Matrix (Synapse behind Matrix Authentication Service). It's an ordinary Matrix bot — @botfather:kampong.social — that you chat with to create and manage named Matrix bot accounts. Tell it "make a bot called Hanso Heartbeat with this avatar," it mints the account + a long-lived access token via the MAS Admin API and DMs you the token back. "delete @x", "list", "rotate" likewise.

Status: implemented (Rust), pre-deployment. The bot is written and CI-green (build + clippy + tests); the MAS Admin API flow is verified against our live MAS 1.17.0. The full design is in DESIGN.md.

The question this answers

"We run MAS + OIDC (Logto). Does every bot really need an identity in our OIDC provider? That can't be right."

It isn't. Running MAS/OIDC does not mean bots need a Logto/Zitadel identity. MAS is the homeserver's OIDC provider; the upstream IdP (Logto) is only an optional human login method. A bot is just a MAS-local user with a long-lived token. This is already proven on our own cluster: no existing bot uses Logto — Draupnir is a plain MAS user + token; the mautrix bridges and Hookshot use appservice tokens. (See DESIGN.md §1.)

Why a bot, not an MCP

The interface is plain Matrix messages, so botfather is reachable everywhere at once: you in Element, and an agent through matrix-mcp, and anything else that can send a Matrix message. No separate MCP server to build, deploy, or auth. It mirrors Telegram's BotFather exactly — a bot you talk to.

First consumer

The Hanso Heartbeat newsletter agent (Lenno's first production agent) needs a Matrix bot for its weekly review handoff. Once botfather exists, Julian DMs it create Hanso Heartbeat, gets the token, and drops it into the agent's Lenno Matrix channel. Lenno never knows how the bot was born — same contract as a Telegram token from BotFather.

Build target (summary)

  • A small, E2EE-capable Matrix bot service, deployed in the matrix namespace via GitOps (ArgoCD), like the other Matrix services.
  • Holds: its own bot access token (bootstrapped once) + a MAS admin OAuth client credential (urn:mas:admin).
  • Obeys an allowlist of MXIDs (initially just Julian).
  • Talks to the MAS Admin API to create/deactivate users + mint/revoke tokens, and to the Synapse client-server API to set each bot's display name + avatar.

See DESIGN.md for the complete spec: exact API flow, the live cluster facts, the command protocol, bootstrap, security, and open items.