Security Policy - TerminaLLM
Overview
TerminaLLM is a mobile SSH terminal client designed with security as a core principle. This document describes our security model, implemented protections, and guidelines for secure usage.
Security Architecture
Threat Model
TerminaLLM handles sensitive SSH credentials and provides access to remote systems. Our security design addresses:
- Credential Theft: Attackers gaining access to stored credentials
- Man-in-the-Middle (MITM): Attackers intercepting SSH connections OR proxy-to-Anthropic API traffic
- Device Compromise: Attackers with physical or malware access to the device
- Brute Force: Attackers attempting to guess MFA codes
- Session Hijacking: Attackers attempting to take over authenticated sessions
- Shoulder Surfing: Visual observation of credentials or terminal content
- T-TCP Daemon Compromise: A local attacker who already has shell access to the user's remote server could subvert the optional T-TCP helper binary the app deploys to
~/.terminallm/. The daemon listens only on a Unix socket inside the user's SSH session (no open TCP ports) and is compiled with-trimpath(no developer filesystem paths embedded). Users can disable deployment via Settings → Help and uninstall by deleting~/.terminallm/on the server
Security Principles
- Credentials Not Stored by Default: SSH passwords are entered at connection time and cleared from memory immediately after use. Users may opt in to the Credential Vault, which encrypts credentials locally using AES-256-GCM with HKDF-Expand key derivation, per-credential random nonces, and configurable TTL expiry
- Defense in Depth: Multiple layers of security (MFA, biometrics, encryption)
- Minimal Privilege: App requests only necessary permissions
- Fail Secure: On errors, default to denying access
- Transparent Security: Users informed of security events via audit logs
Implemented Security Controls
Authentication
- TOTP MFA: Optional time-based one-time passwords (RFC 6238); offered during onboarding and, once enabled, required at every app unlock (
mfa_service.dart) - Biometric Auth: Optional Face ID / Fingerprint as secondary factor (
security_service.dart) - Rate Limiting: TOTP: 5 failed attempts triggers 2-minute lockout (persistent). Connection rate limiting is profile-driven: Paranoid (3/min, 10-min lockout), Casual (5/min, 5-min lockout), Speedster (10/min, 1-min lockout) (
mfa_service.dart,security_service.dart) - Code Reuse Prevention: TOTP codes cannot be reused within time window (
mfa_service.dart) - Session Timeout: Re-authentication required after inactivity (
security_service.dart)
Credential Protection
- No Default Password Storage: Passwords not stored by default; opt-in Credential Vault available (
connect_screen.dart,credential_vault_service.dart) - Credential Vault (Opt-in): AES-256-GCM encryption with HKDF-Expand key derivation, per-credential random nonces, 128-bit auth tags, configurable TTL (
credential_vault_service.dart) - Secure Memory Clearing: Credentials zeroed after use (
terminal_screen.dart,ssh_service.dart) - Platform Encryption: Platform-native encrypted storage (Throughout)
- Clipboard Auto-Clear: Clipboard cleared automatically after paste (default 3s, configurable per security profile: Paranoid 3s, Casual 10s, Speedster disabled) (
security_service.dart)
Connection Security
- SSH Host Key Verification: SHA256 fingerprints stored and verified (
ssh_service.dart) - MITM Detection: Warning on host key change (
terminal_screen.dart) - Connection Timeout: 30-second timeout prevents hanging (
ssh_service.dart) - Keep-Alive: Prevents server-side timeout disconnections (
ssh_service.dart) - TLS Pinning (proxy): Public-key pinning (SubjectPublicKeyInfo SHA-256) on
api.terminallm.app. Three pins rotated for cert renewal: GTS WR3 intermediate (primary), GTS Root R1 (backup), current leaf (Dart-only third pin). Enforced at three layers:network_security_config.xml(Android),NSPinnedDomainsinInfo.plist(iOS), and the DartbadCertificateCallbackinpinned_http_client.dart. Fail-closed: a request whose served cert chain matches none of the three SPKI hashes is rejected without a fallback path. Rotation runbook:docs/tls-pinning-rotation-runbook.md(pinned_http_client.dart,network_security_config.xml,Info.plist) - iOS Privacy Manifest:
ios/Runner/PrivacyInfo.xcprivacydeclares all data collection per Apple's Privacy Manifest requirement.NSPrivacyTracking=false. Reason codes published for every NSPrivacyAccessedAPIType used (UserDefaults, FileTimestamp, SystemBootTime, DiskSpace) (ios/Runner/PrivacyInfo.xcprivacy)
T-TCP Helper Daemon Security
When users opt into Termina-TCP, the app deploys a small Go daemon binary (~6.5 MB uncompressed, ~2.6–2.9 MB gzipped) to ~/.terminallm/ on the user's remote server. The daemon's security posture:
- No open ports: The daemon listens exclusively on a Unix socket inside the user's existing SSH session. There is no listening TCP port for an external attacker to reach. Communication between the app and daemon flows through
ssh execto the Unix socket — the same trust boundary as the SSH session itself - No developer paths in binary: Compiled with
-trimpathso reverse-engineering the binary surfaces no developer filesystem layout. Module paths in the binary point togithub.com/bmills23/terminallm-daemon/..., not the build host - Audit trail on server: Daemon logs to
~/.terminallm/logs/on the user's server (user-accessible). No telemetry sent off-server - User-controlled deployment: Users can disable T-TCP deployment via Settings → Help → "What we install on your server" → toggle off. They can also
rm -rf ~/.terminallm/directly on the server to fully uninstall - No remote auto-update: The daemon is bundled with each app build, deployed once per user-server pair, and never auto-updated from a remote source. Upgrades happen when the user installs a new app version and reconnects
Hive Daemon Security (Swarm, Pro)
When users opt into Swarm, the app installs Hive (about 4.5 MB: a CLI and a daemon binary) to ~/.hive/ on the user's remote server. Hive's security posture:
- Loopback-only listener: The daemon binds exclusively to
127.0.0.1:7333. It is never reachable from the network; only processes on the server itself can connect - Token authentication: A per-install authentication token at
~/.hive/auth-tokenis required for daemon commands, so other local users on a shared server cannot drive the daemon - Driven over SSH only: The app controls Hive by executing
~/.hive/bin/hiveinside the user's existing SSH session. No new inbound network path to the server is created - No remote fetch, no auto-update: Both binaries are bundled inside the app and pushed over the user's own SSH connection at install time. Nothing is downloaded from third-party hosts, and the daemon never self-updates. Upgrades happen only through app updates and explicit reinstall
- Explicit install consent: Hive is installed only when the user taps Install in the Swarm setup, after the "What we install on your server" disclosure. It is never deployed as a side effect of connecting
- Lifecycle managed, easy removal: The daemon runs only while Swarm is in use and is stopped when no longer needed. Users can stop the daemon or uninstall from the Swarm screen's Hive menu, or run
rm -rf ~/.hiveon the server - Agents run as the user: Swarm agents (Claude Code, Codex, etc.) execute on the user's server under the user's own Unix account and AI CLI logins. Their prompts and output stay inside the SSH session; the app never proxies them through TerminaLLM infrastructure
Device Security
- Jailbreak/Root Detection: Warns on compromised devices (
security_service.dart) - Screen Capture Prevention: Platform-native screenshot blocking (
MainActivity.kt,AppDelegate.swift) - Developer Mode Warning: Alerts when developer mode enabled (
security_service.dart)
Port Forwarding Security
- Localhost-only binding: Tunnels bind exclusively to
InternetAddress.loopbackIPv4(127.0.0.1), preventing remote network access (port_forward_service.dart) - Audit trail: Tunnel creation, stop, and errors logged as security events (
port_forward_service.dart) - Auto-cleanup: All tunnels stopped on SSH disconnect or app dispose (
terminal_screen.dart,port_forward_service.dart)
Jump Host Security
- Credential clearing: Jump host password, private key, and passphrase cleared from memory immediately after SSH handshake (
ssh_service.dart) - Host key verification: Both jump host and target host keys independently verified against stored fingerprints (
ssh_service.dart) - Self-reference prevention: UI prevents selecting a profile as its own jump host (circular reference) (
connect_screen.dart) - Vault-based credentials: Jump host credentials resolved from vault at connection time, never passed through navigation (
terminal_screen.dart) - Inline credential fallback: When the vault is disabled or empty, jump host credentials can be entered inline on the connect form without storing them (
connect_screen.dart)
Media Drop Security
- File permissions: All uploaded files set to
chmod 600(owner read/write only) (terminal_screen.dart) - No local caching: Media files streamed to server, not stored on device (
terminal_screen.dart) - Audit logging: Upload events logged with source type, path, and file size (
terminal_screen.dart) - On-demand permissions: Camera and photo library access requested only when feature is used (Platform manifest)
Speech-to-Text Security
- Dual speech engines: Two engines: Built-in uses platform STT (Apple/Google — may send audio to their servers for processing). Whisper runs fully on-device via whisper.cpp — no audio leaves the device. Users choose in Settings. (
speech_service.dart) - No audio storage: TerminaLLM never permanently stores audio. Whisper mode creates a temporary WAV file that is deleted immediately after transcription. (
speech_service.dart) - On-demand permissions: Microphone access requested only when the mic button is tapped (Platform manifest)
- Text-only output: Recognized text is written to the terminal; no audio intermediate stored (
terminal_screen.dart)
AI Chatbot Security
- No server-side message storage: Proxy server processes requests in real-time; conversation content is never logged or persisted (
backend/main.py) - JWT device authentication: Per-device tokens (HS256, 365-day expiry) with no personal information (
backend/auth.py,chatbot_auth_service.dart) - Encrypted local storage: Conversation history and proxy tokens stored in platform-native encrypted storage (
chatbot_service.dart,chatbot_auth_service.dart) - Rate limiting: AI chatbot is available to Pro subscribers only. The proxy server enforces subscription tier checks per device (
backend/main.py) - HTTPS-only: All proxy communication uses HTTPS (enforced by Cloud Run + custom domain) (
backend/deploy-cloudrun.sh)
iOS Live Activities Security
- Lock Screen data exposure: Live Activities display server name, AI tool, state, and file counts on the Lock Screen without device unlock. Users should be aware that bystanders can see active session metadata (
live_activity_service.dart) - App Group isolation: Session data is written to an iOS App Group shared container (
NSUserDefaults) accessible only to the app and its widget extension — not to other apps (live_activity_service.dart) - Auto-expiry: Live Activities end automatically on disconnect or after 8 hours (iOS system limit) (
live_activity_service.dart) - No credentials exposed: Live Activities never display passwords, keys, usernames, or IP addresses — only display name, tool name, and aggregate counts (
live_activity_service.dart)
Audit & Monitoring
- Security Event Logging: 1,000 events retained (
security_service.dart) - Connection Logging: All connection attempts logged (Throughout)
- Host Key Events: Verification decisions logged (
terminal_screen.dart) - Export Capability: Audit logs exportable as JSON (
security_service.dart)
Cryptographic Standards
- TOTP Generation: HMAC-SHA1 (RFC 6238)
- Host Key Fingerprint: SHA-256 (OpenSSH format)
- Credential Storage: AES-256-GCM (Platform-native encrypted storage)
- Credential Vault Key Derivation: HKDF-Expand (HMAC-SHA256) (RFC 5869)
- Credential Vault Encryption: AES-256-GCM (128-bit auth tag, 96-bit nonce) (NIST SP 800-38D)
- Backup Encryption: AES-256-GCM (NIST SP 800-38D)
- Backup Key Derivation: PBKDF2-HMAC-SHA256 (1,000,000 iterations; legacy v2 backups used 600,000 and legacy v1 backups used 150,000) (RFC 2898)
- SSH Transport: Various (negotiated) (SSH Protocol)
Security Event Types
The following events are logged to the audit trail:
Secure Usage Guidelines
For Users
- Verify Host Keys: Always verify SSH fingerprints match expected values
- Secure Your Device: Use device passcode, keep OS updated
- Protect MFA Secret: Don't screenshot or share your TOTP QR code
- Review Audit Logs: Periodically check for unauthorized access attempts
- Avoid Public Networks: Use VPN when on untrusted networks
- Report Issues: Contact us immediately if you suspect compromise
- Contain Your Swarms: Run Swarm agents only in project directories under version control, and review agent output before merging or deploying. Multiple agents can modify files concurrently, and they act with the full privileges of your server account
For Enterprise Deployment
- MDM Integration: Deploy via Mobile Device Management
- Compliance Review: Consult compliance team for regulated environments
- Network Security: Ensure SSH traffic is monitored/logged at network level
- User Training: Educate users on security features and best practices
- Incident Response: Include app in security incident procedures
Known Limitations
- No End-to-End Encryption Verification: App trusts SSH cipher negotiation
- Device Trust: Security depends on device integrity
- No Remote Wipe: No capability to remotely disable compromised instances
- Single User: No multi-user or role-based access control
- Local Audit Only: Audit logs stored locally only
- Dart GC Memory Limitation: Dart's garbage collector may retain copies of credential data in heap memory even after explicit zeroing. Platform-level device encryption and sandboxing mitigates this risk
Vulnerability Disclosure
Reporting Security Issues
If you discover a security vulnerability, DO NOT create a public GitHub issue.
Instead, email us with:
- Description of the vulnerability
- Steps to reproduce
- Potential impact
- Any suggested fixes
Response Timeline
- Acknowledgment: Within 48 hours
- Initial Assessment: Within 7 days
- Fix Timeline: Based on severity
- Critical: 24-48 hours
- High: 7 days
- Medium: 30 days
- Low: 90 days
Recognition
We appreciate responsible disclosure and will:
- Credit reporters in release notes (unless anonymity requested)
- Consider bug bounty for critical issues (at our discretion)
Security Certifications
Currently, TerminaLLM has not undergone formal security certification (SOC 2, ISO 27001, etc.). The security controls documented here represent our best-effort implementation.
For regulated environments (HIPAA, PCI-DSS, FedRAMP), please conduct your own security assessment before deployment.
Updates & Changelog
Security-relevant changes will be documented in:
- Release notes
- This SECURITY.md file
- GitHub security advisories (for vulnerabilities)
Contact
Security Issues:
General Questions:
GitHub: github.com/terminallm-issues/.github/issues
Last Security Review: March 20, 2026