mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-07-24 22:45:19 +00:00
Rebuild Arti from audited source with enforced provenance
Vendored arti.xcframework rebuilt from source (Rust 1.96.0, normalized archive metadata for reproducible hashes). New ARTI-BINARY-PROVENANCE.md records toolchain, rebuild steps, and a SHA256 manifest for every file in the xcframework. A new CI workflow turns that policy into a gate: PRs must keep the binary matching the manifest, and binary changes must ship with source/lockfile/build-script evidence. Also raises TorManager.awaitReady's default timeout from 25s to 75s to match the bootstrap monitor deadline - a shorter wait reported "not ready" while Arti was still legitimately bootstrapping, silently stranding queued relay work. Privacy policy, Tor integration doc, and privacy assessment updated to match the current implementation. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -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 "<sha256> <path>" 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
|
||||||
+42
-16
@@ -1,6 +1,6 @@
|
|||||||
# bitchat Privacy Policy
|
# bitchat Privacy Policy
|
||||||
|
|
||||||
*Last updated: January 2025*
|
*Last updated: June 2026*
|
||||||
|
|
||||||
## Our Commitment
|
## Our Commitment
|
||||||
|
|
||||||
@@ -9,7 +9,7 @@ bitchat is designed with privacy as its foundation. We believe private communica
|
|||||||
## Summary
|
## Summary
|
||||||
|
|
||||||
- **No personal data collection** - We don't collect names, emails, or phone numbers
|
- **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
|
- **No tracking** - We have no analytics, telemetry, or user tracking
|
||||||
- **Open source** - You can verify these claims by reading our code
|
- **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
|
### On Your Device Only
|
||||||
|
|
||||||
1. **Identity Key**
|
1. **Identity Keys**
|
||||||
- A cryptographic key generated on first launch
|
- Cryptographic private keys generated on first launch or when optional Nostr identities are created
|
||||||
- Stored locally in your device's secure storage
|
- Stored locally in your device's secure storage
|
||||||
- Allows you to maintain "favorite" relationships across app restarts
|
- 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**
|
2. **Nickname**
|
||||||
- The display name you choose (or auto-generated)
|
- 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
|
- Stored only on your device
|
||||||
- Allows you to recognize these peers in future sessions
|
- 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
|
### Temporary Session Data
|
||||||
|
|
||||||
During each session, bitchat temporarily maintains:
|
During each session, bitchat temporarily maintains:
|
||||||
- Active peer connections (forgotten when app closes)
|
- Active peer connections (forgotten when app closes)
|
||||||
- Routing information for message delivery
|
- Routing information for message delivery
|
||||||
- Cached messages for offline peers (12 hours max)
|
- 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
|
## What Information is Shared
|
||||||
|
|
||||||
@@ -62,13 +69,21 @@ When you join a password-protected room:
|
|||||||
- Your nickname appears in the member list
|
- Your nickname appears in the member list
|
||||||
- Room owners can see you've joined
|
- 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
|
## What We DON'T Do
|
||||||
|
|
||||||
bitchat **never**:
|
bitchat **never**:
|
||||||
- Collects personal information
|
- Collects personal information
|
||||||
- Tracks your location
|
- Sells or shares your exact GPS location
|
||||||
- Stores data on servers
|
- Stores data on servers we operate
|
||||||
- Shares data with third parties
|
- Sells your data to advertisers or data brokers
|
||||||
- Uses analytics or telemetry
|
- Uses analytics or telemetry
|
||||||
- Creates user profiles
|
- Creates user profiles
|
||||||
- Requires registration
|
- Requires registration
|
||||||
@@ -84,19 +99,27 @@ All private messages use end-to-end encryption:
|
|||||||
## Your Rights
|
## Your Rights
|
||||||
|
|
||||||
You have complete control:
|
You have complete control:
|
||||||
- **Delete Everything**: Triple-tap the logo to instantly wipe all data
|
- **Delete Local State**: Triple-tap the logo to instantly wipe local keys, sessions, caches, and preferences
|
||||||
- **Leave Anytime**: Close the app and your presence disappears
|
- **Leave Anytime**: Close the app and local presence stops; relay-backed presence ages out
|
||||||
- **No Account**: Nothing to delete from servers because there are none
|
- **No Account**: No account record exists for you to delete from us
|
||||||
- **Portability**: Your data never leaves your device unless you export it
|
- **Portability**: Your local state stays on your device unless you send messages, use optional relay-backed features, or export it
|
||||||
|
|
||||||
## Bluetooth & Permissions
|
## Bluetooth & Permissions
|
||||||
|
|
||||||
bitchat requires Bluetooth permission to function:
|
bitchat requires Bluetooth permission to function:
|
||||||
- Used only for peer-to-peer communication
|
- Used only for peer-to-peer communication
|
||||||
- No location data is accessed or stored
|
|
||||||
- Bluetooth is not used for tracking
|
- Bluetooth is not used for tracking
|
||||||
- You can revoke this permission at any time in system settings
|
- 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
|
## 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.
|
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)
|
- **Messages**: Deleted from memory when app closes (unless room retention is enabled)
|
||||||
- **Identity Key**: Persists until you delete the app
|
- **Identity Key**: Persists until you delete the app
|
||||||
- **Favorites**: Persist until you remove them or 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
|
- **Everything Else**: Exists only during active sessions
|
||||||
|
|
||||||
## Security Measures
|
## Security Measures
|
||||||
|
|
||||||
- All communication is encrypted
|
- 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
|
- Open source code for public audit
|
||||||
- Regular security updates
|
- Regular security updates
|
||||||
- Cryptographic signatures prevent tampering
|
- 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:
|
If we update this policy:
|
||||||
- The "Last updated" date will change
|
- The "Last updated" date will change
|
||||||
- The updated policy will be included in the app
|
- 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
|
## Contact
|
||||||
|
|
||||||
@@ -132,7 +158,7 @@ bitchat is an open source project. For privacy questions:
|
|||||||
|
|
||||||
## Philosophy
|
## 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.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -24397,7 +24397,7 @@
|
|||||||
"en" : {
|
"en" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
"state" : "translated",
|
"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" : {
|
"es" : {
|
||||||
|
|||||||
@@ -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.
|
||||||
+7
-26
@@ -2,7 +2,7 @@ Tor-by-default integration (scaffold)
|
|||||||
|
|
||||||
Overview
|
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.
|
- 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
|
Key pieces
|
||||||
- TorManager
|
- TorManager
|
||||||
@@ -12,36 +12,17 @@ Key pieces
|
|||||||
- Provides a shared URLSession configured with a SOCKS5 proxy when Tor is enforced/ready.
|
- 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.
|
- NostrRelayManager and GeoRelayDirectory now use this session and await Tor readiness before starting network activity.
|
||||||
|
|
||||||
Drop‑in steps
|
Artifact maintenance
|
||||||
1) Build or obtain a small Tor framework
|
- Binary provenance, rebuild steps, and current hashes are documented in `docs/ARTI-BINARY-PROVENANCE.md`.
|
||||||
- Recommended: Tor C (client-only) with static linking and dead-strip.
|
- The xcframework must include iOS device, iOS simulator, and macOS arm64 slices.
|
||||||
- Configure Tor with a minimal feature set:
|
- Any refresh should review the Rust source, `Cargo.lock`, generated header, build script, and new hashes together.
|
||||||
./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.
|
|
||||||
|
|
||||||
2) Add the framework to Xcode targets
|
Verification
|
||||||
- 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
|
|
||||||
- On app launch, TorManager.startIfNeeded() is called implicitly by awaitReady().
|
- On app launch, TorManager.startIfNeeded() is called implicitly by awaitReady().
|
||||||
- NostrRelayManager.connect() awaits readiness, then creates WebSocket tasks via TorURLSession.shared.
|
- NostrRelayManager.connect() awaits readiness, then creates WebSocket tasks via TorURLSession.shared.
|
||||||
- GeoRelayDirectory.fetchRemote() awaits readiness, then fetches 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.
|
- 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
|
torrc template
|
||||||
|
|||||||
@@ -4,14 +4,16 @@ BitChat Privacy Assessment
|
|||||||
Scope
|
Scope
|
||||||
- Mesh transport (BLE) behavior and metadata minimization
|
- Mesh transport (BLE) behavior and metadata minimization
|
||||||
- Nostr-based private message fallback (gift-wrapped, end-to-end encrypted)
|
- 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
|
- Read receipts and delivery acknowledgments
|
||||||
- Logging/telemetry posture and controls
|
- Logging/telemetry posture and controls
|
||||||
|
|
||||||
Summary
|
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.
|
- 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.
|
- 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.
|
- 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
|
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.
|
- 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.
|
- 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
|
Read Receipts and Delivery Acks
|
||||||
- Routing policy: Prefer mesh if Noise session established; otherwise use Nostr when mapping exists.
|
- 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.
|
- 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.
|
- 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.
|
- 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.
|
- 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.
|
- Panic: Triple-tap clears keys, sessions, cached state, and disconnects transports.
|
||||||
|
|
||||||
Logging and Telemetry
|
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.
|
- 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.
|
- 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.
|
- 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)
|
Recommendations (Next)
|
||||||
- Add optional coalesced READ behavior for large backlogs.
|
- Add optional coalesced READ behavior for large backlogs.
|
||||||
- Expose a “low-visibility mode” to reduce scanning aggressiveness in sensitive contexts.
|
- Expose a “low-visibility mode” to reduce scanning aggressiveness in sensitive contexts.
|
||||||
- Allow user-configurable Nostr relay set with a “private relays only” toggle.
|
- 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.
|
||||||
|
|||||||
+16
-16
@@ -4,22 +4,6 @@
|
|||||||
<dict>
|
<dict>
|
||||||
<key>AvailableLibraries</key>
|
<key>AvailableLibraries</key>
|
||||||
<array>
|
<array>
|
||||||
<dict>
|
|
||||||
<key>BinaryPath</key>
|
|
||||||
<string>libarti_bitchat.a</string>
|
|
||||||
<key>HeadersPath</key>
|
|
||||||
<string>Headers</string>
|
|
||||||
<key>LibraryIdentifier</key>
|
|
||||||
<string>macos-arm64</string>
|
|
||||||
<key>LibraryPath</key>
|
|
||||||
<string>libarti_bitchat.a</string>
|
|
||||||
<key>SupportedArchitectures</key>
|
|
||||||
<array>
|
|
||||||
<string>arm64</string>
|
|
||||||
</array>
|
|
||||||
<key>SupportedPlatform</key>
|
|
||||||
<string>macos</string>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
<dict>
|
||||||
<key>BinaryPath</key>
|
<key>BinaryPath</key>
|
||||||
<string>libarti_bitchat.a</string>
|
<string>libarti_bitchat.a</string>
|
||||||
@@ -54,6 +38,22 @@
|
|||||||
<key>SupportedPlatformVariant</key>
|
<key>SupportedPlatformVariant</key>
|
||||||
<string>simulator</string>
|
<string>simulator</string>
|
||||||
</dict>
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>BinaryPath</key>
|
||||||
|
<string>libarti_bitchat.a</string>
|
||||||
|
<key>HeadersPath</key>
|
||||||
|
<string>Headers</string>
|
||||||
|
<key>LibraryIdentifier</key>
|
||||||
|
<string>macos-arm64</string>
|
||||||
|
<key>LibraryPath</key>
|
||||||
|
<string>libarti_bitchat.a</string>
|
||||||
|
<key>SupportedArchitectures</key>
|
||||||
|
<array>
|
||||||
|
<string>arm64</string>
|
||||||
|
</array>
|
||||||
|
<key>SupportedPlatform</key>
|
||||||
|
<string>macos</string>
|
||||||
|
</dict>
|
||||||
</array>
|
</array>
|
||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>XFWK</string>
|
<string>XFWK</string>
|
||||||
|
|||||||
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -37,7 +37,8 @@ let package = Package(
|
|||||||
.linkedLibrary("sqlite3"),
|
.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(
|
.binaryTarget(
|
||||||
name: "arti",
|
name: "arti",
|
||||||
path: "Frameworks/arti.xcframework"
|
path: "Frameworks/arti.xcframework"
|
||||||
|
|||||||
@@ -110,7 +110,9 @@ public final class TorManager: ObservableObject {
|
|||||||
public func isForeground() -> Bool { isAppForeground }
|
public func isForeground() -> Bool { isAppForeground }
|
||||||
|
|
||||||
nonisolated
|
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 {
|
await MainActor.run {
|
||||||
if self.isAppForeground { self.startIfNeeded() }
|
if self.isAppForeground { self.startIfNeeded() }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
name = "arti-bitchat"
|
name = "arti-bitchat"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.86"
|
rust-version = "1.90"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
crate-type = ["staticlib"]
|
crate-type = ["staticlib"]
|
||||||
|
|||||||
@@ -133,6 +133,11 @@ create_xcframework() {
|
|||||||
log_info "Stripping $target library..."
|
log_info "Stripping $target library..."
|
||||||
strip -x "$lib_path" 2>/dev/null || true
|
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"
|
cmd="$cmd -library $lib_path"
|
||||||
|
|
||||||
# Add headers if they exist
|
# Add headers if they exist
|
||||||
@@ -151,6 +156,71 @@ create_xcframework() {
|
|||||||
eval "$cmd"
|
eval "$cmd"
|
||||||
|
|
||||||
if [[ -d "$xcframework_path" ]]; then
|
if [[ -d "$xcframework_path" ]]; then
|
||||||
|
cat > "$xcframework_path/Info.plist" << 'EOF'
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>AvailableLibraries</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>BinaryPath</key>
|
||||||
|
<string>libarti_bitchat.a</string>
|
||||||
|
<key>HeadersPath</key>
|
||||||
|
<string>Headers</string>
|
||||||
|
<key>LibraryIdentifier</key>
|
||||||
|
<string>ios-arm64</string>
|
||||||
|
<key>LibraryPath</key>
|
||||||
|
<string>libarti_bitchat.a</string>
|
||||||
|
<key>SupportedArchitectures</key>
|
||||||
|
<array>
|
||||||
|
<string>arm64</string>
|
||||||
|
</array>
|
||||||
|
<key>SupportedPlatform</key>
|
||||||
|
<string>ios</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>BinaryPath</key>
|
||||||
|
<string>libarti_bitchat.a</string>
|
||||||
|
<key>HeadersPath</key>
|
||||||
|
<string>Headers</string>
|
||||||
|
<key>LibraryIdentifier</key>
|
||||||
|
<string>ios-arm64-simulator</string>
|
||||||
|
<key>LibraryPath</key>
|
||||||
|
<string>libarti_bitchat.a</string>
|
||||||
|
<key>SupportedArchitectures</key>
|
||||||
|
<array>
|
||||||
|
<string>arm64</string>
|
||||||
|
</array>
|
||||||
|
<key>SupportedPlatform</key>
|
||||||
|
<string>ios</string>
|
||||||
|
<key>SupportedPlatformVariant</key>
|
||||||
|
<string>simulator</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>BinaryPath</key>
|
||||||
|
<string>libarti_bitchat.a</string>
|
||||||
|
<key>HeadersPath</key>
|
||||||
|
<string>Headers</string>
|
||||||
|
<key>LibraryIdentifier</key>
|
||||||
|
<string>macos-arm64</string>
|
||||||
|
<key>LibraryPath</key>
|
||||||
|
<string>libarti_bitchat.a</string>
|
||||||
|
<key>SupportedArchitectures</key>
|
||||||
|
<array>
|
||||||
|
<string>arm64</string>
|
||||||
|
</array>
|
||||||
|
<key>SupportedPlatform</key>
|
||||||
|
<string>macos</string>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>XFWK</string>
|
||||||
|
<key>XCFrameworkFormatVersion</key>
|
||||||
|
<string>1.0</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
|
EOF
|
||||||
local size=$(du -sh "$xcframework_path" | cut -f1)
|
local size=$(du -sh "$xcframework_path" | cut -f1)
|
||||||
log_info "Created $xcframework_path ($size)"
|
log_info "Created $xcframework_path ($size)"
|
||||||
else
|
else
|
||||||
|
|||||||
Reference in New Issue
Block a user