Skip to content

Commit ce2baa4

Browse files
committed
feat(tor): add isolated Tor infrastructure package
1 parent 8b64b17 commit ce2baa4

51 files changed

Lines changed: 2171 additions & 34 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

AGENTS.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,11 @@ Bull Bitcoin Mobile: self-custodial Bitcoin + Liquid + Lightning wallet. Flutter
3333

3434
## Monorepo / melos
3535

36-
The repo is migrating incrementally to a [melos](https://melos.invertase.dev/) pub-workspace. The Flutter app remains at the repo root through `useRootAsPackage: true`; current members are the `bull_ui` design system, its dev-only catalogue, the pure-Dart `primitives` package, and the pure-Dart `bull_payjoin` package. The makefile remains the canonical entry point across the workspace.
36+
The repo is migrating incrementally to a [melos](https://melos.invertase.dev/) pub-workspace. The Flutter app remains at the repo root through `useRootAsPackage: true`; current members are the `bull_ui` design system, its dev-only catalogue, the pure-Dart `primitives` package, the pure-Dart `bull_payjoin` package, and the Flutter `bull_tor` package. The makefile remains the canonical entry point across the workspace.
3737

3838
- **Run melos through the makefile** (`make bootstrap`), which wraps `fvm dart run melos` so the pinned SDK ([`.fvmrc`](.fvmrc)) is used. Never type bare `melos` (wrong SDK). For melos subcommands without a make target yet, use `fvm dart run melos <cmd>` — and add a make wrapper if it becomes routine.
3939
- **The makefile stays canonical** for daily commands. melos does not replace it: `make deps` is still `fvm flutter pub get --enforce-lockfile`, and the reproducible build chain ([Containerfile.app](Containerfile.app), [build-android.yml](.github/workflows/build-android.yml)) does not run melos. melos is a `dev_dependency` only — never compiled into the app, so the reproducible APK is unaffected.
40-
- **`packages/` and `features/` are reserved homes** for the migration (exception to rule #14 below): pure-Dart shared-foundation packages in `packages/` (shared domain like `wallet`/`secrets` + infrastructure like `storage`/`electrum`, no Flutter UI — `packages/` holds the shared foundation, with `lib/core`'s infra-only spirit per rule #7 preserved for the infra packages); Flutter feature packages (`send`, `receive`, `buy`, `sell`, …) mounted by the root shell in `features/`. A `packages/` member may depend on Flutter only to own a sealed UI component (e.g. `secrets`' `MnemonicView`, which renders the mnemonic without exposing it). As code is extracted, each new package gets `resolution: workspace` and is added to a `workspace:` key in the root pubspec; the root keeps `useRootAsPackage: true` (this combination is supported — see melos PR #927). See ARCHITECTURE.md "Monorepo Migration" for the full layout and the sealed-UI / encapsulation rules.
40+
- **`packages/` and `features/` are reserved homes** for the migration (exception to rule #14 below): app-internal shared-foundation and infrastructure packages live in `packages/` (shared domain like `wallet`/`secrets` + infrastructure like `storage`/`electrum`), with `lib/core`'s infra-only spirit per rule #7 preserved for the infra packages; Flutter feature packages (`send`, `receive`, `buy`, `sell`, …) mounted by the root shell live in `features/`. Packages stay pure Dart by default, but a `packages/` member may depend on Flutter when its encapsulated capability inherently requires a Flutter plugin or platform lifecycle (`bull_tor` is the canonical example), when it owns a sealed UI component (`secrets`' `MnemonicView`, which renders the mnemonic without exposing it), or when it implements the `bull_ui` design system. A Flutter dependency does not permit feature presentation or screens in an infrastructure package. As code is extracted, each new package gets `resolution: workspace` and is added to a `workspace:` key in the root pubspec; the root keeps `useRootAsPackage: true` (this combination is supported — see melos PR #927). See ARCHITECTURE.md "Monorepo Migration" for the full layout and the sealed-UI / encapsulation rules.
4141
- **Workspace verification is measured, not assumed.** Whole-project `fvm flutter analyze` covers the root and all members, and `make bootstrap` succeeds with `enforceLockfile: true` (verified 2026-07-30). Pub workspaces use the single root `pubspec.lock`; do not add member lockfiles. `make unit-test` dispatches Flutter members to `fvm flutter test` and pure-Dart members to `fvm dart test`.
4242

4343
## Architecture — enforce, don't drift

ARCHITECTURE.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -341,16 +341,18 @@ While the architecture described above represents our current standards, the cod
341341

342342
The codebase is migrating, incrementally, from a single Flutter package to a [melos](https://melos.invertase.dev/) pub-workspace. This is an organizational change layered on top of the architecture above — it does not alter the feature-based / layered rules, it gives them a stronger, compile-time boundary.
343343

344-
**Current state.** The app remains at the repo root and is declared as a workspace package through `useRootAsPackage: true`. `packages/bull_ui` owns the design system, `packages/bull_ui_catalogue` is its dev-only Widgetbook, `packages/primitives` owns shared pure-Dart value types and results, and `packages/bull_payjoin` owns the pure-Dart Payjoin contract, engine, and persistence. User-facing feature packages have not moved into `features/` yet.
344+
**Current state.** The app remains at the repo root and is declared as a workspace package through `useRootAsPackage: true`. `packages/bull_ui` owns the design system, `packages/bull_ui_catalogue` is its dev-only Widgetbook, `packages/primitives` owns shared pure-Dart value types and results, `packages/bull_payjoin` owns the pure-Dart Payjoin contract, engine, and persistence, and the Flutter-dependent `packages/tor` (`bull_tor`) owns embedded Tor. User-facing feature packages have not moved into `features/` yet.
345345

346346
**Target layout.** As modules are extracted (one at a time, never a big-bang), they become pub-workspace members:
347347

348348
- **root = the app shell** — thin: routing, DI/get_it wiring, and composition of feature packages. It ships no domain or UI of its own beyond that wiring.
349349
- **`features/`** — Flutter packages, one per user-facing flow (`send`, `receive`, `buy`, `sell`, `recoverbull`, …). Each owns its domain behind its `public/` facade and is mounted into the shell (routes + DI). A feature may carry an `example/` mini-app to run it in isolation during development; the shipped artifact is the shell embedding the feature, not a standalone app.
350-
- **`packages/`**mostly pure-Dart packages with no Flutter UI: the shared foundation consumed by features. This is both shared domain (`wallet`, `secrets`) and infrastructure (`storage`, `electrum`, `blockchain`, …). Two narrow categories may depend on Flutter: a package that must own a sealed UI component (see "Sealed UI" below), and the shared **design-system package** (`bull_ui`, see "Design-system package" below).
350+
- **`packages/`**app-internal foundation and infrastructure consumed by features. Packages stay pure Dart by default (`wallet`, `storage`, `electrum`, `blockchain`, …), but may depend on Flutter when encapsulating an inherently Flutter/platform capability (`bull_tor`), when owning a sealed UI component (see "Sealed UI" below), or when implementing the shared **design-system package** (`bull_ui`, see "Design-system package" below). A Flutter dependency must serve the package's encapsulated capability; it is not permission to add feature presentation or screens.
351351

352352
Each extracted package gets `resolution: workspace` and is listed under a `workspace:` key in the root `pubspec.yaml`; the root app keeps `useRootAsPackage: true` and consumes the members. Dependencies point one way and stay acyclic: **shell → features → packages**. `packages/` never import `features/`; a feature touches a package only through its published API.
353353

354+
**Tor package (`packages/bull_tor`).** Tor is a Bull Mobile-specific Flutter infrastructure package. The Bull-prefixed package name lets it coexist with the Foundation `tor` dependency while consumers migrate in independently testable slices, and keeps its ownership explicit after that dependency is removed. The feature-facing `tor.dart` library publishes validated routes, sealed connection states, typed failures, and the operations consumers need. The narrower shell-facing `tor_adapter.dart` publishes composition, lifecycle, logging, and test seams. Internally the package owns Onion/Arti initialization, state/cache paths, mobile dormancy, the embedded SOCKS listener adapter, and external Orbot SOCKS verification.
355+
354356
**Why it matters for architecture.** Package boundaries turn the existing rules into *enforced* ones rather than conventions:
355357

356358
- The **Dependency Rule** and **acyclic feature graph** become compile errors when violated — a `packages/` infrastructure package physically cannot import a `features/` module, and a feature cannot reach into another feature's internals (only its published API is importable).

Containerfile.tools

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ RUN fvm dart --disable-analytics
267267
# Wrap rustup with a thin shim that does two things before delegating to the
268268
# real binary.
269269
#
270-
# (1) Version pin. cargokit (payjoin_flutter, tor, bull_sdk's rust_builder)
270+
# (1) Version pin. cargokit (payjoin_flutter, tor, onion, bull_sdk's rust_builder)
271271
# hard-codes `rustup run stable cargo build` and `rustup toolchain install
272272
# stable` — its toolchain option is a fixed stable/beta/nightly enum, so it
273273
# cannot name a version. An explicit `rustup run stable` outranks both

ios/Flutter/AppFrameworkInfo.plist

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,6 @@
2121
<key>CFBundleVersion</key>
2222
<string>1.0</string>
2323
<key>MinimumOSVersion</key>
24-
<string>14.0</string>
24+
<string>15.0</string>
2525
</dict>
2626
</plist>

ios/Podfile

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Uncomment this line to define a global platform for your project
2-
platform :ios, '14.0'
2+
platform :ios, '15.0'
33

44
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
55
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
@@ -55,9 +55,9 @@ post_install do |installer|
5555
]
5656

5757
# Suppress pod deployment target warnings — some pods declare targets
58-
# below the Xcode minimum (12.0). Raise them to match our floor.
59-
if config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'].to_f < 12.0
60-
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '12.0'
58+
# below the app minimum. Raise them to match our floor.
59+
if config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'].to_f < 15.0
60+
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '15.0'
6161
end
6262
end
6363
end

ios/Podfile.lock

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ PODS:
44
- Flutter
55
- rust_lib_bull_sdk (0.0.1):
66
- Flutter
7+
- onion (0.0.1):
8+
- Flutter
79
- tor (0.0.1):
810
- Flutter
911
- universal_ble (0.0.1):
@@ -18,6 +20,7 @@ DEPENDENCIES:
1820
- Flutter (from `Flutter`)
1921
- flutter_secure_storage_legacy (from `.symlinks/plugins/flutter_secure_storage_legacy/ios`)
2022
- rust_lib_bull_sdk (from `.symlinks/plugins/rust_lib_bull_sdk/ios`)
23+
- onion (from `.symlinks/plugins/onion/ios`)
2124
- tor (from `.symlinks/plugins/tor/ios`)
2225
- universal_ble (from `.symlinks/plugins/universal_ble/darwin`)
2326
- webview_cookie_manager (from `.symlinks/plugins/webview_cookie_manager/ios`)
@@ -30,6 +33,8 @@ EXTERNAL SOURCES:
3033
:path: ".symlinks/plugins/flutter_secure_storage_legacy/ios"
3134
rust_lib_bull_sdk:
3235
:path: ".symlinks/plugins/rust_lib_bull_sdk/ios"
36+
onion:
37+
:path: ".symlinks/plugins/onion/ios"
3338
tor:
3439
:path: ".symlinks/plugins/tor/ios"
3540
universal_ble:
@@ -43,6 +48,7 @@ SPEC CHECKSUMS:
4348
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
4449
flutter_secure_storage_legacy: 2b1517bd98433d760370884d3e2cc3ed8eeb2538
4550
rust_lib_bull_sdk: 160980033dfde220ad447a6dffa22d4269f8ea6c
51+
onion: 4876b9b0120d40a2817eac0903dfb9bd59832755
4652
tor: 767208930250ef7be241963b75568c55c0a81890
4753
universal_ble: 45519b2aeafe62761e2c6309f8927edb5288b914
4854
webview_cookie_manager: d63a76cabdf42a7ea3d92768ac67d4853a1367f8

ios/Runner.xcodeproj/project.pbxproj

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -465,7 +465,7 @@
465465
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
466466
GCC_WARN_UNUSED_FUNCTION = YES;
467467
GCC_WARN_UNUSED_VARIABLE = YES;
468-
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
468+
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
469469
MTL_ENABLE_DEBUG_INFO = NO;
470470
SDKROOT = iphoneos;
471471
SUPPORTED_PLATFORMS = iphoneos;
@@ -489,7 +489,7 @@
489489
INFOPLIST_FILE = Runner/Info.plist;
490490
INFOPLIST_KEY_CFBundleDisplayName = BULL;
491491
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.finance";
492-
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
492+
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
493493
LD_RUNPATH_SEARCH_PATHS = (
494494
"$(inherited)",
495495
"@executable_path/Frameworks",
@@ -604,7 +604,7 @@
604604
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
605605
GCC_WARN_UNUSED_FUNCTION = YES;
606606
GCC_WARN_UNUSED_VARIABLE = YES;
607-
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
607+
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
608608
MTL_ENABLE_DEBUG_INFO = YES;
609609
ONLY_ACTIVE_ARCH = YES;
610610
SDKROOT = iphoneos;
@@ -656,7 +656,7 @@
656656
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
657657
GCC_WARN_UNUSED_FUNCTION = YES;
658658
GCC_WARN_UNUSED_VARIABLE = YES;
659-
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
659+
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
660660
MTL_ENABLE_DEBUG_INFO = NO;
661661
SDKROOT = iphoneos;
662662
SUPPORTED_PLATFORMS = iphoneos;
@@ -682,7 +682,7 @@
682682
INFOPLIST_FILE = Runner/Info.plist;
683683
INFOPLIST_KEY_CFBundleDisplayName = BULL;
684684
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.finance";
685-
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
685+
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
686686
LD_RUNPATH_SEARCH_PATHS = (
687687
"$(inherited)",
688688
"@executable_path/Frameworks",
@@ -713,7 +713,7 @@
713713
INFOPLIST_FILE = Runner/Info.plist;
714714
INFOPLIST_KEY_CFBundleDisplayName = BULL;
715715
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.finance";
716-
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
716+
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
717717
LD_RUNPATH_SEARCH_PATHS = (
718718
"$(inherited)",
719719
"@executable_path/Frameworks",

linux/flutter/generated_plugins.cmake

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
1414
list(APPEND FLUTTER_FFI_PLUGIN_LIST
1515
flutter_zxing
1616
jni
17+
onion
1718
rust_lib_bull_sdk
1819
tor
1920
)

makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ android: container-app
254254
# read the live pin out of bull-app. Keep in sync with the `channel` in
255255
# bdk-dart's native/rust-toolchain.toml (bdk_dart is transitive via bull_sdk).
256256
BDK_RUST_VERSION ?= 1.85.1
257-
TRACKED_RUST_LIBS := libbdk_dart_ffi.so libtor.so libpayjoin_flutter.so librust_lib_bull_sdk.so
257+
TRACKED_RUST_LIBS := libbdk_dart_ffi.so libtor.so libonion.so libpayjoin_flutter.so librust_lib_bull_sdk.so
258258
verify-rustc-pins:
259259
@command -v strings >/dev/null 2>&1 || { echo "❌ 'strings' (binutils) not found — cannot verify rustc pins. Install binutils; failing closed rather than skipping the check (a skipped check must never read as green)."; exit 1; }
260260
@tmpdir=$$(mktemp -d); \
@@ -278,7 +278,7 @@ verify-rustc-pins:
278278
name=$$(basename "$$so"); \
279279
case "$$name" in \
280280
libbdk_dart_ffi.so) expected="$$bdk_rustc" ;; \
281-
libtor.so|libpayjoin_flutter.so|librust_lib_bull_sdk.so) expected="$$cargokit_rustc" ;; \
281+
libtor.so|libonion.so|libpayjoin_flutter.so|librust_lib_bull_sdk.so) expected="$$cargokit_rustc" ;; \
282282
*) expected="" ;; \
283283
esac; \
284284
embedded=$$(strings "$$so" 2>/dev/null | grep -m1 -o 'rustc version [0-9][0-9A-Za-z.+-]*' | awk '{print $$3}'); \

packages/bull_payjoin/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ dependencies:
1212
bull_sdk:
1313
git:
1414
url: https://github.com/SatoshiPortal/bull_sdk
15-
ref: 62977e2f5159458d79ec8089dc853a8edcb84458
15+
ref: 88e05c9e9d2911f3dcd44b449ee97e27c73c1e51
1616
path: packages/bull_sdk
1717
crypto: ^3.0.7
1818
dio: ^5.9.0

0 commit comments

Comments
 (0)