vanken 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +24 -0
  3. data/README.md +28 -11
  4. data/data/coloring_rules.yml +67 -0
  5. data/docs/performance.md +53 -0
  6. data/docs/releases.md +2 -4
  7. data/docs/usage.md +51 -0
  8. data/exe/vanken-setup-permissions +6 -0
  9. data/lib/vanken/app/capture_controller.rb +66 -1
  10. data/lib/vanken/app/document.rb +102 -39
  11. data/lib/vanken/app/document_analysis.rb +75 -0
  12. data/lib/vanken/app/document_jobs.rb +52 -9
  13. data/lib/vanken/app/expert_info.rb +26 -0
  14. data/lib/vanken/app/io_graph.rb +47 -0
  15. data/lib/vanken/app/navigation.rb +77 -0
  16. data/lib/vanken/app/search_job.rb +62 -0
  17. data/lib/vanken/app/stats_job.rb +45 -0
  18. data/lib/vanken/capture/analyzer.rb +3 -1
  19. data/lib/vanken/capture/analyzer_process.rb +9 -6
  20. data/lib/vanken/capture/analyzer_worker.rb +2 -2
  21. data/lib/vanken/capture/filter_worker.rb +8 -2
  22. data/lib/vanken/capture/helper_main.rb +10 -3
  23. data/lib/vanken/capture/helper_options.rb +6 -2
  24. data/lib/vanken/capture/launcher.rb +1 -0
  25. data/lib/vanken/capture/permission_setup.rb +201 -0
  26. data/lib/vanken/config/analysis_settings.rb +58 -0
  27. data/lib/vanken/config/columns.rb +138 -0
  28. data/lib/vanken/config/messages.rb +105 -0
  29. data/lib/vanken/config/preferences.rb +34 -8
  30. data/lib/vanken/config/profiles.rb +74 -0
  31. data/lib/vanken/config/sessions.rb +69 -0
  32. data/lib/vanken/config/yaml_file.rb +36 -0
  33. data/lib/vanken/core/coloring.rb +86 -0
  34. data/lib/vanken/core/frame_store.rb +2 -2
  35. data/lib/vanken/core/stores.rb +12 -0
  36. data/lib/vanken/gateway/detail_builder.rb +1 -1
  37. data/lib/vanken/gateway/display_capture_filter.rb +102 -0
  38. data/lib/vanken/gateway/dissector.rb +31 -6
  39. data/lib/vanken/gateway/exporter.rb +125 -0
  40. data/lib/vanken/gateway/file_writer.rb +26 -2
  41. data/lib/vanken/gateway/interfaces.rb +59 -0
  42. data/lib/vanken/gateway/resolver.rb +131 -0
  43. data/lib/vanken/gateway/statistics.rb +83 -0
  44. data/lib/vanken/gateway/stream.rb +159 -0
  45. data/lib/vanken/ui/actions.rb +54 -28
  46. data/lib/vanken/ui/analysis_dialogs.rb +371 -0
  47. data/lib/vanken/ui/application.rb +183 -9
  48. data/lib/vanken/ui/coloring_operations.rb +104 -0
  49. data/lib/vanken/ui/column_operations.rb +115 -0
  50. data/lib/vanken/ui/dialogs.rb +41 -26
  51. data/lib/vanken/ui/file_operations.rb +17 -12
  52. data/lib/vanken/ui/filter_operations.rb +43 -15
  53. data/lib/vanken/ui/main_view.rb +74 -29
  54. data/lib/vanken/ui/navigation_operations.rb +86 -0
  55. data/lib/vanken/ui/packet_source.rb +33 -1
  56. data/lib/vanken/ui/selection.rb +8 -2
  57. data/lib/vanken/ui/settings_operations.rb +233 -0
  58. data/lib/vanken/version.rb +1 -1
  59. data/packaging/README.md +41 -1
  60. data/packaging/linux/org.vanken.capture.policy +14 -0
  61. data/packaging/linux/vanken-capture.wrapper +6 -0
  62. data/packaging/linux/vanken.desktop +9 -0
  63. data/packaging/linux/vanken.sudoers +2 -0
  64. data/packaging/macos/chmod-bpf +6 -0
  65. data/packaging/macos/org.vanken.chmod-bpf.plist +8 -0
  66. data/sig/generated/vanken/app/capture_controller.rbs +16 -0
  67. data/sig/generated/vanken/app/document.rbs +23 -1
  68. data/sig/generated/vanken/app/document_analysis.rbs +21 -0
  69. data/sig/generated/vanken/app/document_jobs.rbs +6 -0
  70. data/sig/generated/vanken/app/expert_info.rbs +30 -0
  71. data/sig/generated/vanken/app/io_graph.rbs +45 -0
  72. data/sig/generated/vanken/app/navigation.rbs +31 -0
  73. data/sig/generated/vanken/app/search_job.rbs +21 -0
  74. data/sig/generated/vanken/app/stats_job.rbs +18 -0
  75. data/sig/generated/vanken/capture/helper_options.rbs +6 -0
  76. data/sig/generated/vanken/capture/permission_setup.rbs +43 -0
  77. data/sig/generated/vanken/config/analysis_settings.rbs +31 -0
  78. data/sig/generated/vanken/config/columns.rbs +37 -0
  79. data/sig/generated/vanken/config/messages.rbs +14 -0
  80. data/sig/generated/vanken/config/preferences.rbs +1 -1
  81. data/sig/generated/vanken/config/profiles.rbs +29 -0
  82. data/sig/generated/vanken/config/sessions.rbs +17 -0
  83. data/sig/generated/vanken/config/yaml_file.rbs +11 -0
  84. data/sig/generated/vanken/core/coloring.rbs +52 -0
  85. data/sig/generated/vanken/core/stores.rbs +4 -0
  86. data/sig/generated/vanken/gateway/display_capture_filter.rbs +37 -0
  87. data/sig/generated/vanken/gateway/dissector.rbs +15 -2
  88. data/sig/generated/vanken/gateway/exporter.rbs +27 -0
  89. data/sig/generated/vanken/gateway/file_writer.rbs +3 -0
  90. data/sig/generated/vanken/gateway/interfaces.rbs +12 -0
  91. data/sig/generated/vanken/gateway/resolver.rbs +52 -0
  92. data/sig/generated/vanken/gateway/statistics.rbs +104 -0
  93. data/sig/generated/vanken/gateway/stream.rbs +75 -0
  94. data/sig/generated/vanken/ui/analysis_dialogs.rbs +71 -0
  95. data/sig/generated/vanken/ui/application.rbs +23 -1
  96. data/sig/generated/vanken/ui/coloring_operations.rbs +29 -0
  97. data/sig/generated/vanken/ui/column_operations.rbs +29 -0
  98. data/sig/generated/vanken/ui/dialogs.rbs +5 -3
  99. data/sig/generated/vanken/ui/file_operations.rbs +2 -0
  100. data/sig/generated/vanken/ui/filter_operations.rbs +3 -1
  101. data/sig/generated/vanken/ui/main_view.rbs +14 -2
  102. data/sig/generated/vanken/ui/navigation_operations.rbs +43 -0
  103. data/sig/generated/vanken/ui/packet_source.rbs +4 -0
  104. data/sig/generated/vanken/ui/settings_operations.rbs +41 -0
  105. metadata +61 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6bfbb1b288a09cb5fb170bed462adb00f4ae86aa21ee62c247e9e2cefe2aaa97
