D06 - CLI and UX¶
1. Design mantra¶
First successful protected run in under five minutes. First denial is loud, clear, and actionable - not a stack trace.
Desktop GUI inventory (install, tray, every screen): 07-desktop-ux.md. This file remains CLI-primary.
2. Binary¶
- Name:
denyout - Install channels: see locked table in
07-desktop.md§3 - Must for Dec 1: GitHub Releases + SHA256SUMS,install.sh(Linux/macOS), Windows zip + short doc. Homebrew / winget / apt /go install/ container = later or stretch.
3. Command reference (MVP target)¶
3.1 denyout run¶
denyout run [flags] -- <command> [args...]
| Flag | Default | Description |
|---|---|---|
--config, -c |
search path | Config file |
--mode |
from config / enforce |
enforce\|audit\|blocklist\|ask |
--preset |
models |
Built-in allowlist preset |
--policy |
- | Alias of config for policy-only file |
--tls-mode |
intercept |
intercept\|passthrough |
--notify |
true on desktop |
OS notifications on deny |
--print-env |
false | Print proxy env and exit (no run) |
--port |
0 |
Proxy port (0=ephemeral) |
--ca-dir |
XDG config | CA location |
--session-ca |
true |
Ephemeral CA per run |
--verbose, -v |
false | Debug logs to stderr |
Exit codes:
| Code | Meaning |
|---|---|
| 0 | Child exited 0 |
| 1 | Child non-zero |
| 2 | Config/policy error |
| 3 | Proxy failed to start |
| 4 | Interrupted |
Child SIGINT/SIGTERM: supervisor forwards and waits.
3.2 denyout check¶
Validate that a URL would be allowed under current policy without running an agent.
denyout check "https://evil.test/x?api_key=sk-test"
# DENY secret_in_url
3.3 denyout policy validate¶
denyout policy validate -c denyout.yaml
3.4 denyout audit tail¶
denyout audit tail [-n 50] [--denials] [--path FILE]
Defaults to the desktop audit path (~/.local/state/denyout/audit.jsonl). --denials filters to deny / would_deny only.
3.5 denyout init¶
Write a starter denyout.yaml in CWD with comments.
3.6 denyout version / denyout help¶
Standard.
3.7 denyout desktop (recommended GUI)¶
First-run wizard (Protect computer / Protect AI agents / Not now). Tray restores Protect computer if left on. No localhost wizard by default (--web-ui optional / tests).
denyout desktop [--install] [--setup] [--no-tray] [--web-ui] [--no-open]
--install- Safe desktop preference + autostart + detach tray (used byinstall.sh). Admin failure stays Off.- Tray (compact): Status - Protect computer - My agents... - Off - Open DenyOut... - Quit. Left-click opens Activity. Agents only / lists / tips live in the window - see
07-desktop-ux.md.
install.sh auto-detects Desktop vs Server; force with --desktop / --server. Windows zip includes Start-DenyOut.cmd.
3.8 denyout profile / denyout run --profile¶
Named wrap profiles in ~/.config/denyout/profiles/:
denyout profile create --name bot -- python app.py
denyout profile list
denyout profile run bot
denyout run --profile bot
denyout run --sandbox=srt -- python app.py # Stronger isolation (fail closed if srt missing)
denyout profile pin bot # Pin pending Once hosts; learning off
3.9 denyout gui (advanced)¶
Technical localhost panel (mode/preset/proxy details) - advanced / optional:
denyout gui [--config path] [--no-open]
# or: denyout desktop --web-ui
Same policy engine as CLI; prefer native desktop for non-experts.
3.10 denyout serve¶
Long-running sidecar / compose / systemd proxy:
denyout serve --config /etc/denyout/denyout.yaml --listen 127.0.0.1:8080 \
--health-listen 127.0.0.1:8081 [--metrics-listen 127.0.0.1:9090]
Same policy engine as run. DENYOUT_KILL=1 refuses to start. Hardened claim requires NetworkPolicy - 08-server.md.
4. Zero-config happy path¶
# install (Linux/macOS) - silent auto-detect; force with flags/env
curl -fsSL https://raw.githubusercontent.com/nareyko/denyout/main/install.sh | sh
# curl ... | sh -s -- --desktop|--server
# CLI path (server / advanced)
denyout run -- claude
On first protected run:
- Generate session CA.
- Start proxy with preset
models. - Spawn child with env.
- Print one-line status.
- On deny: desktop notification (default) + stderr line with reason.
5. Denial UX copy (examples)¶
denyout: DENY host_not_allowed https://evil.example/collect
allow it once: denyout ask allow evil.example
or add to denyout.yaml under allow:
- host: evil.example
Secret case (desktop toast):
Blocked a link that looked like it contained a secret.
Host: cdn.example
MUST NOT print the raw secret.
6. Notifications¶
| Platform | Mechanism |
|---|---|
| macOS | osascript / UNUserNotification (later) |
| Linux | notify-send if available |
| Windows | Toast (phase later) |
Title: DenyOut blocked egress
Body: reason + redacted host/URL (≤ 120 chars).
7. Interactive ask mode¶
Terminal UI:
DenyOut: allow outbound HTTPS to newhost.example:443 ?
URL: https://newhost.example/v1/x?q=1
[a] allow once [s] allow for session [n] deny [r] remember host
Non-TTY: deny with ask_unavailable unless --notify approval bridge implemented.
8. Project vs user config UX¶
- Recommend committing
denyout.yamlfor team agents (shared allowlist). - User overrides in XDG for personal machine keys of policy (not secrets).
denyout initexplains this in comments.
9. Help for "it broke my agent"¶
Common failures documented in --help troubleshooting:
| Symptom | Likely cause | Fix |
|---|---|---|
| TLS errors | Client ignores CA | --tls-mode=passthrough or set framework CA env |
| Model calls fail | Missing host in preset | audit mode discover; add host |
| Package install fails | registry blocked | preset models+pkg |
| Everything denied | strict preset |
switch preset |
10. Localization¶
MVP: English CLI messages. Russian docs may live on Arviser; product UI EN-first.
11. Accessibility / scripting¶
- All commands scriptable; no required GUI.
--jsonflag oncheckand denials to stdout for CI (phase 1).
denyout check --json URL
# {"verdict":"deny","reasons":["secret_in_url"],...}