End-to-end encrypted sync backend for Flamingo, the modern desktop API client. Built with Next.js 15 and Supabase, this server enables secure cross-device data synchronization with zero-knowledge architecture β the server never sees plaintext data or encryption keys.
- Two-Phase Device Authorization β Browser-based OAuth-like flow: desktop client creates a temp token, user authenticates via browser, client polls for the session token
- End-to-End Encryption β Server stores only AES-256-GCM encrypted blobs; encryption keys are never transmitted in plaintext
- Multi-Device Sync β Unlimited devices per account with automatic conflict resolution by last-writer-wins
- Selective Sync β Per-data-type sync toggles for 5 categories: history, environments, secrets, collections, settings
- Session Management β Token rotation and revocation, device listing and removal
- Audit Logging β Complete security event trail (connections, key rotations, device approvals, config changes)
- Event Queue β Real-time sync event notifications (
data_updated,config_changed,device_revoked,key_rotated) - Web Dashboard β Full management UI with overview stats, sync settings, device management, and security panel
- Key Rotation β Versioned master key updates with full audit trail
- Password Management β Change password from the web dashboard
- Cloudflare Turnstile β Bot protection on signup flows
- CORS Middleware β Cross-origin support for desktop client API calls
| Layer | Technology |
|---|---|
| Framework | Next.js 15 (App Router) |
| Language | TypeScript 5 |
| Database | Supabase (PostgreSQL) |
| Auth | Supabase Auth (cookie-based) |
| Styling | TailwindCSS 3 |
| Components | Radix UI |
| Icons | Lucide React |
| Bot Protection | Cloudflare Turnstile |
| Deployment | Node.js (Vercel-ready) |
backend-server/
βββ src/
β βββ app/
β β βββ api/
β β β βββ sync/
β β β β βββ init/route.ts # Create temp token
β β β β βββ claim/route.ts # Claim token (browser)
β β β β βββ token/route.ts # Poll for session (client)
β β β β βββ config/route.ts # Sync preferences CRUD
β β β β βββ config/data-type/ # Placeholder
β β β β βββ key/route.ts # Master key storage/retrieval
β β β β βββ data/route.ts # List all encrypted blobs
β β β β βββ data/[type]/route.ts # CRUD per data type
β β β β βββ rotate/route.ts # Token rotation
β β β β βββ revoke/route.ts # Session revocation
β β β βββ devices/
β β β β βββ route.ts # List devices
β β β β βββ [id]/route.ts # Revoke specific device
β β β βββ audit/route.ts # Audit log (last 50 events)
β β β βββ auth/
β β β βββ password/route.ts # Change password
β β β βββ verify-turnstile/ # Cloudflare Turnstile verification
β β βββ dashboard/ # Web dashboard pages
β β β βββ page.tsx # Overview (stats + sync status)
β β β βββ sync/page.tsx # Sync settings toggles
β β β βββ devices/page.tsx # Device management table
β β β βββ security/page.tsx # Security audit + change password
β β βββ login/page.tsx # Login + signup with Turnstile
β β βββ sync/
β β β βββ authorize/page.tsx # Token claim page (OAuth)
β β β βββ complete/page.tsx # Claim success confirmation
β β βββ layout.tsx # Root layout
β β βββ page.tsx # Redirect / β /dashboard
β β βββ globals.css # Global styles + theme vars
β βββ components/
β β βββ dashboard/
β β β βββ header.tsx # User avatar + sign out
β β β βββ sidebar.tsx # Navigation sidebar
β β β βββ sync-status-card.tsx # Per-type status card + loading skeleton
β β βββ login/
β β β βββ login-form.tsx # Auth form with Turnstile
β β βββ ui/ # Radix-based primitives
β β βββ avatar.tsx, badge.tsx, button.tsx, card.tsx
β β βββ dropdown-menu.tsx, input.tsx, label.tsx
β β βββ separator.tsx, skeleton.tsx, switch.tsx
β β βββ table.tsx, tabs.tsx, toast.tsx
β β βββ turnstile.tsx # Cloudflare Turnstile widget
β βββ lib/
β β βββ api-utils.ts # Auth, response helpers, token gen
β β βββ crypto.ts # Client crypto reference (AES, PBKDF2)
β β βββ supabase/
β β β βββ admin.ts # Service-role client
β β β βββ client.ts # Browser client
β β β βββ server.ts # Server cookie-based client
β β βββ types.ts # All TypeScript interfaces
β β βββ utils.ts # cn(), generateId(), formatRelativeTime()
β βββ middleware.ts # CORS + dashboard route protection
β βββ globals.css # Tailwind + theme CSS variables
βββ supabase/
β βββ migrations/
β βββ 001_schema.sql # Full database schema (7 tables)
βββ # Config files (next.config, tailwind, postcss, tsconfig)
Desktop Client Browser Server
| | |
|---- POST /api/sync/init ---->| |
|<--- { temp_token, url } -----| |
| | |
|---- open url in browser ---->| |
| |---- GET /sync/authorize ->|
| | (authenticated via cookie)|
| | If not logged in: |
| | redirect /login?token |
| | |
| |---- POST /api/sync/claim ->|
| | { temp_token } |
| |<--- success ---------------|
| | |
|---- POST /api/sync/token --->| |
| { temp_token } | |
| (polls every 2s) | |
|<--- { session_token } -------| |
| | |
|===== session established ===>| |
|-- PUT /api/sync/key -------->| Store master key |
|-- PUT /api/sync/data/* ----->| Upload encrypted blobs |
- Node.js >= 18
- npm >= 9
- Supabase account (free tier works)
cd backend-server
npm installCreate .env.local in the project root:
# Supabase
NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
SUPABASE_SERVICE_ROLE_KEY=your-service-role-key
# Server
NEXT_PUBLIC_SERVER_URL=http://localhost:3000
# Session
SESSION_EXPIRY_DAYS=90
TEMP_TOKEN_EXPIRY_MINUTES=5
# Cloudflare Turnstile (get at https://dash.cloudflare.com/?to=/:account/turnstile)
NEXT_PUBLIC_TURNSTILE_SITE_KEY=your-turnstile-site-key
TURNSTILE_SECRET_KEY=your-turnstile-secret-keyRun the migration in your Supabase SQL editor:
supabase/migrations/001_schema.sql
This creates all 7 tables, indexes, RLS policies, triggers, and helper functions.
npm run dev # Next.js dev server (port 3000)npm run build # Production build
npm run start # Start production serverAll endpoints return JSON with the structure { success: boolean, data?: T, error?: string, message?: string }.
| Endpoint | Method | Auth | Description |
|---|---|---|---|
/api/sync/init |
POST | None | Create temp token, get login URL |
/api/sync/token |
POST | None | Poll for session token (body: { temp_token }) |
/api/sync/claim |
POST | Cookie/Bearer | Claim a temp token (browser or client) |
/api/sync/rotate |
POST | Bearer | Rotate current session token |
/api/sync/revoke |
POST | Bearer | Revoke current session |
| Endpoint | Method | Auth | Description |
|---|---|---|---|
/api/sync/config |
GET | Bearer/Cookie | Get sync preferences |
/api/sync/config |
PUT | Bearer/Cookie | Update sync preferences |
/api/sync/key |
GET | Bearer/Cookie | Get master encryption key |
/api/sync/key |
PUT | Bearer/Cookie | Store/rotate master encryption key |
/api/sync/data |
GET | Bearer/Cookie | List all encrypted data items |
/api/sync/data/[type] |
GET | Bearer/Cookie | Download encrypted data blob |
/api/sync/data/[type] |
PUT | Bearer/Cookie | Upload encrypted data blob |
/api/sync/data/[type] |
DELETE | Bearer/Cookie | Delete encrypted data |
Valid data types: history, environment, secret, collection, setting.
| Endpoint | Method | Auth | Description |
|---|---|---|---|
/api/devices |
GET | Bearer/Cookie | List connected devices |
/api/devices/[id] |
DELETE | Bearer/Cookie | Revoke a device |
| Endpoint | Method | Auth | Description |
|---|---|---|---|
/api/audit |
GET | Bearer/Cookie | Get last 50 security audit events |
| Endpoint | Method | Auth | Description |
|---|---|---|---|
/api/auth/password |
PUT | Cookie | Change password (body: { current_password, new_password }) |
/api/auth/verify-turnstile |
POST | None | Verify Cloudflare Turnstile token |
| Route | Page | Description |
|---|---|---|
/dashboard |
Overview | Sync status summary, connected devices count, per-type sync cards |
/dashboard/sync |
Sync Settings | Toggle sync per data type (history, environments, secrets, collections, settings) |
/dashboard/devices |
Devices | View all connected devices, revoke access with confirmation |
/dashboard/security |
Security | View audit log (last 50 events), change password, encryption info |
/login |
Login | Sign in or create account with Turnstile bot protection |
/sync/authorize |
OAuth Authorize | Authorize a new sync device (reads ?token= from query) |
/sync/complete |
OAuth Complete | Authorization success confirmation with auto-close countdown |
| Table | Purpose |
|---|---|
sync_temp_tokens |
Short-lived OAuth-like tokens (5-min TTL) |
sync_sessions |
Persistent device sessions (90-day expiry) with encrypted master key storage |
sync_configs |
Per-user sync preferences (5 boolean toggles, all default true) |
sync_data |
Encrypted sync blobs (one row per data type per user, versioned) |
devices |
Registered device metadata with approval status |
audit_logs |
Security event trail (8 event types) |
sync_events |
Event queue for real-time notifications (4 event types) |
All 7 tables have RLS enabled with user_id = auth.uid() isolation policies. API routes authenticate at the application layer and use the admin client (service role) to bypass RLS.
| Event Type | Trigger |
|---|---|
token_claimed |
Temp token claimed |
device_connected |
New session token issued |
config_updated |
Sync preferences changed |
key_stored |
Master key stored or rotated |
token_rotated |
Session token rotated |
token_revoked |
Session revoked |
device_revoked |
Device deleted |
data_cleared |
Sync data deleted |
The middleware (src/middleware.ts) handles:
- Public routes β
/login,/sync/authorize,/sync/complete,/api/*, and/are accessible without authentication - CORS headers β Allows all origins for GET, POST, PUT, DELETE, OPTIONS with
Content-TypeandAuthorizationheaders. Preflight requests return 204 with 86400s max-age. - Dashboard protection β Redirects unauthenticated users to
/login?redirect=<path>for all/dashboard/*routes
- Encryption: AES-256-GCM with unique 12-byte random nonces per operation
- Key Storage: Master key stored as raw base64 on the server (no wrapping β only the key holder can decrypt)
- Session Tokens: Random 128-bit tokens prefixed
flm_sync_, stored as SHA-256 hashes - Temp Tokens: 5-minute TTL, single-use only, prefixed
flm_temp_ - Audit Trail: All sensitive operations logged with event type, IP, user agent, and metadata
- CORS: Pre-configured for cross-origin desktop client access
- Bot Protection: Cloudflare Turnstile widget on signup form
- Content Security: CSP headers set in
next.config.ts
| Script | Description |
|---|---|
npm run dev |
Next.js development server |
npm run build |
Production build |
npm run start |
Start production server |
npm run lint |
Next.js lint |
npm run typecheck |
TypeScript type-check |
MIT License β see LICENSE
Copyright (c) 2024 Javier FernΓ‘ndez (Jallox/Jayox)