feat(network): add relay connection policies

This commit is contained in:
2026-07-23 15:14:03 +02:00
parent cbace73908
commit a0bcc5dbff
40 changed files with 942 additions and 254 deletions

View File

@@ -21,7 +21,13 @@ enum FolderAccessStatus {
enum RelayPreferenceMode: String, Codable, CaseIterable, Sendable {
case automatic
case custom
case strictCustom = "custom"
case customWithDirectFallback = "custom-with-direct-fallback"
case localOnly = "local-only"
var usesCustomRelayURLs: Bool {
self == .strictCustom || self == .customWithDirectFallback
}
}
struct RelayConfiguration: Equatable, Codable, Sendable {
@@ -55,8 +61,8 @@ enum RelayConfigurationValidator {
relayURLs: [String],
retainedRelayURLs: [String] = []
) throws -> RelayConfiguration {
guard mode == .custom else {
return RelayConfiguration(mode: .automatic, relayURLs: retainedRelayURLs)
guard mode.usesCustomRelayURLs else {
return RelayConfiguration(mode: mode, relayURLs: retainedRelayURLs)
}
let relayEntries = relayURLs.enumerated().compactMap { index, value -> (Int, String)? in
@@ -104,7 +110,7 @@ enum RelayConfigurationValidator {
normalizedURLs.append(canonicalURL)
}
return RelayConfiguration(mode: .custom, relayURLs: normalizedURLs)
return RelayConfiguration(mode: mode, relayURLs: normalizedURLs)
}
}
@@ -177,10 +183,9 @@ final class AppPreferencesRepository: ObservableObject {
let data = defaults.data(forKey: Key.relayConfiguration),
let configuration = try? JSONDecoder().decode(RelayConfiguration.self, from: data)
else {
// A stored profile must never silently fall back to public relays. Keeping
// Custom selected with no URLs makes startup fail closed and lets Settings
// offer an explicit repair or reset to Automatic.
return RelayConfiguration(mode: .custom, relayURLs: [])
// A stored profile must never silently fall back to public relays. Strict
// custom with no URLs makes startup fail closed until Settings repairs it.
return RelayConfiguration(mode: .strictCustom, relayURLs: [])
}
return configuration
}

View File

@@ -40,11 +40,18 @@ struct NativeCoreBindingFactory: CoreBindingFactory {
switch networkConfiguration.mode {
case .automatic:
nativeConfiguration = defaultCoreNetworkConfig()
case .custom:
case .strictCustom:
nativeConfiguration = CoreNetworkConfig(
mode: .custom,
mode: .strictCustom,
relayUrls: networkConfiguration.relayURLs
)
case .customWithDirectFallback:
nativeConfiguration = CoreNetworkConfig(
mode: .customWithDirectFallback,
relayUrls: networkConfiguration.relayURLs
)
case .localOnly:
nativeConfiguration = CoreNetworkConfig(mode: .localOnly, relayUrls: [])
}
return try VnidropCore.initializeWithNetworkConfig(
appDataDir: appDataDir,