4
- data.tar.gz: 3f8f754621be04e6fc2a4dac72abae4c10f57a8d3524f018c67825abe18b4e8a
3
+ metadata.gz: 9db141f2b218a50e8cb5d228b14b35d6ff728e3fc48fbd6abddf18e56f903284
4
+ data.tar.gz: 65a40d15a931d59b2e618aab1cfbc9b8fd97bea13d0593b8b1938d611ba5bad2
5
5
  SHA512:
6
- metadata.gz: bc1c63f6df229fbb85db6e00cd69059faa16a69f91469f5cd8a3d7d6ad7b8e0bcce3068ed31ebb4533c77906188b3448026a6e5a1cfe581a2d1f5d4fb058cce0
7
- data.tar.gz: 370e198b35f3c4bb080f773b026ae6005c3457bd4412d9be8b13e1ea868c182b0919cf8d4b041325231a44f1ec8bf2d556dc15eb01ba1bbff47893cde91c6d49
6
+ metadata.gz: 5b7de2090ab6735713dcb9855d67103339b2dfe75a94b51486b9d0bcb797e0b3c8286fcf3b531f50ea8375eaf732721d2124ffdcb32fd0940a69f03b6893c4ab
7
+ data.tar.gz: 3f4600b9db5305b86b35fa821f6dee78bdd323458b50359043f4e252481ba16d6414bfbfd8c8a22e8cec1566280d7933dbbc0df8612015ce4ea07b277a4ece43
data/CHANGELOG.md CHANGED
@@ -1,5 +1,29 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.0
4
+
5
+ - Add `vanken-setup-permissions` to install Linux capture wrappers and polkit or sudoers policies, and configure macOS BPF access through a LaunchDaemon. Installation validates privileged runtime and destination ownership before granting access.
6
+ - Include the Linux desktop entry, capture policies, fixed-path wrappers, and macOS setup files in the gem and downloadable GitHub release assets.
7
+
8
+ Requires Ruby 3.3 or newer. Live capture supports Linux and macOS; Windows supports file inspection. See [capture permissions](https://github.com/ydah/vanken/blob/main/packaging/README.md).
9
+
10
+ Live acquisition and redraw latency on slower systems retain the [documented performance limits](https://github.com/ydah/vanken/blob/main/docs/performance.md).
11
+
12
+ ## 0.2.0
13
+
14
+ - Add editable packet coloring, four search modes, marks, ignored packets, time references, and conversation navigation.
15
+ - Add Follow TCP Stream with retransmission handling, missing-data indicators, searching, saving, and stream exclusion.
16
+ - Add protocol hierarchy, conversations, endpoints, expert information, capture properties, and filtered I/O graphs.
17
+ - Add typed custom columns, Decode As rules, trusted Ruby dissector plugins, packet-range exports, and JSON, NDJSON, text, and CSV exports.
18
+ - Add the command palette, profile-based preferences, Japanese and English screens, high-contrast themes, and keyboard operation in the terminal UI.
19
+ - Add automatic capture stopping, rotating pcapng files, interface traffic graphs, optional asynchronous address resolution, and interrupted-capture recovery.
20
+ - Accelerate supported address and port display filters while preserving ordinary evaluation for other packets and dissector extensions.
21
+ - Preserve captured packets awaiting analysis when saving, and retain filter results and current selection through reanalysis.
22
+
23
+ Live acquisition can outpace analysis on slower systems. Captured packets remain available while queued analysis finishes; redraw latency on measured Linux systems remains above the design target. See [measured performance](https://github.com/ydah/vanken/blob/main/docs/performance.md).
24
+
25
+ Requires Ruby 3.3 or newer. Live capture supports Linux and macOS; Windows supports file inspection. See [capture permissions](https://github.com/ydah/vanken/blob/main/packaging/README.md).
26
+
3
27
  ## 0.1.0
4
28
 
5
29
  Initial release.
data/README.md CHANGED
@@ -10,21 +10,20 @@ Packet acquisition runs in a separate helper; ordered packet analysis runs in an
10
10
  - Linux with a desktop session, or macOS. File inspection is also available on Windows; live capture uses Linux packet sockets or macOS BPF.
11
11
  - Linux desktop dependencies: Vulkan loader and drivers, fonts, and `zenity` for native file dialogs. On Ubuntu: `sudo apt install libvulkan1 mesa-vulkan-drivers fonts-dejavu-core fonts-noto-cjk zenity`.
12
12
 
13
- ## Run from source
13
+ ## Install
14
14
 
15
15
  ```sh
16
- git clone https://github.com/ydah/vanken.git
17
- cd vanken
18
- bundle install
19
- bundle exec exe/vanken capture.pcapng
16
+ gem install vanken
17
+ vanken capture.pcapng
20
18
  ```
21
19
 
22
- Initial RubyGems publication is pending. To install a locally built gem:
20
+ For a source checkout:
23
21
 
24
22
  ```sh
25
- gem build --strict vanken.gemspec
26
- gem install ./vanken-0.1.0.gem
27
- vanken capture.pcapng
23
+ git clone https://github.com/ydah/vanken.git
24
+ cd vanken
25
+ bundle install
26
+ bundle exec exe/vanken capture.pcapng
28
27
  ```
29
28
 
30
29
  Strict gem builds require RubyGems 4.0.16 or newer; older versions reject the pinned redhound prerelease with a recommendation warning.
@@ -43,10 +42,24 @@ frame.len > 1000 && !udp
43
42
 
44
43
  Vanken display filters (VDF) and acquisition filters (BPF) are separate languages. See [display filters](docs/filters.md) for operators, types, and repeated fields.
45
44
 
45
+ ## Analysis
46
+
47
+ Use the menus or the command palette (Ctrl+Shift+K; Command+Shift+K in the macOS desktop application) to find actions. Search by display filter, hexadecimal bytes, string, or regular expression. Mark and ignore packets, change the time reference, and move between packets in the same conversation. Packet and field context menus expose filter and analysis actions.
48
+
49
+ Coloring rules can be edited, reordered, imported, and exported. Add a protocol field as a custom column from the details tree, then change its position, width, or visibility in the column editor. Custom columns sort by their field types.
50
+
51
+ Follow TCP Stream reconstructs each direction, reports missing data, and supports ASCII, hexadecimal, and raw views, searching, saving, and excluding a stream. Statistics include protocol hierarchy, conversations, endpoints, and packet properties. I/O graphs accept display filters per series and intervals from 0.01 to 60 seconds. Expert information links diagnostics to their packets.
52
+
53
+ Decode As rules and registered Ruby dissector plugins are applied to packet summaries, details, and filter workers. Plugins require an explicit trust confirmation before loading. Save captures as pcap or pcapng, export selected ranges, or export dissections as JSON, NDJSON, text, or CSV.
54
+
55
+ Preferences include Japanese and English, dark/light/system/high-contrast themes, analysis limits, and optional asynchronous address resolution. Profiles keep preferences, columns, coloring, bookmarks, Decode As rules, and plugins separate; recent files and window geometry are shared. Stop capture before switching profiles or changing dissectors. Analysis preferences changed during loading or capture are applied when it finishes. Interrupted captures can be recovered or discarded on the next start.
56
+
46
57
  ## Capture
47
58
 
48
59
  Choose Start, select an interface, and optionally set a BPF acquisition filter. Stop preserves captured packets for inspection and saving. Vanken asks before discarding an unsaved capture.
49
60
 
61
+ Capture options include automatic stopping by packet count, duration, or byte count, and rotating pcapng files by size or time with a bounded file count. The welcome screen shows interface traffic rates. Saving includes all durable packets even while analysis is still catching up.
62
+
50
63
  ```sh
51
64
  bundle exec exe/vanken-capture --list-interfaces
52
65
  bundle exec exe/vanken-capture --check --interface lo
@@ -54,6 +67,8 @@ bundle exec exe/vanken-capture --check --interface lo
54
67
 
55
68
  Direct acquisition works when the account already has permission. Privileged launching requires a root-owned installation with a fixed interpreter and dependencies; see [capture helper setup](packaging/README.md). Run the desktop application as a regular user.
56
69
 
70
+ The gem includes `vanken-setup-permissions` for administrator installation of Linux capture permissions and the macOS BPF LaunchDaemon. Review the platform-specific setup guide and the command's `--dry-run` output first. GitHub releases also include the permission policies, desktop entry, wrappers, and macOS script in a packaging archive.
71
+
57
72
  ## Command line
58
73
 
59
74
  ```sh
@@ -64,6 +79,8 @@ bundle exec exe/vanken --headless --smoke
64
79
 
65
80
  `--no-yjit` disables default YJIT activation. `--debug` enables debug logging. Preferences use safe YAML in the platform's user configuration directory; logs rotate without retaining raw packet bytes.
66
81
 
82
+ The terminal UI supports opening captures, selecting packets, applying filters, and starting/stopping capture with keyboard actions. Details of keyboard controls, settings files, and analysis limits are in the [usage guide](docs/usage.md).
83
+
67
84
  ## Development
68
85
 
69
86
  ```sh
@@ -75,9 +92,9 @@ bundle exec ruby script/benchmark.rb
75
92
  script/capture-ci.sh
76
93
  ```
77
94
 
78
- To develop with a sibling Zaniah checkout, set `VANKEN_ZANIAH_PATH=../zaniah` when running Bundler. Production dependencies are redhound `2.0.0.rc2` and Zaniah `~> 0.12.3`, including fixes for growing memory use and repeated style allocations during redraws.
95
+ To develop with a sibling Zaniah checkout, set `VANKEN_ZANIAH_PATH=../zaniah` when running Bundler. Production dependencies are redhound `2.0.0.rc2` and Zaniah `~> 0.12.4`.
79
96
 
80
- See [upstream contracts](docs/upstream.md), [measured performance](docs/performance.md), and [release procedure](docs/releases.md). Version 0.1 implements the file inspection, acquisition, and display filter milestones. Statistics, stream following, coloring, profiles, and later extensions are scheduled for subsequent releases.
97
+ See [upstream contracts](docs/upstream.md), [measured performance](docs/performance.md), and [release procedure](docs/releases.md). Live acquisition can outpace analysis, and redraw latency on slower Linux machines remains above the design target; captured packets are retained while queued analysis finishes.
81
98
 
82
99
  ## License
83
100
 
@@ -0,0 +1,67 @@
1
+ schema_version: 1
2
+ rules:
3
+ - name: Bad TCP
4
+ filter: tcp.analysis.retransmission || tcp.analysis.out_of_order || tcp.analysis.lost_segment || tcp.analysis.duplicate_ack || tcp.analysis.zero_window
5
+ light: { fg: "#F78787", bg: "#121212" }
6
+ dark: { fg: "#F78787", bg: "#1A1A1A" }
7
+ enabled: true
8
+ - name: Malformed
9
+ filter: expert.severity == error
10
+ light: { fg: "#FFFC9C", bg: "#A40000" }
11
+ dark: { fg: "#FFFC9C", bg: "#7A0000" }
12
+ enabled: true
13
+ - name: Checksum Errors
14
+ filter: expert.code == "bad_checksum"
15
+ light: { fg: "#F78787", bg: "#121212" }
16
+ dark: { fg: "#F78787", bg: "#1A1A1A" }
17
+ enabled: true
18
+ - name: TCP RST
19
+ filter: tcp.flags.rst
20
+ light: { fg: "#FFFC9C", bg: "#A40000" }
21
+ dark: { fg: "#FFFC9C", bg: "#7A0000" }
22
+ enabled: true
23
+ - name: TCP SYN/FIN
24
+ filter: tcp.flags.syn || tcp.flags.fin
25
+ light: { fg: "#12272E", bg: "#A0A0A0" }
26
+ dark: { fg: "#E0E0E0", bg: "#4A4A4A" }
27
+ enabled: true
28
+ - name: HTTP
29
+ filter: http
30
+ light: { fg: "#12272E", bg: "#E4FFC7" }
31
+ dark: { fg: "#E4FFC7", bg: "#25331A" }
32
+ enabled: true
33
+ - name: TLS
34
+ filter: tls
35
+ light: { fg: "#12272E", bg: "#D6E8FF" }
36
+ dark: { fg: "#D6E8FF", bg: "#1E2A3A" }
37
+ enabled: true
38
+ - name: DNS
39
+ filter: dns
40
+ light: { fg: "#12272E", bg: "#C9F0FF" }
41
+ dark: { fg: "#C9F0FF", bg: "#16303A" }
42
+ enabled: true
43
+ - name: ICMP
44
+ filter: icmp || icmpv6
45
+ light: { fg: "#12272E", bg: "#FCE0FF" }
46
+ dark: { fg: "#FCE0FF", bg: "#3A2440" }
47
+ enabled: true
48
+ - name: ARP
49
+ filter: arp
50
+ light: { fg: "#12272E", bg: "#FAF0D7" }
51
+ dark: { fg: "#FAF0D7", bg: "#3A331F" }
52
+ enabled: true
53
+ - name: Broadcast
54
+ filter: eth.dst == ff:ff:ff:ff:ff:ff
55
+ light: { fg: "#BABDB6", bg: "#FFFFFF" }
56
+ dark: { fg: "#9A9D96", bg: "#2A2A2A" }
57
+ enabled: true
58
+ - name: UDP
59
+ filter: udp
60
+ light: { fg: "#12272E", bg: "#DAEEFF" }
61
+ dark: { fg: "#DAEEFF", bg: "#1C2833" }
62
+ enabled: true
63
+ - name: TCP
64
+ filter: tcp
65
+ light: { fg: "#12272E", bg: "#E7E6FF" }
66
+ dark: { fg: "#E7E6FF", bg: "#24233A" }
67
+ enabled: true
data/docs/performance.md CHANGED
@@ -111,3 +111,56 @@ The final single-run check with public Zaniah 0.12.3 and the same Ruby, platform
111
111
  Before visible-row batching, the same operation failed its first selection at 105.252 ms. A separate diagnostic reproduced a 107.234 ms background queue wait behind 11 row jobs, while actual detail analysis took 2.677 ms and byte reading 0.129 ms. Nine intermediate renders consumed most of that wait. PacketSource now collects visible-row requests after rendering, fetches them in one background job, and publishes their values in one foreground update. The passing run followed this runtime change; the earlier failure remains part of the evidence.
112
112
 
113
113
  The native and synthetic runs above do not substitute for this five-minute Linux capture test. Varied addresses, large TCP flow sets, reassembly, and plugins are outside the repeated-UDP benchmark's scope.
114
+
115
+ ## Analysis extensions and cBPF
116
+
117
+ The address/port optimization uses the public verified capture-filter compiler for eligible complete Ethernet IPv4/IPv6 TCP/UDP packets. It falls back to VDF for unsupported expressions, truncation, fragmentation, extension headers, common UDP tunnels, other link types, Decode As, or plugins. This preserves the fields seen by the dissector.
118
+
119
+ On 2026-10-02, Ruby 4.0.6 with YJIT on arm64 Darwin compared the same 20,000 complete Ethernet/IPv4/UDP frames and expression `ip.addr == 192.0.2.1 && udp.port == 54321`. Three sequential samples per evaluator all matched 20,000 packets. Median stateless VDF dissection/evaluation took 0.339384 s, and the eligible cBPF path took 0.020937 s: 16.21 times faster. This comparison excludes file IO and worker startup. Run `bundle exec ruby --yjit script/filter-performance.rb 20000` to repeat it.
120
+
121
+ A separate one-million-packet run of the production document measured 0.5529 s for the existing fast filter, 6.2598 s for the four-worker `ip.ttl == 64` scan, and 2.7721 s for the four-worker address/port cBPF scan. Each matched all one million packets. Analysis took 115.6605 s (8,646 packets/s), with 67.2 B/frame retained parent heap before filtering and 81.08 B/frame after the worker scan. This run overlapped integration and UI tests and is not an isolated comparison with the earlier analysis-throughput result. RSS sampling was unavailable and is recorded as null; these heap figures do not establish the RSS target.
122
+
123
+ The I/O graph check seeds actual frame metadata, packed columns, and annotations for 200,000 TCP frames, then times two series at each supported interval. It excludes packet parsing. The observed interval rebuilds took 0.30–0.33 s, each below the one-second target, with every packet counted in both series. Run `bundle exec ruby --yjit script/analysis-performance.rb` to repeat it. Nightly validation retains the one-million-packet benchmark, evaluator comparison, graph timings, and fuzz results as workflow artifacts.
124
+
125
+ ## Release 0.2 and 0.3 verification with public Zaniah 0.12.4
126
+
127
+ The [final nightly run](https://github.com/ydah/vanken/actions/runs/36999466662) tested commit `6d05e2a` with Ruby 3.4.10, YJIT, public redhound 2.0.0.rc2, and public Zaniah 0.12.4 on the shared x86_64 Linux runner. The receiver, complete one-million-packet analysis, all filters, graph accounting, and 5,000 deterministic fuzz cases passed their functional checks. Commit `2169b6a` adds the administrator installer and packaging without changing this analysis or rendering code.
128
+
129
+ | Measurement | Result | Target |
130
+ | --- | ---: | ---: |
131
+ | Receiver, 1,000,000 frames | 181,777 frames/s | ≥ 100,000 |
132
+ | Ingest and analyze, 1,000,000 frames | 4,073 frames/s; 245.5186 s | ≥ 15,000; missed |
133
+ | Parent / combined / after-filter RSS increment | 80.26 / 87.48 / 91.06 B per frame | ≤ 200 |
134
+ | Fast / four-worker slow filter | 1.9499 / 22.9614 s | ≤ 3 / 60 s |
135
+ | Four-worker address/port cBPF filter | 7.1322 s; 1,000,000 matches | Reported separately |
136
+ | Static full application, total / scene render p95 | 73.154 / 71.903 ms | Scene ≤ 33 ms; missed |
137
+ | Growing full application, total / scene render p95 | 44.370 / 42.490 ms | Scene ≤ 33 ms; missed |
138
+
139
+ The growing full-application source produced only 427.6 frames/s during sampling, so that measurement does not establish responsiveness at 5,000 frames/s. The isolated virtual-table ingestion measurement similarly does not replace the complete application or live capture. Job success does not mean all numeric targets passed.
140
+
141
+ The same nightly run compared VDF and cBPF over the same 20,000 packets, with three samples per evaluator. Median times were 1.239493 s and 0.055858 s respectively, a 22.19-fold improvement, with identical match counts. Two-series I/O graph rebuilds for 200,000 frames took 1.024516, 1.011629, 1.005382, 0.996721, and 0.996433 s at intervals 0.01, 0.1, 1, 10, and 60 s. The three shortest intervals narrowly exceeded the one-second target.
142
+
143
+ On the Mac with Ruby 4.0.6, YJIT, and public Zaniah 0.12.4, the final 100,000,032-byte file check displayed its first verified row in 645.209 ms. Twenty distinct selections had p50 26.660 ms, p95 35.778 ms, and maximum 51.223 ms. The first-row target passed; the every-selection 50 ms target failed on the first selection. These are single-run headless production-window measurements with the same exclusions described above.
144
+
145
+ A native Mac run with 120 samples verified opening, selection, filtering, and all 25,256 captured frames. Scene p95 was 28.804 ms and total native tick p95 was 47.850 ms. During sampling, the source delivered 19,453 frames in 4.40697 s, or 4,414.14 frames/s. The scene result meets 33 ms at that observed rate, but does not establish the requested 5,000 frames/s target.
146
+
147
+ The [final five-minute Linux capture run](https://github.com/ydah/vanken/actions/runs/36999897569), at `2169b6a`, used Ruby 3.4.11 with YJIT and the public dependencies above. The actual administrator command installed 74 root-owned files, and capture-boundary tests verified the installation before running the UI with UID/EUID 1000. A real PTY additionally passed file opening, packet selection, filtering, the command palette, capture options, actual capture start/stop, and quit.
148
+
149
+ The independent sender delivered 1,500,000 frames over 300.000066 s at 4,999.9989 frames/s. Kernel received, helper captured, durable, and final analyzed counts all equaled 1,500,000; drops, interface drops, and freezes were zero. Analysis drained 208.956 s after traffic ended, with a maximum sampled backlog of 578,656 frames. This establishes capture integrity and sender pacing while retaining the analysis-throughput limitation.
150
+
151
+ During actual traffic, 2,492 frames rendered and 2,395 showed analyzed-row growth. Scene p95 was 78.807 ms and total render p95 was 81.711 ms, exceeding the 33 ms scene target. The file check in the same run displayed its first row in 788.282 ms, passing one second; selection p95 was 104.404 ms and maximum 111.676 ms, exceeding 50 ms. The earlier measurements remain above for comparison; shared-runner results do not establish a controlled before/after speedup.
152
+
153
+ ### Same-host analysis regression comparison
154
+
155
+ The old Linux analysis time of 206.2345 s and the latest 245.5186 s came from different shared VMs. A separate same-host comparison checks the release procedure's 15% regression threshold without treating those environments as identical.
156
+
157
+ The initial `26b3e7b` source and current `2169b6a` source were archived and run alternately on the same Mac. Both used the current public dependency lockfile, Ruby 4.0.6 with YJIT, a fresh Ruby process and analyzer child, the identical 58-byte UDP fixture and Enumerator, and a 50-frame warm-up. Each timed `Document.ingest(...).wait` with `process_analysis: true` over 100,000 frames. Initialization, warm-up, cleanup, kernel acquisition, and UI were excluded; no other local tests ran concurrently.
158
+
159
+ | Sample | Initial source, seconds | Current source, seconds |
160
+ | --- | ---: | ---: |
161
+ | 1 | 11.307215 | 11.508561 |
162
+ | 2 | 11.623641 | 12.657844 |
163
+ | 3 | 12.123842 | 12.085011 |
164
+ | Median | 11.623641 | 12.085011 |
165
+
166
+ Median elapsed time increased 3.97%; median throughput changed from 8,603.16 to 8,274.71 frames/s, a 3.82% decrease. The 15% threshold was not exceeded for this workload. All six runs verified exactly 100,000 durable and analyzed frames, UDP decoding, no document error, and analyzer-child cleanup. This comparison does not explain the separate shared-VM Linux timing difference or establish the 15,000 frames/s target.
data/docs/releases.md CHANGED
@@ -4,12 +4,10 @@ Work is committed on main. Release notes and CHANGELOG entries include only user
4
4
 
5
5
  Before tagging, run `bundle exec rake`, verify generated RBS is current, run privileged capture and benchmarks, perform native smoke, and build the gem strictly. Version, tag, gemspec dependencies, and lockfile must agree. Exclude captures, spools, test artifacts, local dependencies, and credentials from the gem.
6
6
 
7
- Initial 0.1.0 notes are exactly `Initial release.`. Publication is paused until the owner publishes the first RubyGem and configures trusted publishing. No initial release tag is pushed automatically.
8
-
9
- For the first publication, run `gem push pkg/vanken-0.1.0.gem`, then configure the publisher identity below. Tagging `v0.1.0` afterwards runs validation and creates the GitHub release; the job skips uploading a version already present on RubyGems.
7
+ Initial 0.1.0 notes are exactly `Initial release.`. Its owner publication and trusted publishing setup are complete. The job skips uploading a version already present on RubyGems.
10
8
 
11
9
  Subsequent `vX.Y.Z` tags trigger `.github/workflows/release.yml`, based on the Canopus release job. It verifies version, runs checks, builds the gem, publishes with RubyGems trusted publishing, and creates GitHub release notes from CHANGELOG.
12
10
 
13
11
  Trusted publisher identity: owner `ydah`, repository `vanken`, workflow `release.yml`, GitHub environment `release`.
14
12
 
15
- The design milestones are 0.1 for file inspection, capture, and filters; 0.2 for coloring, search, stream following, statistics, and analysis extensions; and 0.3 for profiles, localization, permission packaging, recovery UI, and additional capture conveniences. Work stops at the initial publication gate before the later milestones.
13
+ The design milestones are 0.1 for file inspection, capture, and filters; 0.2 for coloring, search, stream following, statistics, and analysis extensions; and 0.3 for profiles, localization, permission packaging, recovery UI, and additional capture conveniences. Some 0.3 foundations are included with 0.2 so analysis extensions can share profiles and settings. The 0.3 release completes the administrator setup command and includes permission packaging as downloadable release assets.
data/docs/usage.md ADDED
@@ -0,0 +1,51 @@
1
+ # Using Vanken
2
+
3
+ ## Keyboard and terminal operation
4
+
5
+ The desktop and terminal views share the same commands. Use Tab and Shift+Tab to move between controls, Enter to activate, Escape to close dialogs, and arrow keys in lists. In the terminal and on Linux, use Ctrl for the shortcuts below; the native macOS window uses Command instead.
6
+
7
+ | Action | Shortcut |
8
+ | --- | --- |
9
+ | Open capture | Ctrl+O |
10
+ | Save as | Ctrl+Shift+S |
11
+ | Start or stop capture | Ctrl+E |
12
+ | Restart capture | Ctrl+Shift+R |
13
+ | Preferences | Ctrl+Shift+P |
14
+ | Command palette | Ctrl+Shift+K |
15
+ | Find packet / next / previous | Ctrl+F / Ctrl+N / Ctrl+B |
16
+ | Mark / ignore / time reference | Ctrl+M / Ctrl+D / Ctrl+T |
17
+ | Previous / next packet | Alt+Up / Alt+Down |
18
+ | Previous / next in conversation | Ctrl+, / Ctrl+. |
19
+ | Go to packet | Ctrl+G |
20
+ | Follow TCP Stream | Ctrl+Alt+Shift+T |
21
+ | Decode As | Ctrl+Shift+U |
22
+ | Reload plugins | Ctrl+Shift+L |
23
+ | Quit | Ctrl+Q |
24
+
25
+ Terminals differ in the keys they transmit. Use the command palette for a command whose shortcut is intercepted by the terminal. Type an action name and use Up/Down and Enter to run it. Column movement, width, and visibility have controls in the column editor, so they do not require dragging.
26
+
27
+ ## Profiles and settings
28
+
29
+ Preferences change the appearance, packet list, acquisition defaults, analysis limits, and optional name resolution. The language defaults to Japanese when `LANG` begins with `ja`, and English otherwise. Switching language rebuilds the current screen. High contrast is available from the theme menu or Preferences.
30
+
31
+ Create, duplicate, delete, and switch profiles from Preferences or the Profiles command. The default profile lives in the configuration directory; other profiles live under `profiles/NAME`. Each has `preferences.yml`, `columns.yml`, `coloring_rules.yml`, `filters.yml`, `decode_as.yml`, and `plugins.yml`. Configuration uses schema version 1, safe YAML, and atomic saves. Recent-file history and window geometry are shared outside profiles.
32
+
33
+ Stop acquisition before changing profiles or dissectors. Changes to analysis preferences during acquisition or file loading wait until it finishes. Reanalysis keeps the original bytes, marks, ignored packets, time references, and current selection, then refreshes summaries, details, and filters. Registered Ruby plugins execute with your account's permissions and require a trust confirmation before their first load.
34
+
35
+ ## Exporting and following streams
36
+
37
+ Export all analyzed packets, displayed packets, the selected packet, marked packets, the range between the first and last marks, or explicit ranges such as `1-10,15,20-`. An option excludes ignored packets. pcapng preserves interface metadata; pcap requires compatible link types. Ordinary Save includes all durable packets, including packets still awaiting analysis.
38
+
39
+ JSON follows redhound's packet schema; NDJSON writes one packet per line. CSV exports visible columns, including custom fields. Text exports the protocol tree. Outputs are written to a temporary file and replace the destination only after completion.
40
+
41
+ Follow TCP Stream removes duplicate retransmissions, handles sequence wrap and out-of-order segments, and displays missing-data markers. The display preview is limited to 16 MiB; saving reads the full reconstructed stream. Raw saving omits missing bytes rather than inventing data.
42
+
43
+ ## Limits and recovery
44
+
45
+ I/O graphs offer intervals of 0.01, 0.1, 1, 10, and 60 seconds. They show at most the latest 100,000 intervals and report omitted earlier intervals; choose a longer interval for long captures. Custom-column cells show at most 16 values and 4,096 bytes; the details and byte panes retain the full packet.
46
+
47
+ Acquisition can stop by packet count, duration, or byte count. Ring saving rotates pcapng files by size or time and replaces older slots after the configured file count. A size limit may be crossed by the packet that triggers rotation; packets are never split across files.
48
+
49
+ After an interrupted capture, the next start offers recovery or discard for owned, inactive session directories. Recovery reconstructs the analysis and keeps the capture unsaved until you save it. Sessions still used by a running process are excluded. A failed recovery preserves the raw session for another attempt.
50
+
51
+ See [capture permissions](../packaging/README.md) for administrator setup and [performance measurements](performance.md) for tested workloads and current limits.
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "vanken/capture/permission_setup"
5
+
6
+ exit Vanken::Capture::PermissionSetup.run(ARGV)
@@ -4,11 +4,50 @@ require_relative "../errors"
4
4
  require_relative "../capture/launcher"
5
5
  require_relative "../capture/control_protocol"
6
6
  require_relative "../gateway/file_reader"
7
+ require_relative "../gateway/file_writer"
7
8
  require_relative "document"
8
9
 
9
10
  module Vanken
10
11
  module App
11
12
  class CaptureController
13
+ class RingReader
14
+ def initialize(reader, writer, on_error:)
15
+ @reader, @writer, @on_error = reader, writer, on_error
16
+ @flushed_at = Process.clock_gettime(Process::CLOCK_MONOTONIC)
17
+ end
18
+
19
+ def next_frame(timeout:)
20
+ frame = @reader.next_frame(timeout: timeout)
21
+ @writer << frame if frame
22
+ now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
23
+ if !frame || now - @flushed_at >= 0.05
24
+ @writer.flush
25
+ @flushed_at = now
26
+ end
27
+ frame
28
+ rescue Vanken::FileError, IOError, SystemCallError
29
+ @on_error.call
30
+ raise
31
+ end
32
+
33
+ def eof? = @reader.eof?
34
+ def stop = @reader.stop
35
+ def close
36
+ return if @closed
37
+ @closed = true
38
+ begin
39
+ @writer.write_stats(@reader.stats)
40
+ ensure
41
+ begin
42
+ @writer.close
43
+ ensure
44
+ @reader.close
45
+ end
46
+ end
47
+ end
48
+ end
49
+ private_constant :RingReader
50
+
12
51
  class Error < Vanken::CaptureError
13
52
  attr_reader :code
14
53
 
@@ -20,6 +59,7 @@ module Vanken
20
59
 
21
60
  def initialize(launcher: nil, preferences: nil, on_update: nil, on_document: nil)
22
61
  @preferences, @on_update, @on_document = preferences, on_update, on_document
62
+ @custom_launcher = !launcher.nil?
23
63
  @launcher = launcher || Capture::Launcher.new(strategy: preferences&.get("capture.launcher") || :auto)
24
64
  @mutex = Mutex.new
25
65
  @state, @stats = :idle, {}
@@ -35,6 +75,14 @@ module Vanken
35
75
  def running? = @mutex.synchronize { !!@worker&.alive? }
36
76
  def snapshot = @mutex.synchronize { {state: @state, stats: @stats.dup, error: @error, warning: @warning, document: @document} }
37
77
 
78
+ def preferences=(value)
79
+ @mutex.synchronize do
80
+ raise Error, "cannot change capture preferences while capture is running" if @worker&.alive?
81
+ @preferences = value
82
+ @launcher = Capture::Launcher.new(strategy: value&.get("capture.launcher") || :auto) unless @custom_launcher
83
+ end
84
+ end
85
+
38
86
  def start(options)
39
87
  @mutex.synchronize do
40
88
  raise Error, "capture controller is closed" if @closed
@@ -89,7 +137,9 @@ module Vanken
89
137
  private
90
138
 
91
139
  def run
92
- handle = @launcher.launch(@options)
140
+ helper_options = @options.is_a?(Hash) ? @options.reject { |key, _| key.start_with?("ring_") } : @options
141
+ ring_writer = open_ring
142
+ handle = @launcher.launch(helper_options)
93
143
  @mutex.synchronize do
94
144
  @handle = handle
95
145
  stop_helper if @stop_requested
@@ -99,6 +149,7 @@ module Vanken
99
149
  @mutex.synchronize { @document = doc }
100
150
  @on_document&.call(doc)
101
151
  reader = Gateway::FileReader.new(handle.stdout)
152
+ reader = RingReader.new(reader, ring_writer, on_error: -> { stop }) if ring_writer
102
153
  doc.ingest(reader, live: true)
103
154
  doc.wait
104
155
  unless control.join(3)
@@ -122,6 +173,7 @@ module Vanken
122
173
  control&.join
123
174
  doc&.wait
124
175
  reader&.close unless doc
176
+ ring_writer&.close
125
177
  handle&.close
126
178
  @mutex.synchronize do
127
179
  @handle = nil
@@ -130,6 +182,19 @@ module Vanken
130
182
  notify
131
183
  end
132
184
 
185
+ def open_ring
186
+ return unless @options.is_a?(Hash)
187
+ path = @options["ring_path"].to_s
188
+ return if path.empty?
189
+
190
+ values = %w[max_bytes interval file_count].to_h do |key|
191
+ value = @options["ring_#{key}"]
192
+ [key.to_sym, value && value != 0 ? value : nil]
193
+ end.compact
194
+ raise Error, "ring saving requires a size or time rotation condition" unless values[:max_bytes] || values[:interval]
195
+ Gateway::FileWriter.open(File.expand_path(path), format: :pcapng, **values)
196
+ end
197
+
133
198
  def consume_control(io)
134
199
  while (line = io.gets(Capture::ControlProtocol::MAX_LINE_BYTES + 1))
135
200
  event = Capture::ControlProtocol.parse(line)