API. Edge functions and the service layer.
Plooi uses Supabase Edge Functions for every server-side operation — AI proxying, billing, BYOK key handling, account lifecycle.
Edge functions
| Function | Purpose | Auth |
|---|---|---|
| ai-proxy | Routes AI requests, decrypts BYOK keys server-side, checks credits, logs usage | User token |
| ai-job-worker | Async job queue for long-running runs | Service role |
| store-api-key | Encrypts and stores a user’s provider key (AES-256-GCM + HKDF-SHA256) | User token |
| delete-api-key | Removes a stored provider key | User token |
| validate-api-key | Live-checks a key against the provider before storing | User token |
| get-capabilities | Returns the model registry filtered to the user’s connected providers | User token |
| create-checkout-session | Stripe Checkout | User token |
| create-portal-session | Stripe Customer Portal | User token |
| stripe-webhook | Processes Stripe events (atomic idempotency) | Stripe signature |
| stripe-admin-stats | Admin reporting | Service role |
| retry-stripe-event | Webhook re-try | Service role |
| password-hash | Hashes public link passwords | Service role (cron-gated) |
| send-email | Transactional mail via Resend | Service role (cron-gated) |
| request-account-deletion | GDPR deletion request | User token |
| cancel-account-deletion | Cancel a pending deletion | User token |
| execute-account-deletion | Final deletion run | Service role |
| send-deletion-warnings | Reminder emails before deletion | Service role |
| export-user-data | GDPR data export | User token |
| health | System health probe | Public |
| changelog-create | Changelog automation | Service role |
Full request/response examples are in docs/API_REFERENCE.md.
BYOK key handling
The key-related edge functions are the only path that ever touches plaintext. The browser never writes or reads ciphertext.
store-api-keyderives a 16-byte salt and 12-byte nonce, runs HKDF-SHA256 against the master key, then AES-256-GCM encrypts the plaintext and writes ciphertext touser_api_keys. The master key lives in Supabase Function secrets — outside Postgres.ai-proxyis the only function that decrypts. It pulls the row server-side, decrypts in memory, makes the provider call, then discards the plaintext.- Browser reads go through the
user_api_keys_publicview — onlyid,service_name,key_hint(last four chars), andkey_version. The ciphertext column isREVOKEd from theauthenticatedrole. - Every save, delete, and validate is written to the audit log without plaintext.
See Bring your own key for the full encryption model.
Service layer (frontend)
Key services in the browser-side codebase:
authService— auth and sessionprojectService— projects, nodes, connectionsaiServerService/aiJobService— AI runs and queuestripeService/creditLedgerService— billing and creditscollaborationService— realtime and presenceadminService— admin functionsanalyticsService/featureFlagService— events and flags