diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f198c937..ebefb9cb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -178,18 +178,29 @@ jobs: - name: Generate the project and reject drift run: scripts/check_xcode_project.sh - - name: Check bounded notarization retries + - name: Check bounded notarization retries and warning gate if: matrix.xcode == 'newest' - run: scripts/test-notarize.sh + run: | + scripts/test-notarize.sh + scripts/test-swift-warnings.sh # This compiles a different product graph and activation path from the Developer ID build. The build clears # protected entitlements because CI has no Store profiles; the next step re-signs the isolated product with the # repository plists so the product gate still inspects the actual executable entitlements it is meant to police. - name: Build the Mac App Store product run: | + set +e + LOG="$RUNNER_TEMP/store-build.log" xcodebuild build -project Flowlight.xcodeproj -scheme "Flowlight App Store" \ -configuration AppStore -destination 'generic/platform=macOS' -derivedDataPath "$RUNNER_TEMP/store-build" \ - CODE_SIGN_IDENTITY=- CODE_SIGN_STYLE=Manual DEVELOPMENT_TEAM= CODE_SIGN_ENTITLEMENTS= + CODE_SIGN_IDENTITY=- CODE_SIGN_STYLE=Manual DEVELOPMENT_TEAM= CODE_SIGN_ENTITLEMENTS= > "$LOG" 2>&1 + status=$? + set -e + grep -E "warning:|error:|\\*\\* BUILD" "$LOG" | tail -80 || true + if [[ "$status" -ne 0 ]]; then + exit "$status" + fi + scripts/check-swift-warnings.sh "$LOG" - name: Check the Mac App Store product structure and signed entitlements run: | diff --git a/Flowlight.xcodeproj/project.pbxproj b/Flowlight.xcodeproj/project.pbxproj index e1130cea..efbdce26 100644 --- a/Flowlight.xcodeproj/project.pbxproj +++ b/Flowlight.xcodeproj/project.pbxproj @@ -2145,6 +2145,7 @@ PRODUCT_NAME = Flowlight; SDKROOT = macosx; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) APP_STORE"; + SWIFT_TREAT_WARNINGS_AS_ERRORS = YES; }; name = AppStore; }; @@ -2183,7 +2184,7 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 55; + CURRENT_PROJECT_VERSION = 56; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = ""; ENABLE_HARDENED_RUNTIME = YES; @@ -2198,7 +2199,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; MACOSX_DEPLOYMENT_TARGET = 15.0; - MARKETING_VERSION = 0.15.8; + MARKETING_VERSION = 0.15.9; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -2244,7 +2245,7 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 55; + CURRENT_PROJECT_VERSION = 56; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 38RJUJHKZS; ENABLE_HARDENED_RUNTIME = YES; @@ -2259,7 +2260,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; MACOSX_DEPLOYMENT_TARGET = 15.0; - MARKETING_VERSION = 0.15.8; + MARKETING_VERSION = 0.15.9; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -2324,7 +2325,7 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 55; + CURRENT_PROJECT_VERSION = 56; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = ""; ENABLE_HARDENED_RUNTIME = YES; @@ -2345,7 +2346,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; MACOSX_DEPLOYMENT_TARGET = 15.0; - MARKETING_VERSION = 0.15.8; + MARKETING_VERSION = 0.15.9; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; @@ -2410,6 +2411,7 @@ SDKROOT = macosx; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) APP_STORE"; + SWIFT_TREAT_WARNINGS_AS_ERRORS = YES; }; name = Release; }; @@ -2465,6 +2467,7 @@ PRODUCT_NAME = Flowlight; SDKROOT = macosx; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) APP_STORE"; + SWIFT_TREAT_WARNINGS_AS_ERRORS = YES; }; name = Release; }; @@ -2485,6 +2488,7 @@ SDKROOT = macosx; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) APP_STORE"; + SWIFT_TREAT_WARNINGS_AS_ERRORS = YES; }; name = Debug; }; @@ -2571,6 +2575,7 @@ SDKROOT = macosx; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) APP_STORE"; + SWIFT_TREAT_WARNINGS_AS_ERRORS = YES; }; name = AppStore; }; @@ -2590,6 +2595,7 @@ PRODUCT_NAME = Flowlight; SDKROOT = macosx; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) APP_STORE"; + SWIFT_TREAT_WARNINGS_AS_ERRORS = YES; }; name = Debug; }; diff --git a/Flowlight/Analysis/RulePackEventDelivery.swift b/Flowlight/Analysis/RulePackEventDelivery.swift index 2ea2350e..41b94b05 100644 --- a/Flowlight/Analysis/RulePackEventDelivery.swift +++ b/Flowlight/Analysis/RulePackEventDelivery.swift @@ -104,7 +104,8 @@ final class RulePackEventDelivery: @unchecked Sendable { item.attemptID = thisAttempt item.lease = lease pending[deliveryID] = item - sink(item.batch, lease.claim) { [weak self] committed in + let claim: Claim = { lease.claim() } + sink(item.batch, claim) { [weak self] committed in self?.queue.async { self?.finished(committed: committed, deliveryID: deliveryID, attemptID: thisAttempt) } diff --git a/Flowlight/Analysis/RuleStore.swift b/Flowlight/Analysis/RuleStore.swift index 4bed65b1..c4cae245 100644 --- a/Flowlight/Analysis/RuleStore.swift +++ b/Flowlight/Analysis/RuleStore.swift @@ -9,7 +9,7 @@ import Foundation final class RuleStore: ObservableObject { /// This run of Flowlight. A rule that lasts "until I quit" is measured against it, so nothing has to be swept /// at quit — which a crash or a forced restart would skip anyway. - static let session = UUID().uuidString + nonisolated static let session = UUID().uuidString @Published private(set) var rules: [Rule] = [] @Published private(set) var events: [RuleEventRecord] = [] diff --git a/Flowlight/App/DemoInspection.swift b/Flowlight/App/DemoInspection.swift index 7f681a43..28c86ef2 100644 --- a/Flowlight/App/DemoInspection.swift +++ b/Flowlight/App/DemoInspection.swift @@ -35,7 +35,7 @@ extension DemoData { HTTPHeader(name: "Host", value: host), HTTPHeader(name: "Content-Type", value: "application/json"), HTTPHeader(name: "Content-Length", value: "\(req.count)"), ] : requestHeaders - try db.insertExchange(HTTPExchange( + _ = try db.insertExchange(HTTPExchange( id: nil, started: clock, duration: duration, scheme: "https", host: host, port: 443, method: method, path: path, status: status, requestHeaders: HeaderRedaction.redact(headers), requestBody: req, requestSize: req.count, requestTruncated: false, responseHeaders: [HTTPHeader(name: "Content-Type", value: contentType), HTTPHeader(name: "Date", value: "demo")], diff --git a/Flowlight/Ask/AskQueryRunner.swift b/Flowlight/Ask/AskQueryRunner.swift index 8965e584..0dd04cb1 100644 --- a/Flowlight/Ask/AskQueryRunner.swift +++ b/Flowlight/Ask/AskQueryRunner.swift @@ -148,7 +148,7 @@ enum AskQueryRunner { "apps": Int64(Set(matching.map(\.bundleID)).count)]), summary: who.map { L("%@ in, %@ out for %@", received, sent, $0) } ?? L("%@ in, %@ out", received, sent), - filter: app.map { TrafficFilter(bundleID: bundleID(of: $0, in: rows)) } ?? .none, + filter: app.map { TrafficFilter(bundleID: bundleID(of: $0, in: rows)) } ?? TrafficFilter.none, from: window.from, to: window.to, chart: AskChart(kind: kind, title: who.map { L("Received and sent for %@", $0) } ?? L("Received and sent"), @@ -174,7 +174,7 @@ enum AskQueryRunner { return Result(json: encode(payload), summary: top.isEmpty ? L("nothing moved") : top.map { "\($0.value.name) \(ByteFormat.string($0.value.counters.total))" }.joined(separator: ", "), - filter: .none, from: window.from, to: window.to, + filter: TrafficFilter.none, from: window.from, to: window.to, chart: AskChart(kind: AskChart.kind(requested: chart, natural: points.isEmpty ? .none : .bar), title: L("Busiest apps"), unit: .bytes, points: AskChart.trimmed(points, kind: .bar))) @@ -201,7 +201,7 @@ enum AskQueryRunner { let points = top.map { AskChart.Point(label: $0.key, value: Double($0.value.counters.total)) } return Result(json: encode(payload), summary: top.isEmpty ? L("no destinations") : top.prefix(3).map(\.key).joined(separator: ", "), - filter: app.map { TrafficFilter(bundleID: bundleID(of: $0, in: rows)) } ?? .none, + filter: app.map { TrafficFilter(bundleID: bundleID(of: $0, in: rows)) } ?? TrafficFilter.none, from: window.from, to: window.to, chart: AskChart(kind: kind, title: L("Busiest destinations"), unit: .bytes, points: AskChart.trimmed(points, kind: kind), primaryName: L("Total"))) @@ -229,7 +229,7 @@ enum AskQueryRunner { return Result(json: encode(payload), summary: top.isEmpty ? L("nothing new") : top.prefix(3).map { "\($0.app) → \($0.destination)" }.joined(separator: ", "), - filter: app.map { TrafficFilter(bundleID: bundleID(of: $0, in: inWindow)) } ?? .none, + filter: app.map { TrafficFilter(bundleID: bundleID(of: $0, in: inWindow)) } ?? TrafficFilter.none, from: window.from, to: window.to) } @@ -244,7 +244,7 @@ enum AskQueryRunner { return Result(json: encode(payload), summary: inWindow.isEmpty ? L("no alerts") : L("%lld alerts, newest %@", inWindow.count, inWindow.first?.kind ?? ""), - filter: .none, from: window.from, to: window.to) + filter: TrafficFilter.none, from: window.from, to: window.to) } private static func agents(db: TrafficDatabase, window: (from: Date, to: Date), grain: Granularity) throws -> Result { @@ -273,7 +273,7 @@ enum AskQueryRunner { } return Result(json: encode(payload), summary: payload.isEmpty ? L("no agents") : payload.compactMap { $0["agent"] }.joined(separator: ", "), - filter: .none, from: window.from, to: window.to) + filter: TrafficFilter.none, from: window.from, to: window.to) } private static func overTime(db: TrafficDatabase, window: (from: Date, to: Date), app: String?, diff --git a/Flowlight/Inspection/InspectionController.swift b/Flowlight/Inspection/InspectionController.swift index 4a2130fa..47734054 100644 --- a/Flowlight/Inspection/InspectionController.swift +++ b/Flowlight/Inspection/InspectionController.swift @@ -306,13 +306,13 @@ final class InspectionController: ObservableObject { let (scope, _) = scopeAndList() // A mocked exchange is always kept: an answer Flowlight invented has to be visible wherever it lands. guard scope == .all || exchange.agent != nil || exchange.note != nil || exchange.mockRule != nil || exchange.responseTransform != nil else { return } - guard let self else { return } - Task { @MainActor in + Task { @MainActor [weak self] in // Capture is committed first. The database transaction optionally creates a tiny derived candidate; // the wake below schedules later work and never waits for model inference on the recorder path. - self.db?.async { [weak self] db in + guard let db = self?.db else { return } + db.async { [weak self] db in _ = try db.insertExchange(exchange, enqueueLocalRisk: LocalRiskSettings.enabled) - Task { @MainActor in + Task { @MainActor [weak self] in guard let self else { return } self.recordedCount += 1 self.onRecorded() diff --git a/Flowlight/Localization/Localization.swift b/Flowlight/Localization/Localization.swift index 78d46c0a..db37ced0 100644 --- a/Flowlight/Localization/Localization.swift +++ b/Flowlight/Localization/Localization.swift @@ -140,7 +140,7 @@ final class Localization: ObservableObject { enum LanguageBundle { nonisolated(unsafe) static var current: Bundle = .main /// English, for the fallback below. Resolved once: a bundle lookup per string would be wasteful. - nonisolated(unsafe) static let english: Bundle? = + nonisolated static let english: Bundle? = Bundle.main.path(forResource: "en", ofType: "lproj").flatMap(Bundle.init(path:)) static func string(_ key: String) -> String { diff --git a/Flowlight/Plugins/PluginPackageVerifier.swift b/Flowlight/Plugins/PluginPackageVerifier.swift index 6f4e0924..8472d538 100644 --- a/Flowlight/Plugins/PluginPackageVerifier.swift +++ b/Flowlight/Plugins/PluginPackageVerifier.swift @@ -154,7 +154,7 @@ enum PluginPackageVerifier { private static func verify(_ signature: PluginSignature, digest: String, signer: PluginTrustedSigner) throws { guard let signatureData = Data(base64Encoded: signature.value), - let publicKey = publicKey(fromPEM: signer.publicKeyPEM), + let publicKey = publicKey(from: signer.publicKeyPEM), let parsed = try? P256.Signing.ECDSASignature(derRepresentation: signatureData) else { throw PluginPackageError.invalidSignature } @@ -163,10 +163,13 @@ enum PluginPackageVerifier { } } - private static func publicKey(fromPEM pem: String) -> P256.Signing.PublicKey? { + private static func publicKey(from pem: String) -> P256.Signing.PublicKey? { + if pem.contains("-----BEGIN ") { + return try? P256.Signing.PublicKey(pemRepresentation: pem) + } let lines = pem.split(whereSeparator: \.isNewline).map(String.init).filter { !$0.hasPrefix("-----") } guard let data = Data(base64Encoded: lines.joined()) else { return nil } - return try? P256.Signing.PublicKey(pemRepresentation: pem) ?? (try? P256.Signing.PublicKey(derRepresentation: data)) + return try? P256.Signing.PublicKey(derRepresentation: data) } private static func signatureMessage(digest: String, keyID: String) -> Data { diff --git a/FlowlightTests/AskTests.swift b/FlowlightTests/AskTests.swift index f43c3aa4..574ef31a 100644 --- a/FlowlightTests/AskTests.swift +++ b/FlowlightTests/AskTests.swift @@ -146,6 +146,17 @@ final class AskTests: XCTestCase { XCTAssertEqual(rows.first?["app"], "claude") } + func testUnscopedTrafficAnswersKeepAnEmptyReportsFilter() throws { + try seed() + for query in [AskQuery.trafficTotals, .topApps, .topDestinations, .newDestinations, .alerts, .agents] { + let result = try AskQueryRunner.run( + AskCall(query: query, arguments: ["from": window.from, "to": window.to]), db: db + ) + XCTAssertNotNil(result.filter, "\(query.rawValue) should remain openable in Reports") + XCTAssertTrue(result.filter?.isEmpty == true, "\(query.rawValue) should show every row in its window") + } + } + func testTopDestinationsNamesWhereItWent() throws { try seed() let result = try AskQueryRunner.run(AskCall(query: .topDestinations, arguments: ["from": window.from, "to": window.to]), db: db) diff --git a/FlowlightTests/PluginTests.swift b/FlowlightTests/PluginTests.swift index 534a8010..ef60aebe 100644 --- a/FlowlightTests/PluginTests.swift +++ b/FlowlightTests/PluginTests.swift @@ -146,6 +146,43 @@ final class PluginTests: XCTestCase { XCTAssertEqual(review.manifest.provenance.approvalFingerprint, review.approvalFingerprint) } + func testSignedCommunityPackageAcceptsBase64DERPublicKey() throws { + var manifest = PluginManifest(id: "thirdparty.der-key", name: "DER key", version: "0.1.0", kind: .traffic, + source: .installed, publisher: .thirdParty, guardrailProvider: .custom, + description: "Signed plugin.", privacySummary: "Uses metadata only.", + privacy: PluginPrivacyDisclosure(categories: [.metadataOnly], summary: "Metadata only.")) + manifest.script = "function evaluate(context) { return []; }" + let privateKey = P256.Signing.PrivateKey() + let signer = PluginTrustedSigner( + keyID: "test-der-v1", publisher: .thirdParty, publisherName: "Test DER", + channel: .community, publicKeyPEM: privateKey.publicKey.derRepresentation.base64EncodedString() + ) + let package = try PluginPackageVerifier.signedPackage( + manifest: manifest, keyID: signer.keyID, privateKey: privateKey + ) + let review = try PluginPackageVerifier.review(data: JSONEncoder().encode(package), trustedSigners: [signer]) + XCTAssertEqual(review.signatureStatus, .verifiedCommunity) + } + + func testSignedPackageRejectsMalformedTrustedKey() throws { + var manifest = PluginManifest(id: "thirdparty.bad-key", name: "Bad key", version: "0.1.0", kind: .traffic, + source: .installed, publisher: .thirdParty, guardrailProvider: .custom, + description: "Signed plugin.", privacySummary: "Uses metadata only.", + privacy: PluginPrivacyDisclosure(categories: [.metadataOnly], summary: "Metadata only.")) + manifest.script = "function evaluate(context) { return []; }" + let privateKey = P256.Signing.PrivateKey() + let signer = PluginTrustedSigner(keyID: "test-bad-v1", publisher: .thirdParty, publisherName: "Bad Key", + channel: .community, publicKeyPEM: "not a key") + let package = try PluginPackageVerifier.signedPackage( + manifest: manifest, keyID: signer.keyID, privateKey: privateKey + ) + XCTAssertThrowsError( + try PluginPackageVerifier.review(data: JSONEncoder().encode(package), trustedSigners: [signer]) + ) { error in + XCTAssertEqual(error as? PluginPackageError, .invalidSignature) + } + } + func testSignedPackageRejectsTamperedManifest() throws { var manifest = PluginManifest(id: "thirdparty.tamper", name: "Tamper", version: "0.1.0", kind: .traffic, source: .installed, publisher: .thirdParty, guardrailProvider: .custom, diff --git a/FlowlightTests/RulePackEventDeliveryTests.swift b/FlowlightTests/RulePackEventDeliveryTests.swift index 7dbcdc10..85772f99 100644 --- a/FlowlightTests/RulePackEventDeliveryTests.swift +++ b/FlowlightTests/RulePackEventDeliveryTests.swift @@ -27,6 +27,22 @@ final class RulePackEventDeliveryTests: XCTestCase { XCTAssertEqual(captured[0].events, captured[1].events) } + func testClaimCanCrossAQueueBoundaryAndCommit() { + let committed = expectation(description: "cross-queue claim committed") + let delivery = RulePackEventDelivery(retryDelay: 0.01, attemptTimeout: 1) { _, claim, completion in + DispatchQueue.global().async { + XCTAssertTrue(claim()) + completion(true) + } + } + + delivery.enqueue(event()) { result in + XCTAssertTrue(result) + committed.fulfill() + } + wait(for: [committed], timeout: 2) + } + func testTwoFailedAttemptsFailOpen() { let attempts = LockedValue(0) let allowed = expectation(description: "refusal abandoned") diff --git a/FlowlightTests/RuleTemplateTests.swift b/FlowlightTests/RuleTemplateTests.swift index aa2c2fac..1f10dd71 100644 --- a/FlowlightTests/RuleTemplateTests.swift +++ b/FlowlightTests/RuleTemplateTests.swift @@ -406,6 +406,16 @@ final class RuleTemplateTests: XCTestCase { .allow) } + func testAnAppSessionBlockUsesTheCurrentRunAndExpiresInTheNext() { + guard let template = RuleTemplate.all.first(where: { $0.id == "investigate.app-everywhere-this-session" }), + let rule = template.rules(for: subject).first else { + return XCTFail("the app session template is gone") + } + XCTAssertEqual(rule.schedule.session, RuleStore.session) + XCTAssertEqual(RuleBook.decide(facts(host: "example.com"), rules: [rule], session: RuleStore.session).verdict, .block) + XCTAssertEqual(RuleBook.decide(facts(host: "example.com"), rules: [rule], session: "a-later-run").verdict, .allow) + } + /// The narrow investigation rule has to lose to nothing and bite nothing else: it is the one someone reaches /// for precisely because they don't want to disturb the rest of the Mac. func testBlockingOneAppFromOneDestinationLeavesEveryoneElseAlone() { diff --git a/docs/404.html b/docs/404.html index b9c0e998..bb39da11 100644 --- a/docs/404.html +++ b/docs/404.html @@ -86,7 +86,7 @@

