Docker (recommended)
On Ubuntu or Debian without Docker installed, the script offers to install it for you. Elsewhere, install Docker first.
git clone https://github.com/BrahmjotSingh0/rumo.git
cd rumo
./install.sh # Windows: .\install.ps1
The script generates .env with randomly generated secrets for whatever isn't already set, asks for an optional domain name, then runs docker compose up -d --build. Running it again later is safe: it reuses whatever secrets and settings you already have instead of overwriting them.
Frontend on http://localhost:5173, backend on http://localhost:5000. The Postgres schema is applied automatically on first run. Open /admin and enter the admin token the script printed (also saved in .env as ADMIN_SETUP_TOKEN) to set your logo, name, tagline, and accent color.
./install.sh --domain meet.example.com --yes
This also switches the frontend and backend to be reachable only through Caddy rather than directly over plain HTTP. Re-running the installer later without --domain keeps whatever domain you already configured.
Manual setup (without Docker)
Requires Node.js 22+ and PostgreSQL.
# 1. Create the database and load the schema
createdb rumo
psql rumo < backend/database/schema.sql
# 2. Backend
cd backend
cp .env.example .env # fill in DB credentials
npm install
npm run dev # http://localhost:5000
# 3. Frontend (new terminal)
cd frontend
cp .env.example .env
npm install
npm run dev # http://localhost:5173
See the backend and frontend READMEs for the full environment variable reference and project layout.
getUserMedia on plain HTTP), except on localhost. For a real deployment, put a reverse proxy (Caddy, nginx, Cloudflare Tunnel, etc.) with a TLS certificate in front of frontend/backend, and point CORS_ORIGIN, VITE_API_URL, and VITE_SOCKET_URL at the public HTTPS URLs.
Branding
Open /admin, enter your ADMIN_SETUP_TOKEN, and set the app name, tagline, description, logo, and accent color from a form. It saves to the database and applies immediately, no rebuild or restart.
Prefer a file instead (or never set an admin token)? Edit frontend/public/branding.json:
{
"appName": "Rumo",
"tagline": "Connect, collaborate, create.",
"description": "Free, self-hosted video meetings.",
"logoIcon": "/brand/icon.svg",
"logoFull": "/brand/logo.svg",
"primaryColor": "#2E5BFF"
}
This file is fetched at runtime too (edit it, or bind-mount your own version over it in Docker), and acts as the fallback for anything not set through the admin panel. primaryColor drives a full 50-900 Tailwind shade scale computed on load, so buttons, badges, and focus rings across the landing and pre-join screens follow it automatically. Logo files live in frontend/public/brand/ by default; replace them, update the paths above, or just upload one from /admin.
Languages
UI strings live in frontend/src/i18n/lang.json: one file, each string keyed by language code.
"common.joinMeeting": { "en": "Join Meeting", "es": "Unirse a la reunión" }
To add a language, add its code and label to languages, then add that code to every entry under strings. The language switcher on the home screen picks it up automatically. Currently ships with English and Spanish covering the landing and pre-join screens.
TURN server (optional)
STUN (included, free, via Google's public servers) is enough for most networks. If some participants are behind restrictive NATs/firewalls and can't connect, run your own TURN server (e.g. coturn) and set TURN_SERVERS (backend) plus VITE_TURN_URL, VITE_TURN_USERNAME, VITE_TURN_CREDENTIAL (frontend).
Security notes
| SQL | All queries are parameterized (pg placeholders); no string-built queries. |
| Rate limiting | On by default for the REST API (RATE_LIMIT_* env vars). |
| Headers | helmet sets standard security headers. Content-Security-Policy is off by default since a strict one is easy to break with WebRTC/media/websocket connections. |
| Sessions | None. No cookies, no accounts, so CORS_CREDENTIALS defaults to false. |
| Admin panel | /admin and /api/settings/* writes require ADMIN_SETUP_TOKEN, checked with a timing-safe comparison. Unset it to disable branding changes entirely. |
| TLS | Handled by the optional Caddy reverse proxy (./install.sh --domain ...), or your own proxy in front for other setups. |
| Secrets | .env/.env.production are gitignored everywhere in the repo. Never commit real credentials. |
Host status has no cryptographic backing: whoever creates a room, or is first to join it, becomes host for that session. This is the same trust model as most link-based meeting tools (anyone with the link can join).