Compare commits

..

No commits in common. "1da00ecdf3126cc33bd718efd00a72deb39d610f" and "72b7f1467b18bd1bb376134ad13fa54e2f041c7b" have entirely different histories.

11 changed files with 80 additions and 1783 deletions

View file

@ -42,8 +42,8 @@
D0D4E5A62C8D9E65007F820A /* BurrowCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0D4E5312C8D996F007F820A /* BurrowCore.framework */; };
D0F4FAD32C8DC79C0068730A /* BurrowCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0D4E5312C8D996F007F820A /* BurrowCore.framework */; };
D0F7594E2C8DAB6B00126CF3 /* GRPC in Frameworks */ = {isa = PBXBuildFile; productRef = D078F7E02C8DA375008A8CEC /* GRPC */; };
D0FA10012D10200100112233 /* burrow.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FA10032D10200100112233 /* burrow.pb.swift */; };
D0FA10022D10200100112233 /* burrow.grpc.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FA10042D10200100112233 /* burrow.grpc.swift */; };
D0F759612C8DB24B00126CF3 /* grpc-swift-config.json in Sources */ = {isa = PBXBuildFile; fileRef = D0D4E4962C8D921A007F820A /* grpc-swift-config.json */; };
D0F759622C8DB24B00126CF3 /* swift-protobuf-config.json in Sources */ = {isa = PBXBuildFile; fileRef = D0D4E4972C8D921A007F820A /* swift-protobuf-config.json */; };
D0F7597E2C8DB30500126CF3 /* CGRPCZlib in Frameworks */ = {isa = PBXBuildFile; productRef = D0F7597D2C8DB30500126CF3 /* CGRPCZlib */; };
D0F7598D2C8DB3DA00126CF3 /* Client.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0D4E4992C8D921A007F820A /* Client.swift */; };
/* End PBXBuildFile section */
@ -154,6 +154,8 @@
D0BCC6032A09535900AD070D /* libburrow.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libburrow.a; sourceTree = BUILT_PRODUCTS_DIR; };
D0BF09582C8E6789000D8DEC /* UI.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = UI.xcconfig; sourceTree = "<group>"; };
D0D4E4952C8D921A007F820A /* burrow.proto */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.protobuf; path = burrow.proto; sourceTree = "<group>"; };
D0D4E4962C8D921A007F820A /* grpc-swift-config.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "grpc-swift-config.json"; sourceTree = "<group>"; };
D0D4E4972C8D921A007F820A /* swift-protobuf-config.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "swift-protobuf-config.json"; sourceTree = "<group>"; };
D0D4E4992C8D921A007F820A /* Client.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Client.swift; sourceTree = "<group>"; };
D0D4E49A2C8D921A007F820A /* Logging.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Logging.swift; sourceTree = "<group>"; };
D0D4E49E2C8D921A007F820A /* Network.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Network.swift; sourceTree = "<group>"; };
@ -177,8 +179,6 @@
D0D4E58E2C8D9D0A007F820A /* Constants.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Constants.h; sourceTree = "<group>"; };
D0D4E58F2C8D9D0A007F820A /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = "<group>"; };
D0D4E5902C8D9D0A007F820A /* module.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; path = module.modulemap; sourceTree = "<group>"; };
D0FA10032D10200100112233 /* burrow.pb.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Generated/burrow.pb.swift; sourceTree = "<group>"; };
D0FA10042D10200100112233 /* burrow.grpc.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Generated/burrow.grpc.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -317,8 +317,8 @@
isa = PBXGroup;
children = (
D0D4E4952C8D921A007F820A /* burrow.proto */,
D0FA10032D10200100112233 /* burrow.pb.swift */,
D0FA10042D10200100112233 /* burrow.grpc.swift */,
D0D4E4962C8D921A007F820A /* grpc-swift-config.json */,
D0D4E4972C8D921A007F820A /* swift-protobuf-config.json */,
);
path = Client;
sourceTree = "<group>";
@ -428,6 +428,8 @@
);
dependencies = (
D0F7598A2C8DB34200126CF3 /* PBXTargetDependency */,
D0F7595E2C8DB24400126CF3 /* PBXTargetDependency */,
D0F759602C8DB24400126CF3 /* PBXTargetDependency */,
);
name = Core;
packageProductDependencies = (
@ -615,8 +617,8 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
D0FA10012D10200100112233 /* burrow.pb.swift in Sources */,
D0FA10022D10200100112233 /* burrow.grpc.swift in Sources */,
D0F759612C8DB24B00126CF3 /* grpc-swift-config.json in Sources */,
D0F759622C8DB24B00126CF3 /* swift-protobuf-config.json in Sources */,
D0F7598D2C8DB3DA00126CF3 /* Client.swift in Sources */,
D0D4E56B2C8D9C2F007F820A /* Logging.swift in Sources */,
);
@ -687,6 +689,14 @@
target = D0D4E5302C8D996F007F820A /* Core */;
targetProxy = D0F4FAD12C8DC7960068730A /* PBXContainerItemProxy */;
};
D0F7595E2C8DB24400126CF3 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
productRef = D0F7595D2C8DB24400126CF3 /* GRPCSwiftPlugin */;
};
D0F759602C8DB24400126CF3 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
productRef = D0F7595F2C8DB24400126CF3 /* SwiftProtobufPlugin */;
};
D0F7598A2C8DB34200126CF3 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
productRef = D0F759892C8DB34200126CF3 /* GRPC */;
@ -911,6 +921,16 @@
package = D0B1D10E2C436152004B7823 /* XCRemoteSwiftPackageReference "swift-async-algorithms" */;
productName = AsyncAlgorithms;
};
D0F7595D2C8DB24400126CF3 /* GRPCSwiftPlugin */ = {
isa = XCSwiftPackageProductDependency;
package = D0D4E4822C8D8EF6007F820A /* XCRemoteSwiftPackageReference "grpc-swift" */;
productName = "plugin:GRPCSwiftPlugin";
};
D0F7595F2C8DB24400126CF3 /* SwiftProtobufPlugin */ = {
isa = XCSwiftPackageProductDependency;
package = D0D4E4852C8D8F29007F820A /* XCRemoteSwiftPackageReference "swift-protobuf" */;
productName = "plugin:SwiftProtobufPlugin";
};
D0F7597D2C8DB30500126CF3 /* CGRPCZlib */ = {
isa = XCSwiftPackageProductDependency;
package = D0D4E4822C8D8EF6007F820A /* XCRemoteSwiftPackageReference "grpc-swift" */;

View file

@ -1,761 +0,0 @@
//
// DO NOT EDIT.
// swift-format-ignore-file
//
// Generated by the protocol buffer compiler.
// Source: burrow.proto
//
import GRPC
import NIO
import NIOConcurrencyHelpers
import SwiftProtobuf
/// Usage: instantiate `Burrow_TunnelClient`, then call methods of this protocol to make API calls.
public protocol Burrow_TunnelClientProtocol: GRPCClient {
var serviceName: String { get }
var interceptors: Burrow_TunnelClientInterceptorFactoryProtocol? { get }
func tunnelConfiguration(
_ request: Burrow_Empty,
callOptions: CallOptions?,
handler: @escaping (Burrow_TunnelConfigurationResponse) -> Void
) -> ServerStreamingCall<Burrow_Empty, Burrow_TunnelConfigurationResponse>
func tunnelStart(
_ request: Burrow_Empty,
callOptions: CallOptions?
) -> UnaryCall<Burrow_Empty, Burrow_Empty>
func tunnelStop(
_ request: Burrow_Empty,
callOptions: CallOptions?
) -> UnaryCall<Burrow_Empty, Burrow_Empty>
func tunnelStatus(
_ request: Burrow_Empty,
callOptions: CallOptions?,
handler: @escaping (Burrow_TunnelStatusResponse) -> Void
) -> ServerStreamingCall<Burrow_Empty, Burrow_TunnelStatusResponse>
}
extension Burrow_TunnelClientProtocol {
public var serviceName: String {
return "burrow.Tunnel"
}
/// Server streaming call to TunnelConfiguration
///
/// - Parameters:
/// - request: Request to send to TunnelConfiguration.
/// - callOptions: Call options.
/// - handler: A closure called when each response is received from the server.
/// - Returns: A `ServerStreamingCall` with futures for the metadata and status.
public func tunnelConfiguration(
_ request: Burrow_Empty,
callOptions: CallOptions? = nil,
handler: @escaping (Burrow_TunnelConfigurationResponse) -> Void
) -> ServerStreamingCall<Burrow_Empty, Burrow_TunnelConfigurationResponse> {
return self.makeServerStreamingCall(
path: Burrow_TunnelClientMetadata.Methods.tunnelConfiguration.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeTunnelConfigurationInterceptors() ?? [],
handler: handler
)
}
/// Unary call to TunnelStart
///
/// - Parameters:
/// - request: Request to send to TunnelStart.
/// - callOptions: Call options.
/// - Returns: A `UnaryCall` with futures for the metadata, status and response.
public func tunnelStart(
_ request: Burrow_Empty,
callOptions: CallOptions? = nil
) -> UnaryCall<Burrow_Empty, Burrow_Empty> {
return self.makeUnaryCall(
path: Burrow_TunnelClientMetadata.Methods.tunnelStart.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeTunnelStartInterceptors() ?? []
)
}
/// Unary call to TunnelStop
///
/// - Parameters:
/// - request: Request to send to TunnelStop.
/// - callOptions: Call options.
/// - Returns: A `UnaryCall` with futures for the metadata, status and response.
public func tunnelStop(
_ request: Burrow_Empty,
callOptions: CallOptions? = nil
) -> UnaryCall<Burrow_Empty, Burrow_Empty> {
return self.makeUnaryCall(
path: Burrow_TunnelClientMetadata.Methods.tunnelStop.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeTunnelStopInterceptors() ?? []
)
}
/// Server streaming call to TunnelStatus
///
/// - Parameters:
/// - request: Request to send to TunnelStatus.
/// - callOptions: Call options.
/// - handler: A closure called when each response is received from the server.
/// - Returns: A `ServerStreamingCall` with futures for the metadata and status.
public func tunnelStatus(
_ request: Burrow_Empty,
callOptions: CallOptions? = nil,
handler: @escaping (Burrow_TunnelStatusResponse) -> Void
) -> ServerStreamingCall<Burrow_Empty, Burrow_TunnelStatusResponse> {
return self.makeServerStreamingCall(
path: Burrow_TunnelClientMetadata.Methods.tunnelStatus.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeTunnelStatusInterceptors() ?? [],
handler: handler
)
}
}
@available(*, deprecated)
extension Burrow_TunnelClient: @unchecked Sendable {}
@available(*, deprecated, renamed: "Burrow_TunnelNIOClient")
public final class Burrow_TunnelClient: Burrow_TunnelClientProtocol {
private let lock = Lock()
private var _defaultCallOptions: CallOptions
private var _interceptors: Burrow_TunnelClientInterceptorFactoryProtocol?
public let channel: GRPCChannel
public var defaultCallOptions: CallOptions {
get { self.lock.withLock { return self._defaultCallOptions } }
set { self.lock.withLockVoid { self._defaultCallOptions = newValue } }
}
public var interceptors: Burrow_TunnelClientInterceptorFactoryProtocol? {
get { self.lock.withLock { return self._interceptors } }
set { self.lock.withLockVoid { self._interceptors = newValue } }
}
/// Creates a client for the burrow.Tunnel service.
///
/// - Parameters:
/// - channel: `GRPCChannel` to the service host.
/// - defaultCallOptions: Options to use for each service call if the user doesn't provide them.
/// - interceptors: A factory providing interceptors for each RPC.
public init(
channel: GRPCChannel,
defaultCallOptions: CallOptions = CallOptions(),
interceptors: Burrow_TunnelClientInterceptorFactoryProtocol? = nil
) {
self.channel = channel
self._defaultCallOptions = defaultCallOptions
self._interceptors = interceptors
}
}
public struct Burrow_TunnelNIOClient: Burrow_TunnelClientProtocol {
public var channel: GRPCChannel
public var defaultCallOptions: CallOptions
public var interceptors: Burrow_TunnelClientInterceptorFactoryProtocol?
/// Creates a client for the burrow.Tunnel service.
///
/// - Parameters:
/// - channel: `GRPCChannel` to the service host.
/// - defaultCallOptions: Options to use for each service call if the user doesn't provide them.
/// - interceptors: A factory providing interceptors for each RPC.
public init(
channel: GRPCChannel,
defaultCallOptions: CallOptions = CallOptions(),
interceptors: Burrow_TunnelClientInterceptorFactoryProtocol? = nil
) {
self.channel = channel
self.defaultCallOptions = defaultCallOptions
self.interceptors = interceptors
}
}
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
public protocol Burrow_TunnelAsyncClientProtocol: GRPCClient {
static var serviceDescriptor: GRPCServiceDescriptor { get }
var interceptors: Burrow_TunnelClientInterceptorFactoryProtocol? { get }
func makeTunnelConfigurationCall(
_ request: Burrow_Empty,
callOptions: CallOptions?
) -> GRPCAsyncServerStreamingCall<Burrow_Empty, Burrow_TunnelConfigurationResponse>
func makeTunnelStartCall(
_ request: Burrow_Empty,
callOptions: CallOptions?
) -> GRPCAsyncUnaryCall<Burrow_Empty, Burrow_Empty>
func makeTunnelStopCall(
_ request: Burrow_Empty,
callOptions: CallOptions?
) -> GRPCAsyncUnaryCall<Burrow_Empty, Burrow_Empty>
func makeTunnelStatusCall(
_ request: Burrow_Empty,
callOptions: CallOptions?
) -> GRPCAsyncServerStreamingCall<Burrow_Empty, Burrow_TunnelStatusResponse>
}
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
extension Burrow_TunnelAsyncClientProtocol {
public static var serviceDescriptor: GRPCServiceDescriptor {
return Burrow_TunnelClientMetadata.serviceDescriptor
}
public var interceptors: Burrow_TunnelClientInterceptorFactoryProtocol? {
return nil
}
public func makeTunnelConfigurationCall(
_ request: Burrow_Empty,
callOptions: CallOptions? = nil
) -> GRPCAsyncServerStreamingCall<Burrow_Empty, Burrow_TunnelConfigurationResponse> {
return self.makeAsyncServerStreamingCall(
path: Burrow_TunnelClientMetadata.Methods.tunnelConfiguration.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeTunnelConfigurationInterceptors() ?? []
)
}
public func makeTunnelStartCall(
_ request: Burrow_Empty,
callOptions: CallOptions? = nil
) -> GRPCAsyncUnaryCall<Burrow_Empty, Burrow_Empty> {
return self.makeAsyncUnaryCall(
path: Burrow_TunnelClientMetadata.Methods.tunnelStart.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeTunnelStartInterceptors() ?? []
)
}
public func makeTunnelStopCall(
_ request: Burrow_Empty,
callOptions: CallOptions? = nil
) -> GRPCAsyncUnaryCall<Burrow_Empty, Burrow_Empty> {
return self.makeAsyncUnaryCall(
path: Burrow_TunnelClientMetadata.Methods.tunnelStop.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeTunnelStopInterceptors() ?? []
)
}
public func makeTunnelStatusCall(
_ request: Burrow_Empty,
callOptions: CallOptions? = nil
) -> GRPCAsyncServerStreamingCall<Burrow_Empty, Burrow_TunnelStatusResponse> {
return self.makeAsyncServerStreamingCall(
path: Burrow_TunnelClientMetadata.Methods.tunnelStatus.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeTunnelStatusInterceptors() ?? []
)
}
}
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
extension Burrow_TunnelAsyncClientProtocol {
public func tunnelConfiguration(
_ request: Burrow_Empty,
callOptions: CallOptions? = nil
) -> GRPCAsyncResponseStream<Burrow_TunnelConfigurationResponse> {
return self.performAsyncServerStreamingCall(
path: Burrow_TunnelClientMetadata.Methods.tunnelConfiguration.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeTunnelConfigurationInterceptors() ?? []
)
}
public func tunnelStart(
_ request: Burrow_Empty,
callOptions: CallOptions? = nil
) async throws -> Burrow_Empty {
return try await self.performAsyncUnaryCall(
path: Burrow_TunnelClientMetadata.Methods.tunnelStart.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeTunnelStartInterceptors() ?? []
)
}
public func tunnelStop(
_ request: Burrow_Empty,
callOptions: CallOptions? = nil
) async throws -> Burrow_Empty {
return try await self.performAsyncUnaryCall(
path: Burrow_TunnelClientMetadata.Methods.tunnelStop.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeTunnelStopInterceptors() ?? []
)
}
public func tunnelStatus(
_ request: Burrow_Empty,
callOptions: CallOptions? = nil
) -> GRPCAsyncResponseStream<Burrow_TunnelStatusResponse> {
return self.performAsyncServerStreamingCall(
path: Burrow_TunnelClientMetadata.Methods.tunnelStatus.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeTunnelStatusInterceptors() ?? []
)
}
}
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
public struct Burrow_TunnelAsyncClient: Burrow_TunnelAsyncClientProtocol {
public var channel: GRPCChannel
public var defaultCallOptions: CallOptions
public var interceptors: Burrow_TunnelClientInterceptorFactoryProtocol?
public init(
channel: GRPCChannel,
defaultCallOptions: CallOptions = CallOptions(),
interceptors: Burrow_TunnelClientInterceptorFactoryProtocol? = nil
) {
self.channel = channel
self.defaultCallOptions = defaultCallOptions
self.interceptors = interceptors
}
}
public protocol Burrow_TunnelClientInterceptorFactoryProtocol: Sendable {
/// - Returns: Interceptors to use when invoking 'tunnelConfiguration'.
func makeTunnelConfigurationInterceptors() -> [ClientInterceptor<Burrow_Empty, Burrow_TunnelConfigurationResponse>]
/// - Returns: Interceptors to use when invoking 'tunnelStart'.
func makeTunnelStartInterceptors() -> [ClientInterceptor<Burrow_Empty, Burrow_Empty>]
/// - Returns: Interceptors to use when invoking 'tunnelStop'.
func makeTunnelStopInterceptors() -> [ClientInterceptor<Burrow_Empty, Burrow_Empty>]
/// - Returns: Interceptors to use when invoking 'tunnelStatus'.
func makeTunnelStatusInterceptors() -> [ClientInterceptor<Burrow_Empty, Burrow_TunnelStatusResponse>]
}
public enum Burrow_TunnelClientMetadata {
public static let serviceDescriptor = GRPCServiceDescriptor(
name: "Tunnel",
fullName: "burrow.Tunnel",
methods: [
Burrow_TunnelClientMetadata.Methods.tunnelConfiguration,
Burrow_TunnelClientMetadata.Methods.tunnelStart,
Burrow_TunnelClientMetadata.Methods.tunnelStop,
Burrow_TunnelClientMetadata.Methods.tunnelStatus,
]
)
public enum Methods {
public static let tunnelConfiguration = GRPCMethodDescriptor(
name: "TunnelConfiguration",
path: "/burrow.Tunnel/TunnelConfiguration",
type: GRPCCallType.serverStreaming
)
public static let tunnelStart = GRPCMethodDescriptor(
name: "TunnelStart",
path: "/burrow.Tunnel/TunnelStart",
type: GRPCCallType.unary
)
public static let tunnelStop = GRPCMethodDescriptor(
name: "TunnelStop",
path: "/burrow.Tunnel/TunnelStop",
type: GRPCCallType.unary
)
public static let tunnelStatus = GRPCMethodDescriptor(
name: "TunnelStatus",
path: "/burrow.Tunnel/TunnelStatus",
type: GRPCCallType.serverStreaming
)
}
}
/// Usage: instantiate `Burrow_NetworksClient`, then call methods of this protocol to make API calls.
public protocol Burrow_NetworksClientProtocol: GRPCClient {
var serviceName: String { get }
var interceptors: Burrow_NetworksClientInterceptorFactoryProtocol? { get }
func networkAdd(
_ request: Burrow_Network,
callOptions: CallOptions?
) -> UnaryCall<Burrow_Network, Burrow_Empty>
func networkList(
_ request: Burrow_Empty,
callOptions: CallOptions?,
handler: @escaping (Burrow_NetworkListResponse) -> Void
) -> ServerStreamingCall<Burrow_Empty, Burrow_NetworkListResponse>
func networkReorder(
_ request: Burrow_NetworkReorderRequest,
callOptions: CallOptions?
) -> UnaryCall<Burrow_NetworkReorderRequest, Burrow_Empty>
func networkDelete(
_ request: Burrow_NetworkDeleteRequest,
callOptions: CallOptions?
) -> UnaryCall<Burrow_NetworkDeleteRequest, Burrow_Empty>
}
extension Burrow_NetworksClientProtocol {
public var serviceName: String {
return "burrow.Networks"
}
/// Unary call to NetworkAdd
///
/// - Parameters:
/// - request: Request to send to NetworkAdd.
/// - callOptions: Call options.
/// - Returns: A `UnaryCall` with futures for the metadata, status and response.
public func networkAdd(
_ request: Burrow_Network,
callOptions: CallOptions? = nil
) -> UnaryCall<Burrow_Network, Burrow_Empty> {
return self.makeUnaryCall(
path: Burrow_NetworksClientMetadata.Methods.networkAdd.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeNetworkAddInterceptors() ?? []
)
}
/// Server streaming call to NetworkList
///
/// - Parameters:
/// - request: Request to send to NetworkList.
/// - callOptions: Call options.
/// - handler: A closure called when each response is received from the server.
/// - Returns: A `ServerStreamingCall` with futures for the metadata and status.
public func networkList(
_ request: Burrow_Empty,
callOptions: CallOptions? = nil,
handler: @escaping (Burrow_NetworkListResponse) -> Void
) -> ServerStreamingCall<Burrow_Empty, Burrow_NetworkListResponse> {
return self.makeServerStreamingCall(
path: Burrow_NetworksClientMetadata.Methods.networkList.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeNetworkListInterceptors() ?? [],
handler: handler
)
}
/// Unary call to NetworkReorder
///
/// - Parameters:
/// - request: Request to send to NetworkReorder.
/// - callOptions: Call options.
/// - Returns: A `UnaryCall` with futures for the metadata, status and response.
public func networkReorder(
_ request: Burrow_NetworkReorderRequest,
callOptions: CallOptions? = nil
) -> UnaryCall<Burrow_NetworkReorderRequest, Burrow_Empty> {
return self.makeUnaryCall(
path: Burrow_NetworksClientMetadata.Methods.networkReorder.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeNetworkReorderInterceptors() ?? []
)
}
/// Unary call to NetworkDelete
///
/// - Parameters:
/// - request: Request to send to NetworkDelete.
/// - callOptions: Call options.
/// - Returns: A `UnaryCall` with futures for the metadata, status and response.
public func networkDelete(
_ request: Burrow_NetworkDeleteRequest,
callOptions: CallOptions? = nil
) -> UnaryCall<Burrow_NetworkDeleteRequest, Burrow_Empty> {
return self.makeUnaryCall(
path: Burrow_NetworksClientMetadata.Methods.networkDelete.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeNetworkDeleteInterceptors() ?? []
)
}
}
@available(*, deprecated)
extension Burrow_NetworksClient: @unchecked Sendable {}
@available(*, deprecated, renamed: "Burrow_NetworksNIOClient")
public final class Burrow_NetworksClient: Burrow_NetworksClientProtocol {
private let lock = Lock()
private var _defaultCallOptions: CallOptions
private var _interceptors: Burrow_NetworksClientInterceptorFactoryProtocol?
public let channel: GRPCChannel
public var defaultCallOptions: CallOptions {
get { self.lock.withLock { return self._defaultCallOptions } }
set { self.lock.withLockVoid { self._defaultCallOptions = newValue } }
}
public var interceptors: Burrow_NetworksClientInterceptorFactoryProtocol? {
get { self.lock.withLock { return self._interceptors } }
set { self.lock.withLockVoid { self._interceptors = newValue } }
}
/// Creates a client for the burrow.Networks service.
///
/// - Parameters:
/// - channel: `GRPCChannel` to the service host.
/// - defaultCallOptions: Options to use for each service call if the user doesn't provide them.
/// - interceptors: A factory providing interceptors for each RPC.
public init(
channel: GRPCChannel,
defaultCallOptions: CallOptions = CallOptions(),
interceptors: Burrow_NetworksClientInterceptorFactoryProtocol? = nil
) {
self.channel = channel
self._defaultCallOptions = defaultCallOptions
self._interceptors = interceptors
}
}
public struct Burrow_NetworksNIOClient: Burrow_NetworksClientProtocol {
public var channel: GRPCChannel
public var defaultCallOptions: CallOptions
public var interceptors: Burrow_NetworksClientInterceptorFactoryProtocol?
/// Creates a client for the burrow.Networks service.
///
/// - Parameters:
/// - channel: `GRPCChannel` to the service host.
/// - defaultCallOptions: Options to use for each service call if the user doesn't provide them.
/// - interceptors: A factory providing interceptors for each RPC.
public init(
channel: GRPCChannel,
defaultCallOptions: CallOptions = CallOptions(),
interceptors: Burrow_NetworksClientInterceptorFactoryProtocol? = nil
) {
self.channel = channel
self.defaultCallOptions = defaultCallOptions
self.interceptors = interceptors
}
}
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
public protocol Burrow_NetworksAsyncClientProtocol: GRPCClient {
static var serviceDescriptor: GRPCServiceDescriptor { get }
var interceptors: Burrow_NetworksClientInterceptorFactoryProtocol? { get }
func makeNetworkAddCall(
_ request: Burrow_Network,
callOptions: CallOptions?
) -> GRPCAsyncUnaryCall<Burrow_Network, Burrow_Empty>
func makeNetworkListCall(
_ request: Burrow_Empty,
callOptions: CallOptions?
) -> GRPCAsyncServerStreamingCall<Burrow_Empty, Burrow_NetworkListResponse>
func makeNetworkReorderCall(
_ request: Burrow_NetworkReorderRequest,
callOptions: CallOptions?
) -> GRPCAsyncUnaryCall<Burrow_NetworkReorderRequest, Burrow_Empty>
func makeNetworkDeleteCall(
_ request: Burrow_NetworkDeleteRequest,
callOptions: CallOptions?
) -> GRPCAsyncUnaryCall<Burrow_NetworkDeleteRequest, Burrow_Empty>
}
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
extension Burrow_NetworksAsyncClientProtocol {
public static var serviceDescriptor: GRPCServiceDescriptor {
return Burrow_NetworksClientMetadata.serviceDescriptor
}
public var interceptors: Burrow_NetworksClientInterceptorFactoryProtocol? {
return nil
}
public func makeNetworkAddCall(
_ request: Burrow_Network,
callOptions: CallOptions? = nil
) -> GRPCAsyncUnaryCall<Burrow_Network, Burrow_Empty> {
return self.makeAsyncUnaryCall(
path: Burrow_NetworksClientMetadata.Methods.networkAdd.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeNetworkAddInterceptors() ?? []
)
}
public func makeNetworkListCall(
_ request: Burrow_Empty,
callOptions: CallOptions? = nil
) -> GRPCAsyncServerStreamingCall<Burrow_Empty, Burrow_NetworkListResponse> {
return self.makeAsyncServerStreamingCall(
path: Burrow_NetworksClientMetadata.Methods.networkList.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeNetworkListInterceptors() ?? []
)
}
public func makeNetworkReorderCall(
_ request: Burrow_NetworkReorderRequest,
callOptions: CallOptions? = nil
) -> GRPCAsyncUnaryCall<Burrow_NetworkReorderRequest, Burrow_Empty> {
return self.makeAsyncUnaryCall(
path: Burrow_NetworksClientMetadata.Methods.networkReorder.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeNetworkReorderInterceptors() ?? []
)
}
public func makeNetworkDeleteCall(
_ request: Burrow_NetworkDeleteRequest,
callOptions: CallOptions? = nil
) -> GRPCAsyncUnaryCall<Burrow_NetworkDeleteRequest, Burrow_Empty> {
return self.makeAsyncUnaryCall(
path: Burrow_NetworksClientMetadata.Methods.networkDelete.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeNetworkDeleteInterceptors() ?? []
)
}
}
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
extension Burrow_NetworksAsyncClientProtocol {
public func networkAdd(
_ request: Burrow_Network,
callOptions: CallOptions? = nil
) async throws -> Burrow_Empty {
return try await self.performAsyncUnaryCall(
path: Burrow_NetworksClientMetadata.Methods.networkAdd.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeNetworkAddInterceptors() ?? []
)
}
public func networkList(
_ request: Burrow_Empty,
callOptions: CallOptions? = nil
) -> GRPCAsyncResponseStream<Burrow_NetworkListResponse> {
return self.performAsyncServerStreamingCall(
path: Burrow_NetworksClientMetadata.Methods.networkList.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeNetworkListInterceptors() ?? []
)
}
public func networkReorder(
_ request: Burrow_NetworkReorderRequest,
callOptions: CallOptions? = nil
) async throws -> Burrow_Empty {
return try await self.performAsyncUnaryCall(
path: Burrow_NetworksClientMetadata.Methods.networkReorder.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeNetworkReorderInterceptors() ?? []
)
}
public func networkDelete(
_ request: Burrow_NetworkDeleteRequest,
callOptions: CallOptions? = nil
) async throws -> Burrow_Empty {
return try await self.performAsyncUnaryCall(
path: Burrow_NetworksClientMetadata.Methods.networkDelete.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeNetworkDeleteInterceptors() ?? []
)
}
}
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
public struct Burrow_NetworksAsyncClient: Burrow_NetworksAsyncClientProtocol {
public var channel: GRPCChannel
public var defaultCallOptions: CallOptions
public var interceptors: Burrow_NetworksClientInterceptorFactoryProtocol?
public init(
channel: GRPCChannel,
defaultCallOptions: CallOptions = CallOptions(),
interceptors: Burrow_NetworksClientInterceptorFactoryProtocol? = nil
) {
self.channel = channel
self.defaultCallOptions = defaultCallOptions
self.interceptors = interceptors
}
}
public protocol Burrow_NetworksClientInterceptorFactoryProtocol: Sendable {
/// - Returns: Interceptors to use when invoking 'networkAdd'.
func makeNetworkAddInterceptors() -> [ClientInterceptor<Burrow_Network, Burrow_Empty>]
/// - Returns: Interceptors to use when invoking 'networkList'.
func makeNetworkListInterceptors() -> [ClientInterceptor<Burrow_Empty, Burrow_NetworkListResponse>]
/// - Returns: Interceptors to use when invoking 'networkReorder'.
func makeNetworkReorderInterceptors() -> [ClientInterceptor<Burrow_NetworkReorderRequest, Burrow_Empty>]
/// - Returns: Interceptors to use when invoking 'networkDelete'.
func makeNetworkDeleteInterceptors() -> [ClientInterceptor<Burrow_NetworkDeleteRequest, Burrow_Empty>]
}
public enum Burrow_NetworksClientMetadata {
public static let serviceDescriptor = GRPCServiceDescriptor(
name: "Networks",
fullName: "burrow.Networks",
methods: [
Burrow_NetworksClientMetadata.Methods.networkAdd,
Burrow_NetworksClientMetadata.Methods.networkList,
Burrow_NetworksClientMetadata.Methods.networkReorder,
Burrow_NetworksClientMetadata.Methods.networkDelete,
]
)
public enum Methods {
public static let networkAdd = GRPCMethodDescriptor(
name: "NetworkAdd",
path: "/burrow.Networks/NetworkAdd",
type: GRPCCallType.unary
)
public static let networkList = GRPCMethodDescriptor(
name: "NetworkList",
path: "/burrow.Networks/NetworkList",
type: GRPCCallType.serverStreaming
)
public static let networkReorder = GRPCMethodDescriptor(
name: "NetworkReorder",
path: "/burrow.Networks/NetworkReorder",
type: GRPCCallType.unary
)
public static let networkDelete = GRPCMethodDescriptor(
name: "NetworkDelete",
path: "/burrow.Networks/NetworkDelete",
type: GRPCCallType.unary
)
}
}

View file

@ -1,566 +0,0 @@
// DO NOT EDIT.
// swift-format-ignore-file
// swiftlint:disable all
//
// Generated by the Swift generator plugin for the protocol buffer compiler.
// Source: burrow.proto
//
// For information on using the generated types, please see the documentation:
// https://github.com/apple/swift-protobuf/
import Foundation
import SwiftProtobuf
// If the compiler emits an error on this type, it is because this file
// was generated by a version of the `protoc` Swift plug-in that is
// incompatible with the version of SwiftProtobuf to which you are linking.
// Please ensure that you are building against the same version of the API
// that was used to generate this file.
fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {}
typealias Version = _2
}
public enum Burrow_NetworkType: SwiftProtobuf.Enum, Swift.CaseIterable {
public typealias RawValue = Int
case wireGuard // = 0
case tailnet // = 1
case UNRECOGNIZED(Int)
public init() {
self = .wireGuard
}
public init?(rawValue: Int) {
switch rawValue {
case 0: self = .wireGuard
case 1: self = .tailnet
default: self = .UNRECOGNIZED(rawValue)
}
}
public var rawValue: Int {
switch self {
case .wireGuard: return 0
case .tailnet: return 1
case .UNRECOGNIZED(let i): return i
}
}
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [Burrow_NetworkType] = [
.wireGuard,
.tailnet,
]
}
public enum Burrow_State: SwiftProtobuf.Enum, Swift.CaseIterable {
public typealias RawValue = Int
case stopped // = 0
case running // = 1
case UNRECOGNIZED(Int)
public init() {
self = .stopped
}
public init?(rawValue: Int) {
switch rawValue {
case 0: self = .stopped
case 1: self = .running
default: self = .UNRECOGNIZED(rawValue)
}
}
public var rawValue: Int {
switch self {
case .stopped: return 0
case .running: return 1
case .UNRECOGNIZED(let i): return i
}
}
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [Burrow_State] = [
.stopped,
.running,
]
}
public struct Burrow_NetworkReorderRequest: Sendable {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
public var id: Int32 = 0
public var index: Int32 = 0
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
}
public struct Burrow_WireGuardPeer: Sendable {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
public var endpoint: String = String()
public var subnet: [String] = []
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
}
public struct Burrow_WireGuardNetwork: Sendable {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
public var address: String = String()
public var dns: String = String()
public var peer: [Burrow_WireGuardPeer] = []
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
}
public struct Burrow_NetworkDeleteRequest: Sendable {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
public var id: Int32 = 0
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
}
public struct Burrow_Network: @unchecked Sendable {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
public var id: Int32 = 0
public var type: Burrow_NetworkType = .wireGuard
public var payload: Data = Data()
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
}
public struct Burrow_NetworkListResponse: Sendable {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
public var network: [Burrow_Network] = []
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
}
public struct Burrow_Empty: Sendable {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
}
public struct Burrow_TunnelStatusResponse: Sendable {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
public var state: Burrow_State = .stopped
public var start: SwiftProtobuf.Google_Protobuf_Timestamp {
get {return _start ?? SwiftProtobuf.Google_Protobuf_Timestamp()}
set {_start = newValue}
}
/// Returns true if `start` has been explicitly set.
public var hasStart: Bool {return self._start != nil}
/// Clears the value of `start`. Subsequent reads from it will return its default value.
public mutating func clearStart() {self._start = nil}
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
fileprivate var _start: SwiftProtobuf.Google_Protobuf_Timestamp? = nil
}
public struct Burrow_TunnelConfigurationResponse: Sendable {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
public var addresses: [String] = []
public var mtu: Int32 = 0
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
}
// MARK: - Code below here is support for the SwiftProtobuf runtime.
fileprivate let _protobuf_package = "burrow"
extension Burrow_NetworkType: SwiftProtobuf._ProtoNameProviding {
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
0: .same(proto: "WireGuard"),
1: .same(proto: "Tailnet"),
]
}
extension Burrow_State: SwiftProtobuf._ProtoNameProviding {
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
0: .same(proto: "Stopped"),
1: .same(proto: "Running"),
]
}
extension Burrow_NetworkReorderRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let protoMessageName: String = _protobuf_package + ".NetworkReorderRequest"
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "id"),
2: .same(proto: "index"),
]
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try { try decoder.decodeSingularInt32Field(value: &self.id) }()
case 2: try { try decoder.decodeSingularInt32Field(value: &self.index) }()
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.index != 0 {
try visitor.visitSingularInt32Field(value: self.index, fieldNumber: 2)
}
try unknownFields.traverse(visitor: &visitor)
}
public static func ==(lhs: Burrow_NetworkReorderRequest, rhs: Burrow_NetworkReorderRequest) -> Bool {
if lhs.id != rhs.id {return false}
if lhs.index != rhs.index {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension Burrow_WireGuardPeer: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let protoMessageName: String = _protobuf_package + ".WireGuardPeer"
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "endpoint"),
2: .same(proto: "subnet"),
]
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try { try decoder.decodeSingularStringField(value: &self.endpoint) }()
case 2: try { try decoder.decodeRepeatedStringField(value: &self.subnet) }()
default: break
}
}
}
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if !self.endpoint.isEmpty {
try visitor.visitSingularStringField(value: self.endpoint, fieldNumber: 1)
}
if !self.subnet.isEmpty {
try visitor.visitRepeatedStringField(value: self.subnet, fieldNumber: 2)
}
try unknownFields.traverse(visitor: &visitor)
}
public static func ==(lhs: Burrow_WireGuardPeer, rhs: Burrow_WireGuardPeer) -> Bool {
if lhs.endpoint != rhs.endpoint {return false}
if lhs.subnet != rhs.subnet {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension Burrow_WireGuardNetwork: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let protoMessageName: String = _protobuf_package + ".WireGuardNetwork"
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "address"),
2: .same(proto: "dns"),
3: .same(proto: "peer"),
]
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try { try decoder.decodeSingularStringField(value: &self.address) }()
case 2: try { try decoder.decodeSingularStringField(value: &self.dns) }()
case 3: try { try decoder.decodeRepeatedMessageField(value: &self.peer) }()
default: break
}
}
}
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if !self.address.isEmpty {
try visitor.visitSingularStringField(value: self.address, fieldNumber: 1)
}
if !self.dns.isEmpty {
try visitor.visitSingularStringField(value: self.dns, fieldNumber: 2)
}
if !self.peer.isEmpty {
try visitor.visitRepeatedMessageField(value: self.peer, fieldNumber: 3)
}
try unknownFields.traverse(visitor: &visitor)
}
public static func ==(lhs: Burrow_WireGuardNetwork, rhs: Burrow_WireGuardNetwork) -> Bool {
if lhs.address != rhs.address {return false}
if lhs.dns != rhs.dns {return false}
if lhs.peer != rhs.peer {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension Burrow_NetworkDeleteRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let protoMessageName: String = _protobuf_package + ".NetworkDeleteRequest"
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "id"),
]
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try { try decoder.decodeSingularInt32Field(value: &self.id) }()
default: break
}
}
}
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if self.id != 0 {
try visitor.visitSingularInt32Field(value: self.id, fieldNumber: 1)
}
try unknownFields.traverse(visitor: &visitor)
}
public static func ==(lhs: Burrow_NetworkDeleteRequest, rhs: Burrow_NetworkDeleteRequest) -> Bool {
if lhs.id != rhs.id {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension Burrow_Network: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let protoMessageName: String = _protobuf_package + ".Network"
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "id"),
2: .same(proto: "type"),
3: .same(proto: "payload"),
]
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try { try decoder.decodeSingularInt32Field(value: &self.id) }()
case 2: try { try decoder.decodeSingularEnumField(value: &self.type) }()
case 3: try { try decoder.decodeSingularBytesField(value: &self.payload) }()
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.type != .wireGuard {
try visitor.visitSingularEnumField(value: self.type, fieldNumber: 2)
}
if !self.payload.isEmpty {
try visitor.visitSingularBytesField(value: self.payload, fieldNumber: 3)
}
try unknownFields.traverse(visitor: &visitor)
}
public static func ==(lhs: Burrow_Network, rhs: Burrow_Network) -> Bool {
if lhs.id != rhs.id {return false}
if lhs.type != rhs.type {return false}
if lhs.payload != rhs.payload {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension Burrow_NetworkListResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let protoMessageName: String = _protobuf_package + ".NetworkListResponse"
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "network"),
]
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try { try decoder.decodeRepeatedMessageField(value: &self.network) }()
default: break
}
}
}
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if !self.network.isEmpty {
try visitor.visitRepeatedMessageField(value: self.network, fieldNumber: 1)
}
try unknownFields.traverse(visitor: &visitor)
}
public static func ==(lhs: Burrow_NetworkListResponse, rhs: Burrow_NetworkListResponse) -> Bool {
if lhs.network != rhs.network {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension Burrow_Empty: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let protoMessageName: String = _protobuf_package + ".Empty"
public static let _protobuf_nameMap = SwiftProtobuf._NameMap()
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
// Load everything into unknown fields
while try decoder.nextFieldNumber() != nil {}
}
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
try unknownFields.traverse(visitor: &visitor)
}
public static func ==(lhs: Burrow_Empty, rhs: Burrow_Empty) -> Bool {
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension Burrow_TunnelStatusResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let protoMessageName: String = _protobuf_package + ".TunnelStatusResponse"
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "state"),
2: .same(proto: "start"),
]
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try { try decoder.decodeSingularEnumField(value: &self.state) }()
case 2: try { try decoder.decodeSingularMessageField(value: &self._start) }()
default: break
}
}
}
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every if/case branch local when no optimizations
// are enabled. https://github.com/apple/swift-protobuf/issues/1034 and
// https://github.com/apple/swift-protobuf/issues/1182
if self.state != .stopped {
try visitor.visitSingularEnumField(value: self.state, fieldNumber: 1)
}
try { if let v = self._start {
try visitor.visitSingularMessageField(value: v, fieldNumber: 2)
} }()
try unknownFields.traverse(visitor: &visitor)
}
public static func ==(lhs: Burrow_TunnelStatusResponse, rhs: Burrow_TunnelStatusResponse) -> Bool {
if lhs.state != rhs.state {return false}
if lhs._start != rhs._start {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension Burrow_TunnelConfigurationResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let protoMessageName: String = _protobuf_package + ".TunnelConfigurationResponse"
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "addresses"),
2: .same(proto: "mtu"),
]
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try { try decoder.decodeRepeatedStringField(value: &self.addresses) }()
case 2: try { try decoder.decodeSingularInt32Field(value: &self.mtu) }()
default: break
}
}
}
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if !self.addresses.isEmpty {
try visitor.visitRepeatedStringField(value: self.addresses, fieldNumber: 1)
}
if self.mtu != 0 {
try visitor.visitSingularInt32Field(value: self.mtu, fieldNumber: 2)
}
try unknownFields.traverse(visitor: &visitor)
}
public static func ==(lhs: Burrow_TunnelConfigurationResponse, rhs: Burrow_TunnelConfigurationResponse) -> Bool {
if lhs.addresses != rhs.addresses {return false}
if lhs.mtu != rhs.mtu {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}

View file

@ -0,0 +1,11 @@
{
"invocations": [
{
"protoFiles": [
"burrow.proto",
],
"server": false,
"visibility": "public"
}
]
}

View file

@ -0,0 +1,10 @@
{
"invocations": [
{
"protoFiles": [
"burrow.proto",
],
"visibility": "public"
}
]
}

View file

@ -284,7 +284,6 @@ private struct AccountDraft {
var identityName = ""
var wireGuardConfig = ""
var discoveryEmail = ""
var tailnetProvider: TailnetProvider = .tailscale
var authority = ""
var tailnet = ""
@ -328,9 +327,6 @@ private struct ConfigurationSheetView: View {
@State private var errorMessage: String?
@State private var loginSessionID: String?
@State private var loginStatus: TailnetLoginStatus?
@State private var discoveryStatus: TailnetDiscoveryResponse?
@State private var discoveryError: String?
@State private var isDiscoveringTailnet = false
@State private var authorityProbeStatus: TailnetAuthorityProbeStatus?
@State private var authorityProbeError: String?
@State private var isProbingAuthority = false
@ -453,9 +449,6 @@ private struct ConfigurationSheetView: View {
.onChange(of: draft.authority) { _, _ in
resetAuthorityProbe()
}
.onChange(of: draft.discoveryEmail) { _, _ in
resetTailnetDiscoveryFeedback()
}
.onDisappear {
pollingTask?.cancel()
webAuthenticationTask?.cancel()
@ -466,37 +459,7 @@ private struct ConfigurationSheetView: View {
@ViewBuilder
private var tailnetSections: some View {
Section("Connection") {
TextField("Email address", text: $draft.discoveryEmail)
.textInputAutocapitalization(.never)
.keyboardType(.emailAddress)
.burrowLoginField()
.autocorrectionDisabled()
Button {
discoverTailnetAuthority()
} label: {
Label {
Text(isDiscoveringTailnet ? "Finding Server" : "Find Server")
} icon: {
Image(systemName: isDiscoveringTailnet ? "hourglass" : "at.circle")
}
}
.buttonStyle(.borderless)
.disabled(isDiscoveringTailnet || normalizedOptional(draft.discoveryEmail) == nil)
if let discoveryStatus {
tailnetDiscoveryCard(status: discoveryStatus, failure: nil)
} else if let discoveryError {
tailnetDiscoveryCard(status: nil, failure: discoveryError)
}
Picker(
"Provider",
selection: Binding(
get: { draft.tailnetProvider },
set: { applyTailnetProvider($0) }
)
) {
Picker("Provider", selection: $draft.tailnetProvider) {
ForEach(TailnetProvider.allCases) { provider in
Text(provider.title).tag(provider)
}
@ -540,14 +503,14 @@ private struct ConfigurationSheetView: View {
}
Section("Authentication") {
if tailnetUsesWebLogin {
if draft.tailnetProvider.usesWebLogin {
tailnetWebLoginCard
} else {
TextField("Username", text: $draft.username)
.burrowLoginField()
.autocorrectionDisabled()
Picker("Authentication", selection: $draft.authMode) {
ForEach(availableTailnetAuthModes) { mode in
ForEach([AccountAuthMode.none, .password, .preauthKey]) { mode in
Text(mode.title).tag(mode)
}
}
@ -620,7 +583,7 @@ private struct ConfigurationSheetView: View {
HStack(spacing: 8) {
summaryBadge(draft.tailnetProvider.title)
summaryBadge(
tailnetUsesWebLogin ? "Web Sign-In" : draft.authMode.title
draft.tailnetProvider.usesWebLogin ? "Web Sign-In" : draft.authMode.title
)
}
}
@ -693,7 +656,7 @@ private struct ConfigurationSheetView: View {
.foregroundStyle(.secondary)
}
} else {
Text("Burrow launches the local bridge, then opens the real provider sign-in page in-app.")
Text("Burrow launches the local bridge, then opens the real Tailscale sign-in page in-app.")
.font(.footnote)
.foregroundStyle(.secondary)
}
@ -733,41 +696,6 @@ private struct ConfigurationSheetView: View {
)
}
private func tailnetDiscoveryCard(
status: TailnetDiscoveryResponse?,
failure: String?
) -> some View {
VStack(alignment: .leading, spacing: 6) {
if let status {
Text("Discovered \(status.provider.title)")
.font(.subheadline.weight(.medium))
Text(status.authority)
.font(.footnote.monospaced())
.foregroundStyle(.secondary)
.textSelection(.enabled)
if let oidcIssuer = status.oidcIssuer {
Text("OIDC: \(oidcIssuer)")
.font(.footnote)
.foregroundStyle(.secondary)
.lineLimit(3)
.textSelection(.enabled)
}
} else if let failure {
Text("Discovery failed")
.font(.subheadline.weight(.medium))
.foregroundStyle(.red)
Text(failure)
.font(.footnote)
.foregroundStyle(.secondary)
}
}
.padding(12)
.background(
RoundedRectangle(cornerRadius: 16)
.fill(.thinMaterial)
)
}
private func summaryBadge(_ label: String) -> some View {
Text(label)
.font(.caption.weight(.medium))
@ -834,12 +762,12 @@ private struct ConfigurationSheetView: View {
}
}
if availableTailnetAuthModes.count > 1 {
if !draft.tailnetProvider.usesWebLogin {
Menu("Authentication") {
ForEach(availableTailnetAuthModes) { mode in
ForEach([AccountAuthMode.none, .password, .preauthKey]) { mode in
Button(mode.title) {
draft.authMode = mode
if mode == .none || mode == .web {
if mode == .none {
draft.secret = ""
}
}
@ -920,7 +848,7 @@ private struct ConfigurationSheetView: View {
case .tor:
return "Save Account"
case .tailnet:
if tailnetUsesWebLogin {
if draft.tailnetProvider.usesWebLogin {
return loginStatus?.running == true ? "Save Account" : "Start Sign-In"
}
return "Save Account"
@ -937,12 +865,12 @@ private struct ConfigurationSheetView: View {
if normalizedOptional(draft.accountName) == nil || normalizedOptional(draft.identityName) == nil {
return true
}
if draft.tailnetProvider.usesWebLogin {
return false
}
if draft.tailnetProvider.requiresControlURL && normalizedOptional(draft.authority) == nil {
return true
}
if tailnetUsesWebLogin {
return false
}
if draft.authMode != .none && normalizedOptional(draft.secret) == nil {
return true
}
@ -1027,14 +955,14 @@ private struct ConfigurationSheetView: View {
}
private func submitTailnet() async throws {
if tailnetUsesWebLogin {
if draft.tailnetProvider.usesWebLogin {
if loginStatus?.running == true {
webAuthenticationTask?.cancel()
webAuthenticationTask = nil
try await saveTailnetAccount(secret: nil, username: nil)
dismiss()
} else {
try await startTailnetLogin()
try await startTailscaleLogin()
}
return
}
@ -1045,13 +973,13 @@ private struct ConfigurationSheetView: View {
dismiss()
}
private func startTailnetLogin() async throws {
private func startTailscaleLogin() async throws {
let response = try await TailnetBridgeClient.startLogin(
TailnetLoginStartRequest(
accountName: normalized(draft.accountName, fallback: "default"),
identityName: normalized(draft.identityName, fallback: "apple"),
hostname: normalizedOptional(draft.hostname),
controlURL: normalizedOptional(draft.authority) ?? draft.tailnetProvider.defaultAuthority
controlURL: draft.tailnetProvider.defaultAuthority
)
)
loginSessionID = response.sessionID
@ -1082,7 +1010,7 @@ private struct ConfigurationSheetView: View {
case .tailnetLogin:
draft.tailnetProvider = .tailscale
do {
try await startTailnetLogin()
try await startTailscaleLogin()
} catch {
errorMessage = error.localizedDescription
}
@ -1150,14 +1078,14 @@ private struct ConfigurationSheetView: View {
let provider = draft.tailnetProvider
let title = titleOrFallback(
hostnameFallback(
from: tailnetUsesWebLogin ? (loginStatus?.tailnetName ?? "") : draft.authority,
from: provider.usesWebLogin ? (loginStatus?.tailnetName ?? "") : draft.authority,
fallback: provider.title
)
)
let payload = TailnetNetworkPayload(
provider: provider,
authority: normalizedOptional(draft.authority) ?? normalizedOptional(provider.defaultAuthority ?? ""),
authority: normalizedOptional(provider.defaultAuthority ?? draft.authority),
account: normalized(draft.accountName, fallback: "default"),
identity: normalized(draft.identityName, fallback: "apple"),
tailnet: normalizedOptional(loginStatus?.tailnetName ?? draft.tailnet),
@ -1166,7 +1094,7 @@ private struct ConfigurationSheetView: View {
var noteParts: [String] = [
provider.title,
tailnetUsesWebLogin
provider.usesWebLogin
? "State: \(loginStatus?.backendState ?? "NeedsLogin")"
: "Auth: \(draft.authMode.title)",
]
@ -1195,7 +1123,7 @@ private struct ConfigurationSheetView: View {
hostname: payload.hostname,
username: username,
tailnet: payload.tailnet,
authMode: tailnetUsesWebLogin ? .web : draft.authMode,
authMode: provider.usesWebLogin ? .web : draft.authMode,
note: noteParts.joined(separator: ""),
createdAt: .now,
updatedAt: .now
@ -1227,25 +1155,18 @@ private struct ConfigurationSheetView: View {
}
private func applyTailnetProvider(_ provider: TailnetProvider) {
resetTailnetDiscoveryFeedback()
draft.tailnetProvider = provider
applyTailnetDefaults(for: provider)
}
private func applyTailnetDefaults(for provider: TailnetProvider) {
draft.authority = provider.defaultAuthority ?? ""
loginStatus = nil
loginSessionID = nil
pollingTask?.cancel()
if provider == .tailscale {
if provider.usesWebLogin {
draft.authMode = .web
draft.username = ""
draft.secret = ""
} else {
if !availableTailnetAuthModes.contains(draft.authMode) {
draft.authMode = provider.supportsWebLogin ? .web : .none
}
if draft.authMode == .web && !provider.supportsWebLogin {
if draft.authMode == .web {
draft.authMode = .none
}
}
@ -1281,41 +1202,6 @@ private struct ConfigurationSheetView: View {
authorityProbeError = nil
}
private func resetTailnetDiscoveryFeedback() {
discoveryStatus = nil
discoveryError = nil
}
private func discoverTailnetAuthority() {
guard let email = normalizedOptional(draft.discoveryEmail) else {
discoveryStatus = nil
discoveryError = "Enter an email address first."
return
}
isDiscoveringTailnet = true
discoveryStatus = nil
discoveryError = nil
Task { @MainActor in
defer { isDiscoveringTailnet = false }
do {
let discovery = try await TailnetDiscoveryClient.discover(email: email)
discoveryStatus = discovery
draft.tailnetProvider = discovery.provider
draft.authority = discovery.authority
if discovery.provider.supportsWebLogin, discovery.oidcIssuer != nil {
draft.authMode = .web
draft.username = ""
draft.secret = ""
}
probeTailnetAuthority()
} catch {
discoveryError = error.localizedDescription
}
}
}
private func pasteWireGuardConfiguration() {
guard let clipboardString else { return }
draft.wireGuardConfig = clipboardString
@ -1361,21 +1247,6 @@ private struct ConfigurationSheetView: View {
return host
}
private var tailnetUsesWebLogin: Bool {
draft.authMode == .web && draft.tailnetProvider.supportsWebLogin
}
private var availableTailnetAuthModes: [AccountAuthMode] {
switch draft.tailnetProvider {
case .tailscale:
[.web]
case .headscale:
[.web, .none, .password, .preauthKey]
case .burrow:
[.none, .password, .preauthKey]
}
}
@ViewBuilder
private func labeledValue(_ label: String, _ value: String) -> some View {
VStack(alignment: .leading, spacing: 2) {

View file

@ -33,13 +33,6 @@ struct TailnetLoginStartRequest: Codable, Sendable {
var controlURL: String?
}
struct TailnetDiscoveryResponse: Codable, Sendable {
var domain: String
var provider: TailnetProvider
var authority: String
var oidcIssuer: String?
}
struct TailnetLoginStatus: Codable, Sendable {
var backendState: String
var authURL: String?
@ -98,7 +91,7 @@ enum TailnetBridgeClient {
return try decoder.decode(TailnetLoginStatus.self, from: data)
}
fileprivate static func validate(response: URLResponse, data: Data) throws {
private static func validate(response: URLResponse, data: Data) throws {
guard let http = response as? HTTPURLResponse else {
throw URLError(.badServerResponse)
}
@ -111,32 +104,6 @@ enum TailnetBridgeClient {
}
}
enum TailnetDiscoveryClient {
private static let baseURL = URL(string: "http://127.0.0.1:8080")!
static func discover(email: String) async throws -> TailnetDiscoveryResponse {
guard var components = URLComponents(
url: baseURL.appendingPathComponent("v1/tailnet/discover"),
resolvingAgainstBaseURL: false
) else {
throw URLError(.badURL)
}
components.queryItems = [
URLQueryItem(name: "email", value: email)
]
guard let url = components.url else {
throw URLError(.badURL)
}
let (data, response) = try await URLSession.shared.data(from: url)
try TailnetBridgeClient.validate(response: response, data: data)
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
return try decoder.decode(TailnetDiscoveryResponse.self, from: data)
}
}
enum TailnetAuthorityProbeClient {
static func probe(provider: TailnetProvider, authority: String) async throws -> TailnetAuthorityProbeStatus {
let normalizedAuthority = normalizeAuthority(authority)
@ -341,13 +308,8 @@ enum TailnetProvider: String, CaseIterable, Codable, Identifiable, Sendable {
}
}
var supportsWebLogin: Bool {
switch self {
case .tailscale, .headscale:
true
case .burrow:
false
}
var usesWebLogin: Bool {
self == .tailscale
}
var requiresControlURL: Bool {
@ -370,7 +332,7 @@ enum TailnetProvider: String, CaseIterable, Codable, Identifiable, Sendable {
case .tailscale:
"Use Tailscale's real browser login flow."
case .headscale:
"Use your Headscale control plane with browser or key-based sign-in."
"Store a Headscale control-plane endpoint and credentials."
case .burrow:
"Store Burrow control-plane credentials."
}

View file

@ -5,18 +5,17 @@ use std::{env, path::Path};
use anyhow::{Context, Result};
use axum::{
extract::{Json, Path as AxumPath, Query, State},
extract::{Json, Path as AxumPath, State},
http::{header::AUTHORIZATION, HeaderMap, StatusCode},
response::IntoResponse,
routing::{get, post},
Router,
};
use serde::Deserialize;
use tokio::signal;
use crate::control::{
discovery, LocalAuthRequest, LocalAuthResponse, MapRequest, MapResponse, RegisterRequest,
RegisterResponse, TailnetDiscovery, BURROW_TAILNET_DOMAIN,
LocalAuthRequest, LocalAuthResponse, MapRequest, MapResponse, RegisterRequest,
RegisterResponse, BURROW_TAILNET_DOMAIN,
};
#[derive(Clone, Debug)]
@ -106,11 +105,6 @@ struct AppState {
tailscale: tailscale::TailscaleBridgeManager,
}
#[derive(Debug, Deserialize)]
struct TailnetDiscoveryQuery {
email: String,
}
type AppResult<T> = Result<T, (StatusCode, String)>;
pub async fn serve() -> Result<()> {
@ -145,7 +139,6 @@ pub fn build_router(config: AuthServerConfig) -> Router {
.route("/v1/auth/login", post(login_local))
.route("/v1/control/register", post(control_register))
.route("/v1/control/map", post(control_map))
.route("/v1/tailnet/discover", get(tailnet_discover))
.route("/v1/tailscale/login/start", post(tailscale_login_start))
.route("/v1/tailscale/login/:session_id", get(tailscale_login_status))
.with_state(AppState {
@ -212,19 +205,6 @@ async fn control_map(
Ok(Json(response))
}
async fn tailnet_discover(
Query(query): Query<TailnetDiscoveryQuery>,
) -> AppResult<Json<TailnetDiscovery>> {
if query.email.trim().is_empty() {
return Err((StatusCode::BAD_REQUEST, "email is required".to_owned()));
}
let discovery = discovery::discover_tailnet(&query.email)
.await
.map_err(|err| (StatusCode::BAD_GATEWAY, err.to_string()))?;
Ok(Json(discovery))
}
async fn tailscale_login_start(
State(state): State<AppState>,
Json(request): Json<tailscale::TailscaleLoginStartRequest>,
@ -414,17 +394,4 @@ mod tests {
assert!(map.dns.expect("dns").magic_dns);
Ok(())
}
#[tokio::test]
async fn tailnet_discover_requires_email() -> Result<()> {
let app = build_router(AuthServerConfig::default());
let response = app
.oneshot(
Request::get("/v1/tailnet/discover?email=")
.body(Body::empty())?,
)
.await?;
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
Ok(())
}
}

View file

@ -1,212 +0,0 @@
use anyhow::{anyhow, Context, Result};
use reqwest::{Client, StatusCode, Url};
use serde::{Deserialize, Serialize};
use super::TailnetProvider;
pub const TAILNET_DISCOVERY_REL: &str = "https://burrow.net/rel/tailnet-control-server";
const TAILNET_DISCOVERY_PATH: &str = "/.well-known/burrow-tailnet";
const WEBFINGER_PATH: &str = "/.well-known/webfinger";
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct TailnetDiscovery {
pub domain: String,
pub provider: TailnetProvider,
pub authority: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub oidc_issuer: Option<String>,
}
#[derive(Clone, Debug, Default, Deserialize)]
struct WebFingerDocument {
#[serde(default)]
links: Vec<WebFingerLink>,
}
#[derive(Clone, Debug, Default, Deserialize)]
struct WebFingerLink {
#[serde(default)]
rel: String,
#[serde(default)]
href: Option<String>,
}
pub async fn discover_tailnet(email: &str) -> Result<TailnetDiscovery> {
let domain = email_domain(email)?;
let base_url = Url::parse(&format!("https://{domain}"))
.with_context(|| format!("invalid discovery domain {domain}"))?;
let client = Client::builder()
.user_agent("burrow-tailnet-discovery")
.timeout(std::time::Duration::from_secs(10))
.build()
.context("failed to build tailnet discovery client")?;
discover_tailnet_at(&client, email, &base_url).await
}
pub async fn discover_tailnet_at(
client: &Client,
email: &str,
base_url: &Url,
) -> Result<TailnetDiscovery> {
let domain = email_domain(email)?;
if let Some(discovery) = discover_well_known(client, base_url).await? {
return Ok(TailnetDiscovery { domain, ..discovery });
}
if let Some(authority) = discover_webfinger(client, email, base_url).await? {
return Ok(TailnetDiscovery {
domain,
provider: TailnetProvider::Headscale,
authority,
oidc_issuer: None,
});
}
Err(anyhow!("no tailnet discovery metadata found for {domain}"))
}
pub fn email_domain(email: &str) -> Result<String> {
let trimmed = email.trim();
let (_, domain) = trimmed
.rsplit_once('@')
.ok_or_else(|| anyhow!("email address must include a domain"))?;
let domain = domain.trim().trim_matches('.').to_ascii_lowercase();
if domain.is_empty() {
return Err(anyhow!("email address must include a domain"));
}
Ok(domain)
}
async fn discover_well_known(client: &Client, base_url: &Url) -> Result<Option<TailnetDiscovery>> {
let url = base_url
.join(TAILNET_DISCOVERY_PATH)
.context("failed to build tailnet discovery URL")?;
let response = client
.get(url)
.header("accept", "application/json")
.send()
.await
.context("tailnet well-known request failed")?;
match response.status() {
StatusCode::OK => response
.json::<TailnetDiscovery>()
.await
.context("invalid tailnet discovery document")
.map(Some),
StatusCode::NOT_FOUND => Ok(None),
status => Err(anyhow!("tailnet well-known lookup failed with HTTP {status}")),
}
}
async fn discover_webfinger(client: &Client, email: &str, base_url: &Url) -> Result<Option<String>> {
let mut url = base_url
.join(WEBFINGER_PATH)
.context("failed to build webfinger URL")?;
url.query_pairs_mut()
.append_pair("resource", &format!("acct:{email}"))
.append_pair("rel", TAILNET_DISCOVERY_REL);
let response = client
.get(url)
.header("accept", "application/jrd+json, application/json")
.send()
.await
.context("tailnet webfinger request failed")?;
match response.status() {
StatusCode::OK => {
let document = response
.json::<WebFingerDocument>()
.await
.context("invalid webfinger document")?;
Ok(document
.links
.into_iter()
.find(|link| link.rel == TAILNET_DISCOVERY_REL)
.and_then(|link| link.href)
.filter(|href| !href.trim().is_empty()))
}
StatusCode::NOT_FOUND => Ok(None),
status => Err(anyhow!("tailnet webfinger lookup failed with HTTP {status}")),
}
}
#[cfg(test)]
mod tests {
use axum::{routing::get, Router};
use serde_json::json;
use tokio::net::TcpListener;
use super::*;
#[test]
fn extracts_domain_from_email() {
assert_eq!(email_domain("Contact@Burrow.net").unwrap(), "burrow.net");
assert!(email_domain("contact").is_err());
}
#[tokio::test]
async fn discovers_from_well_known_document() -> Result<()> {
let router = Router::new().route(
TAILNET_DISCOVERY_PATH,
get(|| async {
axum::Json(json!({
"domain": "burrow.net",
"provider": "headscale",
"authority": "https://ts.burrow.net",
"oidc_issuer": "https://auth.burrow.net/application/o/ts/"
}))
}),
);
let listener = TcpListener::bind("127.0.0.1:0").await?;
let base_url = Url::parse(&format!("http://{}", listener.local_addr()?))?;
let server = tokio::spawn(async move { axum::serve(listener, router).await });
let client = Client::builder().build()?;
let discovery = discover_tailnet_at(&client, "contact@burrow.net", &base_url).await?;
assert_eq!(discovery.provider, TailnetProvider::Headscale);
assert_eq!(discovery.authority, "https://ts.burrow.net");
assert_eq!(discovery.domain, "burrow.net");
server.abort();
Ok(())
}
#[tokio::test]
async fn falls_back_to_webfinger_authority() -> Result<()> {
let router = Router::new()
.route(
TAILNET_DISCOVERY_PATH,
get(|| async { (StatusCode::NOT_FOUND, "") }),
)
.route(
WEBFINGER_PATH,
get(|| async {
axum::Json(json!({
"subject": "acct:contact@burrow.net",
"links": [
{
"rel": TAILNET_DISCOVERY_REL,
"href": "https://ts.burrow.net"
}
]
}))
}),
);
let listener = TcpListener::bind("127.0.0.1:0").await?;
let base_url = Url::parse(&format!("http://{}", listener.local_addr()?))?;
let server = tokio::spawn(async move { axum::serve(listener, router).await });
let client = Client::builder().build()?;
let discovery = discover_tailnet_at(&client, "contact@burrow.net", &base_url).await?;
assert_eq!(discovery.provider, TailnetProvider::Headscale);
assert_eq!(discovery.authority, "https://ts.burrow.net");
server.abort();
Ok(())
}
}

View file

@ -1,5 +1,4 @@
pub mod config;
pub mod discovery;
use std::collections::BTreeMap;
@ -7,7 +6,6 @@ use serde::{Deserialize, Serialize};
use serde_json::Value;
pub use config::{TailnetConfig, TailnetProvider};
pub use discovery::{TailnetDiscovery, TAILNET_DISCOVERY_REL};
pub const BURROW_CAPABILITY_VERSION: i32 = 1;
pub const BURROW_TAILNET_DOMAIN: &str = "burrow.net";

View file

@ -259,12 +259,9 @@ in
encode gzip zstd
@oidcConfig path /.well-known/openid-configuration
redir @oidcConfig https://${config.services.burrow.authentik.domain}/application/o/${config.services.burrow.authentik.forgejoProviderSlug}/.well-known/openid-configuration 308
@tailnetConfig path /.well-known/burrow-tailnet
header @tailnetConfig Content-Type application/json
respond @tailnetConfig "{\"domain\":\"${cfg.siteDomain}\",\"provider\":\"headscale\",\"authority\":\"https://${config.services.burrow.headscale.domain}\",\"oidc_issuer\":\"https://${config.services.burrow.authentik.domain}/application/o/${config.services.burrow.authentik.headscaleProviderSlug}/\"}" 200
@webfinger path /.well-known/webfinger
header @webfinger Content-Type application/jrd+json
respond @webfinger "{\"subject\":\"{query.resource}\",\"links\":[{\"rel\":\"http://openid.net/specs/connect/1.0/issuer\",\"href\":\"https://${config.services.burrow.authentik.domain}/application/o/${config.services.burrow.authentik.forgejoProviderSlug}/\"},{\"rel\":\"https://burrow.net/rel/tailnet-control-server\",\"href\":\"https://${config.services.burrow.headscale.domain}\"}]}" 200
respond @webfinger "{\"subject\":\"{query.resource}\",\"links\":[{\"rel\":\"http://openid.net/specs/connect/1.0/issuer\",\"href\":\"https://${config.services.burrow.authentik.domain}/application/o/${config.services.burrow.authentik.forgejoProviderSlug}/\"}]}" 200
@root path /
redir @root ${homeRepoUrl} 308
respond 404