That page isn't here

Try the home page,

diff --git a/docs/about/index.html b/docs/about/index.html index 92908690..857dee54 100644 --- a/docs/about/index.html +++ b/docs/about/index.html @@ -48,7 +48,7 @@ - + @@ -153,7 +153,7 @@

Thanks

diff --git a/docs/de/about/index.html b/docs/de/about/index.html index 0a10282d..d2a9cca4 100644 --- a/docs/de/about/index.html +++ b/docs/de/about/index.html @@ -48,7 +48,7 @@ - + @@ -152,7 +152,7 @@

Dank

diff --git a/docs/de/docs/index.html b/docs/de/docs/index.html index 0578cbee..a0e329c3 100644 --- a/docs/de/docs/index.html +++ b/docs/de/docs/index.html @@ -48,7 +48,7 @@ - + @@ -76,7 +76,7 @@

Dokumentation

Flowlight benutzen

-

Alles vom ersten Start bis zum Feinschliff an den Agentenregeln. Flowlight 0.15.8, macOS 15 oder neuer.

+

Alles vom ersten Start bis zum Feinschliff an den Agentenregeln. Flowlight 0.15.9, macOS 15 oder neuer.

diff --git a/docs/de/index.html b/docs/de/index.html index c048a255..02ea1621 100644 --- a/docs/de/index.html +++ b/docs/de/index.html @@ -48,7 +48,7 @@ - + @@ -90,7 +90,7 @@

