feat: add fluxer upstream source and self-hosting documentation

- Clone of github.com/fluxerapp/fluxer (official upstream)
- SELF_HOSTING.md: full VM rebuild procedure, architecture overview,
  service reference, step-by-step setup, troubleshooting, seattle reference
- dev/.env.example: all env vars with secrets redacted and generation instructions
- dev/livekit.yaml: LiveKit config template with placeholder keys
- fluxer-seattle/: existing seattle deployment setup scripts
This commit is contained in:
Vish
2026-03-13 00:55:14 -07:00
parent 5ceda343b8
commit 3b9d759b4b
5859 changed files with 1923440 additions and 0 deletions

384
fluxer/SELF_HOSTING.md Normal file
View File

@@ -0,0 +1,384 @@
# Fluxer — Self-Hosting & VM Rebuild Guide
This document covers how to spin up the Fluxer dev instance from scratch on a new VM.
The running reference deployment is at `st.vish.gg` (seattle VM, `66.94.122.170` / `100.82.197.124`).
---
## Architecture overview
All services run as Docker containers via `dev/compose.yaml`, connected on a shared Docker network (`fluxer-shared`).
Caddy acts as the internal reverse proxy, routing all traffic on port `8088` to the appropriate service.
Cloudflared tunnels external HTTPS traffic (no open inbound ports required).
```
Internet
└── Cloudflare Tunnel (cloudflared)
└── Caddy :8088
├── /api/* → api:8080 (Node.js REST API)
├── /gateway/* → gateway:8080 (Erlang WebSocket gateway)
├── /media/* → media:8080 (media proxy)
├── /admin/* → admin:8080 (Elixir admin panel)
├── /marketing/* → marketing:8080 (Elixir marketing site)
├── /metrics/* → metrics:8080 (Rust metrics service)
├── /livekit/* → livekit:7880 (LiveKit voice/video)
├── /s3/* → minio:9000 (S3-compatible object storage)
└── /* → /app/dist (built frontend SPA)
```
### Services
| Container | Image/Build | Purpose |
|-----------|-------------|---------|
| `caddy` | `caddy:2` | Internal reverse proxy |
| `cloudflared` | `cloudflare/cloudflared` | Tunnel (no inbound ports) |
| `api` | `node:24-bookworm-slim` | REST API (`tsx watch`) |
| `worker` | `node:24-bookworm-slim` | Background job worker |
| `gateway` | `erlang:28-slim` | WebSocket gateway (compiled with rebar3) |
| `media` | build from `fluxer_media_proxy/` | Media proxy |
| `admin` | build from `fluxer_admin/` | Admin panel |
| `marketing` | build from `fluxer_marketing/` | Marketing site |
| `docs` | `node:24-bookworm-slim` | Docs site |
| `metrics` | build from `fluxer_metrics/` | Metrics aggregator |
| `postgres` | `postgres:17` | Primary database |
| `cassandra` | `scylladb/scylla:latest` | Time-series / message data |
| `redis` | `valkey/valkey:latest` | Cache / pub-sub |
| `minio` | `minio/minio` | S3-compatible object storage |
| `meilisearch` | `getmeili/meilisearch:v1.25.0` | Full-text search |
| `clamav` | `clamav/clamav:latest` | Virus scanning (can be disabled) |
| `livekit` | `livekit/livekit-server:latest` | Voice & video |
| `cassandra-migrate` | `debian:bookworm-slim` | One-shot DB migration runner |
---
## Prerequisites
- Debian/Ubuntu VM (tested on Debian 12 bookworm)
- Docker + Docker Compose plugin installed
- `git` installed
- A Cloudflare tunnel token (from Cloudflare Zero Trust dashboard)
- Domain pointed at Cloudflare (e.g. `st.vish.gg`)
- At least 4 vCPU / 8GB RAM recommended (ScyllaDB + ClamAV are hungry)
### Install Docker (if not present)
```bash
curl -fsSL https://get.docker.com | sh
systemctl enable --now docker
```
---
## Step-by-step rebuild
### 1. Clone the repo
```bash
git clone https://github.com/fluxerapp/fluxer /root/fluxer
cd /root/fluxer
```
### 2. Create the shared Docker network
Must be created before starting any containers — it's declared `external: true` in the compose file.
```bash
docker network create fluxer-shared
```
### 3. Build the frontend app
Caddy serves the pre-built SPA from `fluxer_app/dist/`. This must be built before starting Caddy,
otherwise it will mount an empty directory and serve nothing.
```bash
# Install pnpm
corepack enable pnpm
# Install workspace deps
pnpm install
# Build the app (outputs to fluxer_app/dist/)
cd fluxer_app
pnpm build
cd ..
```
> Note: The build requires Node 22+ and pnpm. If the VM doesn't have Node installed globally,
> run the build inside a container:
> ```bash
> docker run --rm -v $(pwd):/workspace -w /workspace/fluxer_app node:24-bookworm-slim \
> bash -lc "corepack enable pnpm && CI=true pnpm install && pnpm build"
> ```
### 4. Build the cassandra-migrate binary
The Cassandra migration runner is a Rust binary that must be pre-compiled.
```bash
# Install Rust if needed
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source ~/.cargo/env
# Build
cd scripts/cassandra-migrate
cargo build --release
cd ../..
```
The compose file expects the binary at `scripts/cassandra-migrate/target/release/cassandra-migrate`.
### 5. Set up the environment file
```bash
cp dev/.env.example dev/.env
```
Edit `dev/.env` and fill in:
| Variable | What to set |
|----------|-------------|
| `FLUXER_APP_ENDPOINT` | Your public URL e.g. `https://st.vish.gg` |
| `FLUXER_API_PUBLIC_ENDPOINT` | `https://st.vish.gg/api` |
| `FLUXER_GATEWAY_ENDPOINT` | `wss://st.vish.gg/gateway` |
| `FLUXER_MARKETING_ENDPOINT` | `https://st.vish.gg` |
| `FLUXER_ADMIN_ENDPOINT` | `https://st.vish.gg/admin` |
| `FLUXER_MEDIA_ENDPOINT` | `https://st.vish.gg/media` |
| `FLUXER_INVITE_ENDPOINT` | `https://st.vish.gg` |
| `CLOUDFLARE_TUNNEL_TOKEN` | Token from Cloudflare Zero Trust dashboard |
| `SECRET_KEY_BASE` | Generate: `openssl rand -hex 64` |
| `GATEWAY_RPC_SECRET` | Generate: `openssl rand -hex 32` |
| `GATEWAY_ADMIN_SECRET` | Generate: `openssl rand -hex 32` |
| `MEDIA_PROXY_SECRET_KEY` | Generate: `openssl rand -hex 32` |
| `SUDO_MODE_SECRET` | Generate: `openssl rand -hex 32` |
| `LIVEKIT_API_KEY` | Generate: `openssl rand -hex 16` |
| `LIVEKIT_API_SECRET` | Generate: `openssl rand -hex 32` |
| `VAPID_PUBLIC_KEY` | Generate with web-push: `npx web-push generate-vapid-keys` |
| `VAPID_PRIVATE_KEY` | (paired with above) |
| `ADMIN_OAUTH2_CLIENT_ID` | OAuth2 app ID (from Fluxer admin panel after first boot) |
| `ADMIN_OAUTH2_CLIENT_SECRET` | OAuth2 secret |
| `PASSKEY_RP_ID` | Your domain e.g. `st.vish.gg` |
| `PASSKEY_ALLOWED_ORIGINS` | `https://st.vish.gg` |
Everything else can stay at its default dev value on first boot.
### 6. Set up the LiveKit config
```bash
cp dev/livekit.yaml dev/livekit.yaml
```
Edit `dev/livekit.yaml` — replace the API key/secret with the values you set in `.env`:
```yaml
keys:
"<LIVEKIT_API_KEY>": "<LIVEKIT_API_SECRET>"
webhook:
api_key: "<LIVEKIT_API_KEY>"
urls:
- "http://api:8080/webhooks/livekit"
```
### 7. Start the stack
```bash
cd /root/fluxer/dev
docker compose up -d
```
On first boot, `cassandra-migrate` will run automatically and apply schema migrations.
ScyllaDB takes ~90 seconds to become healthy before migrations run.
Watch progress:
```bash
docker compose logs -f cassandra-migrate
docker compose logs -f api
```
### 8. Verify everything is up
```bash
docker compose ps
```
Expected running containers: `caddy`, `cloudflared`, `api`, `worker`, `gateway`, `media`, `admin`,
`marketing`, `docs`, `metrics`, `clamav`, `meilisearch`, `redis`, `minio`, `livekit`
Expected exited (one-shot): `cassandra-migrate`, `minio-setup`
The app should be accessible at your configured domain via the Cloudflare tunnel.
---
## Managing the stack
### Start / stop
```bash
cd /root/fluxer/dev
# Start all
docker compose up -d
# Stop all (keep volumes)
docker compose down
# Stop and wipe all data
docker compose down -v
```
### View logs
```bash
# All services
docker compose logs -f
# Specific service
docker compose logs -f api
docker compose logs -f gateway
```
### Rebuild a specific service after code changes
```bash
# Rebuild and restart one service
docker compose up -d --build media
# Rebuild frontend app and restart caddy
cd /root/fluxer
pnpm --filter fluxer_app build
docker compose -f dev/compose.yaml restart caddy
```
### Update from upstream
```bash
cd /root/fluxer
git pull origin main
# Rebuild frontend
pnpm install
pnpm --filter fluxer_app build
# Restart services that auto-install deps (api, worker, docs)
docker compose -f dev/compose.yaml restart api worker docs
# Rebuild services with Dockerfiles
docker compose -f dev/compose.yaml up -d --build media admin marketing metrics
```
---
## Configuration reference
### Key files
| File | Purpose |
|------|---------|
| `dev/compose.yaml` | Main docker compose for the dev stack |
| `dev/.env` | All environment variables (not committed — see `.env.example`) |
| `dev/Caddyfile.dev` | Caddy reverse proxy routing config |
| `dev/livekit.yaml` | LiveKit server config (API keys, webhook URL) |
### Port mappings (host)
| Port | Service |
|------|---------|
| `8088` | Caddy (all traffic enters here, then routed internally) |
| `7880` | LiveKit HTTP |
| `7882/udp` | LiveKit RTC |
| `7999/udp` | LiveKit RTC |
| `9042` | ScyllaDB CQL (direct access) |
| `8123` | ClickHouse (only with `--profile clickhouse`) |
All other services communicate internally over `fluxer-shared` Docker network.
### Optional profiles
```bash
# Enable ClickHouse-backed metrics
docker compose --profile clickhouse up -d
# Enable search (meilisearch is included by default in dev)
docker compose --profile search up -d
```
---
## Cloudflare tunnel setup
1. Go to Cloudflare Zero Trust → Networks → Tunnels → Create tunnel
2. Name it (e.g. `fluxer-seattle`)
3. Copy the tunnel token → set as `CLOUDFLARE_TUNNEL_TOKEN` in `dev/.env`
4. Add a public hostname:
- Subdomain: `st` (or your chosen subdomain)
- Domain: `vish.gg`
- Service: `http://localhost:8088`
5. The `cloudflared` container will connect automatically on `docker compose up`
---
## Troubleshooting
### Gateway won't start
The Erlang gateway compiles from source on first start (`rebar3 compile`) — this takes 2-5 minutes.
Watch with `docker compose logs -f gateway`. If it exits, check for missing deps or compilation errors.
### ScyllaDB/Cassandra migration fails
ScyllaDB needs ~90 seconds to fully initialise. The `cassandra-migrate` container has a `sleep 30`
but on slow machines it may still fail. Restart it manually:
```bash
docker compose run --rm cassandra-migrate
```
### API exits immediately
Almost always a missing or malformed `.env` value. Check:
```bash
docker compose logs api | head -30
```
Common causes: `DATABASE_URL` unreachable, missing `SECRET_KEY_BASE`, malformed `REDIS_URL`.
### Frontend shows blank page
The `fluxer_app/dist/` directory is empty — the app hasn't been built yet.
Run the build step (Step 3 above) then restart Caddy:
```bash
docker compose restart caddy
```
### ClamAV takes forever to start
ClamAV downloads its virus definition database on first start (~500MB). This is normal.
It won't be marked healthy until the download completes (~5-10 min on first boot).
ClamAV can be disabled by setting `CLAMAV_ENABLED=false` in `.env` — the other services don't depend on it.
---
## Seattle reference deployment
The running instance at `st.vish.gg` uses this exact setup. Key details:
| Property | Value |
|----------|-------|
| Host | Seattle VM (`66.94.122.170` / Tailscale `100.82.197.124`) |
| SSH | `ssh root@seattle` or `ssh root@seattle-tailscale` |
| Install path | `/root/fluxer/` |
| Compose path | `/root/fluxer/dev/` |
| Domain | `st.vish.gg` |
| Tunnel | Cloudflare Zero Trust (`cloudflared`) |
| `AUTO_JOIN_INVITE_CODE` | `FRIENDS` (anyone with this code can register) |
To check status on seattle:
```bash
ssh seattle-tailscale "cd /root/fluxer/dev && docker compose ps"
ssh seattle-tailscale "cd /root/fluxer/dev && docker compose logs --tail 20 api"
```