Security & Privacy
Multi-layer security architecture protecting your financial data at every level
🔐 JWT Authentication Active
Supabase Auth with JWT tokens. Every API request is authenticated and verified server-side.
🛡️ Row Level Security Active
PostgreSQL RLS ensures users can only access their own data, enforced at the database level.
⏱️ Rate Limiting Active
100 req/min REST, 30 msg/min WebSocket per user. Prevents abuse and DDoS attempts.
🔒 TLS Encryption Active
All traffic encrypted via HTTPS/WSS. Caddy automatically manages TLS certificates.
✅ Input Validation Active
Zod schema validation on all inputs. No raw SQL, parameterized queries only.
⚠️ Action Confirmation Active
Dangerous commands (delete wallet, close debt) require explicit user confirmation.
On this page
1 Authentication Flow
KeychainService. Web uses localStorage with Supabase SDK handling automatic token refresh.
2 Row Level Security (RLS)
Every table has RLS enabled with policies that ensure users can only access their own data. This is enforced at the PostgreSQL level — even if application code has a bug, the database won't expose another user's data.
CREATE POLICY "Users can access own wallets" ON wallets FOR ALL USING (auth.uid() = user_id);
CREATE POLICY "Users can access own transactions" ON transactions FOR ALL USING (auth.uid() = user_id);
CREATE POLICY "Users can access own categories" ON categories FOR ALL USING (auth.uid() = user_id);
CREATE POLICY "Users can access own contacts" ON contacts FOR ALL USING (auth.uid() = user_id);
All other tables (recurring_templates, ai_notifications, chat_messages, action_history, automation_rules, etc.) follow the same pattern: USING (auth.uid() = user_id).
SUPABASE_SERVICE_KEY (service role) to bypass RLS for server-side operations like cron jobs and admin tasks. This key is never exposed to clients.
3 Middleware Chain
Every request passes through a security middleware chain before reaching route handlers:
4 Input Validation
All user input is validated using Zod schemas before processing. This prevents injection attacks and ensures data integrity.
Protection Against Common Attacks
| Attack Vector | Protection | Layer |
|---|---|---|
| SQL Injection | Parameterized Supabase client, no raw SQL with user input | Database |
| XSS (Cross-Site Scripting) | React auto-escapes output; SwiftUI is safe by default | Client |
| CSRF | JWT in Authorization header (not cookies) | Auth |
| Unauthorized Access | RLS at DB level + auth middleware at API level | Database + API |
| Brute Force | Rate limiting (100 req/min per user) | Middleware |
| Man-in-the-Middle | TLS encryption via Caddy (auto-renewing certificates) | Transport |
| Data Exposure | Service keys server-only; public keys for client auth only | Config |
5 Rate Limiting
| Channel | Limit | Window | Algorithm |
|---|---|---|---|
| REST API | 100 requests | Per minute per user | Sliding window |
| WebSocket | 30 messages | Per minute per user | Sliding window |
6 Dangerous Actions Confirmation
Commands that modify or delete important data require explicit user confirmation. The action is stored as "pending" and executed only after confirmation.
| Command | Risk Level | What It Does |
|---|---|---|
| delete_wallet | High | Deletes wallet and orphans all associated transactions |
| delete_transaction | Medium | Permanently removes transaction and adjusts wallet balance |
| close_debt | Medium | Archives contact and all debt records |
| delete_goal | Medium | Removes savings goal |
| delete_budget | Medium | Removes budget limit from category |
| delete_recurring | Medium | Cancels recurring payment template |
| delete_category | Medium | Removes category (transactions become uncategorized) |
7 Secret Management
SUPABASE_SERVICE_KEY and OPENAI_API_KEY are stored only in the server's .env file and never included in client bundles, git repositories, or public responses.
8 Client Security
📱 iOS
Token storage: Keychain (encrypted, hardware-backed)
Network: HTTPS only, certificate pinning ready
UI: SwiftUI (auto XSS protection)
Auth refresh: Automatic on app foreground
🌐 Web
Token storage: localStorage (Supabase SDK)
Network: HTTPS only via Caddy
UI: React (auto XSS protection)
Auth refresh: Automatic via Supabase client
🔌 WebSocket
Auth: JWT required as first message
Encryption: WSS (TLS)
Rate limit: 30 msg/min per user
Timeout: Auto-disconnect on inactivity