Sieh, was deine Apps im Netz tun.
brew install --cask xinbetween/tap/flowlight

-

v0.15.8macOS 15+UniversalGPL-3.0Keine Telemetrie

+

v0.15.9macOS 15+UniversalGPL-3.0Keine Telemetrie

@@ -572,7 +572,7 @@

Wisse, was deinen Mac verlässt.

diff --git a/docs/de/privacy/index.html b/docs/de/privacy/index.html index c4315723..9ac4332d 100644 --- a/docs/de/privacy/index.html +++ b/docs/de/privacy/index.html @@ -48,7 +48,7 @@ - + @@ -189,7 +189,7 @@

Kontakt

diff --git a/docs/de/threat-model/index.html b/docs/de/threat-model/index.html index 2a303d52..dac47be2 100644 --- a/docs/de/threat-model/index.html +++ b/docs/de/threat-model/index.html @@ -48,7 +48,7 @@ - + @@ -225,7 +225,7 @@

Eine Schwachstelle melden

diff --git a/docs/docs/index.html b/docs/docs/index.html index 1f43bcec..35ac715a 100644 --- a/docs/docs/index.html +++ b/docs/docs/index.html @@ -48,7 +48,7 @@ - + @@ -76,7 +76,7 @@

Documentation

Using Flowlight

