- Go 99.3%
- Shell 0.5%
- Dockerfile 0.2%
| cmd/mautrix-jmpchat | ||
| connector | ||
| docs | ||
| patched-xmpp@4a8ebaf259 | ||
| .gitignore | ||
| .gitmodules | ||
| .mise.toml | ||
| AGENTS.md | ||
| build-go.sh | ||
| DEVEL.md | ||
| docker-run.sh | ||
| Dockerfile | ||
| example-config.yaml | ||
| go.mod | ||
| go.sum | ||
| MEMO.md | ||
| README.md | ||
| simple-config.yaml | ||
| TODO.md | ||
mautrix-jmpchat
mautrix-jmpchat is a Matrix appservice bridge for JMP and the Cheogram XMPP network.
Its purpose is to let a Matrix user operate a JMP-backed XMPP account from Matrix: direct chats, Cheogram phone-number chats, and related account actions are bridged through Matrix portal rooms.
This repository lives inside a matrix-docker-ansible-deploy workspace and is written to integrate cleanly with that playbook's appservice deployment model.
What This Is
JMP gives users a real phone number that works through XMPP/Jabber apps such as Cheogram. This bridge connects that XMPP side to Matrix using mautrix bridgev2.
The project is not a generic bridge template anymore. It is specifically aimed at:
- Matrix <-> JMP/Cheogram XMPP bridging
- Cheogram phone-number contacts such as
+14169938000@cheogram.com - Cheogram multi-recipient group chats
- deployment as a self-hosted Matrix appservice, especially with
matrix-docker-ansible-deploy
The cheogram-android/ tree is kept around as a behavior reference while bringing this bridge closer to real Cheogram/JMP behavior.
Current Scope
The codebase currently includes bridge logic for:
- XMPP login using a JMP/Cheogram JID and password
- per-user XMPP connections through
gosrc.io/xmpp, with local patches inpatched-xmpp/ - Matrix portal rooms for DMs and Cheogram-style group chats
- roster fetches, bookmarks/discovery, and MUC joins
- MAM-based history sync/backfill
- management-room commands for chat creation, sync, discovery, blocking, and reporting spam
- Matrix -> XMPP handling for messages, reactions, and read receipts
This is still an active bridge implementation, not a finished polished product. When behavior differs from Cheogram Android, Cheogram is the reference.
Repository Layout
cmd/mautrix-jmpchat/main.go: binary entrypointconnector/: bridge implementationpatched-xmpp/: local fork ofgosrc.io/xmppused by this bridgedocker-run.sh: container entrypoint used by the imageDockerfile: container build for deploymentDEVEL.md: maintainer-oriented Go/codebase walkthroughdocs/CHEOGRAM_ANALYSIS.md: notes comparing this bridge to Cheogram Android and JMP behavior
Important Implementation Notes
- The Go module replaces upstream XMPP with a local fork:
replace gosrc.io/xmpp => ./patched-xmpp
- The binary identifies itself as
A Matrix-XMPP bridge for JMP.chat/Cheogram. - The bridge registers custom management-room commands in
connector/my_connector.go. - The Docker image persists runtime state in
/data.
Logging In And Basic Usage
From a Matrix user's point of view, the bridge is normally used through its management room.
Typical flow:
- Install and start the bridge, then register its appservice with your homeserver.
- Open a DM with the bridge bot or otherwise reach the bridge management room.
- Run
login <jid>. - When prompted, enter your JMP/Cheogram XMPP JID and password.
- After login, let the bridge connect and create its per-user space/management-room structure.
- Use commands like
sync,create, andlist-contactsto discover chats or create new ones. - Talk in the bridged portal rooms that the bridge creates.
Current login expectations:
- the username is your XMPP JID, for example
user@cheogram.com - the password is your JMP/Cheogram account password
- the bridge verifies the credentials by making a real XMPP connection during login
Important behavior:
- Matrix rooms created directly by a user are not automatically bridged to JMP
- use bridge-created portal rooms for actual message routing
- use
create <jid>orcreate <jid> [jid...]to start new bridged DMs or Cheogram group chats - use
syncto pull in roster/bookmarks and trigger backfill of history
If a user logs in successfully, the bridge stores a user login entry, starts a per-user XMPP client, and then uses portal rooms to carry future Matrix <-> XMPP traffic.
Management Room Commands
When the bridge is running and a user has a management room, the quick-help text currently includes:
login <jid>logout [login_id]logout [login_id] purgedelete-all-portals yespingsync [full|30d]sync-statuscreate <jid> [jid...]list-contactslist-contactedblock [--report] <jid>report <jid>gateway-actions [jid]gateway-form <jid> <node>gateway-submit <session> field=valuelist-logins [--verbose]list-usersset-name <jid> <name>
Recent bridge-specific additions include:
ping: show login state, XMPP session state, and run an XMPP server ping when connectedlist-contacts: show the loaded XMPP rosterlist-contacted: show contacts derived from bridged rooms, including split-out Cheogram group participantsblock <jid>: block a contact over XMPP blockingblock --report <jid>/report <jid>: report spam using Cheogram-style XMPP reporting and block the contact
Common User Workflows
Start a direct chat with a phone-number contact:
create +14169938000@cheogram.com
Start a Cheogram group chat with multiple contacts:
create +14169938000@cheogram.com +13127968000@cheogram.com
Sync existing server-side state and recent history:
sync
Run a deeper sync/backfill:
sync full
Inspect contacts and already-contacted users:
list-contacts
list-contacted
Block or report spam:
block +14169938000@cheogram.com
block --report +14169938000@cheogram.com
Local Development
Build the binary:
go build ./cmd/mautrix-jmpchat
Run the bridge from source:
go run ./cmd/mautrix-jmpchat -c config.yaml -r registration.yaml
Generate a registration file:
go run ./cmd/mautrix-jmpchat -g -c config.yaml -r registration.yaml
Generate an example config file:
go run ./cmd/mautrix-jmpchat -e -c config.yaml
Run tests:
go test ./...
go test ./connector
If you use the repo's configured toolchain wrapper:
mise install
mise exec -- go test ./connector
Docker Runtime Behavior
The container image is built around /usr/bin/mautrix-jmpchat and docker-run.sh.
The Dockerfile hardcodes the Go patch release for reproducible image builds; keep it in sync with .mise.toml when updating Go.
On startup:
- If
/data/config.yamldoes not exist, the container generates it and exits. - If
/data/registration.yamldoes not exist, the container generates it and exits. - Once both files exist, the bridge starts normally from
/data.
That means the normal first-run flow is:
- Start the container once to get
/data/config.yaml. - Edit
/data/config.yaml. - Start it again to get
/data/registration.yaml. - Install the registration into your homeserver.
- Start the bridge again for normal operation.
Configuration Notes
At minimum, you will need to review and set:
homeserver.addresshomeserver.domainappservice.addressappservice.hostnameappservice.portappservice.idappservice.bot.usernameappservice.as_tokenappservice.hs_tokenappservice.username_templatedatabase.*bridge.permissions
The generated registration.yaml must match the appservice values in config.yaml.
For JMP/Cheogram usage, users log in with their XMPP JID and password, for example user@cheogram.com.
Using With matrix-docker-ansible-deploy
This repo is intended to plug into a matrix-docker-ansible-deploy setup rather than act as a standalone polished installer.
In practice, that means:
- build a container image from this repository
- persist
/data - expose the bridge on the appservice listener configured in
config.yaml - register the generated
registration.yamlwith the homeserver managed by the playbook - keep the bridge and homeserver on the same private network when possible
The exact Ansible wiring depends on how your playbook is customized, but the bridge follows the same appservice lifecycle expected by the playbook: persistent config, generated registration, then long-running service.
JMP Context
Minimal context that matters for this bridge:
- JMP provides phone numbers and messaging/calling through XMPP/Jabber
- Cheogram is the main reference client for that network
- phone-number contacts are represented as XMPP JIDs
- group texting is represented through Cheogram-specific group JID conventions
This README is intentionally not a JMP marketing page. For product/user-facing information, see:
- https://jmp.chat/
- https://f-droid.org/en/packages/com.cheogram.android/
- https://play.google.com/store/apps/details?id=com.cheogram.android.playstore
Development References
DEVEL.mdfor a codebase-specific Go tutorial and maintenance guidedocs/CHEOGRAM_ANALYSIS.mdfor protocol and behavior notesTODO.mdfor remaining bridge-parity work
Caveats
example-config.yamlstill contains some template-era naming and defaults; treat it as a starting point, not authoritative product documentation.- This bridge depends on local
patched-xmppbehavior, so upstream library documentation is not always enough. - Full compatibility with Cheogram/JMP behavior is still in progress.