rename Fence to Greywall as GreyHaven sandboxing component

Rebrand the project from Fence to Greywall, the sandboxing layer of the
GreyHaven platform. This updates:

- Go module path to gitea.app.monadical.io/monadical/greywall
- Binary name, CLI help text, and all usage examples
- Config paths (~/.config/greywall/greywall.json), env vars (GREYWALL_*)
- Log prefixes ([greywall:*]), temp file prefixes (greywall-*)
- All documentation, scripts, CI workflows, and example files
- README rewritten with GreyHaven branding and Fence attribution

Directory/file renames: cmd/fence → cmd/greywall, pkg/fence → pkg/greywall,
docs/why-fence.md → docs/why-greywall.md, example JSON files, and banner.
This commit is contained in:
2026-02-10 16:00:24 -06:00
parent 481616455a
commit da3a2ac3a4
68 changed files with 586 additions and 586 deletions

View File

@@ -1,26 +1,26 @@
# Fence Documentation
# Greywall Documentation
Fence is a sandboxing tool that restricts network and filesystem access for arbitrary commands. It's most useful for running semi-trusted code (package installs, build scripts, CI jobs, unfamiliar repos) with controlled side effects.
Greywall is a sandboxing tool that restricts network and filesystem access for arbitrary commands. It's most useful for running semi-trusted code (package installs, build scripts, CI jobs, unfamiliar repos) with controlled side effects.
## Getting Started
- [Quickstart](quickstart.md) - Install fence and run your first sandboxed command in 5 minutes
- [Why Fence](why-fence.md) - What problem it solves (and what it doesn't)
- [Quickstart](quickstart.md) - Install greywall and run your first sandboxed command in 5 minutes
- [Why Greywall](why-greywall.md) - What problem it solves (and what it doesn't)
## Guides
- [Concepts](concepts.md) - Mental model: OS sandbox + local proxies + config
- [Troubleshooting](troubleshooting.md) - Common failure modes and fixes
- [Using Fence with AI agents](agents.md) - Defense-in-depth and policy standardization
- [Using Greywall with AI agents](agents.md) - Defense-in-depth and policy standardization
- [Recipes](recipes/README.md) - Common workflows (npm/pip/git/CI)
- [Templates](./templates.md) - Copy/paste templates you can start from
## Reference
- [README](../README.md) - CLI usage
- [Library Usage (Go)](library.md) - Using Fence as a Go package
- [Configuration](./configuration.md) - How to configure Fence
- [Architecture](../ARCHITECTURE.md) - How fence works under the hood
- [Library Usage (Go)](library.md) - Using Greywall as a Go package
- [Configuration](./configuration.md) - How to configure Greywall
- [Architecture](../ARCHITECTURE.md) - How greywall works under the hood
- [Security model](security-model.md) - Threat model, guarantees, and limitations
- [Linux security features](linux-security-features.md) - Landlock, seccomp, eBPF details and fallback behavior
- [Testing](testing.md) - How to run tests and write new ones
@@ -36,20 +36,20 @@ See [`examples/`](../examples/README.md) for runnable demos.
```bash
# Block all network (default)
fence <command>
greywall <command>
# Use custom config
fence --settings ./fence.json <command>
greywall --settings ./greywall.json <command>
# Debug mode (verbose output)
fence -d <command>
greywall -d <command>
# Monitor mode (show blocked requests)
fence -m <command>
greywall -m <command>
# Expose port for servers
fence -p 3000 <command>
greywall -p 3000 <command>
# Run shell command
fence -c "echo hello && ls"
greywall -c "echo hello && ls"
```

View File

@@ -1,6 +1,6 @@
# Using Fence with AI Agents
# Using Greywall with AI Agents
Many popular coding agents already include sandboxing. Fence can still be useful when you want a tool-agnostic policy layer that works the same way across:
Many popular coding agents already include sandboxing. Greywall can still be useful when you want a tool-agnostic policy layer that works the same way across:
- local developer machines
- CI jobs
@@ -15,7 +15,7 @@ Treat an agent as "semi-trusted automation":
- Allowlist only the network destinations you actually need
- Use `-m` (monitor mode) to audit blocked attempts and tighten policy
Fence can also reduce the risk of running agents with fewer interactive permission prompts (e.g. "skip permissions"), as long as your Fence config tightly scopes writes and outbound destinations. It's defense-in-depth, not a substitute for the agent's own safeguards.
Greywall can also reduce the risk of running agents with fewer interactive permission prompts (e.g. "skip permissions"), as long as your Greywall config tightly scopes writes and outbound destinations. It's defense-in-depth, not a substitute for the agent's own safeguards.
## Example: API-only agent
@@ -33,7 +33,7 @@ Fence can also reduce the risk of running agents with fewer interactive permissi
Run:
```bash
fence --settings ./fence.json <agent-command>
greywall --settings ./greywall.json <agent-command>
```
## Popular CLI coding agents
@@ -43,7 +43,7 @@ We provide these template for guardrailing CLI coding agents:
- [`code`](/internal/templates/code.json) - Strict deny-by-default network filtering via proxy. Works with agents that respect `HTTP_PROXY`. Blocks cloud metadata APIs, protects secrets, restricts dangerous commands.
- [`code-relaxed`](/internal/templates/code-relaxed.json) - Allows direct network connections for agents that ignore `HTTP_PROXY`. Same filesystem/command protections as `code`, but `deniedDomains` only enforced for proxy-respecting apps.
You can use it like `fence -t code -- claude`.
You can use it like `greywall -t code -- claude`.
| Agent | Works with template | Notes |
|-------|--------| ----- |
@@ -60,7 +60,7 @@ Note: On Linux, if OpenCode or Gemini CLI is installed via Linuxbrew, Landlock c
## Protecting your environment
Fence includes additional "dangerous file protection" (writes blocked regardless of config) to reduce persistence and environment-tampering vectors like:
Greywall includes additional "dangerous file protection" (writes blocked regardless of config) to reduce persistence and environment-tampering vectors like:
- `.git/hooks/*`
- shell startup files (`.zshrc`, `.bashrc`, etc.)

View File

@@ -1,6 +1,6 @@
# Benchmarking
This document describes how to run, interpret, and compare sandbox performance benchmarks for Fence.
This document describes how to run, interpret, and compare sandbox performance benchmarks for Greywall.
## Quick Start
@@ -29,9 +29,9 @@ go test -run=^$ -bench=. -benchmem ./internal/sandbox/...
### Layer 1: CLI Benchmarks (`scripts/benchmark.sh`)
**What it measures**: Real-world agent cost - full `fence` invocation including proxy startup, socat bridges (Linux), and sandbox-exec/bwrap setup.
**What it measures**: Real-world agent cost - full `greywall` invocation including proxy startup, socat bridges (Linux), and sandbox-exec/bwrap setup.
This is the most realistic benchmark for understanding the cost of running agent commands through Fence.
This is the most realistic benchmark for understanding the cost of running agent commands through Greywall.
```bash
# Full benchmark suite
@@ -51,7 +51,7 @@ This is the most realistic benchmark for understanding the cost of running agent
| Option | Description |
|--------|-------------|
| `-b, --binary PATH` | Path to fence binary (default: ./fence) |
| `-b, --binary PATH` | Path to greywall binary (default: ./greywall) |
| `-o, --output DIR` | Output directory (default: ./benchmarks) |
| `-n, --runs N` | Minimum runs per benchmark (default: 30) |
| `-q, --quick` | Quick mode: fewer runs, skip slow benchmarks |
@@ -92,13 +92,13 @@ benchstat bench.txt
```bash
# Quick syscall cost breakdown
strace -f -c ./fence -- true
strace -f -c ./greywall -- true
# Context switches, page faults
perf stat -- ./fence -- true
perf stat -- ./greywall -- true
# Full profiling (flamegraph-ready)
perf record -F 99 -g -- ./fence -- git status
perf record -F 99 -g -- ./greywall -- git status
perf report
```
@@ -106,10 +106,10 @@ perf report
```bash
# Time Profiler via Instruments
xcrun xctrace record --template 'Time Profiler' --launch -- ./fence -- true
xcrun xctrace record --template 'Time Profiler' --launch -- ./greywall -- true
# Quick call-stack snapshot
./fence -- sleep 5 &
./greywall -- sleep 5 &
sample $! 5 -file sample.txt
```
@@ -150,7 +150,7 @@ The overhead factor decreases as the actual workload increases (because sandbox
1. Run benchmarks on each platform independently
2. Compare overhead factors, not absolute times
3. Use the same fence version and workloads
3. Use the same greywall version and workloads
```bash
# On macOS
@@ -256,7 +256,7 @@ Linux initialization is ~3,700x slower because it must:
macOS only generates a Seatbelt profile string (very cheap).
### Cold Start Overhead (one `fence` invocation per command)
### Cold Start Overhead (one `greywall` invocation per command)
| Workload | Linux | macOS |
|----------|-------|-------|
@@ -264,7 +264,7 @@ macOS only generates a Seatbelt profile string (very cheap).
| Python | 124 ms | 33 ms |
| Git status | 114 ms | 25 ms |
This is the realistic cost for scripts running `fence -c "command"` repeatedly.
This is the realistic cost for scripts running `greywall -c "command"` repeatedly.
### Warm Path Overhead (pre-initialized manager)
@@ -289,9 +289,9 @@ Overhead decreases as the actual workload increases (sandbox setup is fixed cost
## Impact on Agent Usage
### Long-Running Agents (`fence claude`, `fence codex`)
### Long-Running Agents (`greywall claude`, `greywall codex`)
For agents that run as a child process under fence:
For agents that run as a child process under greywall:
| Phase | Cost |
|-------|------|
@@ -306,11 +306,11 @@ Child processes inherit the sandbox - no re-initialization, no WrapCommand overh
| `git status` | 2.1 ms | 5.9 ms |
| Python script | 11 ms | 15 ms |
**Bottom line**: For `fence <agent>` usage, sandbox overhead is a one-time startup cost. Tool calls inside the agent run at native speed.
**Bottom line**: For `greywall <agent>` usage, sandbox overhead is a one-time startup cost. Tool calls inside the agent run at native speed.
### Per-Command Invocation (`fence -c "command"`)
### Per-Command Invocation (`greywall -c "command"`)
For scripts or CI running fence per command:
For scripts or CI running greywall per command:
| Session | Linux Cost | macOS Cost |
|---------|------------|------------|

View File

@@ -1,15 +1,15 @@
# Concepts
Fence combines two ideas:
Greywall combines two ideas:
1. **An OS sandbox** to enforce "no direct network" and restrict filesystem operations.
2. **Local filtering proxies** (HTTP + SOCKS5) to selectively allow outbound traffic by domain.
## Network model
By default, fence blocks all outbound network access.
By default, greywall blocks all outbound network access.
When you allow domains, fence:
When you allow domains, greywall:
- Starts local HTTP and SOCKS5 proxies
- Sets proxy environment variables (`HTTP_PROXY`, `HTTPS_PROXY`, `ALL_PROXY`)
@@ -29,13 +29,13 @@ These are separate on purpose. A typical safe default for dev servers is:
## Filesystem model
Fence is designed around "read mostly, write narrowly":
Greywall is designed around "read mostly, write narrowly":
- **Reads**: allowed by default (you can block specific paths via `denyRead`).
- **Writes**: denied by default (you must opt-in with `allowWrite`).
- **denyWrite**: overrides `allowWrite` (useful for protecting secrets and dangerous files).
Fence also protects some dangerous targets regardless of config (e.g. shell startup files and git hooks). See `ARCHITECTURE.md` for the full list.
Greywall also protects some dangerous targets regardless of config (e.g. shell startup files and git hooks). See `ARCHITECTURE.md` for the full list.
## Debug vs Monitor mode

View File

@@ -1,6 +1,6 @@
# Configuration
Fence reads settings from `~/.config/fence/fence.json` by default (or `~/Library/Application Support/fence/fence.json` on macOS). Legacy `~/.fence.json` is also supported. Pass `--settings ./fence.json` to use a custom path. Config files support JSONC.
Greywall reads settings from `~/.config/greywall/greywall.json` by default (or `~/Library/Application Support/greywall/greywall.json` on macOS). Legacy `~/.greywall.json` is also supported. Pass `--settings ./greywall.json` to use a custom path. Config files support JSONC.
Example config:
@@ -60,7 +60,7 @@ You can also extend other config files using absolute or relative paths:
```json
{
"extends": "/etc/fence/company-base.json",
"extends": "/etc/greywall/company-base.json",
"filesystem": {
"denyRead": ["~/company-secrets/**"]
}
@@ -143,7 +143,7 @@ Example:
### Default Denied Commands
When `useDefaults` is `true` (the default), fence blocks these dangerous commands:
When `useDefaults` is `true` (the default), greywall blocks these dangerous commands:
- System control: `shutdown`, `reboot`, `halt`, `poweroff`, `init 0/6`
- Kernel manipulation: `insmod`, `rmmod`, `modprobe`, `kexec`
@@ -155,7 +155,7 @@ To disable defaults: `"useDefaults": false`
### Command Detection
Fence detects blocked commands in:
Greywall detects blocked commands in:
- Direct commands: `git push origin main`
- Command chains: `ls && git push` or `ls; git push`
@@ -260,26 +260,26 @@ SSH host patterns support wildcards anywhere:
## Importing from Claude Code
If you've been using Claude Code and have already built up permission rules, you can import them into fence:
If you've been using Claude Code and have already built up permission rules, you can import them into greywall:
```bash
# Preview import (prints JSON to stdout)
fence import --claude
greywall import --claude
# Save to the default config path
fence import --claude --save
greywall import --claude --save
# Import from a specific file
fence import --claude -f ~/.claude/settings.json --save
greywall import --claude -f ~/.claude/settings.json --save
# Save to a specific output file
fence import --claude -o ./fence.json
greywall import --claude -o ./greywall.json
# Import without extending any template (minimal config)
fence import --claude --no-extend --save
greywall import --claude --no-extend --save
# Import and extend a different template
fence import --claude --extend local-dev-server --save
greywall import --claude --extend local-dev-server --save
```
### Default Template
@@ -294,7 +294,7 @@ Use `--no-extend` if you want a minimal config without these defaults, or `--ext
### Permission Mapping
| Claude Code | Fence |
| Claude Code | Greywall |
|-------------|-------|
| `Bash(xyz)` allow | `command.allow: ["xyz"]` |
| `Bash(xyz:*)` deny | `command.deny: ["xyz"]` |
@@ -302,9 +302,9 @@ Use `--no-extend` if you want a minimal config without these defaults, or `--ext
| `Write(path)` allow | `filesystem.allowWrite: [path]` |
| `Write(path)` deny | `filesystem.denyWrite: [path]` |
| `Edit(path)` | Same as `Write(path)` |
| `ask` rules | Converted to deny (fence doesn't support interactive prompts) |
| `ask` rules | Converted to deny (greywall doesn't support interactive prompts) |
Global tool permissions (e.g., bare `Read`, `Write`, `Grep`) are skipped since fence uses path/command-based rules.
Global tool permissions (e.g., bare `Read`, `Write`, `Grep`) are skipped since greywall uses path/command-based rules.
## See Also

View File

@@ -1,11 +1,11 @@
# Library Usage (Go)
Fence can be used as a Go library to sandbox commands programmatically.
Greywall can be used as a Go library to sandbox commands programmatically.
## Installation
```bash
go get github.com/Use-Tusk/fence
go get gitea.app.monadical.io/monadical/greywall
```
## Quick Start
@@ -17,25 +17,25 @@ import (
"fmt"
"os/exec"
"github.com/Use-Tusk/fence/pkg/fence"
"gitea.app.monadical.io/monadical/greywall/pkg/greywall"
)
func main() {
// Check platform support
if !fence.IsSupported() {
if !greywall.IsSupported() {
fmt.Println("Sandboxing not supported on this platform")
return
}
// Create config
cfg := &fence.Config{
Network: fence.NetworkConfig{
cfg := &greywall.Config{
Network: greywall.NetworkConfig{
AllowedDomains: []string{"api.example.com"},
},
}
// Create and initialize manager
manager := fence.NewManager(cfg, false, false)
manager := greywall.NewManager(cfg, false, false)
defer manager.Cleanup()
if err := manager.Initialize(); err != nil {
@@ -64,7 +64,7 @@ func main() {
Returns `true` if the current platform supports sandboxing (macOS or Linux).
```go
if !fence.IsSupported() {
if !greywall.IsSupported() {
log.Fatal("Platform not supported")
}
```
@@ -74,7 +74,7 @@ if !fence.IsSupported() {
Returns a default configuration with all network blocked.
```go
cfg := fence.DefaultConfig()
cfg := greywall.DefaultConfig()
cfg.Network.AllowedDomains = []string{"example.com"}
```
@@ -83,18 +83,18 @@ cfg.Network.AllowedDomains = []string{"example.com"}
Loads configuration from a JSON file. Supports JSONC (comments allowed).
```go
cfg, err := fence.LoadConfig(fence.DefaultConfigPath())
cfg, err := greywall.LoadConfig(greywall.DefaultConfigPath())
if err != nil {
log.Fatal(err)
}
if cfg == nil {
cfg = fence.DefaultConfig() // File doesn't exist
cfg = greywall.DefaultConfig() // File doesn't exist
}
```
#### `DefaultConfigPath() string`
Returns the default config file path (`~/.config/fence/fence.json` on Linux, `~/Library/Application Support/fence/fence.json` on macOS, with fallback to legacy `~/.fence.json`).
Returns the default config file path (`~/.config/greywall/greywall.json` on Linux, `~/Library/Application Support/greywall/greywall.json` on macOS, with fallback to legacy `~/.greywall.json`).
#### `NewManager(cfg *Config, debug, monitor bool) *Manager`
@@ -113,7 +113,7 @@ Creates a new sandbox manager.
Sets up sandbox infrastructure (starts HTTP and SOCKS proxies). Called automatically by `WrapCommand` if not already initialized.
```go
manager := fence.NewManager(cfg, false, false)
manager := greywall.NewManager(cfg, false, false)
defer manager.Cleanup()
if err := manager.Initialize(); err != nil {
@@ -222,8 +222,8 @@ type SSHConfig struct {
### Allow specific domains
```go
cfg := &fence.Config{
Network: fence.NetworkConfig{
cfg := &greywall.Config{
Network: greywall.NetworkConfig{
AllowedDomains: []string{
"registry.npmjs.org",
"*.github.com",
@@ -236,8 +236,8 @@ cfg := &fence.Config{
### Restrict filesystem access
```go
cfg := &fence.Config{
Filesystem: fence.FilesystemConfig{
cfg := &greywall.Config{
Filesystem: greywall.FilesystemConfig{
AllowWrite: []string{".", "/tmp"},
DenyRead: []string{"~/.ssh", "~/.aws"},
},
@@ -247,8 +247,8 @@ cfg := &fence.Config{
### Block dangerous commands
```go
cfg := &fence.Config{
Command: fence.CommandConfig{
cfg := &greywall.Config{
Command: greywall.CommandConfig{
Deny: []string{
"rm -rf /",
"git push",
@@ -261,7 +261,7 @@ cfg := &fence.Config{
### Expose dev server port
```go
manager := fence.NewManager(cfg, false, false)
manager := greywall.NewManager(cfg, false, false)
manager.SetExposedPorts([]int{3000})
defer manager.Cleanup()
@@ -271,12 +271,12 @@ wrapped, _ := manager.WrapCommand("npm run dev")
### Load and extend config
```go
cfg, err := fence.LoadConfig(fence.DefaultConfigPath())
cfg, err := greywall.LoadConfig(greywall.DefaultConfigPath())
if err != nil {
log.Fatal(err)
}
if cfg == nil {
cfg = fence.DefaultConfig()
cfg = greywall.DefaultConfig()
}
// Add additional restrictions

View File

@@ -1,6 +1,6 @@
# Linux Security Features
Fence uses multiple layers of security on Linux, with graceful fallback when features are unavailable.
Greywall uses multiple layers of security on Linux, with graceful fallback when features are unavailable.
## Security Layers
@@ -13,13 +13,13 @@ Fence uses multiple layers of security on Linux, with graceful fallback when fea
## Feature Detection
Fence automatically detects available features and uses the best available combination.
Greywall automatically detects available features and uses the best available combination.
To see what features are detected:
```bash
# Check what features are available on your system
fence --linux-features
greywall --linux-features
# Example output:
# Linux Sandbox Features:
@@ -41,7 +41,7 @@ fence --linux-features
Landlock is applied via an **embedded wrapper** approach:
1. bwrap spawns `fence --landlock-apply -- <user-command>`
1. bwrap spawns `greywall --landlock-apply -- <user-command>`
2. The wrapper applies Landlock kernel restrictions
3. The wrapper `exec()`s the user command
@@ -75,25 +75,25 @@ This provides **defense-in-depth**: both bwrap mounts AND Landlock kernel restri
- **Impact**: `--unshare-net` is skipped; network is not fully isolated
- **Cause**: Running in Docker, GitHub Actions, or other environments without `CAP_NET_ADMIN`
- **Fallback**: Proxy-based filtering still works; filesystem/PID/seccomp isolation still active
- **Check**: Run `fence --linux-features` and look for "Network namespace (--unshare-net): false"
- **Check**: Run `greywall --linux-features` and look for "Network namespace (--unshare-net): false"
- **Workaround**: Run with `sudo`, or in Docker use `--cap-add=NET_ADMIN`
> [!NOTE]
> This is the most common "reduced isolation" scenario. Fence automatically detects this at startup and adapts. See the troubleshooting guide for more details.
> This is the most common "reduced isolation" scenario. Greywall automatically detects this at startup and adapts. See the troubleshooting guide for more details.
### When bwrap is not available
- **Impact**: Cannot run fence on Linux
- **Impact**: Cannot run greywall on Linux
- **Solution**: Install bubblewrap: `apt install bubblewrap` or `dnf install bubblewrap`
### When socat is not available
- **Impact**: Cannot run fence on Linux
- **Impact**: Cannot run greywall on Linux
- **Solution**: Install socat: `apt install socat` or `dnf install socat`
## Blocked Syscalls (seccomp)
Fence blocks dangerous syscalls that could be used for sandbox escape or privilege escalation:
Greywall blocks dangerous syscalls that could be used for sandbox escape or privilege escalation:
| Syscall | Reason |
|---------|--------|
@@ -111,13 +111,13 @@ Fence blocks dangerous syscalls that could be used for sandbox escape or privile
## Violation Monitoring
On Linux, violation monitoring (`fence -m`) shows:
On Linux, violation monitoring (`greywall -m`) shows:
| Source | What it shows | Requirements |
|--------|---------------|--------------|
| `[fence:http]` | Blocked HTTP/HTTPS requests | None |
| `[fence:socks]` | Blocked SOCKS connections | None |
| `[fence:ebpf]` | Blocked filesystem access + syscalls | CAP_BPF or root |
| `[greywall:http]` | Blocked HTTP/HTTPS requests | None |
| `[greywall:socks]` | Blocked SOCKS connections | None |
| `[greywall:ebpf]` | Blocked filesystem access + syscalls | CAP_BPF or root |
**Notes**:
@@ -127,7 +127,7 @@ On Linux, violation monitoring (`fence -m`) shows:
## Comparison with macOS
| Feature | macOS (Seatbelt) | Linux (fence) |
| Feature | macOS (Seatbelt) | Linux (greywall) |
|---------|------------------|---------------|
| Filesystem control | Native | bwrap + Landlock |
| Glob patterns | Native regex | Expanded at startup |
@@ -181,12 +181,12 @@ sudo apk add bubblewrap socat
For full violation visibility without root:
```bash
# Grant CAP_BPF to the fence binary
sudo setcap cap_bpf+ep /usr/local/bin/fence
# Grant CAP_BPF to the greywall binary
sudo setcap cap_bpf+ep /usr/local/bin/greywall
```
Or run fence with sudo when monitoring is needed:
Or run greywall with sudo when monitoring is needed:
```bash
sudo fence -m <command>
sudo greywall -m <command>
```

View File

@@ -5,16 +5,16 @@
### From Source (recommended for now)
```bash
git clone https://github.com/Use-Tusk/fence
cd fence
go build -o fence ./cmd/fence
sudo mv fence /usr/local/bin/
git clone https://gitea.app.monadical.io/monadical/greywall
cd greywall
go build -o greywall ./cmd/greywall
sudo mv greywall /usr/local/bin/
```
### Using Go Install
```bash
go install github.com/Use-Tusk/fence/cmd/fence@latest
go install gitea.app.monadical.io/monadical/greywall/cmd/greywall@latest
```
### Linux Dependencies
@@ -32,30 +32,30 @@ sudo dnf install bubblewrap socat
sudo pacman -S bubblewrap socat
```
### Do I need sudo to run fence?
### Do I need sudo to run greywall?
No, for most Linux systems. Fence works without root privileges because:
No, for most Linux systems. Greywall works without root privileges because:
- Package-manager-installed `bubblewrap` is typically already setuid
- Fence detects available capabilities and adapts automatically
- Greywall detects available capabilities and adapts automatically
If some features aren't available (like network namespaces in Docker/CI), fence falls back gracefully - you'll still get filesystem isolation, command blocking, and proxy-based network filtering.
If some features aren't available (like network namespaces in Docker/CI), greywall falls back gracefully - you'll still get filesystem isolation, command blocking, and proxy-based network filtering.
Run `fence --linux-features` to see what's available in your environment.
Run `greywall --linux-features` to see what's available in your environment.
## Verify Installation
```bash
fence --version
greywall --version
```
## Your First Sandboxed Command
By default, fence blocks all network access:
By default, greywall blocks all network access:
```bash
# This will fail - network is blocked
fence curl https://example.com
greywall curl https://example.com
```
You should see something like:
@@ -66,7 +66,7 @@ curl: (56) CONNECT tunnel failed, response 403
## Allow Specific Domains
Create a config file at `~/.config/fence/fence.json` (or `~/Library/Application Support/fence/fence.json` on macOS):
Create a config file at `~/.config/greywall/greywall.json` (or `~/Library/Application Support/greywall/greywall.json` on macOS):
```json
{
@@ -79,7 +79,7 @@ Create a config file at `~/.config/fence/fence.json` (or `~/Library/Application
Now try again:
```bash
fence curl https://example.com
greywall curl https://example.com
```
This time it succeeds!
@@ -89,7 +89,7 @@ This time it succeeds!
Use `-d` to see what's happening under the hood:
```bash
fence -d curl https://example.com
greywall -d curl https://example.com
```
This shows:
@@ -103,7 +103,7 @@ This shows:
Use `-m` to see only violations and blocked requests:
```bash
fence -m npm install
greywall -m npm install
```
This is useful for:
@@ -117,7 +117,7 @@ This is useful for:
Use `-c` to run compound commands:
```bash
fence -c "echo hello && ls -la"
greywall -c "echo hello && ls -la"
```
## Expose Ports for Servers
@@ -125,14 +125,14 @@ fence -c "echo hello && ls -la"
If you're running a server that needs to accept connections:
```bash
fence -p 3000 -c "npm run dev"
greywall -p 3000 -c "npm run dev"
```
This allows external connections to port 3000 while keeping outbound network restricted.
## Next steps
- Read **[Why Fence](why-fence.md)** to understand when fence is a good fit (and when it isn't).
- Read **[Why Greywall](why-greywall.md)** to understand when greywall is a good fit (and when it isn't).
- Learn the mental model in **[Concepts](concepts.md)**.
- Use **[Troubleshooting](troubleshooting.md)** if something is blocked unexpectedly.
- Start from copy/paste configs in **[`docs/templates/`](templates/README.md)**.

View File

@@ -18,7 +18,7 @@ Goal: make CI steps safer by default: minimal egress and controlled writes.
Run:
```bash
fence --settings ./fence.json -c "make test"
greywall --settings ./greywall.json -c "make test"
```
## Add only what you need
@@ -26,7 +26,7 @@ fence --settings ./fence.json -c "make test"
Use monitor mode to discover what a job tries to reach:
```bash
fence -m --settings ./fence.json -c "make test"
greywall -m --settings ./greywall.json -c "make test"
```
Then allowlist only:

View File

@@ -18,7 +18,7 @@ Goal: allow fetching code from a limited set of hosts.
Run:
```bash
fence --settings ./fence.json git clone https://github.com/OWNER/REPO.git
greywall --settings ./greywall.json git clone https://github.com/OWNER/REPO.git
```
## SSH clone
@@ -28,5 +28,5 @@ SSH traffic may go through SOCKS5 (`ALL_PROXY`) depending on your git/ssh config
If it fails, use monitor/debug mode to see what was blocked:
```bash
fence -m --settings ./fence.json git clone git@github.com:OWNER/REPO.git
greywall -m --settings ./greywall.json git clone git@github.com:OWNER/REPO.git
```

View File

@@ -18,7 +18,7 @@ Goal: allow npm to fetch packages, but block unexpected egress.
Run:
```bash
fence --settings ./fence.json npm install
greywall --settings ./greywall.json npm install
```
## Iterate with monitor mode
@@ -26,7 +26,7 @@ fence --settings ./fence.json npm install
If installs fail, run:
```bash
fence -m --settings ./fence.json npm install
greywall -m --settings ./greywall.json npm install
```
Then add the minimum extra domains required for your workflow (private registries, GitHub tarballs, etc.).

View File

@@ -18,19 +18,19 @@ Goal: allow Python dependency fetching while keeping egress minimal.
Run:
```bash
fence --settings ./fence.json pip install -r requirements.txt
greywall --settings ./greywall.json pip install -r requirements.txt
```
For Poetry:
```bash
fence --settings ./fence.json poetry install
greywall --settings ./greywall.json poetry install
```
## Iterate with monitor mode
```bash
fence -m --settings ./fence.json poetry install
greywall -m --settings ./greywall.json poetry install
```
If you use private indexes, add those domains explicitly.

View File

@@ -1,19 +1,19 @@
# Security Model
Fence is intended as defense-in-depth for running semi-trusted commands with reduced side effects (package installs, build scripts, CI jobs, unfamiliar repos).
Greywall is intended as defense-in-depth for running semi-trusted commands with reduced side effects (package installs, build scripts, CI jobs, unfamiliar repos).
It is not designed to be a strong isolation boundary against actively malicious code that is attempting to escape.
## Threat model (what Fence helps with)
## Threat model (what Greywall helps with)
Fence is useful when you want to reduce risk from:
Greywall is useful when you want to reduce risk from:
- Supply-chain scripts that unexpectedly call out to the network
- Tools that write broadly across your filesystem
- Accidental leakage of secrets via "phone home" behavior
- Unfamiliar repos that run surprising commands during install/build/test
## What Fence enforces
## What Greywall enforces
### Network
@@ -25,7 +25,7 @@ Important: domain filtering does not inspect content. If you allow a domain, cod
#### How allowlisting works
Fence combines OS-level enforcement with proxy-based allowlisting:
Greywall combines OS-level enforcement with proxy-based allowlisting:
- The OS sandbox / network namespace is expected to block direct outbound connections.
- Domain allowlisting happens via local HTTP/SOCKS proxies and proxy environment variables (`HTTP_PROXY`, `HTTPS_PROXY`, `ALL_PROXY`).
@@ -41,11 +41,11 @@ Localhost is separate from "external domains":
- **Writes are denied by default**; you must opt in with `allowWrite`.
- **denyWrite** can block specific files/patterns even if the parent directory is writable.
- **denyRead** can block reads from sensitive paths.
- Fence includes an internal list of always-protected targets (e.g. shell configs, git hooks) to reduce common persistence vectors.
- Greywall includes an internal list of always-protected targets (e.g. shell configs, git hooks) to reduce common persistence vectors.
### Environment sanitization
Fence strips dangerous environment variables before passing them to sandboxed commands:
Greywall strips dangerous environment variables before passing them to sandboxed commands:
- `LD_*` (Linux): `LD_PRELOAD`, `LD_LIBRARY_PATH`, etc.
- `DYLD_*` (macOS): `DYLD_INSERT_LIBRARIES`, `DYLD_LIBRARY_PATH`, etc.
@@ -57,11 +57,11 @@ This prevents a library injection attack where a sandboxed process writes a mali
- `-m/--monitor` helps you discover what a command *tries* to access (blocked only).
- `-d/--debug` shows more detail to understand why something was blocked.
## Limitations (what Fence does NOT try to solve)
## Limitations (what Greywall does NOT try to solve)
- **Hostile code containment**: assume determined attackers may escape via kernel/OS vulnerabilities.
- **Resource limits**: CPU, memory, disk, fork bombs, etc. are out of scope.
- **Content-based controls**: Fence does not block data exfiltration to *allowed* destinations.
- **Content-based controls**: Greywall does not block data exfiltration to *allowed* destinations.
- **Proxy limitations / protocol edge cases**: some programs may not respect proxy environment variables, so they won't get domain allowlisting unless you configure them to use a proxy (e.g. Node.js `http`/`https` without a proxy-aware client).
### Practical examples of proxy limitations
@@ -71,14 +71,14 @@ The proxy approach works well for many tools (curl, wget, git, npm, pip), but no
- Node.js native `http`/`https` (use a proxy-aware client, e.g. `undici` + `ProxyAgent`)
- Raw socket connections (custom TCP/UDP protocols)
Fence's OS-level sandbox is still expected to block direct outbound connections; bypassing the proxy should fail rather than silently succeeding.
Greywall's OS-level sandbox is still expected to block direct outbound connections; bypassing the proxy should fail rather than silently succeeding.
### Domain-based filtering only
Fence does not inspect request content. If you allow a domain, a sandboxed process can still exfiltrate data to that domain.
Greywall does not inspect request content. If you allow a domain, a sandboxed process can still exfiltrate data to that domain.
### Not a hostile-code containment boundary
Fence is defense-in-depth for running semi-trusted code, not a strong isolation boundary against malware designed to escape sandboxes.
Greywall is defense-in-depth for running semi-trusted code, not a strong isolation boundary against malware designed to escape sandboxes.
For implementation details (how proxies/sandboxes/bridges work), see [`ARCHITECTURE.md`](../ARCHITECTURE.md).

View File

@@ -1,6 +1,6 @@
# Config Templates
Fence includes built-in config templates for common use cases. Templates are embedded in the binary, so you can use them directly without copying files.
Greywall includes built-in config templates for common use cases. Templates are embedded in the binary, so you can use them directly without copying files.
## Using templates
@@ -8,13 +8,13 @@ Use the `-t` / `--template` flag to apply a template:
```bash
# Use a built-in template
fence -t npm-install npm install
greywall -t npm-install npm install
# Wraps Claude Code
fence -t code -- claude
greywall -t code -- claude
# List available templates
fence --list-templates
greywall --list-templates
```
You can also copy and customize templates from [`internal/templates/`](/internal/templates/).

View File

@@ -67,9 +67,9 @@ go test -v -count=1 ./internal/sandbox/...
#### Sandboxed Build Environments (Nix, etc.)
If you're packaging fence for a distribution (e.g., Nix, Homebrew, Debian), note that some integration tests will be skipped when running `go test` during the build.
If you're packaging greywall for a distribution (e.g., Nix, Homebrew, Debian), note that some integration tests will be skipped when running `go test` during the build.
Fence's Landlock integration on Linux uses a wrapper approach: the `fence` binary re-executes itself with `--landlock-apply` inside the sandbox. Test binaries (e.g., `sandbox.test`) don't have this handler, so Landlock-specific tests automatically skip when not running as the `fence` CLI.
Greywall's Landlock integration on Linux uses a wrapper approach: the `greywall` binary re-executes itself with `--landlock-apply` inside the sandbox. Test binaries (e.g., `sandbox.test`) don't have this handler, so Landlock-specific tests automatically skip when not running as the `greywall` CLI.
Tests that skip include those calling `skipIfLandlockNotUsable()`:
@@ -77,25 +77,25 @@ Tests that skip include those calling `skipIfLandlockNotUsable()`:
- `TestLinux_LandlockProtectsGitHooks`
- `TestLinux_LandlockProtectsGitConfig`
- `TestLinux_LandlockProtectsBashrc`
- `TestLinux_LandlockAllowsTmpFence`
- `TestLinux_LandlockAllowsTmpGreywall`
- `TestLinux_PathTraversalBlocked`
- `TestLinux_SeccompBlocksDangerousSyscalls`
| Test Type | What it tests | Landlock coverage |
|-----------|---------------|-------------------|
| `go test` (integration) | Go APIs, bwrap isolation, command blocking | Skipped (test binary can't use `--landlock-apply`) |
| `smoke_test.sh` | Actual `fence` CLI end-to-end | ✅ Full coverage |
| `smoke_test.sh` | Actual `greywall` CLI end-to-end | ✅ Full coverage |
For full test coverage including Landlock, run the smoke tests against the built binary (see "Smoke Tests" section below).
**Nested sandboxing limitations:**
- **macOS**: Nested Seatbelt sandboxing is not supported. If the build environment already uses `sandbox-exec` (like Nix's Darwin sandbox), fence's tests cannot create another sandbox. The kernel returns `forbidden-sandbox-reinit`. This is a macOS limitation.
- **macOS**: Nested Seatbelt sandboxing is not supported. If the build environment already uses `sandbox-exec` (like Nix's Darwin sandbox), greywall's tests cannot create another sandbox. The kernel returns `forbidden-sandbox-reinit`. This is a macOS limitation.
- **Linux**: Tests should work in most build sandboxes, but Landlock tests will skip as explained above. Runtime functionality is unaffected.
### Smoke Tests
Smoke tests verify the compiled `fence` binary works end-to-end. Unlike integration tests (which test internal Go APIs), smoke tests exercise the CLI interface.
Smoke tests verify the compiled `greywall` binary works end-to-end. Unlike integration tests (which test internal Go APIs), smoke tests exercise the CLI interface.
**File:** [`scripts/smoke_test.sh`](/scripts/smoke_test.sh)
@@ -105,7 +105,7 @@ Smoke tests verify the compiled `fence` binary works end-to-end. Unlike integrat
- Filesystem restrictions via settings file
- Command blocking via settings file
- Network blocking
- Environment variable injection (FENCE_SANDBOX, HTTP_PROXY)
- Environment variable injection (GREYWALL_SANDBOX, HTTP_PROXY)
- Tool compatibility (python3, node, git, rg) - ensure that frequently used tools don't break in sandbox
**Run:**
@@ -115,10 +115,10 @@ Smoke tests verify the compiled `fence` binary works end-to-end. Unlike integrat
./scripts/smoke_test.sh
# Test specific binary
./scripts/smoke_test.sh ./path/to/fence
./scripts/smoke_test.sh ./path/to/greywall
# Enable network tests (requires internet)
FENCE_TEST_NETWORK=1 ./scripts/smoke_test.sh
GREYWALL_TEST_NETWORK=1 ./scripts/smoke_test.sh
```
## Platform-Specific Behavior
@@ -158,7 +158,7 @@ The `integration_test.go` file provides helpers for writing sandbox tests:
```go
// Skip helpers
skipIfAlreadySandboxed(t) // Skip if running inside Fence
skipIfAlreadySandboxed(t) // Skip if running inside Greywall
skipIfCommandNotFound(t, "python3") // Skip if command missing
// Run a command under the sandbox
@@ -237,10 +237,10 @@ go test -v -run TestSpecificTest ./internal/sandbox/...
```bash
# Replicate what the test does
./fence -c "the-command-that-failed"
./greywall -c "the-command-that-failed"
# With a settings file
./fence -s /path/to/settings.json -c "command"
./greywall -s /path/to/settings.json -c "command"
```
### Check platform capabilities

View File

@@ -2,9 +2,9 @@
## Nested Sandboxing Not Supported
Fence cannot run inside another sandbox that uses the same underlying technology.
Greywall cannot run inside another sandbox that uses the same underlying technology.
**macOS (Seatbelt)**: If you try to run fence inside an existing `sandbox-exec` sandbox (e.g., Nix's Darwin build sandbox), you'll see:
**macOS (Seatbelt)**: If you try to run greywall inside an existing `sandbox-exec` sandbox (e.g., Nix's Darwin build sandbox), you'll see:
```text
Sandbox: sandbox-exec(...) deny(1) forbidden-sandbox-reinit
@@ -12,11 +12,11 @@ Sandbox: sandbox-exec(...) deny(1) forbidden-sandbox-reinit
This is a macOS kernel limitation - nested Seatbelt sandboxes are not allowed. There is no workaround.
**Linux (Landlock)**: Landlock supports stacking (nested restrictions), but fence's test binaries cannot use the Landlock wrapper (see [Testing docs](testing.md#sandboxed-build-environments-nix-etc)).
**Linux (Landlock)**: Landlock supports stacking (nested restrictions), but greywall's test binaries cannot use the Landlock wrapper (see [Testing docs](testing.md#sandboxed-build-environments-nix-etc)).
## "bwrap: loopback: Failed RTM_NEWADDR: Operation not permitted" (Linux)
This error occurs when fence tries to create a network namespace but the environment lacks the `CAP_NET_ADMIN` capability. This is common in:
This error occurs when greywall tries to create a network namespace but the environment lacks the `CAP_NET_ADMIN` capability. This is common in:
- **Docker containers** (unless run with `--privileged` or `--cap-add=NET_ADMIN`)
- **GitHub Actions** and other CI runners
@@ -25,7 +25,7 @@ This error occurs when fence tries to create a network namespace but the environ
**What happens now:**
Fence automatically detects this limitation and falls back to running **without network namespace isolation**. The sandbox still provides:
Greywall automatically detects this limitation and falls back to running **without network namespace isolation**. The sandbox still provides:
- Filesystem restrictions (read-only root, allowWrite paths)
- PID namespace isolation
@@ -41,7 +41,7 @@ Fence automatically detects this limitation and falls back to running **without
**To check if your environment supports network namespaces:**
```bash
fence --linux-features
greywall --linux-features
```
Look for "Network namespace (--unshare-net): true/false"
@@ -51,7 +51,7 @@ Look for "Network namespace (--unshare-net): true/false"
1. **Run with elevated privileges:**
```bash
sudo fence <command>
sudo greywall <command>
```
2. **In Docker, add capability:**
@@ -96,20 +96,20 @@ On most systems with package-manager-installed bwrap, this error shouldn't occur
This usually means:
- the process tried to reach a domain that is **not allowed**, and
- the request went through fence's HTTP proxy, which returned `403`.
- the request went through greywall's HTTP proxy, which returned `403`.
Fix:
- Run with monitor mode to see what was blocked:
- `fence -m <command>`
- `greywall -m <command>`
- Add the required destination(s) to `network.allowedDomains`.
## "It works outside fence but not inside"
## "It works outside greywall but not inside"
Start with:
- `fence -m <command>` to see what's being denied
- `fence -d <command>` to see full proxy and sandbox detail
- `greywall -m <command>` to see what's being denied
- `greywall -d <command>` to see full proxy and sandbox detail
Common causes:
@@ -134,7 +134,7 @@ const response = await fetch(url, {
});
```
Fence's OS-level sandbox should still block direct connections; the above makes your requests go through the filtering proxy so allowlisting works as intended.
Greywall's OS-level sandbox should still block direct connections; the above makes your requests go through the filtering proxy so allowlisting works as intended.
## Local services (Redis/Postgres/etc.) fail inside the sandbox
@@ -156,7 +156,7 @@ If you're running a server inside the sandbox that must accept connections:
Writes are denied by default.
- Add the minimum required writable directories to `filesystem.allowWrite`.
- Protect sensitive targets with `filesystem.denyWrite` (and note fence protects some targets regardless).
- Protect sensitive targets with `filesystem.denyWrite` (and note greywall protects some targets regardless).
Example:

View File

@@ -1,6 +1,6 @@
# Why Fence?
# Why Greywall?
Fence exists to reduce the blast radius of running commands you don't fully trust (or don't fully understand yet).
Greywall exists to reduce the blast radius of running commands you don't fully trust (or don't fully understand yet).
Common situations:
@@ -9,11 +9,11 @@ Common situations:
- Running CI jobs where you want default-deny egress and tightly scoped writes
- Auditing what a command *tries* to do before you let it do it
Fence is intentionally simple: it focuses on network allowlisting (by domain) and filesystem write restrictions (by path), wrapped in a pragmatic OS sandbox (macOS `sandbox-exec`, Linux `bubblewrap`).
Greywall is intentionally simple: it focuses on network allowlisting (by domain) and filesystem write restrictions (by path), wrapped in a pragmatic OS sandbox (macOS `sandbox-exec`, Linux `bubblewrap`).
## What problem does it solve?
Fence helps you answer: "What can this command touch?"
Greywall helps you answer: "What can this command touch?"
- **Network**: block all outbound by default; then allow only the domains you choose.
- **Filesystem**: default-deny writes; then allow writes only where you choose (and deny sensitive writes regardless).
@@ -21,9 +21,9 @@ Fence helps you answer: "What can this command touch?"
This is especially useful for supply-chain risk and "unknown repo" workflows where you want a safer default than "run it and hope".
## When Fence is useful even if tools already sandbox
## When Greywall is useful even if tools already sandbox
Some coding agents and platforms ship sandboxing (Seatbelt/Landlock/etc.). Fence still provides value when you want:
Some coding agents and platforms ship sandboxing (Seatbelt/Landlock/etc.). Greywall still provides value when you want:
- **Tool-agnostic policy**: apply the same rules to any command, not only inside one agent.
- **Standardization**: commit/review a config once, use it across developers and CI.
@@ -32,7 +32,7 @@ Some coding agents and platforms ship sandboxing (Seatbelt/Landlock/etc.). Fence
## Non-goals
Fence is **not** a hardened containment boundary for actively malicious code.
Greywall is **not** a hardened containment boundary for actively malicious code.
- It does **not** attempt to prevent resource exhaustion (CPU/RAM/disk), timing attacks, or kernel-level escapes.
- Domain allowlisting is not content inspection: if you allow a domain, code can exfiltrate via that domain.