feat(privacy): PrivacyRoutingFlow facade reading from live pref flows

Read-side seam that snapshots TorSettingsFlow + I2pSettingsFlow + the
preferredClearnetTransport StateFlow into a PrivacySettings, then delegates
to PrivacyRouter.route. Gives the I2P daemon manager and the eventual HTTP
routing rewrite a single stable API to consult instead of poking every flow
themselves.

DualHttpClientManager is not touched in this commit — that grows to tri-state
in the chunk where the I2P daemon lands a real proxy port.

AppModules:
- Constructs `privacyRouting` alongside roleBasedHttpClientBuilder
- Uses torPrefs.value + i2pPrefs.value + privacyPrefs (added last commit)

No callers wired yet — this is the seam those callers will move onto.
This commit is contained in:
Claude
2026-05-18 22:31:26 +00:00
parent 637bb4aba5
commit fdd954c2ed
2 changed files with 57 additions and 0 deletions
@@ -41,6 +41,7 @@ import com.vitorpamplona.amethyst.model.preferences.OtsSharedPreferences
import com.vitorpamplona.amethyst.model.preferences.PrivacySharedPreferences
import com.vitorpamplona.amethyst.model.preferences.TorSharedPreferences
import com.vitorpamplona.amethyst.model.preferences.UiSharedPreferences
import com.vitorpamplona.amethyst.model.privacyOptions.PrivacyRoutingFlow
import com.vitorpamplona.amethyst.model.privacyOptions.RoleBasedHttpClientBuilder
import com.vitorpamplona.amethyst.model.torState.AccountsTorStateConnector
import com.vitorpamplona.amethyst.model.torState.TorRelayState
@@ -271,6 +272,11 @@ class AppModules(
// Offers easy methods to know when connections are happening through Tor or not
val roleBasedHttpClientBuilder = RoleBasedHttpClientBuilder(okHttpClients, torPrefs.value)
// Read-side facade over PrivacyRouter consulted by upcoming HTTP/daemon work.
// Routes hidden services strictly to their matching daemon (Blocked when off)
// and clearnet to whichever transport the user picked as preferred.
val privacyRouting = PrivacyRoutingFlow(torPrefs.value, i2pPrefs.value, privacyPrefs)
val electrumXClient by lazy {
Log.d("AppModules", "ElectrumXClient Init")
val client =
@@ -0,0 +1,51 @@
/*
* Copyright (c) 2025 Vitor Pamplona
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
* Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.vitorpamplona.amethyst.model.privacyOptions
import com.vitorpamplona.amethyst.commons.privacy.FeatureRole
import com.vitorpamplona.amethyst.commons.privacy.PrivacyRoute
import com.vitorpamplona.amethyst.commons.privacy.PrivacyRouter
import com.vitorpamplona.amethyst.commons.privacy.PrivacySettings
import com.vitorpamplona.amethyst.model.preferences.PrivacySharedPreferences
import com.vitorpamplona.amethyst.ui.i2p.I2pSettingsFlow
import com.vitorpamplona.amethyst.ui.tor.TorSettingsFlow
// Read-side facade over PrivacyRouter that snapshot-reads from the three live
// pref flows (Tor, I2P, preferred clearnet transport). Exists so callers don't
// have to know how to assemble a PrivacySettings every time — and so the daemon
// work and HTTP routing rewrite that come next have one stable seam to consult.
class PrivacyRoutingFlow(
val tor: TorSettingsFlow,
val i2p: I2pSettingsFlow,
val privacy: PrivacySharedPreferences,
) {
fun routeFor(
role: FeatureRole,
url: String,
): PrivacyRoute = PrivacyRouter.route(url, role, snapshot())
fun snapshot(): PrivacySettings =
PrivacySettings(
tor = tor.toSettings(),
i2p = i2p.toSettings(),
preferredClearnetTransport = privacy.preferredClearnetTransport.value,
)
}