Commit 86106aa0f9
Verified · cmc
README.md +33 −13
| @@ -20,12 +20,14 @@ the others. | ||
| 20 | 20 | ▼ ▼ ▼ ▼ ▼ |
| 21 | 21 | terminal-pet garden menubar-pet sonifier wallpaperd |
| 22 | 22 | (TUI) (TUI) (SwiftUI) (AVAudio) (image/window) |
| 23 | ✅ built ✅ built planned planned planned | |
| 23 | 24 | ``` |
| 24 | 25 | |
| 25 | All five draw from the **same signal bus**. Renderers never poll hardware, | |
| 26 | never read the shell — they subscribe over a local Unix socket | |
| 27 | (`$XDG_RUNTIME_DIR/signald.sock`). That buys one privacy boundary to defend | |
| 28 | instead of five, cheap renderers, and reuse of the collection layer into | |
| 26 | **Two faces are built.** The other three are the roadmap, not the product — | |
| 27 | see "Phase plan". Every face draws from the **same signal bus**: renderers | |
| 28 | never poll hardware, never read the shell, they subscribe over a local Unix | |
| 29 | socket (`$XDG_RUNTIME_DIR/signald.sock`). That buys one privacy boundary to | |
| 30 | defend instead of five, cheap renderers, and reuse of the collection layer into | |
| 29 | 31 | non-toy outputs (e-ink dashboards, printed posters). |
| 30 | 32 | |
| 31 | 33 | ## The privacy boundary, enforced by construction |
| @@ -120,6 +122,7 @@ ambient-companions/ | ||
| 120 | 122 | │ │ └── tests/privacy_invariant.rs |
| 121 | 123 | │ ├── signald/ # the daemon (collectors + live fan-out) |
| 122 | 124 | │ │ ├── src/lib.rs # git + terminal collectors, hardware ingest, publish |
| 125 | │ │ ├── src/supervisor.rs # collector liveness, restart, CollectorUp | |
| 123 | 126 | │ │ ├── src/history.rs # SQLite (WAL) history store + recent() query |
| 124 | 127 | │ │ ├── src/hub.rs # last-value cache + live fan-out |
| 125 | 128 | │ │ ├── src/main.rs # CLI, producer loop, self-attestation |
| @@ -127,8 +130,12 @@ ambient-companions/ | ||
| 127 | 130 | │ │ ├── tests/streaming.rs # last-value cache + live update |
| 128 | 131 | │ │ ├── tests/hardware_ingest.rs # collector frames reach a subscriber |
| 129 | 132 | │ │ └── tests/differential_secret_typing.rs # the full privacy ship-gate |
| 130 | │ └── terminal-garden/ # first renderer: a socket subscriber | |
| 131 | │ ├── src/lib.rs # signals → plots → render (unit-tested) | |
| 133 | │ ├── signal-client/ # socket path + frame iteration (shared) | |
| 134 | │ ├── terminal-garden/ # first renderer: git aggregates as plots | |
| 135 | │ │ ├── src/lib.rs # signals → plots → render (unit-tested) | |
| 136 | │ │ └── src/main.rs # live subscribe + redraw loop | |
| 137 | │ └── terminal-pet/ # second renderer: shell + machine + health | |
| 138 | │ ├── src/lib.rs # signals → PetState → render (unit-tested) | |
| 132 | 139 | │ └── src/main.rs # live subscribe + redraw loop |
| 133 | 140 | ├── shell-hooks/ # zsh hooks: aggregate-only terminal collector |
| 134 | 141 | │ ├── signald-hooks.zsh |
| @@ -185,8 +192,8 @@ and — for the differential test — `zpty` modules ship with zsh). | ||
| 185 | 192 | the key, and appends `<epoch_ms> <keys> <session_seconds> <session_id>` count |
| 186 | 193 | records (numbers only) to a spool; `collectors::terminal` consumes the spool |
| 187 | 194 | and derives `keys_per_min` and `session_seconds`. The **differential |
| 188 | secret-typing test is active and passing** — the privacy ship-gate. *The | |
| 189 | Terminal Pet renderer is still todo.* | |
| 195 | secret-typing test is active and passing** — the privacy ship-gate. The | |
| 196 | Terminal Pet renderer landed in v1.0. | |
| 190 | 197 | - **Phase 3 — macOS IOKit hardware collector. ✅ Done (v0.3).** |
| 191 | 198 | `macos-collector/` (a sibling Swift package, `swift build`) reads aggregate |
| 192 | 199 | hardware scalars via **IOKit only — no `powermetrics`, no root**: CPU load |
| @@ -198,7 +205,7 @@ and — for the differential test — `zpty` modules ship with zsh). | ||
| 198 | 205 | documented and pinned by a shared canonical-frame test on both sides |
| 199 | 206 | (`crates/signal-schema/tests/hardware_wire.rs` decodes the exact bytes the |
| 200 | 207 | Swift encoder commits to in `macos-collector/Tests/.../WireTests.swift`). |
| 201 | *Still todo:* the menu-bar permadeath pet. | |
| 208 | *The menu-bar permadeath pet is post-1.0.* | |
| 202 | 209 | - **Hardware ingest. ✅ Done (v0.4).** |
| 203 | 210 | `signald` spawns `macos-collector` as a child (`--collector <path>`, or found |
| 204 | 211 | on `PATH`) and reads its stdout with the same `wire::read_frame` the socket |
| @@ -220,9 +227,21 @@ and — for the differential test — `zpty` modules ship with zsh). | ||
| 220 | 227 | roots it was told to watch. A reader now **skips** a frame it cannot decode |
| 221 | 228 | instead of dying on it (`wire::Frame::Skipped`), so an older renderer keeps |
| 222 | 229 | working against a newer daemon. References to an uncommitted spec are gone. |
| 223 | - **Phase 4** — sonification (SSH-utility first, then continuous). *Out of scope.* | |
| 230 | - **v1.0 — trust it unattended. ✅ Done (v1.0.0).** | |
| 231 | 1.0 is not feature completeness and not an API promise to anyone else. It is | |
| 232 | one claim: **the daemon runs unattended for weeks, recovers from its own | |
| 233 | failures, and says so when it cannot.** | |
| 234 | `signald/src/supervisor.rs` owns collector liveness. A panic in the git or | |
| 235 | terminal collector costs one tick instead of the producer thread; the | |
| 236 | hardware child is respawned with backoff; `run_git` kills a `git` that hangs, | |
| 237 | because a Rust thread cannot be. Health rides the bus as | |
| 238 | `SignalName::CollectorUp`, one per `Source` — the schema's `v5` addition — | |
| 239 | so a dead collector reaches a face rather than a log file. `terminal-pet` | |
| 240 | renders it: a pet that cannot feel its own hardware looks sick. | |
| 241 | - **Phase 3 renderer — menu-bar permadeath pet.** *Post-1.0.* | |
| 242 | - **Phase 4** — sonification (SSH-utility first, then continuous). *Post-1.0.* | |
| 224 | 243 | - **Phase 5** — live wallpaper (homelab, Path A) + e-ink/poster reuse. |
| 225 | *Out of scope.* | |
| 244 | *Post-1.0.* | |
| 226 | 245 | |
| 227 | 246 | ## Install |
| 228 | 247 | |
| @@ -249,10 +268,11 @@ Then add one line to `.zshrc` for the terminal collector: | ||
| 249 | 268 | source "$(brew --prefix)/share/ambient-companions/signald-hooks.zsh" |
| 250 | 269 | ``` |
| 251 | 270 | |
| 252 | Open a new shell and watch the garden: | |
| 271 | Open a new shell and watch a face: | |
| 253 | 272 | |
| 254 | 273 | ```sh |
| 255 | terminal-garden | |
| 274 | terminal-garden # your repos, as plants that grow and wilt | |
| 275 | terminal-pet # the shell and the machine, as a mood | |
| 256 | 276 | ``` |
| 257 | 277 | |
| 258 | 278 | `brew services` logs to `$(brew --prefix)/var/log/`. To run the agent by hand |