Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
77 commits
Select commit Hold shift + click to select a range
b4d91d5
feat: add one-tap iBridge Mobile bootstrap
DannyBaanks Oct 2, 2026
7da7038
feat: retain mobile bootstrap identity in memory
DannyBaanks Oct 2, 2026
22d9272
feat: wire iBridge Mobile bootstrap into desktop
DannyBaanks Oct 2, 2026
c5399e7
ci: add fast test gate for iBridge
DannyBaanks Oct 2, 2026
688f8c3
feat: add iBridge Mobile install operation
DannyBaanks Oct 2, 2026
524d222
test: define one-tap mobile bootstrap contract
DannyBaanks Oct 2, 2026
16b3b43
feat: add one-tap iBridge Mobile installer button
DannyBaanks Oct 2, 2026
5171d31
feat: add native mobile signing core
DannyBaanks Oct 2, 2026
87e3eca
feat: implement iOS sign-and-install FFI
DannyBaanks Oct 2, 2026
fc650c6
feat: add native iBridge Mobile XcodeGen project
DannyBaanks Oct 2, 2026
ac7ebc6
feat: configure iBridge Mobile app sandbox
DannyBaanks Oct 2, 2026
13661eb
feat: bridge SwiftUI to Rust signing core
DannyBaanks Oct 2, 2026
30a37ce
feat: persist mobile secrets in Keychain
DannyBaanks Oct 2, 2026
ce52a4e
feat: import desktop bootstrap into iOS Keychain
DannyBaanks Oct 2, 2026
3bb8c3f
feat: discover the iPhone local developer address
DannyBaanks Oct 2, 2026
a8fee03
feat: stabilize iBridge Mobile bootstrap contract
DannyBaanks Oct 2, 2026
f126e75
feat: expose mobile bootstrap contract for verification
DannyBaanks Oct 2, 2026
22bed48
fix: align iOS importer with desktop bootstrap contract
DannyBaanks Oct 2, 2026
7816f38
fix: align mobile bundle id with bootstrap matcher
DannyBaanks Oct 2, 2026
0a0a88a
test: define mobile account secret boundary
DannyBaanks Oct 2, 2026
99a14ec
fix: read Apple credential from iOS Keychain in Rust
DannyBaanks Oct 2, 2026
c87984f
feat: transfer one-time mobile login secret during bootstrap
DannyBaanks Oct 2, 2026
bc5a1b1
fix: keep Apple credential inside iOS Keychain
DannyBaanks Oct 2, 2026
208db3e
feat: bridge SwiftUI to Rust signing core safely
DannyBaanks Oct 2, 2026
6184e5c
feat: retain login material for one-tap mobile bootstrap
DannyBaanks Oct 2, 2026
7cc93cc
feat: implement mobile sign install and refresh flow
DannyBaanks Oct 2, 2026
91170be
feat: import one-tap account handoff into mobile keychain
DannyBaanks Oct 2, 2026
bc14b62
feat: add iBridge Mobile SwiftUI entry point
DannyBaanks Oct 2, 2026
8d2dec4
feat: add one-tap mobile signing UI
DannyBaanks Oct 2, 2026
05f58a2
feat: add on-device TCP sideload and self-refresh runtime
DannyBaanks Oct 2, 2026
9577343
fix: wire mobile model to bootstrap keychain contract
DannyBaanks Oct 2, 2026
93c5ce2
fix: discover LocalDevVPN peer instead of Wi-Fi address
DannyBaanks Oct 2, 2026
d047daf
feat: make mobile bootstrap payload consumable on-device
DannyBaanks Oct 2, 2026
2238e4c
ci: build resignable iBridge Mobile IPA on macOS
DannyBaanks Oct 2, 2026
e0bbe9a
feat: consume one-time bootstrap and restore mobile account
DannyBaanks Oct 2, 2026
b6b732d
feat: register mobile runtime and gate desktop updater
DannyBaanks Oct 2, 2026
b29a161
feat: detect native iOS runtime in frontend
DannyBaanks Oct 2, 2026
a605a80
test: align mobile bundle contract with Swift target
DannyBaanks Oct 2, 2026
5a08985
feat: add native iPhone sideload and self-refresh UI
DannyBaanks Oct 2, 2026
0b10a9b
fix: align desktop discovery with mobile bundle identifier
DannyBaanks Oct 2, 2026
2baf79d
feat: boot directly into iBridge Mobile on iPhone and iPad
DannyBaanks Oct 2, 2026
01906c1
feat: define iBridge iOS identity for Tauri mobile
DannyBaanks Oct 2, 2026
86d2aae
ci: build the actual Tauri iBridge fork as an iOS IPA
DannyBaanks Oct 2, 2026
5f52dc5
build: add libc for iOS tunnel peer discovery
DannyBaanks Oct 2, 2026
1dae95b
feat: autodetect iOS tunnel peer for one-tap refresh
DannyBaanks Oct 2, 2026
7a49247
feat: scaffold native iBridge tunnel plugin
DannyBaanks Oct 2, 2026
85957f6
feat: wire iOS sources for tunnel plugin
DannyBaanks Oct 2, 2026
061e820
feat: expose native tunnel controls to Rust
DannyBaanks Oct 2, 2026
63da875
feat: package Swift tunnel controller with Tauri
DannyBaanks Oct 2, 2026
3952bf9
fix: add iBridge desktop copy with English and Spanish fallback
DannyBaanks Oct 2, 2026
29a6ae5
feat: manage embedded packet tunnel from iBridge Mobile
DannyBaanks Oct 2, 2026
7f91f86
feat: embed local packet reflector extension source
DannyBaanks Oct 2, 2026
90decc3
feat: link native tunnel plugin only on iOS
DannyBaanks Oct 2, 2026
b3905ce
feat: expose embedded tunnel controls to mobile UI
DannyBaanks Oct 2, 2026
da3ffb0
ci: cancel stale iBridge test runs
DannyBaanks Oct 2, 2026
d116360
feat: register embedded iOS tunnel plugin and commands
DannyBaanks Oct 2, 2026
38fd8b0
ci: cancel stale mobile builds
DannyBaanks Oct 2, 2026
8fc6345
feat: auto-start embedded tunnel in iBridge Mobile
DannyBaanks Oct 2, 2026
27807b8
feat: add iBridge Mobile Network Extension entitlement
DannyBaanks Oct 2, 2026
e5f4f9f
feat: add tunnel provider entitlement
DannyBaanks Oct 2, 2026
e0e24e3
feat: embed packet tunnel extension in generated iOS project
DannyBaanks Oct 2, 2026
353b4e8
feat: bind iBridge Mobile to embedded tunnel Xcode template
DannyBaanks Oct 2, 2026
30e94a2
docs: attribute embedded StosVPN packet reflector
DannyBaanks Oct 2, 2026
2e6abfa
ci: verify embedded tunnel inside iBridge Mobile IPA
DannyBaanks Oct 2, 2026
1c20c13
feat: add iBridge Mobile operation copy
DannyBaanks Oct 2, 2026
80790aa
feat: preconfigure Network Extensions before signing iBridge Mobile
DannyBaanks Oct 2, 2026
f1e5958
fix: canonicalize iBridge Mobile bundle identifier
DannyBaanks Oct 2, 2026
c81475e
fix: align embedded tunnel bundle identifier
DannyBaanks Oct 2, 2026
5657e97
fix: align Xcode tunnel extension bundle identifier
DannyBaanks Oct 2, 2026
6842bf7
fix: build Network Extension feature dictionary explicitly
DannyBaanks Oct 2, 2026
550590c
chore: present desktop fork as iBridge
DannyBaanks Oct 2, 2026
9cc93da
fix: pin mobile runtime to iBridge tunnel peer
DannyBaanks Oct 2, 2026
84eae75
chore: remove obsolete tunnel discovery dependency
DannyBaanks Oct 2, 2026
2d40b15
chore: align experimental native mobile bundle id
DannyBaanks Oct 2, 2026
3d9d4c8
fix: allow error suggestions on iOS
DannyBaanks Oct 2, 2026
0c4a9b8
fix: unblock mobile and desktop release builds
DannyBaanks Oct 2, 2026
a5e69e6
fix: deduplicate iOS NetworkExtension framework
DannyBaanks Oct 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 115 additions & 0 deletions .github/workflows/mobile.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
name: Build iBridge Mobile

