This repository is the deployable configuration and support code for
xmp.pm, an invite-only, donation-funded public XMPP service.
It includes the public website, XMPP discovery files, Cloudflare Worker invite workflow, D1 migrations, ejabberd configuration, nginx/gateway templates, systemd units, backup helpers, and the outbound VPS agent used for operational automation.
| Item | Value |
|---|---|
| Service domain | xmp.pm |
| Public site | https://xmp.pm/ |
| XMPP admin | admin@xmp.pm |
| Admin email | vesselwave@protonmail.com |
| Registration | Invite-only |
| Chat archive retention | 7 days by default |
| Backups | 14 daily encrypted snapshots |
| Website analytics/tracking | None run by xmp.pm; Cloudflare handles edge delivery and security metadata |
Visitors and users
|
| HTTPS: website, invite requests, status links, Telegram webhook, agent API
v
Cloudflare Worker + Assets + D1
|
| outbound polling only
v
VPS agent
|
| local commands / health checks
v
ejabberd + PostgreSQL + gateway proxy + backups
xmp.pm pages and invite routes are served by Cloudflare Worker Assets and the
Worker in apps/worker/. Native XMPP traffic and the WebSocket/BOSH/upload
gateway are served from the VPS through xmpp.xmp.pm.
| Path | Purpose |
|---|---|
apps/website/ |
Static public site, invite pages, legal/info pages, XEP-0156 files |
apps/worker/ |
Cloudflare Worker invite workflow, Telegram approvals, agent API |
apps/agent/ |
Python VPS agent for invite creation, monitoring, smoke checks |
config/ |
DNS template, nginx notes, ejabberd configuration |
migrations/ |
Cloudflare D1 schema migrations |
ops/ |
Deploy scripts, VPS templates, systemd units, backup/cert-sync helpers |
docs/ops/ |
Operator runbooks for deploy, restore, and failure handling |
tests/ |
Worker and website tests |
Install the local tools used by the repo:
- Bun for the Worker and TypeScript tests
uvfor the Python agent environmentjustfor task shortcutsshellcheckandshfmtfor shell script checks
Then run:
bun install
bun run assets # download latest stable Converse.js; generated dist/ stays untracked
cd apps/agent && uv sync && cd ../..
just checkUseful commands:
just --list # show available tasks
just assets # refresh latest stable Converse.js distribution
just dev # fetch assets, then run local Wrangler dev server on port 4000
just typecheck # TypeScript only
just test # Bun + pytest
just lint # Python and shell lint
just fmt # Python and shell formatting
just smoke # public post-deploy smoke checksStart with the example files and keep real secrets out of git:
cp wrangler.toml.example wrangler.toml
cp .env.example .envSet Cloudflare Worker secrets with Wrangler instead of committing them:
bunx wrangler secret put TELEGRAM_ADMIN_CHAT_ID
bunx wrangler secret put TELEGRAM_ADMIN_USER_IDS
bunx wrangler secret put TELEGRAM_BOT_TOKEN
bunx wrangler secret put TELEGRAM_WEBHOOK_SECRET
bunx wrangler secret put AGENT_BEARER_TOKEN
bunx wrangler secret put TURNSTILE_SECRET_KEYThe VPS agent environment is installed separately as /etc/xmppm-agent.env.
Use .env.example as the reference for variable names.
The root justfile wraps the common deploy tasks:
just deploy-worker # Cloudflare Worker/D1/Assets
just deploy-vps # VPS stack
just deploy-agent # VPS agent only
just deploy-proxy # xmpp.xmp.pm HTTPS gateway onlyThe underlying deploy script is ops/deploy.sh:
ops/deploy.sh # VPS stack by default
ops/deploy.sh --only all # Worker + VPS
ops/deploy.sh --reset --only all--reset is destructive and intended only for this service's zero-user or
launch-test rebuild windows.
Backup and restore steps are in
docs/ops/backup-restore.md.
Use config/dns-records as the Cloudflare zone template.
Expected ownership:
xmp.pmandwww.xmp.pmare Worker-routed hostnames with proxied placeholder records.- Native XMPP SRV records target
xmpp.xmp.pm. xmpp.xmp.pm/ws,xmpp.xmp.pm/bosh, and/uploadare served by the VPS gateway.- XEP-0156 discovery files live in
apps/website/.well-known/.
After deploy, the basic public checks are:
curl -fsS https://xmp.pm/ >/dev/null
curl -fsS https://xmp.pm/info.html >/dev/null
curl -fsS https://xmp.pm/.well-known/host-meta | grep xmpp.xmp.pm
curl -fsS https://xmp.pm/request >/dev/null
curl -fsS https://xmpp.xmp.pm/healthzImportant operator references:
Local-only ejabberd Web Admin is bound to 127.0.0.1:5444 by
config/ejabberd.yml. Open an SSH tunnel, keep that terminal running, then
browse https://127.0.0.1:5444/admin:
just webui-tunnelEssential service accounts:
admin@xmp.pm- primary admin accounttest@xmp.pm- live service check accounttest2@xmp.pm- live service check account
Do not purge these accounts during account cleanup unless replacing the operational/test-account plan first.
The initial public cohort is intentionally capped at 10 active users on the current small VPS. Re-evaluate CPU, memory, swap, disk, PostgreSQL, ejabberd, and upload usage before raising the cap to 25 or 50 active users.
| Data | Retention |
|---|---|
| MAM chat archive | 7 days default |
| MAM opt-out | yes, if client supports |
| Offline messages | 30 days or until delivered, whichever is sooner |
| HTTP uploads | 7 days |
| Upload quota | 100 MB/user |
| IP/security logs | up to 7 days |
| Abuse reports/evidence | up to 7 days |
| Backups | 14 daily encrypted snapshots |
| Invite tokens | 14 days or used once |
| Deleted accounts | removed live immediately; backups age out within 14 days |
| Website analytics/tracking | none run by xmp.pm; Cloudflare handles edge delivery and security metadata |
- Keep real secrets out of
.env.exampleandwrangler.toml.example. - Set Worker secrets with
bunx wrangler secret put .... - Keep
/agent/*and/telegram/webhook/*behind their bearer/header secrets. - The trusted-IP invite shortcut only trusts Cloudflare's
CF-Connecting-IPheader. Do not auto-approve based on client-controlledX-Forwarded-For. - Before publishing changes, run a secret scan and review tracked config files that intentionally contain live infrastructure details.
See SECURITY.md for vulnerability reporting and public-repo
handling notes.
Project code is MIT licensed.
config/ejabberd.yml: GPLv2, following the upstream config lineage.config/dns-records: public domain.- Converse.js is fetched from npm with its MPL-2.0 license notices.