From bb6934de5dd53bd78feecfbdaf72dd18f0ae44f5 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Tue, 24 Jun 2025 03:26:52 +0200 Subject: [PATCH] Add local bypass --- mac/VibeTunnel/Core/Services/BunServer.swift | 23 ++++++++ .../Core/Services/ServerManager.swift | 11 ++++ .../Core/Services/SessionMonitor.swift | 14 ++++- .../Settings/DashboardSettingsView.swift | 57 ++++++++++++------- 4 files changed, 84 insertions(+), 21 deletions(-) diff --git a/mac/VibeTunnel/Core/Services/BunServer.swift b/mac/VibeTunnel/Core/Services/BunServer.swift index 17a3f72c..a0bda128 100644 --- a/mac/VibeTunnel/Core/Services/BunServer.swift +++ b/mac/VibeTunnel/Core/Services/BunServer.swift @@ -1,5 +1,6 @@ import Foundation import OSLog +import CryptoKit /// Server state enumeration enum ServerState { @@ -44,6 +45,21 @@ final class BunServer { var port: String = "" var bindAddress: String = "127.0.0.1" + + /// Local authentication token for bypassing auth on localhost + private let localAuthToken: String = { + // Generate a secure random token for this session + let randomData = Data((0..<32).map { _ in UInt8.random(in: 0...255) }) + return randomData.base64EncodedString() + .replacingOccurrences(of: "+", with: "-") + .replacingOccurrences(of: "/", with: "_") + .replacingOccurrences(of: "=", with: "") + }() + + /// Get the local auth token for use in HTTP requests + var localToken: String { + localAuthToken + } // MARK: - Initialization @@ -150,6 +166,13 @@ final class BunServer { // OS authentication is the default, no special flags needed break } + + // Add local bypass authentication for the Mac app + if authMode != "none" { + // Enable local bypass with our generated token + vibetunnelArgs += " --allow-local-bypass --local-auth-token \(localAuthToken)" + logger.info("Local authentication bypass enabled for Mac app") + } // Create wrapper to run vibetunnel with a parent death signal // Using a subshell that monitors parent process and kills vibetunnel if parent dies diff --git a/mac/VibeTunnel/Core/Services/ServerManager.swift b/mac/VibeTunnel/Core/Services/ServerManager.swift index 8fae7c75..c9590c75 100644 --- a/mac/VibeTunnel/Core/Services/ServerManager.swift +++ b/mac/VibeTunnel/Core/Services/ServerManager.swift @@ -224,6 +224,9 @@ class ServerManager { } logger.info("Started server on port \(self.port)") + + // Pass the local auth token to SessionMonitor + SessionMonitor.shared.setLocalAuthToken(server.localToken) // Trigger cleanup of old sessions after server starts await triggerInitialCleanup() @@ -253,6 +256,9 @@ class ServerManager { await server.stop() bunServer = nil isRunning = false + + // Clear the auth token from SessionMonitor + SessionMonitor.shared.setLocalAuthToken(nil) // Reset crash tracking when manually stopped consecutiveCrashes = 0 @@ -316,6 +322,11 @@ class ServerManager { var request = URLRequest(url: url) request.httpMethod = "POST" request.timeoutInterval = 10 + + // Add local auth token if available + if let server = bunServer { + request.setValue(server.localToken, forHTTPHeaderField: "X-VibeTunnel-Local") + } // Make the cleanup request let (data, response) = try await URLSession.shared.data(for: request) diff --git a/mac/VibeTunnel/Core/Services/SessionMonitor.swift b/mac/VibeTunnel/Core/Services/SessionMonitor.swift index d802884f..c90b8b5e 100644 --- a/mac/VibeTunnel/Core/Services/SessionMonitor.swift +++ b/mac/VibeTunnel/Core/Services/SessionMonitor.swift @@ -29,11 +29,17 @@ final class SessionMonitor { private var lastFetch: Date? private let cacheInterval: TimeInterval = 2.0 private let serverPort: Int + private var localAuthToken: String? private init() { let port = UserDefaults.standard.integer(forKey: "serverPort") self.serverPort = port > 0 ? port : 4_020 } + + /// Set the local auth token for server requests + func setLocalAuthToken(_ token: String?) { + self.localAuthToken = token + } /// Number of running sessions var sessionCount: Int { @@ -69,7 +75,13 @@ final class SessionMonitor { throw URLError(.badURL) } - let request = URLRequest(url: url, timeoutInterval: 3.0) + var request = URLRequest(url: url, timeoutInterval: 3.0) + + // Add local auth token if available + if let token = localAuthToken { + request.setValue(token, forHTTPHeaderField: "X-VibeTunnel-Local") + } + let (data, response) = try await URLSession.shared.data(for: request) guard let httpResponse = response as? HTTPURLResponse, diff --git a/mac/VibeTunnel/Presentation/Views/Settings/DashboardSettingsView.swift b/mac/VibeTunnel/Presentation/Views/Settings/DashboardSettingsView.swift index b006d3dc..7550894b 100644 --- a/mac/VibeTunnel/Presentation/Views/Settings/DashboardSettingsView.swift +++ b/mac/VibeTunnel/Presentation/Views/Settings/DashboardSettingsView.swift @@ -64,6 +64,43 @@ struct DashboardSettingsView: View { restartServerWithNewPort: restartServerWithNewPort, serverManager: serverManager ) + + // Dashboard URL display + VStack(spacing: 4) { + if accessMode == .localhost { + HStack(spacing: 5) { + Text("Dashboard available at") + .font(.caption) + .foregroundStyle(.secondary) + + if let url = URL(string: "http://127.0.0.1:\(serverPort)") { + Link(url.absoluteString, destination: url) + .font(.caption) + .foregroundStyle(.blue) + } + } + } else if accessMode == .network { + if let ip = localIPAddress { + HStack(spacing: 5) { + Text("Dashboard available at") + .font(.caption) + .foregroundStyle(.secondary) + + if let url = URL(string: "http://\(ip):\(serverPort)") { + Link(url.absoluteString, destination: url) + .font(.caption) + .foregroundStyle(.blue) + } + } + } else { + Text("Fetching local IP address...") + .font(.caption) + .foregroundStyle(.secondary) + } + } + } + .frame(maxWidth: .infinity) + .padding(.vertical, 8) NgrokIntegrationSection( ngrokEnabled: $ngrokEnabled, @@ -427,26 +464,6 @@ private struct AccessModeView: View { restartServerWithNewBindAddress() } } - - if accessMode == .network { - if let ip = localIPAddress { - HStack { - Text("Dashboard available at") - .font(.caption) - .foregroundStyle(.secondary) - - if let url = URL(string: "http://\(ip):\(serverPort)") { - Link(url.absoluteString, destination: url) - .font(.caption) - .foregroundStyle(.blue) - } - } - } else { - Text("Fetching local IP address...") - .font(.caption) - .foregroundStyle(.secondary) - } - } } } }