diff --git a/README.md b/README.md index 026b746..aceca2e 100644 --- a/README.md +++ b/README.md @@ -25,9 +25,13 @@ skills, MCP servers, and plugins. reach for Claude when the local model would obviously flail. - **Tier-0 SLM routing.** A tiny local model classifies each prompt and handles trivial tasks itself, keeping the heavy provider for real work. -- **Built-in network firewall + secret scanner.** Paths are canonicalised - (TOCTOU-safe), network egress is gated, tool output is scanned for - secrets before reaching the model. +- **Content boundary + secret scanner.** Every outgoing LLM message + and incoming tool result is scanned for secrets (regex + Shannon + entropy on long tokens), redacted or blocked at the content level. + Paths are canonicalised (TOCTOU-safe), Unicode is sanitized + (homoglyphs, BiDi tricks), and a `SafeProvider` boundary keeps + incognito-mode data out of long-lived stores. *(Per-host network + egress allowlist is on the roadmap, not in place today.)* - **Provider-agnostic from day one.** Anthropic, OpenAI, Google, Mistral, Ollama, llama.cpp, plus subprocess CLIs (`claude`, `codex`, `agy`, `vibe`). Mix cloud and local in the same session. @@ -462,9 +466,18 @@ built-in batching skill. gnoma runs tools and shell commands on your behalf. The [`internal/security`](internal/security) package canonicalises every path -(TOCTOU-safe), gates network access through a configurable firewall, and -scans tool output for secrets before it ever reaches the model. The -`SafeProvider` boundary keeps incognito-mode data out of long-lived stores. +(TOCTOU-safe), scans every outgoing LLM message and incoming tool result +for secrets (regex + Shannon entropy) before it reaches the model, and +sanitizes Unicode (homoglyphs, BiDi tricks). The `SafeProvider` boundary +keeps incognito-mode data out of long-lived stores. + +> **Scope note.** The current "firewall" is a content boundary — it +> redacts/blocks secrets in inputs and outputs. It is **not** a +> network-egress firewall: outgoing HTTP from tools and providers goes +> through stock `http.Client`, with no per-host allowlist or +> dial-layer enforcement. Per-host egress rules and a per-session +> audit log of blocked/redacted events are tracked in +> [TODO.md](TODO.md). ### Entropy false-positive reduction diff --git a/TODO.md b/TODO.md index af3ca2c..e6bc6f0 100644 --- a/TODO.md +++ b/TODO.md @@ -4,6 +4,27 @@ Active work, newest first. ## In flight +- **Security boundary — egress controls + session audit log.** The + current `Firewall` is a content boundary only (scans messages and + tool results for secrets via regex + Shannon entropy, redacts or + blocks, logs via `log/slog`). It does not enforce network egress — + outgoing HTTP from tools and providers uses stock `http.Client` + with no per-host allowlist or dial-layer interception. Two follow- + ups surfaced from the r/SideProject v0.3.0 launch thread + (2026-05-24, `u/Secret_Theme3192`): + 1. **Per-session audit log of blocked/redacted events** — + grep-able file at `.gnoma/sessions//audit.jsonl` so the + user can answer "what did the firewall do this session?" in + one command. Today the `slog` output goes to whatever sink is + configured, with no per-session grouping. + 2. **Per-host egress allowlist (HTTP transport layer)** — open + design question: host-level (`allow api.openai.com, deny *`) + vs per-tool (`bash can only hit these hosts`). Reply asked + the commenter for their mental model; revisit when feedback + lands. The README and v0.3.0 Reddit post phrasing oversold + "network egress gated"; corrected in the same commit as this + TODO entry. + - **Tool-router specialization (functiongemma)** — gated on telemetry, not committed. Phase A.2 adds did-switch-rate measurement to the two-stage `select_category` path; Phase A.3 (LoRA fine-tune of