Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions Flowlight.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -2252,7 +2252,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 73;
CURRENT_PROJECT_VERSION = 74;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES;
Expand All @@ -2267,7 +2267,7 @@
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 15.0;
MARKETING_VERSION = 0.16.11;
MARKETING_VERSION = 0.17.0;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
PRODUCT_NAME = "$(TARGET_NAME)";
Expand Down Expand Up @@ -2313,7 +2313,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 73;
CURRENT_PROJECT_VERSION = 74;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = 38RJUJHKZS;
ENABLE_HARDENED_RUNTIME = YES;
Expand All @@ -2328,7 +2328,7 @@
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 15.0;
MARKETING_VERSION = 0.16.11;
MARKETING_VERSION = 0.17.0;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
PRODUCT_NAME = "$(TARGET_NAME)";
Expand Down Expand Up @@ -2393,7 +2393,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 73;
CURRENT_PROJECT_VERSION = 74;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES;
Expand All @@ -2414,7 +2414,7 @@
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 15.0;
MARKETING_VERSION = 0.16.11;
MARKETING_VERSION = 0.17.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
Expand Down
25 changes: 17 additions & 8 deletions Flowlight/App/AppNavigation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ enum SidebarItem: String, CaseIterable, Identifiable {
switch self {
case .live: return "Live"
case .agents: return "AI Agents"
case .reports: return "Reports"
case .visualize: return "Visualize"
case .reports: return "History"
case .visualize: return "Connections"
case .alerts: return "Alerts"
case .rules: return "Rules"
case .ask: return "Ask"
Expand All @@ -55,8 +55,8 @@ enum SidebarItem: String, CaseIterable, Identifiable {
switch self {
case .live: return L("Live")
case .agents: return L("AI Agents")
case .reports: return L("Reports")
case .visualize: return L("Visualize")
case .reports: return L("History")
case .visualize: return L("Connections")
case .alerts: return L("Alerts")
case .rules: return L("Rules")
case .ask: return L("Ask")
Expand Down Expand Up @@ -157,7 +157,10 @@ struct RuleRequest: Equatable {
struct ReportRequest: Equatable {
var filter: TrafficFilter
var granularity: Granularity?
var start: Date? = nil
var end: Date?
var duration: TimeInterval? = nil
var preset: HistoryRangePreset? = nil
var id = UUID()
}

Expand Down Expand Up @@ -192,9 +195,13 @@ final class AppNavigation: ObservableObject {
}

func updateCurrentReportScope(filter: TrafficFilter, granularity: Granularity,
endDate: Date, followNow: Bool) {
startDate: Date, endDate: Date, followNow: Bool,
preset: HistoryRangePreset?) {
currentReportScope = ReportRequest(filter: filter, granularity: granularity,
end: followNow ? nil : endDate)
start: followNow ? nil : startDate,
end: followNow ? nil : endDate,
duration: endDate.timeIntervalSince(startDate),
preset: preset)
}

func goBack() {
Expand Down Expand Up @@ -224,8 +231,10 @@ final class AppNavigation: ObservableObject {
rulesTab = .packs
}

func showReport(filter: TrafficFilter, granularity: Granularity? = nil, end: Date? = nil) {
reportRequest = ReportRequest(filter: filter, granularity: granularity, end: end)
func showReport(filter: TrafficFilter, granularity: Granularity? = nil,
start: Date? = nil, end: Date? = nil) {
reportRequest = ReportRequest(filter: filter, granularity: granularity,
start: start, end: end)
followLink(to: .reports)
}

Expand Down
28 changes: 22 additions & 6 deletions Flowlight/App/FlowlightApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ struct FlowlightApp: App {
MenuBarExtra {
MenuBarContent()
.environmentObject(monitor)
.environmentObject(extensionManager)
.environmentObject(nav)
.environmentObject(updater)
.environmentObject(focus)
Expand All @@ -207,6 +208,7 @@ struct FlowlightApp: App {

struct MenuBarLabel: View {
@EnvironmentObject var monitor: TrafficMonitor
@EnvironmentObject var extensionManager: ExtensionManager
@EnvironmentObject var updater: UpdateChecker
@EnvironmentObject var focus: FocusStore
@Environment(\.openWindow) private var openWindow
Expand All @@ -225,13 +227,27 @@ struct MenuBarLabel: View {
#endif
}

@ViewBuilder private var label: some View {
if showRates {
Text("↓\(ByteFormat.compact(monitor.currentIn)) ↑\(ByteFormat.compact(monitor.currentOut))")
.monospacedDigit()
} else {
Image(systemName: monitor.unacknowledgedAlerts > 0 ? "exclamationmark.arrow.triangle.2.circlepath" : "arrow.up.arrow.down.circle")
private var label: some View {
HStack(spacing: 4) {
if extensionManager.state.showsMenuBarNotice {
Image(systemName: "exclamationmark.triangle.fill")
.accessibilityHidden(true)
}
if showRates {
Text("↓\(ByteFormat.compact(monitor.currentIn)) ↑\(ByteFormat.compact(monitor.currentOut))")
.monospacedDigit()
} else {
Image(systemName: monitor.unacknowledgedAlerts > 0 ? "exclamationmark.arrow.triangle.2.circlepath" : "arrow.up.arrow.down.circle")
.accessibilityHidden(true)
}
}
.accessibilityElement(children: .ignore)
.accessibilityLabel(extensionManager.state.showsMenuBarNotice
? L("Flowlight, restart required to finish updating the filter")
: L("Flowlight network activity"))
.help(extensionManager.state.showsMenuBarNotice
? L("Restart or reinstall the filter to finish the update")
: L("Open Flowlight"))
}
}

Expand Down
18 changes: 9 additions & 9 deletions Flowlight/Ask/FeatureGuide.swift
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ extension FeatureGuide {
FeatureGuide(
id: "rules", title: "Rules",
summary: "Block or allow anything: an app, an agent, a destination, a URL, or a pairing of those — forever, until a time, until Flowlight quits, or between chosen hours. The most specific rule wins and a tie goes to the block.",
steps: ["Open Rules and press +, or right-click any row in Live, Reports, AI Agents or Inspect and choose Block.",
steps: ["Open Rules and press +, or right-click any row in Live, History, AI Agents or Inspect and choose Block.",
"Name an app or a destination, pick block or allow, and choose how long it lasts.",
"Pause Blocking in the toolbar stands every rule down for a while."],
screen: .rules,
Expand Down Expand Up @@ -142,19 +142,19 @@ extension FeatureGuide {
keywords: ["alert", "anomaly", "warning", "threshold", "notification", "exfiltration"]),

FeatureGuide(
id: "reports", title: "Reports",
id: "reports", title: "History",
summary: "History from second to year, grouped by app, destination or address, with charts, a Worth a look mode that points at apps whose destinations don't resemble their peers, and CSV export.",
steps: ["Open Reports.", "Pick a granularity, then group by App › Domain › IP, Destination › App › IP, or IP › App.",
steps: ["Open History.", "Pick a granularity, then group by App › Domain › IP, Destination › App › IP, or IP › App.",
"Use the Way out menu to narrow to the network, peer-to-peer Wi-Fi, this Mac or a tunnel."],
screen: .reports,
keywords: ["history", "chart", "csv", "export csv", "breakdown", "worth a look", "group"]),
keywords: ["history", "reports", "chart", "csv", "export csv", "breakdown", "worth a look", "group"]),

FeatureGuide(
id: "visualize", title: "Traffic and AI visualization",
id: "visualize", title: "Connections",
summary: "One native map with separate Traffic and AI activity modes. Traffic shows every app-to-destination connection; AI activity shows proved agent clients, their attributed tools and MCP servers, LLM services, and every other destination they reached.",
steps: ["Open Visualize and choose Traffic or AI activity.",
steps: ["Open Connections and choose Traffic or AI activity.",
"Pick a time period and select a node or connection for exact byte and connection totals.",
"For a grouped LLM service, choose an exact host and address before opening Reports, Inspect, or an unsaved blocking rule."],
"For a grouped LLM service, choose an exact host and address before opening History, Inspect, or an unsaved blocking rule."],
screen: .visualize,
caveats: ["Connections are network flows, not HTTP request counts.",
"Observed means traffic exists. Runs means stored process lineage; it is not a proved invocation.",
Expand All @@ -165,7 +165,7 @@ extension FeatureGuide {
FeatureGuide(
id: "channels", title: "Peer-to-peer Wi-Fi",
summary: "AirDrop, Handoff, AirPlay, Sidecar and Universal Control go straight to a device in the room over AWDL. Flowlight labels those flows rather than showing them as traffic to an address with no name.",
steps: ["Open Reports.", "Use the Way out menu in the toolbar and choose Peer-to-peer Wi-Fi."],
steps: ["Open History.", "Use the Way out menu in the toolbar and choose Peer-to-peer Wi-Fi."],
screen: .reports,
keywords: ["airdrop", "awdl", "handoff", "airplay", "sidecar", "peer to peer", "nearby"]),

Expand All @@ -186,7 +186,7 @@ extension FeatureGuide {
"Pick which fields may leave, and add any headers your collector needs."],
screen: nil,
caveats: ["Headers are kept in the login Keychain, not in preferences.",
"Whatever it sends appears in Live and Reports like any other app's traffic."],
"Whatever it sends appears in Live and History like any other app's traffic."],
keywords: ["otlp", "siem", "splunk", "datadog", "collector", "opentelemetry", "forward"]),

FeatureGuide(
Expand Down
2 changes: 2 additions & 0 deletions Flowlight/Capture/ExtensionManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ final class ExtensionManager: NSObject, ObservableObject {
/// macOS accepted the replacement but will not perform it until the Mac restarts. Nothing the app does
/// changes that: the extension's lifecycle belongs to the system, not to this process.
case needsReboot
var showsMenuBarNotice: Bool { self == .needsReboot }

var label: String {
switch self {
case .unknown: return L("Unknown")
Expand Down
34 changes: 26 additions & 8 deletions Flowlight/Localization/de.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -1083,12 +1083,6 @@
"App › Domain › IP, Destination › App › IP, or IP › App" = "App › Domain › IP, Ziel › App › IP oder IP › App";
"Choose a longer window or clear filters." = "Längeren Zeitraum wählen oder Filter entfernen.";
"Back" = "Zurück";
"Return to the previous report scope" = "Zum vorherigen Berichtsumfang zurück";
"Granularity" = "Auflösung";
"Earlier (⌘[)" = "Früher (⌘[)";
"Earlier" = "Früher";
"Later (⌘])" = "Später (⌘])";
"Later" = "Später";
"Now" = "Jetzt";
"Showing only:" = "Nur angezeigt:";
"App: %@" = "App: %@";
Expand All @@ -1112,7 +1106,6 @@
"Peak year" = "Spitzenjahr";
"Bytes" = "Bytes";
"Filtered traffic" = "Gefilterter Datenverkehr";
"Click a bar to zoom in" = "Auf einen Balken klicken, um hineinzuzoomen";
"Use the Left and Right Arrow keys to inspect time buckets." = "Mit der linken und rechten Pfeiltaste Zeitabschnitte prüfen.";
"Use the Left and Right Arrow keys to inspect time buckets, then press Return to zoom in." = "Mit der linken und rechten Pfeiltaste Zeitabschnitte prüfen und dann die Eingabetaste zum Hineinzoomen drücken.";
"1 new flow" = "1 neuer Flow";
Expand Down Expand Up @@ -1799,7 +1792,6 @@
"Could not prepare managed-pack review demo: %@" = "Die Demo zur Überprüfung verwalteter Pakete konnte nicht vorbereitet werden: %@";

/* 0.16.0 Datenverkehrsvisualisierung */
"Visualize" = "Visualisieren";
"Map" = "Karte";
"List" = "Liste";
"App, domain, or IP" = "App, Domain oder IP";
Expand Down Expand Up @@ -1875,3 +1867,29 @@
"%lld exact destinations" = "%lld exakte Ziele";
"%@ to %@, received %@, sent %@, %lld connections" = "%@ zu %@, %@ empfangen, %@ gesendet, %lld Verbindungen";
"Show domain family in Reports" = "Domainfamilie in Berichte anzeigen";

/* 0.17.0 History and Connections */
"1 mo" = "1 Mon.";
"15 min" = "15 Min.";
"1d" = "1 T.";
"1h" = "1 Std.";
"1w" = "1 Wo.";
"1y" = "1 J.";
"6 mo" = "6 Mon.";
"6h" = "6 Std.";
"All" = "Alle";
"All history" = "Gesamter Verlauf";
"Drag the selection to move through history, drag either edge to resize it, or use the Left and Right Arrow keys." = "Ziehen Sie die Auswahl, um sich durch den Verlauf zu bewegen. Ziehen Sie an einem der Ränder, um ihre Größe zu ändern, oder verwenden Sie die Links- und Rechtspfeiltaste.";
"Flowlight network activity" = "Flowlight-Netzwerkaktivität";
"Flowlight, restart required to finish updating the filter" = "Flowlight, Neustart zum Abschließen der Filteraktualisierung erforderlich";
"Full history" = "Gesamter Verlauf";
"History" = "Verlauf";
"History overview failed: %@" = "Verlaufsübersicht fehlgeschlagen: %@";
"History range navigator" = "Zeitraumnavigation für den Verlauf";
"Nearby" = "Umgebung";
"Nearby context" = "Zeitlicher Kontext";
"Open the filter update notice in Flowlight" = "Hinweis zur Filteraktualisierung in Flowlight öffnen";
"Range" = "Zeitraum";
"Restart or reinstall the filter to finish the update" = "Filter neu starten oder neu installieren, um die Aktualisierung abzuschließen";
"Return to the previous history scope" = "Zum vorherigen Verlaufsbereich zurückkehren";
"Selected" = "Ausgewählt";
34 changes: 26 additions & 8 deletions Flowlight/Localization/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -1032,12 +1032,6 @@
"App › Domain › IP, Destination › App › IP, or IP › App" = "App › Domain › IP, Destination › App › IP, or IP › App";
"Choose a longer window or clear filters." = "Choose a longer window or clear filters.";
"Back" = "Back";
"Return to the previous report scope" = "Return to the previous report scope";
"Granularity" = "Granularity";
"Earlier (⌘[)" = "Earlier (⌘[)";
"Earlier" = "Earlier";
"Later (⌘])" = "Later (⌘])";
"Later" = "Later";
"Now" = "Now";
"Showing only:" = "Showing only:";
"App: %@" = "App: %@";
Expand All @@ -1061,7 +1055,6 @@
"Peak year" = "Peak year";
"Bytes" = "Bytes";
"Filtered traffic" = "Filtered traffic";
"Click a bar to zoom in" = "Click a bar to zoom in";
"Use the Left and Right Arrow keys to inspect time buckets." = "Use the Left and Right Arrow keys to inspect time buckets.";
"Use the Left and Right Arrow keys to inspect time buckets, then press Return to zoom in." = "Use the Left and Right Arrow keys to inspect time buckets, then press Return to zoom in.";
"1 new flow" = "1 new flow";
Expand Down Expand Up @@ -1798,7 +1791,6 @@
"Could not prepare managed-pack review demo: %@" = "Could not prepare managed-pack review demo: %@";

/* 0.16.0 traffic visualization */
"Visualize" = "Visualize";
"Map" = "Map";
"List" = "List";
"App, domain, or IP" = "App, domain, or IP";
Expand Down Expand Up @@ -1874,3 +1866,29 @@
"%lld exact destinations" = "%lld exact destinations";
"%@ to %@, received %@, sent %@, %lld connections" = "%@ to %@, received %@, sent %@, %lld connections";
"Show domain family in Reports" = "Show domain family in Reports";

/* 0.17.0 History and Connections */
"1 mo" = "1 mo";
"15 min" = "15 min";
"1d" = "1d";
"1h" = "1h";
"1w" = "1w";
"1y" = "1y";
"6 mo" = "6 mo";
"6h" = "6h";
"All" = "All";
"All history" = "All history";
"Drag the selection to move through history, drag either edge to resize it, or use the Left and Right Arrow keys." = "Drag the selection to move through history, drag either edge to resize it, or use the Left and Right Arrow keys.";
"Flowlight network activity" = "Flowlight network activity";
"Flowlight, restart required to finish updating the filter" = "Flowlight, restart required to finish updating the filter";
"Full history" = "Full history";
"History" = "History";
"History overview failed: %@" = "History overview failed: %@";
"History range navigator" = "History range navigator";
"Nearby" = "Nearby";
"Nearby context" = "Nearby context";
"Open the filter update notice in Flowlight" = "Open the filter update notice in Flowlight";
"Range" = "Range";
"Restart or reinstall the filter to finish the update" = "Restart or reinstall the filter to finish the update";
"Return to the previous history scope" = "Return to the previous history scope";
"Selected" = "Selected";
Loading
Loading