Skip to content

Commit 272c6d1

Browse files
committed
tui: Phase 6 — add README.md
Lightweight architecture overview and workflow diagrams (entry, source-scan, analyzer) plus brief recipes for adding a new source and a new page. Keeps the single source of truth for TUI shape next to the code instead of in an external doc. Made-with: Cursor
1 parent e9906e8 commit 272c6d1

1 file changed

Lines changed: 126 additions & 0 deletions

File tree

pkg/tui/README.md

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
# TruffleHog TUI
2+
3+
The TUI is a Bubble Tea program that lets the user configure a scan or an
4+
analyzer interactively. When it exits, control hands back to `kingpin` (for
5+
scans) or to `pkg/analyzer.Run` (for analyzer flows).
6+
7+
## Architecture
8+
9+
Three layers, nothing else:
10+
11+
1. **`app` — the router.** Owns the page stack, global key handling, the
12+
shared style sheet and key bindings, and the parent-process handoff
13+
(argv for kingpin, SecretInfo for the analyzer). Pages know nothing
14+
about each other; they emit navigation messages and the router
15+
reacts.
16+
17+
2. **`pages/*` — one directory per page.** Every page implements
18+
`app.Page`, receives `app.ResizeMsg` for layout, and communicates
19+
upward with `app.PushMsg` / `app.PopMsg` / `app.ReplaceMsg` /
20+
`app.ExitMsg` / `app.RunAnalyzerMsg`. Layout is kebab-case
21+
(`pages/source-picker/`, `pages/analyzer-form/`, …).
22+
23+
3. **`components/form` + `sources` — declarative configuration.**
24+
`sources.Definition` is a registry entry for each scan source:
25+
title, description, `form.FieldSpec`s, validators, optional
26+
`BuildArgs` override. `form.Form` renders the fields, validates
27+
them, and produces a kingpin-style `[]string` arg vector. The few
28+
sources with irreducibly complex arg-emission rules (elasticsearch,
29+
jenkins, postman) provide a `BuildArgs` hook.
30+
31+
```mermaid
32+
graph TD
33+
subgraph Runtime
34+
main[main.go] -->|argv| tui[pkg/tui.Run]
35+
end
36+
37+
subgraph pkg/tui
38+
tui -->|builds| Model[app.Model router]
39+
Model --> Pages[pages/*]
40+
Pages -->|uses| Form[components/form]
41+
Pages -->|uses| Registry[sources registry]
42+
end
43+
44+
Model -->|ExitMsg| kingpin
45+
Model -->|RunAnalyzerMsg| analyzer.Run
46+
```
47+
48+
## Workflows
49+
50+
### Entry
51+
52+
```mermaid
53+
flowchart LR
54+
start([main.go]) --> argv{argv}
55+
argv -->|no args| wizard[wizard]
56+
argv -->|analyze| picker[analyzer-picker]
57+
argv -->|analyze known| form[analyzer-form]
58+
argv -->|analyze unknown| picker
59+
```
60+
61+
### Source scan
62+
63+
```mermaid
64+
sequenceDiagram
65+
participant U as user
66+
participant W as wizard
67+
participant SP as source-picker
68+
participant SC as source-config
69+
participant R as router
70+
participant K as kingpin
71+
72+
U->>W: choose "Scan a source"
73+
W->>R: PushMsg(source-picker)
74+
U->>SP: pick source
75+
SP->>R: PushMsg(source-config, SourceID)
76+
U->>SC: fill source tab → tab
77+
U->>SC: fill trufflehog tab → tab
78+
U->>SC: enter on run tab
79+
SC->>R: ExitMsg(argv)
80+
R->>K: argv
81+
```
82+
83+
### Analyzer
84+
85+
```mermaid
86+
sequenceDiagram
87+
participant U as user
88+
participant AP as analyzer-picker
89+
participant AF as analyzer-form
90+
participant R as router
91+
participant A as analyzer.Run
92+
93+
U->>AP: pick analyzer type
94+
AP->>R: PushMsg(analyzer-form, KeyType)
95+
U->>AF: fill fields
96+
AF->>R: RunAnalyzerMsg(type, info)
97+
R->>A: dispatch
98+
```
99+
100+
## Adding a new source
101+
102+
1. Create `pkg/tui/sources/<id>/` with a `Definition()` returning a
103+
`sources.Definition`.
104+
2. Declare fields with `form.FieldSpec` — pick the appropriate `Kind`
105+
(`KindText`, `KindSecret`, `KindCheckbox`, `KindSelect`) and
106+
`EmitMode` (`EmitPositional`, `EmitLongFlagEq`, `EmitPresence`,
107+
`EmitRepeatedLongFlagEq`, `EmitConstant`, …). Add validators from
108+
`form` (`Required`, `Integer`, `OneOf`) and cross-field
109+
`Constraint`s (`XOrGroup`) as needed.
110+
3. If the arg-emission logic isn't expressible declaratively, set
111+
`Definition.BuildArgs` to a `func(values map[string]string) []string`.
112+
4. Add `func init() { sources.Register(Definition()) }` and blank-import
113+
the package from `pkg/tui/tui.go` so the registry picks it up.
114+
5. Add a test case to `pkg/tui/sources/sources_test.go` covering the
115+
emitted arg vector.
116+
117+
## Adding a new page
118+
119+
1. Create `pkg/tui/pages/<kebab-name>/` with a `Page` type satisfying
120+
`app.Page`.
121+
2. Export an `ID` constant re-declaring the appropriate `app.PageID` (or
122+
add a new one to `pkg/tui/app/page.go`).
123+
3. Register a factory in `pkg/tui/tui.go`'s `registerPages`.
124+
4. Emit `app.PushMsg` / `app.PopMsg` / `app.ExitMsg` /
125+
`app.RunAnalyzerMsg` from the page as appropriate; never touch the
126+
stack directly.

0 commit comments

Comments
 (0)