Skip to content

Commit a38e079

Browse files
machado144claude
andauthored
docs: audit and refresh docs to match shipped features (#12)
## What A docs audit + refresh to bring all four user-facing doc surfaces back in sync with what actually ships. Recent PRs (dashboard #9, IPv6 #10, Go bump #11, and earlier masking) added features and changed defaults that never made it into the docs. Kept it useful and tight — no bloat, just fixing what was missing or wrong. ## Gaps fixed | Area | Before | After | |------|--------|-------| | `aigate serve` / web dashboard | undocumented everywhere | documented in README, user guide, AI ref, `help-ai` | | Audit log (`~/.aigate/audit.jsonl`) | undocumented | own section in user guide + `help-ai`, with event examples | | Default config example (user guide) | missing `*.p12`, `terraform.tfstate`, `*.tfvars`, `ncat/netcat/rsync/ftp`, `registry.npmjs.org`, `proxy.golang.org`, and the whole `mask_stdout` block | matches `InitDefaultConfig` | | `aws_secret` preset | absent (docs said "5 presets") | added; correct description (`AWS_SECRET_ACCESS_KEY=` match), "6 presets" | | Go version | badge `1.24+`, "From Source (Go 1.24+)" | `1.25+` (go.mod is 1.25.11) | | README Features | claimed cgroups resource limits are **enforced** | removed — the user guide correctly notes they're *not yet* enforced | | AI architecture map | missing `audit_service.go`, `masker.go`, `internal/web/`, `setup.go`, `help_ai.go`, escape tests | added, plus a note on the append-only audit log + read-only dashboard design | | `help-ai` | no `doctor`, no `serve`/dashboard | added `doctor` to setup + a DASHBOARD & AUDIT LOG section | ## Files - `README.md` - `docs/user/README.md` - `docs/AI/README.md` - `actions/help_ai.go` (the `aigate help-ai` output) ## Verified - `go build ./...` and `go test -short ./...` pass - pre-commit hooks green: structlint, gofmt, govet, golangci-lint 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 0ff4dc7 commit a38e079

4 files changed

Lines changed: 87 additions & 14 deletions

File tree

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<div align="center">
44
<img src="docs/banner.png" alt="AiGate" width="400">
55
<p>
6-
<img src="https://img.shields.io/badge/Go-1.24%2B-00ADD8?style=flat-square&logo=go" alt="Go Version">
6+
<img src="https://img.shields.io/badge/Go-1.25%2B-00ADD8?style=flat-square&logo=go" alt="Go Version">
77
<img src="https://img.shields.io/badge/OS-Linux%20%7C%20macOS%20%7C%20WSL-darkblue?style=flat-square" alt="OS Support">
88
<img src="https://img.shields.io/badge/License-MIT-green?style=flat-square" alt="License">
99
</p>
@@ -53,7 +53,7 @@ AI coding tools rely on application-level permission systems that can be bypasse
5353
- **Network isolation** - `bwrap --unshare-net` + `slirp4netns` + `iptables` (+ `ip6tables` for IPv6 when available) restrict egress to allowed domains (Linux)
5454
- **Command blocking** - Deny execution of dangerous commands (curl, wget, ssh)
5555
- **Output masking** - Redact secrets (API keys, tokens) from stdout/stderr before they reach the terminal
56-
- **Resource limits** - cgroups v2 enforce memory, CPU, PID limits (Linux)
56+
- **Audit log + dashboard** - Every run and blocked command is recorded to `~/.aigate/audit.jsonl`; `aigate serve` exposes a local web dashboard over it
5757
- **Tool-agnostic** - Works with any AI tool: Claude Code, Cursor, Copilot, Aider
5858
- **Sensible defaults** - Ships with deny rules for .env, secrets/, .ssh/, *.pem, etc.
5959
- **Project-level config** - `.aigate.yaml` extends global rules per project
@@ -79,6 +79,7 @@ aigate deny net --except api.anthropic.com # Restrict network
7979
aigate allow read .env # Remove a deny rule
8080
aigate run -- claude # Run AI tool in sandbox
8181
aigate status # Show current rules
82+
aigate serve # Local web dashboard over the audit log
8283
aigate help-ai # Show AI-friendly usage examples
8384
aigate reset --force # Remove everything
8485
```

actions/help_ai.go

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ SETUP (one-time)
2424
sudo aigate setup # Create OS group "ai-agents" and user "ai-runner"
2525
aigate init # Create default config at ~/.aigate/config.yaml
2626
aigate init --force # Re-create config (overwrites existing)
27+
aigate doctor # Check prerequisites and active isolation mode
2728
2829
FILE RESTRICTIONS (deny read)
2930
aigate deny read .env # Block a single file
@@ -53,6 +54,14 @@ RUNNING SANDBOXED
5354
CHECKING STATUS
5455
aigate status # Show all rules, group/user, limits
5556
57+
DASHBOARD & AUDIT LOG
58+
Every run and blocked command is appended to ~/.aigate/audit.jsonl (JSON Lines).
59+
Events: run_started (rule counts) and blocked (matched deny_exec rule).
60+
61+
tail -f ~/.aigate/audit.jsonl # Follow events as plain text
62+
aigate serve # Local web dashboard → http://127.0.0.1:8080
63+
aigate serve --addr 127.0.0.1:9000 # Custom address (or AIGATE_ADDR env var)
64+
5665
CONFIGURATION
5766
Global config: ~/.aigate/config.yaml
5867
Project config: .aigate.yaml (in project root, merged with global)
@@ -90,6 +99,7 @@ CONFIGURATION
9099
- openai
91100
- anthropic
92101
- aws_key
102+
- aws_secret
93103
- github
94104
- bearer
95105
@@ -116,13 +126,14 @@ OUTPUT MASKING (mask_stdout)
116126
addition to kernel-level sandbox protections (defense-in-depth).
117127
118128
Built-in presets:
119-
openai sk-... / sk-proj-... → sk-***
120-
anthropic sk-ant-... → sk-ant-***
121-
aws_key AKIA... (access key ID) → AKIA***
122-
github ghp_, gho_, ghu_, ghs_, ghr_ → ghp_***
123-
bearer Bearer <token> → Bearer ***
129+
openai sk-... / sk-proj-... → sk-***
130+
anthropic sk-ant-... → sk-ant-***
131+
aws_key AKIA... (access key ID) → AKIA***
132+
aws_secret AWS_SECRET_ACCESS_KEY=... → AWS_SECRET_ACCESS_KEY=***
133+
github ghp_, gho_, ghu_, ghs_, ghr_ → ghp_***
134+
bearer Bearer <token> → Bearer ***
124135
125-
All 5 presets are enabled by default (aigate init).
136+
All 6 presets are enabled by default (aigate init).
126137
127138
Pattern options:
128139
regex RE2-compatible regular expression (required)
@@ -146,7 +157,7 @@ WHAT THE AI AGENT SEES INSIDE THE SANDBOX
146157
[aigate] deny_read: .env, secrets/, *.pem
147158
[aigate] deny_exec: curl, wget, ssh
148159
[aigate] allow_net: api.anthropic.com (all other outbound connections will be blocked)
149-
[aigate] mask_stdout: openai, anthropic, aws_key, github, bearer
160+
[aigate] mask_stdout: openai, anthropic, aws_key, aws_secret, github, bearer
150161
151162
Denied files contain a marker instead of their content:
152163
[aigate] access denied: this file is protected by sandbox policy. See /tmp/.aigate-policy for all active restrictions.

docs/AI/README.md

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,28 +15,40 @@ domain/ Pure data structures
1515
1616
services/ Core business logic
1717
platform.go Platform interface + Executor interface + resolvePatterns
18-
platform_linux.go Linux: setfacl, groupadd/useradd, RunSandboxed dispatch
18+
platform_linux.go Linux: setfacl, groupadd/useradd, RunSandboxed dispatch, splitDNSByFamily
1919
platform_linux_bwrap.go Linux bwrap path: buildBwrapArgs, runWithBwrap, runWithBwrapNetFilter
2020
platform_darwin.go macOS: chmod +a, dscl, sandbox-exec
21-
config_service.go Config load/save/merge (global + project)
21+
config_service.go Config load/save/merge (global + project), InitDefaultConfig
2222
rule_service.go Rule CRUD (add/remove/list deny rules)
2323
runner_service.go Sandboxed process launcher
24+
masker.go MaskingWriter + builtin mask_stdout presets
25+
audit_service.go Append/read ~/.aigate/audit.jsonl; AuditWriter wraps run output
26+
27+
internal/web/ Local dashboard (aigate serve)
28+
server.go HTTP server (loopback by default)
29+
handlers.go JSON API + page handlers over the audit log
30+
static/, templates/ Embedded JS/CSS/HTML assets
2431
2532
actions/ CLI command handlers
26-
init.go Create group, user, default config
33+
setup.go Create OS group + user (sudo)
34+
init.go Write default config
2735
deny.go Add deny rules (read, exec, net subcommands)
2836
allow.go Remove deny rules
2937
run.go Run command inside sandbox
3038
status.go Show current sandbox state
3139
reset.go Remove group, user, config
3240
doctor.go Check prerequisites and active isolation mode
41+
help_ai.go Print AI-friendly usage reference
42+
(serve is defined inline in main.go)
3343
3444
helpers/ Logging and error types
3545
logger.go zerolog console logger
3646
errors.go Sentinel errors
3747
3848
integration/ End-to-end CLI tests
39-
cli_test.go Build binary, run real commands
49+
cli_test.go Build binary, run real commands
50+
sandbox_test.go Sandbox behaviour end-to-end
51+
sandbox_escape_test.go Adversarial escape tests (-short skips them)
4052
```
4153

4254
## Key Design Decisions
@@ -47,6 +59,7 @@ integration/ End-to-end CLI tests
4759
- **No CGO**: All platform operations use `exec.Command` to call system utilities (setfacl, groupadd, dscl, chmod, bwrap, slirp4netns, iptables/ip6tables).
4860
- **IPv6 sandbox is opt-in by capability detection**: `ipv6SandboxSupported()` requires both kernel v6 enabled and `ip6tables` on PATH. Either missing → sandbox runs IPv4-only. Partial v6 (NAT but no filter) is refused — it would silently bypass `allow_net`.
4961
- **Config merging**: Global config (`~/.aigate/config.yaml`) + project config (`.aigate.yaml`) merge with project extending global.
62+
- **Audit log is append-only JSON Lines**: `AuditService` writes `run_started` / `blocked` events to `~/.aigate/audit.jsonl` under a mutex. `aigate serve` (`internal/web`) is a read-only dashboard over that file; it never mutates sandbox state. Keep the dashboard out of the sandbox enforcement path.
5063

5164
## Testing
5265

docs/user/README.md

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ curl -L https://github.com/AxeForging/aigate/releases/latest/download/aigate-dar
8181
sudo mv aigate-darwin-arm64 /usr/local/bin/aigate
8282
```
8383

84-
### From Source (Go 1.24+)
84+
### From Source (Go 1.25+)
8585
```sh
8686
go install github.com/AxeForging/aigate@latest
8787
```
@@ -192,6 +192,17 @@ Example output:
192192
Isolation mode: bwrap + slirp4netns (full isolation)
193193
```
194194

195+
### serve
196+
197+
Run a local web dashboard over the audit log. Read-only, binds to loopback by default — nothing is exposed off-host.
198+
199+
```sh
200+
aigate serve # http://127.0.0.1:8080
201+
aigate serve --addr 127.0.0.1:9000 # custom address (or AIGATE_ADDR env var)
202+
```
203+
204+
The dashboard shows live counters and a timeline of `run_started` and `blocked` events drawn from `~/.aigate/audit.jsonl` (see [Audit log](#audit-log)).
205+
195206
### reset
196207

197208
Remove everything (group, user, config):
@@ -217,27 +228,49 @@ deny_read:
217228
- "~/.ssh/"
218229
- "*.pem"
219230
- "*.key"
231+
- "*.p12"
220232
- "~/.aws/"
221233
- "~/.gcloud/"
222234
- "~/.kube/config"
223235
- "~/.npmrc"
224236
- "~/.pypirc"
237+
- "terraform.tfstate"
238+
- "*.tfvars"
225239
deny_exec:
226240
- "curl"
227241
- "wget"
228242
- "nc"
243+
- "ncat"
244+
- "netcat"
229245
- "ssh"
230246
- "scp"
247+
- "rsync"
248+
- "ftp"
231249
- "kubectl delete"
232250
- "kubectl exec"
233251
allow_net:
234252
- "api.anthropic.com"
235253
- "api.openai.com"
236254
- "api.github.com"
255+
- "registry.npmjs.org"
256+
- "proxy.golang.org"
237257
resource_limits:
238258
max_memory: "4G"
239259
max_cpu_percent: 80
240260
max_pids: 1000
261+
mask_stdout:
262+
presets:
263+
- openai
264+
- anthropic
265+
- aws_key
266+
- aws_secret
267+
- github
268+
- bearer
269+
patterns:
270+
# Generic key=value / key: value assignments for common secret names
271+
- regex: "(?:api_?key|secret|password|passwd|token|credential)\\s*[=:]\\s*\\S+"
272+
show_prefix: 0
273+
case_insensitive: true
241274
```
242275
243276
### Output Masking (mask_stdout)
@@ -251,6 +284,7 @@ resource_limits:
251284
| `openai` | `sk-...` / `sk-proj-...` | `sk-***` |
252285
| `anthropic` | `sk-ant-...` | `sk-ant-***` |
253286
| `aws_key` | `AKIA...` (access key ID) | `AKIA***` |
287+
| `aws_secret` | `AWS_SECRET_ACCESS_KEY=...` assignment | `AWS_SECRET_ACCESS_KEY=***` |
254288
| `github` | `ghp_`, `gho_`, `ghu_`, `ghs_`, `ghr_` | `ghp_***` |
255289
| `bearer` | `Bearer <token>` in headers/logs | `Bearer ***` |
256290

@@ -380,6 +414,20 @@ Without `bwrap`, aigate falls back to `unshare --user --map-root-user` + shell s
380414

381415
Resource limits (`max_memory`, `max_cpu_percent`, `max_pids`) are defined in the config but **not yet enforced**. Enforcement via cgroups v2 controllers is planned for a future release.
382416

417+
## Audit log
418+
419+
Each sandboxed run appends a line to `~/.aigate/audit.jsonl` (JSON Lines). Two kinds of events are recorded:
420+
421+
- `run_started` — a sandbox launched, with the command and a count of active `deny_read` / `deny_exec` / `allow_net` / masking rules
422+
- `blocked` — a command was refused by the `deny_exec` preflight check, with the matched rule
423+
424+
```json
425+
{"time":"2026-06-08T17:00:00Z","kind":"run_started","command":"claude","work_dir":"/home/me/proj","counts":{"deny_read":15,"deny_exec":11,"allow_net":5,"masking":7}}
426+
{"time":"2026-06-08T17:01:12Z","kind":"blocked","rule":"curl","command":"curl ifconfig.me","source":"preflight"}
427+
```
428+
429+
The file is plain text — `tail -f ~/.aigate/audit.jsonl` works, or run [`aigate serve`](#serve) for a live dashboard.
430+
383431
## Troubleshooting
384432

385433
### "operation requires elevated privileges"

0 commit comments

Comments
 (0)