Three steps: run a hub, become the keymaster, install the client on every phone and laptop that needs it. Plan on about an hour if you’re comfortable at a terminal, longer if it’s your first server.
Cove is self-hosted. Every group runs its own hub — a small Python server that holds the tamper-evident log for that group and nobody else’s. Nothing about your group’s messages ever touches infrastructure the Cove project controls.
The easiest way to run one is Docker on a small Linux VM (a $5/month box is more than enough for a board of 20). If you already run other services on a Cloudflare Tunnel, Cove drops in the same way.
docker and docker compose. You do not need Python, a virtualenv, or pip install. The bootstrap ceremony runs inside the freshly-built container, which brings its own Python and deps.
Since v0.4.76 there’s a single script that handles the whole ceremony — container tear-down, state wipe, image rebuild, bootstrap, key-custody halt, and container start — with a SHA-verified handoff prompt so the root key can’t leave the ceremony half-done. Use it:
git clone https://github.com/cloudseeder/cove.git
cd cove
# One command per hub. The name you pass becomes the Compose
# project name, and drives the defaults for state dir, container
# name, and public hostname.
./scripts/genesis.sh myorg
The name you pass (myorg, lwccoa, whatever) becomes the identifier the script uses everywhere — state directory ./myorg-state, container name myorg-hub, default public hostname myorg-hub.oap.dev. Override any of these with flags if the defaults don’t fit your setup:
./scripts/genesis.sh flhoa \
--port 8002 \
--hostname flhoa-hub.oap.dev \
--org-name "FL HOA" \
--keymaster "Jane Smith"
Run ./scripts/genesis.sh --help for the full flag list. Notably, --reuse-pubkey <hex> attests an existing pubkey against the fresh hub root instead of generating a new keypair — that’s the “same identity, N hubs” federation entry point.
The script pauses after generating keys and prints SHA-256 fingerprints for root.priv, root.pub, and the initial member’s .priv/.pub. You paste each into offline storage (password manager, encrypted USB, paper), sha256sum each on the receiving machine, and confirm to the script — only then does it shred the priv files from the host and start the container. The hub refuses to start if root.priv is still on disk.
--port so they don’t collide.
# Verify:
curl http://127.0.0.1:8000/healthz
# {"status":"ok","version":"..."}
Point a domain at the container (Cloudflare Tunnel or Caddy both work — walked through in the docker README) and you have a running hub.
If you’re already running a Cloudflare Tunnel, scripts/tunnel_add.sh automates the ingress-rule + DNS-CNAME + reload step for a new hub:
sudo ./scripts/tunnel_add.sh <hostname> <port>
# e.g.
sudo ./scripts/tunnel_add.sh flhoa-hub.oap.dev 8002
Reads your tunnel name from /etc/cloudflared/config.yml, creates the CNAME via cloudflared tunnel route dns, inserts the ingress block above your http_status:404 catch-all, reloads cloudflared, and verifies with a /healthz check on the public URL. Idempotent — running twice is a no-op after the first success.
The bootstrap ceremony created one member for you: the keymaster. That’s the person who mints invite codes for everyone else, approves new members, and holds the group’s custodial responsibility. It’s almost always you if you set up the hub.
Open the PWA at app.cove.oap.dev (or install it to the dock — instructions in Step 3). At the landing screen, expand “Signing in from a new device? Use your Cove vault” and paste the keymaster’s .pub key you saved offline during Step 1. You won’t have a vault yet on a fresh install — the sign-in will fail. Fall back to “Get started” and choose the paste-an-existing-key option; paste both the .priv and .pub to sign in for the first time.
Once you’re authenticated, open Admin → Identity vault → Create vault with passphrase. Pick a strong passphrase, label it “Backup passphrase”, and click Create. The hub now holds an encrypted copy of your key that only your passphrase can unlock. On every future device you use, you’ll sign in with just your public key + passphrase — no more file-passing.
From there, the rest of the group onboards through invite codes you mint from the Admin panel. No more terminal work.
Cove has three client surfaces at different levels of polish. Pilot-era honesty: the desktop app is the best-supported, the PWA is the everyday-mobile answer, native mobile is not yet built.
Signed and notarized. Download the .dmg from GitHub Releases, open, drag to Applications.
ReadyUnsigned Tauri builds attached to every GitHub release. Works, but expect Gatekeeper/SmartScreen warnings.
UnsignedNot built yet. The PWA covers this well enough that native isn’t urgent.
Later/Applications/Cove.app to the trash first, then Safari’s File → Add to Dock… will offer to install the PWA properly.
Cove’s identity vault (v0.4.76+) turns your keypair into something you can carry to every phone, laptop, and tablet you use — without ever passing the priv file around. Once you’ve created a vault (Step 2), you sign in on a new device in three fields:
That’s it. No fresh invite, no re-attestation. The vault ciphertext is fetched from your hub, unwrapped locally with whichever unlock method you picked, and your session opens with the same identity you had on device #1.
A Passkey is a biometric-backed credential your device holds — Face ID or Touch ID on Apple, fingerprint or PIN on Android or Windows. It’s the same technology 1Password, Google, and Apple use for password-less sign-in. In Cove, you register a Passkey under Admin → Identity vault → Add Passkey. Every device tied to the same Passkey ecosystem (all your Apple devices sharing one iCloud, or all your Android devices sharing one Google account) gets one-tap sign-in.
Passkeys don’t sync between iCloud and Google Password Manager — that’s an industry limitation, not a Cove one. The vault closes the gap: on each ecosystem you add a fresh Passkey (which stays inside that ecosystem’s sync) plus the shared passphrase. iPhone unlocks with iCloud-synced Face ID; Pixel unlocks with Google-synced fingerprint; Windows laptop unlocks with the passphrase. All three end up holding the same Ed25519 identity because the vault has three different wrappers around one canonical CEK.
The vault is opaque to the hub — if you lose every unlock method, no one can help you decrypt it. Always keep at least one method the hub’s ecosystem can’t revoke: a passphrase in your password manager, a printed recovery-code slot (roadmap), or a hardware security key you keep in a drawer. The Identity vault section shows a warning banner when only one slot remains.
From here, the day-to-day is: mint invite codes for the rest of the group, hand them out out-of-band (text, in person, on paper), and start posting. Every message is signed, every delivery is receipted, every thread stays whole.
If you hit a wall, the source repo has issue templates. This is a small project by design — the response won’t be instant, but it will be real.