Self-hosting the sync server

The server is a small FastAPI + Postgres + Caddy stack. Any €4/month VPS runs it; it only shuffles ciphertext, so 2 vCPU / 4 GB is generous. Disk is the only resource that really grows: budget roughly 1 GB per heavy user (encrypted PDFs and, where enabled, project files; experiment outputs stay local except what remote runs ship back, capped at 100 MB per file).

Self-hosting is optional: during the beta you can simply use the hosted server at https://sync-dev.tramea.org (registration is gated by your invite code).

Deploy in five steps

# 1. a VPS (Ubuntu 24.04) + a DNS A record: sync.yourdomain.org → VPS IP
# 2. on the VPS
apt update && apt install -y docker.io docker-compose-v2 ufw
ufw allow 22 && ufw allow 80 && ufw allow 443 && ufw --force enable

# 3. copy the server/ directory, then configure
cp .env.example .env
#   ROS_SERVER_SECRET=$(openssl rand -base64 48)
#   POSTGRES_PASSWORD=$(openssl rand -hex 24)
#   SERVER_DOMAIN=sync.yourdomain.org
#   ROS_SERVER_INVITE_CODE=…        # optional: invite-only registration

# 4. launch — Caddy fetches a Let's Encrypt certificate automatically
docker compose up -d --build

# 5. verify
curl https://sync.yourdomain.org/v1/health
A real certificate is required for the mobile app — it will not accept self-signed TLS. Public hostname + Caddy = automatic.

Invite-only registration

Set ROS_SERVER_INVITE_CODE in .env to require an invite code at registration — hand the code to the people you invite. Leave it empty for open registration.

Optional: AI relay and lab-local AI

Both are off until you configure them; sync never depends on either.

  • Hosted AI relay — to offer your users the "plan credits" assistant provider, set ROS_SERVER_AI_KEY (and optionally ROS_SERVER_AI_PROVIDER / ROS_SERVER_AI_MODEL; the default is Anthropic with a Haiku-class model) in .env. Questions are metered per account per month, forwarded to the provider, and never stored or logged — this is the server's only plaintext route, and users must explicitly select it. Without the key, the endpoint answers 503 and the provider simply doesn't appear in apps.
  • Lab-local AI — point a lab's members at an OpenAI-compatible model on the lab's own hardware (Ollama, vLLM, …): org-ai ORG_ID http://ai.lab.local:11434/v1 --model mistral-small (see Administration below). Members' apps pick it up as the "Lab server (local model)" provider and default to it; prompts then never leave the lab network. Pass an empty endpoint to disconnect.

Administration

A small admin CLI runs inside the app container:

docker compose exec app python -m ros_server.admin stats
docker compose exec app python -m ros_server.admin accounts
docker compose exec app python -m ros_server.admin backup -o backup.tar.gz
docker compose exec app python -m ros_server.admin set-plan you@lab.org pro
docker compose exec app python -m ros_server.admin create-org my-lab --seats 20
docker compose exec app python -m ros_server.admin org-add <org-id> member@lab.org
docker compose exec app python -m ros_server.admin org-ai <org-id> http://ai.lab.local:11434/v1
docker compose exec app python -m ros_server.admin team-create <org-id> "Team Memory"
docker compose exec app python -m ros_server.admin team-add <team-id> lead@lab.org --lead
docker compose exec app python -m ros_server.admin set-org-role pi@lab.org admin
docker compose exec app python -m ros_server.admin org-remove member@lab.org --revoke

The last one is the departure flow: it takes a member out of the organization, and with --revoke also removes them from every org-owned space and team — the server refuses their further pulls. Their personal spaces and local data are untouched.

Everything it touches is metadata — plans, seats, usage counts, backups. It cannot read anyone's content; there is no key to read it with.

Operations

  • Backups: docker compose exec db pg_dump -U ros ros > backup.sql weekly, plus the blob volume. Even total server loss is survivable — every device holds plaintext.
  • Updates: copy the new server/, docker compose up -d --build.
  • Accounts: users manage themselves — passphrase change, recovery, device revocation and account deletion are all self-service in the app.