Screencap uses a backend server for social features like friends, shared projects, and collaborative rooms. By default, it connects to the official hosted backend, but you can deploy your own instance for privacy or team use.
The backend is a Next.js application that provides:
- User registration with Ed25519 cryptographic key pairs
- Friends system with friend requests and relationships
- Rooms for collaborative project sharing
- End-to-end encrypted event sharing
- Chat messaging between friends and room members
- Rate limiting and request signature verification
All sensitive data (screenshots, event content) is encrypted client-side before upload. The server only stores encrypted blobs and cannot read your content.
- Node.js 18+ or Bun
- PostgreSQL database (Vercel Postgres, Supabase, Neon, or self-hosted)
The easiest way to deploy your own backend:
Fork screencap-website to your GitHub account.
- Go to vercel.com and create a new project
- Import your forked repository
- Add a Postgres database:
- In your project dashboard, go to Storage → Create Database → Postgres
- Vercel will automatically set the
POSTGRES_*environment variables
After deployment, initialize the database schema:
curl -X POST https://your-deployment.vercel.app/api/initYou should receive:
{"success": true, "message": "Database initialized"}In the Screencap app:
- Open Settings → System
- Enable Use custom backend
- Enter your deployment URL (e.g.,
https://your-deployment.vercel.app) - Click Test to verify the connection
- Click Save
For other platforms (Railway, Render, AWS, self-hosted):
git clone https://github.com/yourorg/screencap-website.git
cd screencap-website
npm install # or bun installCreate .env.local:
POSTGRES_URL="postgres://user:password@host:5432/database?sslmode=require"
POSTGRES_PRISMA_URL="postgres://user:password@host:5432/database?sslmode=require"
POSTGRES_URL_NON_POOLING="postgres://user:password@host:5432/database?sslmode=require"npm run build
npm startOr for development:
npm run devcurl -X POST http://localhost:3000/api/initThe backend creates these tables:
| Table | Purpose |
|---|---|
users |
User accounts with usernames |
user_devices |
Device keys for authentication |
friend_requests |
Pending friend requests |
friendships |
Established friendships |
user_blocks |
Blocked users |
rooms |
Collaborative rooms/projects |
room_members |
Room membership and roles |
room_invites |
Pending room invitations |
room_member_keys |
E2EE key distribution |
room_events |
Encrypted shared events |
chat_threads |
Chat conversations |
chat_messages |
Individual messages |
published_projects |
Public project pages |
rate_limits |
API rate limiting |
All authenticated endpoints require these headers:
x-user-id: User IDx-device-id: Device IDx-ts: Unix timestamp (ms)x-sig: Ed25519 signature ofMETHOD\nPATH\nTS\nBODY_SHA256
| Method | Path | Description |
|---|---|---|
POST |
/api/init |
Initialize database |
POST |
/api/users/register |
Register new user |
| Method | Path | Description |
|---|---|---|
GET |
/api/me |
Get current user info |
POST |
/api/users/rename |
Change username |
GET/POST |
/api/friends |
List friends / Send request |
POST |
/api/friends/requests/:id/accept |
Accept friend request |
GET/POST |
/api/rooms |
List rooms / Create room |
GET |
/api/rooms/:id/members |
List room members |
POST |
/api/rooms/:id/invites |
Invite user to room |
GET/POST |
/api/rooms/:id/events |
List / Create room events |
GET |
/api/chats |
List chat threads |
POST |
/api/chats/:threadId/messages |
Send message |
Every authenticated request is signed with the user's Ed25519 private key:
canonical = METHOD + "\n" + PATH + "\n" + TIMESTAMP + "\n" + SHA256(BODY)
signature = Ed25519.sign(canonical, privateKey)
The server verifies signatures using the public key registered during account creation.
Room events and images are encrypted client-side using:
- AES-256-GCM for event payloads
- X25519 for key exchange between room members
- Room keys are distributed via encrypted envelopes
The server never sees plaintext event content or images.
Default limits (configurable):
- User registration: 5 requests/hour per IP
- API calls: 100 requests/minute per user
curl https://your-backend.vercel.app/api/initOn Vercel: Project → Logs → Runtime Logs
For self-hosted: Check your application logs for errors.
- Verify the URL is correct and accessible
- Check if the server is running
- Ensure HTTPS is configured for production
- Check server logs for database connection errors
- Verify PostgreSQL is running and accessible
- Run
/api/initto ensure tables exist
Run the initialization endpoint:
curl -X POST https://your-backend/api/init- Ensure client and server clocks are synchronized (within 5 minutes)
- Verify the user's device keys are correctly stored
- Ensure your backend is up-to-date (older backends verified signatures using only the URL pathname and will reject requests where the client signs pathname + query string, e.g.
fetchRoomEvents?since=...)
Warning: User accounts and social data cannot be migrated between backends. Switching backends requires creating a new account.
To migrate:
- Disable sharing in Screencap settings
- Change the backend URL
- Create a new username on the new backend
- Re-add friends and recreate rooms
Local data (screenshots, timeline) is unaffected.
When updating your backend:
- Pull the latest changes from the repository
- Run
npm installto update dependencies - Deploy the new version
- The database schema is designed to be backwards-compatible
For breaking changes, check the release notes and migration guides.