lsofrs 4.8.3

Modern, high-performance lsof implementation in Rust
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <meta name="color-scheme" content="dark light">
  <meta name="description" content="lsofrs — engineering report. Rust rewrite of lsof: 23,057 lines of production Rust + 7,999 lines of tests, 1,639 #[test] functions, 31 themes, 7-tab TUI, macOS + Linux + FreeBSD. 5–21× faster than lsof / lsofng on hyperfine.">
  <title>lsofrs &mdash; Engineering Report</title>
  <link rel="preconnect" href="https://fonts.googleapis.com">
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
  <link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;600;700;900&family=Share+Tech+Mono&display=swap" rel="stylesheet">
  <link rel="stylesheet" href="hud-static.css">
  <link rel="stylesheet" href="tutorial.css">
  <style>
    .tutorial-main { max-width: 76rem; }
    .bar-wrap { background:var(--bg-primary);border:1px solid var(--border);border-radius:2px;height:18px;position:relative;overflow:hidden; }
    .bar-fill { height:100%;border-radius:1px;transition:width 1.2s cubic-bezier(.22,1,.36,1); }
    .bar-fill.green  { background:linear-gradient(90deg,#39ff14,#20c00a);box-shadow:0 0 8px rgba(57,255,20,.4); }
    .bar-fill.cyan   { background:linear-gradient(90deg,#05d9e8,#0891b2);box-shadow:0 0 8px rgba(5,217,232,.4); }
    .bar-fill.yellow { background:linear-gradient(90deg,#ffb800,#e8a000);box-shadow:0 0 8px rgba(255,184,0,.35); }
    .bar-fill.magenta{ background:linear-gradient(90deg,#d300c5,#a000a0);box-shadow:0 0 8px rgba(211,0,197,.35); }
    .bar-pct { position:absolute;right:6px;top:0;line-height:18px;font-size:10px;font-weight:700;color:#fff;text-shadow:0 0 4px #000;font-family:'Orbitron',sans-serif; }

    .file-table { width:100%;border-collapse:collapse;margin:0.6rem 0;font-size:12px; }
    .file-table th { background:var(--bg-secondary);color:var(--cyan);font-family:'Orbitron',sans-serif;font-size:10px;font-weight:700;letter-spacing:1.2px;text-transform:uppercase;text-align:left;padding:7px 10px;border:1px solid var(--border); }
    .file-table td { padding:6px 10px;border:1px solid var(--border);color:var(--text-dim);vertical-align:middle; }
    .file-table tr:hover td { background:var(--bg-hover); }
    .file-table td:first-child { font-family:'Share Tech Mono',monospace;color:var(--accent-light);font-weight:600;white-space:nowrap; }
    .file-table .num { text-align:right;font-family:'Share Tech Mono',monospace; }
    .file-table .total-row td { background:var(--bg-secondary);font-weight:700;color:var(--text);border-top:2px solid var(--cyan); }
    .file-table code { font-size:11px;color:var(--accent-light);background:var(--bg-primary);padding:1px 4px;border-radius:2px; }

    .stat-grid { display:grid;grid-template-columns:repeat(auto-fill,minmax(14rem,1fr));gap:0.75rem;margin:1.2rem 0; }
    .stat-card { border:1px solid var(--border);border-top:3px solid var(--cyan);background:var(--bg-card);padding:1rem 1.2rem;border-radius:2px;text-align:center; }
    .stat-card .stat-val { font-family:'Orbitron',sans-serif;font-size:28px;font-weight:900;color:var(--cyan);line-height:1.1;text-shadow:0 0 20px var(--cyan-glow); }
    .stat-card .stat-val.accent { color:var(--accent);text-shadow:0 0 20px var(--accent-glow); }
    .stat-card .stat-val.green  { color:var(--green);text-shadow:0 0 20px rgba(57,255,20,.3); }
    .stat-card .stat-label { font-family:'Orbitron',sans-serif;font-size:9px;font-weight:700;letter-spacing:2px;text-transform:uppercase;color:var(--text-muted);margin-top:0.5rem; }
    @keyframes glow-pulse { 0%,100%{text-shadow:0 0 20px var(--cyan-glow)}50%{text-shadow:0 0 40px var(--cyan-glow),0 0 80px var(--cyan-dim)} }
    .stat-card .stat-val { animation:glow-pulse 3s ease-in-out infinite; }

    .mapping-grid { display:grid;grid-template-columns:repeat(auto-fill,minmax(20rem,1fr));gap:0.65rem;margin:0.8rem 0; }
    .mapping-card { border:1px solid var(--border);border-left:3px solid var(--magenta);background:var(--bg-card);padding:0.6rem 0.9rem;border-radius:2px; }
    .mapping-card h4 { font-family:'Orbitron',sans-serif;font-size:10px;font-weight:700;letter-spacing:1.5px;text-transform:uppercase;color:var(--magenta);margin:0 0 0.3rem; }
    .mapping-card p { margin:0;font-size:11px;color:var(--text-dim);line-height:1.5; }
    .mapping-card code { font-size:10.5px;color:var(--accent-light);background:var(--bg-primary);padding:1px 4px;border-radius:2px; }

    .section-rule { border:none;border-top:1px dashed var(--border);margin:2rem 0; }

    .feature-grid { display:grid;grid-template-columns:repeat(auto-fill,minmax(22rem,1fr));gap:0.65rem;margin:0.8rem 0; }
    .feature-card { border:1px solid var(--border);border-left:3px solid var(--cyan);background:var(--bg-card);padding:0.7rem 1rem;border-radius:2px; }
    .feature-card h4 { font-family:'Orbitron',sans-serif;font-size:10px;font-weight:700;letter-spacing:1.5px;text-transform:uppercase;color:var(--cyan);margin:0 0 0.3rem; }
    .feature-card p { margin:0;font-size:11px;color:var(--text-dim);line-height:1.55; }
    .feature-card code { font-size:10.5px;color:var(--accent-light);background:var(--bg-primary);padding:1px 4px;border-radius:2px; }
    .feature-card ul { margin:0.3rem 0 0;padding-left:1.2rem;font-size:11px;color:var(--text-dim);line-height:1.6; }
    .feature-card li code { font-size:10px; }
  </style>
</head>
<body>
  <div class="app tutorial-app" id="reportApp">
    <div class="crt-scanline" id="crtH" aria-hidden="true"></div>
    <div class="crt-scanline-v" id="crtV" aria-hidden="true"></div>

    <header class="tutorial-header">
      <div class="tutorial-header-inner">
        <div>
          <h1 class="tutorial-brand">// LSOFRS &mdash; ENGINEERING REPORT</h1>
          <nav class="tutorial-crumbs" aria-label="Breadcrumb">
            <span class="current">Engineering Report</span>
            <span class="sep">/</span>
            <a href="index.html">Docs</a>
            <span class="sep">/</span>
            <a href="https://github.com/MenkeTechnologies/lsofrs" target="_blank" rel="noopener noreferrer">GitHub</a>
          </nav>
          <p style="margin:0.35rem 0 0;font-family:'Share Tech Mono',monospace;font-size:11px;color:var(--text-dim);letter-spacing:0.03em;opacity:0.75;">
            Rust rewrite of lsof &middot; zero-copy <code>repr(C)</code> FFI &middot; rayon work-stealing per-PID &middot; cross-platform (Darwin + Linux + FreeBSD) &middot; ratatui TUI with 7 tabs &middot; 31 themes
          </p>
        </div>
        <div class="tutorial-toolbar">
          <button type="button" class="btn btn-secondary" id="btnTheme" title="Toggle light/dark">Theme</button>
          <button type="button" class="btn btn-secondary active" id="btnCrt" title="CRT scanline overlay">CRT</button>
          <button type="button" class="btn btn-secondary active" id="btnNeon" title="Neon border pulse">Neon</button>
        </div>
      </div>
    </header>

    <main class="tutorial-main">

      <!-- ═══════════════════════════════════════ -->
      <!-- SECTION 1: EXECUTIVE SUMMARY           -->
      <!-- ═══════════════════════════════════════ -->
      <h2 class="tutorial-title"><span class="step-hash">&gt;_</span>EXECUTIVE SUMMARY</h2>
      <p class="tutorial-subtitle"><strong>lsofrs</strong> is a single-binary Rust implementation of <code>lsof</code>. Three platform backends (<code>src/darwin.rs</code> via libproc FFI, <code>src/linux.rs</code> via <code>/proc</code>, <code>src/freebsd.rs</code> via sysctl + procfs) feed a shared <code>Vec&lt;Process&gt;</code> through a composable filter pipeline (<code>src/filter.rs</code>) into one of fourteen output / live-monitor modes. Per-PID FD enumeration runs on rayon's work-stealing pool; every TTY mode shares a <code>TuiMode</code> trait (<code>src/tui_app.rs</code>) for common keybindings, alternate-screen entry/exit, and atomic frame rendering. <strong>23,057 production Rust lines + 7,999 test lines + 1,639 <code>#[test]</code> functions</strong>. <code>hyperfine</code> wall-clock: <strong>14.2 ms</strong> for the default invocation vs <strong>169.8 ms</strong> for <code>lsof 4.91</code> (~12&times; speedup), <strong>21&times;</strong> on terse PID output.</p>

      <div class="stat-grid">
        <div class="stat-card"><div class="stat-val">23,057</div><div class="stat-label">Production Rust Lines</div></div>
        <div class="stat-card"><div class="stat-val">7,999</div><div class="stat-label">Test Rust Lines</div></div>
        <div class="stat-card"><div class="stat-val accent">31,056</div><div class="stat-label">Total Rust Lines</div></div>
        <div class="stat-card"><div class="stat-val green">1,639</div><div class="stat-label">#[test] Functions</div></div>
        <div class="stat-card"><div class="stat-val">28</div><div class="stat-label">Production Modules</div></div>
        <div class="stat-card"><div class="stat-val">10</div><div class="stat-label">Test Modules</div></div>
        <div class="stat-card"><div class="stat-val">38</div><div class="stat-label">CLI Args (#[arg])</div></div>
        <div class="stat-card"><div class="stat-val">31</div><div class="stat-label">Themes</div></div>
        <div class="stat-card"><div class="stat-val">7</div><div class="stat-label">TUI Tabs</div></div>
        <div class="stat-card"><div class="stat-val">13</div><div class="stat-label">Direct Dependencies</div></div>
        <div class="stat-card"><div class="stat-val">3</div><div class="stat-label">Platforms (Darwin/Linux/FreeBSD)</div></div>
        <div class="stat-card"><div class="stat-val">216</div><div class="stat-label">Git Commits</div></div>
      </div>

      <div style="margin:1.2rem 0;">
        <p style="font-size:11px;color:var(--text-muted);letter-spacing:0.5px;text-transform:uppercase;margin-bottom:4px;font-family:'Orbitron',sans-serif;font-weight:700;">Source Distribution &mdash; 31,056 lines</p>
        <div class="bar-wrap" style="height:26px;">
          <div class="bar-fill cyan" style="width:74.2%;"></div>
          <span class="bar-pct" style="font-size:12px;">23,057 production / 7,999 tests &middot; 74.2% production</span>
        </div>
        <p style="font-size:10px;color:var(--text-muted);margin-top:4px;">Production: 28 files in <code>src/</code>. Tests: 10 integration modules in <code>tests/</code> + inline <code>#[cfg(test)]</code> blocks in 20 of the production files. Total <code>#[test]</code> count is taken from <code>grep -rh '#[test]' tests/*.rs src/*.rs | wc -l</code>.</p>
      </div>

      <hr class="section-rule">

      <!-- ═══════════════════════════════════════ -->
      <!-- SECTION 1b: SCALE &amp; POSITION         -->
      <!-- ═══════════════════════════════════════ -->
      <h2 class="tutorial-title"><span class="step-hash">~</span>BENCHMARK POSITION</h2>
      <p class="tutorial-subtitle"><code>hyperfine</code> wall-clock (10 runs, 3 warmup, ~470 processes / ~5000 open files on macOS). Reproducible from <a href="https://github.com/MenkeTechnologies/lsofrs#-performance">README &sect; PERFORMANCE</a>. The rayon-parallel per-PID enumeration + zero-copy FFI + <code>OnceCell</code>-cached <code>getpwuid</code> together push the steady-state cost below 15 ms.</p>

      <table class="file-table">
        <thead>
          <tr>
            <th>Workload</th>
            <th class="num">lsofrs (Rust)</th>
            <th class="num">lsof 4.91 (C)</th>
            <th class="num">lsofng (C)</th>
            <th class="num">Speedup vs lsof</th>
          </tr>
        </thead>
        <tbody>
          <tr style="background:rgba(57,255,20,0.05);">
            <td>All open files (default)</td>
            <td class="num" style="color:var(--green);"><strong>14.2 ms</strong></td>
            <td class="num">169.8 ms</td>
            <td class="num">173.0 ms</td>
            <td class="num"><strong>12&times;</strong></td>
          </tr>
          <tr>
            <td>Network only (<code>-i TCP</code>)</td>
            <td class="num" style="color:var(--green);"><strong>7.2 ms</strong></td>
            <td class="num">91.7 ms</td>
            <td class="num">88.1 ms</td>
            <td class="num"><strong>13&times;</strong></td>
          </tr>
          <tr>
            <td>Terse PIDs (<code>-t</code>)</td>
            <td class="num" style="color:var(--green);"><strong>6.9 ms</strong></td>
            <td class="num">101.4 ms</td>
            <td class="num">142.9 ms</td>
            <td class="num"><strong>15&times;</strong></td>
          </tr>
          <tr>
            <td>Structured JSON (<code>-J</code>)</td>
            <td class="num" style="color:var(--green);"><strong>29.3 ms</strong></td>
            <td class="num">156.1 ms (<code>-F pcfn</code>)</td>
            <td class="num">136.7 ms</td>
            <td class="num"><strong>5&times;</strong></td>
          </tr>
        </tbody>
      </table>

      <div class="feature-grid" style="margin-top:1.2rem;">
        <div class="feature-card">
          <h4>Where the speedup comes from</h4>
          <p>Per-PID FD enumeration runs on rayon's work-stealing pool &mdash; CPU count = parallel degree. Darwin uses raw <code>#[repr(C)]</code> structs sized to libproc's kernel headers, so the gather pass never allocates per-FD. v4.8.3 added a <code>OnceCell</code>-cached <code>getpwuid</code> path, precomputed canonical filter paths, and removed every per-record allocation from the hot loop (see <a href="https://github.com/MenkeTechnologies/lsofrs/commit/1464becf31">commit 1464becf31</a>).</p>
        </div>
        <div class="feature-card">
          <h4>Why feature set still beats both incumbents</h4>
          <p>Modes that ship in <code>lsofrs</code> but neither <code>lsof</code> nor <code>lsofng</code>: unified 7-tab TUI, top-N FD dashboard, summary bar charts, stale-FD finder, listening-ports view, process tree with FD-type breakdown, pipe / unix-socket IPC map, net-by-remote-host view, file-open/close watch, single-PID follow, FD-leak detector, delta highlighting, CSV export, 31 themes with editor.</p>
        </div>
        <div class="feature-card">
          <h4>Single-binary footprint</h4>
          <p>One static <code>lsofrs</code> / <code>lsf</code> binary &mdash; clap derive parser, serde JSON, RFC 4180 CSV, ratatui TUI, crossterm terminal control, nix POSIX bindings, libc FFI, rayon work-stealing, regex selector, chrono timestamps, TOML config &mdash; all linked in. <strong>13 direct dependencies</strong>, all foundational crates likely to still build cleanly in 2035 (<code>clap</code>, <code>serde</code>, <code>libc</code>, <code>nix</code>, <code>regex</code>, <code>rayon</code>, <code>ratatui</code>, <code>crossterm</code>, <code>chrono</code>, <code>toml</code>, <code>dirs</code>, <code>users</code>, <code>serde_json</code>).</p>
        </div>
        <div class="feature-card">
          <h4>Cross-platform parity</h4>
          <p>All three platform backends return the same <code>Process</code> + <code>OpenFile</code> + <code>SocketInfo</code> shapes from <code>src/types.rs</code>. Everything above <code>filter.rs</code> is platform-agnostic, so adding a new TUI tab or output format doesn't fan out across three implementations &mdash; it ships once.</p>
        </div>
      </div>

      <hr class="section-rule">

      <!-- ═══════════════════════════════════════ -->
      <!-- SECTION 2: SUBSYSTEM BREAKDOWN          -->
      <!-- ═══════════════════════════════════════ -->
      <h2 class="tutorial-title"><span class="step-hash">#</span>SUBSYSTEM BREAKDOWN</h2>
      <p class="tutorial-subtitle">Source partitioned by role. TUI &amp; theming dominate at 31.3% of production Rust &mdash; <code>tui_tabs.rs</code> alone is 5,308 lines for the unified 7-tab dashboard (mouse, tooltips, theme chooser + editor). Selection / filter is the second-biggest subsystem (2,659 lines) because every selector composes with every other under OR / AND mode with regex, range, and negation grammar.</p>

      <table class="file-table">
        <thead><tr><th>Subsystem</th><th>Key Files</th><th class="num">Lines</th><th class="num">% of prod</th><th style="min-width:120px;">Share</th><th>Description</th></tr></thead>
        <tbody>
          <tr><td style="color:var(--accent);">TUI &amp; Theming</td><td><code>tui_tabs, tui_app, theme, config</code></td><td class="num">7,221</td><td class="num">31.3%</td><td><div class="bar-wrap"><div class="bar-fill magenta" style="width:31.3%"></div></div></td><td>Unified 7-tab dashboard (<code>--tui</code>) with mouse + tooltips + theme chooser/editor (<code>tui_tabs.rs</code>, 5,308); shared <code>TuiMode</code> trait + ratatui framework (<code>tui_app.rs</code>); 31 named palettes via <code>ThemeName</code> enum (<code>theme.rs</code>); TOML config persistence at <code>~/.lsofrs.conf</code> (<code>config.rs</code>)</td></tr>
          <tr><td style="color:var(--accent);">Selection / Filter</td><td><code>filter</code></td><td class="num">2,659</td><td class="num">11.5%</td><td><div class="bar-wrap"><div class="bar-fill cyan" style="width:11.5%"></div></div></td><td>PID / PGID / user / command / FD / network-spec / path selectors, OR-mode (default) and AND-mode (<code>-a</code>) composition, comma-lists, ranges (<code>0-10</code>), exclusion (<code>^root</code>), regex (<code>/&hellip;/</code>), precomputed canonical paths</td></tr>
          <tr><td style="color:var(--accent);">Platform Gather</td><td><code>darwin, linux, freebsd</code></td><td class="num">2,908</td><td class="num">12.6%</td><td><div class="bar-wrap"><div class="bar-fill cyan" style="width:12.6%"></div></div></td><td>macOS libproc FFI with zero-copy <code>#[repr(C)]</code> structs (<code>darwin.rs</code>, 1,253); <code>/proc</code> reader with parallel per-PID rayon (<code>linux.rs</code>, 820); sysctl + procfs decoder including kqueue (<code>freebsd.rs</code>, 835)</td></tr>
          <tr><td style="color:var(--accent);">Live Modes</td><td><code>top, summary, monitor, follow, watch, leak, delta</code></td><td class="num">3,787</td><td class="num">16.4%</td><td><div class="bar-wrap"><div class="bar-fill cyan" style="width:16.4%"></div></div></td><td>Live top-N FD dashboard (<code>top.rs</code>, 1,008); aggregate summary + bars in static and live form (<code>summary.rs</code>, 904); classic full-screen monitor (<code>monitor.rs</code>); single-PID follow (<code>follow.rs</code>); file-open/close watch (<code>watch.rs</code>); circular-buffer FD-leak detector (<code>leak.rs</code>); iteration-diff engine for change highlighting (<code>delta.rs</code>)</td></tr>
          <tr><td style="color:var(--accent);">Single-Shot Views</td><td><code>tree, ports, stale, pipe_chain, net_map</code></td><td class="num">2,285</td><td class="num">9.9%</td><td><div class="bar-wrap"><div class="bar-fill cyan" style="width:9.9%"></div></div></td><td>Process tree with FD inheritance + per-PID type breakdown (<code>tree.rs</code>); listening-ports summary like <code>ss -tlnp</code> (<code>ports.rs</code>); deleted-file FD finder (<code>stale.rs</code>); pipe / unix-socket IPC map (<code>pipe_chain.rs</code>); connections grouped by remote host (<code>net_map.rs</code>)</td></tr>
          <tr><td style="color:var(--accent);">CLI &amp; Dispatch</td><td><code>cli, main, lib, strutil</code></td><td class="num">1,722</td><td class="num">7.5%</td><td><div class="bar-wrap"><div class="bar-fill cyan" style="width:7.5%"></div></div></td><td>clap derive parser with 38 <code>#[arg]</code> attributes and custom help display (<code>cli.rs</code>, 1,341); mode-dispatch entry point including repeat / leak-detect loops (<code>main.rs</code>, 282); library facade gated by <code>#[cfg(target_os)]</code> (<code>lib.rs</code>); safe UTF-8 truncation for fixed-width column display, no mid-codepoint slices (<code>strutil.rs</code>)</td></tr>
          <tr><td style="color:var(--accent);">Output / Serialization</td><td><code>output, json, csv_out</code></td><td class="num">1,608</td><td class="num">7.0%</td><td><div class="bar-wrap"><div class="bar-fill cyan" style="width:7.0%"></div></div></td><td>Columnar + field (<code>-F</code>) formatting with TTY-detected ANSI theming (<code>output.rs</code>, 770); serde-backed JSON array (<code>json.rs</code>, 441); RFC 4180-compliant CSV with proper quoting (<code>csv_out.rs</code>, 397)</td></tr>
          <tr><td style="color:var(--accent);">Type System</td><td><code>types</code></td><td class="num">867</td><td class="num">3.8%</td><td><div class="bar-wrap"><div class="bar-fill cyan" style="width:3.8%"></div></div></td><td>Shared cross-platform data shapes: <code>Process</code>, <code>OpenFile</code>, <code>SocketInfo</code>, <code>NetSpec</code>, <code>FdType</code>, theme enums &mdash; 11 public <code>struct</code> / <code>enum</code> definitions, all <code>#[derive(Serialize, Deserialize)]</code> for the JSON contract</td></tr>
          <tr><td style="color:var(--green);">Tests</td><td><code>tests/*.rs + #[cfg(test)] inline</code></td><td class="num">7,999</td><td class="num">25.8%</td><td><div class="bar-wrap"><div class="bar-fill green" style="width:25.8%"></div></div></td><td>1,639 <code>#[test]</code> functions across 10 integration modules (<code>tests/</code>) + 20 inline <code>#[cfg(test)]</code> blocks. Covers JSON / CSV contracts (233 tests in <code>json_and_csv_contracts.rs</code>), CLI dispatch (177 in <code>integration.rs</code>, 86 in <code>cli_combinations.rs</code>, 63 in <code>dispatch_contracts.rs</code>), library smoke / filter semantics / color output / binary aliasing</td></tr>
        </tbody>
        <tfoot><tr class="total-row"><td colspan="2" style="font-family:'Orbitron',sans-serif;font-size:10px;letter-spacing:1px;">TOTAL (incl. tests)</td><td class="num">31,056</td><td class="num">100%</td><td></td><td></td></tr></tfoot>
      </table>

      <hr class="section-rule">

      <!-- ═══════════════════════════════════════ -->
      <!-- SECTION 3: TOP 20 FILES                 -->
      <!-- ═══════════════════════════════════════ -->
      <h2 class="tutorial-title"><span class="step-hash">$</span>TOP 20 FILES BY SIZE</h2>
      <p class="tutorial-subtitle">The 20 largest files account for <strong>84.2%</strong> of the codebase (Rust under <code>src/</code> + <code>tests/</code>). <code>tui_tabs.rs</code> is by far the largest single file &mdash; the entire 7-tab dashboard, mouse handling, tooltips, and theme picker live there. The four largest test modules (<code>json_and_csv_contracts</code>, <code>integration</code>, <code>cli_combinations</code>, <code>dispatch_contracts</code>) cover 6,602 lines &mdash; every flag combination, every JSON shape, every CSV escape rule.</p>

      <table class="file-table">
        <thead><tr><th>File</th><th class="num">Lines</th><th>Role</th></tr></thead>
        <tbody>
          <tr><td>src/tui_tabs.rs</td><td class="num">5,308</td><td>Unified 7-tab TUI dashboard (TOP / SUMMARY / PORTS / TREE / NET-MAP / PIPES / STALE), mouse handling, hover &amp; right-click tooltips, theme chooser + 6-color editor, bottom-bar segments with verbose tooltips, atomic frame rendering</td></tr>
          <tr><td>src/filter.rs</td><td class="num">2,659</td><td>Selection &amp; filter composition: PID / PGID / user / command / FD / network-spec / path selectors with comma-lists, ranges, exclusion, regex; OR (default) and AND (<code>-a</code>) modes; precomputed canonical filter paths (v4.8.3 perf path)</td></tr>
          <tr><td>tests/json_and_csv_contracts.rs</td><td class="num">2,581</td><td>233 <code>#[test]</code> functions pinning every JSON field, every CSV escape rule, every per-flag output shape &mdash; refactors can&rsquo;t silently break downstream consumers</td></tr>
          <tr><td>tests/integration.rs</td><td class="num">2,090</td><td>177 end-to-end tests: full binary invocation, every flag, every output mode</td></tr>
          <tr><td>src/cli.rs</td><td class="num">1,341</td><td>clap derive args (38 <code>#[arg]</code> attributes, 20 short flags), custom help display, mode-flag groupings, version printing</td></tr>
          <tr><td>src/darwin.rs</td><td class="num">1,253</td><td>macOS libproc FFI: <code>proc_listpids</code>, <code>proc_pidinfo</code>, <code>proc_pidfdinfo</code>, <code>proc_pidpath</code>; zero-copy <code>#[repr(C)]</code> structs sized to kernel headers; per-PID FD scan parallelized with rayon</td></tr>
          <tr><td>tests/cli_combinations.rs</td><td class="num">1,142</td><td>86 tests covering every selector / mode combinator (<code>-a -p -u -i</code> AND mode, regex command match, range FDs, exclusion)</td></tr>
          <tr><td>src/top.rs</td><td class="num">1,008</td><td>Live top-N FD dashboard: sort cycling (FDs &rarr; PID &rarr; USER &rarr; REG &rarr; SOCK &rarr; PIPE &rarr; OTHER &rarr; DELTA &rarr; CMD), distribution bar column, delta tracking, <code>+/-</code> to grow / shrink N</td></tr>
          <tr><td>src/theme.rs</td><td class="num">947</td><td>31 named cyberpunk palettes (<code>NeonSprawl</code>, <code>BladeRunner</code>, <code>Matrix</code>, <code>SolarFlare</code>, &hellip;) via the <code>ThemeName</code> enum with display name + 6-color resolver; custom-palette support persisted through <code>config.rs</code></td></tr>
          <tr><td>src/summary.rs</td><td class="num">904</td><td>Aggregate FD breakdown with bar charts, top-N processes, per-user totals; doubles as a live TUI mode under <code>--summary -r N</code> via the shared <code>TuiMode</code> trait</td></tr>
          <tr><td>src/types.rs</td><td class="num">867</td><td>Cross-platform shared shapes: <code>Process</code>, <code>OpenFile</code>, <code>SocketInfo</code>, <code>NetSpec</code>, <code>FdType</code>, etc. &mdash; 11 public structs / enums, all serde-serializable for the JSON contract</td></tr>
          <tr><td>src/freebsd.rs</td><td class="num">835</td><td>FreeBSD gather: <code>sysctl(KERN_PROC_FILEDESC)</code> + <code>/proc</code> when mounted, FreeBSD-specific socket / kqueue / shm decoding</td></tr>
          <tr><td>src/linux.rs</td><td class="num">820</td><td>Linux gather: <code>/proc/&lt;pid&gt;/{stat,status,fd,fdinfo}</code> reader, /proc/net/{tcp,tcp6,udp,udp6,unix} parser for socket decoding; parallelized per-PID with rayon</td></tr>
          <tr><td>tests/dispatch_contracts.rs</td><td class="num">789</td><td>63 tests pinning the mode-dispatch matrix in <code>main.rs</code>: which mode wins when multiple flags are passed, exit-code contracts, alternate-screen lifecycle</td></tr>
          <tr><td>src/output.rs</td><td class="num">770</td><td>Columnar + field (<code>-F</code>) formatting, TTY-detected ANSI theming, cyberpunk header swap (<code>PROCESS</code>/<code>PRC</code>/<code>H4XOR</code> on TTY, <code>COMMAND</code>/<code>PID</code>/<code>USER</code> when piped), Unicode-safe column widths via <code>strutil.rs</code></td></tr>
          <tr><td>src/net_map.rs</td><td class="num">608</td><td>Network connections grouped by remote host: aggregates each peer&rsquo;s open-socket count, supports JSON output and per-user filtering</td></tr>
          <tr><td>src/delta.rs</td><td class="num">577</td><td>Iteration-diff engine for change highlighting: stable record IDs, additions in green, removals in red, used by <code>--delta -r N</code> and all live modes</td></tr>
          <tr><td>src/tui_app.rs</td><td class="num">564</td><td>Shared <code>TuiMode</code> trait + ratatui scaffold: alternate-screen entry/exit, common keybindings (<code>1-9</code>, <code>p</code> pause, <code>?</code> help, <code>c</code>/<code>C</code> theme, <code>x</code> border, <code>/</code> filter, <code>y</code> copy, <code>e</code> export, <code>q</code> quit), atomic frame rendering</td></tr>
          <tr><td>src/tree.rs</td><td class="num">556</td><td>Hierarchical process tree with FD inheritance, per-PID type breakdown (<code>[REG:12 IPv4:3 PIPE:2]</code>), notable-file listing inline; JSON tree with nested children</td></tr>
          <tr><td>src/ports.rs</td><td class="num">450</td><td>Listening-ports summary like <code>ss -tlnp</code> but cross-platform (macOS + Linux + FreeBSD); supports JSON output and per-user filtering</td></tr>
        </tbody>
        <tfoot><tr class="total-row"><td style="font-family:'Orbitron',sans-serif;font-size:10px;letter-spacing:1px;">TOP 20 SUBTOTAL</td><td class="num">26,069</td><td>84.0% of 31,056-line Rust slice</td></tr></tfoot>
      </table>

      <hr class="section-rule">

      <!-- ═══════════════════════════════════════ -->
      <!-- SECTION 4: EXECUTION PIPELINE          -->
      <!-- ═══════════════════════════════════════ -->
      <h2 class="tutorial-title"><span class="step-hash">@</span>EXECUTION PIPELINE</h2>
      <p class="tutorial-subtitle">One invocation flows through three stages: <strong>gather</strong> (platform-specific FFI, parallel per-PID), <strong>filter</strong> (composable selectors, AND/OR mode), <strong>output</strong> (one of: columnar TTY, columnar plain, JSON, CSV, field, or live TUI). The shared <code>Process</code> / <code>OpenFile</code> shape from <code>src/types.rs</code> means every backend feeds every output mode without intermediate marshaling.</p>

      <pre style="margin:0.5rem 0;padding:1rem;border:1px solid var(--border);background:var(--bg-primary);color:var(--text-dim);font-size:11px;line-height:1.7;overflow-x:auto;">
  argv  ──▶  cli.rs (clap derive, 38 args)
                  │
                  ▼
         ┌────────────────┐
         │   main.rs      │  mode dispatch:
         │   (282 lines)  │   tui / top / summary -r / monitor /
         │                │   watch / follow / leak-detect /
         │                │   tree / ports / stale / pipe-chain /
         │                │   net-map / csv / json / field / list
         └────────┬───────┘
                  │
       ┌──────────┴──────────┐
       │                     │
       ▼                     ▼
 #[cfg(target_os = …)]  filter.rs  (2,659 lines)
 ┌───────────┐           - PID / PGID / USER / CMD
 │ darwin.rs │           - FD range / exclusion
 │ (1,253)   │           - network spec (4|6|TCP|UDP|:port)
 │ libproc   │           - path canonicalization
 │ FFI       │           - AND mode (-a) / OR mode (default)
 │ repr(C)   │           - regex via /…/
 └─────┬─────┘
       │
 ┌───────────┐
 │ linux.rs  │
 │ (820)     │
 │ /proc fs  │     ┌──────────────────────┐
 │ rayon     │ ──▶ │  Vec&lt;Process&gt;        │
 │ per-PID   │     │  (types.rs, 867 ln)  │
 └─────┬─────┘     │   Process            │
       │           │   ├─ OpenFile        │
 ┌───────────┐     │   │   ├─ FdType      │
 │freebsd.rs │     │   │   └─ SocketInfo  │
 │ (835)     │     │   └─ command/uid/…   │
 │sysctl+proc│     └──────────┬───────────┘
 └───────────┘                │
                              ▼
                ┌─────────────────────────────┐
                │   output dispatch           │
                ├─────────────────────────────┤
                │ output.rs   - columnar/TTY  │
                │ json.rs     - serde JSON    │
                │ csv_out.rs  - RFC 4180      │
                │ tui_app.rs  - TuiMode trait │
                │   └─ tui_tabs, top,         │
                │      summary, monitor,      │
                │      follow, watch, leak    │
                └─────────────────────────────┘</pre>

      <hr class="section-rule">

      <!-- ═══════════════════════════════════════ -->
      <!-- SECTION 5: TYPE SYSTEM                  -->
      <!-- ═══════════════════════════════════════ -->
      <h2 class="tutorial-title"><span class="step-hash">&amp;</span>TYPE SYSTEM</h2>
      <p class="tutorial-subtitle">Eleven public <code>struct</code> / <code>enum</code> definitions in <code>src/types.rs</code> (867 lines) carry every fact between the gather, filter, and output layers. All of them <code>#[derive(Serialize, Deserialize)]</code> so the JSON contract is one derive macro &mdash; no hand-rolled escaping, no schema drift.</p>

      <div class="mapping-grid">
        <div class="mapping-card"><h4>Process</h4><p>Per-PID record: <code>pid</code>, <code>ppid</code>, <code>pgid</code>, <code>uid</code>, <code>user</code>, <code>command</code>, <code>open_files: Vec&lt;OpenFile&gt;</code>. Populated by each platform backend, consumed by every filter and output mode.</p></div>
        <div class="mapping-card"><h4>OpenFile</h4><p>One row of <code>lsof</code> output: <code>fd</code>, <code>fd_kind</code>, <code>fd_type</code> (<code>FdType</code> enum), <code>device</code>, <code>size_off</code>, <code>node</code>, <code>name</code>, optional <code>SocketInfo</code> when the FD is a socket.</p></div>
        <div class="mapping-card"><h4>FdType</h4><p>Enum of the file-descriptor classes lsofrs decodes: <code>REG</code>, <code>DIR</code>, <code>CHR</code>, <code>BLK</code>, <code>FIFO</code>, <code>PIPE</code>, <code>IPv4</code>, <code>IPv6</code>, <code>unix</code>, <code>KQUEUE</code>, <code>POSIX_SHM</code>, <code>POSIX_SEM</code>, plus platform-specific extras.</p></div>
        <div class="mapping-card"><h4>SocketInfo</h4><p>Decoded socket facts: <code>protocol</code> (TCP/UDP/unix), <code>local_addr</code> + <code>local_port</code>, <code>remote_addr</code> + <code>remote_port</code>, <code>state</code> (LISTEN/ESTABLISHED/&hellip;). Drives <code>--ports</code>, <code>--net-map</code>, and <code>-i</code> filtering.</p></div>
        <div class="mapping-card"><h4>NetSpec</h4><p>Parsed form of the <code>-i</code> argument: address-family (any/4/6), protocol (any/TCP/UDP), host, port. One parsed predicate filters against every <code>SocketInfo</code>.</p></div>
        <div class="mapping-card"><h4>FilterOptions</h4><p>The composed selector bundle handed to <code>filter.rs</code>: lists of PIDs / users / commands / FDs / paths / net-specs, plus AND-vs-OR mode bit. Single owner of every selector flag.</p></div>
        <div class="mapping-card"><h4>OutputFormat</h4><p>Enum chosen by <code>cli.rs</code>: <code>Columnar</code>, <code>Field(&lt;chars&gt;)</code>, <code>Json</code>, <code>Csv</code>, <code>Terse</code>. <code>main.rs</code> picks the right serializer per variant.</p></div>
        <div class="mapping-card"><h4>ThemeName</h4><p>31-variant enum of named cyberpunk palettes (<code>NeonSprawl</code>, <code>BladeRunner</code>, <code>Matrix</code>, &hellip;). <code>ThemeName::ALL</code> is the canonical iteration order used by the theme chooser.</p></div>
        <div class="mapping-card"><h4>LsofTheme</h4><p>Resolved 6-color palette ready for ratatui: header / row / accent / good / warn / bad. Built from a <code>ThemeName</code> or from a custom palette loaded out of <code>~/.lsofrs.conf</code>.</p></div>
        <div class="mapping-card"><h4>SortColumn / SortDir</h4><p>Cycled by <code>s</code> / <code>r</code> in the top dashboard. Drives all live-mode column ordering through the shared <code>TuiMode</code> trait.</p></div>
        <div class="mapping-card"><h4>DeltaState</h4><p>Iteration-to-iteration diff bookkeeping consumed by <code>delta.rs</code>. Tracks which records are NEW (green), GONE (red), or UNCHANGED so live modes can highlight churn.</p></div>
      </div>

      <hr class="section-rule">

      <!-- ═══════════════════════════════════════ -->
      <!-- SECTION 6: MODES MATRIX                 -->
      <!-- ═══════════════════════════════════════ -->
      <h2 class="tutorial-title"><span class="step-hash">%</span>MODES MATRIX</h2>
      <p class="tutorial-subtitle">Fifteen distinct entry points dispatched from <code>main.rs</code> based on which flag is set. Single-shot modes print and exit; live modes share <code>tui_app.rs</code>'s <code>TuiMode</code> trait for common keybindings and atomic frame rendering.</p>

      <table class="file-table">
        <thead><tr><th>Flag</th><th>Module</th><th class="num">Lines</th><th>Mode</th><th>Description</th></tr></thead>
        <tbody>
          <tr><td><code>(default)</code></td><td><code>output.rs</code></td><td class="num">770</td><td>single-shot</td><td>Columnar output (cyberpunk on TTY, plain when piped); the original <code>lsof</code> default</td></tr>
          <tr><td><code>--tui</code></td><td><code>tui_tabs.rs</code></td><td class="num">5,308</td><td>live (TuiMode)</td><td>Unified 7-tab dashboard with mouse + tooltips + theme picker + theme editor</td></tr>
          <tr><td><code>--top [N]</code></td><td><code>top.rs</code></td><td class="num">1,008</td><td>live (TuiMode)</td><td>Live top-N processes sorted by FD count with distribution bars + delta</td></tr>
          <tr><td><code>--summary [-r N]</code></td><td><code>summary.rs</code></td><td class="num">904</td><td>both</td><td>Aggregate stats + bar charts; <code>-r N</code> turns it into a live TuiMode</td></tr>
          <tr><td><code>--monitor</code> / <code>-W</code></td><td><code>monitor.rs</code></td><td class="num">288</td><td>live (TuiMode)</td><td>Classic full-screen alternate-buffer monitor (<code>top(1)</code>-style)</td></tr>
          <tr><td><code>--watch FILE</code></td><td><code>watch.rs</code></td><td class="num">369</td><td>live (stream)</td><td>Timestamped <code>+OPEN</code> / <code>-CLOSE</code> events for a single path</td></tr>
          <tr><td><code>--follow PID</code></td><td><code>follow.rs</code></td><td class="num">192</td><td>live</td><td>Single-PID FD tracker; new opens <code>+NEW</code> green, closes <code>-DEL</code> red</td></tr>
          <tr><td><code>--leak-detect[=I,N]</code></td><td><code>leak.rs</code></td><td class="num">339</td><td>live</td><td>Per-PID FD-count sampler with circular buffer; flags monotonically increasing counts</td></tr>
          <tr><td><code>--tree</code></td><td><code>tree.rs</code></td><td class="num">556</td><td>single-shot</td><td>Hierarchical process tree with per-PID FD-type breakdown</td></tr>
          <tr><td><code>--ports</code></td><td><code>ports.rs</code></td><td class="num">450</td><td>single-shot</td><td>Listening-ports summary like <code>ss -tlnp</code>, cross-platform</td></tr>
          <tr><td><code>--stale</code></td><td><code>stale.rs</code></td><td class="num">313</td><td>single-shot</td><td>Deleted-file FD finder (disk-space leak / zombie-handle detector)</td></tr>
          <tr><td><code>--pipe-chain</code></td><td><code>pipe_chain.rs</code></td><td class="num">358</td><td>single-shot</td><td>Pipe + unix-socket IPC topology between processes</td></tr>
          <tr><td><code>--net-map</code></td><td><code>net_map.rs</code></td><td class="num">608</td><td>single-shot</td><td>Network connections grouped by remote host</td></tr>
          <tr><td><code>--csv</code></td><td><code>csv_out.rs</code></td><td class="num">397</td><td>single-shot</td><td>RFC 4180-compliant CSV export</td></tr>
          <tr><td><code>--json</code> / <code>-J</code></td><td><code>json.rs</code></td><td class="num">441</td><td>single-shot</td><td>Serde-backed JSON array (full <code>OpenFile</code> shape)</td></tr>
          <tr><td><code>--delta -r N</code></td><td><code>delta.rs</code></td><td class="num">577</td><td>live (repeat)</td><td>Color-coded diff between iterations (green=new, red=gone)</td></tr>
          <tr><td><code>-F &lt;chars&gt;</code></td><td><code>output.rs</code></td><td class="num">&mdash;</td><td>single-shot</td><td>Per-record field output (p=pid, c=cmd, f=fd, n=name, &hellip;) &mdash; lsof-compatible</td></tr>
          <tr><td><code>-t</code></td><td><code>output.rs</code></td><td class="num">&mdash;</td><td>single-shot</td><td>Terse PIDs-only output (scripting path)</td></tr>
        </tbody>
      </table>

      <hr class="section-rule">

      <!-- ═══════════════════════════════════════ -->
      <!-- SECTION 7: DEPENDENCY AUDIT             -->
      <!-- ═══════════════════════════════════════ -->
      <h2 class="tutorial-title"><span class="step-hash">!</span>DEPENDENCY AUDIT</h2>
      <p class="tutorial-subtitle">Thirteen direct dependencies, all foundational crates from the durable-by-design tier (<code>libc</code>, <code>nix</code>, <code>serde</code>, <code>clap</code>, <code>regex</code>, <code>rayon</code>, <code>chrono</code>) with one TUI layer (<code>ratatui</code> + <code>crossterm</code>). Picked for "will this still build cleanly in 2035" &mdash; no curl-pipe-bash installers, no flaky build scripts, no nightly-only features.</p>

      <table class="file-table">
        <thead><tr><th>Crate</th><th>Version</th><th>Purpose</th></tr></thead>
        <tbody>
          <tr><td><code>clap</code></td><td>4.x (derive)</td><td>CLI parsing with derive macros &mdash; backs <code>cli.rs</code>'s 38 <code>#[arg]</code> attributes</td></tr>
          <tr><td><code>serde</code></td><td>1.x (derive)</td><td>Trait-driven serialization for JSON + TOML config; one derive macro pins the JSON contract</td></tr>
          <tr><td><code>serde_json</code></td><td>1.x</td><td>JSON serializer used by <code>--json</code> / <code>-J</code></td></tr>
          <tr><td><code>crossterm</code></td><td>0.29</td><td>Terminal control (alternate screen, raw mode, key/mouse events) under every live mode</td></tr>
          <tr><td><code>libc</code></td><td>0.2</td><td>Raw FFI types for Darwin libproc structs and POSIX <code>getpwuid</code></td></tr>
          <tr><td><code>nix</code></td><td>0.31</td><td>Safe POSIX bindings &mdash; signal, term, ioctl, user, fs, net, hostname feature set</td></tr>
          <tr><td><code>users</code></td><td>0.11</td><td>Username lookup wrapped in a <code>OnceCell</code> cache (v4.8.3 hot-path optimization)</td></tr>
          <tr><td><code>regex</code></td><td>1.x</td><td>Regex matcher for command-name selectors via <code>/&hellip;/</code> grammar</td></tr>
          <tr><td><code>chrono</code></td><td>0.4</td><td>Timestamps for <code>--watch</code> open/close events and live-mode footers</td></tr>
          <tr><td><code>rayon</code></td><td>1.x</td><td>Work-stealing thread pool for per-PID FD enumeration &mdash; CPU count = parallel degree</td></tr>
          <tr><td><code>ratatui</code></td><td>0.30</td><td>TUI rendering primitives (widgets, layouts, frame) under <code>tui_app.rs</code>'s <code>TuiMode</code> trait</td></tr>
          <tr><td><code>toml</code></td><td>1.1</td><td>Config-file parser for <code>~/.lsofrs.conf</code> (theme persistence + custom palettes)</td></tr>
          <tr><td><code>dirs</code></td><td>6.x</td><td>OS-correct home-directory lookup for <code>~/.lsofrs.conf</code></td></tr>
        </tbody>
        <tfoot><tr class="total-row"><td>TOTAL</td><td>13 direct</td><td>All from the foundational / durable tier &mdash; no nightly, no proc-macro-heavy frameworks, no opinionated DI containers</td></tr></tfoot>
      </table>

      <hr class="section-rule">

      <!-- ═══════════════════════════════════════ -->
      <!-- SECTION 8: TEST SURFACE                 -->
      <!-- ═══════════════════════════════════════ -->
      <h2 class="tutorial-title"><span class="step-hash">?</span>TEST SURFACE</h2>
      <p class="tutorial-subtitle">1,639 <code>#[test]</code> functions across 10 integration modules and 20 inline <code>#[cfg(test)]</code> blocks. The integration suite is contract-heavy: every JSON field, every CSV escape, every CLI flag combination is pinned so refactors can't silently shift output shape.</p>

      <table class="file-table">
        <thead><tr><th>Test Module</th><th class="num">Lines</th><th class="num">#[test]</th><th>Focus</th></tr></thead>
        <tbody>
          <tr><td>tests/json_and_csv_contracts.rs</td><td class="num">2,581</td><td class="num">233</td><td>Every JSON field, every CSV escape, every per-flag output shape</td></tr>
          <tr><td>tests/integration.rs</td><td class="num">2,090</td><td class="num">177</td><td>End-to-end binary invocation, every flag, every output mode</td></tr>
          <tr><td>tests/cli_combinations.rs</td><td class="num">1,142</td><td class="num">86</td><td>Selector / mode combinator matrix (<code>-a -p -u -i</code>, regex, ranges)</td></tr>
          <tr><td>tests/dispatch_contracts.rs</td><td class="num">789</td><td class="num">63</td><td>Mode-dispatch precedence in <code>main.rs</code>, exit-code contracts, alternate-screen lifecycle</td></tr>
          <tr><td>tests/library_smoke.rs</td><td class="num">393</td><td class="num">28</td><td>Library-crate facade smoke tests (<code>pub mod cli; pub mod types; pub mod filter; &hellip;</code>)</td></tr>
          <tr><td>tests/json_wrappers.rs</td><td class="num">391</td><td class="num">43</td><td>JSON envelope shapes for the structured modes (summary, ports, tree, &hellip;)</td></tr>
          <tr><td>tests/filters_and_paths.rs</td><td class="num">268</td><td class="num">26</td><td>Path canonicalization, FD-range parsing, exclusion grammar</td></tr>
          <tr><td>tests/json_shape.rs</td><td class="num">147</td><td class="num">13</td><td>Top-level JSON shape pins for the default <code>--json</code> output</td></tr>
          <tr><td>tests/color_output.rs</td><td class="num">146</td><td class="num">6</td><td>TTY-vs-piped ANSI behavior &mdash; cyberpunk on TTY, plain when piped</td></tr>
          <tr><td>tests/lsf_binary.rs</td><td class="num">52</td><td class="num">5</td><td><code>lsf</code> short-form alias contract &mdash; same binary, same behavior, both bin targets in Cargo.toml</td></tr>
        </tbody>
        <tfoot>
          <tr class="total-row"><td>tests/ subtotal</td><td class="num">7,999</td><td class="num">680</td><td>10 integration modules</td></tr>
          <tr class="total-row"><td>+ inline #[cfg(test)] in src/</td><td class="num">&mdash;</td><td class="num">947</td><td>20 modules with inline test blocks (filter.rs alone has 208 inline tests)</td></tr>
          <tr class="total-row" style="border-top:2px solid var(--accent);"><td><strong>TOTAL</strong></td><td class="num"><strong>7,999+</strong></td><td class="num"><strong>1,639</strong></td><td><strong>Pinned contracts across CLI, JSON, CSV, filters, dispatch, color, library facade</strong></td></tr>
        </tfoot>
      </table>

      <hr class="section-rule">

      <!-- ═══════════════════════════════════════ -->
      <!-- SECTION 9: SHIPPING ARTIFACTS           -->
      <!-- ═══════════════════════════════════════ -->
      <h2 class="tutorial-title"><span class="step-hash">*</span>SHIPPING ARTIFACTS</h2>

      <div class="feature-grid">
        <div class="feature-card">
          <h4>Two binaries from one source</h4>
          <p>Cargo.toml declares both <code>[[bin]] name = "lsofrs"</code> and <code>[[bin]] name = "lsf"</code> at the same <code>src/main.rs</code> path. The short form is a quicker-to-type alias &mdash; same code, same behavior, both end up on <code>PATH</code> after <code>cargo install lsofrs</code>.</p>
        </div>
        <div class="feature-card">
          <h4>Man page</h4>
          <p>310-line roff page at <code>lsofrs.1</code> &mdash; every flag documented, copy with <code>sudo cp lsofrs.1 /usr/local/share/man/man1/</code> then <code>man lsofrs</code>.</p>
        </div>
        <div class="feature-card">
          <h4>Zsh completion</h4>
          <p>101-line <code>#compdef lsofrs</code> script at <code>completions/_lsofrs</code>; drop into any <code>fpath</code> entry (e.g. <code>/usr/local/share/zsh/site-functions/</code>) and reload via <code>compinit</code>.</p>
        </div>
        <div class="feature-card">
          <h4>Library crate</h4>
          <p>The <code>lsofrs</code> crate also exposes a library facade (<code>pub mod cli; pub mod types; pub mod filter; pub mod output; pub mod json; pub mod csv_out; &hellip;</code>) gated by <code>#[cfg(target_os)]</code> so downstream tools can reuse the gather + filter + serialize stack without shelling out. Documented on <a href="https://docs.rs/lsofrs">docs.rs/lsofrs</a>.</p>
        </div>
        <div class="feature-card">
          <h4>Config persistence</h4>
          <p><code>~/.lsofrs.conf</code> is a TOML file written by <code>src/config.rs</code>. Stores the last-used <code>--tui</code> tab, the active <code>ThemeName</code>, and any custom 6-color palettes built in the in-app editor (<code>C</code>).</p>
        </div>
        <div class="feature-card">
          <h4>CI</h4>
          <p>GitHub Actions workflow at <code>.github/workflows/ci.yml</code> &mdash; rustfmt, clippy, test on stable. Rust toolchain pinned to <code>stable</code> via <code>rust-toolchain.toml</code> with <code>rustfmt</code> + <code>clippy</code> components required.</p>
        </div>
      </div>

      <hr class="section-rule">

      <!-- ═══════════════════════════════════════ -->
      <!-- SECTION 10: INVARIANTS                  -->
      <!-- ═══════════════════════════════════════ -->
      <h2 class="tutorial-title"><span class="step-hash">=</span>INVARIANTS</h2>
      <p class="tutorial-subtitle">Constraints the codebase enforces or relies on. Listed here so future changes know which lines are load-bearing.</p>

      <div class="feature-grid">
        <div class="feature-card">
          <h4>Single gather shape</h4>
          <p>All three platform backends must return <code>Vec&lt;Process&gt;</code> using the same <code>Process</code> / <code>OpenFile</code> / <code>SocketInfo</code> types from <code>src/types.rs</code>. Every output mode + filter consumes the same shape &mdash; no per-platform output branches.</p>
        </div>
        <div class="feature-card">
          <h4>TTY-detected coloring only</h4>
          <p>Output coloring + cyberpunk headers (<code>H4XOR</code>, <code>CL4SS</code>, <code>T4RGET</code>) are gated by <code>std::io::IsTerminal</code>. Pipelines always get plain ASCII headers and no ANSI escapes &mdash; safe for downstream tools like <code>jq</code>, <code>awk</code>, spreadsheets.</p>
        </div>
        <div class="feature-card">
          <h4>JSON contract is pinned</h4>
          <p>233 tests in <code>tests/json_and_csv_contracts.rs</code> + 43 in <code>tests/json_wrappers.rs</code> + 13 in <code>tests/json_shape.rs</code> = <strong>289 JSON-shape tests</strong>. Renaming a field in <code>OpenFile</code> breaks the build until tests are updated &mdash; downstream consumers can't be silently broken.</p>
        </div>
        <div class="feature-card">
          <h4>No mid-codepoint slices</h4>
          <p><code>src/strutil.rs</code> provides safe UTF-8 truncation for fixed-width column display. Output code never slices bytes by index &mdash; international filenames + emoji process names display cleanly without panics.</p>
        </div>
        <div class="feature-card">
          <h4>One shared TuiMode trait</h4>
          <p>Every live mode (<code>--tui</code>, <code>--top</code>, <code>--summary -r</code>, <code>--monitor</code>, &hellip;) implements the <code>TuiMode</code> trait in <code>src/tui_app.rs</code>. Adding a new keybinding once propagates everywhere; no per-mode reimplementations of <code>q</code> / <code>p</code> / <code>?</code> / <code>c</code>.</p>
        </div>
        <div class="feature-card">
          <h4>OR mode is the default</h4>
          <p>Selectors compose with OR by default; <code>-a</code> switches the whole predicate to AND. This matches lsof's grammar so existing scripts port directly &mdash; pinned by tests in <code>cli_combinations.rs</code>.</p>
        </div>
        <div class="feature-card">
          <h4>Zero-allocation hot loop</h4>
          <p>v4.8.3 removed every per-record allocation from the gather loop: <code>OnceCell</code>-cached <code>getpwuid</code>, precomputed filter paths, no per-FD intermediate <code>Vec</code>. This is what bought the 12&times; default-mode speedup over <code>lsof 4.91</code> &mdash; future PRs touching <code>darwin.rs</code> / <code>linux.rs</code> / <code>filter.rs</code> hot paths should re-bench with <code>hyperfine</code>.</p>
        </div>
        <div class="feature-card">
          <h4>Both binaries always ship together</h4>
          <p><code>Cargo.toml</code> has <code>[[bin]] name = "lsofrs"</code> and <code>[[bin]] name = "lsf"</code> at the same <code>src/main.rs</code> path. <code>tests/lsf_binary.rs</code> pins this aliasing &mdash; <code>lsf</code> must remain a name-only alias forever; behavior divergence is a regression.</p>
        </div>
      </div>

      <hr class="section-rule">

      <!-- ═══════════════════════════════════════ -->
      <!-- SECTION 11: WHERE NEXT                  -->
      <!-- ═══════════════════════════════════════ -->
      <h2 class="tutorial-title"><span class="step-hash">&gt;&gt;</span>WHERE NEXT</h2>
      <ul style="font-size:13px;color:var(--text-dim);line-height:1.7;">
        <li><a href="index.html">Docs index</a> &mdash; quickstart, mode catalog, selection grammar, JSON shape, interactive controls.</li>
        <li><a href="https://github.com/MenkeTechnologies/lsofrs#readme">README</a> &mdash; install, every flag with examples, full benchmark table, screenshots.</li>
        <li><a href="https://docs.rs/lsofrs">docs.rs/lsofrs</a> &mdash; rustdoc for the library crate.</li>
        <li><a href="https://github.com/MenkeTechnologies/lsofrs/blob/main/lsofrs.1">lsofrs.1</a> &mdash; full man page (also <code>man lsofrs</code> after install).</li>
        <li><a href="https://github.com/MenkeTechnologies/lsofrs/issues">Issues</a> &mdash; bug reports, feature requests, perf regressions.</li>
        <li><a href="https://crates.io/crates/lsofrs">crates.io/crates/lsofrs</a> &mdash; releases, downloads, version history.</li>
      </ul>
    </main>

    <footer style="border-top:1px dashed var(--border);padding:1rem 1.5rem;text-align:center;font-family:'Share Tech Mono',ui-monospace,monospace;font-size:11px;color:var(--text-muted);">
      lsofrs v4.8.3 engineering report &middot; built by <a href="https://github.com/MenkeTechnologies">MenkeTechnologies</a> &middot;
      <a href="index.html">Docs</a> &middot;
      <a href="https://github.com/MenkeTechnologies/lsofrs">GitHub</a> &middot;
      <a href="https://crates.io/crates/lsofrs">crates.io</a> &middot;
      <a href="https://docs.rs/lsofrs">docs.rs</a>
    </footer>
  </div>

  <script src="hud-theme.js"></script>
</body>
</html>