-

Everything from the first launch to tuning the agent rules. Flowlight 0.15.8, macOS 15 or later.

+

Everything from the first launch to tuning the agent rules. Flowlight 0.15.9, macOS 15 or later.

diff --git a/docs/es/about/index.html b/docs/es/about/index.html index e39ab351..d361e4c0 100644 --- a/docs/es/about/index.html +++ b/docs/es/about/index.html @@ -48,7 +48,7 @@ - + @@ -152,7 +152,7 @@

Agradecimientos

diff --git a/docs/es/docs/index.html b/docs/es/docs/index.html index 3f22d46a..2bcf1a14 100644 --- a/docs/es/docs/index.html +++ b/docs/es/docs/index.html @@ -48,7 +48,7 @@ - + @@ -76,7 +76,7 @@

Documentación

Usar Flowlight

-

Todo, desde el primer arranque hasta el ajuste de las reglas de agentes. Flowlight 0.15.8, macOS 15 o posterior.

+

Todo, desde el primer arranque hasta el ajuste de las reglas de agentes. Flowlight 0.15.9, macOS 15 o posterior.

diff --git a/docs/es/index.html b/docs/es/index.html index 0a82d7bb..9a5c0191 100644 --- a/docs/es/index.html +++ b/docs/es/index.html @@ -48,7 +48,7 @@ - + @@ -90,7 +90,7 @@

Observa la actividad de red de tus apps.

brew install --cask xinbetween/tap/flowlight

-

v0.15.8macOS 15+UniversalGPL-3.0Sin telemetría

+

v0.15.9macOS 15+UniversalGPL-3.0Sin telemetría

@@ -572,7 +572,7 @@

Ten claro qué sale de tu Mac.

diff --git a/docs/es/privacy/index.html b/docs/es/privacy/index.html index 644daa52..8e591cf5 100644 --- a/docs/es/privacy/index.html +++ b/docs/es/privacy/index.html @@ -48,7 +48,7 @@ - + @@ -189,7 +189,7 @@

Contacto

diff --git a/docs/es/threat-model/index.html b/docs/es/threat-model/index.html index 956828b6..0d907231 100644 --- a/docs/es/threat-model/index.html +++ b/docs/es/threat-model/index.html @@ -48,7 +48,7 @@ - + @@ -221,7 +221,7 @@

Informar de una vulnerabilidad

diff --git a/docs/fr/about/index.html b/docs/fr/about/index.html index 08169d0e..b800ae8b 100644 --- a/docs/fr/about/index.html +++ b/docs/fr/about/index.html @@ -48,7 +48,7 @@ - + @@ -152,7 +152,7 @@

Remerciements

diff --git a/docs/fr/docs/index.html b/docs/fr/docs/index.html index c251c31e..20fa511d 100644 --- a/docs/fr/docs/index.html +++ b/docs/fr/docs/index.html @@ -48,7 +48,7 @@ - + @@ -76,7 +76,7 @@

Documentation

Utiliser Flowlight

-

Tout, du premier lancement au réglage des règles des agents. Flowlight 0.15.8, macOS 15 ou version ultérieure.

+

Tout, du premier lancement au réglage des règles des agents. Flowlight 0.15.9, macOS 15 ou version ultérieure.

diff --git a/docs/fr/index.html b/docs/fr/index.html index 170d2f7c..3ba9265a 100644 --- a/docs/fr/index.html +++ b/docs/fr/index.html @@ -48,7 +48,7 @@ - + @@ -90,7 +90,7 @@

Voyez l’activité réseau de vos apps.

brew install --cask xinbetween/tap/flowlight

-

v0.15.8macOS 15+UniverselGPL-3.0Sans télémétrie

+

v0.15.9macOS 15+UniverselGPL-3.0Sans télémétrie

@@ -572,7 +572,7 @@

Sachez ce qui quitte votre Mac.

diff --git a/docs/fr/privacy/index.html b/docs/fr/privacy/index.html index c87961fc..5b33f81b 100644 --- a/docs/fr/privacy/index.html +++ b/docs/fr/privacy/index.html @@ -48,7 +48,7 @@ - + @@ -189,7 +189,7 @@

Contact

diff --git a/docs/fr/threat-model/index.html b/docs/fr/threat-model/index.html index 1d8332ef..a92ca8f7 100644 --- a/docs/fr/threat-model/index.html +++ b/docs/fr/threat-model/index.html @@ -48,7 +48,7 @@ - + @@ -224,7 +224,7 @@

Signaler une vulnérabilité

diff --git a/docs/index.html b/docs/index.html index 2578e368..9030c8e3 100644 --- a/docs/index.html +++ b/docs/index.html @@ -48,7 +48,7 @@ - + @@ -90,7 +90,7 @@

See your apps' network activity.
brew install --cask xinbetween/tap/flowlight

-

v0.15.8macOS 15+UniversalGPL-3.0No telemetry

+

v0.15.9macOS 15+UniversalGPL-3.0No telemetry

@@ -594,7 +594,7 @@

Know what leaves your Mac.

diff --git a/docs/it/about/index.html b/docs/it/about/index.html index 1d15c808..f6dd591a 100644 --- a/docs/it/about/index.html +++ b/docs/it/about/index.html @@ -48,7 +48,7 @@ - + @@ -152,7 +152,7 @@

Ringraziamenti

diff --git a/docs/it/docs/index.html b/docs/it/docs/index.html index 68d10346..d6485672 100644 --- a/docs/it/docs/index.html +++ b/docs/it/docs/index.html @@ -48,7 +48,7 @@ - + @@ -76,7 +76,7 @@

Documentazione

Usare Flowlight

-

Tutto, dal primo avvio alla messa a punto delle regole per gli agenti. Flowlight 0.15.8, macOS 15 o successivo.

+

Tutto, dal primo avvio alla messa a punto delle regole per gli agenti. Flowlight 0.15.9, macOS 15 o successivo.

diff --git a/docs/it/index.html b/docs/it/index.html index 13fbf7d9..ae8403d3 100644 --- a/docs/it/index.html +++ b/docs/it/index.html @@ -48,7 +48,7 @@ - + @@ -90,7 +90,7 @@

Vedi la rete delle tue app.
Ca Metti una stella su GitHub

brew install --cask xinbetween/tap/flowlight

-

v0.15.8macOS 15+UniversaleGPL-3.0Nessuna telemetria

+

v0.15.9macOS 15+UniversaleGPL-3.0Nessuna telemetria

