| .. | ||
| .env.example | ||
| backup.sh | ||
| docker-compose.yml | ||
| README.md | ||
| restore.sh | ||
Crew-owned Cordn coordinator
This kit runs a Cordn MLS delivery-service coordinator that a crew owns. It is transport only: the coordinator sees ciphertext and routing metadata (who, when, which group), never message plaintext. It can withhold or delay messages; it cannot forge members' messages.
This is a server kit. SkateSpots remains a Cordn client; it does not embed a coordinator in the APK.
What you need
- A small Linux server or always-on home machine with Docker + Compose.
- Outbound HTTPS/WSS access. No inbound port is required — the coordinator connects out to its ContextVM relay(s).
- A domain is not required for the coordinator itself.
- A crew willing to trust the operator with availability and metadata.
1. Install
cp .env.example .env
chmod 600 .env
openssl rand -hex 32
Put the generated value into CORDN_SERVER_PRIVATE_KEY. Set
CORDN_RELAY_URLS to the relay(s) you want; the default is ContextVM’s public
relay. Set a useful CORDN_SERVER_NAME.
docker compose up -d
docker compose logs -f cordn
The server prints its coordinator public identity/add link. It may show an
nprofile or a cordn.net/chat/coordinators?c=… link.
If your build does not print an add link, derive the public key from the private
key using the Cordn CLI or nostr-tools, then encode an nprofile with the same
relay hints. Keep the private key on the server.
2. Adopt it in SkateSpots
Either:
- Settings → Coordinator → paste the
nprofile/add-link → Add; or - Crew edit → Coordinator → paste it and save; or
- scan the coordinator QR.
Use exactly the relay hints from the server. A mismatch produces an honest “Not seen” health state even when the server is actually running.
The coordinator should show Online (or “Stale” if its heartbeat is old). “Offline” means presence was not found, not necessarily that the service is broken.
3. What the crew should know
- The coordinator can censor or drop messages.
- It cannot read MLS plaintext.
- It can see who, when and which group.
- It can disappear; local messages remain on members' devices, but delivery pauses until the coordinator returns or the crew switches.
- The operator should not also become a crew admin automatically.
- A loss of the database is a loss of delivery history; back it up.
4. Backup
./backup.sh
Backups land in ./backups/ as a compressed copy of the coordinator volume.
Store them off the server, encrypted.
5. Restore
Stop the coordinator, restore the archive into the cordn-data volume, start it
again:
docker compose stop cordn
./restore.sh backups/cordn-YYYYMMDD-HHMMSS.tgz
docker compose start cordn
6. Upgrade
docker compose pull
docker compose up -d
The image is pinned only by the latest tag. If you need reproducible upgrades,
pin a digest and record it in your crew runbook.
7. Move to a new coordinator
MLS delivery state is tied to the coordinator’s database and identity. Before switching:
- back up the current coordinator;
- export/retain the crew’s local chat state on members’ devices;
- start the new coordinator;
- paste its
nprofileinto the crew record; - have members reopen the private chat and re-establish delivery;
- update the crew operator/notice.
Do not claim the switch is seamless unless the old and new coordinator state have actually been migrated.
Security notes
- Never commit
.env; it contains the coordinator signing key. - The coordinator key is not a crew admin key and must not be reused as one.
- Run it behind a trusted host/network; outbound relay traffic is metadata.
- Keep Docker and the image current.
- Do not expose an OpenCode or coordinator admin port publicly.
- This is not a BFT or multi-coordinator system. One coordinator is a delivery service under the crew's social trust.
Files
docker-compose.yml— persistent SQLite deployment..env.example— required environment.backup.sh/restore.sh— volume snapshots.