Let AI agents operate your production machines - safely. Reads run; every write is blocked and queued for a human to approve before it touches anything. No SSH, no VPN, no open ports.
Teams increasingly want AI agents to do things on real infrastructure - restart a service, scale a deployment, tail logs, roll something back. But every way of granting that access is all-or-nothing: an SSH key, a VPN, or an open port hands over broad, standing, hard-to-audit control of the box.
That's fine for a careful human. It's a serious problem for a probabilistic agent: a single hallucinated kubectl delete pods --all -n payments or rm -rf on production is unrecoverable. So "AI on prod" tends to collapse into one of two bad states - a liability (full access, fingers crossed) or blocked entirely (agents kept away from anything that matters).
I wanted pointing an AI at real infrastructure to be a system you can operate with confidence, not a leap of faith. The core idea is a split: reads flow freely, writes stop for a human, and everything is audited.
The subtle - and most important - part is what a human approves. In Reach you approve a structured rule, not a command string. Approving the string kubectl delete pods -n payments is dangerous because it can be extended past the check (… | tee /etc/x, … && rm -rf). Approving the rule {verb: delete, resource: pods, namespace: payments} can't be - it authorises a shape of action, once, reusably. That distinction is the difference between an agent you tolerate and one you trust.
Reach is a control plane plus lightweight agents, driven by a CLI, an MCP server, or a web console at /ui.
┌─────────────────────────────────────────────────────────────────┐
│ Local machine │
│ │
│ ┌───────────┐ ┌─────────────┐ │
│ │ CLI │ │ MCP server │ │
│ │ (reach) │ │ (reach mcp) │ │
│ └─────┬─────┘ └──────┬──────┘ │
│ │ │ stdio (JSON-RPC) │
│ │ ┌──────┴──────┐ │
│ │ │ MCP client │ (Claude Code, Cursor, etc.) │
│ │ └─────────────┘ │
└─────────┼───────────────────────────────────────────────────────┘
│ HTTPS
▼
┌─────────────────────────────────────────────────────────────────┐
│ Backend │
│ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ FastAPI (Docker / Kubernetes) or Lambda │ │
│ └────────────────────────────────────────────────────────┘ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ PostgreSQL (default) or DynamoDB (AWS) │ │
│ └────────────────────────────────────────────────────────┘ │
└─────────────────────────────▲───────────────────────────────────┘
│ HTTPS (outbound from agent)
┌─────────────────────────────┴───────────────────────────────────┐
│ Remote machine / Kubernetes cluster │
│ │
│ ┌──────────────┐ │
│ │ reach-agent │ host: systemd/launchd · k8s: Helm Deployment│
│ └──────────────┘ │
└─────────────────────────────────────────────────────────────────┘
client-go). On a host it's a systemd/launchd service sandboxed with Landlock; in Kubernetes it's a Helm Deployment whose replicas share one identity (derived from the kube-system namespace UID) with a lease-elected leader, and the token lives in a managed Secret - nothing on the pod's disk.kubectl in-cluster - each enforcing policy the way that fits its substrate.deregister on a genuine machine shutdown (a service restart doesn't churn the record), backstop a heartbeat reaper - inheriting the fleet's mode, tags, and rules.Command flow (5 steps): submit (POST /jobs, annotated read/write) → the agent polls (POST /agent/sync) → executes under policy → posts the result (secrets redacted) → the CLI/MCP retrieves it. The agent has no channel back to the submitter - everything routes through the backend, and nothing is ever pushed to an agent.
Controlled execution is the whole product, so the security model is the design - two enforcement points, server-side then agent-side, that must both be bypassed:
approved (production: reads run, every write needs a pre-approved rule), readonly (the safe default for a new agent), and wild (opt-in, personal boxes; break-glass wild can be armed with an auto-reverting duration so it can't be left open).argv and run with execve (no shell); approvals are JSON rules {bin, args[]} (host) or {verb, resource, namespace, name} (k8s), matched positionally with * and a trailing .... A write that needs shell features simply can't be a rule - so it's unapprovable, which is exactly why an approved action can't be extended (… | tee, … && rm -rf) to smuggle something past the check.kubectl allowlist (arguments resolving to a local file are rejected, so a job can't read its own ServiceAccount token).HMAC-SHA256(pepper, token) hashes (passwords use scrypt). The agent has credential-only identity (it never sends an agent id), tokens are bound to a machine fingerprint and auto-rotate every 30 days..env, kubectl get secret); output is redacted for recognisable secrets at two layers (backend + MCP, so a secret never reaches the model), and each command runs under a timeout with capped output.Honest scope: Reach is controlled, audited execution - not a sandbox for arbitrary untrusted commands, and not a boundary against the machine's own root/owner (whoever controls the host can read the agent's token). It's built to point an AI at infrastructure you own.
Making agents outbound-only removes an entire class of exposure (no ports, no inbound auth surface) and makes deployment trivial behind firewalls - at the cost of a little polling latency, which is the right trade for infrastructure control.
The structured-rule model is the security thesis. It costs a bit more friction than "approve this string once," but it's the only version where an approval can't be quietly widened.
Rather than fork the backend, the same handlers run on FastAPI and on Lambda with a Postgres/DynamoDB storage split. That keeps behaviour identical across Local, Docker, Kubernetes, and serverless deployments - the logic can't drift between them.
An MCP server (wired up by reach agent-init) lets Claude Code / Cursor drive a machine through Reach directly, so the safety model sits transparently under the tools people already use - and is deliberately read-only for approvals so the AI can't sign off its own request.
The agent is a single Go binary on the standard library alone - no client-go - so it drops onto a host or into a cluster with a tiny footprint and no runtime to manage. The cost is re-implementing the slice of Kubernetes client logic it needs, kept honest against the backend by tests.
The agent never sends or stores an agent_id; it authenticates purely by a fingerprint-bound token hash. There's no id on the machine to steal or spoof, and a stolen token can't be replayed from a different host.
| Decision | Gained | Gave up |
|---|---|---|
| Outbound-poll agents | No open ports; NAT/firewall-friendly; smaller attack surface | Polling latency vs. a pushed connection |
| Approve a rule | Approvals can't be extended past the check; reusable | More up-front friction than approving a string |
| Two runtimes (FastAPI + Lambda) | Local, Docker, K8s and serverless from one codebase | Handlers must stay runtime- and storage-agnostic |
| Self-hosted only | Your machines and audit data never leave your control | You run and operate the backend yourself |
Default readonly | A new agent is safe the moment it enrolls | Real operations need a deliberate switch to approved |
approved mean the same thing on a raw host (Landlock, arbitrary binaries) and inside Kubernetes (kubectl bounded by RBAC) took very different mechanisms behind one consistent mental model.


wild / read-only / approved) and scoped permissions before the join token is issued.
approved mode are gated (note the rejected kubectl delete).

Zero to an AI agent running commands on a real machine, in three steps:
# 1. Start Reach - runs the backend, creates your tenant + first agent, installs the CLI $ curl -fsSL https://releases.reach.nabeem.com/local-setup.sh | bash # 2. Install the agent on the machine to control (the script prints a ready-to-paste command) # curl … | sudo bash for a host · helm install … for Kubernetes # 3. Wire your AI tool (Claude Code / Cursor) in over MCP $ reach agent-init # now the agent has controlled, audited access - try it: $ reach exec -- hostname
In approved mode, a write is gated until a human signs off the rule - then it runs, this time and next:
$ reach exec --agent prod -- kubectl delete pods --all -n payments Status: REJECTED - approval required; a request was sent to your operator. # operator approves the rule once - structured, not a string: # { verb: delete, resource: pods, namespace: payments, name: * } $ reach approvals approve appr_9f2c Approved. $ reach exec --agent prod -- kubectl delete pods --all -n payments Status: SUCCEEDED
On AWS, swap step 1 for lambda-setup.sh. Docker, Kubernetes, and production hardening are covered in the repo's SELF_HOSTING.md.
TOKEN_PEPPER; leak the database and the pepper together and tokens become forgeable (it also can't be rotated without reissuing everything).wild mode / in reads is out of scope; kernel exploits below Landlock are too.Directions I'm exploring (not commitments):
The repository is the source of truth - issues and ideas welcome.