@@ -572,7 +572,7 @@

Sappi che cosa esce dal tuo Mac.

diff --git a/docs/it/privacy/index.html b/docs/it/privacy/index.html index eeb23e6c..e8270be3 100644 --- a/docs/it/privacy/index.html +++ b/docs/it/privacy/index.html @@ -48,7 +48,7 @@ - + @@ -189,7 +189,7 @@

Contatti

diff --git a/docs/it/threat-model/index.html b/docs/it/threat-model/index.html index f9d8d952..8847cd85 100644 --- a/docs/it/threat-model/index.html +++ b/docs/it/threat-model/index.html @@ -48,7 +48,7 @@ - + @@ -223,7 +223,7 @@

Segnalare una vulnerabilità

diff --git a/docs/ja/about/index.html b/docs/ja/about/index.html index a28beb19..aaf4cdd2 100644 --- a/docs/ja/about/index.html +++ b/docs/ja/about/index.html @@ -48,7 +48,7 @@ - + @@ -153,7 +153,7 @@

謝辞

diff --git a/docs/ja/docs/index.html b/docs/ja/docs/index.html index d396d313..a8202299 100644 --- a/docs/ja/docs/index.html +++ b/docs/ja/docs/index.html @@ -48,7 +48,7 @@ - + @@ -76,7 +76,7 @@

ドキュメント

Flowlight の使い方

-

初回起動からエージェントのルールの調整まで、すべてここに。Flowlight 0.15.8、macOS 15 以降。

+

初回起動からエージェントのルールの調整まで、すべてここに。Flowlight 0.15.9、macOS 15 以降。

diff --git a/docs/ja/index.html b/docs/ja/index.html index db83f7c0..19c0553f 100644 --- a/docs/ja/index.html +++ b/docs/ja/index.html @@ -48,7 +48,7 @@ - + @@ -90,7 +90,7 @@

アプリの通信が見える。
brew install --cask xinbetween/tap/flowlight

-

v0.15.8macOS 15+ユニバーサルGPL-3.0テレメトリなし

+

v0.15.9macOS 15+ユニバーサルGPL-3.0テレメトリなし

@@ -571,7 +571,7 @@

Mac から何が出ていくのかを知る。

diff --git a/docs/ja/privacy/index.html b/docs/ja/privacy/index.html index c0d5795f..c4ad2698 100644 --- a/docs/ja/privacy/index.html +++ b/docs/ja/privacy/index.html @@ -48,7 +48,7 @@ - + @@ -187,7 +187,7 @@

連絡先

diff --git a/docs/ja/threat-model/index.html b/docs/ja/threat-model/index.html index 28106874..056d33a0 100644 --- a/docs/ja/threat-model/index.html +++ b/docs/ja/threat-model/index.html @@ -48,7 +48,7 @@ - + @@ -222,7 +222,7 @@

脆弱性を報告する

diff --git a/docs/ko/about/index.html b/docs/ko/about/index.html index 3521a808..47df6bef 100644 --- a/docs/ko/about/index.html +++ b/docs/ko/about/index.html @@ -48,7 +48,7 @@ - + @@ -153,7 +153,7 @@

감사

diff --git a/docs/ko/docs/index.html b/docs/ko/docs/index.html index 519d1053..9aea60b9 100644 --- a/docs/ko/docs/index.html +++ b/docs/ko/docs/index.html @@ -48,7 +48,7 @@ - + @@ -76,7 +76,7 @@

문서

Flowlight 사용하기

-

첫 실행부터 에이전트 규칙 조정까지 전부. Flowlight 0.15.8, macOS 15 이상.

+

첫 실행부터 에이전트 규칙 조정까지 전부. Flowlight 0.15.9, macOS 15 이상.

diff --git a/docs/ko/index.html b/docs/ko/index.html index 0b7c6a3b..4235d1e1 100644 --- a/docs/ko/index.html +++ b/docs/ko/index.html @@ -48,7 +48,7 @@ - + @@ -90,7 +90,7 @@

앱이 무엇을 하는지 봅니다.

brew install --cask xinbetween/tap/flowlight

-

v0.15.8macOS 15+유니버설GPL-3.0텔레메트리 없음

+

v0.15.9macOS 15+유니버설GPL-3.0텔레메트리 없음

@@ -572,7 +572,7 @@

내 Mac에서 무엇이 나가는지 아세요.

diff --git a/docs/ko/privacy/index.html b/docs/ko/privacy/index.html index b73b094b..257a5908 100644 --- a/docs/ko/privacy/index.html +++ b/docs/ko/privacy/index.html @@ -48,7 +48,7 @@ - + @@ -190,7 +190,7 @@

문의

diff --git a/docs/ko/threat-model/index.html b/docs/ko/threat-model/index.html index cade84ae..a5ab59b2 100644 --- a/docs/ko/threat-model/index.html +++ b/docs/ko/threat-model/index.html @@ -48,7 +48,7 @@ - + @@ -221,7 +221,7 @@

취약점 신고하기

diff --git a/docs/llms-full.txt b/docs/llms-full.txt index 0be73c27..bb3a5e3b 100644 --- a/docs/llms-full.txt +++ b/docs/llms-full.txt @@ -62,7 +62,7 @@ Documentation Using Flowlight - Everything from the first launch to tuning the agent rules. Flowlight 0.15.8, macOS 15 or later. + Everything from the first launch to tuning the agent rules. Flowlight 0.15.9, macOS 15 or later. On this page @@ -894,7 +894,7 @@ Understand your AI agents. brew install --cask xinbetween/tap/flowlightCopy - v0.15.8macOS 15+UniversalGPL-3.0No telemetry + v0.15.9macOS 15+UniversalGPL-3.0No telemetry connectionslive @@ -1629,8 +1629,20 @@ Releases Downloads, checksums and full notes for each version are on GitHub Releases. + 0.15.9 +October 27, 2026Latest + + Make Store builds warning-free. Ambiguous optionals, isolation boundaries, sendable callbacks, + capture ownership, and stale compatibility code now compile cleanly with current Xcode. + + Turn new warnings into release failures. Store targets promote Swift warnings to errors, while CI + separately rejects first-party diagnostics the compiler does not elevate. + + Keep toolchain notices actionable. Xcode metadata notices and dependency warnings remain visible + without being mistaken for Flowlight source regressions. + 0.15.8 -October 26, 2026Latest +October 26, 2026 Recover from transient Apple stapling failures. After notarization succeeds, app, DMG, and package ticket stapling now retries bounded connection-loss failures without submitting the artifact to Apple a second time. diff --git a/docs/llms.txt b/docs/llms.txt index bc76398a..83ac8ce5 100644 --- a/docs/llms.txt +++ b/docs/llms.txt @@ -1,6 +1,6 @@ # Flowlight -> Free, open-source (GPL-3.0) application-aware network monitor for macOS, with focused visibility into AI agents. It attributes observed TCP and UDP activity to the application that made it and records the destination, protocol and byte counts, keeping local history from second to year. Recognized AI agents are listed by name along with the tools and MCP servers they start, under per-agent allowlists. It can also refuse, once asked: a rule blocks an application, a destination or a URL for as long as you specify, and a guardrail withholds a tool from an agent before its model is offered it. Runs on macOS 15 or later; capture is by a sampler or a Network Extension. Current version: 0.15.8. +> Free, open-source (GPL-3.0) application-aware network monitor for macOS, with focused visibility into AI agents. It attributes observed TCP and UDP activity to the application that made it and records the destination, protocol and byte counts, keeping local history from second to year. Recognized AI agents are listed by name along with the tools and MCP servers they start, under per-agent allowlists. It can also refuse, once asked: a rule blocks an application, a destination or a URL for as long as you specify, and a guardrail withholds a tool from an agent before its model is offered it. Runs on macOS 15 or later; capture is by a sampler or a Network Extension. Current version: 0.15.9. - [Download Flowlight.dmg](https://github.com/xinbetween/flowlight/releases/latest/download/Flowlight.dmg) - [Source code](https://github.com/xinbetween/flowlight) diff --git a/docs/plugins/index.html b/docs/plugins/index.html index c3d5e766..e7d82f84 100644 --- a/docs/plugins/index.html +++ b/docs/plugins/index.html @@ -28,7 +28,7 @@ - + @@ -211,7 +211,7 @@

