Getting Started
This guide covers running your own Colibri AppView and pointing a client at it. It does not cover deploying the colibri.social website.
What you’ll run
Section titled “What you’ll run”An AppView deployment is three services, plus a PDS:
- The AppView itself (Rust/Rocket), which serves the XRPC API on port
8000. - Tap, an ATproto firehose consumer that handles backfill and streams live events to the AppView.
- A single PostgreSQL database that both Tap and the AppView share.
- A PDS, which hosts the repository of every community created on your instance. Either one you already run, or the reference one in
docker-compose.pds.yml.
In production, docker-compose.yml pulls the published release image and runs the first three together. For local development you run Postgres and Tap from the docker-compose.dev.yml compose file and the AppView with cargo run. See the AppView architecture page for how these fit together and for RAM/CPU sizing guidance, and Development if you’re working on Colibri rather than deploying it.
Prerequisites
Section titled “Prerequisites”- A PDS you control, plus its admin password. This is required: communities are AT Protocol accounts, and the AppView creates and hosts them on this PDS. Community creation, deletion and migration all fail without it. See The PDS below.
- A domain with TLS, e.g.
appview.example.com. This is not optional: the AppView identifies itself with adid:webderived from a hostname, it serves its DID document at/.well-known/did.json, and user PDSs proxy authenticated requests to it over HTTPS. A bare IP will not work. - Docker and Docker Compose.
- Rust
1.96+, if you build locally withcargo run - A Linux server meeting the recommended specs. Linux is the only supported and tested platform.
Run it locally
Section titled “Run it locally”-
Clone the repository and create your config from the template:
Terminal window git clone https://github.com/colibri-social/appview.gitcd appviewcp .env.example .env -
Fill in the .env file, then start the dependencies (Postgres and Tap) with the dev compose file:
Terminal window docker compose -f docker-compose.dev.yml up -
Run the AppView. Database migrations run automatically on boot:
Terminal window cargo runIt listens on
http://127.0.0.1:8000; API routes live under/xrpc/.
Deploy to production
Section titled “Deploy to production”The production docker-compose.yml pulls the published release image (ghcr.io/colibri-social/appview:latest, built from the repository’s Dockerfile) and runs the AppView alongside Postgres and Tap, reading the same .env.
-
Fill in
.env(see Configuration), then bring the whole stack up:Terminal window docker compose up -d# ...or, to run the reference PDS in the same stack (this file is# docker-compose.yml plus a `pds` service, so use it instead of that one):docker compose -f docker-compose.pds.yml up -d -
Put the AppView behind a TLS-terminating reverse proxy (Caddy, nginx, Traefik, etc.) on your domain, forwarding to port
8000. -
Confirm your DID document resolves at your domain and lists the
#colibri_appview,#colibri_notif, and#colibri_humservice endpoints:Terminal window curl https://appview.example.com/.well-known/did.json -
Confirm the AppView identifies itself correctly:
Terminal window curl https://appview.example.com/xrpc/social.colibri.server.describeServerYou should get back JSON with
"software": "colibri-appview".
The PDS
Section titled “The PDS”Communities are not rows in the AppView’s database. Each one is an AT Protocol account with its own repository, and the PDS at PDS_LOC is what hosts them. social.colibri.community.create mints an invite code and then an account there using PDS_ADMIN_PASS, stores the resulting credentials encrypted, and writes the community’s records to the new repo. Deletion and migration use the same admin access.
PDS_LOC is checked at boot but only used by those endpoints, so a misconfigured PDS doesn’t stop the AppView from starting. It instead fails the first time somebody tries to create a community.
If you don’t already run a PDS, the AppView repository ships docker-compose.pds.yml, docker-compose.yml plus a pds service running the official Bluesky PDS preconfigured for this: admin password shared with the AppView, handle domain derived from APPVIEW_HANDLE_DOMAIN, invite-gated account creation, blobs on a named volume. Run it instead of the plain compose file. It needs PDS_HOSTNAME, PDS_JWT_SECRET and PDS_PLC_ROTATION_KEY in your .env (the file itself documents how to generate the keys), and two things you have to arrange yourself:
- TLS on
PDS_HOSTNAME. The PDS publisheshttps://${PDS_HOSTNAME}as the service endpoint in every community DID document it registers with the PLC directory, so that’s the URL everyone else will resolve. The compose file publishes the container on127.0.0.1:3000by default, for a reverse proxy on the same host, setPDS_BIND_ADDR=0.0.0.0or attach the service to your proxy’s network if the proxy lives elsewhere. - Wildcard DNS for community handles. Handles like
c-3lk2....${APPVIEW_HANDLE_DOMAIN}are answered by the PDS, so*.${APPVIEW_HANDLE_DOMAIN}must resolve to it under a certificate that covers it. The simplest correct setup isAPPVIEW_HANDLE_DOMAINequal toPDS_HOSTNAME.
Back up PDS_PLC_ROTATION_KEY. It’s the key that can update the DID documents of every community hosted on that PDS, and losing it is not recoverable.
For running one on a laptop, where a separate docker-compose.pds.dev.yml pairs a localhost PDS with its own private PLC directory, see Running a PDS.
Configuration
Section titled “Configuration”Configuration is read from environment variables (via a .env file in the repo root). The ones you’re most likely to touch:
| Variable | Required | Purpose |
|---|---|---|
APPVIEW_DID |
Yes | The did:web this instance identifies as, e.g. did:web:appview.example.com. Defaults to did:web:api.colibri.social, so a self-hosted instance must set it. |
DATABASE_URL |
Yes | Connection string for the shared Postgres database. |
K256_PRIVATE_KEY |
Yes | secp256k1 private key used to sign ATproto operations, service-auth tokens, and the DID document. Hex-encoded 32-byte key (64 hex chars, no 0x). Generate one with openssl rand -hex 32 |
PDS_LOC |
Yes | URL of the PDS where the AppView creates and hosts community repositories. See The PDS. |
PDS_ADMIN_PASS |
Yes | Admin password for that PDS, so the AppView can create community accounts. |
APPVIEW_HANDLE_DOMAIN |
Yes | Domain communities get their handles from (a community example becomes example.${APPVIEW_HANDLE_DOMAIN}). *.${APPVIEW_HANDLE_DOMAIN} must resolve to the PDS. |
PDS_HOSTNAME / PDS_JWT_SECRET / PDS_PLC_ROTATION_KEY |
Only for the reference PDS | Read by the PDS compose files, not by the AppView. Ignored if you point PDS_LOC at a PDS you already operate. |
CREDENTIAL_ENCRYPTION_KEY |
Yes | Base64-encoded 32-byte key used to encrypt stored community credentials at rest. Generate one with openssl rand -base64 32 |
TAP_HOSTNAME / TAP_ADMIN_PASSWORD |
Yes | Where the AppView reaches Tap, and Tap’s admin password. |
VAPID_PUBLIC_KEY / VAPID_PRIVATE_KEY / VAPID_SUBJECT |
No | Web Push keypair for background notifications. Unset disables background push. Generate a keypair with npx web-push generate-vapid-keys |
KLIPY_API_KEY |
No | Enables the GIF picker endpoints. Unset disables them. |
HUMMING_ENABLED |
No | Cross-instance presence relaying. On by default, set to false to opt out entirely. |
SFU_ANNOUNCED_IP |
No* | Public IP announced in the voice SFU’s ICE candidates. Required behind NAT (most cloud hosts) or clients can’t reach the media ports. See Voice below. |
.env.example documents the remaining options. Prefer it for other variables and explainers, the table above only covers the essentials.
Community voice/video runs on an SFU (mediasoup) embedded in the AppView. A few things to get right when self-hosting:
- Announced IP & media ports. WebRTC media flows directly to the AppView host, not through the reverse proxy. Set
SFU_ANNOUNCED_IPto your server’s public IP, and open the port rangeSFU_RTC_MIN_PORT-SFU_RTC_MAX_PORT(default40000-40100) on your firewall for both UDP and TCP (the SFU falls back to ICE-over-TCP when UDP is blocked). The productiondocker-compose.ymlalready publishes this range on the host from those same variables. Without this, users connect but hear no audio. - TURN for restrictive networks. A direct path to the media ports fails for clients behind symmetric NAT or strict firewalls. Point them at a TURN server via
SFU_ICE_SERVERS, a JSON array ofRTCIceServerobjects, e.g.[{"urls":["turn:turn.example.com:3478"],"username":"user","credential":"pass"}]. The AppView hands these to every client at call setup. Run your own coturn or use a managed TURN provider. Unset means direct connection only.
Connecting a client
Section titled “Connecting a client”The client isn’t tied to any particular AppView. In a running client, open the user settings, then Preferences and set the AppView URL to your instance (https://appview.example.com). The client probes describeServer, accepts the host only if it reports software: "colibri-appview", and then re-authenticates so your session is scoped to the new AppView.
Authentication scopes are pinned to the AppView’s did:web and cannot use a wildcard, so a client will only authorize AppViews it has been configured to trust. That means you generally can’t point an arbitrary hosted client at a brand-new self-hosted AppView and log in. Building and hosting that client UI is out of scope for this guide, the shared UI is published as the @colibri-social/client library (see the client architecture page).
Federation between instances
Section titled “Federation between instances”If your community’s members are spread across more than one AppView, off-protocol signals (online status, typing, voice presence) are relayed between instances by Humming, which is on by default. To run a fully isolated instance that never talks to other AppViews, start with HUMMING_ENABLED=false. See the Humming overview for more information.
See also
Section titled “See also”- AppView Architecture: the services, sizing, and how they connect.
- Client Architecture: how the client talks to an AppView and how it’s packaged.
- AppView Specification: the full XRPC endpoint and event reference.
- Development: running the stack locally to work on it.
- Running a PDS: the PDS requirement in detail.