aigate is a Go CLI tool that creates OS-level sandboxes for AI coding agents (Claude Code, Cursor, Copilot, etc.). It uses kernel-enforced isolation (ACLs, namespaces, cgroups) to restrict what AI tools can read, execute, and access on the network. Think of it as a Python venv but for AI permissions.
main.go Entry point, service wiring, CLI commands
flags.go All CLI flag definitions
domain/ Pure data structures
types.go Rule, Config, SandboxProfile, ResourceLimits
services/ Core business logic
platform.go Platform interface + Executor interface + resolvePatterns
platform_linux.go Linux: setfacl, groupadd/useradd, RunSandboxed dispatch
platform_linux_bwrap.go Linux bwrap path: buildBwrapArgs, runWithBwrap, runWithBwrapNetFilter
platform_darwin.go macOS: chmod +a, dscl, sandbox-exec
config_service.go Config load/save/merge (global + project)
rule_service.go Rule CRUD (add/remove/list deny rules)
runner_service.go Sandboxed process launcher
actions/ CLI command handlers
init.go Create group, user, default config
deny.go Add deny rules (read, exec, net subcommands)
allow.go Remove deny rules
run.go Run command inside sandbox
status.go Show current sandbox state
reset.go Remove group, user, config
doctor.go Check prerequisites and active isolation mode
helpers/ Logging and error types
logger.go zerolog console logger
errors.go Sentinel errors
integration/ End-to-end CLI tests
cli_test.go Build binary, run real commands
- Platform interface: Linux and macOS use completely different OS mechanisms. The
Platforminterface abstracts this withnewPlatform()factory via build tags. - Executor interface: All
exec.Commandcalls go throughExecutor, enabling unit tests without root. Exception:runWithBwrapNetFilterusesexec.Commanddirectly because it needscmd.Start()+ExtraFilesfor the info-fd pipe, which the Executor interface does not expose. - bwrap-first on Linux:
RunSandboxedprefers bwrap when available; falls back tounshare-based shell scripts. bwrap uses declarative bind mounts (no shell injection risk), resolves symlinks for bind destinations, and handles capabilities via--uid 0 --cap-addfor the network path. - No CGO: All platform operations use
exec.Commandto call system utilities (setfacl, groupadd, dscl, chmod, bwrap, slirp4netns). - Config merging: Global config (
~/.aigate/config.yaml) + project config (.aigate.yaml) merge with project extending global.
# All tests
go test ./... -v
# Unit tests only (services)
go test ./services -v
# Integration tests only (builds binary)
go test ./integration -v- Create
actions/mycommand.gowith struct + constructor + Execute method - Add flags to
flags.goif needed - Register in
main.gounderapp.Commands - Add integration test in
integration/cli_test.go
- Create
services/platform_newos.gowith//go:build newosconstraint - Implement
newPlatform()factory and allPlatforminterface methods - Add test file
services/platform_newos_test.go
github.com/urfave/cliv1 - CLI frameworkgithub.com/rs/zerolog- Structured logginggopkg.in/yaml.v3- Config file parsing