Review checklist

diff --git a/docs/privacy/index.html b/docs/privacy/index.html index c27e7841..84ff2e03 100644 --- a/docs/privacy/index.html +++ b/docs/privacy/index.html @@ -48,7 +48,7 @@ - + @@ -196,7 +196,7 @@

Contact

diff --git a/docs/pt-PT/about/index.html b/docs/pt-PT/about/index.html index 19a530b0..4e5838e9 100644 --- a/docs/pt-PT/about/index.html +++ b/docs/pt-PT/about/index.html @@ -48,7 +48,7 @@ - + @@ -152,7 +152,7 @@

Agradecimentos

diff --git a/docs/pt-PT/docs/index.html b/docs/pt-PT/docs/index.html index 16e7847d..b1ba08f9 100644 --- a/docs/pt-PT/docs/index.html +++ b/docs/pt-PT/docs/index.html @@ -48,7 +48,7 @@ - + @@ -76,7 +76,7 @@

Documentação

Usar o Flowlight

-

Tudo, da primeira abertura ao ajuste das regras dos agentes. Flowlight 0.15.8, macOS 15 ou posterior.

+

Tudo, da primeira abertura ao ajuste das regras dos agentes. Flowlight 0.15.9, macOS 15 ou posterior.

diff --git a/docs/pt-PT/index.html b/docs/pt-PT/index.html index a1ecb552..7636c467 100644 --- a/docs/pt-PT/index.html +++ b/docs/pt-PT/index.html @@ -48,7 +48,7 @@ - + @@ -90,7 +90,7 @@

Veja a atividade de rede das suas apps.

brew install --cask xinbetween/tap/flowlight

-

v0.15.8macOS 15+UniversalGPL-3.0Sem telemetria

+

v0.15.9macOS 15+UniversalGPL-3.0Sem telemetria

@@ -572,7 +572,7 @@

Saiba o que sai do seu Mac.

diff --git a/docs/pt-PT/privacy/index.html b/docs/pt-PT/privacy/index.html index 9fc703a9..e4864065 100644 --- a/docs/pt-PT/privacy/index.html +++ b/docs/pt-PT/privacy/index.html @@ -48,7 +48,7 @@ - + @@ -189,7 +189,7 @@

Contacto

diff --git a/docs/pt-PT/threat-model/index.html b/docs/pt-PT/threat-model/index.html index 7c380f49..c518b92a 100644 --- a/docs/pt-PT/threat-model/index.html +++ b/docs/pt-PT/threat-model/index.html @@ -48,7 +48,7 @@ - + @@ -222,7 +222,7 @@

Comunicar uma vulnerabilidade

diff --git a/docs/releases/index.html b/docs/releases/index.html index 2530d2e9..789456dd 100644 --- a/docs/releases/index.html +++ b/docs/releases/index.html @@ -28,7 +28,7 @@ - + @@ -61,7 +61,19 @@

What's new

-

0.15.8

Latest
+

0.15.9

Latest
+
    +
  • Make Store builds warning-free. Ambiguous optionals, isolation boundaries, sendable callbacks, + capture ownership, and stale compatibility code now compile cleanly with current Xcode.
  • +
  • Turn new warnings into release failures. Store targets promote Swift warnings to errors, while CI + separately rejects first-party diagnostics the compiler does not elevate.
  • +
  • Keep toolchain notices actionable. Xcode metadata notices and dependency warnings remain visible + without being mistaken for Flowlight source regressions.
  • +
+
+ +
+

0.15.8

  • Recover from transient Apple stapling failures. After notarization succeeds, app, DMG, and package ticket stapling now retries bounded connection-loss failures without submitting the artifact to Apple a second time.
  • @@ -1349,7 +1361,7 @@

    What's new

