diff --git a/.github/workflows/arti-provenance.yml b/.github/workflows/arti-provenance.yml new file mode 100644 index 00000000..5d50564e --- /dev/null +++ b/.github/workflows/arti-provenance.yml @@ -0,0 +1,85 @@ +name: Arti Binary Provenance + +# The Arti xcframework is a vendored binary; these checks turn the policy in +# docs/ARTI-BINARY-PROVENANCE.md into an enforced gate: +# 1. The checked-in binary must match the hash manifest in the provenance doc. +# 2. A PR that changes the binary must also change at least one provenance +# input (Rust source, lockfile, build script, or the doc itself). + +on: + push: + branches: + - main + paths: + - "localPackages/Arti/**" + - "docs/ARTI-BINARY-PROVENANCE.md" + pull_request: + paths: + - "localPackages/Arti/**" + - "docs/ARTI-BINARY-PROVENANCE.md" + +jobs: + verify-hashes: + name: Verify xcframework hashes against provenance doc + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v5 + + - name: Compare artifact hashes with manifest + run: | + set -euo pipefail + doc="docs/ARTI-BINARY-PROVENANCE.md" + + # Extract the manifest: lines of " " from the doc. + grep -E '^[0-9a-f]{64} localPackages/Arti/Frameworks/arti\.xcframework/' "$doc" \ + | sort -k2 > expected.txt + + if [ ! -s expected.txt ]; then + echo "::error::No hash manifest found in $doc" + exit 1 + fi + + # Hash the same file set the doc documents. + find localPackages/Arti/Frameworks/arti.xcframework -maxdepth 3 -type f -print0 \ + | sort -z | xargs -0 sha256sum | sed 's/ \.\// /' | sort -k2 > actual.txt + + if ! diff -u expected.txt actual.txt; then + echo "::error::Checked-in arti.xcframework does not match the manifest in $doc. If the binary change is intentional, rebuild per the doc and update the manifest in the same PR." + exit 1 + fi + echo "All $(wc -l < actual.txt) artifact hashes match the provenance manifest." + + require-provenance-evidence: + name: Binary changes must ship with provenance inputs + runs-on: ubuntu-latest + if: github.event_name == 'pull_request' + + steps: + - name: Checkout code + uses: actions/checkout@v5 + with: + fetch-depth: 0 + + - name: Check changed files + run: | + set -euo pipefail + base="origin/${{ github.base_ref }}" + git fetch --no-tags --depth=1 origin "${{ github.base_ref }}" + changed=$(git diff --name-only "$base"...HEAD) + echo "Changed files:" + echo "$changed" + + if ! echo "$changed" | grep -q '^localPackages/Arti/Frameworks/arti\.xcframework/'; then + echo "No binary artifact changes; nothing to verify." + exit 0 + fi + + if echo "$changed" | grep -Eq '^(localPackages/Arti/(Cargo\.(toml|lock)|build-ios\.sh|arti-bitchat/)|docs/ARTI-BINARY-PROVENANCE\.md)'; then + echo "Binary change is accompanied by provenance inputs." + exit 0 + fi + + echo "::error::arti.xcframework changed without matching source, lockfile, build-script, or provenance-doc changes. See docs/ARTI-BINARY-PROVENANCE.md (\"Do not accept an xcframework-only update\")." + exit 1 diff --git a/PRIVACY_POLICY.md b/PRIVACY_POLICY.md index e27866bb..03d770de 100644 --- a/PRIVACY_POLICY.md +++ b/PRIVACY_POLICY.md @@ -1,6 +1,6 @@ # bitchat Privacy Policy -*Last updated: January 2025* +*Last updated: June 2026* ## Our Commitment @@ -9,7 +9,7 @@ bitchat is designed with privacy as its foundation. We believe private communica ## Summary - **No personal data collection** - We don't collect names, emails, or phone numbers -- **No servers** - Everything happens on your device and through peer-to-peer connections +- **No accounts or company servers** - Mesh chat works peer-to-peer; optional Nostr features use public or user-selected relays - **No tracking** - We have no analytics, telemetry, or user tracking - **Open source** - You can verify these claims by reading our code @@ -17,11 +17,11 @@ bitchat is designed with privacy as its foundation. We believe private communica ### On Your Device Only -1. **Identity Key** - - A cryptographic key generated on first launch +1. **Identity Keys** + - Cryptographic private keys generated on first launch or when optional Nostr identities are created - Stored locally in your device's secure storage - Allows you to maintain "favorite" relationships across app restarts - - Never leaves your device + - Private keys never leave your device; public keys are shared when needed for messaging 2. **Nickname** - The display name you choose (or auto-generated) @@ -38,12 +38,19 @@ bitchat is designed with privacy as its foundation. We believe private communica - Stored only on your device - Allows you to recognize these peers in future sessions +5. **Optional Location Channel State** + - Your selected geohash channel, bookmarked geohashes, teleport flags, and bookmark display names + - Stored locally on your device so the location-channel UI can restore your choices + - Per-geohash Nostr identities are derived locally from a device seed stored in secure storage + - Exact latitude and longitude are not persisted by bitchat + ### Temporary Session Data During each session, bitchat temporarily maintains: - Active peer connections (forgotten when app closes) - Routing information for message delivery - Cached messages for offline peers (12 hours max) +- Your current location while optional location channels are enabled, used locally to compute geohash channels and friendly place names ## What Information is Shared @@ -62,13 +69,21 @@ When you join a password-protected room: - Your nickname appears in the member list - Room owners can see you've joined +### With Nostr Relays (Optional Features) + +If you enable Nostr-backed features: +- Private fallback messages to mutual favorites are sent as encrypted NIP-17 gift wraps. Relays can see event metadata, but not message content. +- Public location-channel messages, location notes, and presence are scoped with geohash tags. Relays and other participants can see the geohash tag, event kind, timestamp, and public key used for that geohash. +- Exact GPS coordinates are not included in Nostr events by bitchat. The geohash precision you choose can still reveal an approximate area, from region-level to building-level. +- Automatic presence heartbeats are limited to low-precision geohashes (region, province, and city). More precise geohash posts happen only when you use those channels or location notes. + ## What We DON'T Do bitchat **never**: - Collects personal information -- Tracks your location -- Stores data on servers -- Shares data with third parties +- Sells or shares your exact GPS location +- Stores data on servers we operate +- Sells your data to advertisers or data brokers - Uses analytics or telemetry - Creates user profiles - Requires registration @@ -84,19 +99,27 @@ All private messages use end-to-end encryption: ## Your Rights You have complete control: -- **Delete Everything**: Triple-tap the logo to instantly wipe all data -- **Leave Anytime**: Close the app and your presence disappears -- **No Account**: Nothing to delete from servers because there are none -- **Portability**: Your data never leaves your device unless you export it +- **Delete Local State**: Triple-tap the logo to instantly wipe local keys, sessions, caches, and preferences +- **Leave Anytime**: Close the app and local presence stops; relay-backed presence ages out +- **No Account**: No account record exists for you to delete from us +- **Portability**: Your local state stays on your device unless you send messages, use optional relay-backed features, or export it ## Bluetooth & Permissions bitchat requires Bluetooth permission to function: - Used only for peer-to-peer communication -- No location data is accessed or stored - Bluetooth is not used for tracking - You can revoke this permission at any time in system settings +## Location Permission + +Location permission is optional and is used only for location channels: +- Used to compute local geohash channels and display names +- Requested as when-in-use permission +- Exact coordinates are not shared in messages or stored by bitchat +- Selected and bookmarked geohashes may persist locally until you remove them, use panic wipe, or delete the app +- You can revoke this permission at any time in system settings + ## Children's Privacy bitchat does not knowingly collect information from children. The app has no age verification because it collects no personal information from anyone. @@ -106,12 +129,15 @@ bitchat does not knowingly collect information from children. The app has no age - **Messages**: Deleted from memory when app closes (unless room retention is enabled) - **Identity Key**: Persists until you delete the app - **Favorites**: Persist until you remove them or delete the app +- **Location channel choices**: Selected/bookmarked geohashes persist locally until removed, panic-wiped, or the app is deleted +- **Nostr relay data**: Public geohash events and encrypted gift wraps may be retained by relays according to each relay's policy - **Everything Else**: Exists only during active sessions ## Security Measures - All communication is encrypted -- No data transmitted to servers (there are none) +- No accounts or company servers +- Optional Nostr relays receive only the events needed for Nostr-backed private fallback or public location channels - Open source code for public audit - Regular security updates - Cryptographic signatures prevent tampering @@ -121,7 +147,7 @@ bitchat does not knowingly collect information from children. The app has no age If we update this policy: - The "Last updated" date will change - The updated policy will be included in the app -- No retroactive changes can affect data (since we don't collect any) +- No retroactive changes can make us collect data already held only in your app ## Contact @@ -132,7 +158,7 @@ bitchat is an open source project. For privacy questions: ## Philosophy -Privacy isn't just a feature—it's the entire point. bitchat proves that modern communication doesn't require surrendering your privacy. No accounts, no servers, no surveillance. Just people talking freely. +Privacy isn't just a feature—it's the entire point. bitchat proves that modern communication doesn't require surrendering your privacy. No accounts, no company servers, no analytics. Just people talking freely. --- diff --git a/bitchat/Localizable.xcstrings b/bitchat/Localizable.xcstrings index 8cbbf031..ac92becf 100644 --- a/bitchat/Localizable.xcstrings +++ b/bitchat/Localizable.xcstrings @@ -24397,7 +24397,7 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "chat with people near you using geohash channels. only a coarse geohash is shared, never exact GPS. your IP address is hidden by routing all traffic over tor." + "value" : "chat with people near you using geohash channels. the selected geohash is public and may reveal an approximate area; exact GPS is never shared. your IP address is hidden by routing all traffic over tor." } }, "es" : { diff --git a/docs/ARTI-BINARY-PROVENANCE.md b/docs/ARTI-BINARY-PROVENANCE.md new file mode 100644 index 00000000..a13240ba --- /dev/null +++ b/docs/ARTI-BINARY-PROVENANCE.md @@ -0,0 +1,88 @@ +# Arti Binary Provenance + +This repo vendors a prebuilt Arti static-library xcframework at: + +`localPackages/Arti/Frameworks/arti.xcframework` + +SwiftPM links it through `localPackages/Arti/Package.swift` as the binary target named `arti`. Treat changes to this artifact like dependency updates: review the Rust sources, lockfile, build script, produced headers, and artifact hashes together. + +## Source Inputs + +- Rust workspace: `localPackages/Arti/Cargo.toml` +- Crate: `localPackages/Arti/arti-bitchat` +- Dependency lockfile: `localPackages/Arti/Cargo.lock` +- Build script: `localPackages/Arti/build-ios.sh` +- Exported C header: `localPackages/Arti/Frameworks/include/arti.h` + +The crate declares `rust-version = "1.90"` and uses `arti-client` / `tor-rtcompat` `0.38` with minimal Tokio/Rustls features. The current lockfile requires Rust 1.90 or newer. The build script currently targets: + +- `aarch64-apple-ios` +- `aarch64-apple-ios-sim` +- `aarch64-apple-darwin` + +It builds release static libraries with size-oriented flags (`opt-level=z`, fat LTO, one codegen unit, `panic=abort`, stripped symbols), normalizes static-archive metadata with `xcrun libtool -static -D`, then packages them with `xcodebuild -create-xcframework`. + +## Regenerating The Artifact + +From the repo root: + +```sh +cd localPackages/Arti +rustup toolchain install 1.96.0 +rustup default 1.96.0 +rustup target add aarch64-apple-ios aarch64-apple-ios-sim aarch64-apple-darwin +cargo install cbindgen +./build-ios.sh +``` + +After rebuilding, verify that: + +- `Cargo.lock` changes are intentional and reviewed. +- `Frameworks/include/arti.h` still matches the exported FFI functions used by `TorManager`. +- `Frameworks/arti.xcframework` contains iOS device, iOS simulator, and macOS arm64 slices. +- The main app still passes iOS tests and the macOS build. + +## Audited Rebuild + +The June 2026 artifact below was rebuilt from source on this host with: + +```text +rustc 1.96.0 (ac68faa20 2026-05-25) +cargo 1.96.0 (30a34c682 2026-05-25) +rustup 1.29.0 (28d1352db 2026-03-05) +cbindgen 0.29.3 +Xcode 26.5 +Build version 17F42 +``` + +Rust 1.86.0 was also checked during the audit and no longer builds this lockfile because `typed-index-collections@3.4.0` requires Rust 1.90.0 or newer. + +The build script now normalizes static-archive metadata and writes a stable xcframework `Info.plist`. Two consecutive no-source-change rebuilds on this host produced the same hashes below. + +## Current Artifact Hashes + +Run this from the repo root to verify the checked-in artifact: + +```sh +find localPackages/Arti/Frameworks/arti.xcframework -maxdepth 3 -type f -print0 | sort -z | xargs -0 shasum -a 256 +``` + +Current hashes: + +```text +2083d44eafc765db1ffa2691a5c5fabe60b4edbb82b574169ca0c6b98e245e3a localPackages/Arti/Frameworks/arti.xcframework/Info.plist +551655904834748c9dc36034fdbc9465e7533aef1e4a6514b4fcc75875b93058 localPackages/Arti/Frameworks/arti.xcframework/ios-arm64-simulator/Headers/arti.h +85febff37b751df667a3cab8222de2e1450cefe44b5b62c419adcbce48b9663f localPackages/Arti/Frameworks/arti.xcframework/ios-arm64-simulator/libarti_bitchat.a +551655904834748c9dc36034fdbc9465e7533aef1e4a6514b4fcc75875b93058 localPackages/Arti/Frameworks/arti.xcframework/ios-arm64/Headers/arti.h +fd25ee379d709a794733fc3c052746d1e6f7b25fec23e5f5234008a3434ce879 localPackages/Arti/Frameworks/arti.xcframework/ios-arm64/libarti_bitchat.a +551655904834748c9dc36034fdbc9465e7533aef1e4a6514b4fcc75875b93058 localPackages/Arti/Frameworks/arti.xcframework/macos-arm64/Headers/arti.h +8c426a41dc3eb76cc3e3e22e3356b9d11dbebdf0a0f248c5ac892e1839352c75 localPackages/Arti/Frameworks/arti.xcframework/macos-arm64/libarti_bitchat.a +``` + +## Review Checklist + +- Record `rustc --version`, `cargo --version`, `cbindgen --version`, and `xcodebuild -version` in the PR when refreshing the binary. +- Include the hash output above after any binary change. +- If a rebuild changes only xcframework/library bytes, record the new hashes and app validation evidence in the PR. +- Keep `target/`, `.build/`, and `.swiftpm/` out of source control. +- Do not accept an xcframework-only update without matching source, lockfile, or build-script evidence explaining where it came from. diff --git a/docs/TOR-INTEGRATION.md b/docs/TOR-INTEGRATION.md index 0d714834..165acfe3 100644 --- a/docs/TOR-INTEGRATION.md +++ b/docs/TOR-INTEGRATION.md @@ -2,7 +2,7 @@ Tor-by-default integration (scaffold) Overview - All network traffic is routed via a local Tor SOCKS5 proxy by default, with fail-closed behavior when Tor isn’t ready. There are no user-visible settings. -- This repo now includes a minimal TorManager and TorURLSession to make dropping in an embedded Tor framework straightforward. +- This repo vendors an Arti-backed Swift package under `localPackages/Arti`, including a Rust static-library xcframework linked by SwiftPM. Key pieces - TorManager @@ -12,36 +12,17 @@ Key pieces - Provides a shared URLSession configured with a SOCKS5 proxy when Tor is enforced/ready. - NostrRelayManager and GeoRelayDirectory now use this session and await Tor readiness before starting network activity. -Drop‑in steps -1) Build or obtain a small Tor framework - - Recommended: Tor C (client-only) with static linking and dead-strip. - - Configure Tor with a minimal feature set: - ./configure \ - --enable-static \ - --disable-asciidoc --disable-unittests --disable-manpage \ - --disable-zstd --disable-lzma --enable-zlib \ - --disable-systemd --disable-ptrace --disable-seccomp - CFLAGS="-Os -fdata-sections -ffunction-sections" \ - LDFLAGS="-Wl,-dead_strip" - - Build a tiny OpenSSL/LibreSSL (no engines, strip symbols) or reuse system crypto where permitted on macOS. +Artifact maintenance +- Binary provenance, rebuild steps, and current hashes are documented in `docs/ARTI-BINARY-PROVENANCE.md`. +- The xcframework must include iOS device, iOS simulator, and macOS arm64 slices. +- Any refresh should review the Rust source, `Cargo.lock`, generated header, build script, and new hashes together. -2) Add the framework to Xcode targets - - Drop your xcframework into `Frameworks/`. The project is prewired in `project.yml` to link/embed `Frameworks/tor-nolzma.xcframework` (rename yours to match, or update the path). - - Ensure the binary includes the slices you need (iOS device/simulator and/or macOS). If your xcframework lacks simulator slices, you can still build/run on device or macOS arm64; simulator will fail to link. - - On iOS, it will be embedded and signed automatically. - -3) Wire Tor bootstrap in TorManager.startTor() - - Two paths are already implemented: - - If a module named `Tor` is present (iCepa API), it starts `TORThread` directly. - - Otherwise, it attempts a dynamic load (`dlopen`) of a bundled framework binary named `tor-nolzma.framework/tor-nolzma` (or `Tor.framework/Tor`), resolves `tor_run_main`, and launches Tor on a background thread. - - `TorManager` writes a torrc and then probes `127.0.0.1:39050` until ready. - -4) Verify networking +Verification - On app launch, TorManager.startIfNeeded() is called implicitly by awaitReady(). - NostrRelayManager.connect() awaits readiness, then creates WebSocket tasks via TorURLSession.shared. - GeoRelayDirectory.fetchRemote() awaits readiness, then fetches via TorURLSession.shared. -5) Optional macOS optimization +Optional macOS optimization - Detect a system Tor binary (e.g., /opt/homebrew/bin/tor) and run it as a subprocess to avoid bundling. Keep the embedded fallback for portability. torrc template diff --git a/docs/privacy-assessment.md b/docs/privacy-assessment.md index cba3aa2e..bcb5c178 100644 --- a/docs/privacy-assessment.md +++ b/docs/privacy-assessment.md @@ -4,14 +4,16 @@ BitChat Privacy Assessment Scope - Mesh transport (BLE) behavior and metadata minimization - Nostr-based private message fallback (gift-wrapped, end-to-end encrypted) +- Nostr-backed public geohash channels, presence heartbeats, and location notes +- Optional CoreLocation use for geohash channel discovery - Read receipts and delivery acknowledgments - Logging/telemetry posture and controls Summary -- No accounts, no servers for mesh; Nostr used only for mutual favorites, with end-to-end Noise encryption encapsulated in gift wraps. +- No accounts and no project-operated servers. Mesh traffic is peer-to-peer; Nostr is used for mutual-favorite private fallback and public geohash features. - BLE announces contain only nickname and Noise pubkey. No device name, no plaintext identity beyond what the user broadcasts. - Discovery and flooding incorporate jitter and TTL caps to reduce linkability and propagation radius of encrypted payloads. -- UI and storage remain ephemeral; message content is not persisted to disk. Minimal state (e.g., read-receipt IDs) is stored for UX and is bounded/cleaned. +- UI and storage remain mostly ephemeral; message content is not persisted to disk by default. Minimal local state (e.g., read-receipt IDs, favorites, selected/bookmarked geohashes) is stored for UX and is bounded or user-wipeable. - Logging defaults to conservative levels; debug verbosity is suppressed for release builds. A single env var can raise/lower threshold when needed. BLE Privacy Considerations @@ -35,6 +37,14 @@ Nostr Private Messaging Fallback - Read/delivery acks: Also encapsulated in gift wraps, preserving content secrecy and minimizing metadata. - Relay policy variance: Some relays apply “web-of-trust” policies and may reject events; BitChat tolerates partial delivery and still prefers mesh when available. +Location Channels and Geohash Public Chats +- Location permission: Optional when-in-use CoreLocation access computes local geohash channel options. Exact coordinates are held in memory only and are not included in BitChat or Nostr payloads. +- Local state: Selected channel, teleported geohashes, bookmarks, and bookmark display names are stored in `UserDefaults`; the panic action clears location presence state along with identity/session state. +- Geohash precision: User-selected channels can range from region-level to building-level. Public geohash messages and location notes expose the selected geohash tag to relays and participants. +- Presence minimization: Automatic presence heartbeats are restricted to low-precision region/province/city geohashes and use randomized timing. +- Per-geohash identities: Public geohash Nostr identities are derived from a device seed stored in the keychain, reducing cross-channel linkability compared with a single stable public key. +- Relay metadata: Relays can observe event kind, geohash tag, public key, timestamp, and network metadata. Content in public geohash channels is intentionally public to that channel. + Read Receipts and Delivery Acks - Routing policy: Prefer mesh if Noise session established; otherwise use Nostr when mapping exists. - Throttling: Nostr READ acks are queued and rate-limited (~3/s) to prevent relay rate limits during backlogs. @@ -44,6 +54,9 @@ Data Retention and State - Messages: Ephemeral in-memory only; history is bounded per chat and trimmed. - Read-receipt IDs: Stored in `UserDefaults` for UX continuity; periodically pruned to IDs present in memory. - Favorites: Noise and optional Nostr keys with petnames; can be wiped via panic action. +- Location channels: Exact coordinates are not persisted by BitChat. Selected/bookmarked geohashes, teleport flags, and bookmark display names persist locally until removed, panic-wiped, or the app is deleted. +- Geohash identities: Device seed is stored in the keychain and used to derive per-geohash Nostr identities deterministically. +- Relay persistence: Public geohash events, location notes, and encrypted gift wraps may be retained by relays according to each relay's policy. - Panic: Triple-tap clears keys, sessions, cached state, and disconnects transports. Logging and Telemetry @@ -56,9 +69,11 @@ Residual Risks and Mitigations - RF fingerprinting: BLE presence is observable at the RF layer; mitigated by minimal announce content and platform MAC randomization. - Timing correlation: Announce/relay jitter reduces but does not eliminate timing analysis. Avoids synchronized bursts. - Relay metadata: Nostr relays can see that an account posts gift wraps; content remains end-to-end encrypted. Favor mesh path when in range. +- Geohash inference: Public location-channel tags reveal approximate area. Mitigated by explicit channel selection, low-precision automatic presence, and per-geohash identities. +- Bookmark persistence: Locally stored geohash bookmarks may reveal places of interest on a seized/unlocked device. Mitigated by panic wipe and local-only storage. Recommendations (Next) - Add optional coalesced READ behavior for large backlogs. - Expose a “low-visibility mode” to reduce scanning aggressiveness in sensitive contexts. - Allow user-configurable Nostr relay set with a “private relays only” toggle. - +- Add a user-facing precision warning before posting in block/building-level geohash channels. diff --git a/localPackages/Arti/Frameworks/arti.xcframework/Info.plist b/localPackages/Arti/Frameworks/arti.xcframework/Info.plist index 5e9b24d6..2cfc3283 100644 --- a/localPackages/Arti/Frameworks/arti.xcframework/Info.plist +++ b/localPackages/Arti/Frameworks/arti.xcframework/Info.plist @@ -4,22 +4,6 @@ AvailableLibraries - - BinaryPath - libarti_bitchat.a - HeadersPath - Headers - LibraryIdentifier - macos-arm64 - LibraryPath - libarti_bitchat.a - SupportedArchitectures - - arm64 - - SupportedPlatform - macos - BinaryPath libarti_bitchat.a @@ -54,6 +38,22 @@ SupportedPlatformVariant simulator + + BinaryPath + libarti_bitchat.a + HeadersPath + Headers + LibraryIdentifier + macos-arm64 + LibraryPath + libarti_bitchat.a + SupportedArchitectures + + arm64 + + SupportedPlatform + macos + CFBundlePackageType XFWK diff --git a/localPackages/Arti/Frameworks/arti.xcframework/ios-arm64-simulator/libarti_bitchat.a b/localPackages/Arti/Frameworks/arti.xcframework/ios-arm64-simulator/libarti_bitchat.a index 3b5d7176..9ab40ec7 100644 Binary files a/localPackages/Arti/Frameworks/arti.xcframework/ios-arm64-simulator/libarti_bitchat.a and b/localPackages/Arti/Frameworks/arti.xcframework/ios-arm64-simulator/libarti_bitchat.a differ diff --git a/localPackages/Arti/Frameworks/arti.xcframework/ios-arm64/libarti_bitchat.a b/localPackages/Arti/Frameworks/arti.xcframework/ios-arm64/libarti_bitchat.a index 35bf4c67..86b0de10 100644 Binary files a/localPackages/Arti/Frameworks/arti.xcframework/ios-arm64/libarti_bitchat.a and b/localPackages/Arti/Frameworks/arti.xcframework/ios-arm64/libarti_bitchat.a differ diff --git a/localPackages/Arti/Frameworks/arti.xcframework/macos-arm64/libarti_bitchat.a b/localPackages/Arti/Frameworks/arti.xcframework/macos-arm64/libarti_bitchat.a index 3884360b..e9525474 100644 Binary files a/localPackages/Arti/Frameworks/arti.xcframework/macos-arm64/libarti_bitchat.a and b/localPackages/Arti/Frameworks/arti.xcframework/macos-arm64/libarti_bitchat.a differ diff --git a/localPackages/Arti/Package.swift b/localPackages/Arti/Package.swift index c32ad6cf..1fe587da 100644 --- a/localPackages/Arti/Package.swift +++ b/localPackages/Arti/Package.swift @@ -37,7 +37,8 @@ let package = Package( .linkedLibrary("sqlite3"), ] ), - // Binary framework containing the Rust static library + // Binary framework containing the Rust static library. + // Provenance and rebuild steps: repo-root docs/ARTI-BINARY-PROVENANCE.md .binaryTarget( name: "arti", path: "Frameworks/arti.xcframework" diff --git a/localPackages/Arti/Sources/TorManager.swift b/localPackages/Arti/Sources/TorManager.swift index 45958b59..cd831ba5 100644 --- a/localPackages/Arti/Sources/TorManager.swift +++ b/localPackages/Arti/Sources/TorManager.swift @@ -110,7 +110,9 @@ public final class TorManager: ObservableObject { public func isForeground() -> Bool { isAppForeground } nonisolated - public func awaitReady(timeout: TimeInterval = 25.0) async -> Bool { + // Default matches the bootstrap monitor deadline (75s); a shorter wait here + // reports "not ready" while Arti is still legitimately bootstrapping. + public func awaitReady(timeout: TimeInterval = 75.0) async -> Bool { await MainActor.run { if self.isAppForeground { self.startIfNeeded() } } diff --git a/localPackages/Arti/arti-bitchat/Cargo.toml b/localPackages/Arti/arti-bitchat/Cargo.toml index fcb8abf5..5a835194 100644 --- a/localPackages/Arti/arti-bitchat/Cargo.toml +++ b/localPackages/Arti/arti-bitchat/Cargo.toml @@ -2,7 +2,7 @@ name = "arti-bitchat" version = "0.1.0" edition = "2021" -rust-version = "1.86" +rust-version = "1.90" [lib] crate-type = ["staticlib"] diff --git a/localPackages/Arti/build-ios.sh b/localPackages/Arti/build-ios.sh index 3b16b52e..ca01025f 100755 --- a/localPackages/Arti/build-ios.sh +++ b/localPackages/Arti/build-ios.sh @@ -133,6 +133,11 @@ create_xcframework() { log_info "Stripping $target library..." strip -x "$lib_path" 2>/dev/null || true + # Normalize archive metadata so repeated rebuilds are byte-stable. + local normalized_path="$lib_path.normalized" + xcrun libtool -static -D -no_warning_for_no_symbols "$lib_path" -o "$normalized_path" + mv "$normalized_path" "$lib_path" + cmd="$cmd -library $lib_path" # Add headers if they exist @@ -151,6 +156,71 @@ create_xcframework() { eval "$cmd" if [[ -d "$xcframework_path" ]]; then + cat > "$xcframework_path/Info.plist" << 'EOF' + + + + + AvailableLibraries + + + BinaryPath + libarti_bitchat.a + HeadersPath + Headers + LibraryIdentifier + ios-arm64 + LibraryPath + libarti_bitchat.a + SupportedArchitectures + + arm64 + + SupportedPlatform + ios + + + BinaryPath + libarti_bitchat.a + HeadersPath + Headers + LibraryIdentifier + ios-arm64-simulator + LibraryPath + libarti_bitchat.a + SupportedArchitectures + + arm64 + + SupportedPlatform + ios + SupportedPlatformVariant + simulator + + + BinaryPath + libarti_bitchat.a + HeadersPath + Headers + LibraryIdentifier + macos-arm64 + LibraryPath + libarti_bitchat.a + SupportedArchitectures + + arm64 + + SupportedPlatform + macos + + + CFBundlePackageType + XFWK + XCFrameworkFormatVersion + 1.0 + + +EOF local size=$(du -sh "$xcframework_path" | cut -f1) log_info "Created $xcframework_path ($size)" else