on:
push:
branches:
- "*"
pull_request:
branches:
- "*"
workflow_dispatch:
release:
types: [published]

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

permissions:
contents: write

jobs:
mobile:
runs-on: macos-latest
steps:
- uses: actions/checkout@v6

- name: Install Rust iOS target
uses: dtolnay/rust-toolchain@stable
with:
targets: aarch64-apple-ios

- uses: oven-sh/setup-bun@v2
with:
bun-version: latest

- name: Install frontend dependencies
run: bun i --frozen-lockfile

- name: Initialize Tauri iOS project
run: bun tauri ios init --ci

- name: Build frontend assets
run: bun run build

- name: Compile unsigned Tauri iOS app
shell: bash
run: |
set -euo pipefail
PROJECT=$(find src-tauri/gen/apple -maxdepth 1 -name '*.xcodeproj' -print -quit)
if [ -z "$PROJECT" ]; then
echo "Generated Xcode project not found" >&2
exit 1
fi

SCHEME=$(xcodebuild -project "$PROJECT" -list -json | python3 -c '
import json, sys
data = json.load(sys.stdin)
schemes = data.get("project", {}).get("schemes", [])
scheme = next((value for value in schemes if value.endswith("_iOS")), None)
if not scheme:
raise SystemExit("No generated iOS scheme found")
print(scheme)
')

mkdir -p mobile-ci/app
xcodebuild \
-project "$PROJECT" \
-scheme "$SCHEME" \
-configuration release \
-sdk iphoneos \
-destination 'generic/platform=iOS' \
CODE_SIGNING_ALLOWED=NO \
CODE_SIGNING_REQUIRED=NO \
DEVELOPMENT_TEAM= \
ARCHS=arm64 \
ONLY_ACTIVE_ARCH=YES \
CONFIGURATION_BUILD_DIR="$PWD/mobile-ci/app" \
build

- name: Package resignable IPA
shell: bash
run: |
set -euo pipefail
APP=$(find mobile-ci/app -maxdepth 1 -type d -name '*.app' -print -quit)
if [ -z "$APP" ]; then
echo "Built iOS app not found" >&2
exit 1
fi

if [ ! -d "$APP/PlugIns/IBridgeTunnelProvider.appex" ]; then
echo "Embedded IBridgeTunnelProvider.appex not found" >&2
find "$APP" -maxdepth 3 -print
exit 1
fi

mkdir -p mobile-ci/Payload
cp -R "$APP" mobile-ci/Payload/iBridge.app
(
cd mobile-ci
zip -qry iBridge-Mobile.ipa Payload
)
test -s mobile-ci/iBridge-Mobile.ipa

- name: Upload IPA artifact
uses: actions/upload-artifact@v7
with:
name: iBridge-Mobile
path: mobile-ci/iBridge-Mobile.ipa
if-no-files-found: error

- name: Attach IPA to release
if: github.event_name == 'release'
env:
GH_TOKEN: ${{ github.token }}
run: gh release upload "${{ github.event.release.tag_name }}" mobile-ci/iBridge-Mobile.ipa --clobber
40 changes: 40 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: Test iBridge

on:
push:
branches:
- "*"
pull_request:
branches:
- "*"

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
test:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v6

- name: Install Rust stable
uses: dtolnay/rust-toolchain@stable

- name: Install Linux dependencies
run: |
sudo apt-get update
sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf perl make

- uses: oven-sh/setup-bun@v2
with:
bun-version: latest

- name: Install frontend dependencies
run: bun i --frozen-lockfile

- name: Rust tests
run: cargo test --manifest-path src-tauri/Cargo.toml

- name: Frontend build
run: bun run build
50 changes: 50 additions & 0 deletions THIRD_PARTY_NOTICES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Third-party notices

## StosVPN packet reflection technique

iBridge Mobile contains an adapted packet-reflection implementation in
`src-tauri/ios/IBridgeTunnelProvider/PacketTunnelProvider.swift` based on
[SideStore/StosVPN](https://github.com/SideStore/StosVPN), specifically the
`PacketTunnelProvider.swift` implementation authored by the SideStore Team.

iBridge is not StosVPN and does not imply endorsement by the SideStore Team.
The adapted code is used only as the local `NEPacketTunnelProvider` transport
inside iBridge Mobile.

Source reference: SideStore/StosVPN commit
`010bc141e6c2303b218924686f830e3249c66842`.

### StosVPN License

Copyright (c) 2025 SideStore Team

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

**Attribution is clearly given** to the original project and author(s) in a
prominent place (e.g., README, About page, or documentation).

Any derived project **must clearly state** that it is **based on** or **uses
code from** the original project.

Redistribution, rebranding, or publishing of the **entire project** (or a
substantially similar copy) under the same or similar name or branding,
**without explicit written permission**, is **prohibited**.

This clause is intended to prevent unauthorized re-uploads, clones, or
misrepresentation of the original work.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
102 changes: 102 additions & 0 deletions mobile/iBridgeMobile/Bootstrap.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import Foundation

struct BootstrapPayload: Codable {
let schema: String
let deviceUdid: String
let deviceName: String
let deviceVersion: String
let appleId: String
let anisetteServer: String
let pairingPath: String
let secretPath: String
let createdAt: String
}

struct AccountSecretPayload: Codable {
let schema: String
let appleId: String
let password: String
}

struct MobileConfiguration {
let appleID: String
let anisetteServer: String
let deviceUDID: String
let deviceName: String
let deviceVersion: String

static func load() -> MobileConfiguration? {
let defaults = UserDefaults.standard
guard
let appleID = defaults.string(forKey: "bootstrap.appleID"),
let anisetteServer = defaults.string(forKey: "bootstrap.anisetteServer"),
let deviceUDID = defaults.string(forKey: "bootstrap.deviceUDID"),
let deviceName = defaults.string(forKey: "bootstrap.deviceName"),
let deviceVersion = defaults.string(forKey: "bootstrap.deviceVersion")
else { return nil }
return MobileConfiguration(
appleID: appleID,
anisetteServer: anisetteServer,
deviceUDID: deviceUDID,
deviceName: deviceName,
deviceVersion: deviceVersion
)
}
}

enum BootstrapImporter {
static let pairingAccount = "devicePairing"
static let passwordAccount = "applePassword"

static func importIfPresent() throws {
let documents = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let directory = documents.appendingPathComponent("iBridgeBootstrap", isDirectory: true)
let payloadURL = directory.appendingPathComponent("bootstrap.json")
guard FileManager.default.fileExists(atPath: payloadURL.path) else { return }

let payload = try JSONDecoder().decode(BootstrapPayload.self, from: Data(contentsOf: payloadURL))
guard payload.schema == "ibridge.mobile-bootstrap/1" else {
throw NSError(
domain: "iBridgeBootstrap",
code: 1,
userInfo: [NSLocalizedDescriptionKey: "Unsupported bootstrap schema"]
)
}

let pairingURL = documents.appendingPathComponent(payload.pairingPath)
let secretURL = documents.appendingPathComponent(payload.secretPath)
let pairing = try Data(contentsOf: pairingURL)
let secret = try JSONDecoder().decode(AccountSecretPayload.self, from: Data(contentsOf: secretURL))

guard secret.schema == "ibridge.mobile-account-secret/1" else {
throw NSError(
domain: "iBridgeBootstrap",
code: 2,
userInfo: [NSLocalizedDescriptionKey: "Unsupported account secret schema"]
)
}
guard secret.appleId.caseInsensitiveCompare(payload.appleId) == .orderedSame else {
throw NSError(
domain: "iBridgeBootstrap",
code: 3,
userInfo: [NSLocalizedDescriptionKey: "Bootstrap account identity did not match"]
)
}

try KeychainStore.set(pairing, account: pairingAccount)
try KeychainStore.setString(secret.password, account: passwordAccount)

let defaults = UserDefaults.standard
defaults.set(payload.appleId, forKey: "bootstrap.appleID")
defaults.set(payload.anisetteServer, forKey: "bootstrap.anisetteServer")
defaults.set(payload.deviceUdid, forKey: "bootstrap.deviceUDID")
defaults.set(payload.deviceName, forKey: "bootstrap.deviceName")
defaults.set(payload.deviceVersion, forKey: "bootstrap.deviceVersion")
defaults.set(payload.createdAt, forKey: "bootstrap.createdAt")

try? FileManager.default.removeItem(at: secretURL)
try? FileManager.default.removeItem(at: pairingURL)
try? FileManager.default.removeItem(at: payloadURL)
try? FileManager.default.removeItem(at: directory)
}
}
7 changes: 7 additions & 0 deletions mobile/iBridgeMobile/BridgingHeader.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#ifndef IBRIDGE_CORE_H
#define IBRIDGE_CORE_H

char *ibridge_sign_and_install(const char *request_json);
void ibridge_string_free(char *value);

#endif
Loading
Loading