shell-hooks/README.md
36 lines · 1764 bytes
1# shell-hooks
2
3Shell side of the terminal collector. **Live as of v0.2.** These
4snippets belong in the dotfiles repo and are sourced by the user's `.zshrc`;
5they are kept here so the privacy contract lives next to the code it constrains
6(and so the differential secret-typing test can drive the real file).
7
8## The one rule
9
10**Aggregate-only.** The hooks emit a keystroke count and a session duration.
11They never read, store, or transmit the content of a command or a keystroke.
12
13- No input tap, no `CGEventTap`, no PTY sniffing.
14- Never reference the `zle` line buffer or capture argv.
15- What leaves the shell is a number, flushed on `precmd`.
16
17v0.2 ships the `zle` keypress counter: a widget wraps `self-insert`, does
18`(( _SIGNALD_KEYS++ ))`, then calls the built-in insert. It receives the key in
19the editor and discards it — the character is never assigned to a variable that
20outlives the widget and never leaves the shell. On each `precmd` the hook
21appends one count record — `<epoch_ms> <keys> <session_seconds> <session_id>`,
22numbers only — to `$SIGNALD_SPOOL`. The session id is the shell's pid, so
23several shells can share one spool and `signald` still derives each shell's
24rate separately. `signald` consumes the spool on every tick (renames it aside,
25reads it, deletes it), so it never grows.
26
27This contract is enforced by the forbidden-symbol scan **and** the differential
28secret-typing test in `crates/signal-schema/tests/privacy_invariant.rs` (plus
29the full-pipeline gate in `crates/signald/tests/`), which drive *this file* with
30a planted secret and fail the build if it — in any encoding — reaches the spool,
31the wire, or SQLite.
32
33## Files
34
35- `signald-hooks.zsh` — the `zle` keystroke counter + `precmd` count flush
36 (aggregate-only).