Quick start¶
This guide runs a complete local stack with the official images: a
tuwunel homeserver and Cumments
as its Matrix Application Service. The ready-made compose file lives at
misc/docker/compose.yaml and is a minimal,
self-contained example: both services are configured entirely through
environment variables; the only manual step outside the compose file is
generating the AppService registration and copying its tokens in.
Prerequisites¶
- Docker with Compose (v2).
curl, for creating the first Matrix account.
1. Copy the compose file¶
Copy the example into a directory of its own so the generated
registration.yaml does not end up inside the repository:
mkdir -p ~/cumments-demo && cd ~/cumments-demo
cp /path/to/cumments/misc/docker/compose.yaml docker-compose.yml
For a local test the defaults work as-is: the Matrix server name is
localhost:8008, tuwunel is published on port 8008, and the Cumments API on
port 7931. For a real deployment, create a .env file next to the compose
file before generating the registration:
MATRIX_DOMAIN=matrix.example.com
MATRIX_DOMAIN is the Matrix server name, i.e. the part after : in user IDs
and aliases. Both services read it from the same variable, so changing it in
one place keeps them consistent.
2. Generate the AppService registration¶
docker run --rm --entrypoint cumments \
ghcr.io/curious-r/cumments:latest \
appservice generate-registration \
--server-name localhost:8008 \
--url http://cumments:7931 > registration.yaml
--server-namemust equalMATRIX_DOMAIN(localhost:8008for the local default).--urlis the callback URL the homeserver uses to push events. Inside Compose this is the service name,http://cumments:7931; behind a reverse proxy use the public URL instead.- The YAML is written to stdout and saved as
registration.yaml. The matchingas_tokenandhs_tokenare printed to stderr — copy them for the next step.
3. Fill in the tokens¶
Open docker-compose.yml and replace the two placeholders:
CUMMENTS__MATRIX__APPSERVICE__AS_TOKEN: "<as_token>"
CUMMENTS__MATRIX__APPSERVICE__HS_TOKEN: "<hs_token>"
The same file is mounted into both containers: tuwunel loads it as its AppService registration, and Cumments validates its configuration against it at startup, so a typo or a mismatched token fails fast.
4. Start the stack¶
docker compose up -d
docker compose logs -f tuwunel
docker compose logs -f cumments
Cumments should log Configuration loaded successfully., Database
initialized., and Server listening on 0.0.0.0:7931.
The example intentionally keeps registration open on tuwunel so the first account can be created with one request. Set
TUWUNEL_REGISTRATION_TOKENand removeTUWUNEL_YES_I_AM_VERY_VERY_SURE_I_WANT_AN_OPEN_REGISTRATION_SERVER_PRONE_TO_ABUSEbefore exposing the homeserver beyond your machine.
5. Create an account and register the first site admin¶
The first account registered on tuwunel is granted homeserver admin privileges. Cumments itself does not read a fixed admin account from configuration. Create an account for whoever will run the site:
curl -sS -X POST http://localhost:8008/_matrix/client/v3/register \
-H 'Content-Type: application/json' \
-d '{"username":"alice","password":"your-password","auth":{"type":"m.login.dummy"}}'
The response contains the user_id (e.g. @alice:localhost:8008).
If alice is registering for herself, she can DM the bot
!cumments site register my-blog: the bot registers the site, creates the
Space and makes her the first site admin immediately. The API path below is
the generic path — use it when the registrant and the first admin are
different accounts:
# Registration is mandatory before the site can receive comments. Pick an id
# (it becomes the Space/room aliases), or omit the body for a random id.
curl -sS -X POST http://localhost:7931/api/v1/sites \
-H "Content-Type: application/json" \
-d '{"site_id":"my-blog"}' > site.json
SITE_ID=my-blog
CLAIM=$(jq -r .claim_token site.json)
curl -sS -X POST "http://localhost:7931/api/v1/sites/$SITE_ID/admins" \
-H "Content-Type: application/json" \
-H "X-Cumments-Claim-Token: $CLAIM" \
-d '{"user_id":"@alice:localhost:8008"}' | tee admin.json
The response contains a one-time verify_token. The account proves ownership
by sending exactly cumments-claim:<token> as a direct message to the
AppService bot (@_cumments_bot:localhost:8008):
VERIFY_TOKEN=$(jq -r .verify_token admin.json)
# From the alice account in any Matrix client, DM this text to the bot:
# cumments-claim:$VERIFY_TOKEN
After verification the admin holds power 100 in the site Space and every comment room, and can appoint managers and per-room moderators (see site governance).
6. Verify¶
- Open the demo frontend (
misc/demo/index.html) againsthttp://localhost:7931and post a comment. - In Matrix, check that a Space (
Comments: <site>), a comment room (Comments: <site>/<page>), and the virtual user were created, and that the admin account appears with power 100 in both. - The comment should appear in the frontend in real time via SSE.
If comments exist in Matrix but not in the API, rebuild the read model from Matrix history:
docker compose exec cumments cumments backfill
Optional: room version 12¶
Room version 12 hardens rooms (hash-based room IDs, immutable creator power).
On tuwunel, set TUWUNEL_DEFAULT_ROOM_VERSION: "12" in the compose
environment, or request v12 per room with
CUMMENTS__MATRIX__APPSERVICE__ROOM_VERSION: "12" (see
configuration.md).
Existing comment rooms on an older room version can be upgraded without recreating the site (the target version must be newer than the room's current version):
docker compose exec cumments cumments rooms upgrade <room_id> 12
Pre-v12 rooms work too: new Cumments rooms give the bot explicit tombstone power (150) so it can perform the upgrade. Legacy pre-v12 rooms whose bot is only 100 have no in-product upgrade path.
The homeserver moves the room alias to the replacement, Cumments re-adopts
the new room, re-links it into the site Space, re-invites site roles, and
supersedes the old room. The same operation is available to operators through
!cumments room <room_id> upgrade <version> --confirm and
POST /api/v1/operator/rooms/{room_id}/upgrade; site admins can trigger it
themselves with !cumments site <site_id> page <page_slug> upgrade <version>
--confirm or POST /api/v1/sites/{site_id}/pages/{page_slug}/upgrade
(claim token). Every path executes the upgrade as the bot, which stays the
new room's creator. Because several surrounding standards are still open
(MSC4168/MSC4433), the current behavior includes a few documented
compromises — see Architecture.
Troubleshooting¶
- Compose refuses to start: the
./registration.yamlbind mount does not exist yet. Run step 2 first. as_token/hs_tokenmismatch: the values in the compose file do not match the registration YAML. Regenerate both together, or copy them from the stderr output of step 2.- Cumments logs
invalid hs_token: the token registered on the homeserver differs fromCUMMENTS__MATRIX__APPSERVICE__HS_TOKEN; fix and restart both services. - Comments exist in Matrix but not in the API: the push queue was blocked
or a transaction was never acked; restart the service and, if needed, run
cumments backfill. - The server name changed after first start: tuwunel stores the server name
in its database and cannot change it later. Remove the
tuwunel-datavolume (docker compose down -v) and start over.