diff --git a/docs/sitemap.xml b/docs/sitemap.xml index 677ea3b4..b2ed251a 100644 --- a/docs/sitemap.xml +++ b/docs/sitemap.xml @@ -1,55 +1,55 @@ - https://flowlight.xinbetween.com/about/2026-10-26 - https://flowlight.xinbetween.com/docs/2026-10-26 - https://flowlight.xinbetween.com/2026-10-26 - https://flowlight.xinbetween.com/plugins/2026-10-26 - https://flowlight.xinbetween.com/privacy/2026-10-26 - https://flowlight.xinbetween.com/releases/2026-10-26 - https://flowlight.xinbetween.com/threat-model/2026-10-26 - https://flowlight.xinbetween.com/de/about/2026-10-26 - https://flowlight.xinbetween.com/de/docs/2026-10-26 - https://flowlight.xinbetween.com/de/2026-10-26 - https://flowlight.xinbetween.com/de/privacy/2026-10-26 - https://flowlight.xinbetween.com/de/threat-model/2026-10-26 - https://flowlight.xinbetween.com/es/about/2026-10-26 - https://flowlight.xinbetween.com/es/docs/2026-10-26 - https://flowlight.xinbetween.com/es/2026-10-26 - https://flowlight.xinbetween.com/es/privacy/2026-10-26 - https://flowlight.xinbetween.com/es/threat-model/2026-10-26 - https://flowlight.xinbetween.com/fr/about/2026-10-26 - https://flowlight.xinbetween.com/fr/docs/2026-10-26 - https://flowlight.xinbetween.com/fr/2026-10-26 - https://flowlight.xinbetween.com/fr/privacy/2026-10-26 - https://flowlight.xinbetween.com/fr/threat-model/2026-10-26 - https://flowlight.xinbetween.com/it/about/2026-10-26 - https://flowlight.xinbetween.com/it/docs/2026-10-26 - https://flowlight.xinbetween.com/it/2026-10-26 - https://flowlight.xinbetween.com/it/privacy/2026-10-26 - https://flowlight.xinbetween.com/it/threat-model/2026-10-26 - https://flowlight.xinbetween.com/pt-PT/about/2026-10-26 - https://flowlight.xinbetween.com/pt-PT/docs/2026-10-26 - https://flowlight.xinbetween.com/pt-PT/2026-10-26 - https://flowlight.xinbetween.com/pt-PT/privacy/2026-10-26 - https://flowlight.xinbetween.com/pt-PT/threat-model/2026-10-26 - https://flowlight.xinbetween.com/ja/about/2026-10-26 - https://flowlight.xinbetween.com/ja/docs/2026-10-26 - https://flowlight.xinbetween.com/ja/2026-10-26 - https://flowlight.xinbetween.com/ja/privacy/2026-10-26 - https://flowlight.xinbetween.com/ja/threat-model/2026-10-26 - https://flowlight.xinbetween.com/zh-Hans/about/2026-10-26 - https://flowlight.xinbetween.com/zh-Hans/docs/2026-10-26 - https://flowlight.xinbetween.com/zh-Hans/2026-10-26 - https://flowlight.xinbetween.com/zh-Hans/privacy/2026-10-26 - https://flowlight.xinbetween.com/zh-Hans/threat-model/2026-10-26 - https://flowlight.xinbetween.com/zh-Hant/about/2026-10-26 - https://flowlight.xinbetween.com/zh-Hant/docs/2026-10-26 - https://flowlight.xinbetween.com/zh-Hant/2026-10-26 - https://flowlight.xinbetween.com/zh-Hant/privacy/2026-10-26 - https://flowlight.xinbetween.com/zh-Hant/threat-model/2026-10-26 - https://flowlight.xinbetween.com/ko/about/2026-10-26 - https://flowlight.xinbetween.com/ko/docs/2026-10-26 - https://flowlight.xinbetween.com/ko/2026-10-26 - https://flowlight.xinbetween.com/ko/privacy/2026-10-26 - https://flowlight.xinbetween.com/ko/threat-model/2026-10-26 + https://flowlight.xinbetween.com/about/2026-10-27 + https://flowlight.xinbetween.com/docs/2026-10-27 + https://flowlight.xinbetween.com/2026-10-27 + https://flowlight.xinbetween.com/plugins/2026-10-27 + https://flowlight.xinbetween.com/privacy/2026-10-27 + https://flowlight.xinbetween.com/releases/2026-10-27 + https://flowlight.xinbetween.com/threat-model/2026-10-27 + https://flowlight.xinbetween.com/de/about/2026-10-27 + https://flowlight.xinbetween.com/de/docs/2026-10-27 + https://flowlight.xinbetween.com/de/2026-10-27 + https://flowlight.xinbetween.com/de/privacy/2026-10-27 + https://flowlight.xinbetween.com/de/threat-model/2026-10-27 + https://flowlight.xinbetween.com/es/about/2026-10-27 + https://flowlight.xinbetween.com/es/docs/2026-10-27 + https://flowlight.xinbetween.com/es/2026-10-27 + https://flowlight.xinbetween.com/es/privacy/2026-10-27 + https://flowlight.xinbetween.com/es/threat-model/2026-10-27 + https://flowlight.xinbetween.com/fr/about/2026-10-27 + https://flowlight.xinbetween.com/fr/docs/2026-10-27 + https://flowlight.xinbetween.com/fr/2026-10-27 + https://flowlight.xinbetween.com/fr/privacy/2026-10-27 + https://flowlight.xinbetween.com/fr/threat-model/2026-10-27 + https://flowlight.xinbetween.com/it/about/2026-10-27 + https://flowlight.xinbetween.com/it/docs/2026-10-27 + https://flowlight.xinbetween.com/it/2026-10-27 + https://flowlight.xinbetween.com/it/privacy/2026-10-27 + https://flowlight.xinbetween.com/it/threat-model/2026-10-27 + https://flowlight.xinbetween.com/pt-PT/about/2026-10-27 + https://flowlight.xinbetween.com/pt-PT/docs/2026-10-27 + https://flowlight.xinbetween.com/pt-PT/2026-10-27 + https://flowlight.xinbetween.com/pt-PT/privacy/2026-10-27 + https://flowlight.xinbetween.com/pt-PT/threat-model/2026-10-27 + https://flowlight.xinbetween.com/ja/about/2026-10-27 + https://flowlight.xinbetween.com/ja/docs/2026-10-27 + https://flowlight.xinbetween.com/ja/2026-10-27 + https://flowlight.xinbetween.com/ja/privacy/2026-10-27 + https://flowlight.xinbetween.com/ja/threat-model/2026-10-27 + https://flowlight.xinbetween.com/zh-Hans/about/2026-10-27 + https://flowlight.xinbetween.com/zh-Hans/docs/2026-10-27 + https://flowlight.xinbetween.com/zh-Hans/2026-10-27 + https://flowlight.xinbetween.com/zh-Hans/privacy/2026-10-27 + https://flowlight.xinbetween.com/zh-Hans/threat-model/2026-10-27 + https://flowlight.xinbetween.com/zh-Hant/about/2026-10-27 + https://flowlight.xinbetween.com/zh-Hant/docs/2026-10-27 + https://flowlight.xinbetween.com/zh-Hant/2026-10-27 + https://flowlight.xinbetween.com/zh-Hant/privacy/2026-10-27 + https://flowlight.xinbetween.com/zh-Hant/threat-model/2026-10-27 + https://flowlight.xinbetween.com/ko/about/2026-10-27 + https://flowlight.xinbetween.com/ko/docs/2026-10-27 + https://flowlight.xinbetween.com/ko/2026-10-27 + https://flowlight.xinbetween.com/ko/privacy/2026-10-27 + https://flowlight.xinbetween.com/ko/threat-model/2026-10-27 diff --git a/docs/threat-model/index.html b/docs/threat-model/index.html index 3904116d..063a382e 100644 --- a/docs/threat-model/index.html +++ b/docs/threat-model/index.html @@ -48,7 +48,7 @@ - + @@ -231,7 +231,7 @@

Reporting a vulnerability

diff --git a/docs/zh-Hans/about/index.html b/docs/zh-Hans/about/index.html index ae8bec95..d4a9cfb0 100644 --- a/docs/zh-Hans/about/index.html +++ b/docs/zh-Hans/about/index.html @@ -48,7 +48,7 @@ - + @@ -152,7 +152,7 @@

致谢

diff --git a/docs/zh-Hans/docs/index.html b/docs/zh-Hans/docs/index.html index 20d4fb9c..fcdb0a13 100644 --- a/docs/zh-Hans/docs/index.html +++ b/docs/zh-Hans/docs/index.html @@ -48,7 +48,7 @@ - + @@ -76,7 +76,7 @@

文档

使用 Flowlight

-

从第一次启动到调整代理规则,需要的都在这里。Flowlight 0.15.8,macOS 15 或更高版本。

+

从第一次启动到调整代理规则,需要的都在这里。Flowlight 0.15.9,macOS 15 或更高版本。

diff --git a/docs/zh-Hans/index.html b/docs/zh-Hans/index.html index e966ac4b..2511d8ae 100644 --- a/docs/zh-Hans/index.html +++ b/docs/zh-Hans/index.html @@ -48,7 +48,7 @@ - + @@ -90,7 +90,7 @@

看清应用的网络活动。

brew install --cask xinbetween/tap/flowlight

-

v0.15.8macOS 15+通用架构GPL-3.0无遥测

+

v0.15.9macOS 15+通用架构GPL-3.0无遥测

@@ -569,7 +569,7 @@

知道什么离开了你的 Mac。

diff --git a/docs/zh-Hans/privacy/index.html b/docs/zh-Hans/privacy/index.html index 8cee23e1..91238ffc 100644 --- a/docs/zh-Hans/privacy/index.html +++ b/docs/zh-Hans/privacy/index.html @@ -48,7 +48,7 @@ - + @@ -187,7 +187,7 @@

联系方式

diff --git a/docs/zh-Hans/threat-model/index.html b/docs/zh-Hans/threat-model/index.html index 7dfed887..c151a968 100644 --- a/docs/zh-Hans/threat-model/index.html +++ b/docs/zh-Hans/threat-model/index.html @@ -48,7 +48,7 @@ - + @@ -210,7 +210,7 @@

