bee-tui 1.9.0

Production-grade k9s-style terminal cockpit for Ethereum Swarm Bee node operators.
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
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
# bee-tui — Production Plan v2

**Status:** canonical. Supersedes [`archive/PLAN-v1.md`](archive/PLAN-v1.md).

**Stack:** Rust + Ratatui 0.30 + Tokio + crossterm 0.29 on top of bee-rs ≥ 1.3.0.

**One sentence:** bee-tui is the operator's truth-teller — a live terminal cockpit that surfaces the state Bee's API hides (bucket collisions, redistribution skip reasons, unhealthy gates, NAT reality).

---

## 0. What changed from v1 (read this first)

Four streams of research reshaped the plan:

| Source | Insight | Plan change |
|---|---|---|
| Operator pain points | Top 3 anxieties are **stamps, cheques, "why am I unhealthy/not earning"** — not multi-node | Pivot v0.1 to single-node-deep. Multi-node moves to v0.4 |
| Operator pain points | Bee community is actively **retiring depth+amount** in favor of volume+duration (issue #4992) | Stamp screen leads with volume+duration; depth/amount in collapsible "advanced" |
| Bee internals | `utilization` = **MaxBucketCount, not average** — the API itself is misleading | Stamp screen MUST show bucket histogram; "100% utilization" is the symptom, not the cause |
| Bee internals | `LastPlayedRound` is stale when frozen/skipped; redistribution has 4 phases per 152-block round | Lottery screen with phase-by-phase round timeline + skip reasons |
| Competitor cockpits | Lightning's lntop archived (190★ peak); ipfs-tui dead at 20★; lncli-curses dead at 7★ | Risk is real. Validation step required before coding |
| Competitor cockpits | k9s playbook on a Web3 node is **untried** — that's the actual opportunity | Adopt k9s patterns wholesale: `:command`, watch/informer split, command log |
| 2026 Ratatui SOTA | Community converged on **official `component` template** — not gitui-style, not Elm | Switch architecture: `cargo generate ratatui/templates component` as starting point |
| 2026 Ratatui SOTA | `color-eyre`, `config 0.15`, `crossterm 0.29`, `ratatui 0.30`, `tokio-tungstenite 0.29`, `tachyonfx 0.25` (now under ratatui org) | Updated BoM throughout |

---

## 1. Validation gate (do this BEFORE writing code)

The strongest signal from the prior-art survey: **every previous decentralized-storage TUI died from author-only adoption.** ipfs-tui (20★, dead), lncli-curses (7★, dead), lntop (190★ peak, archived 2025). The structural reason: most network operators run fleets behind Prometheus/Grafana, not single nodes in tmux.

**Mitigation:** Before any code, validate by posting a 60-second screenshot mockup in the `#node-operators` Discord channel (forum is dead per research) and asking five concrete questions:

1. Would you run this in tmux daily?
2. Which one screen would you actually use? (we want answers, not "all of them")
3. Are you already using bee-dashboard / Grafana? Why or why not?
4. What's the one thing you wish you could see live that you can't today?
5. Would you trust it to issue cashouts/topups/dilutes?

**Success criteria for proceeding:** ≥3 of 5 say "yes, daily" AND there's convergence on which screens matter.

**Failure criteria:** "I just curl the API + check Grafana" wins → don't build the TUI; ship a curated Grafana dashboard pack instead.

This is one week of patience that saves three months of misdirected building.

---

## 2. Product framing (sharpened)

**Audience size (honest):** ~100s–low-1000s of serious Bee operators today, of whom ~20 are highly engaged power-users (ldeffenb, crtahlin, 0xCardiE, mfw78, attila-lendvai recur across years of issues). Day-one realistic adoption: 50–200 people. This will never be a "popular" tool. It can be the *de-facto* tool for the people who matter.

**The thesis:** Bee operators are developer-leaning (gateways, debugging, exploration), not passive stakers. That's the same audience that adopted k9s and lazygit. The niche is empty because nobody has tried the **k9s playbook** on a Web3 node — every previous attempt was a viewer (lntop) or installer (EthPillar). Be the first.

---

## 3. Product principles (from research)

1. **Truth over API.** Where the API misleads (`utilization` = max-bucket, `isReachable` flickers under symmetric NAT, `LastPlayedRound` stale on skip), the TUI shows the real state with explanation. This is the #1 differentiator vs bee-dashboard.
2. **Encode tribal knowledge.** Institutional Bee wisdom lives in GitHub issue threads. Surface it inline: tooltips, micro-explanations, "why this matters" hints. Operators learn by reading the TUI.
3. **Confirmation-gated writes.** Operators have low trust (issue #5216 lost data in 1.x.y migration just weeks before research). Every state-changing action — cashout, dilute, topup, disconnect — needs a dry-run + confirm step.
4. **Transparent like lazygit.** Show the underlying HTTP request/response in a command log pane. Operators trust transparent tools.
5. **Diagnostic-bundle export.** One key produces a Discord-paste-ready snippet for `#node-operators`. Forum is dead, chat is alive — meet operators where they support each other.
6. **Watch streams, not polling-from-views.** k9s pattern: a watch/informer layer feeds an event bus; views subscribe. Don't poll inside render code.
7. **Per-view refresh policy.** tig pattern: `auto` / `periodic(Ns)` / `manual` / `after-command`. Health screen wants 2s; chunk explorer wants manual; queue wants stream.
8. **Configurable columns.** lntop pattern: gateway operator vs storage staker vs developer want different columns. Make peer/stamp tables column-configurable from `bee-tui.toml`.

---

## 4. Final scope: what's IN v1.0

Operator-pain-validated screen list, ranked by severity × frequency from research:

| # | Screen | Top pain it solves | API surface | Severity |
|---|---|---|---|---|
| 1 | **Health gates** | "Why is my node unhealthy?" (#5428, #5396) | `/health`, `/readiness`, `/redistributionstate`, `/status`, `/chainstate`, `/wallet` | Severe |
| 2 | **Stamps** (volume+duration first; bucket histogram; usable countdown) | Depth/amount confusion (#4992) + bucket collision (#4292, #3122) + "is this batch usable yet" | `/stamps`, `/stamps/{id}`, `/stamps/{id}/buckets`, `/chainstate` | Severe |
| 3 | **SWAP / cheques** (uncashed-IN list, cashout candidates, gas break-even) | Cheque cashout confusion (#4663 + 8 dupes over 4 years) | `/balances`, `/timesettlements`, `/settlements`, `/chequebook/*`, `/wallet` | Severe |
| 4 | **Lottery** (round phases, skip reasons, last 20 rounds, rchash benchmark) | "Why am I not earning rewards?" (#4849) | `/redistributionstate`, `/stake`, `/rchash` | High |
| 5 | **Warmup** (phased progress on first launch) | 25–60min cold-start opacity (#4746) | `/health`, `/readiness`, `/status`, `/topology` | High |
| 6 | **Peers + bin saturation** | Sync/topology confusion + per-bin starvation hidden in API | `/status/peers`, `/peers`, `/topology`, `/blocklist`, `/pingpong` | High |
| 7 | **Reachability/NAT** (public addrs, inbound count, AutoNAT) | "I have peers but I'm unreachable?" (#4194, #5380) | `/status`, `/topology`, `/addresses` | Medium |
| 8 | **RPC health** (block delta, error rate, pending tx queue) | RPC fragility blamed on Bee (#4419, #5388 et al) | `/chainstate`, `/transactions` | Medium |
| 9 | **Tags + uploads** (synced/seen/sent live) | Pinning large files broken (#4864), upload progress missing | `/tags`, `/tags/{uid}`, WS `/chunks/stream` | Medium |
| 10 | **Command log** (HTTP req/res tail, lazygit-style) | Trust + tutorial + "logs are broken" (#4636) | All — passive observer | Medium-foundational |

### What's OUT of v1.0

- **Multi-node** — moves to v0.4 after single-node-deep is proven loved
- **PSS/GSOC chat console** — niche; v0.5
- **Mantaray browser** — niche; v0.5
- **Identity/key management** — security-sensitive, stays in swarm-cli
- **Big file uploads** — pipe to a file from CLI, not a TUI
- **Manifest authoring / ACT grantee editing** — forms territory, swarm-cli

---

## 5. bee-rs prerequisites (bee-rs v1.3.0)

Confirmed from the audit at `bee-rs` source (v1.2.0):

| # | Gap | Effort | Why blocking |
|---|---|---|---|
| 1 | `/chunks/stream` WebSocket upload | Medium (~150 LOC) | Tags/uploads screen WS path |
| 2 | `/rchash/{depth}/{a1}/{a2}` endpoint | Small (~30 LOC) | Lottery rchash benchmark button |
| 3 | `/timesettlements` endpoint | Small (~25 LOC) | SWAP refresh-vs-cheque split |
| 4 | `Client::with_token(url, token)` constructor | Small | Token-protected nodes |
| 5 | `tracing::instrument` on `Inner::send` | Small | Command log pane gets timings free |
| 6 | `Client::ping() -> Duration` | Trivial | Connection-status bar |

Audit highlights the WS plumbing already exists in `pss/mod.rs:14`, `gsoc/mod.rs:73` (tokio-tungstenite 0.24); item 1 inverts the existing `Subscription` pattern from receiver-of-Bytes to sender-of-Bytes.

**Ship bee-rs 1.3.0 BEFORE bee-tui v0.1.** This is one week of work and it unblocks everything.

See [`research/03-bee-rs-audit.md`](research/03-bee-rs-audit.md) for the full audit.

---

## 6. Architecture (final, current to mid-2026)

### Starting point

```bash
cargo generate --git https://github.com/ratatui/templates component bee-tui
```

The official `component` template is the 2026 de-facto starting point. Every actively-developed multi-pane Ratatui app converged on this shape (television, atuin, gitui, b4n, lazyjj). Don't re-derive it.

### Architectural pattern

**k9s playbook + Ratatui component template:**

```
┌─────────────────────────────────────────────────────┐
│                       App                           │
│  - root CancellationToken                           │
│  - mpsc::UnboundedSender<Action>  (single ingest)   │
│  - Vec<Box<dyn Component>>                          │
│  - BeeWatch (watch/informer layer)                  │
└──────────────┬──────────────────────────────────────┘
               │ subscribes via watch::Receiver<T>
   ┌───────────┴───────────────┐
   │       BeeWatch            │  separate from views
   │  - per-resource pollers   │  (k9s pattern)
   │  - WS subscribers         │
   │  - publish to watch::Sender│
   └─────┬─────────────┬────────┘
         │             │
   ┌─────▼────┐  ┌─────▼────┐
   │ poll_status │ │ poll_stamps │  ...
   │ tick=2s     │ │ tick=10s    │
   │ child token │ │ child token │
   └────────────┘ └────────────┘
```

### Rules locked in

1. **`crossterm::event::EventStream`** with `StreamExt::fuse()` — drop the read-thread.
2. **Two intervals, not one.** `tick` at 250ms (logic) + `render` at 16–33ms (60fps cap). Atuin and Television both do this.
3. **`tokio::select!` directly.** No higher-level abstraction has displaced it. Skip rat-salsa unless you need its built-in dialog plumbing.
4. **`tokio_util::sync::CancellationToken` parent/child tree.** App owns root token; each long task gets a child. Quitting cancels root; switching context cancels subtree. This is what avoids atuin-style "queue requests forever" with Bee's 30–60s feed lookups.
5. **`watch::Sender<Snapshot>` per long-poll source** — last-value semantics, no backpressure.
6. **`mpsc::UnboundedSender<Action>` for inputs and triggered actions.**
7. **Stream-driven WS via `tokio-tungstenite 0.29`** — already in bee-rs for PSS/GSOC; reused for `/chunks/stream`.

### Module layout

```
bee-tui/
  Cargo.toml
  src/
    main.rs                 # tokio::main, color_eyre::install, terminal::init
    app.rs                  # App, run loop, Action enum
    action.rs               # Action variants (input + watch updates + side-effects)
    components/             # Component trait per ratatui template
      mod.rs
      health/               # screen 1 — health gates
      stamps/               # screen 2 — stamps (volume+duration UX)
      swap/                 # screen 3 — cheques + cashouts
      lottery/              # screen 4 — redistribution rounds
      warmup/               # screen 5 — phased bootstrap
      peers/                # screen 6 — peers + bin saturation
      network/              # screen 7 — NAT + reachability
      rpc/                  # screen 8 — RPC health
      uploads/              # screen 9 — tags + chunks/stream WS
      command_log/          # screen 10 — HTTP req/res tail
      overlay/
        help.rs
        command_bar.rs      # k9s :command
        confirm.rs          # write-action confirmation
        diagnostic_bundle.rs # Discord-paste export
    watch/                  # k9s-style watch/informer layer
      mod.rs
      status.rs             # /status @ 2s
      stamps.rs             # /stamps @ 10s; /buckets on focus
      redistribution.rs     # /redistributionstate @ block-rate
      chequebook.rs         # /chequebook/cheque @ 30s
      tags.rs               # /tags @ 5s
      chunks_stream.rs      # WS /chunks/stream
    api/
      client.rs             # bee_rs::Client wrapper + ping helper
      ratelimit.rs          # ensure we don't DoS our own node
    state/
      snapshot.rs           # typed cache of latest watch values
      derived.rs            # derived state (worst_bucket_eta, gas_breakeven)
    config.rs               # config 0.15 layered TOML + env
    theme.rs                # 4-color severity palette
    keymap.rs               # tig-style layered: view → generic → builtin
    tui.rs                  # terminal init/restore + panic hook
    tracing_init.rs         # tracing-appender to file, never stdout
    diagnostic.rs           # bundle generator
```

### Anti-patterns avoided (from research)

| Avoid | Why | Source |
|---|---|---|
| god `App` struct with 25+ popup fields | gitui's pain point; unmaintainable | gitui review |
| Bubble Tea-style single `Update` switch | Doesn't scale to multi-stream watch | ipfs-tui died from this |
| Polling from inside view code | Tightly couples view to backend; blocks UI | k9s separates explicitly |
| Mouse-required navigation | Operators are over SSH | universal |
| One global refresh rate | Bee state changes at 4 different rates (block, status, stamps, peers) | tig has been right since 2006 |
| Hardcoded columns | Gateway op vs storage op vs dev want different views | lntop |
| Modal dialog soup | Operators hate it | every honest review |
| Truecolor gradients | Poor contrast under SSH; color-blind hostile | btop's mistake |
| Nerd Fonts dependency | SSH-from-anywhere reality | universal |

---

## 7. Library BoM (verified mid-2026)

| Crate | Version | Purpose |
|---|---|---|
| `ratatui` | 0.30.0 | Core; modular workspace, no_std-capable |
| `crossterm` | 0.29.0 | Terminal backend |
| `tokio` | 1.52.x | Async runtime, `features = ["full"]` |
| `tokio-util` | 0.7.x | `CancellationToken`, `StreamExt` |
| `tokio-tungstenite` | 0.29.0 | WebSocket client (matches bee-rs upgrade) |
| `bee-rs` | ≥1.3.0 | API client (with prereq work shipped) |
| `color-eyre` | 0.6.5 | Errors + panic hook (replaces anyhow) |
| `tracing` + `tracing-subscriber` + `tracing-appender` | current | File logging only; never stdout |
| `config` | 0.15.x | Layered TOML+env (replaces figment) |
| `directories` | 6.x | XDG paths |
| `serde` + `toml` | current | |
| `tui-input` | 0.15.x | Command bar + form fields |
| `tui-tree-widget` | 0.24.x | Mantaray browser (v0.5) |
| `tui-popup` | 0.7.x | Confirm dialogs |
| `throbber-widgets-tui` | 0.11.x | Spinners during async ops |
| `tachyonfx` | 0.25.x | State-transition effects (now under `ratatui/` org) |
| `indexmap` | current | Stable peer iteration |
| `nucleo` | 0.5.0 | Fuzzy search across stamps/peers/contexts |
| `insta` | 1.47.x | Snapshot tests on rendered `Buffer` |
| `wiremock` | current | Fake Bee responses for integration tests |
| `assert_cmd` | current | Binary smoke tests |
| `cargo-dist` | 0.31.x | Release pipeline (still named cargo-dist on crates.io) |
| `vhs` (charmbracelet) | binary | Visual regression in CI |

**Optional:** `ratzilla 0.3.x` — bonus path to ship a WASM web build of bee-tui from the same code. Defer to v1.x.

**MSRV:** 1.85 (matches bee-rs).

---

## 8. Screen specs (operator-validated)

Each screen below names: data shown, API source, refresh policy, key actions, and the *operator anxiety it answers*.

### S1. Health gates (default screen on launch)

**Anxiety:** "Why is my node unhealthy?" (#5428, #5396, #4849)

**Layout:** vertical list of gates, each green/red with WHY:

```
╭─ HEALTH ─────────────────────── prod-1 · 192.168.1.5:1633 · 2.7.1 ──╮
│ ✓ API reachable                 (3.2ms)                             │
│ ✓ Chain RPC                     block 8412930 · Δ +1 (1.2s)         │
│ ✓ Wallet funded                 0.092 ETH · stake 10 BZZ            │
│ ✓ Warmup complete               (started 3h12m ago)                 │
│ ✓ Peers                         87 connected · 12 inbound           │
│ ⚠ Bin saturation                bin 4 starving (3/8)                │
│ ✓ Reserve                       145,231 chunks · radius 8           │
│ ✗ Healthy for redistribution    storageRadius (8) < committed (9)   │
│   └─ wait for next 30-min reserve worker tick (in 12m)              │
│ ✓ Not frozen                    last frozen: never                  │
│ ✓ Sufficient funds to play      ~38 rounds runway                   │
╰──────────────────────────────────────────────────────────────────────╯
```

**Data sources:** `/health`, `/readiness`, `/wallet`, `/status`, `/redistributionstate`, `/chainstate`, `/topology` saturation (derived).

**Refresh:** 2s tick.

**Encoded knowledge:** the `storageRadius` decreases ONLY on the 30-min wakeup tick (`pkg/storer/storer.go:411`). Without this hint, operators stare at the gate forever wondering why nothing happens.

**Keys:** `↵` jump to relevant screen for any failing gate.

### S2. Stamps (volume+duration first)

**Anxiety:** depth/amount confusion (#4992 — community moving to retire), bucket collision (#3122, #4292)

**Per batch row:**

```
LABEL          VOLUME    USED   TTL        WORST BUCKET   STATUS    TOPUP
prod-mainnet   16 GB     34%    47d 12h    ▇▇▇▇▇▇▇░ 87%   ⚠ skewed   8.4 BZZ
batch-2        4 GB      12%    18d  3h    ▇▇░░░░░░ 22%   ✓         2.1 BZZ
batch-3        2 GB       0%    pending    -              ⏳ usable in 6 blocks (~72s)
```

**Bottom panel on focus** — bucket histogram of selected batch (`/stamps/{id}/buckets`):

```
Bucket fill (depth=22, bucketDepth=16, 65536 buckets, max=64 chunks each):
  worst:  64/64 (100%) ← uploads will FAIL on this bucket
  p99:    58/64
  p50:    18/64
  p01:     0/64
  ETA to bucket overflow: ~8 minutes at current rate
```

**Data sources:** `/stamps` @ 10s, `/stamps/{id}/buckets` on focus, `/chainstate` for usable countdown.

**Truth-telling:** `utilization` returned by API is `MaxBucketCount` not average (`pkg/postage/stampissuer.go:218-223`). Show BOTH "API utilization (worst bucket)" and "effective average". An immutable batch is dead when the worst bucket hits 100% even if average says 50%.

**Keys:** `b` buy new (volume + duration form), `t` topup, `d` dilute, `↵` bucket histogram, `c` copy batch ID.

### S3. SWAP / cheques

**Anxiety:** cashout confusion (#4663 + 8 dupes since 2021)

**Three tables:**

1. **Uncashed-IN** (cheques others sent us):

```
PEER (alias)              VALUE     CASHABLE NOW   GAS COST   PROFITABLE?
8a2f...c1   gateway-eu    0.082 ETH   YES          0.003 ETH  ✓ +0.079
b4c8...d7   storage-7     0.041 ETH   YES          0.003 ETH  ✓ +0.038
77a3...88   relay-uk      0.0008 ETH  no           0.003 ETH  ✗ wait
```

2. **Issued-OUT** (cheques we sent, drains our chequebook):

```
PEER          VALUE       LAST ISSUED   CHEQUEBOOK BALANCE
storage-3     0.018 ETH   2h ago        0.42 ETH (109 cheques runway)
```

3. **Refresh-vs-cheque split** (per peer, last 24h):

```
PEER          REFRESH-IN    REFRESH-OUT   CHEQUE-IN   CHEQUE-OUT
gateway-eu    142 BZZ       0             0.082 ETH   0
storage-3     12 BZZ        38 BZZ        0           0.018 ETH
```

**Data sources:** `/balances`, `/timesettlements`, `/settlements`, `/chequebook/balance`, `/chequebook/cheque`, `/wallet`, `/chainstate` (current gas price).

**Truth-telling:** computes "profitable to cash now?" using current gas price (from `/chainstate.currentPrice` or web3 estimate). Operators stop cashing 0.0008 ETH cheques when they see -0.002 ETH net.

**Keys:** `c` cashout focused row (with confirm), `C` cashout all profitable, `d` deposit, `w` withdraw, `f` filter to profitable only.

### S4. Lottery (redistribution rounds)

**Anxiety:** "Why am I not earning rewards?" (#4849)

**Top:** round timeline with current block tick:

```
Round 14528 · block 14528*152+118 = block-of-round 118/152
┌─ commit ──┬─ reveal ──┬─ claim ──┬─ sample ──┐
│ 0..38     │ 38..76    │ 76..152  │ ←→ next   │
│ ✓ done    │ ✓ done    │   ███▒░  │  pending  │
└───────────┴───────────┴──────────┴───────────┘
```

**Bottom:** last 20 rounds with phase outcomes + skip reasons:

```
ROUND   SELECTED  COMMIT  REVEAL  WIN  REWARD     REASON IF SKIPPED
14527   ✓         ✓       ✓       ✗   -          someone else won
14526   ✗         -       -       -   -          not selected (anchor depth too deep)
14525   ✓         ✗       -       -   -          INSUFFICIENT FUNDS to play
14524   ✓         ✓       ✓       ✓   142 BZZ    won
14523   ✓         ✗       -       -   -          FROZEN until 14530
...
```

**Side panel:** rchash benchmark — "your sampler took 18.3s last round; deadline is 38 blocks ≈ 95s — safe."

**Data sources:** `/redistributionstate` (round, phase, lastWonRound, lastFrozenRound, isFrozen, isHealthy, RoundData last 10), `/stake`, `/rchash` (on demand).

**Truth-telling:** the *reason* for each skip — research surfaced that `LastPlayedRound` is stale when frozen/skipped (`pkg/storageincentives/agent.go:286`). Reconstructing skip reasons from preconditions (`agent.go:394-447`) is what no other tool does.

**Keys:** `r` run rchash benchmark, `s` show stake details, `f` filter rounds.

### S5. Warmup (auto-shown when `isWarmingUp=true`)

**Anxiety:** 25–60 minute cold-start opacity (#4746)

```
WARMUP · 12m 38s elapsed
✓ batchstore sync           (chunks 142,000 / ~145,000)  98%
✓ postage snapshot loaded   (487 batches)
▒ peer bootstrap            (87 / target 100, bin 4 starving)
░ kademlia depth stable     (depth=8, fluctuating)
░ reserve fill              (0 / 65,536 chunks)
░ stabilization             (waiting on subscriber)
ETA: ~8 more minutes
```

**Data sources:** `/health`, `/status`, `/topology`, observed deltas.

**Refresh:** 1s tick.

### S6. Peers + bin saturation

**Top:** sortable peer table (configurable cols per `bee-tui.toml`):

```
PEER             ALIAS         BIN  PROXIMITY  SYNC RATE  RESERVE   REACHABLE
8a2f..c1          gateway-eu     8    8         142/s      45,000    in+out
b4c8..d7          storage-7      7    7          88/s      31,000    out only
...
```

**Bottom:** bin saturation strip:

```
BIN   POPULATION   TARGET   STATUS
0     ████████████████ 18    8/18    ✓ over
...
4     ███             3      8/18    ⚠ STARVING
...
```

**Truth-telling:** per-bin starvation isn't visible in `/status/peers`; derive from `/topology` populations vs `SaturationPeers=8`/`OverSaturationPeers=18` constants (`pkg/topology/kademlia/kademlia.go:54-55`).

**Keys:** `↵` drill peer (balance, settlements, cheque, kademlia bin), `x` disconnect, `b` blocklist, `p` ping.

### S7. Network / NAT

**Anxiety:** "I have peers but I'm unreachable" (#4194)

```
PUBLIC ADDRS (advertised):
  /ip4/77.x.x.x/tcp/1634/p2p/16Uiu...
  /ip6/2a01:.../tcp/1634/p2p/16Uiu...

INBOUND CONNECTIONS:    12 (last 5min: 3 new)
OUTBOUND CONNECTIONS:   75
NAT TYPE (AutoNAT):     full-cone (stable for 9m)
PORT-CHECK:             tcp/1634 reachable from observer ✓
RELAY CANDIDATES:       2 known
```

**Truth-telling:** `isReachable` flickers under symmetric NAT — show stability over 10 min, not snapshot. If flickering, suggest port-forward.

### S8. RPC health

```
RPC ENDPOINT:    https://rpc.gnosischain.example
LATENCY:         142ms p50 · 380ms p99 (last 100 calls)
ERROR RATE:      0.4% (last 1h)
BLOCK HEIGHT:    8412930 · Δ +1 vs network · 1.2s ago
PENDING TX:      2 stuck > 2min — [view]
```

**Truth-telling:** RPC fragility blamed on Bee but not Bee's fault. Show this prominently.

### S9. Tags + uploads

Active tags table with split/sent/synced live progress (WS `/chunks/stream` feed). Per-tag drill shows seen/stored/sent/synced delta over time.

### S10. Command log (always available, `^L` to toggle)

lazygit-style append-only HTTP req/res tail:

```
14:32:18  GET  /status                       200  3ms
14:32:20  GET  /stamps                       200  18ms
14:32:21  POST /stamps/topup/abc.../1000     200  2.1s
14:32:23  WS   /chunks/stream                — connected
```

This is the trust anchor + live tutorial. Operators learn the API by watching it.

### Common UI

**Top bar:** profile name • bee version • API latency • pending writes • clock
**Bottom bar:** dynamic keymap legend (lazygit pattern — changes per focus)
**Command bar:** `:` opens k9s-style — `:stamps`, `:peers /reachable`, `:health`, `:quit`, `:diagnose` (export bundle)
**Help overlay:** `?` shows screen-specific keymap
**Diagnostic bundle export:** `:diagnose` produces a redacted Discord-paste-ready snippet with health gates state + version + last 50 commands

---

## 9. Config

`~/.config/bee-tui/config.toml`:

```toml
[ui]
theme = "default"               # default | mono | high-contrast
ascii_fallback = false
fps_cap = 60
tick_ms = 250

[refresh]                       # tig-style per-resource policy
status      = "periodic:2s"
stamps      = "periodic:10s"
buckets     = "manual"
chequebook  = "periodic:30s"
tags        = "periodic:5s"
chunks      = "stream"

[[nodes]]
name    = "prod-1"
url     = "http://10.0.1.5:1633"
token   = "@env:BEE_TOKEN_PROD1"
default = true

[columns.peers]                 # lntop-style configurable columns
fields = ["overlay", "alias", "bin", "proximity", "sync_rate", "reserve", "reachable"]

[keys]                          # tig-style layered overrides
quit = "q"
help = "?"
command = ":"
diagnostic_bundle = "ctrl+d"
```

---

## 10. Testing

| Layer | Tool | Scope |
|---|---|---|
| Unit | `cargo test` | `update()` per `Action`, derived-state computation |
| View snapshot | `insta` + `Buffer::pretty_print` | Each component at fixed sizes (80×24, 132×40, 200×60) |
| API integration | `wiremock` | Mock Bee responses for every endpoint; assert state transitions |
| Live integration | bee-rs Sepolia rig | Reuse existing per memory (BEE_BATCH_ID env); behind `--features sepolia-tests` |
| Binary smoke | `assert_cmd` | `--version`, `--check-config`, `--diagnostic-bundle` |
| Visual regression | VHS `.tape` files | Screen recordings → diffable text golden output |

**CI matrix:** ubuntu-latest, macos-latest, windows-latest × stable, 1.85 (MSRV). `cargo-deny`, `cargo-audit`, `clippy --all-targets -- -D warnings`.

---

## 11. Release & distribution

- `cargo-dist` for installers + prebuilt archives (Linux x86_64-musl + aarch64-musl, macOS universal, Windows x86_64)
- `cargo binstall bee-tui` for Rust-toolchain users
- Homebrew tap, Scoop, AUR, MSI
- Cosign-signed releases (matches bee-go/bee-rs posture)
- `lto = "fat"`, `codegen-units = 1`, `strip = true` — target binary <8 MB
- mdBook docs site with VHS-recorded GIF demos
- `awesome-swarm` PR at v1.0
- Optional: Ratzilla web preview at `bee-tui.dev/demo` (same code, WASM build)

---

## 12. Roadmap

| Version | Scope | Effort |
|---|---|---|
| **Validation gate** | Discord post + 5 operator interviews | 1 week |
| **bee-rs 1.3.0** | 6 prereqs (WS upload + 2 endpoints + ergonomics) | 1 week |
| **bee-tui v0.1** | S1 Health, S2 Stamps, S10 Command log; single-node; CI; insta tests | 2 weeks |
| **bee-tui v0.2** | S3 SWAP, S4 Lottery + rchash benchmark | 1.5 weeks |
| **bee-tui v0.3** | S5 Warmup, S6 Peers, S7 NAT, S8 RPC | 1.5 weeks |
| **bee-tui v0.4** | Multi-node config + `:context` switcher + diagnostic bundle | 1 week |
| **bee-tui v0.5** | S9 Uploads + WS chunks/stream; theme system | 1 week |
| **bee-tui v0.9** | Polish, mdBook, VHS demos, cargo-dist setup, beta with the 50–200 | 1 week |
| **bee-tui v1.0** | Stable; awesome-swarm PR; blog post | release-only |

**Total: ~10 weeks** from green-light to v1.0, including prereqs and validation. Without prereqs/validation: ~8 weeks of pure bee-tui work.

---

## 13. Risks (sharpened)

| Risk | Likelihood | Mitigation |
|---|---|---|
| **Niche too small to matter** (lntop-style fade) | Medium | Validation gate; pivot to Grafana pack if signal weak |
| **Bee API drift between minor versions** | High | Pin bee-rs minor; show API version in header; warn-banner on mismatch |
| **WS reconnect storms on flaky home networks** | Medium | Exp backoff, max 5 retries, manual retry banner |
| **30–60s feed lookups freeze a screen** | High | Per-screen `CancellationToken` child, throbber, request-in-flight indicator |
| **Logging stdout corrupting alt-screen** | Certain if mishandled | `tracing-appender` non-blocking to file; never stdout |
| **Operators on Windows Terminal with broken Unicode** | Medium | `--ascii` flag; fallback box-drawing |
| **Token leaks in logs / bundles** | Severe if leaked | Redaction layer; never log full URL with auth; bundle export sanitizes |
| **Author burnout (single-maintainer death)** | High historically | mdBook contributing guide; component template structure makes onboarding cheap |
| **Pushsync silent chunk loss (#5400)** masquerades as TUI bug | Medium | Surface stewardship status per ref; banner if Bee version has known issues |

---

## 14. Open decisions

1. **Repo location:** `github.com/ethswarm-tools/bee-tui` (matches bee-go, bee-bench)?
2. **Crate name reservation:** `bee-tui` — reserve via dummy publish before announcing?
3. **License:** match bee-rs (MIT or Apache-2.0)?
4. **Bee version target:** `2.7.x` API version 8.0.0 — drop older?
5. **Validation outcome:** if Discord signal is weak, build the **Grafana dashboard pack** instead. Are you open to that pivot?
6. **Multi-node priority:** scoped to v0.4; if a multi-node setup is the killer use case, swap to v0.1.
7. **PSS/GSOC console:** cut to v0.5; is this a marketing-screenshots feature wanted earlier?

---

## 15. Concrete next step

If this v2 plan looks right, the right next move is **Step 1 of the Validation Gate** — post the screen mockups to `#node-operators` Discord. Drafting:
- the post text + screenshot mockup descriptions
- the 5-question template
- a simple Google Form / Markdown reply template

Or if validation is skipped (every operator pain point above has GitHub issue evidence with reactions), the right next move is **bee-rs v1.3.0** (1 week of work to close the 6 prereqs).

---

## Research index

- [`research/01-bee-feature-surface.md`]research/01-bee-feature-surface.md — Bee API endpoint inventory and TUI-shaped data sources
- [`research/02-tui-design-patterns.md`]research/02-tui-design-patterns.md — Design patterns from k9s, lazygit, btop, gh dash, dive, bottom
- [`research/03-bee-rs-audit.md`]research/03-bee-rs-audit.md — bee-rs v1.2.0 endpoint audit + 6 prereq gaps
- [`research/04-production-ratatui-patterns.md`]research/04-production-ratatui-patterns.md — Architecture patterns from gitui, atuin, bottom, gpg-tui
- [`research/05-operator-pain-points.md`]research/05-operator-pain-points.md — Field research: top 10 Bee operator pain points with GitHub issue evidence
- [`research/06-bee-internals.md`]research/06-bee-internals.md — Deep dive into Bee subsystem internals
- [`research/07-competitor-cockpits.md`]research/07-competitor-cockpits.md — Decentralized-storage TUI prior art
- [`research/08-ratatui-2026-sota.md`]research/08-ratatui-2026-sota.md — Current 2026 Ratatui state-of-the-art