# 4DA Security Audit Guide **Version:** 1.0.0 **Last updated:** 2026-03-30 **Applies to:** 4DA v1.0.0 (commit history on `main`) This document maps every trust-critical code path in the 4DA codebase so that security auditors and technically minded users can verify 4DA's privacy and security claims without reading 300+ Rust modules. --- ## 1. Purpose 4DA claims to be privacy-first, local-first, and zero-telemetry. This guide exists so you do not have to take our word for it. It provides: - A complete inventory of every outbound network connection the application makes - The exact files and line ranges where API keys are handled - Verification commands you can run yourself to confirm each claim - A description of what a vulnerability would look like in each area **Who this is for:** Security researchers, enterprise IT teams evaluating 4DA for deployment, privacy-conscious users, and anyone performing due diligence on the codebase. **What this is not:** Marketing material. Every file path and line number in this document is real and verifiable against the source code. --- ## 2. Architecture Overview 4DA is a [Tauri 2.0](https://v2.tauri.app/) desktop application. The mental model: ``` +---------------------------------------------------+ | React/TypeScript Frontend (Webview) | | - Runs in a system webview (WebView2/WebKit) | | - Restricted by Content Security Policy | | - Communicates ONLY via Tauri IPC invoke() | +---------------------------------------------------+ | IPC Bridge | (only registered #[tauri::command] functions) +---------------------------------------------------+ | Rust Backend (Tauri process) | | - All network requests originate here | | - All data storage managed here | | - SQLite + sqlite-vec for local database | | - OS keychain for secret storage | +---------------------------------------------------+ | +---------------------------------------------------+ | Local Filesystem | | - data/4da.db (SQLite) | | - data/settings.json (non-sensitive prefs) | | - OS Keychain (API keys) | +---------------------------------------------------+ ``` **Key architectural constraint:** The frontend is treated as untrusted. It cannot make network requests directly (CSP blocks this). All external communication passes through explicitly registered Rust command handlers. --- ## 3. Quick Audit Checklist Ten things to verify, each with a single command. Run these from the repository root. | # | Claim | Verification Command | |---|-------|---------------------| | 1 | Telemetry module makes zero network calls | `grep -rn "reqwest\|fetch\|HttpClient\|\.send()" src-tauri/src/telemetry.rs` (expect: no matches) | | 2 | API keys are redacted in Debug output | `grep -n "REDACTED" src-tauri/src/settings/types.rs` (expect: lines 40, 49, 293, 571) | | 3 | API keys are zeroized on drop | `grep -n "zeroize" src-tauri/src/settings/types.rs` (expect: lines 9, 59, 60) | | 4 | CSP blocks arbitrary connect-src | `grep "connect-src" src-tauri/tauri.conf.json` (expect: explicit allowlist, no wildcards) | | 5 | No `frame-src` allowed | `grep "frame-src" src-tauri/tauri.conf.json` (expect: `frame-src 'none'`) | | 6 | Toolkit HTTP has domain allowlist | `grep -A 25 "ALLOWED_DOMAINS" src-tauri/src/toolkit_http.rs` (expect: ~20 specific domains) | | 7 | Data export strips sensitive fields | `grep -A 20 "SENSITIVE_KEYS" src-tauri/src/data_export.rs` (expect: 18 key patterns) | | 8 | Team crypto uses XChaCha20Poly1305 | `grep "XChaCha20Poly1305" src-tauri/src/team_sync_crypto.rs` (expect: import and usage) | | 9 | Private keys zeroized on drop | `grep -A 5 "impl Drop" src-tauri/src/team_sync_crypto.rs` (expect: `key.zeroize()`) | | 10 | Update signatures verified | `grep "pubkey" src-tauri/tauri.conf.json` (expect: base64-encoded Minisign public key) | --- ## 4. Trust-Critical Code Map ### 4.1 API Key Storage and Handling **Files:** - `src-tauri/src/settings/types.rs` -- Settings struct definitions, Debug redaction, zeroize - `src-tauri/src/settings/keystore.rs` -- Platform keychain integration (199 lines) - `src-tauri/src/settings/` -- Full settings management module **How it works:** API keys are stored in the OS platform keychain under the service name `com.4da.app`: - **Windows:** Credential Manager - **macOS:** Keychain - **Linux:** Secret Service (GNOME Keyring / KWallet) Four key names are managed (see `keystore.rs` line 16): ``` llm_api_key, openai_api_key, x_api_key, license_key ``` **Security measures implemented:** 1. **Zeroize on drop** (`types.rs` lines 57-62): `LLMProvider` implements `Drop` which calls `.zeroize()` on `api_key` and `openai_api_key` fields, overwriting memory with zeros when the struct is deallocated. 2. **Debug redaction** (`types.rs` lines 31-55): Custom `Debug` impl for `LLMProvider` prints `[REDACTED]` instead of key values. Same for `LicenseConfig` (lines 286-302) and the top-level `Settings` struct (lines 552-589) which redacts `x_api_key`. 3. **Automatic migration** (`keystore.rs` lines 127-198): `migrate_from_plaintext()` moves keys from `settings.json` to the OS keychain. After migration, plaintext fields are cleared. 4. **API error sanitization** (`llm.rs` lines 29-57): `sanitize_api_error()` redacts strings matching API key patterns (`sk-*`, `pk_*`, long alphanumeric tokens) from error messages before they reach logs or the frontend. **What a vulnerability would look like:** - A code path that logs `settings.llm.api_key` directly (bypassing the Debug impl) - A serialization path that writes keys to disk without redaction - A frontend-exposed command that returns raw key values **Verification:** ```bash # Confirm no raw key logging (should return only REDACTED references) grep -rn "api_key" src-tauri/src/ --include="*.rs" | grep -i "log\|info!\|warn!\|debug!\|error!\|tracing" | grep -v "REDACTED\|redact\|mask\|strip\|sanitize\|key_name\|test\|//\|doc" # Confirm zeroize dependency is used grep "zeroize" src-tauri/Cargo.toml ``` --- ### 4.2 Network Boundary (All Outbound Connections) This is the most critical section. Every byte that leaves the machine originates from one of the paths listed below. There are no others. **HTTP Client definitions** (`src-tauri/src/http_client.rs`, 63 lines): | Client | Purpose | Timeout | |--------|---------|---------| | `HTTP_CLIENT` | General-purpose (license validation, API probes) | 30s | | `PROBE_CLIENT` | Health checks, status probes | 15s | | `TEAM_CLIENT` | Team relay operations | 15s | Additional purpose-built clients exist in specific modules (documented in `http_client.rs` lines 8-17): `embeddings.rs` (90s), `llm.rs` (120s), `webhooks.rs` (10s), and others. **Complete inventory of outbound network destinations:** | Module | File | Destination | Purpose | User-initiated? | |--------|------|-------------|---------|-----------------| | Source adapters | `src-tauri/src/sources/hackernews.rs` | `hacker-news.firebaseio.com` | Fetch HN stories | Automatic (monitoring) | | Source adapters | `src-tauri/src/sources/reddit.rs` | `www.reddit.com` | Fetch Reddit posts | Automatic (monitoring) | | Source adapters | `src-tauri/src/sources/github.rs` | `api.github.com` | Fetch trending repos, CVEs | Automatic (monitoring) | | Source adapters | `src-tauri/src/sources/arxiv.rs` | `export.arxiv.org` | Fetch arXiv papers | Automatic (monitoring) | | Source adapters | `src-tauri/src/sources/rss.rs` | User-configured RSS URLs | Fetch RSS feeds | User-configured | | Source adapters | `src-tauri/src/sources/twitter.rs` | `api.x.com` | Fetch tweets (BYOK) | User-configured | | Source adapters | `src-tauri/src/sources/youtube.rs` | `www.youtube.com` | Fetch channel RSS | User-configured | | Source adapters | `src-tauri/src/sources/lobsters.rs` | `lobste.rs` | Fetch Lobsters stories | Automatic (monitoring) | | Source adapters | `src-tauri/src/sources/devto.rs` | `dev.to` | Fetch Dev.to articles | Automatic (monitoring) | | Source adapters | `src-tauri/src/sources/producthunt.rs` | `www.producthunt.com` | Fetch PH posts | Automatic (monitoring) | | Source adapters | `src-tauri/src/sources/cve.rs` | `api.github.com` (GHSA) | Fetch security advisories | Automatic (monitoring) | | LLM provider | `src-tauri/src/llm.rs` | `api.anthropic.com` | Anthropic Claude API | User-configured (BYOK) | | LLM provider | `src-tauri/src/llm.rs` | `api.openai.com` | OpenAI GPT API | User-configured (BYOK) | | LLM provider | `src-tauri/src/llm.rs` | `localhost:11434` | Ollama (local) | User-configured | | Embeddings | `src-tauri/src/embeddings.rs` | `api.openai.com` or `localhost:11434` | Text embeddings | Automatic (indexing) | | License validation | `src-tauri/src/settings/license.rs` | `api.keygen.sh` | Validate license key | On activation + periodic | | App updates | Tauri updater plugin | `github.com/.../releases` | Check for updates | Automatic (configurable) | | Team relay | `src-tauri/src/team_sync_commands.rs` | User-configured relay URL | Encrypted metadata sync | User-configured (opt-in) | | Webhooks | `src-tauri/src/webhooks.rs` | Admin-configured URLs | Enterprise event webhooks | Admin-configured | | SSO/OIDC | `src-tauri/src/sso_crypto.rs` | IdP discovery endpoints | JWKS key fetch | Enterprise SSO | | Digest email | Uses `lettre` SMTP | User-configured SMTP server | Send digest emails | User-configured | **What is NOT in this list (and should never be):** - Telemetry endpoints - Analytics services - Crash reporters - Advertising networks - 4DA Systems servers (beyond GitHub releases for updates) **What a vulnerability would look like:** - A `reqwest` call in `telemetry.rs` (there are currently zero) - An HTTP client sending data to a domain not in the CSP allowlist - User content (article text, search queries, project file contents) sent to any endpoint other than the user-configured LLM provider **Verification:** ```bash # Find ALL outbound HTTP calls in the entire Rust codebase grep -rn "\.send().await\|\.post(\|\.get(" src-tauri/src/ --include="*.rs" | grep -v "test\|#\[cfg(test)\]" | grep -v "//" | sort # Confirm telemetry.rs has zero network calls grep -c "reqwest\|HttpClient\|\.send()" src-tauri/src/telemetry.rs # Expected output: 0 # List all reqwest usage by file grep -rl "reqwest" src-tauri/src/ --include="*.rs" | sort ``` --- ### 4.3 IPC Boundary (Frontend to Backend) **How Tauri IPC works:** The frontend can only call Rust functions that are explicitly registered in the `invoke_handler`. This registration happens in `src-tauri/src/lib.rs` starting at line 471. The complete command list spans lines 471-891 (~420 lines of command registrations). Every `invoke()` call from the frontend must match one of these registered handlers. Unregistered calls are silently rejected by Tauri. **Capability system** (`src-tauri/capabilities/default.json`): ```json { "identifier": "default", "windows": ["main"], "permissions": [ "core:default", "opener:default", "notification:default", "deep-link:default", "updater:default" ] } ``` This restricts the main window to only the listed Tauri plugin permissions. No filesystem access, no shell access, no clipboard access beyond what these plugins provide. **What a vulnerability would look like:** - A `#[tauri::command]` function that accepts arbitrary SQL or shell commands - A command that returns raw file contents without path validation - A registered command that was intended to be internal-only **Verification:** ```bash # Count total registered IPC commands grep -c "::" src-tauri/src/lib.rs | head -1 # Or more precisely, count lines in the generate_handler block: sed -n '/generate_handler/,/\]/p' src-tauri/src/lib.rs | grep "::" | wc -l # List all registered commands sed -n '/generate_handler/,/\]/p' src-tauri/src/lib.rs | grep "::" | sed 's/.*:://' | sed 's/,.*//' | sort # Find all #[tauri::command] functions across the codebase grep -rn "#\[tauri::command\]" src-tauri/src/ --include="*.rs" | wc -l # Compare with the generate_handler count to detect orphaned commands ``` --- ### 4.4 Content Security Policy **File:** `src-tauri/tauri.conf.json` line 35 The CSP restricts what the webview can do at the browser engine level: ``` default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self' https://api.anthropic.com https://api.openai.com http://localhost:11434 https://hacker-news.firebaseio.com https://export.arxiv.org https://www.reddit.com https://api.github.com https://api.keygen.sh; font-src 'self' data:; frame-src 'none'; object-src 'none'; base-uri 'self' ``` **Key restrictions:** - `frame-src: 'none'` -- No iframes allowed (blocks clickjacking and embedding attacks) - `object-src: 'none'` -- No Flash/Java/plugin embedding - `script-src: 'self'` -- No inline scripts, no external script sources - `connect-src` -- Explicit allowlist of external domains (only LLM providers, source APIs, and license validation) - `base-uri: 'self'` -- Prevents base tag injection **Note on `style-src 'unsafe-inline'`:** This is common in Tauri/React apps where CSS-in-JS is used. It does not enable script injection because `script-src` is restricted to `'self'`. The app has three hard dependencies on inline styles: (1) `index.html` carries an inline `