报告漏洞

diff --git a/docs/zh-Hant/about/index.html b/docs/zh-Hant/about/index.html index 283bbd07..6f1286bf 100644 --- a/docs/zh-Hant/about/index.html +++ b/docs/zh-Hant/about/index.html @@ -48,7 +48,7 @@ - + @@ -152,7 +152,7 @@

致謝

diff --git a/docs/zh-Hant/docs/index.html b/docs/zh-Hant/docs/index.html index 002d201f..7b874ac6 100644 --- a/docs/zh-Hant/docs/index.html +++ b/docs/zh-Hant/docs/index.html @@ -48,7 +48,7 @@ - + @@ -76,7 +76,7 @@

說明文件

使用 Flowlight

-

從第一次啟動到調整代理規則,全都在這裡。Flowlight 0.15.8,macOS 15 或以上版本。

+

從第一次啟動到調整代理規則,全都在這裡。Flowlight 0.15.9,macOS 15 或以上版本。

diff --git a/docs/zh-Hant/index.html b/docs/zh-Hant/index.html index c03ee4e8..11d527ac 100644 --- a/docs/zh-Hant/index.html +++ b/docs/zh-Hant/index.html @@ -48,7 +48,7 @@ - + @@ -90,7 +90,7 @@

看見每個 App 的網路活動。
brew install --cask xinbetween/tap/flowlight

-

v0.15.8macOS 15+通用架構GPL-3.0無遙測

+

v0.15.9macOS 15+通用架構GPL-3.0無遙測

@@ -570,7 +570,7 @@

知道有什麼離開了你的 Mac。

diff --git a/docs/zh-Hant/privacy/index.html b/docs/zh-Hant/privacy/index.html index 6cc5e54e..c47fb29e 100644 --- a/docs/zh-Hant/privacy/index.html +++ b/docs/zh-Hant/privacy/index.html @@ -48,7 +48,7 @@ - + @@ -187,7 +187,7 @@

聯絡

diff --git a/docs/zh-Hant/threat-model/index.html b/docs/zh-Hant/threat-model/index.html index 6bb3536c..10c46531 100644 --- a/docs/zh-Hant/threat-model/index.html +++ b/docs/zh-Hant/threat-model/index.html @@ -48,7 +48,7 @@ - + @@ -211,7 +211,7 @@

回報漏洞

diff --git a/project.yml b/project.yml index 24a8c49b..ee520893 100644 --- a/project.yml +++ b/project.yml @@ -15,8 +15,8 @@ settings: DEVELOPMENT_TEAM: "" CODE_SIGN_STYLE: Automatic ENABLE_HARDENED_RUNTIME: YES - MARKETING_VERSION: "0.15.8" - CURRENT_PROJECT_VERSION: "55" + MARKETING_VERSION: "0.15.9" + CURRENT_PROJECT_VERSION: "56" configs: AppStore: DEVELOPMENT_TEAM: 38RJUJHKZS @@ -105,6 +105,7 @@ targets: GENERATE_INFOPLIST_FILE: NO SKIP_INSTALL: YES SWIFT_ACTIVE_COMPILATION_CONDITIONS: "$(inherited) APP_STORE" + SWIFT_TREAT_WARNINGS_AS_ERRORS: YES dependencies: - sdk: NetworkExtension.framework FlowlightAppStore: @@ -124,6 +125,7 @@ targets: GENERATE_INFOPLIST_FILE: NO ASSETCATALOG_COMPILER_APPICON_NAME: AppIcon SWIFT_ACTIVE_COMPILATION_CONDITIONS: "$(inherited) APP_STORE" + SWIFT_TREAT_WARNINGS_AS_ERRORS: YES dependencies: - target: FlowlightStoreExtension embed: true diff --git a/scripts/check-swift-warnings.sh b/scripts/check-swift-warnings.sh new file mode 100755 index 00000000..9d8538f0 --- /dev/null +++ b/scripts/check-swift-warnings.sh @@ -0,0 +1,19 @@ +#!/bin/bash +# Fails when an Xcode build log contains a warning attributed to Flowlight's Swift source. +# Toolchain notices (for example, AppIntents metadata extraction) have no project source path and are not release blockers. +set -euo pipefail + +if [[ $# -ne 1 || ! -f "$1" ]]; then + echo "usage: scripts/check-swift-warnings.sh " >&2 + exit 2 +fi + +log=$1 +warnings=$(grep -E '^/.*/(Flowlight|FlowlightExtension|FlowlightResponseScriptHelper|Shared)/.*\.swift:[0-9]+:[0-9]+: warning:' "$log" || true) +if [[ -n "$warnings" ]]; then + echo "Flowlight Swift compiler warnings are release blockers:" >&2 + echo "$warnings" >&2 + exit 1 +fi + +echo "Flowlight Swift sources are warning-free." diff --git a/scripts/test-swift-warnings.sh b/scripts/test-swift-warnings.sh new file mode 100755 index 00000000..83d4777f --- /dev/null +++ b/scripts/test-swift-warnings.sh @@ -0,0 +1,26 @@ +#!/bin/bash +# Deterministic coverage for the first-party Swift warning gate. No Xcode build is required. +set -euo pipefail +cd "$(dirname "$0")/.." + +temporary=$(mktemp -d "${TMPDIR:-/tmp}/flowlight-swift-warnings-test.XXXXXX") +trap 'rm -rf "$temporary"' EXIT + +cat > "$temporary/clean.log" <<'LOG' +2026-10-26 appintentsmetadataprocessor[1:2] warning: Metadata extraction skipped, no AppIntents.framework dependency found +/tmp/dependency/Sources/Library.swift:10:3: warning: dependency warning outside Flowlight source +** BUILD SUCCEEDED ** +LOG +scripts/check-swift-warnings.sh "$temporary/clean.log" + +cat > "$temporary/dirty.log" <<'LOG' +/tmp/checkout/Flowlight/Analysis/RuleStore.swift:412:90: warning: actor-isolated property used from nonisolated context +** BUILD SUCCEEDED ** +LOG +if scripts/check-swift-warnings.sh "$temporary/dirty.log" > "$temporary/output" 2>&1; then + echo "first-party Swift warning was not rejected" >&2 + exit 1 +fi +grep -Fq 'RuleStore.swift:412:90: warning:' "$temporary/output" + +echo "Swift warning fixtures passed: toolchain and dependency notices allowed, first-party warning rejected." diff --git a/site/pages/releases.html b/site/pages/releases.html index 2052d7fd..b8c78ab3 100644 --- a/site/pages/releases.html +++ b/site/pages/releases.html @@ -13,7 +13,19 @@

What's new

-

0.15.8

Latest
+

0.15.9

Latest
+
    +
  • Make Store builds warning-free. Ambiguous optionals, isolation boundaries, sendable callbacks, + capture ownership, and stale compatibility code now compile cleanly with current Xcode.
  • +
  • Turn new warnings into release failures. Store targets promote Swift warnings to errors, while CI + separately rejects first-party diagnostics the compiler does not elevate.
  • +
  • Keep toolchain notices actionable. Xcode metadata notices and dependency warnings remain visible + without being mistaken for Flowlight source regressions.
  • +
+
+ +
+

0.15.8

  • Recover from transient Apple stapling failures. After notarization succeeds, app, DMG, and package ticket stapling now retries bounded connection-loss failures without submitting the artifact to Apple a second time.