# Security ## Access Control - **Optional Access Keys**: `ACCESS_KEYS` environment variable for usage restriction - **Rate Limiting**: Applied to search and inference endpoints - **Server-side Validation**: Access keys verified before proxying to SearXNG - **Key Timeout**: `ACCESS_KEY_TIMEOUT_HOURS` controls cache duration ### Access Key Validation Flow 1. User enters access key on the **AccessPage** UI 2. Client hashes the key client-side using argon2id 3. Server validates the hash against configured `ACCESS_KEYS` via `validateAccessKeyServerHook` 4. On success: key hash is stored in localStorage with timestamp 5. On subsequent loads, `useAccessKeyValidation` in `App.tsx` calls `verifyStoredAccessKey()` to check if the cached key is still valid 6. If expired (based on `ACCESS_KEY_TIMEOUT_HOURS`), user is prompted to re-enter Whether access keys are enabled is read at runtime from `/api/config`. When that request fails, the app shell refuses to render rather than assuming access keys are off, so a request that never arrives cannot skip the access key page. ### `/api/config` Exposure `/api/config` is unauthenticated by design: the client needs it before it can prove anything, and the access key page itself depends on it. It returns only whether a feature is on plus its display defaults, and never returns `ACCESS_KEYS`, `INTERNAL_OPENAI_COMPATIBLE_API_KEY`, or any other secret. Adding a field to `ServerConfig` in `shared/serverConfig.ts` publishes it to anyone who can reach the instance, so keep secrets out of that interface. ### Search Token Lifecycle Every HTTP request from client to backend carries a `token` query parameter for CSRF protection: 1. **Token Generation**: On build/startup, `regenerateSearchToken()` writes a random token to `{os.tempdir()}/minisearch-token`, readable only by the user running the build (`0600`) 2. **Client Injection**: The token is injected as `VITE_SEARCH_TOKEN` compile-time constant via Vite's `define` option 3. **Per-Request Auth**: Client includes token as `?token=` parameter on all `/search/text` and `/search/images` requests 4. **Server Verification**: `handleTokenVerification()` in `searchEndpointServerHook.ts` validates the token before proxying to SearXNG 5. **Session Tracking**: Validated tokens are stored in an in-memory `Set` (`verifiedTokens.ts`) for session counting ## Privacy - **Local-First Storage**: All data stored in IndexedDB, no cloud sync - **No Tracking**: No telemetry, analytics, or user tracking - **SearXNG Integration**: All web searches routed through privacy-focused metasearch - **No External Requests**: Optional browser-only mode for complete privacy ## Data Protection - **Access Key Hashing**: Access keys hashed using argon2id before storage (via hash-wasm) - **TTL-based Cleanup**: Automatic cleanup of cached data - **No PII Collection**: No personally identifiable information stored - **User Control**: Users can export and delete all their data ## Security Best Practices - Input validation on all endpoints - Sanitization of user-generated content - Search token generation: a per-build/per-startup token written to a temp file (`server/searchToken.ts`), using 32 bytes from the `node:crypto` CSPRNG, with the file restricted to its owner (`0600`) - HTTPS enforcement in production - Regular dependency updates via Renovate - **Argon2 Hashing**: Access keys hashed using argon2id for secure validation (not storage encryption) - **Cross-Origin Isolation**: COOP/COEP headers for SharedArrayBuffer security - **CSRF Protection**: Search tokens validated via argon2 hash comparison, over a 32-byte digest ## Server-Side Security Modules | Module | Purpose | |--------|---------| | `server/searchToken.ts` | Reads/writes the CSRF token from `{tempdir}/minisearch-token` | | `server/verifiedTokens.ts` | In-memory `Set` of verified session tokens | | `server/searchesSinceLastRestart.ts` | In-memory counters for abuse monitoring | | `server/searchEndpointServerHook.ts` | Proxies text/image search to SearXNG after token verification (via `handleTokenVerification`) | | `server/verifyTokenAndRateLimit.ts` | Verifies the Argon2 token hash and enforces rate limiting (10 requests per 10 seconds) shared by search and inference endpoints | | `server/handleTokenVerification.ts` | Middleware bridge that calls `verifyTokenAndRateLimit` and writes 400/401/429 error responses for the search and inference endpoints | | `server/configEndpointServerHook.ts` | Serves the non-secret runtime config at `/api/config`, including whether access keys are enabled | ## Threat Model - **Local Environment**: Assumes trusted local execution - **Network Requests**: All external requests go through SearXNG proxy - **AI Models**: Models run locally or through trusted providers - **Data Exfiltration**: Prevented by local-first architecture ## Related Topics - **Configuration**: `docs/configuration.md` - Environment variables for access control - **Overview**: `docs/overview.md` - Security architecture and data flow - **AI Integration**: `docs/ai-integration.md` - Privacy implications of inference types