尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證Stable Discord Analog
翻譯準備中
取自儲存庫 README
正在載入完整 README。 以下為已儲存的摘錄。
Living chat — your server, your rules.
Early Alpha. Outcome is under active development and not yet production-hardened.
Don't use it for sensitive communications yet. Issue reports are very welcome — file them here.
Outcome is a self-hosted, Discord-style chat platform that runs entirely on your
hardware — no cloud dependency, no telemetry, no paid tiers. This repository is the
deployment kit: one compose file + prebuilt Docker images. You don't build anything
from source, and a working instance is one docker compose up -d away.檢視完整 README ↗Living chat — your server, your rules.
🇷🇺 Читать по-русски → README.ru.md
Stable. Outcome runs in production every day — on outcome.ru and in companies' own spaces — and releases land weekly. Found a bug? Open an issue here; it gets read.
Outcome is a self-hosted, Discord-style chat platform that runs entirely on your
hardware — no cloud dependency, no telemetry, no paid tiers. This repository is the
deployment kit: one compose file + prebuilt Docker images. You don't build anything
from source, and a working instance is one docker compose up -d away. The source is free
software (AGPL-3.0).
Try it first, without installing anything: make a free account at outcome.ru — the same app, hosted by us. Or go straight to the Quick start.
Everything around the two Outcome images is stock open source pulled from public registries:
| Component | Role |
|---|---|
skayloo/outcome-server | Outcome API (.NET) — REST, WebSocket realtime, voice signaling |
skayloo/outcome-frontend | Outcome web app (React) behind nginx |
| caddy-docker-proxy | Edge TLS, auto Let's Encrypt |
| PostgreSQL 16 + PgBouncer | Database + connection pooling |
| Redis | Realtime fan-out across API replicas |
| Garage | File storage (S3) for uploads |
| LiveKit | Voice/video SFU (WebRTC) |
A Linux host (x86_64) with a public IP. 2 vCPU / 4 GB RAM is a comfortable start.
Docker Engine 24+ with the compose plugin (docker compose version should work).
A domain you control.
Open (or forwarded, if behind NAT) ports:
| Port | Why |
|---|---|
| 80, 443 tcp (+ 443 udp) | Web, API, WebSocket, HTTP/3 |
| 7881 tcp | Voice/video media — TCP fallback |
| 7882 udp | Voice/video media — main path |
| 5349 tcp | TURN relay over TLS, for networks that block the two above (TURN_TLS_PORT) |
Point these records at your server's IP before starting (Caddy needs them resolvable to obtain certificates):
| Record | Value |
|---|---|
A @ (or your subdomain) | your server IP |
A www | your server IP |
A turn | your server IP (the TURN relay's certificate) |
git clone https://github.com/Skayloo/Outcome.git
cd Outcome
cp .env.example .env
nano .env # fill everything — the file explains each value and how to generate secrets
docker compose up -d
First boot takes a minute or two: Postgres initializes, migrations run (safe with multiple API replicas — they serialize behind an advisory lock), Caddy obtains certificates.
Then open https://your-domain — the first-run wizard creates the owner account and
hands you an unlimited invite code for your friends. Registration is open by default; an
invite code puts the new account straight into your server. Make registration invite-only,
or close it, in the admin console (/admin → Server settings).
Forgotten passwords. The server sends no e-mail, so there is no "reset by e-mail" link:
an administrator sets a new password in /admin → Users → the person's menu → "Set
password". It signs the person out everywhere; give them the new password yourself.
The mobile app connects to your instance too — enter your domain in the "Change server" field on its login screen.
LIVEKIT_NODE_IP in .env must be the server's public IP — it's advertised to
browsers for the media connection (media bypasses the HTTP proxy entirely).7882 open = good calls. If UDP is blocked, media falls back to TCP 7881.The bundled Caddy wants ports 80 and 443 for itself. If another reverse proxy already holds
them, switch Caddy off and publish the web app on a local port instead — create
docker-compose.override.yml next to docker-compose.yml:
services:
caddy:
profiles: ["off"] # not started
frontend:
ports:
- "127.0.0.1:8080:80"
Then point your proxy at http://127.0.0.1:8080 for your-domain, with WebSocket
upgrades allowed and request bodies up to 110 MB. For example, Caddy:
your-domain {
request_body { max_size 110MB }
reverse_proxy 127.0.0.1:8080
}
or nginx (inside your server { … } for the domain, with its certificate):
client_max_body_size 110m;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 3600s;
}
If the proxy itself runs in Docker, publish no port: put the web app on the proxy's network
and point the proxy at http://outcome-web:80 instead —
services:
caddy:
profiles: ["off"]
frontend:
networks:
default: {}
proxy:
aliases: ["outcome-web"]
networks:
proxy:
external: true
name: your-proxys-network # docker network ls
(caddy-docker-proxy needs nothing more: the web app already carries the labels it reads.)
Voice still goes straight to LiveKit on 7881/7882, not through the proxy. The TURN relay
on 5349 starts only once the bundled Caddy has a certificate for turn.your-domain, so
with Caddy off it stays off and calls use 7881/7882 alone.
docker compose pull
docker compose up -d
Database migrations run automatically on API start. Pin OUTCOME_TAG in .env if you
prefer explicit version bumps over latest.
Files used to live in MinIO, which no longer publishes its images; they now live in
Garage. An install that has run MinIO still has its image and
its minio_data volume — copy the files across once:
git pull # the kit: compose file and garage.toml
# add GARAGE_RPC_SECRET, GARAGE_ACCESS_KEY and GARAGE_SECRET_KEY to .env (commands at the top of
# .env.example) — and keep your MINIO_ACCESS_KEY / MINIO_SECRET_KEY lines for now
docker compose up -d garage
docker compose --profile migrate run --rm storage-migrate # copies every file, deletes nothing
docker compose up -d # the server now uses Garage
docker compose --profile migrate rm -sf minio
Check that pictures and files open, then remove the old volume
(docker volume rm <project>_minio_data, see docker volume ls) and the two MINIO_* keys.
All state lives in named Docker volumes: pgdata (database), garage_data and garage_meta (uploaded
files), caddy_data (certificates).
Consistent database dump without stopping anything:
docker compose exec postgres pg_dump -U outcome -Fc outcome > outcome-$(date +%F).dump
Also back up your .env.
The API is stateless and already runs replicated (SERVER_REPLICAS) behind the built-in
routing, with PgBouncer multiplexing database connections and Redis fanning out realtime
events — raising the replica count on a bigger box is usually all you need.
Outcome stands on excellent open source. Besides the stack components listed above, special thanks to pabloFuente/livekit-server-sdk-dotnet — the .NET server SDK for LiveKit that powers Outcome's voice tokens, room management and webhooks.
The contents of this repository (compose file, configuration, documentation) are MIT-licensed — see LICENSE.
The outcome-server and outcome-frontend images are built from
Outcome-source-blue, which is free software
under the GNU Affero General Public License v3 (AGPL-3.0-only): run it for any purpose,
commercial included, change it, build your own images. If you let people use a modified version
over a network, offer them its source.