- Option 1: Docker — A standalone Node.js server you run yourself. Recommended for self-hosting.
- Option 2: Supabase — The hosted cloud option (or self-hosted Supabase). This is the current default.
Option 1: Docker (Recommended for Self-Hosting)
Theserver/ directory contains a standalone YesPaPa remote server built with Express, WebSocket, SQLite, and JWT authentication. It implements the full remote server protocol and is the reference self-hosted implementation.
Prerequisites
- Docker and Docker Compose
Quick Start
Pull the image from DockerHub and run it:docker-compose.yml:
Configuration
Environment variables are set indocker-compose.yml:
| Variable | Default | Description |
|---|---|---|
PORT | 8080 | Port the server listens on |
JWT_SECRET | change-me-to-a-random-string | Must be changed. Secret used to sign JWT tokens. Use a long random string (32+ characters). |
DATABASE_PATH | /app/data/yespapa.db | Path to the SQLite database inside the container |
EXPO_PUSH_ENABLED | false | Set to true to enable push notifications via Expo Push API |
docker-compose.yml:
Data Persistence
All data is stored in a Docker volume namedyespapa-data, mapped to /app/data inside the container. This volume persists across container restarts and rebuilds. The SQLite database (yespapa.db) lives in this volume.
To back up your data:
Connecting YesPaPa to Your Docker Server
Duringyespapa init, provide your server URL and select the selfhosted server type:
https:// URL instead.
If already initialized, you can reinitialize:
API Endpoints
The Docker server exposes these endpoints:| Endpoint | Description |
|---|---|
GET /health | Health check, returns server status and version |
/api/auth/* | Authentication (register, login, token refresh) |
/api/hosts/* | Host registration and management |
/api/commands/* | Command approval queue (create, list, approve, deny) |
/api/grace-periods/* | Grace period management |
/api/push/* | Push notification registration |
/ws | WebSocket endpoint for real-time command and grace period updates |
Push Notifications
Push notifications are disabled by default. To enable them:- Set
EXPO_PUSH_ENABLED=trueindocker-compose.yml - Restart the container:
docker compose restart
https://exp.host/--/api/v2/push/send.
Running Without Docker
You can also run the server directly with Node.js (v18+):Updating
To update to a newer version:Docker Troubleshooting
Container fails to start
- Check logs:
docker compose logs yespapa-server - Ensure port 8080 is not in use:
lsof -i :8080 - Verify the Docker volume exists:
docker volume ls | grep yespapa-data
”Connection refused” from YesPaPa daemon
- Ensure the server is running:
docker compose ps - If the daemon is on the same machine, use
http://localhost:8080orhttp://host.docker.internal:8080 - If on a different machine, ensure the server port is accessible (check firewalls)
- Verify with:
curl http://your-server-ip:8080/health
Database errors or corruption
- Stop the server:
docker compose down - Back up the current database from the volume
- Remove the volume to start fresh:
docker volume rm server_yespapa-data - Restart:
docker compose up -d
WebSocket connection drops
- If behind a reverse proxy (nginx, Caddy), ensure WebSocket upgrade is configured
- For nginx, add to your location block:
Option 2: Supabase (Hosted / Cloud)
Supabase is the current default remote backend. You can use the hosted Supabase platform (free tier available) or self-host the full Supabase stack.What You Need
- A Supabase project (free tier works)
- The Supabase CLI (
npx supabase)
Setup Steps
1. Create a Supabase Project
Go to supabase.com/dashboard and create a new project. Note your:- Project URL:
https://<your-project-ref>.supabase.co - Anon Key: Found in Settings > API > Project API keys >
anonpublic - Service Role Key: Found in Settings > API > Project API keys >
service_role(used by Edge Functions only, never exposed to clients)
2. Apply Database Migrations
From the repo root:| Table | Purpose |
|---|---|
hosts | Registered machines (name, fingerprint, push token) |
commands | Intercepted commands pending approval |
grace_periods | Active auto-bypass tokens |
3. Enable Realtime
The migrations automatically addcommands and grace_periods to the Supabase Realtime publication. Verify in your dashboard under Database > Replication that both tables are listed.
4. Deploy Edge Functions
Thepush_notification Edge Function sends push notifications via Expo when commands are inserted:
SUPABASE_URLSUPABASE_SERVICE_ROLE_KEY
5. Enable Anonymous Auth
YesPaPa uses Supabase anonymous auth so the daemon can authenticate without user accounts:- Go to Authentication > Providers in your Supabase dashboard
- Enable Anonymous Sign-ins
6. Configure YesPaPa to Use Your Supabase Server
Duringyespapa init, when prompted for the remote server:
Schema Reference
hosts Table
commands Table
grace_periods Table
RLS Policies
All tables use the same pattern — users can only access rows they own:Supabase Troubleshooting
”Remote connection failed” during init
- Verify the URL is correct and includes
https:// - Check that the anon key is the
anonkey, not theservice_rolekey - Ensure anonymous auth is enabled in your Supabase project
Push notifications not arriving
- Verify the Edge Function is deployed:
npx supabase functions list - Check Edge Function logs:
npx supabase functions logs push_notification - Ensure the mobile app has a valid push token (check
hosts.push_tokenin the database)
Commands stuck as “pending”
- Check Realtime is enabled for the
commandstable - Verify the daemon is connected:
yespapa statusshould show “Remote: configured” - Check daemon logs:
cat ~/.yespapa/daemon.log
Security Considerations
These apply regardless of which backend you use.Zero-Trust Architecture
Even when self-hosting, the remote server is never trusted for security decisions:- TOTP codes are validated locally by the daemon, not by the server
- Grace tokens are HMAC-signed with the TOTP seed, which never leaves the machine
- The server is a relay — it forwards approval requests and responses, but cannot forge approvals
- If the server is compromised, the worst case is denial of service (commands stuck pending). Attackers cannot approve commands
Recommended Hardening
- Use TLS — Always use HTTPS in production. For the Docker server, place it behind a reverse proxy (nginx, Caddy, Traefik) with TLS termination
- Set a strong JWT secret — For the Docker server, use a randomly generated string of at least 32 characters
- Restrict network access — Limit who can reach the server port. Use firewall rules or bind to a private network
- Set up monitoring — Watch for unusual patterns in the commands table
- Back up your database — Especially host registrations and push tokens
- Rotate secrets — Periodically rotate JWT secrets (Docker) or API keys (Supabase) and update client configs
Alternative Backends
The Docker server (server/ directory) is the reference self-hosted implementation. It provides the full remote server protocol using Express, WebSocket, SQLite, and JWT authentication. For most self-hosting scenarios, this is all you need.
If you need a different stack, the remote server protocol is intentionally simple — a REST API with WebSocket subscriptions. You could reimplement it with:
- Self-hosted Supabase — Run the full Supabase stack on your own infrastructure using the migrations in
supabase/ - PostgreSQL + PostgREST + pg_notify — Same REST+realtime pattern without Supabase
- Firebase — Firestore + Cloud Functions + FCM
- Custom server — Any language/framework that provides REST + WebSocket + push notifications
- A REST API for CRUD on
hosts,commands,grace_periods - Real-time subscriptions for
commandsandgrace_periodschanges - A push notification endpoint (Expo Push API compatible)
- Token-based authentication (JWT or equivalent)