Fully integrated SSH tunneling support. Set up Local, Remote, and Dynamic (SOCKS5) port forwarding directly from the new Tunnels screen. Tunnels can be set to auto-start when the connection opens, allowing seamless background networking.
The entire app has been redesigned with a premium aesthetic featuring layered midnight navy surfaces, glowing neon accents, and frosted glass components. The new ConnectionTile is taller, more legible, and features a smooth swipe-to-reveal action menu.
Passwords are now stored exclusively in the hardware-backed Android Keystore alongside private keys, completing the migration away from plaintext local storage. The biometric lock has also been rewritten at the routing layer for instant, secure enforcement.
- Swipe-to-Reveal Actions: Compact swipeable tiles for instant SFTP, Config, and Quick Commands.
- Built-in Tailscale: Embedded tsnet networking. Join your tailnet from inside OPA without a separate VPN app.
Add a server once — host, port, username, auth method — and it's saved forever. Your connection list becomes a launchpad. Tap any tile and you're in a full SSH session before the thought finishes.
Each connection gets a color-coded accent bar so you can spot production, staging, and dev at a glance.
Swipe left on any connection tile and three buttons slide in:
- SFTP — browse remote files
- Config — edit connection details
- QCMD — launch quick commands against this server
Release and the tile snaps back. No accidental triggers, no friction.
The terminal is a first-class mobile experience:
- Auto-fit font — 80+ columns in portrait, 120+ in landscape
- Rotate to immersive — landscape hides everything but the shell
- Smart keyboard bar — TAB, ESC, arrows, Ctrl+C always one tap away
- VT100/256-color — full terminal emulation via xterm.dart
- Scrollback — never miss output that scrolls off screen
Generate Ed25519 key pairs in the app, or import existing ones. Private keys never leave your device — stored encrypted in Android Keystore.
20+ pre-built quick commands for the tools you actually use:
- AI Agents: Claude Code, opencode, aider, Gemini CLI, Codex
- Dev Tools: htop, lazygit, tmux, nvim, btop, fastfetch
- System: journalctl, docker ps, df -h, free -m, ss -tulpn
- Network: iperf3, mtr, nmap, tcpdump, traceroute
Architecture section: Home Screen (ConnectionTile) to Terminal Screen (xterm.dart)
OPA App
Home Screen (ConnectionTile) --> Terminal Screen (xterm.dart)
--> dartssh2 (SSH via password, key, or both)
--> Tailscale (embedded tsnet: WireGuard, MagicDNS, DERP)
--> Target server
Persistent storage:
Hive DB --> profiles, quick commands
Android Keystore --> private keys (hardware-backed)
SharedPrefs --> onboarding state
| Decision | Why |
|---|---|
| Flutter + Dart | Cross-platform, native perf, mature ecosystem |
| dartssh2 | Pure-Dart SSH -- no native bindings, full control |
| xterm.dart | VT100/256-color terminal widget with scrollback |
| Tailscale (embedded) | Per-process tsnet, no VPN app or root needed |
| ConnectionTile (swipe) | GestureDetector + AnimationController, no extra deps |
| Hive | Fast local NoSQL, no SQLite boilerplate |
| Riverpod | Compile-safe state, context-independent |
| Android Keystore | HW-backed storage, keys never leave secure enclave |
lib/
+-- main.dart # App entry -- Hive init, theme, routing
+-- app_theme.dart # Dark glassmorphism theme
+-- app_router.dart # GoRouter + onboarding guard
+-- models/
| +-- connection_profile.dart # SSH profile (host, port, auth)
| +-- ssh_key_pair.dart # Ed25519 key metadata
| +-- quick_command.dart # Saved command + presets
+-- services/
| +-- ssh_service.dart # dartssh2 wrapper
| +-- key_service.dart # Key gen + Keystore CRUD
| +-- onboarding_service.dart # First-launch detection
| +-- update_service.dart # GitHub Releases update check
| +-- profile_storage_service.dart # Hive persistence
| +-- hive_adapters.dart # Hive type adapters
+-- screens/
| +-- home_screen.dart # Connection tiles + commands
| +-- onboarding_screen.dart # 4-slide welcome flow
| +-- terminal_screen.dart # xterm.dart + auto-fit + keyboard bar
| +-- profile_editor_screen.dart # Add/edit connections
| +-- key_management_screen.dart # Generate/import/delete keys
| +-- quick_commands_screen.dart # Preset browser + custom
+-- widgets/
| +-- connection_tile.dart # Swipeable tile -- tap/swipe actions
| +-- key_card.dart # Glassmorphism key card
+-- utils/
+-- constants.dart # Colors, spacing, constants
+-- agent_presets.dart # 20+ agent/tool presets
+-- ssh_key_encoder.dart # Ed25519 wire format encoding
Download the latest APK from GitHub Releases. Auto-updates are built in -- OPA checks for new versions on launch.
Prerequisites: Flutter SDK >= 3.0, Go >= 1.26 (for Tailscale native compilation), Android NDK.
git clone https://github.com/2241812/OPA_Flutter.git
cd OPA_Flutter
flutter pub get
flutter run # Run on connected device
flutter build apk --release # Release APK- Swipe through the 4-slide onboarding or tap Skip.
- Tap + to add your first server (host, port 22, username, auth method).
- Set up key-based auth: SSH Keys -> + -> Generate Ed25519 Key -> Copy. On your server: echo "ssh-ed25519 AAAAC3... opa" >> ~/.ssh/authorized_keys
- Tap any connection tile to open the terminal instantly.
- Swipe left for SFTP, Config, or QCMD quick commands.
Windows (OpenSSH Server)
# Install (run as Administrator)
Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0
# Start and enable
Start-Service sshd
Set-Service -Name sshd -StartupType Automatic
# Find your IP
ipconfigLinux
sudo apt install openssh-server
sudo systemctl enable --now sshmacOS
System Settings -> General -> Sharing -> toggle Remote Login ON.OPA embeds package:tailscale - userspace Tailscale networking powered by upstream Go tsnet. No separate Tailscale app needed.
- Join your tailnet from inside OPA - authenticate once, connect forever
- MagicDNS - reach servers by .ts.net hostname, no IP wrangling
- WireGuard encryption - every byte between you and your server
- DERP relay fallback - works through restrictive NATs
- Per-process - no system-wide VPN, no root, no VPN profile
- A Tailscale or Headscale tailnet
- An auth key from your tailnet admin
- Enable Tailscale in OPA Settings and enter your auth key
The node identity is persisted in the app support directory (excluded from cloud backups).
| Package | Purpose |
|---|---|
| dartssh2 | Pure-Dart SSH client - auth, shell, exec, SFTP |
| xterm | Terminal emulator - VT100, 256-color, scrollback |
| tailscale | Embedded WireGuard tailnet via Go tsnet FFI |
| pinenacl | Ed25519 key pair generation |
| flutter_secure_storage | Encrypted on-device key storage |
| hive + hive_flutter | Local NoSQL profiles and commands |
| flutter_riverpod | Compile-safe state management |
| go_router | Declarative routing with transitions |
| google_fonts | Inter (UI) + JetBrains Mono (terminal) |
| flutter_animate | Spring/fade/slide animations |
| url_launcher | APK download links |
| package_info_plus | Runtime version for update checks |
| local_auth | Biometric app lock |
| Layer | How OPA Protects You |
|---|---|
| Private keys | Android Keystore - hardware-backed, never exposed as plaintext |
| Passwords | Hive database - local only, never sent externally |
| SSH transport | Direct socket, encrypted by SSH protocol, no MITM |
| Tailscale | WireGuard end-to-end, DERP relayed only when necessary |
| Network | Fully offline - zero telemetry, zero cloud, zero third-party |
| App access | Optional biometric lock (fingerprint or face) |
| Backup | Tailscale state dir excluded from cloud backups |
- SFTP file browser - upload, download, manage remote files
- Port forwarding UI - local, remote, dynamic (SOCKS5) tunnels
- Jump host / bastion - chain through servers
- SSH agent forwarding - use local keys remotely
- Session recording and replay
- Profile import and export
- Custom preset editor
- iOS support
- Split APKs by ABI for smaller downloads
- Tailscale Taildrop - P2P file transfers over tailnet
MIT Built with love by Renzo Javier
OPA is not affiliated with OpenSSH, Tailscale, or any referenced tool.