From da824b972846831021257e16da3cef0923088d37 Mon Sep 17 00:00:00 2001 From: Ian Gulliver Date: Tue, 20 Oct 2026 21:47:06 -0700 Subject: [PATCH] Remove tsnet debug logging --- docs/macos.md | 2 +- mac/Sources/Byre/Remote.swift | 2 +- mac/Sources/Byre/Startup.swift | 1 - mac/Sources/Byre/Tailnet.swift | 58 ---------------------------------- 4 files changed, 2 insertions(+), 61 deletions(-) diff --git a/docs/macos.md b/docs/macos.md index d45df1f..08c4e7c 100644 --- a/docs/macos.md +++ b/docs/macos.md @@ -10,7 +10,7 @@ The app joins the tailnet as its own node and reaches byre's HTTPS origin on byr ## Tailnet -`Tailnet.swift` runs one node per app install with libtailscale, through the C API of TailscaleKit (`tailscale_new` and the rest, from the umbrella header): hostname `byre-app-` (the slug as the server's, see "Tailnet" in `docs/operations.md`), not ephemeral, its state in `tsnet` in `run.fc.byre.mac` under Application Support, its log (`tailscale_set_logfd`, a pipe) in the app's log as `tsnet:` lines. It starts at launch (`tailnetNode`, which exits the app if libtailscale can't start) and opens the node's loopback SOCKS5 proxy (`tailscale_loopback`). `API` sets that proxy, with user `tsnet` and the proxy credential, as the `proxyConfigurations` of its `URLSession`, so REST, the SSE streams (`EventStream`), the command channel (`Remote`) and terminals (`Terminal`) all go over the tailnet; only `GET /api/tailnet` to the local service's loopback address uses `URLSession.shared`. `Tailnet` polls the node's status (`tailscale_status_json`) every second, and reads `GET /api/tailnet` at the local service's loopback address (the `loopback` `byre check` prints, found once at launch) every 30 s, or every 2 s while the Server tab or the sign-in screen shows (`serviceWatchers`), since a Mac with no service of its own gets a refused connection each time. Until the app's node is `Running`, the window shows a spinner with "Starting Tailscale…"; while the app's node is `NeedsLogin` or `NeedsMachineAuth`, or the local service's node is `NeedsLogin`, it shows only the sign-in screen instead: a row per node (the app's, and the local service's when one answers), each **Signed in**, or **Sign in to Tailscale**, which opens that node's `AuthURL` in the browser. Servers are added without typing in two ways. When the local service's node runs with an `origin`, that origin is saved as a server (a `ServerRecord`, so iCloud carries it to every Mac on the account). Once the app's node runs, and every 10 s after, the app also takes each online tailnet peer owned by its node's user whose MagicDNS name starts with `byre-` but not `byre-app-`, and saves it when `GET /api/admin/status` answers there within 5 s, probing all of them at once. Either way a forgotten server comes back the next time it is found. Preview forwards dial through the node too (see "Previews" in `docs/operations.md`). +`Tailnet.swift` runs one node per app install with libtailscale, through the C API of TailscaleKit (`tailscale_new` and the rest, from the umbrella header): hostname `byre-app-` (the slug as the server's, see "Tailnet" in `docs/operations.md`), not ephemeral, its state in `tsnet` in `run.fc.byre.mac` under Application Support. It starts at launch (`tailnetNode`, which exits the app if libtailscale can't start) and opens the node's loopback SOCKS5 proxy (`tailscale_loopback`). `API` sets that proxy, with user `tsnet` and the proxy credential, as the `proxyConfigurations` of its `URLSession`, so REST, the SSE streams (`EventStream`), the command channel (`Remote`) and terminals (`Terminal`) all go over the tailnet; only `GET /api/tailnet` to the local service's loopback address uses `URLSession.shared`. `Tailnet` polls the node's status (`tailscale_status_json`) every second, and reads `GET /api/tailnet` at the local service's loopback address (the `loopback` `byre check` prints, found once at launch) every 30 s, or every 2 s while the Server tab or the sign-in screen shows (`serviceWatchers`), since a Mac with no service of its own gets a refused connection each time. Until the app's node is `Running`, the window shows a spinner with "Starting Tailscale…"; while the app's node is `NeedsLogin` or `NeedsMachineAuth`, or the local service's node is `NeedsLogin`, it shows only the sign-in screen instead: a row per node (the app's, and the local service's when one answers), each **Signed in**, or **Sign in to Tailscale**, which opens that node's `AuthURL` in the browser. Servers are added without typing in two ways. When the local service's node runs with an `origin`, that origin is saved as a server (a `ServerRecord`, so iCloud carries it to every Mac on the account). Once the app's node runs, and every 10 s after, the app also takes each online tailnet peer owned by its node's user whose MagicDNS name starts with `byre-` but not `byre-app-`, and saves it when `GET /api/admin/status` answers there within 5 s, probing all of them at once. Either way a forgotten server comes back the next time it is found. Preview forwards dial through the node too (see "Previews" in `docs/operations.md`). `mac/bundle.sh` builds TailscaleKit from `github.com/tailscale/libtailscale` at the commit pinned in the script (`libtailscale=`): it clones it into `~/Library/Caches/byre/build/libtailscale-`, runs its `make -C swift macos` (Go's `c-archive` build, then `xcodebuild` of the framework, unsigned) and wraps the framework with `xcodebuild -create-xcframework`, once per commit, under the build lock. It copies that into the shared build directory's `mac/Frameworks/TailscaleKit.xcframework`, which `mac/Package.swift` names as a binary target, and bundles and signs the framework in `Contents/Frameworks` like Sparkle's. To move to a newer libtailscale, change the pinned commit. diff --git a/mac/Sources/Byre/Remote.swift b/mac/Sources/Byre/Remote.swift index 3b30cde..ec62722 100644 --- a/mac/Sources/Byre/Remote.swift +++ b/mac/Sources/Byre/Remote.swift @@ -115,7 +115,7 @@ final class Remote { case .success: self.receive(task) case .failure(let error): - log.notice("client connection closed:\(errorText(error), privacy: .public); \(tailnetNode.peerState(self.api.origin.host()!), privacy: .public)") + log.notice("client connection closed:\(errorText(error), privacy: .public)") self.ws = nil try? await Task.sleep(for: retryAfter) self.start() diff --git a/mac/Sources/Byre/Startup.swift b/mac/Sources/Byre/Startup.swift index 3c054f3..52a4144 100644 --- a/mac/Sources/Byre/Startup.swift +++ b/mac/Sources/Byre/Startup.swift @@ -253,7 +253,6 @@ final class Connection { Notifier.shared.deliver() return true } catch { - log.notice("connect \(self.origin.absoluteString, privacy: .public) failed: \(errorText(error), privacy: .public); \(tailnetNode.peerState(self.origin.host()!), privacy: .public)") await store.stop() if !stopped { phase = .failed(explain(error, origin)) diff --git a/mac/Sources/Byre/Tailnet.swift b/mac/Sources/Byre/Tailnet.swift index 160cecf..2dd76c7 100644 --- a/mac/Sources/Byre/Tailnet.swift +++ b/mac/Sources/Byre/Tailnet.swift @@ -40,7 +40,6 @@ final class TailnetNode: Sendable { Self.check(sd, "set hostname", unsafe tailscale_set_hostname(sd, hostname)) Self.check(sd, "set dir", unsafe tailscale_set_dir(sd, dir.path)) Self.check(sd, "set ephemeral", tailscale_set_ephemeral(sd, 0)) - Self.check(sd, "set logfd", tailscale_set_logfd(sd, Self.logPipe())) Self.check(sd, "start", tailscale_start(sd)) var addr = [CChar](repeating: 0, count: 64) var proxyCred = [CChar](repeating: 0, count: 33) @@ -61,49 +60,6 @@ final class TailnetNode: Sendable { } } - private static let logs = Pipe() - - private static func logPipe() -> Int32 { - let lines = LineBuffer() - logs.fileHandleForReading.readabilityHandler = { handle in - for line in lines.add(handle.availableData) { - log.notice("tsnet: \(line, privacy: .public)") - } - } - return dup(logs.fileHandleForWriting.fileDescriptor) - } - - func peerState(_ host: String) -> String { - var out: UnsafeMutablePointer? - guard unsafe tailscale_status_json(sd, &out) == 0, let out = unsafe out else { - return "no status: \(Self.errorMessage(sd))" - } - defer { - unsafe free(out) - } - struct Peer: Decodable { - let DNSName: String - let Online: Bool - let InNetworkMap: Bool - let InMagicSock: Bool - let InEngine: Bool - let LastHandshake: String - let CurAddr: String - let Relay: String - } - struct Status: Decodable { - let BackendState: String - let Peer: [String: Peer]? - } - guard let status = try? JSONDecoder().decode(Status.self, from: unsafe Data(bytes: out, count: strlen(out))) else { - return "undecodable status" - } - guard let p = (status.Peer ?? [:]).values.first(where: { $0.DNSName == host + "." || $0.DNSName == host }) else { - return "state=\(status.BackendState) peer \(host) not in status" - } - return "state=\(status.BackendState) online=\(p.Online) netmap=\(p.InNetworkMap) magicsock=\(p.InMagicSock) engine=\(p.InEngine) handshake=\(p.LastHandshake) addr=\(p.CurAddr) relay=\(p.Relay)" - } - static func errorMessage(_ sd: tailscale) -> String { var buf = [CChar](repeating: 0, count: 1024) _ = unsafe tailscale_errmsg(sd, &buf, buf.count) @@ -167,20 +123,6 @@ final class TailnetNode: Sendable { } } -private final class LineBuffer: @unchecked Sendable { - private var pending = Data() - - func add(_ data: Data) -> [String] { - pending.append(data) - var lines: [String] = [] - while let nl = pending.firstIndex(of: 0x0A) { - lines.append(String(decoding: pending[pending.startIndex..