Skip to content

Machine-isolated agents with egress usage monitoring - #93

Draft
zredlined wants to merge 6 commits into
mainfrom
alwatson/two-box-openshell
Draft

zredlined wants to merge 6 commits into
mainfrom
alwatson/two-box-openshell

Conversation

@zredlined

@zredlined zredlined commented Oct 15, 2026 •

Copy link
Copy Markdown
Collaborator

Run frontier agents on a machine you expect them to break: their controls live on another machine they can't reach, and every allowed request counts against a budget.

The egress usage monitoring builds on Hugging Face's proof of concept and RFC by Hugo Larcher; we're building this together with the Hugging Face team.

Agent containment architecture

The setup is one k3s cluster across two or more machines:

  • Control node: the k3s server, the OpenShell gateway, every sandbox's supervisor, and an egress watchdog.
  • Sandbox node: runs sandbox workloads and nothing else. More sandbox nodes join the same way.

It uses released OpenShell 0.1.4 with no changes to OpenShell.

Try it

You need two Ubuntu 24.04 machines or VMs that can reach each other. From projects/use-case-examples/agent-containment:

# On the control node
CONTROL_IP=<control-node-ip> deploy/control-node.sh      # prints the sandbox-node command
# On each sandbox node
NODE_IP=<sandbox-node-ip> deploy/sandbox-node.sh <token> <control-node-ip>
# On the control node
deploy/watchdog.sh
deploy/verify.sh

deploy/rehearse-k3d.sh runs the same thing on a single Docker host, without sudo.

Exfil budgets live in the sandbox policy

The watchdog counts the bytes each sandbox sends to each allowed host. When a sandbox goes over its budget, the watchdog denies the request and quarantines that sandbox. The budget sits in the sandbox policy, next to the rules it constrains:

network_middlewares:
  egress-watchdog:
    middleware: egress-watchdog
    on_error: fail_closed
    config:
      budget:
        bytes_per_minute: 1048576   # on_exceed: deny (default) or finding
    endpoints:
      include: ["httpbin.org"]

Feedback wanted on this shape:

  • names and units;
  • per-host vs per-sandbox budgets;
  • what else belongs here, such as requests per minute or writes per minute.

Tested

On a DGX Station (control node) and a DGX Spark (sandbox node), both arm64 and on the same local network:

  • From zero: control-node.sh took 42 s and sandbox-node.sh 13 s. verify.sh passes all 9 checks: placement, allowed and refused requests, and watchdog accounting.
  • Exfil: 64 KiB POSTs to an allowed host get sixteen 200s (1 MiB), then 403s, including a follow-up GET.
  • Added latency: about 1 ms at p50 and 3 ms at p90, over 60 requests against httpbin.org.
  • Boundary: from a root shell on the sandbox node, all of these are refused:
    • the node credential reading the gateway's keys;
    • the join token fetching the cluster CA (the sandbox node gets a join-only token);
    • the gateway API without a client certificate;
    • the watchdog without a valid token.
  • Unit tests: 22 (uv run pytest).

What's next

  • The control node can cut the sandbox node off. The sandbox node is wired only to the control node, so the control node can disconnect its network outright.
  • Escape tripwire on the control node. If the sandbox node starts a connection it never should, or its node credential probes for secrets, the control node cordons it and stops its sandboxes.
  • Stage-two quarantine. Swap in a deny-all policy and detach providers, keeping the sandbox for forensics.
  • More detectors. A fleet detector (many sandboxes writing to one host), plus requests/min and writes/min budgets.

Runs agent sandboxes on a sandbox node and everything that governs them
(gateway, supervisors, egress watchdog) on a control node, as one k3s
cluster. Uses released OpenShell 0.1.4 with no changes.

- deploy/: control-node.sh, sandbox-node.sh, watchdog.sh, verify.sh,
  and a single-host k3d rehearsal
- src/twobox_watchdog/: supervisor middleware with TLS and gateway JWT
  auth, per-sandbox byte accounting, budgets set in sandbox policy, and
  quarantine on exceed
@github-actions

github-actions Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Documentation preview

View the deployed preview

Built from 71445fa.

@zredlined zredlined changed the title Add two-box OpenShell use-case example Machine-isolated agents with egress usage monitoring Oct 15, 2026
@zredlined zredlined self-assigned this Oct 15, 2026
Add SPDX headers to the two-box example's scripts, configs, and
package files. Clarify protobuf 6.33.6, whose PyPI metadata reports a
non-SPDX "3-Clause BSD License", as BSD-3-Clause.
Name the example for its use case: machine-isolated agents with egress
usage monitoring. Two-box remains the name of the deployment pattern.

- projects/use-case-examples/two-box-openshell -> agent-containment
- twobox_watchdog -> egress_watchdog (middleware egress-watchdog)
- node label openshell.io/box -> openshell.io/role
- credit Hugging Face's egress usage monitoring proof of concept
Replace the host-process watchdog with a pod on the control node: a
pinned stock uv Python image runs the checkout, so there is no image to
build. A network policy admits only the gateway and sandbox supervisors,
so sandbox nodes can no longer reach the watchdog. The control node no
longer needs uv installed.
Every node can reach in-cluster addresses and NodePorts, so a sandbox
node's host could open connections to the gateway, which relied on its
client certificate alone. A network policy now admits only sandbox
supervisors, gateway replicas, and the control node itself.
- escape '@' in the checkout path for k3d's volume flag
- download the helm and OpenShell CLI builds for the host OS, and verify
  checksums with shasum on Linux and macOS
- read the checkout owner with GNU or BSD stat
- let the operator CLI through the gateway policy with OPERATOR_CIDR, which
  the rehearsal sets to Docker's network gateway
- leave the default kubeconfig untouched

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant