Security model, secret handling, and trust boundaries for MasterSelects.
MasterSelects is a local-first application. Rendering, editing, and most analysis happen in the browser, but the app also has two explicit local bridge surfaces:
- The Vite dev bridge used in development
- The Native Helper bridge used for production/local companion workflows
The main trust boundaries are:
- The browser origin
- The dev bridge auth token injected by Vite
- The Native Helper auth token generated at startup
- Explicit allowed file roots
- IndexedDB for encrypted API keys
- OPFS for the SAM 2 model cache
External services are only contacted when the user enables a feature that needs them, such as AI chat, transcription, AI video generation, or multicam EDL generation.
API keys are stored encrypted in IndexedDB using the Web Crypto API:
- Each browser instance generates a unique AES-256-GCM key
- The encryption key is stored alongside the encrypted secrets in IndexedDB
- This blocks casual inspection, but not same-origin scripts or browser extensions with storage access
The .keys.enc export/import path remains disabled. The previous implementation relied on a deterministic hardcoded passphrase, so it was only obfuscation. Keys must be re-entered manually on a new machine until a passphrase-based scheme is implemented.
| Key | Service | Storage |
|---|---|---|
openai |
OpenAI API | Encrypted IndexedDB |
assemblyai |
AssemblyAI | Encrypted IndexedDB |
deepgram |
Deepgram | Encrypted IndexedDB |
piapi |
PiAPI gateway | Encrypted IndexedDB |
kieai |
Kie.ai | Encrypted IndexedDB |
youtube |
YouTube Data API | Encrypted IndexedDB |
klingAccessKey |
Kling AI | Encrypted IndexedDB |
klingSecretKey |
Kling AI | Encrypted IndexedDB |
All log output is scanned for common secret patterns and redacted before it is stored in the log buffer or exposed through the AI bridge.
This applies to:
- Log messages
- Data objects attached to log entries
- Error messages and stack traces
- AI tool bridge responses
| Pattern | Example |
|---|---|
| OpenAI / Anthropic API keys | sk-proj-..., sk-ant-..., sk-... |
| Bearer tokens | Bearer eyJ... |
x-api-key header values |
x-api-key: abc123... |
| URL key parameters | ?key=AIzaSy... |
| Long hex tokens | 40+ hex chars |
| Long alphanumeric tokens | 40+ chars |
| Type | Why |
|---|---|
| UUIDs | Used as clip and track IDs |
| Hex color codes | Short hex strings like #ff4444 |
| Short strings | Anything under the secret-length thresholds |
| Normal log text | Common messages, numbers, paths |
The Vite dev bridge exposes local HTTP endpoints for AI tooling and local file access. The browser only attaches the dev bridge token when __DEV_BRIDGE_TOKEN__ is present.
The current flow is:
POST /api/ai-tools -> Vite server -> HMR -> browser -> executeAITool()
The bridge also serves local file endpoints used by the AI media tools:
/api/local-file/api/local-files
Those routes are protected by:
- A bearer token injected by Vite
- Loopback-only origins
- Explicit allowed roots
- Absolute-path validation plus traversal rejection
Allowed roots are seeded from the Vite config from the project root, temp directory, Desktop, Documents, Downloads, and Videos, and can be extended through MASTERSELECTS_ALLOWED_FILE_ROOTS.
The Native Helper runs on 127.0.0.1 only and uses its own random auth token:
- HTTP on port
9877 - WebSocket on port
9876 GET /ai-toolsandGET /api/ai-toolsare status-only and do not require authPOST /ai-toolsandPOST /api/ai-toolsrequire the bearer tokenGET /startup-tokenis localhost-only and lets the browser discover the helper token
The helper also writes its auth token to a temp file named masterselects-helper.token so the browser can discover it during startup.
The helper enforces:
- Bearer-token authentication for HTTP and WebSocket requests
- Origin checks for the WebSocket connection
- Explicit allowed file roots for file reads, uploads, and directory listing
- Rejection of traversal and UNC paths
The AI chat approval UI is separate from these bridge checks. It is a user-experience gate for mutating or sensitive tools, not the underlying security boundary.
- IndexedDB encryption is only defense against casual inspection. A same-origin script or extension with storage access can still read the keys.
- Development does not add CSP headers by default.
- Log redaction is pattern-based. Unrecognized secret formats may still leak if they reach the logger before redaction rules are added.
- The dev bridge token is local-process-scoped. The token file is stored in the project root as
.ai-bridge-token, so any local process with file access can read it. - The Native Helper can be started with
--no-auth, but that disables the auth boundary entirely and is not recommended. - API keys are still sent to external services over HTTPS when you enable AI features that need them.
If you discover a security vulnerability:
- Do not open a public GitHub issue
- Contact the maintainers privately
- Include steps to reproduce the issue
- Allow reasonable time for a fix before disclosure
Source: src/services/security/redact.ts, src/services/logger.ts, src/services/security/fileAccessBroker.ts, src/services/security/devBridgeAuth.ts, vite.config.ts, tools/native-helper/src/server.rs, tools/native-helper/src/main.rs, src/components/panels/AIChatPanel.tsx