Skip to content

security

Claude edited this page Sep 23, 2026 · 2 revisions

Security

MyPrompt runs as an Electron desktop app plus a standalone library. Three trust boundaries matter: the renderer/main process split, provider API keys at rest, and the split between public and privileged service clients.

Process isolation

desktop/main.ts creates the BrowserWindow with contextIsolation: true and nodeIntegration: false, so renderer code has no Node access. The only bridge is desktop/preload.ts, which exposes a fixed window.sentraDesktop surface through contextBridge: shell state, auth, workspace, a generic invoke, stream subscribe/unsubscribe, and window controls. Every channel is handled in the main process (desktop/ipc/core.ts, auth.ts, workspace.ts). The surface is narrow on purpose and should not be widened for ordinary changes. See Hazards and pitfalls.

Provider keys at rest

User-supplied provider keys are encrypted with AES-256-GCM in lib/crypto.ts. getEncryptionKey reads ENCRYPTION_KEY; a 64-character hex value is used as raw key bytes, any other value is hashed with SHA-256. Each encrypt call uses a fresh 16-byte IV and returns { encrypted, iv, authTag }, all three of which are stored. lib/llm/user-api-keys.ts writes those columns through Prisma (upsertUserProviderKey) and decrypts on demand in resolveProviderApiKey, which falls back to a process env key before throwing MissingProviderApiKeyError. The LOCAL provider needs no key. No .env value is committed; .env.example lists names only.

Public versus admin clients

lib/supabase/public.ts builds a client from the public URL plus anon key for user-scoped calls. lib/supabase/admin.ts builds a separate client from SUPABASE_SERVICE_ROLE_KEY; its header restricts it to admin operations (user management, subscription tier updates) and says never to expose it to the client. It is imported only by lib/billing/subscription-service.ts.

Payment webhooks

lib/billing/xendit-client.ts verifies inbound webhooks in verifyWebhookToken with timingSafeEqual against XENDIT_CALLBACK_TOKEN, returning false on a length mismatch rather than throwing.

Rate limiting

lib/auth/shared-rate-limit.ts buckets counters by time window and hashes the raw key with SHA-256 (hashKey), so no IP or email is stored in clear. Counters live under a unique (action, scope, keyHash, windowStart) constraint; a P2002 race retries rather than double-counts. lib/auth/abuse-protection.ts applies per-IP and per-email rules to register, forgot-password, and resend-verification.

Guest mode

A session without an access_token is shown as a guest badge in desktop/main.ts (buildShellBadges). Guest optimize and evaluate calls route through resolveGuestProvider (lib/llm/provider-readiness.ts) to whichever provider the environment has a key for, never a user key.

Boundary Control Where
Renderer to Node contextIsolation: true, nodeIntegration: false desktop/main.ts
Renderer to main Fixed window.sentraDesktop via contextBridge desktop/preload.ts
Provider keys at rest AES-256-GCM with ENCRYPTION_KEY, per-key IV lib/crypto.ts
Public vs admin Supabase Separate anon and service-role clients lib/supabase/public.ts, lib/supabase/admin.ts
Xendit webhooks timingSafeEqual token check lib/billing/xendit-client.ts
Auth abuse Per-IP and per-email limits, SHA-256 key hashing lib/auth/abuse-protection.ts

Clone this wiki locally