# 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 ```bash 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`. ```bash 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 ```bash ./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: ```bash docker compose stop cordn ./restore.sh backups/cordn-YYYYMMDD-HHMMSS.tgz docker compose start cordn ``` ## 6. Upgrade ```bash 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: 1. back up the current coordinator; 2. export/retain the crew’s local chat state on members’ devices; 3. start the new coordinator; 4. paste its `nprofile` into the crew record; 5. have members reopen the private chat and re-establish delivery; 6. 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.