尚未检测到更新。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.