From f0ffc039d19143f33b1e7b418d5f8b2ed22c3d8d Mon Sep 17 00:00:00 2001 From: jwinterm Date: Tue, 7 Jul 2026 16:14:32 -0400 Subject: [PATCH] Prefer Arti for managed Tor browser setup --- ampbrowser/adapters.py | 13 ++++++++---- ampbrowser/launch.py | 5 ++++- ampbrowser/platforms.py | 6 +++--- docs/generated/adapters.md | 2 +- docs/generated/platform-capabilities.md | 6 +++--- docs/mobile-browser.md | 10 +++++++-- docs/transport-management.md | 6 +++--- tests/test_adapters.py | 19 ++++++++++++++++- tests/test_launch.py | 27 ++++++++++++++++++++++++- 9 files changed, 75 insertions(+), 19 deletions(-) diff --git a/ampbrowser/adapters.py b/ampbrowser/adapters.py index f75215a..b4c9de4 100644 --- a/ampbrowser/adapters.py +++ b/ampbrowser/adapters.py @@ -58,6 +58,11 @@ class TransportAdapter: steps.append(f"wait for {self.endpoint}") return tuple(steps) elif platform == "ios": + if self.name == "tor": + steps.append("start foreground-only iOS Arti client session") + steps.append("attach in-app onion networking bridge") + steps.append("wait for in-app Tor readiness") + return tuple(steps) steps.append(f"start foreground-only iOS session for {self.name}") steps.append(f"start foreground-only iOS {self.name} transport") steps.append(f"wait for {self.endpoint}") @@ -75,11 +80,11 @@ ADAPTERS: dict[str, TransportAdapter] = { endpoint="socks5://127.0.0.1:9050", adopt_check="SOCKS on 127.0.0.1:9050", managed_state=".ampb/transports/tor", - install_strategy="install Tor provider", - start_strategy="start managed Tor daemon", + install_strategy="install or bundle Arti/Tor provider", + start_strategy="start managed Arti SOCKS proxy", stop_policy="stop only AMPB-owned Tor daemon", - note="Tor SOCKS proxy", - commands=("tor",), + note="Tor via existing SOCKS proxy or managed Arti/Tor runtime", + commands=("arti", "tor"), connect_host="127.0.0.1", connect_port=9050, ), diff --git a/ampbrowser/launch.py b/ampbrowser/launch.py index 7624ede..7058e97 100644 --- a/ampbrowser/launch.py +++ b/ampbrowser/launch.py @@ -100,7 +100,10 @@ def _setup_steps(browse_plan: BrowsePlan) -> tuple[str, ...]: steps.extend(adapter.setup_steps(installed=True, platform=platform)) return tuple(steps) if platform == "ios" and not browse_plan.status.installed: - steps.append(f"enable foreground iOS {browse_plan.route.transport} session") + if browse_plan.route.transport == "tor": + steps.append("enable bundled iOS Arti Tor runtime") + else: + steps.append(f"enable foreground iOS {browse_plan.route.transport} session") steps.extend(adapter.setup_steps(installed=True, platform=platform)) return tuple(steps) steps.extend(adapter.setup_steps(installed=browse_plan.status.installed, platform=platform)) diff --git a/ampbrowser/platforms.py b/ampbrowser/platforms.py index 6ff32d3..d7cbe13 100644 --- a/ampbrowser/platforms.py +++ b/ampbrowser/platforms.py @@ -27,9 +27,9 @@ PLATFORM_CAPABILITIES = ( PlatformCapability("clearnet", "desktop", "ready", "ready", "unsupported", "unsupported", "Use system browser or isolated desktop profile."), PlatformCapability("clearnet", "android", "ready", "ready", "unsupported", "unsupported", "Use Android browser shell, WebView, or Custom Tabs."), PlatformCapability("clearnet", "ios", "ready", "ready", "unsupported", "unsupported", "Use iOS browser shell with platform web constraints."), - PlatformCapability("tor", "desktop", "ready", "ready", "planned", "planned", "Adopt local Tor or prompt before managed desktop daemon setup."), - PlatformCapability("tor", "android", "planned", "planned", "planned", "planned", "Use an app-owned foreground service or compatible installed Tor provider."), - PlatformCapability("tor", "ios", "foreground-only", "planned", "foreground-only", "constrained", "Plan for in-app foreground sessions; do not assume always-on background service."), + PlatformCapability("tor", "desktop", "ready", "ready", "planned", "planned", "Adopt local Tor or prompt before managed Arti SOCKS setup."), + PlatformCapability("tor", "android", "planned", "planned", "planned", "planned", "Use an app-owned foreground service; prefer Arti/Tor runtime integration where available."), + PlatformCapability("tor", "ios", "foreground-only", "planned", "foreground-only", "bundled", "Use foreground embedded Arti sessions; do not assume background service or global proxy."), PlatformCapability("i2p", "desktop", "ready", "ready", "planned", "planned", "Adopt local I2P proxy or prompt before managed desktop daemon setup."), PlatformCapability("i2p", "android", "planned", "planned", "planned", "planned", "Use an app-owned foreground service or compatible installed I2P provider."), PlatformCapability("i2p", "ios", "constrained", "planned", "foreground-only", "constrained", "Treat iOS I2P support as foreground-only until native adapter constraints are proven."), diff --git a/docs/generated/adapters.md b/docs/generated/adapters.md index b320be3..8a03c48 100644 --- a/docs/generated/adapters.md +++ b/docs/generated/adapters.md @@ -6,7 +6,7 @@ This file is generated from code. Do not edit it by hand. | Adapter | Endpoint | Adopt Check | Install Strategy | Start Strategy | Stop Policy | Note | | --- | --- | --- | --- | --- | --- | --- | -| `tor` | `socks5://127.0.0.1:9050` | SOCKS on 127.0.0.1:9050 | install Tor provider | start managed Tor daemon | stop only AMPB-owned Tor daemon | Tor SOCKS proxy | +| `tor` | `socks5://127.0.0.1:9050` | SOCKS on 127.0.0.1:9050 | install or bundle Arti/Tor provider | start managed Arti SOCKS proxy | stop only AMPB-owned Tor daemon | Tor via existing SOCKS proxy or managed Arti/Tor runtime | | `i2p` | `http://127.0.0.1:4444` | HTTP proxy on 127.0.0.1:4444 | install I2P provider | start managed I2P router | stop only AMPB-owned I2P router | I2P HTTP proxy | | `reticulum` | `rns://local` | RNS tools and configured interfaces | install Reticulum provider | start configured Reticulum interface | stop only AMPB-owned Reticulum process | Reticulum adapter is planned for resilient/private routing; it is not an anonymity layer | | `ipfs` | `http://127.0.0.1:8080` | HTTP gateway on 127.0.0.1:8080 | install IPFS/Kubo provider | start managed IPFS gateway | stop only AMPB-owned IPFS process | IPFS local gateway for content-addressed distribution; not an anonymity layer | diff --git a/docs/generated/platform-capabilities.md b/docs/generated/platform-capabilities.md index 5ba57a5..567f4dc 100644 --- a/docs/generated/platform-capabilities.md +++ b/docs/generated/platform-capabilities.md @@ -9,9 +9,9 @@ This file is generated from code. Do not edit it by hand. | `clearnet` | `desktop` | `ready` | `ready` | `unsupported` | `unsupported` | Use system browser or isolated desktop profile. | | `clearnet` | `android` | `ready` | `ready` | `unsupported` | `unsupported` | Use Android browser shell, WebView, or Custom Tabs. | | `clearnet` | `ios` | `ready` | `ready` | `unsupported` | `unsupported` | Use iOS browser shell with platform web constraints. | -| `tor` | `desktop` | `ready` | `ready` | `planned` | `planned` | Adopt local Tor or prompt before managed desktop daemon setup. | -| `tor` | `android` | `planned` | `planned` | `planned` | `planned` | Use an app-owned foreground service or compatible installed Tor provider. | -| `tor` | `ios` | `foreground-only` | `planned` | `foreground-only` | `constrained` | Plan for in-app foreground sessions; do not assume always-on background service. | +| `tor` | `desktop` | `ready` | `ready` | `planned` | `planned` | Adopt local Tor or prompt before managed Arti SOCKS setup. | +| `tor` | `android` | `planned` | `planned` | `planned` | `planned` | Use an app-owned foreground service; prefer Arti/Tor runtime integration where available. | +| `tor` | `ios` | `foreground-only` | `planned` | `foreground-only` | `bundled` | Use foreground embedded Arti sessions; do not assume background service or global proxy. | | `i2p` | `desktop` | `ready` | `ready` | `planned` | `planned` | Adopt local I2P proxy or prompt before managed desktop daemon setup. | | `i2p` | `android` | `planned` | `planned` | `planned` | `planned` | Use an app-owned foreground service or compatible installed I2P provider. | | `i2p` | `ios` | `constrained` | `planned` | `foreground-only` | `constrained` | Treat iOS I2P support as foreground-only until native adapter constraints are proven. | diff --git a/docs/mobile-browser.md b/docs/mobile-browser.md index 90897ca..deb1277 100644 --- a/docs/mobile-browser.md +++ b/docs/mobile-browser.md @@ -1,6 +1,6 @@ # Mobile Browser -> Status: draft | Updated 2026-07-06 | Applies to: AMPB mobile shells +> Status: draft | Updated 2026-07-07 | Applies to: AMPB mobile shells Mobile is a first-class AMPB target. The core router, policy model, consent flow, profile isolation, and fixture checks should stay shared across desktop, Android, and iOS. Native @@ -11,7 +11,8 @@ mobile shells should implement only the platform-specific browser and transport Android is the primary mobile target for full AMPB browsing. Tor, I2P, IPFS, and Reticulum adapters should run only after a URL selects that transport and the user approves setup. Managed adapters must use visible, stoppable foreground services and -app-owned state. +app-owned state. Tor should prefer an Arti/Tor runtime path when the native shell can +support it cleanly. ## iOS @@ -20,6 +21,11 @@ assume foreground-only sessions for managed transports unless a reviewed platfor capability proves otherwise. The iOS shell should still share routing, policy, consent, fixture, Gemini, IPFS gateway, and clearnet behavior with the rest of AMPB. +Tor on iOS should be modeled as an embedded Arti session, not a global daemon install. +The first vertical should prove foreground `.onion` browsing through an app-owned Arti +runtime and a browser networking bridge before promising background operation or a +system-wide proxy. + ## Capability Matrix Generated platform capability metadata lives in diff --git a/docs/transport-management.md b/docs/transport-management.md index 5b8b583..b9a80b6 100644 --- a/docs/transport-management.md +++ b/docs/transport-management.md @@ -1,6 +1,6 @@ # Transport Management -> Status: draft | Updated 2026-07-05 | Applies to: AMPB transport adapters +> Status: draft | Updated 2026-07-07 | Applies to: AMPB transport adapters Transport management follows one rule: use what is already healthy, and manage missing transports only after the user opens a URL that needs them and approves setup. @@ -15,7 +15,7 @@ Transport policy comes from `.ampb/config.toml` or an explicit `--config` path. 2. Adopt running transports that match the selected URL. 3. For missing transports, show a prompt that names what will be installed or started. 4. Resolve a configured binary and state directory only after consent. -5. Start the daemon with AMPB-owned state. +5. Start the daemon, library runtime, or foreground service with AMPB-owned state. 6. Wait for the health endpoint. 7. Launch the browser profile only after the transport is ready. 8. Stop only AMPB-owned daemons. @@ -30,7 +30,7 @@ leases, tunnels, and daemon logs in that tree are local state and must not be co ## Current Adapters -- Tor: detect SOCKS on `127.0.0.1:9050`; managed start planned. +- Tor: detect SOCKS on `127.0.0.1:9050`; managed setup prefers Arti/Tor runtime ownership. - I2P: detect HTTP proxy on `127.0.0.1:4444`; managed start planned. - IPFS: detect HTTP gateway on `127.0.0.1:8080`; managed gateway start planned. - Gemini: built-in route and render path; no daemon required for static browsing. diff --git a/tests/test_adapters.py b/tests/test_adapters.py index afa31f4..83c04aa 100644 --- a/tests/test_adapters.py +++ b/tests/test_adapters.py @@ -24,10 +24,27 @@ class AdaptersTest(unittest.TestCase): self.assertIsNotNone(adapter) self.assertEqual("stop only AMPB-owned Tor daemon", adapter.stop_policy) self.assertEqual( - ("install Tor provider", "start managed Tor daemon", "wait for socks5://127.0.0.1:9050"), + ( + "install or bundle Arti/Tor provider", + "start managed Arti SOCKS proxy", + "wait for socks5://127.0.0.1:9050", + ), adapter.setup_steps(installed=False, platform="desktop"), ) + def test_ios_tor_adapter_prefers_foreground_arti_session(self) -> None: + adapter = adapter_for("tor") + + self.assertIsNotNone(adapter) + self.assertEqual( + ( + "start foreground-only iOS Arti client session", + "attach in-app onion networking bridge", + "wait for in-app Tor readiness", + ), + adapter.setup_steps(installed=True, platform="ios"), + ) + def test_ipfs_adapter_declares_gateway_not_anonymity(self) -> None: adapter = adapter_for("ipfs") diff --git a/tests/test_launch.py b/tests/test_launch.py index d97a081..cc9c6b0 100644 --- a/tests/test_launch.py +++ b/tests/test_launch.py @@ -56,7 +56,7 @@ class PrepareOpenTest(unittest.TestCase): self.assertEqual("setup-approved", plan.status) self.assertTrue(plan.dry_run) self.assertTrue(plan.consent_granted) - self.assertEqual(("start managed Tor daemon", "wait for socks5://127.0.0.1:9050"), plan.setup_steps) + self.assertEqual(("start managed Arti SOCKS proxy", "wait for socks5://127.0.0.1:9050"), plan.setup_steps) def test_custom_state_dir_changes_profile_path(self) -> None: config = AppConfig(state_dir=".local/ampb", transport_modes={}) @@ -110,6 +110,31 @@ class PrepareOpenTest(unittest.TestCase): plan.setup_steps, ) + def test_ios_tor_setup_uses_bundled_arti_runtime(self) -> None: + status = TransportStatus( + transport="tor", + installed=False, + running=False, + endpoint="socks5://127.0.0.1:9050", + adoptable=False, + manage_supported=True, + note="Tor SOCKS proxy", + ) + + with patch("ampbrowser.plan.inspect_transport", return_value=status): + plan = prepare_open("http://example.onion/", platform="ios") + + self.assertEqual("consent-required", plan.status) + self.assertEqual( + ( + "enable bundled iOS Arti Tor runtime", + "start foreground-only iOS Arti client session", + "attach in-app onion networking bridge", + "wait for in-app Tor readiness", + ), + plan.setup_steps, + ) + if __name__ == "__main__": unittest.main()