Add proxy subscription runtime support

Add daemon RPCs, Apple and GTK import flows, packet proxy runtime support, diagnostics, and BEPs for proxy subscription handling.

Redact subscription URL secrets from fetch errors before they reach logs or UI surfaces.
This commit is contained in:
JettChenT 2026-06-01 15:23:17 +08:00
parent 97c569fb35
commit d1638726ca
46 changed files with 15079 additions and 456 deletions

View file

@ -108,6 +108,83 @@ public struct Burrow_TailnetLoginStatusResponse: Sendable {
public init() {}
}
public struct Burrow_ProxySubscriptionImportRequest: Sendable {
public var url: String = ""
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
}
public struct Burrow_ProxySubscriptionNodePreview: Sendable {
public var ordinal: Int32 = 0
public var name: String = ""
public var `protocol`: String = ""
public var server: String = ""
public var port: Int32 = 0
public var warnings: [String] = []
public var runtimeSupported: Bool = false
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
}
public struct Burrow_ProxySubscriptionRejectedEntry: Sendable {
public var ordinal: Int32 = 0
public var reason: String = ""
public var scheme: String = ""
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
}
public struct Burrow_ProxySubscriptionPreviewResponse: Sendable {
public var suggestedName: String = ""
public var detectedFormat: String = ""
public var compatibleCount: Int32 = 0
public var rejectedCount: Int32 = 0
public var node: [Burrow_ProxySubscriptionNodePreview] = []
public var rejected: [Burrow_ProxySubscriptionRejectedEntry] = []
public var warnings: [String] = []
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
}
public struct Burrow_ProxySubscriptionApplyRequest: Sendable {
public var id: Int32 = 0
public var url: String = ""
public var name: String = ""
public var selectedOrdinal: Int32 = 0
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
}
public struct Burrow_ProxySubscriptionApplyResponse: Sendable {
public var id: Int32 = 0
public var importedCount: Int32 = 0
public var selectedOrdinal: Int32 = 0
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
}
public struct Burrow_ProxySubscriptionSelectRequest: Sendable {
public var id: Int32 = 0
public var selectedOrdinal: Int32 = 0
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
}
public struct Burrow_ProxySubscriptionSelectResponse: Sendable {
public var id: Int32 = 0
public var selectedOrdinal: Int32 = 0
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
}
public struct Burrow_TunnelPacket: Sendable {
public var payload = Data()
public var unknownFields = SwiftProtobuf.UnknownStorage()
@ -417,6 +494,239 @@ extension Burrow_TunnelPacket: SwiftProtobuf.Message, SwiftProtobuf._MessageImpl
}
}
extension Burrow_ProxySubscriptionImportRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let protoMessageName: String = "burrow.ProxySubscriptionImportRequest"
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "url")
]
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
switch fieldNumber {
case 1: try decoder.decodeSingularStringField(value: &self.url)
default: break
}
}
}
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if !self.url.isEmpty {
try visitor.visitSingularStringField(value: self.url, fieldNumber: 1)
}
try unknownFields.traverse(visitor: &visitor)
}
}
extension Burrow_ProxySubscriptionNodePreview: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let protoMessageName: String = "burrow.ProxySubscriptionNodePreview"
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "ordinal"),
2: .same(proto: "name"),
3: .same(proto: "protocol"),
4: .same(proto: "server"),
5: .same(proto: "port"),
6: .same(proto: "warnings"),
7: .standard(proto: "runtime_supported"),
]
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
switch fieldNumber {
case 1: try decoder.decodeSingularInt32Field(value: &self.ordinal)
case 2: try decoder.decodeSingularStringField(value: &self.name)
case 3: try decoder.decodeSingularStringField(value: &self.`protocol`)
case 4: try decoder.decodeSingularStringField(value: &self.server)
case 5: try decoder.decodeSingularInt32Field(value: &self.port)
case 6: try decoder.decodeRepeatedStringField(value: &self.warnings)
case 7: try decoder.decodeSingularBoolField(value: &self.runtimeSupported)
default: break
}
}
}
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if self.ordinal != 0 { try visitor.visitSingularInt32Field(value: self.ordinal, fieldNumber: 1) }
if !self.name.isEmpty { try visitor.visitSingularStringField(value: self.name, fieldNumber: 2) }
if !self.`protocol`.isEmpty { try visitor.visitSingularStringField(value: self.`protocol`, fieldNumber: 3) }
if !self.server.isEmpty { try visitor.visitSingularStringField(value: self.server, fieldNumber: 4) }
if self.port != 0 { try visitor.visitSingularInt32Field(value: self.port, fieldNumber: 5) }
if !self.warnings.isEmpty { try visitor.visitRepeatedStringField(value: self.warnings, fieldNumber: 6) }
if self.runtimeSupported { try visitor.visitSingularBoolField(value: self.runtimeSupported, fieldNumber: 7) }
try unknownFields.traverse(visitor: &visitor)
}
}
extension Burrow_ProxySubscriptionRejectedEntry: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let protoMessageName: String = "burrow.ProxySubscriptionRejectedEntry"
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "ordinal"),
2: .same(proto: "reason"),
3: .same(proto: "scheme"),
]
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
switch fieldNumber {
case 1: try decoder.decodeSingularInt32Field(value: &self.ordinal)
case 2: try decoder.decodeSingularStringField(value: &self.reason)
case 3: try decoder.decodeSingularStringField(value: &self.scheme)
default: break
}
}
}
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if self.ordinal != 0 { try visitor.visitSingularInt32Field(value: self.ordinal, fieldNumber: 1) }
if !self.reason.isEmpty { try visitor.visitSingularStringField(value: self.reason, fieldNumber: 2) }
if !self.scheme.isEmpty { try visitor.visitSingularStringField(value: self.scheme, fieldNumber: 3) }
try unknownFields.traverse(visitor: &visitor)
}
}
extension Burrow_ProxySubscriptionPreviewResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let protoMessageName: String = "burrow.ProxySubscriptionPreviewResponse"
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .standard(proto: "suggested_name"),
2: .standard(proto: "detected_format"),
3: .standard(proto: "compatible_count"),
4: .standard(proto: "rejected_count"),
5: .same(proto: "node"),
6: .same(proto: "rejected"),
7: .same(proto: "warnings"),
]
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
switch fieldNumber {
case 1: try decoder.decodeSingularStringField(value: &self.suggestedName)
case 2: try decoder.decodeSingularStringField(value: &self.detectedFormat)
case 3: try decoder.decodeSingularInt32Field(value: &self.compatibleCount)
case 4: try decoder.decodeSingularInt32Field(value: &self.rejectedCount)
case 5: try decoder.decodeRepeatedMessageField(value: &self.node)
case 6: try decoder.decodeRepeatedMessageField(value: &self.rejected)
case 7: try decoder.decodeRepeatedStringField(value: &self.warnings)
default: break
}
}
}
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if !self.suggestedName.isEmpty { try visitor.visitSingularStringField(value: self.suggestedName, fieldNumber: 1) }
if !self.detectedFormat.isEmpty { try visitor.visitSingularStringField(value: self.detectedFormat, fieldNumber: 2) }
if self.compatibleCount != 0 { try visitor.visitSingularInt32Field(value: self.compatibleCount, fieldNumber: 3) }
if self.rejectedCount != 0 { try visitor.visitSingularInt32Field(value: self.rejectedCount, fieldNumber: 4) }
if !self.node.isEmpty { try visitor.visitRepeatedMessageField(value: self.node, fieldNumber: 5) }
if !self.rejected.isEmpty { try visitor.visitRepeatedMessageField(value: self.rejected, fieldNumber: 6) }
if !self.warnings.isEmpty { try visitor.visitRepeatedStringField(value: self.warnings, fieldNumber: 7) }
try unknownFields.traverse(visitor: &visitor)
}
}
extension Burrow_ProxySubscriptionApplyRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let protoMessageName: String = "burrow.ProxySubscriptionApplyRequest"
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "id"),
2: .same(proto: "url"),
3: .same(proto: "name"),
4: .standard(proto: "selected_ordinal"),
]
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
switch fieldNumber {
case 1: try decoder.decodeSingularInt32Field(value: &self.id)
case 2: try decoder.decodeSingularStringField(value: &self.url)
case 3: try decoder.decodeSingularStringField(value: &self.name)
case 4: try decoder.decodeSingularInt32Field(value: &self.selectedOrdinal)
default: break
}
}
}
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if self.id != 0 { try visitor.visitSingularInt32Field(value: self.id, fieldNumber: 1) }
if !self.url.isEmpty { try visitor.visitSingularStringField(value: self.url, fieldNumber: 2) }
if !self.name.isEmpty { try visitor.visitSingularStringField(value: self.name, fieldNumber: 3) }
if self.selectedOrdinal != 0 { try visitor.visitSingularInt32Field(value: self.selectedOrdinal, fieldNumber: 4) }
try unknownFields.traverse(visitor: &visitor)
}
}
extension Burrow_ProxySubscriptionApplyResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let protoMessageName: String = "burrow.ProxySubscriptionApplyResponse"
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "id"),
2: .standard(proto: "imported_count"),
3: .standard(proto: "selected_ordinal"),
]
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
switch fieldNumber {
case 1: try decoder.decodeSingularInt32Field(value: &self.id)
case 2: try decoder.decodeSingularInt32Field(value: &self.importedCount)
case 3: try decoder.decodeSingularInt32Field(value: &self.selectedOrdinal)
default: break
}
}
}
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if self.id != 0 { try visitor.visitSingularInt32Field(value: self.id, fieldNumber: 1) }
if self.importedCount != 0 { try visitor.visitSingularInt32Field(value: self.importedCount, fieldNumber: 2) }
if self.selectedOrdinal != 0 { try visitor.visitSingularInt32Field(value: self.selectedOrdinal, fieldNumber: 3) }
try unknownFields.traverse(visitor: &visitor)
}
}
extension Burrow_ProxySubscriptionSelectRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let protoMessageName: String = "burrow.ProxySubscriptionSelectRequest"
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "id"),
2: .standard(proto: "selected_ordinal"),
]
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
switch fieldNumber {
case 1: try decoder.decodeSingularInt32Field(value: &self.id)
case 2: try decoder.decodeSingularInt32Field(value: &self.selectedOrdinal)
default: break
}
}
}
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if self.id != 0 { try visitor.visitSingularInt32Field(value: self.id, fieldNumber: 1) }
if self.selectedOrdinal != 0 { try visitor.visitSingularInt32Field(value: self.selectedOrdinal, fieldNumber: 2) }
try unknownFields.traverse(visitor: &visitor)
}
}
extension Burrow_ProxySubscriptionSelectResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let protoMessageName: String = "burrow.ProxySubscriptionSelectResponse"
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "id"),
2: .standard(proto: "selected_ordinal"),
]
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
switch fieldNumber {
case 1: try decoder.decodeSingularInt32Field(value: &self.id)
case 2: try decoder.decodeSingularInt32Field(value: &self.selectedOrdinal)
default: break
}
}
}
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if self.id != 0 { try visitor.visitSingularInt32Field(value: self.id, fieldNumber: 1) }
if self.selectedOrdinal != 0 { try visitor.visitSingularInt32Field(value: self.selectedOrdinal, fieldNumber: 2) }
try unknownFields.traverse(visitor: &visitor)
}
}
public struct TailnetClient: Client, GRPCClient {
public let channel: GRPCChannel
public var defaultCallOptions: CallOptions
@ -487,6 +797,52 @@ public struct TailnetClient: Client, GRPCClient {
}
}
public struct ProxySubscriptionClient: Client, GRPCClient {
public let channel: GRPCChannel
public var defaultCallOptions: CallOptions
public init(channel: any GRPCChannel) {
self.channel = channel
self.defaultCallOptions = .init()
}
public func previewImport(
_ request: Burrow_ProxySubscriptionImportRequest,
callOptions: CallOptions? = nil
) async throws -> Burrow_ProxySubscriptionPreviewResponse {
try await self.performAsyncUnaryCall(
path: "/burrow.ProxySubscriptions/PreviewImport",
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: []
)
}
public func applyImport(
_ request: Burrow_ProxySubscriptionApplyRequest,
callOptions: CallOptions? = nil
) async throws -> Burrow_ProxySubscriptionApplyResponse {
try await self.performAsyncUnaryCall(
path: "/burrow.ProxySubscriptions/ApplyImport",
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: []
)
}
public func selectNode(
_ request: Burrow_ProxySubscriptionSelectRequest,
callOptions: CallOptions? = nil
) async throws -> Burrow_ProxySubscriptionSelectResponse {
try await self.performAsyncUnaryCall(
path: "/burrow.ProxySubscriptions/SelectNode",
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: []
)
}
}
public struct TunnelPacketClient: Client, GRPCClient {
public let channel: GRPCChannel
public var defaultCallOptions: CallOptions

View file

@ -25,6 +25,8 @@ public enum Burrow_NetworkType: SwiftProtobuf.Enum, Swift.CaseIterable {
public typealias RawValue = Int
case wireGuard // = 0
case tailnet // = 1
case trojan // = 2
case proxySubscription // = 3
case UNRECOGNIZED(Int)
public init() {
@ -35,6 +37,8 @@ public enum Burrow_NetworkType: SwiftProtobuf.Enum, Swift.CaseIterable {
switch rawValue {
case 0: self = .wireGuard
case 1: self = .tailnet
case 2: self = .trojan
case 3: self = .proxySubscription
default: self = .UNRECOGNIZED(rawValue)
}
}
@ -43,6 +47,8 @@ public enum Burrow_NetworkType: SwiftProtobuf.Enum, Swift.CaseIterable {
switch self {
case .wireGuard: return 0
case .tailnet: return 1
case .trojan: return 2
case .proxySubscription: return 3
case .UNRECOGNIZED(let i): return i
}
}
@ -51,6 +57,8 @@ public enum Burrow_NetworkType: SwiftProtobuf.Enum, Swift.CaseIterable {
public static let allCases: [Burrow_NetworkType] = [
.wireGuard,
.tailnet,
.trojan,
.proxySubscription,
]
}
@ -217,6 +225,8 @@ public struct Burrow_TunnelConfigurationResponse: Sendable {
public var routes: [String] = []
public var excludedRoutes: [String] = []
public var dnsServers: [String] = []
public var searchDomains: [String] = []
@ -236,6 +246,8 @@ extension Burrow_NetworkType: SwiftProtobuf._ProtoNameProviding {
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
0: .same(proto: "WireGuard"),
1: .same(proto: "Tailnet"),
2: .same(proto: "Trojan"),
3: .same(proto: "ProxySubscription"),
]
}
@ -544,6 +556,7 @@ extension Burrow_TunnelConfigurationResponse: SwiftProtobuf.Message, SwiftProtob
4: .standard(proto: "dns_servers"),
5: .standard(proto: "search_domains"),
6: .standard(proto: "include_default_route"),
7: .standard(proto: "excluded_routes"),
]
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
@ -558,6 +571,7 @@ extension Burrow_TunnelConfigurationResponse: SwiftProtobuf.Message, SwiftProtob
case 4: try { try decoder.decodeRepeatedStringField(value: &self.dnsServers) }()
case 5: try { try decoder.decodeRepeatedStringField(value: &self.searchDomains) }()
case 6: try { try decoder.decodeSingularBoolField(value: &self.includeDefaultRoute) }()
case 7: try { try decoder.decodeRepeatedStringField(value: &self.excludedRoutes) }()
default: break
}
}
@ -573,6 +587,9 @@ extension Burrow_TunnelConfigurationResponse: SwiftProtobuf.Message, SwiftProtob
if !self.routes.isEmpty {
try visitor.visitRepeatedStringField(value: self.routes, fieldNumber: 3)
}
if !self.excludedRoutes.isEmpty {
try visitor.visitRepeatedStringField(value: self.excludedRoutes, fieldNumber: 7)
}
if !self.dnsServers.isEmpty {
try visitor.visitRepeatedStringField(value: self.dnsServers, fieldNumber: 4)
}
@ -589,6 +606,7 @@ extension Burrow_TunnelConfigurationResponse: SwiftProtobuf.Message, SwiftProtob
if lhs.addresses != rhs.addresses {return false}
if lhs.mtu != rhs.mtu {return false}
if lhs.routes != rhs.routes {return false}
if lhs.excludedRoutes != rhs.excludedRoutes {return false}
if lhs.dnsServers != rhs.dnsServers {return false}
if lhs.searchDomains != rhs.searchDomains {return false}
if lhs.includeDefaultRoute != rhs.includeDefaultRoute {return false}