Relay is a desktop application built with Electron that handles sensitive operational data. This document outlines our security model, threat considerations, and implementation details.
Main Process:
- Runs with full Node.js access
- Manages file system operations, IPC handlers, and system integrations
- No direct exposure to untrusted content
Renderer Process:
- Context isolation enabled (
contextIsolation: true) - Node integration disabled (
nodeIntegration: false) - Sandbox mode enabled (
sandbox: true) - Communication only via secure IPC bridge (preload script)
Preload Script:
- Acts as a secure bridge between main and renderer
- Exposes only explicitly defined APIs via
contextBridge - No direct access to Node.js or Electron APIs from renderer
IPC operations are protected by a token-bucket rate limiter (src/main/rateLimiter.ts). Each bucket has a configurable burst capacity and per-second refill rate:
| Bucket | Burst | Refill | Scope |
|---|---|---|---|
fileImport |
5 | 1 per 10s | CSV/file import operations |
dataMutation |
100 | 10/s | CRUD operations (add/remove/update) |
dataReload |
3 | 1 per 2s | Full data reload requests |
fsOperations |
10 | 2/s | Open-path, open-external shell operations |
network |
10 | 1/s | Outbound network requests |
rendererLogging |
60 | 20/s | Renderer-to-main log forwarding |
When a bucket is exhausted the request is rejected and a retryAfterMs value is returned. Rate-limit events are logged to the ipc logger.
The application enforces a strict Content Security Policy:
Production:
default-src 'self';
script-src 'self' 'sha256-[hash]';
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
connect-src 'self' [whitelisted APIs];
font-src 'self' data:;
frame-src 'self' [trusted domains];
Development:
- Relaxed script-src to allow HMR:
'unsafe-eval' 'unsafe-inline' - All other restrictions remain in place
Additional Headers:
X-Content-Type-Options: nosniff- Prevents MIME type sniffingX-Frame-Options: DENY- Prevents clickjackingX-XSS-Protection: 1; mode=block- Enables XSS filteringReferrer-Policy: strict-origin-when-cross-origin- Controls referrer information
AI Chat Feature:
- Uses
<webview>tags to embed external AI services (Gemini, ChatGPT) - Partition Isolation: All AI webviews use
partition="ai-chat-session"for isolated storage/cookies - Automatic Cleanup: Session data is cleared when the tab is unmounted or app exits
- User Agent Masking: Uses modern Chrome user agent to ensure compatibility
- No File Access: AI webviews cannot access local file system
- Suspended State: Webviews are suspended when not active to reduce resource usage
Security Considerations:
- AI webviews are explicitly marked as untrusted content
- No sensitive application data is shared with AI services
- Users are warned that data clears on tab exit
- Consider implementing additional CSP for webview content if needed
Storage:
- Sensitive credentials (proxy auth, API keys) use Electron's
safeStorageAPI - Data is encrypted at rest using OS-level encryption:
- Windows: Data Protection API (DPAPI)
- macOS: Keychain
- Linux: libsecret / gnome-keyring
- Credentials are stored in app's userData directory with OS-level encryption
Authentication Flow:
- HTTP 401 challenges are intercepted by the main process
- A secure nonce (32 random bytes, hex-encoded) is generated and associated with the auth callback
- User is prompted securely via IPC; the renderer must present the correct nonce to complete auth
- Nonces are one-time-use and expire after 5 minutes — expired/invalid nonces are rejected
- Credentials can be optionally cached in-memory using
safeStorageencryption (30-minute TTL, refreshed on use) - Periodic cleanup (every 60 seconds) prunes expired nonces and cached credentials
Path Validation:
- All file paths are validated against allowed directories
- Path traversal attempts (
../,..\\) are blocked - Absolute paths outside data directory are rejected
- Implemented in:
src/main/pathValidation.ts,src/main/utils/pathSafety.ts
IPC Validation:
- All IPC messages are validated using Zod schemas
- Type-safe validation for all inputs
- Rate limiting on sensitive operations
- Implemented in:
src/shared/ipcValidation.ts,src/main/ipcHandlersValidation.ts
Cache Handler Validation:
- Cache IPC handlers (
CACHE_READ,CACHE_WRITE,CACHE_SNAPSHOT) validate thecollectionparameter against a hard-coded allowlist of known collection names (e.g.contacts,servers,oncall,notes,alert_history, etc.) CACHE_WRITEadditionally validates theactionparameter against an allowlist ofcreate | update | deleteand rejects non-object records- Invalid requests are logged and silently dropped — no data is returned or written
- Implemented in:
src/main/handlers/cacheHandlers.ts
Backup Restore Validation:
- Backup filenames are validated with a strict regex (
/^[\w.-]+\.zip$/) and explicitly reject path traversal sequences (..) - This prevents an attacker from tricking the restore handler into overwriting arbitrary files outside the backup directory
- After a successful restore, the offline cache is invalidated to prevent serving stale data
- Implemented in:
src/main/handlers/backupHandlers.ts
Data Validation:
- CSV imports are validated for encoding, structure, and content
- Phone numbers, emails, and other fields are sanitized
- Size limits enforced on file uploads and imports
Uncaught Exceptions:
- Uncaught exceptions display a synchronous dialog with Quit and Continue buttons (default: Quit)
- Choosing "Continue" logs a warning and lets the user keep working — useful for non-fatal errors that would otherwise force-quit the app
- The error message is shown to the user in the dialog
Unhandled Promise Rejections:
- Tracked with a rolling-window counter (3 rejections within 60 seconds triggers a notification)
- When the threshold is exceeded, a stability warning is broadcast to all renderer windows via
app:error-notification - The counter resets after each notification to prevent spam
Implemented in: src/main/app/errorHandlers.ts
All structured log data passes through PII redaction (src/shared/logRedaction.ts) before being written:
- Key-based redaction: Fields matching sensitive key patterns (
password,token,api_key,secret,email,phone,address, etc.) are replaced with[REDACTED] - String-value scanning: All string values (including
Error.messageandError.stack) are scanned for email addresses and phone numbers, which are replaced with[REDACTED_EMAIL]and[REDACTED_PHONE]respectively - Error objects:
Errorinstances are serialized to{ name, message, stack }with PII redaction applied tomessageandstack - Circular reference protection: Object graphs with circular references are safely handled with
[Circular]placeholders
The app enforces a hard limit of 5 simultaneous auxiliary windows (MAX_AUX_WINDOWS in src/main/app/windowFactory.ts). When the limit is reached, further createAuxWindow requests are silently rejected with a warning log. Destroyed windows are cleaned up before each check.
ACID Transactions:
- PocketBase stores all application data in an embedded SQLite database
- All writes are protected by SQLite ACID transactions — partial writes and corruption are not possible
- No application-level file locking or atomic rename operations are required
Offline Write Queue:
- When the PocketBase server is unreachable, writes are queued in
PendingChanges(local SQLite via better-sqlite3) SyncManagerreplays the queue and resolves conflicts when the connection is restored- Conflict resolution is last-write-wins with server records taking precedence
Backup Strategy:
BackupManagercreates backups via the PocketBase Admin API (pb.backups.create())- Backups are stored in
pb_data/backups/as timestamped.ziparchives - Retention policy enforces a maximum of 10 backups; older backups are pruned automatically
-
Local Data Tampering
- Mitigation: SQLite ACID transactions, PocketBase access controls, validation
- Severity: Medium
-
Path Traversal Attacks
- Mitigation: Strict path validation, sandboxing
- Severity: High
-
IPC Message Injection
- Mitigation: Type-safe validation, rate limiting
- Severity: High
-
Credential Theft
- Mitigation: OS-level encryption, context isolation
- Severity: Critical
-
XSS in Renderer Process
- Mitigation: CSP, context isolation, input sanitization
- Severity: High
-
Untrusted Content in WebViews
- Mitigation: Partition isolation, no file access, cleanup
- Severity: Medium
-
Physical Access to Device
- Users with physical access can extract data
- OS-level encryption provides some protection
-
Memory Dumps
- Sensitive data may be present in memory during operation
- Consider implementing memory clearing for sensitive operations
-
Supply Chain Attacks
- Dependencies are not fully audited
- Regular updates and vulnerability scanning recommended
-
Never expose Node.js APIs directly to renderer
- Always use IPC with validation
- Use preload script as the only bridge
-
Validate all inputs
- Use Zod schemas for type-safe validation
- Never trust data from renderer or external sources
-
Use parameterized queries and filter escaping
- PocketBase is now the data store — use
escapeFilter()frompocketbase.tsfor filter values - Never interpolate user input directly into PocketBase filter strings
- PocketBase is now the data store — use
-
Minimize use of
anytype- Leverage TypeScript's strict mode
- Use proper types for better security guarantees
-
Log security-relevant events
- Track authentication attempts
- Monitor file access patterns
- Alert on validation failures
-
Keep the application updated
- Security patches are released regularly
- Enable auto-updates if available
-
Use strong system passwords
- Credential encryption relies on OS-level security
- Strong system passwords protect stored credentials
-
Be cautious with AI chat
- Data is not sent to external services by default
- Session data clears on tab exit for privacy
-
Review data directory permissions
- Ensure data directory has appropriate file permissions
- Consider full-disk encryption for sensitive data
If you discover a security vulnerability, please report it to the project maintainers via:
- GitHub Security Advisories (preferred)
- Email to the maintainer (see repository)
Please do not open public issues for security vulnerabilities.
- Description of the vulnerability
- Steps to reproduce
- Impact assessment
- Suggested mitigation (if any)
- Initial Response: Within 48 hours
- Status Update: Within 7 days
- Fix Timeline: Varies based on severity (Critical: 7 days, High: 14 days, Medium: 30 days)
-
Enhanced WebView Security
- Implement stricter CSP for webview content
- Add permission system for webview capabilities
- Consider alternatives to webview tags
-
PocketBase Hardening
- Enforce collection-level access rules for all PB collections
- Audit filter escaping across all renderer services
- Consider encrypted PocketBase data directory for high-security deployments
-
Enhanced Logging
- Security event logging framework
- Anomaly detection for suspicious patterns
- Audit trail for sensitive operations
-
Network Security
- Certificate pinning for critical APIs
- Request signing for authenticated APIs
- Rate limiting on network requests
-
Code Signing
- Sign application binaries for release
- Implement update verification
- Add integrity checks for critical files
This application handles operational data and may be subject to various compliance requirements:
- Data Residency: In server mode, all data is stored locally in the embedded PocketBase SQLite database. In client mode, data lives on the designated server node.
- Data Retention: User controls retention via the application or direct PocketBase admin access
- Data Encryption: OS-level encryption for the config secret (
safeStorage); PocketBase database is plaintext SQLite — use full-disk encryption for sensitive deployments - Access Controls: Two-tier auth (superuser + app user); superuser access is restricted to localhost
Organizations using this application should assess their specific compliance requirements and implement additional controls as needed.
-
1.1.0 (2026-03-27): Updated to reflect current codebase
- Documented cache handler validation (collection allowlist, action validation)
- Documented backup restore filename validation (path traversal protection)
- Documented rate limiter buckets with current configuration
- Documented uncaughtException "Continue" option and unhandled rejection threshold
- Documented PII log redaction (including Error message/stack scanning)
- Documented auxiliary window limit (max 5)
- Updated credential management with nonce expiry, cache TTL, and cleanup details
-
1.0.0 (2026-01-24): Initial security documentation
- Documented current security architecture
- Established threat model
- Defined security best practices
Last Updated: 2026-03-27 Reviewed By: Security Agent Next Review: 2026-09-27