Getting Started
This guide covers running your own Colibri AppView. It does not cover deploying the colibri.social website.
What you’ll run
Section titled “What you’ll run”- The AppView, which serves the XRPC API, the two WebSockets and the voice SFU in one process.
- A PDS running the spaces alpha, which hosts every community account your
instance creates. Either one you already run, or the one in
docker-compose.pds.yml.
Storage defaults to libSQL against a file, so there is no database service to run. Postgres is available as an overlay if you would rather use it.
Prerequisites
Section titled “Prerequisites”- A PDS you control running the spaces alpha
(
ghcr.io/bluesky-social/atproto:pds-spaces-alpha), plus its admin password. This is required: communities are AT Protocol accounts and the AppView creates them there. A stock PDS will not do, because it does not implementcom.atproto.simplespace. - A domain with TLS, for example
appview.example.com. The AppView identifies itself with adid:webderived from a hostname, serves its DID document at/.well-known/did.json, and both clients and community PDSs reach it over HTTPS. A bare IP will not work. - Wildcard DNS for community handles. Communities get handles under
COMMUNITY_HANDLE_DOMAIN, answered by the PDS, so*.${COMMUNITY_HANDLE_DOMAIN}must resolve to it under a covering certificate. - Docker and Docker Compose, or Node 24 and pnpm to run it directly.
- A Linux server. It is the only tested platform.
Run it
Section titled “Run it”-
Clone the repository and create your config:
Terminal window git clone https://github.com/colibri-social/api.gitcd appviewcp .env.example .env -
Fill in
.env. Six values have no default and the AppView refuses to boot without them:APPVIEW_DID,PUBLIC_URL,SIGNING_KEY,CREDENTIAL_ENCRYPTION_KEY,PDS_URLandCOMMUNITY_HANDLE_DOMAIN. The file carries theopenssllines that generate the two secrets. -
Build and start it. There is no published image, so compose builds one from the
Dockerfilein the repository. Expect the first build to take a few minutes.Terminal window docker compose up -d --buildThat runs the AppView alone on libSQL, against the
appview-datavolume, pointed at whateverPDS_URLnames. Rebuild with the same command after pulling new commits, since nothing updates the image for you. -
Put it behind a TLS-terminating reverse proxy on your domain.
-
Confirm your DID document resolves and lists
#colibri_appviewand#atproto_space_syncer:Terminal window curl https://appview.example.com/.well-known/did.json -
Confirm it identifies itself:
Terminal window curl https://appview.example.com/xrpc/social.colibri.beta.server.describeServer
Compose overlays
Section titled “Compose overlays”docker-compose.yml is the base. Three files compose on top of it:
| Overlay | What it adds |
|---|---|
docker-compose.postgres.yml |
Postgres, and rewrites DATABASE_URL to use it |
docker-compose.pds.yml |
A local spaces-alpha PDS and a private PLC directory, for developing without a PDS of your own |
docker-compose.integration.yml |
The same pair, ephemeral, for the integration suite |
docker compose -f docker-compose.yml -f docker-compose.pds.yml upThe spaces-alpha PDS is published for amd64 only, so it runs under emulation on an arm64 host. The PLC
directory is built from docker/plc rather than pulled, because every published
PLC image dates from 2023 and predates the operation format this PDS emits.
Configuration
Section titled “Configuration”The full list is in .env.example. These are the ones you are likely to touch:
| Variable | Required | Purpose |
|---|---|---|
APPVIEW_DID |
Yes | The did:web this instance answers as, for example did:web:appview.example.com |
PUBLIC_URL |
Yes | Where this AppView is reachable from the public internet |
SIGNING_KEY |
Yes | secp256k1 private key, 64 hex characters, published in the DID document |
CREDENTIAL_ENCRYPTION_KEY |
Yes | 32 bytes base64, encrypts community passwords at rest |
PDS_URL |
Yes | The spaces-alpha PDS where communities are created |
COMMUNITY_HANDLE_DOMAIN |
Yes | Domain community handles live under. *. of it must resolve to the PDS |
PDS_ADMIN_PASSWORD |
No | Without it the AppView serves reads but cannot create a community or repair one whose password stopped working |
DATABASE_URL |
No | Defaults to file:./data/colibri.db. A postgres:// URL switches driver |
CORS_ORIGINS |
No | Browser origins allowed to call /xrpc, comma-separated, or * for any. Unset, production allows none, so a client on another origin needs it |
SYNC_WORKERS, SYNC_SWEEP_SECONDS |
No | Sync worker pool size and sweep interval |
SERVICE_AUTH_MAX_LIFETIME_SECONDS |
No | Rejects service auth tokens whose expiry is further away than this |
JETSTREAM_URL |
No | Jetstream v2 host. Only identity and account events are consumed |
VAPID_*, FCM_SERVICE_ACCOUNT_JSON |
No | Web Push and Android push. Unset disables background notifications |
KLIPY_API_KEY |
No | Enables the GIF picker |
SFU_ANNOUNCED_IP |
No | Public IP announced in the SFU’s ICE candidates. Required behind NAT |
Voice runs on a mediasoup SFU inside the AppView process. WebRTC media flows directly to the host rather than through your reverse proxy, and it needs two things arranged for it:
- Announced IP and media ports. Set
SFU_ANNOUNCED_IPto your public IP and openSFU_RTC_MIN_PORTthroughSFU_RTC_MAX_PORT(40000 to 40100 by default) for both UDP and TCP, because the SFU falls back to ICE over TCP when UDP is blocked. Without this, users connect and hear nothing. - TURN for restrictive networks. Clients behind symmetric NAT cannot reach
the media ports directly. Point them at a TURN server with
SFU_ICE_SERVERS, a JSON array ofRTCIceServerobjects. Unset means direct connection only.
Connecting a client
Section titled “Connecting a client”Open user settings, then Preferences, and set the AppView URL to your
instance. The client probes
describeServer,
accepts the host only if it reports software: "colibri-appview", and
re-authenticates so your session is scoped to the new AppView.
Authentication scopes are pinned to an AppView’s did:web and cannot use a
wildcard, so a client only authorises AppViews it was configured to trust. You
generally cannot point an arbitrary hosted client at a brand-new self-hosted
AppView and sign in. The shared UI is published as @colibri-social/client.
See also
Section titled “See also”- AppView Architecture: what you are running.
- Permissioned Spaces: the credential exchange that makes reachability a requirement.
- Sync: what the sweep interval controls.
- Development: running the stack to work on it.