Mobile Client
Mobile Client
Section titled “Mobile Client”The Bifrost Mobile Client is a cross-platform application built with React Native and Expo, providing VPN management on iOS and Android devices.
Important — the mobile app is a remote control, not an on-device tunnel. The Expo app manages a Bifrost client running elsewhere on your network over the REST API (see Connecting to Client). It does not establish an encrypted tunnel on the phone itself. The native
PacketTunnelProvider(iOS) andBifrostVpnService(Android) files in the repository are non-functional placeholders: they are unbuildable skeletons and the current on-device path would forward raw packets without a WireGuard/Noise handshake (i.e. cleartext, not secure). For that reason the native VPN path is gated off and must not be relied on for confidentiality. A real on-device WireGuard tunnel is planned future work.
Features
Section titled “Features”- Home Screen: VPN connection status and quick toggle
- Servers Screen: Server selection with latency indicators
- Stats Screen: Real-time traffic statistics and connection details
- Settings Screen: Configuration management and preferences
Installation
Section titled “Installation”App Stores
Section titled “App Stores”- iOS: App Store (coming soon)
- Android: Google Play (coming soon)
Building from Source
Section titled “Building from Source”Prerequisites
Section titled “Prerequisites”- Node.js 18+
- npm or yarn
- Expo CLI
- For iOS: Xcode 14+ (macOS only)
- For Android: Android Studio with SDK
# Clone repositorygit clone https://github.com/rennerdo30/bifrost-proxy.gitcd bifrost-proxy/mobile
# Install dependenciesnpm install
# Start development servernpx expo startRunning on Device/Simulator
Section titled “Running on Device/Simulator”# iOS Simulator (macOS only)npx expo run:ios
# Android Emulatornpx expo run:android
# On physical devicenpx expo start --tunnel# Then scan QR code with Expo Go appBuilding for Production
Section titled “Building for Production”Using EAS Build (recommended):
# Install EAS CLInpm install -g eas-cli
# Login to Expo accounteas login
# Build for iOSeas build --platform ios
# Build for Androideas build --platform android
# Build for botheas build --platform allScreens
Section titled “Screens”Home Screen
Section titled “Home Screen”The main dashboard showing connection status:
┌─────────────────────────────────────┐│ Bifrost VPN │├─────────────────────────────────────┤│ ││ ┌───────────────┐ ││ │ │ ││ │ 🔒 │ ││ │ Connected │ ││ │ │ ││ └───────────────┘ ││ ││ [ Disconnect ] ││ ││ ┌─────────────┐ ┌─────────────┐ ││ │ ↑ Sent │ │ ↓ Received │ ││ │ 1.2 GB │ │ 3.4 GB │ ││ └─────────────┘ └─────────────┘ ││ ││ Server: us-west.example.com ││ Connected: 2h 34m ││ │├─────────────────────────────────────┤│ 🏠 Home │ 📡 Servers │ 📊 Stats │ ⚙️ │└─────────────────────────────────────┘Servers Screen
Section titled “Servers Screen”Server selection with status indicators:
┌─────────────────────────────────────┐│ Available Servers │├─────────────────────────────────────┤│ ││ ┌─────────────────────────────────┐││ │ 🟢 US West ✓ 45ms │││ │ us-west.example.com │││ │ HTTP │││ └─────────────────────────────────┘││ ││ ┌─────────────────────────────────┐││ │ 🟢 Europe 120ms │││ │ eu.example.com │││ │ HTTP │││ └─────────────────────────────────┘││ ││ ┌─────────────────────────────────┐││ │ 🟡 Asia Pacific 200ms │││ │ asia.example.com │││ │ SOCKS5 │││ └─────────────────────────────────┘││ ││ ┌─────────────────────────────────┐││ │ 🔴 Development offline │││ │ dev.example.com │││ │ HTTP │││ └─────────────────────────────────┘││ │├─────────────────────────────────────┤│ 🏠 Home │ 📡 Servers │ 📊 Stats │ ⚙️ │└─────────────────────────────────────┘Status indicators:
- 🟢 Online - Server available
- 🟡 Busy - High load
- 🔴 Offline - Server unavailable
Stats Screen
Section titled “Stats Screen”Detailed connection statistics:
┌─────────────────────────────────────┐│ Connection Stats │├─────────────────────────────────────┤│ ││ ┌─────────────┐ ┌─────────────┐ ││ │ ↑ Sent │ │ ↓ Received │ ││ │ 1.2 GB │ │ 3.4 GB │ ││ └─────────────┘ └─────────────┘ ││ ││ Current Session ││ ───────────────────────────────── ││ Duration 2h 34m ││ Total Data 4.6 GB ││ Status Connected ││ ││ Connection Details ││ ───────────────────────────────── ││ Protocol WireGuard ││ Encryption ChaCha20-Poly1305 ││ Port 51820 ││ MTU 1420 ││ ││ Network ││ ───────────────────────────────── ││ Local IP 10.0.0.2 ││ Gateway 10.0.0.1 ││ DNS 1.1.1.1, 8.8.8.8 ││ Interface bifrost0 ││ │├─────────────────────────────────────┤│ 🏠 Home │ 📡 Servers │ 📊 Stats │ ⚙️ │└─────────────────────────────────────┘Note: Protocol/encryption/IP fields above describe the connection of the remote Bifrost client the app controls (reported over the REST API). They do not indicate an encrypted tunnel running on the phone itself — see the warning at the top of this page.
Settings Screen
Section titled “Settings Screen”Configuration and preferences:
┌─────────────────────────────────────┐│ Settings │├─────────────────────────────────────┤│ ││ Connection ││ ───────────────────────────────── ││ Auto-connect [ 🔘 ] ││ Connect on app launch ││ ││ Kill Switch [🔘 ] ││ Block traffic if VPN disconnects ││ ││ Split Tunneling [ 🔘 ] ││ Exclude certain apps ││ [Configure →] ││ ││ Server ││ ───────────────────────────────── ││ ┌─────────────────────────────────┐││ │ proxy.example.com:7080 [Save] │││ └─────────────────────────────────┘││ ││ Notifications ││ ───────────────────────────────── ││ Connection Alerts [🔘 ] ││ Notify on connect/disconnect ││ ││ Data & Privacy ││ ───────────────────────────────── ││ [ Clear Cached Data ] ││ ││ About ││ ───────────────────────────────── ││ Version 1.0.0 ││ Server Status Connected ││ │├─────────────────────────────────────┤│ 🏠 Home │ 📡 Servers │ 📊 Stats │ ⚙️ │└─────────────────────────────────────┘Configuration
Section titled “Configuration”The mobile client connects to a Bifrost client running on your network. Configure the server address in Settings.
Connecting to Client
Section titled “Connecting to Client”- Start the Bifrost client on your computer/server
- Note the API address (default:
http://192.168.x.x:7383) - Enter this address in the mobile app Settings
API Configuration
Section titled “API Configuration”The mobile app uses these API endpoints:
| Endpoint | Description |
|---|---|
GET /api/v1/status | Client status |
GET /api/v1/vpn/status | VPN connection status |
POST /api/v1/vpn/enable | Enable VPN |
POST /api/v1/vpn/disable | Disable VPN |
GET /api/v1/servers | Available servers |
POST /api/v1/servers/{id}/select | Select server |
GET /api/v1/config | Get configuration |
PUT /api/v1/config | Update configuration |
Development
Section titled “Development”Project Structure
Section titled “Project Structure”mobile/├── src/│ ├── components/ # Reusable UI components│ ├── screens/ # Screen components│ │ ├── HomeScreen.tsx│ │ ├── ServersScreen.tsx│ │ ├── StatsScreen.tsx│ │ └── SettingsScreen.tsx│ ├── services/ # API and utilities│ │ └── api.ts│ └── App.tsx # Main app component├── app.json # Expo configuration├── package.json└── tsconfig.jsonTechnology Stack
Section titled “Technology Stack”- React Native: Cross-platform UI framework
- Expo: Development and build tooling
- React Query: Data fetching and caching
- React Navigation: Screen navigation
Adding a New Screen
Section titled “Adding a New Screen”- Create screen component in
src/screens/ - Add to navigation in
App.tsx - Add tab bar icon if needed
API Integration
Section titled “API Integration”The app uses React Query for data fetching:
import { useQuery, useMutation } from '@tanstack/react-query'import { api } from '../services/api'
function MyScreen() { const { data, isLoading, error } = useQuery({ queryKey: ['vpn-status'], queryFn: api.getVPNStatus, refetchInterval: 3000, })
const mutation = useMutation({ mutationFn: api.enableVPN, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['vpn-status'] }) }, })
// ...}Accessibility
Section titled “Accessibility”The Bifrost Mobile Client is designed with accessibility in mind, supporting both VoiceOver (iOS) and TalkBack (Android) screen readers.
Supported Accessibility Features
Section titled “Supported Accessibility Features”| Feature | Support |
|---|---|
| Screen Reader | Full support |
| Accessibility Labels | All interactive elements |
| Accessibility Roles | Semantic roles for buttons, tabs, switches |
| Accessibility States | Disabled, selected, checked states |
| Accessibility Hints | Context-aware action hints |
VoiceOver Testing (iOS)
Section titled “VoiceOver Testing (iOS)”How to Test
Section titled “How to Test”- Enable VoiceOver: Settings > Accessibility > VoiceOver
- Navigate using swipe gestures
- Double-tap to activate
- Use two-finger scroll for lists
Expected Behavior
Section titled “Expected Behavior”Home Screen:
- Connect/Disconnect button announces: “Connect to VPN, button” or “Disconnect from VPN, button”
- Status text announces: “Connection status: Protected” or similar
- Error alerts are announced immediately with role “alert”
- Upload/Download cards announce: “Upload: 1.2 GB” and “Download: 3.4 GB”
Servers Screen:
- Server items announce full context: “Server Name, address, protocol, status, selected/not selected”
- Offline servers announce disabled state
- Pull-to-refresh announces “Pull to refresh server list”
Stats Screen:
- All stat rows announce label and value pairs: “Duration: 2h 34m”
- StatusCard components announce: “Total Sent: 1.2 GB”, “Total Received: 3.4 GB”
Settings Screen:
- Switch controls announce their label and current state
- Configure button announces: “Configure split tunneling, button”
- Text input announces: “Server address, Enter the server address in host:port format”
Tab Navigation:
- Tab icons announce: “Home tab, selected” or “Settings tab”
- Uses proper tab role with selected state
TalkBack Testing (Android)
Section titled “TalkBack Testing (Android)”How to Test
Section titled “How to Test”- Enable TalkBack: Settings > Accessibility > TalkBack
- Navigate by swiping left/right
- Double-tap to activate
- Use two-finger scroll for content
Expected Behavior
Section titled “Expected Behavior”All behaviors match VoiceOver testing above with Android-specific variations:
importantForAccessibility="no"hides decorative elementsimportantForAccessibility="no-hide-descendants"groups child elements- Swipe gestures map to Android TalkBack conventions
- Focus indicators appear on currently selected element
Accessibility Implementation Details
Section titled “Accessibility Implementation Details”The following accessibility props are used throughout the app:
// Button example<TouchableOpacity accessibilityRole="button" accessibilityLabel="Connect to VPN" accessibilityHint="Double tap to establish VPN connection" accessibilityState={{ disabled: isLoading }}>
// Switch example<Switch accessibilityLabel="Auto-connect toggle" accessibilityHint="When enabled, the app will connect automatically on launch" accessibilityState={{ checked: isEnabled }}/>
// Grouped content example<View accessible={true} accessibilityRole="summary" accessibilityLabel={`${title}: ${value}`}> <Text importantForAccessibility="no">{title}</Text> <Text importantForAccessibility="no">{value}</Text></View>Known Accessibility Considerations
Section titled “Known Accessibility Considerations”- Emoji Icons: Tab bar uses emoji icons which are hidden from screen readers; proper labels are provided instead
- Pull-to-Refresh: Custom accessibility labels describe the refresh action
- Dynamic Content: Status updates may not be immediately announced; users should navigate to the status area to hear updates
- Error States: Error messages use the
alertrole for immediate screen reader announcement
Troubleshooting
Section titled “Troubleshooting”Cannot Connect to Client
Section titled “Cannot Connect to Client”- Verify client is running and API is enabled
- Check network connectivity (same network)
- Ensure correct IP address and port
- Check firewall settings on client machine
App Crashes on Start
Section titled “App Crashes on Start”- Clear app data and reinstall
- Check for conflicting VPN apps
- Update to latest version
High Battery Usage
Section titled “High Battery Usage”- Increase refetch intervals in settings
- Disable notifications if not needed
- Disconnect when not in use
Stats Not Updating
Section titled “Stats Not Updating”- Check network connection
- Verify client is running
- Pull down to refresh manually