Self-host the server
outbrief-server does one thing: it relays ciphertext between your computer and your devices. It has no key and cannot read reports, briefs, or replies. It deletes ciphertext when a call ends, and it never calls an LLM.
Deploy
Section titled “Deploy”You need Node ≥ 22.18, pnpm 9, and MySQL 8.
-
Get the code and install dependencies:
终端窗口 git clone https://github.com/outbriefapp/outbrief-servercd outbrief-serverpnpm install -
Start it. The deployment injects the connection string; the user and password below are placeholders:
终端窗口 OUTBRIEF_DATABASE_URL='mysql://USER:PASSWORD@HOST:3306/outbrief' PORT=8787 pnpm startTables are created and migrated on startup.
-
Find the claim code in the log and write it down:
Claim code: XXXX-XXXX-XXXX -
Health check:
GET /healthz
The repo’s railway.json sets the health check and restart policy. Set the rest in Railway:
- Add a MySQL 8 service (image
mysql:8.4, volume on/var/lib/mysql). Private network only; no public TCP proxy. - Set the server’s variables:
OUTBRIEF_DATABASE_URLreferencing${{MySQL.MYSQL_URL}}. Never write the connection string into the repoPORT=8787OUTBRIEF_FCM_SERVICE_ACCOUNTfor call pushes (see below)
- Set replicas to 1 and attach a volume to the server (for example
/data; the code never touches it). Railway does not let two deployments hold the same volume, so a release stops the old one before starting the new one, which is exactly the single-instance rule. - For a custom domain, add the CNAME and
_railway-verifyTXT records Railway gives you. On Cloudflare, keep it DNS only (gray cloud) so Railway can issue the certificate.
Who can create an account
Section titled “Who can create an account”| Deployment | Setting | Behavior |
|---|---|---|
| Private (default) | OUTBRIEF_OPEN_SIGNUP=false |
The first device enters the claim code from the log. After that, signup closes and other devices join with pairing codes |
| Public cloud | OUTBRIEF_OPEN_SIGNUP=true |
Anyone can create an account, no claim code, rate limited |
Environment
Section titled “Environment”Nothing is required, and there is no .env file to create.
| Variable | Default | Meaning |
|---|---|---|
OUTBRIEF_DATABASE_URL |
The local docker outbrief database |
MySQL connection string, injected by the platform |
PORT |
8787 |
Listen port |
OUTBRIEF_OPEN_SIGNUP |
false |
Open signup, see above |
OUTBRIEF_FCM_SERVICE_ACCOUNT |
Unset | Firebase service account key for call pushes, as the whole JSON or a file path. If set but unreadable, the server fails to start |
Call pushes (optional)
Section titled “Call pushes (optional)”Without pushes, calls arrive over the event stream while the app is open. To ring an Android phone after the app is swiped away, set up pushes:
- In the Firebase console, create a project and add an Android app with package
ai.outbrief.app. - Put that project’s service account key (the whole JSON) in the server’s
OUTBRIEF_FCM_SERVICE_ACCOUNT. - Build the Android app with the same project’s
google-services.json; see Android package.
A push carries no content, only the call id. The phone then fetches the ciphertext with its own token, decrypts, and rings. Without a receipt the server pushes again every 10 seconds, up to 3 times. Design: ADR 0010.
- Run it locally for development: Development and builds
- Every endpoint: Server API (Chinese)