damus
damus copied to clipboard
embed Tor
what happens
When adding a .onion ending LN address to my profile, damus users cannot zap me.
suggestion
Will advises embedding Tor in Damus will allow zapping a .onion address to work.
other benefits
Tor would always work. see related #876
https://damus.io/nevent1qqsdtx6zr666ps53x8elwr7l627tmdqt2efjlu2as3n6juwtmnvv7scprfmhxue69uhkummnw3ezummjv9hxwetsd9kxctnyv4mqzrthwden5te0dehhxtnvdakqz9thwden5te0wfjkccte9ekk7um5wgh8qatzqyv8wumn8ghj7am9d33k7mt99ehx7um5wgh8w6twv5yjhsls
This would go well with low data mode #2226
LLM OUTPUT
Nice — embedding Tor into Damus is a great way to give users stronger network-level privacy. Below I’ll give a practical, engineer-oriented plan you can act on now: two realistic integration approaches (external vs embedded Tor), step-by-step implementation notes, example Swift snippets (starting Tor and making HTTP/Web requests via Tor), important caveats (WebSocket handling, battery / App Store / license), and a short PR checklist you can use when opening a change on the Damus repo. I looked at the Damus repo and common iOS Tor tooling while preparing this. ([[GitHub](https://github.com/damus-io/damus?utm_source=chatgpt.com)][1])
---
# High-level options (pick one)
1. **External Tor (recommended for fastest rollout)**
Let users run a system Tor app (Orbot / OnionBrowser helper) and point Damus traffic to it (local SOCKS5). Pros: simpler, no native Tor binary to bundle, fewer build/export complications. Cons: requires user to install/start Orbot or similar. ([[Apple](https://apps.apple.com/us/app/orbot/id1609461599?utm_source=chatgpt.com)][2])
2. **Embed Tor in-app (full integration)**
Bundle a Tor client (Tor.framework / iCepa Tor.framework) inside Damus and run it in-process. Pros: single-app UX, more control. Cons: larger app size, more build complexity, crypto/export and App Store review implications, battery/CPU impact. Use Tor.framework or iCepa Tor.framework and run a Tor thread inside the app. ([[guide.onionmobile.dev](https://guide.onionmobile.dev/tor-on-ios/tor.framework-for-ios?utm_source=chatgpt.com)][3])
---
# Recommended approach for Damus (practical)
Start with **External Tor** for a fast MVP: add a “Use Tor” toggle that, when enabled, tries these fallbacks in order:
1. Detect an available local SOCKS5 proxy (127.0.0.1:9050 or 9150).
2. If found, route relay connections through the local SOCKS5 proxy.
3. If not found, show a short help screen instructing the user to install Orbot/Onion Browser or enable the bundled Tor option (if you later add in-app Tor).
This gets privacy benefits to users immediately and avoids bundling native Tor while you prototype in-app Tor.
---
# Embedding Tor in Damus — step-by-step
### A. Add Tor.framework (or iCepa Tor.framework) to the Xcode project
* Get a prebuilt Tor.framework (e.g., iCepa’s Tor.framework) or build Tor for iOS yourself (more work). Add the framework to the app target (Embed & Sign). ([[GitHub](https://github.com/iCepa/Tor.framework?utm_source=chatgpt.com)][4])
### B. Start Tor inside the app
Use the framework’s helper classes (examples use `TORThread`, `TORConfiguration`, `TORController`) to configure and start tor on a local SOCKS port (commonly 9050 or an ephemeral port). Example (pseudo-Swift based on Tor.framework patterns):
```swift
import Foundation
// if Tor.framework exposes TORThread/TORConfiguration as ObjC/Swift APIs:
let torConfig = TORConfiguration()
torConfig.socksPort = 9050
torConfig.dataDirectory = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first!
torConfig.logCallback = { level, msg in
print("tor: \(level) \(msg)")
}
let torThread = TORThread(configuration: torConfig)
torThread.start() // runs Tor in a background thread
// Optionally wait for tor to report 'BOOTSTRAP PROGRESS=100' via TORController
(Concrete API names vary with the Tor.framework build you choose — see the Tor Mobile guide.) ([guide.onionmobile.dev]3)
C. Route HTTP(s) and REST calls through Tor
For normal HTTP requests you can create a URLSession that uses a local SOCKS proxy wrapper — or use a helper package (e.g., SwiftTor) that creates a URLSession which routes via the in-app Tor SOCKS proxy. Example usage pattern (idea):
// Using a helper wrapper (conceptual)
let torSession = try SwiftTor.session(usingSocksProxyHost: "127.0.0.1", port: 9050)
let req = URLRequest(url: URL(string: "https://example-relay.example/ws")!)
let task = torSession.dataTask(with: req) { data, resp, err in
// parse relay HTTP responses (if any)
}
task.resume()
SwiftTor and similar projects show how to wrap URLSession to tunnel via the Tor SOCKS endpoint. ([GitHub]5)
D. Handling WebSocket / nostr relay connections
This is the trickiest bit: Nostr uses WebSockets (ws/wss). WebSocket libraries (URLSessionWebSocketTask / Starscream) typically open TCP connections directly; to route them via SOCKS5 you must either:
- Use a WebSocket client that supports connecting over a SOCKS5 proxy (some libraries support custom socket streams), or
- Open a raw socket to the SOCKS5 proxy, perform the SOCKS5 handshake to the remote relay host, then upgrade to WebSocket over that socket (implement the HTTP/WebSocket handshake on the socket stream), or
- Use a local HTTP proxy that supports CONNECT and let the WebSocket client use the HTTP proxy (but Tor is SOCKS5, not HTTP CONNECT; to use HTTP CONNECT you’d need an HTTP proxy in front of Tor, not desirable).
Practically:
- Evaluate Starscream (or your existing WebSocket lib) to see if it can accept a custom
InputStream/OutputStreamor a pre-connected socket. If so, create the socket via the Tor framework's socket APIs or via a SOCKS5 handshake, then hand that to the WebSocket client. - Alternatively, use Tor’s
libeventAPIs exposed by the framework to create a tor-proxied socket and do the WebSocket handshake over it. The Tor.framework / iCepa builds often includetor_sockethelpers for this. ([guide.onionmobile.dev]3)
E. Configuration, persistence, and UX
- Let users opt into “Route traffic through Tor” with explicit wording about performance/battery.
- Provide a connection status indicator (Bootstrap progress).
- Persist the Tor data directory safely in Application Support; rotate or expire it per user privacy settings.
- Add a diagnostics view to show bootstrap logs and Tor circuit info for debugging.
Example flow for implementing WebSocket-over-Tor (concrete plan)
-
Start embedded Tor on port 9050 and wait for bootstrap complete. (TORThread/TORController)
-
When opening a relay WebSocket:
- Create a TCP socket to 127.0.0.1:9050.
- Speak SOCKS5 to request a connection to
relay.example.com:443(or 80 for ws). - Once SOCKS replies “connected”, perform the WebSocket upgrade handshake over that socket (send
GET /path HTTP/1.1withUpgrade: websocket, correct headers). - Hand the opened streams to your WebSocket message parser or manage frames yourself.
-
Test thoroughly on device (not just simulator).
This avoids relying on system-level proxy settings and gives you programmatic control.
Important caveats & compliance
- App Store & entitlements: embedding Tor and using strong crypto (OpenSSL) can trigger export compliance and App Store review scrutiny. Document the use of crypto in your app submission and follow Apple’s guidance. Tor bundles include OpenSSL and other libraries — be prepared to fill export forms. ([GitHub]4)
- Licensing: Damus is GPL-3 (repo), Tor components have their own licenses — check compatibility and include license notices where required. ([GitHub]1)
- Battery / performance: Tor increases CPU, network usage and can keep background threads alive. Test on real devices and offer users an option to disable or limit Tor usage.
- Privacy limits: Tor hides network observers from learning destination endpoints, but app-level identifiers (e.g., pubkey used in Nostr, application metadata, or stored caches) can still deanonymize a user. Consider combining Tor with other privacy improvements (no persistent identifying metadata, rotate or zero local caches where possible).
- WebKit / Browser limitations: if Damus embeds a browser view, know Apple forces WebKit on iOS; Onion Browser notes some Tor Browser features can’t fully transfer to iOS browsers. This mainly affects webviews, not raw socket traffic. ([support.torproject.org]6)
Useful references (quick)
- Damus repo (source): Damus iOS. ([GitHub]1)
- Tor Mobile / Tor.framework usage guide (examples using
TORThread,TORConfiguration,TORController). ([guide.onionmobile.dev]3) - iCepa / Tor.framework prebuilt artifacts (practical prebuilt framework). ([GitHub]4)
- Tor.framework on CocoaPods / packaging notes. ([cocoapods.org]7)
- Swift helper showing example URLSession-over-Tor approach (SwiftTor). ([GitHub]5)
Quick code + PR checklist you can use when opening changes
Files to add / change
Sources/Networking/TorManager.swift— start/stop Tor, manage bootstrap state.Sources/Networking/TorURLSession.swift— helper to produceURLSessionproxied via Tor.Sources/Networking/WebSocketTorAdapter.swift— wrapper to open WebSocket streams over SOCKS5 socket.UI/Settings/TorSettingsView.swift— toggle + docs + diagnostics.- Update
Info.plist– App Transport Security exceptions only if needed for dev/testing (don’t ship unsafe exceptions).
PR description checklist
- [ ] Why: privacy benefit and opt-in UX.
- [ ] What: list of added files, frameworks (Tor.framework version), and how it’s bundled.
- [ ] How: short explanation of WebSocket approach (Socks5 socket + handshake).
- [ ] Privacy & compliance: license notices added; export compliance note for App Store.
- [ ] Tests: unit test for TorManager bootstrap detection; integration test that a test WebSocket server can be reached via Tor (CI only if permitted).
- [ ] QA notes: expected slower connection times, battery considerations, how to disable.
Final recommendations (practical next steps you can do immediately)
- Add an in-app “Use Tor (requires Orbot or bundled Tor)” toggle and implement SOCKS detection to fast-ship external Tor support. (fastest)
- Prototype embedded Tor locally using iCepa Tor.framework and a
TorManagerthat starts Tor and exposes bootstrap progress. Build a dev branch and wire in aURLSessionor test WebSocket adapter. (prototype) - Implement and test WebSocket-over-SOCKS approach for Nostr relays (this will need the most developer time and careful testing). (hard part)
If you want, I can generate:
- a concrete
TorManager.swiftstarter file (with realistic API calls based on the Tor.framework pattern), and - a
WebSocketTorAdapter.swiftsketch that performs a SOCKS5 handshake then performs a WebSocket upgrade over the socket (you can drop that into your Damus branch and iterate).
Which of those two files would you like me to produce now?