iOS Mobile Companion
Native iOS app for ServerBee with QR pairing, push notifications, and real-time monitoring.
ServerBee provides a native iOS companion app that brings server monitoring to your phone: real-time metrics, alert history, and server management on the go. Verified notification setup and final scheduled-task outcome notifications are available, with successes requiring opt-in. Encrypted alert triggers and recoveries are available through confirmed mobile subscriptions. Administrator security-rule notifications use the same verified encrypted delivery path.
Availability
This repository does not currently publish an installable iOS artifact or an App Store/TestFlight acquisition link. The guide below applies to a build you receive from your ServerBee operator or build from source. To build it yourself, install Xcode and XcodeGen, then run:
cd apps/ios
xcodegen generate
open ServerBee.xcodeprojSource builds require your Apple development team. The official-app design keeps APNs credentials at a dedicated publisher-operated Relay; official-app users and self-hosted Server operators do not need the publisher's keys. This repository does not yet distribute that signed app. A local build does not prove live APNs delivery.
Features
- QR Code Pairing -- Scan a QR code from the web app to authenticate instantly, no manual token entry required
- Verified Notification Setup -- Explicit permission and subscription setup with authenticated registration; final task outcomes for administrator owners, with success opt-in; encrypted alert triggers and recoveries without a notification group; administrator security-rule matches
- Real-time Metrics -- Live server status, CPU, memory, disk, and network metrics streamed via WebSocket
- Alert History -- View recent alerts and their status (triggered/resolved) with detailed information
- Secure Authentication -- Bearer token-based auth with automatic refresh (15-min access token, 30-day refresh token)
Requirements
- iOS 17.0 or later
- A ServerBee server instance (v0.8.0+) with mobile auth enabled
- For verified notification setup: a configured HTTPS Push Relay and a correctly signed, correctly signed physical device
Pairing Your Device
1. Enable Mobile Auth (Server-side)
Mobile authentication is enabled by default in v0.8.0+. No additional server configuration is required unless you want to adjust token TTLs.
Optional configuration in server.toml:
[mobile]
access_ttl = 900 # Access token lifetime in seconds (default: 15 minutes)
refresh_ttl = 2592000 # Refresh token lifetime in seconds (default: 30 days)2. Start Pairing from Web App
- Log in to your ServerBee web app
- Go to Settings → Mobile Devices
- Click Pair New Device
- A QR code will be displayed with a 5-minute expiry timer
3. Scan with iOS App
- Open the ServerBee iOS app
- Tap Scan QR Code on the welcome screen
- Point your camera at the QR code displayed in the web app
- The app will automatically authenticate and show your server list
Recovering an Expired Login
If the app cannot refresh or safely sign out an old login, it preserves the original Server and account and opens session recovery. On the same Server's web app, log in as that original account and generate a fresh pairing QR code from Settings → Mobile Devices. In the iOS recovery page, use Scan QR Code to recover session. This scan verifies the account for cleanup; it does not sign in a replacement login. If the page lists sessions, select the original session by its device name and timestamps, then confirm Sign out selected session. The app retains that exact selection for retries and leaves other devices and newer logins intact. Once cleanup is confirmed, use the normal pairing flow to log in again. After a restart or an expired recovery QR authorization, scan a fresh code. Password and TOTP recovery is also available when you use those credentials.
Managing Paired Devices
View Paired Devices
In the web app, go to Settings → Mobile Devices to see all paired iOS devices:
- Device name (e.g., "iPhone 15 Pro")
- Paired date and time
- Last active timestamp
Revoke Access
To remove a device's access:
- In the Mobile Devices list, find the device you want to remove
- Click the Revoke button
- Confirm the action
The device will be immediately logged out and all its tokens invalidated.
Verified Notification Setup
Signing in does not request notification permission. Open Settings → Notifications, choose subscriptions, then tap Enable notifications. The Server confirms the saved preferences before iOS asks for permission. Security and task subscriptions require an administrator. Successful-task delivery is disabled by default; eligible task recipients can opt in.
The notification page uses permissions confirmed by the Server, even if the cached login role is outdated. After an administrator is demoted, saving subscriptions or disabling notifications clears unavailable security and task categories. Failed saves do not replace the confirmed preferences.
A failed subscription save remains visible after returning to the app, reconnecting or recovering registration. The page retains the unconfirmed choices until a successful save or a newer settings revision from the Server confirms the submitted permitted preferences. A successful registration alone does not confirm those choices.
The screen reports system permission and Server registration independently. Failed saves retain the previous confirmed preferences and show an error. Retry notification setup retries verification, and Open notification settings opens iOS permission settings. Setup is reconciled on launch, foreground entry and restored connectivity without prompting during sign-in.
Enable Alerts and recoveries to receive alert transitions. Use Send test notification to test only this authenticated installation. The Server encrypts the message, the public Relay forwards its ciphertext to APNs, and the Notification Service Extension decrypts and localizes it. The screen reports provider acceptance separately from device presentation, which must be observed on a real device. Foreground APNs delivery uses one system banner while WebSocket updates only update app state. Tapping a valid test opens the current account; old account/deployment or invalid payloads cannot navigate. Final task failures and opted-in successes use the same encrypted pipeline. Administrators can also enable security-rule notifications.
Tests first enter a durable encrypted Server outbox. Pending means queued locally, Retryable means provider acceptance is unconfirmed and another attempt is scheduled, Accepted is an APNs provider receipt, Permanent stops retries, and Expired ends the test 30 minutes after its original creation. Retries and Server restarts never extend that deadline. The screen refreshes the current test status; retrying a lost response retains the same logical test identity. Delivery can be duplicated after an ambiguous network failure, so neither acceptance nor retry promises exactly-once display.
Before every attempt the Server checks the account, current role, mobile session, subscription and registration revision. Re-registering the identical token, environment, content key and deployment preserves the revision, ciphertext and original retry deadline; a real identity change still fences old work. Disabling delivery, logout, device/account revocation, session expiry or a registration replacement stops later eligible attempts. Already in-flight or accepted APNs notifications cannot be retracted. Queue records contain ciphertext, not another plaintext notification history. Terminal ciphertext is erased; secret-free delivery receipts remain to prevent duplicate enqueueing from restarting the expiry window.
Targeted Test API
POST /api/mobile/push/test requires expected_revision and a client-retained UUID event_id. It queues encrypted work and returns its pending status with presentation=unobserved. Retry an ambiguous response with the same UUID and revision; GET /api/mobile/push/test/{event_id} reads only the current authenticated installation/login receipt. A deliberate new test uses a new UUID. Status recovery never restarts the original 30-minute deadline.
Alert Triggers and Recoveries
The Server queues encrypted notifications for each currently eligible subscribed installation, independently of external notification groups. Enabled rules retain their existing coverage, maintenance, trigger-mode and suppression behavior. Recovery follows the existing evaluator's transition behavior, including during maintenance. Unsubscribing before dispatch stops later eligible sends for that installation. External channels retain their own groups; migrated installations stay excluded from legacy APNs delivery.
Taps wait for authentication restoration and validate the current deployment, account and installation. The alert target includes the rule, Server, event dimension and trigger cycle. A deleted alert or overwritten cycle shows Alert not found with Back to alerts, rather than opening a newer cycle. The extension localizes trigger/recovery text in English or Simplified Chinese; decryption failure shows generic text without a navigation target.
Queued delivery and extension presentation expire 30 minutes after creation. Tapping a notification already shown in Notification Center after that deadline still authenticates its exact target and requests current Server detail. Expiry does not authorize access: a changed login is rejected, and a deleted or unavailable alert uses the same safe fallback.
Final Task Failures
Enable Final task failures as an administrator. A manually started scheduled task notifies its initiator; an automatic run notifies its creator, independently of other subscribed administrators. Each eligible installation receives one logical summary after all targets finish their final attempts. Intermediate failures stay silent; a successful final retry follows the success opt-in policy. Final failed exits, timeouts, offline targets and execution denials are counted separately, including runs with no executable targets. Interrupted or incomplete runs are never treated as successful. After all final attempts drain, the Server durably records their summary before queue admission. A failed admission or Server restart automatically retries that recorded summary with the original run identity and 30-minute delivery window, checking current access and subscriptions again. Intermediate retry rows are not a completion signal.
Summaries contain counts and task/run identifiers, never task names, commands or command output. Tapping opens authenticated results filtered to that exact run, including when an already displayed notification is tapped after its delivery window. Expired new deliveries still render a generic fallback; a tap retains the authenticated target and checks current Server access. Deleted tasks, unavailable runs or lost access show a dismissible fallback; old account/deployment notifications cannot navigate. Current administrator access, ownership and subscription are checked again before queued delivery. No notification group is required. Successful-run notifications require a separate opt-in.
Successful Task Runs
Successful task runs is disabled by default and appears only when the Server confirms current task access. Enable it and save subscriptions to receive one encrypted summary for a complete run in which every target ultimately succeeded, including success after a retry. This choice is independent of Final task failures; a mixed or failed run follows only the failure subscription and never emits a duplicate success summary. Interrupted, cancelled or incomplete runs cannot produce a success summary.
Successful summaries use the same initiator/creator ownership, per-installation eligibility, run identity, authenticated navigation and 30-minute window as failures. They contain only counts and identifiers, never task names, commands or output. Failed saves retain the previous confirmed preference and keep the unconfirmed choice and error visible. Opting out or losing administrator access cancels pending eligible success delivery when the queue rechecks access before sending; in-flight or accepted APNs requests cannot be retracted.
Security Rule Notifications
Administrators can enable Security rule matches and save the Server-confirmed subscription. Members cannot subscribe. A raw detection alone never sends a mobile notification: an enabled SSH new-IP login, brute-force or port-scan rule must admit it. Thresholds, username/CIDR exclusions, Server coverage, maintenance and the existing sliding deduplication window apply. A matching event produces one logical security notification per eligible installation, even if multiple rules match, without requiring a notification group. External channels keep their configured groups.
An authorized Agent WebSocket transfers each original detection to a Server-owned recovery queue without waiting for storage retries inside its message or lifecycle lock. Before the first successful raw write, this retention is in memory and cannot survive process death. Connection replacement or closure does not discard accepted work, but authority transfer/revocation or capability removal cancels raw writes and unresolved rule admissions that are no longer authorized. The Server persists the original security event, authority binding, maintenance decision at the original event time, and recovery marker, then commits the rule cooldown together with a durable recipient intent. Maintenance lookup failures preserve raw history and browser updates while leaving rule admission unresolved; recovery never treats a failed lookup as permission. Once stored, the maintenance decision survives delayed recovery and restart across a window beginning or ending. Originals sharing a rule deduplication key are admitted in original time order across memory retention, missing maintenance snapshots and database reopen; an unresolved older event holds only that key, while other ready keys and installations continue. Rule cooldown remains monotonic in persistent storage. Only then does it encrypt and queue all eligible installations in one transaction. A queue write failure preserves raw history, browser updates, firewall actions and external notifications, leaves no partial fan-out, and retries the original intent automatically after recovery or a Server restart. Recipient ownership, session and registration revision are fixed by the intent and rechecked before queueing; replacement registrations cannot inherit an older intent. Security notifications expire 30 minutes after the event is recorded. Before each attempt, the Server rechecks the current administrator role, security subscription and mobile session. A queued notification stops after a role downgrade or subscription change. Multiple devices are evaluated independently. Retries after an ambiguous network failure can still lead to duplicate device presentation.
The Notification Service Extension localizes the admitted event type. Tapping opens the corresponding Server security feed and event detail after verifying the deployment/account, current administrator role and resource identities. A deleted, unavailable or unauthorized target shows a dismissible unavailable screen; an old account or deployment payload cannot navigate. Local tests do not establish foreground/background/terminated APNs delivery.
Privacy Boundary
The Server owns account/session authorization and recipient selection. The Relay sees APNs tokens, source IPs, timing, request sizes, environment, event/delivery metadata and encrypted content. It must never receive content keys or plaintext notification content. Users see this explanation before requesting permission.
Registration is confirmed by the authenticated Server with account, installation, session and revision checks. The app never contacts Relay. Each paired login has an independently scoped shared content key; account replacement and logout remove the old key. Pending registration retries cannot overwrite a replacement account.
The Worker is public and stateless. A known valid device token can be abused for junk or replayed notifications; generic fallback text may appear when decryption fails. Requests or an attacker's own token can consume quota without another person's token. Encryption protects business content, not admission. Hard request/resource bounds and best-effort isolate-local rate limits reduce exposure but cannot guarantee a global budget or prevent all abuse.
Server and Relay Configuration
Set [push_relay].url or SERVERBEE_PUSH_RELAY__URL to the dedicated HTTPS Relay. No notification group or legacy APNs channel is required. Verified migration removes this account's old token and permanently excludes that installation/account from legacy direct-APNs fan-out, even after disabling or unregistering. Other installations retain their legacy channels. Existing external channels continue to use their own configuration.
The Cloudflare Worker uses WebCrypto JWT signing and platform fetch with a fixed topic and APNs host/environment allowlist. Worker bindings are APNS_TEAM_ID, APNS_KEY_ID, APNS_TOPIC, APNS_ENVIRONMENTS and the APNS_PRIVATE_KEY secret containing PKCS#8 PEM text. They belong exclusively to Relay. No App Attest, challenge/grant service, Relay database, Bun/OpenSSL production runtime or self-managed HTTP/2 pool is required. Do not point tests at Heeler's Relay.
For the isolated signing/deployment runbook, troubleshooting by layer, and the genuine-device acceptance record, see Push Relay Operations. No live device acceptance is established by a successful local check.
Security Considerations
- Token Rotation: Refresh tokens are rotated on every use (single-use). Consumed refresh tokens cannot authenticate APIs or refresh again. Their retained hashes can only revoke the original session, supporting cleanup after a staged app upgrade or a lost rotation response.
- Fixed Expiry: Access tokens have fixed 15-minute expiry (no sliding renewal) to limit exposure if stolen
- Secure Storage: Tokens are stored in iOS Keychain with appropriate accessibility levels
- Session Expiry: Expired access-token sessions are removed hourly. Paired-device refresh sessions are not deleted by that task: the device list hides them after expiry, refresh rotation preserves their identity and installation registration, and logout or revoke deletes them together with their retained revocation-proof hashes.
Troubleshooting
QR Code Won't Scan
- Ensure the QR code is displayed clearly on your screen
- Check that the QR code hasn't expired (5-minute timeout)
- Try generating a new QR code from the web app
Notification Setup Fails
- Check the configured Relay URL and its HTTPS trust chain.
- Check Settings → Notifications for permission, verification and save errors.
- Confirm the paired device has not been revoked and the session has not expired.
- Match sandbox/production APNs environment to the signed app, and verify the embedded Notification Service Extension. Simulator success is not live APNs evidence.
- Use the final HTTPS Server URL before registering a content key. Server requests reject redirects; failed setup can be retried at the configured endpoint.
- Check signing for the app-private credential group and the app/extension shared content-key group. Decryption failures show generic text.
- Enable Alerts and recoveries, or Final task failures and optional Successful task runs as an administrator. Administrator security-rule notifications use the same verified encrypted delivery path.
Authentication Failures
- If the app shows "Session Expired", it will automatically refresh the token
- If refresh fails, you'll need to re-pair using a new QR code
- Check that your server time is correctly synced (NTP)
Technical Details
Authentication Flow
- Login/QR Pair: POST
/api/mobile/auth/loginor QR pair endpoint → returns access_token + refresh_token - API Requests: Include
Authorization: Bearer {access_token}header - Token Refresh: On 401, POST
/api/mobile/auth/refreshwith refresh_token → new token pair - Logout: POST
/api/mobile/auth/revokewith a deletion-only proof and the exact originalexpected_session_id; the older authenticated/logoutroute remains available for compatibility
Offline Sign-out Recovery
Before changing verified notification settings, the app confirms a deletion-only capability for that exact mobile session. When an established session signs out offline, its normal credentials are cleared only after its deletion capability is saved in the app-private Keychain. Pending cleanup retries on launch, foreground/network recovery, and before login or pairing; it never uses a replacement account's credentials. An acknowledgement for an already-absent original session does not delete another login using the same installation.
An upgraded installation without a confirmed capability must connect once before secure sign-out can complete. Storage errors or a full pending-cleanup list also block destructive credential clearing or replacement login. The app reports these cases rather than losing the original session's cleanup proof. Existing push content keys and test identities keep their original scope across migration and proof bootstrap. Server-side sign-out cannot retract notifications already in flight or accepted by APNs.
If an upgraded session has no confirmed deletion capability and its credentials are permanently rejected, the app shows Recover session and stops ordinary authenticated requests, including after a restart or reinstall that retains Keychain data. Verify the original account with its password and, when required, a verification code. The Server removes only the captured original session or confirms that it is already absent; only then does the app release its saved identity and return to login. Retry secure sign-out retries the original cleanup credentials without creating a new login. A lost response, unavailable Server, unsupported Server version, or ambiguous identity keeps the recovery record. If the original credentials no longer identify a session, reauthentication lists only the original account and installation's sessions, including expired ones. Choose the original session by its device name and dates, then confirm sign-out. The selected ID is saved before cleanup so a lost response can retry that exact target after a restart. Other sessions are never selected automatically. Unattributed state remains blocked with an explicit error.
WebSocket Connection
The iOS app maintains a WebSocket connection for real-time updates:
- Connection URL:
wss://your-server/api/ws/servers - Authentication via
Authorization: Bearerheader during handshake - Automatic reconnection with exponential backoff
- Connection auto-closes when access token expires (reconnect triggers refresh)