nanny scans the stores that claude-code, opencode and pi write — session transcripts, agent memories, cached pastes, shell snapshots, tool output, opencode's SQLite database — plus shell history and repository working trees, and records where and when a credential leaked into them. Detection is by shape alone. nanny deliberately has no read access to any credential store (~/.agents, ~/.ssh, each harness's own auth file), so the monitor is not itself a process worth compromising. The cost is false positives, managed with per-rule confidence, entropy floors and allow patterns. The central invariant is that a matched secret never leaves the scanner: Secret<'a> borrows, does not clone into an owned form, does not serialise, and Debugs as <redacted, N bytes>. What is stored is a salted keyed hash, a length, and a preview with every matched span masked — so nanny's own database cannot become the next spill. Records and alerts only; it never edits a harness's files. Rules carry a RotationHint as the seam for optional rotation later. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XAqHWfdMAsYu1o36tgeima
61 lines
1.5 KiB
TOML
61 lines
1.5 KiB
TOML
[workspace]
|
|
resolver = "3"
|
|
members = ["crates/nanny-entities", "crates/nanny-core", "crates/nanny-data", "crates/nanny-daemon", "crates/nanny-cli"]
|
|
|
|
[workspace.package]
|
|
version = "0.1.0"
|
|
edition = "2024"
|
|
rust-version = "1.85"
|
|
license = "GPL-3.0-or-later"
|
|
authors = ["Rob Thijssen <rob@thijssen.email>"]
|
|
repository = "https://git.lair.cafe/grenade/nanny"
|
|
|
|
[workspace.dependencies]
|
|
# internal
|
|
nanny-entities = { path = "crates/nanny-entities", version = "=0.1.0" }
|
|
nanny-core = { path = "crates/nanny-core", version = "=0.1.0" }
|
|
nanny-data = { path = "crates/nanny-data", version = "=0.1.0" }
|
|
|
|
# runtime
|
|
tokio = { version = "1", features = ["full"] }
|
|
async-trait = "0.1"
|
|
futures = "0.3"
|
|
|
|
# types / serialisation
|
|
serde = { version = "1", features = ["derive"] }
|
|
serde_json = "1"
|
|
toml = "0.8"
|
|
time = { version = "0.3", features = ["serde-well-known", "macros", "formatting", "parsing", "local-offset"] }
|
|
|
|
# errors
|
|
thiserror = "2"
|
|
anyhow = "1"
|
|
|
|
# detection
|
|
regex = "1"
|
|
memchr = "2"
|
|
blake3 = "1"
|
|
ignore = "0.4"
|
|
globset = "0.4"
|
|
|
|
# persistence
|
|
sqlx = { version = "0.8", default-features = false, features = ["sqlite", "runtime-tokio-rustls", "macros", "migrate", "time"] }
|
|
|
|
# filesystem watching
|
|
notify = "8"
|
|
notify-debouncer-full = "0.5"
|
|
|
|
# config
|
|
figment = { version = "0.10", features = ["toml", "env"] }
|
|
directories = "6"
|
|
|
|
# observability
|
|
tracing = "0.1"
|
|
tempfile = "3"
|
|
tracing-subscriber = { version = "0.3", features = ["env-filter", "json"] }
|
|
|
|
# binaries
|
|
clap = { version = "4", features = ["derive", "env"] }
|
|
ratatui = "0.29"
|
|
notify-rust = "4"
|