|
1 | 1 | # Sluice |
2 | 2 |
|
3 | | -[](../../actions/workflows/ci.yml) |
4 | | -[](../../actions/workflows/e2e-linux.yml) |
5 | | -[](../../actions/workflows/e2e-macos.yml) |
| 3 | +Credential-injecting approval proxy for AI agents. Sluice sits between your AI agent and the internet, ensuring every outbound connection and tool call is governed by policy, approved by a human when needed, and never exposes real credentials to the agent. |
6 | 4 |
|
7 | | -Credential-injecting approval proxy for AI agents. Two layers of governance: MCP-level (semantic tool control) and network-level (all-protocol interception). Asks for human approval via Telegram, injects credentials, and forwards. |
| 5 | +## Why Sluice |
8 | 6 |
|
9 | | -**Status:** v0.0.1-alpha. Core proxy, policy engine, MCP gateway, and Telegram approval flow functional. |
| 7 | +AI agents need API keys, database credentials, and service tokens to do useful work. Giving them real credentials is risky. They can leak secrets in tool outputs, exfiltrate data to unexpected endpoints, or make destructive API calls without oversight. |
10 | 8 |
|
11 | | -## Quick Start |
| 9 | +Sluice solves this with two layers of governance: |
12 | 10 |
|
13 | | -```bash |
14 | | -go build -o sluice ./cmd/sluice/ |
15 | | -./sluice --db sluice.db --config examples/config.toml |
16 | | -``` |
| 11 | +- **MCP Gateway** -- intercepts tool calls between the agent and MCP servers. Sees tool names, arguments, and responses. Blocks dangerous operations (file writes, exec, deletions) and redacts secrets from responses. Governs local tools that never touch the network. |
| 12 | +- **SOCKS5 Proxy** -- intercepts every TCP and UDP connection from the agent's container. Supports HTTP, HTTPS, WebSocket, gRPC, SSH, IMAP, SMTP, DNS, and QUIC/HTTP3. Injects real credentials at the network level via MITM so the agent never sees them. |
17 | 13 |
|
18 | | -On first run with an empty database, `--config` seeds the DB from the TOML file. Subsequent runs use the SQLite store directly. |
| 14 | +The agent gets phantom tokens (random strings that look like real API keys). Sluice swaps them for real credentials in-flight. If the agent leaks a phantom token, it's useless outside the proxy. |
19 | 15 |
|
20 | | -Test with curl: |
| 16 | +## How It Works |
21 | 17 |
|
22 | | -```bash |
23 | | -curl -x socks5h://127.0.0.1:1080 https://api.anthropic.com/ |
24 | 18 | ``` |
| 19 | +Container (Docker / Apple Container / macOS VM): |
| 20 | + AI Agent (OpenClaw) -- uses phantom tokens, thinks they're real |
| 21 | + tun2proxy -- routes all traffic to SOCKS5 |
25 | 22 |
|
26 | | -## CLI Flags |
| 23 | +Host: |
| 24 | + Sluice SOCKS5 Proxy -- policy + MITM + credential injection |
| 25 | + Sluice MCP Gateway -- tool-level policy + argument inspection |
| 26 | + Telegram Bot -- human approval for "ask" verdicts |
| 27 | +``` |
27 | 28 |
|
28 | | -| Flag | Default | Description | |
29 | | -|------|---------|-------------| |
30 | | -| `--listen` | `127.0.0.1:1080` | SOCKS5 listen address | |
31 | | -| `--db` | `sluice.db` | Path to SQLite policy database | |
32 | | -| `--config` | (none) | TOML seed file (imported only when DB is empty) | |
33 | | -| `--audit` | `audit.jsonl` | Path to audit log file | |
34 | | -| `--telegram-token` | `$TELEGRAM_BOT_TOKEN` | Telegram bot token for approval flow | |
35 | | -| `--telegram-chat-id` | `$TELEGRAM_CHAT_ID` | Telegram chat ID for approvals | |
36 | | -| `--health-addr` | `127.0.0.1:3000` | Health check HTTP address | |
37 | | -| `--shutdown-timeout` | `10s` | Graceful shutdown timeout | |
38 | | -| `--runtime` | `auto` | Container runtime: `docker`, `apple`, `none`, `auto` | |
39 | | -| `--container-name` | `openclaw` | Agent container name (env: `SLUICE_AGENT_CONTAINER`) | |
40 | | -| `--docker-socket` | (auto-detect) | Docker socket path for container management | |
41 | | -| `--phantom-dir` | (none) | Shared volume path for phantom token files (enables hot-reload) | |
| 29 | +Every connection is evaluated against policy rules (allow / deny / ask). "Ask" verdicts send a Telegram notification with inline buttons. The agent blocks until the human responds. Credentials are managed via Telegram commands or CLI, stored encrypted with age, and hot-reloaded into the agent container without restarts. |
42 | 30 |
|
43 | | -## CLI Subcommands |
| 31 | +## Quick Start |
44 | 32 |
|
45 | | -### Policy management |
| 33 | +### Docker (Linux) |
46 | 34 |
|
47 | | -``` |
48 | | -sluice policy list [--verdict allow|deny|ask|redact] [--db sluice.db] |
49 | | -sluice policy add allow <destination> [--ports 443,80] [--name "reason"] |
50 | | -sluice policy add deny <destination> [--name "reason"] |
51 | | -sluice policy add ask <destination> [--ports 443] [--name "reason"] |
52 | | -sluice policy remove <id> |
53 | | -sluice policy import <path.toml> |
54 | | -sluice policy export |
55 | | -``` |
| 35 | +The recommended setup for Linux. Three containers share a network namespace: sluice (proxy), tun2proxy (routes all traffic through SOCKS5), and your AI agent. |
56 | 36 |
|
57 | | -### MCP upstream management |
| 37 | +```bash |
| 38 | +# 1. Clone and configure |
| 39 | +git clone https://github.com/nnemirovsky/sluice.git && cd sluice |
| 40 | +cp examples/config.toml config.toml # edit policy rules |
58 | 41 |
|
59 | | -``` |
60 | | -sluice mcp add <name> --command <cmd> [--args "arg1,arg2"] [--env "KEY=VAL,..."] [--timeout 120] |
61 | | -sluice mcp list |
62 | | -sluice mcp remove <name> |
63 | | -sluice mcp # start MCP gateway |
64 | | -``` |
| 42 | +# 2. Set up credentials |
| 43 | +export TELEGRAM_BOT_TOKEN="your-bot-token" |
| 44 | +export TELEGRAM_CHAT_ID="your-chat-id" |
65 | 45 |
|
66 | | -### Credential management |
| 46 | +# 3. Start |
| 47 | +docker compose up -d |
67 | 48 |
|
68 | | -``` |
69 | | -sluice cred add <name> [--destination host] [--ports 443] [--header Authorization] [--template "Bearer {value}"] |
70 | | -sluice cred list |
71 | | -sluice cred remove <name> |
| 49 | +# 4. Add API credentials (phantom tokens auto-generated, hot-reloaded to agent) |
| 50 | +docker exec sluice sluice cred add anthropic_api_key \ |
| 51 | + --destination api.anthropic.com --ports 443 \ |
| 52 | + --header x-api-key |
72 | 53 | ``` |
73 | 54 |
|
74 | | -When `--destination` is provided, `sluice cred add` also creates an allow rule and binding in the store. |
| 55 | +### Apple Container (macOS) |
| 56 | + |
| 57 | +Native macOS micro-VMs via Virtualization.framework. Lightweight isolation with sub-second boot. Runs Linux guests. |
| 58 | + |
| 59 | +```bash |
| 60 | +# 1. Build sluice |
| 61 | +go build -o sluice ./cmd/sluice/ |
75 | 62 |
|
76 | | -### Other subcommands |
| 63 | +# 2. Start with Apple Container runtime |
| 64 | +./sluice --runtime apple --container-name openclaw \ |
| 65 | + --phantom-dir /tmp/sluice-phantoms |
77 | 66 |
|
| 67 | +# 3. Network routing (requires root for pf rules) |
| 68 | +sudo ./scripts/apple-container-setup.sh |
78 | 69 | ``` |
79 | | -sluice cert generate # generate CA certificate for HTTPS MITM |
80 | | -sluice audit verify # verify audit log hash chain integrity |
| 70 | + |
| 71 | +### macOS VM (via tart) |
| 72 | + |
| 73 | +Full macOS guest VM with access to Apple frameworks (iMessage, EventKit, Keychain, Shortcuts). Use this when your agent needs to interact with Apple ecosystem services that are unavailable in Linux containers. |
| 74 | + |
| 75 | +```bash |
| 76 | +# 1. Install tart |
| 77 | +brew install cirruslabs/cli/tart |
| 78 | + |
| 79 | +# 2. Build sluice |
| 80 | +go build -o sluice ./cmd/sluice/ |
| 81 | + |
| 82 | +# 3. Start with macOS VM runtime |
| 83 | +./sluice --runtime macos \ |
| 84 | + --vm-image ghcr.io/cirruslabs/macos-sequoia-base:latest \ |
| 85 | + --container-name openclaw \ |
| 86 | + --phantom-dir /tmp/sluice-phantoms |
| 87 | + |
| 88 | +# 4. Host network routing (requires root for pf rules) |
| 89 | +sudo ./scripts/macos-vm-setup.sh |
81 | 90 | ``` |
82 | 91 |
|
83 | | -## Policy Store |
| 92 | +Requires macOS with Apple Silicon (M1+). The macOS EULA allows up to 2 additional macOS VMs per Apple-branded host. |
84 | 93 |
|
85 | | -All runtime policy state is stored in a SQLite database (default: `sluice.db`). TOML files are used only for initial seeding via `sluice policy import`. The CLI, Telegram commands, and approval buttons all write to the same database. Changes persist across restarts. |
| 94 | +## Policy |
86 | 95 |
|
87 | | -### TOML Seed File Format |
| 96 | +All policy is stored in SQLite and persists across restarts. Seed from TOML on first run, then manage via CLI or Telegram. |
88 | 97 |
|
89 | 98 | ```toml |
90 | 99 | [policy] |
91 | | -default = "deny" # "allow", "deny", or "ask" |
92 | | -timeout_sec = 120 # timeout for ask verdicts |
93 | | - |
94 | | -[vault] |
95 | | -provider = "age" |
96 | | - |
97 | | -# Network rules (destination field) |
| 100 | +default = "deny" |
98 | 101 |
|
| 102 | +# Network rules |
99 | 103 | [[allow]] |
100 | 104 | destination = "api.anthropic.com" |
101 | 105 | ports = [443] |
102 | 106 |
|
103 | | -[[allow]] |
104 | | -destination = "*.github.com" # glob: * matches within one DNS label |
105 | | -ports = [443, 80] |
106 | | - |
107 | | -[[deny]] |
108 | | -destination = "169.254.169.254" # block metadata endpoint |
109 | | - |
110 | 107 | [[ask]] |
111 | 108 | destination = "*.openai.com" |
112 | 109 | ports = [443] |
113 | 110 |
|
114 | | -# Tool rules (tool field) |
| 111 | +[[deny]] |
| 112 | +destination = "169.254.169.254" # block cloud metadata |
115 | 113 |
|
| 114 | +# MCP tool rules |
116 | 115 | [[allow]] |
117 | 116 | tool = "github__list_*" |
118 | | -name = "read-only github list" |
119 | 117 |
|
120 | 118 | [[deny]] |
121 | 119 | tool = "exec__*" |
122 | | -name = "block all exec" |
123 | | - |
124 | | -# Content inspection rules (pattern field) |
125 | 120 |
|
| 121 | +# Content inspection |
126 | 122 | [[deny]] |
127 | 123 | pattern = "(?i)(sk-[a-zA-Z0-9_-]{20,})" |
128 | | -name = "api key in tool arguments" |
| 124 | +name = "block API keys in tool arguments" |
129 | 125 |
|
130 | 126 | [[redact]] |
131 | 127 | pattern = "(?i)(sk-[a-zA-Z0-9_-]{20,})" |
132 | | -replacement = "[REDACTED_API_KEY]" |
133 | | -name = "api key in responses" |
| 128 | +replacement = "[REDACTED]" |
| 129 | +name = "redact API keys in responses" |
134 | 130 | ``` |
135 | 131 |
|
136 | | -Rules use a unified format. Each `[[allow]]`, `[[deny]]`, `[[ask]]`, or `[[redact]]` entry carries exactly one of: `destination` (network rule), `tool` (MCP tool rule), or `pattern` (content inspection rule). The section name determines the verdict. |
137 | | - |
138 | | -Glob patterns: `*` matches within a single DNS label (not across dots). `**` matches across dots (any depth of subdomains). `?` matches a single non-dot character. Matching is case-insensitive (RFC 4343). An empty ports list matches all ports. |
| 132 | +Glob patterns: `*` matches within a single DNS label. `**` matches across dots. Evaluation order: deny, allow, ask, default. |
139 | 133 |
|
140 | | -Evaluation order: deny rules first, then allow, then ask, then the default verdict. |
| 134 | +## Credential Providers |
141 | 135 |
|
142 | | -## Telegram Approval Bot |
| 136 | +Sluice supports multiple credential backends. Set `provider` in `[vault]` config: |
143 | 137 |
|
144 | | -When configured, connections matching `ask` policy rules trigger an approval request via Telegram. The bot sends an inline keyboard message to the configured chat with three options: Allow Once, Always Allow, and Deny. |
| 138 | +| Provider | Auth | Notes | |
| 139 | +|----------|------|-------| |
| 140 | +| `age` (default) | Auto-generated X25519 key | Local encrypted files, no dependencies | |
| 141 | +| `env` | Environment variables | Credential name maps to env var | |
| 142 | +| `hashicorp` | Token or AppRole | HashiCorp Vault KV v2 | |
| 143 | +| `1password` | Service Account token | Via official Go SDK | |
| 144 | +| `bitwarden` | Access token | Via `bws` CLI | |
| 145 | +| `keepass` | Password + optional key file | Local .kdbx files, auto-reloads on change | |
| 146 | +| `gopass` | CLI auth | Via `gopass` binary | |
145 | 147 |
|
146 | | -The connection blocks until the user responds or the timeout expires (controlled by `timeout_sec`, default 120s). On timeout, the connection is denied. |
| 148 | +Chain multiple providers with `providers = ["1password", "age"]`. First provider with the credential wins. |
147 | 149 |
|
148 | | -"Always Allow" writes a persistent allow rule to the SQLite store with `source="approval"`. The rule survives restarts. |
| 150 | +## Telegram Bot |
149 | 151 |
|
150 | | -Without Telegram configured, all `ask` verdicts are treated as `deny`. |
151 | | - |
152 | | -### Telegram Commands |
153 | | - |
154 | | -Commands are only accepted from the configured chat ID. |
| 152 | +Manage sluice from your phone. Approve connections, add credentials, update policy. |
155 | 153 |
|
156 | 154 | | Command | Description | |
157 | 155 | |---------|-------------| |
158 | 156 | | `/policy show` | List current rules | |
159 | | -| `/policy allow <dest>` | Add allow rule (persisted to SQLite) | |
160 | | -| `/policy deny <dest>` | Add deny rule (persisted to SQLite) | |
161 | | -| `/policy remove <id>` | Remove rule by ID | |
162 | | -| `/cred add <name> <value>` | Add credential | |
163 | | -| `/cred list` | List credential names | |
164 | | -| `/cred rotate <name> <value>` | Replace credential | |
165 | | -| `/cred remove <name>` | Remove credential | |
166 | | -| `/status` | Show proxy status | |
167 | | -| `/audit recent [N]` | Show last N audit entries (default 10) | |
168 | | -| `/help` | Show available commands | |
169 | | - |
170 | | -Policy changes via Telegram are persisted to the SQLite store and survive restarts. |
171 | | - |
172 | | -## Hot Reload |
173 | | - |
174 | | -Send SIGHUP to recompile the policy engine from the SQLite store without restarting the proxy. Existing connections are not affected. New connections use the updated policy. SIGHUP also updates the policy engine used by Telegram command handlers. |
175 | | - |
176 | | -Telegram bot credentials are read from environment variables (`TELEGRAM_BOT_TOKEN`, `TELEGRAM_CHAT_ID`) at startup and cannot be hot-reloaded. Changing Telegram credentials requires a full restart. |
177 | | - |
178 | | -```bash |
179 | | -kill -HUP $(pgrep sluice) |
180 | | -``` |
| 157 | +| `/policy allow <dest>` | Add allow rule | |
| 158 | +| `/policy deny <dest>` | Add deny rule | |
| 159 | +| `/cred add <name>` | Add credential (value sent as next message, auto-deleted) | |
| 160 | +| `/cred rotate <name>` | Replace credential, hot-reload agent | |
| 161 | +| `/status` | Proxy stats and pending approvals | |
| 162 | +| `/audit recent [N]` | Last N audit entries | |
181 | 163 |
|
182 | 164 | ## Audit Log |
183 | 165 |
|
184 | | -JSON Lines format written to the audit file path. Each line includes a `prev_hash` field with the blake3 hash of the previous line for tamper detection. Verify the chain with: |
| 166 | +Tamper-evident JSON Lines log with blake3 hash chaining. Every connection, tool call, approval, and denial is recorded. |
185 | 167 |
|
186 | 168 | ```bash |
187 | | -sluice audit verify |
| 169 | +sluice audit verify # check hash chain integrity |
188 | 170 | ``` |
189 | 171 |
|
190 | | -## Docker Compose |
191 | | - |
192 | | -Three-container architecture: sluice + tun2proxy + openclaw. All agent traffic is routed through sluice's SOCKS5 proxy via TUN device. Phantom tokens are delivered to the agent via a shared volume (`sluice-phantoms`). See `compose.yml` for details. |
193 | | - |
194 | | -## Apple Container |
195 | | - |
196 | | -Apple Container (macOS Virtualization.framework micro-VMs) is supported as an alternative to Docker. It provides native macOS isolation with access to Apple frameworks (EventKit, Messages, CallKit) that Linux containers cannot reach. |
197 | | - |
198 | | -```bash |
199 | | -./sluice --runtime apple --container-name openclaw |
200 | | -``` |
201 | | - |
202 | | -Traffic routing uses macOS pf rules to redirect VM bridge traffic through tun2proxy on the host to sluice's SOCKS5 proxy. APNS traffic (port 5223) is detected as a distinct protocol for policy rules. |
203 | | - |
204 | | -See `docs/apple-container-quickstart.md` for full setup instructions. |
| 172 | +## Protocol Support |
205 | 173 |
|
206 | | -## Standalone Mode |
| 174 | +| Protocol | Credential Injection | Content Inspection | |
| 175 | +|----------|---------------------|--------------------| |
| 176 | +| HTTP/HTTPS | MITM phantom swap | Full request/response | |
| 177 | +| gRPC | Header phantom swap | Metadata | |
| 178 | +| WebSocket | Handshake + text frames | Text frame content | |
| 179 | +| SSH | Jump host, key from vault | -- | |
| 180 | +| IMAP/SMTP | AUTH command proxy | -- | |
| 181 | +| DNS | -- | Domain-level policy | |
| 182 | +| QUIC/HTTP3 | HTTP/3 MITM | Full request/response | |
207 | 183 |
|
208 | | -Run sluice as a proxy without any container runtime: |
209 | | - |
210 | | -```bash |
211 | | -./sluice --runtime none --listen 127.0.0.1:1080 |
212 | | -``` |
213 | | - |
214 | | -Then configure your application to use the proxy: |
215 | | - |
216 | | -```bash |
217 | | -export ALL_PROXY=socks5://localhost:1080 |
218 | | -``` |
219 | | - |
220 | | -Credential injection (MITM proxy) and MCP gateway work normally. Only container lifecycle management is disabled. |
221 | | - |
222 | | -## Testing |
223 | | - |
224 | | -```bash |
225 | | -make test # unit tests |
226 | | -make test-coverage # unit tests with HTML coverage report |
227 | | -make test-e2e # all e2e tests |
228 | | -make test-e2e-docker # Linux e2e tests via Docker Compose (builds containers) |
229 | | -make test-e2e-linux # Linux e2e tests (go test with linux build tag) |
230 | | -make test-e2e-macos # macOS e2e tests (Apple Container) |
231 | | -``` |
232 | | - |
233 | | -E2e tests use build tags (`e2e`, `linux`, `darwin`) and live in `e2e/`. CI enforces a minimum 75% coverage threshold. |
| 184 | +## Requirements |
234 | 185 |
|
235 | | -See [CONTRIBUTING.md](CONTRIBUTING.md) for details on writing tests. |
| 186 | +| Runtime | Requirements | |
| 187 | +|---------|-------------| |
| 188 | +| Docker | Docker Engine | |
| 189 | +| Apple Container | macOS, `container` CLI | |
| 190 | +| macOS VM | macOS, Apple Silicon, `tart` CLI | |
| 191 | +| All | Telegram bot token (optional, for approval flow) | |
236 | 192 |
|
237 | | -## Requirements |
| 193 | +## License |
238 | 194 |
|
239 | | -- Go 1.22+ |
240 | | -- Telegram bot token (from @BotFather) for the approval flow (optional) |
241 | | -- Docker (optional, for container deployment) |
242 | | -- macOS with Apple Container runtime (optional, for Apple Container deployment) |
| 195 | +See [LICENSE](LICENSE). |
0 commit comments