Know what breaks before you touch a Linux box. Built for the AI agents doing the touching.
twin keeps a read-only, evidence-backed map of a Linux host: services, processes, listening
ports, live connections, containers, config files, packages, Kubernetes objects, and what
depends on what. One call tells you what is going on, or what a command would break, before you
run it.
$ twin preflight "sudo systemctl restart docker"
twin preflight
══════════════
fail STOP restart docker — 1 dependent(s) interrupted: shop-web-1
— command sudo systemctl restart docker
STOP · restart docker
├── target service:docker.service
├── risk high · evidence weak (15/100)
├── reason 1 running container(s) stop when the Docker daemon restarts
├── affected shop-web-1 — shop-web-1 stops while the Docker daemon restarts (live-restore disabled)
└── advice validate configuration first: `dockerd --validate`A 20-second recording of real output on a synthetic host is in docs/demo.cast
(asciinema play docs/demo.cast).
Nothing was restarted. twin cannot change the host. That guarantee is enforced in code and by the
test suite, which is why it is safe to hand to an agent.
Agents (Claude Code, Codex, Cursor, Gemini CLI, Copilot) are now routinely asked to "fix nginx",
"free some disk" or "upgrade openssl" on real machines. To do that they run 10–20 shell commands
(systemctl status, ss -tlnp, lsof, docker ps, cat on unit files) to rebuild a picture
of the host, then run mutating commands without knowing who depends on what.
twin collapses the investigation into one call and adds the missing step: a blast-radius
check before every mutating command.
| Without twin | With twin |
|---|---|
systemctl status + ss -tlnp + lsof -i + cat /etc/... + docker ps + journalctl |
twin brief nginx |
| "restarting docker should be fine" | twin preflight "systemctl restart docker" → STOP, names every container that goes down |
| "what changed since it broke?" across logs and package history | twin what-changed --since 1h --target api |
Hope the agent doesn't rm -rf a data dir |
hook auto-checks every agent shell command |
# Static binary for x86_64/aarch64 Linux, checksum-verified, no sudo, into ~/.local/bin
curl -fsSL https://github.com/KM-Alee/twin-ops/releases/latest/download/install.sh | sh
twin setup claude-code # create twin's store, scan this host, print agent config
twin brief # what runs here, what's exposed, what's fragile
twin brief postgresql # everything about one service/container/port/file
twin preflight "apt remove openssl"Releases also ship .deb and .rpm packages, and packaging/aur/PKGBUILD builds twin-bin.
From source: cargo build --release. Commands create twin's own store on first use; an
existing store is only migrated by twin init.
For full process and socket coverage, run as root or give the binary read capabilities:
sudo setcap cap_dac_read_search,cap_sys_ptrace+ep "$(command -v twin)". Without them,
twin still works and reports hidden processes as unknown, never as absent. twin setup
tells you how many are hidden.
On a remote machine, any MCP client can run ssh host twin mcp as its server command.
twin setup <agent> prepares twin and prints ready-to-paste config; twin agent-config <agent> prints only the config. Neither writes your agent's files, so you review and merge the
snippets yourself.
twin agent-config claude-code # MCP server + PreToolUse hook (shell and file tools) + SKILL.md
twin agent-config codex # MCP server + PreToolUse hook (shell and apply_patch) + AGENTS.md
twin agent-config cursor # MCP server + beforeShellExecution hook
twin agent-config gemini # MCP server + BeforeTool hook
twin agent-config vscode # MCP server for Copilot agent mode
twin agent-config generic # AGENTS.md instructions for any shell-capable agentThere are three integration layers. Use any or all of them:
-
MCP server (
twin mcp). Works with every MCP client and provides seven read-only tools:Tool Use it to twin_overvieworient on a machine: failed units, network exposure, hub services, containers, recent changes twin_briefget everything about one target in a single call twin_preflightpredict a command's blast radius (go / caution / stop) twin_changessee restarts, config edits, listener and package changes in a window twin_whysee the evidence chain behind a dependency twin_findlook up exact ids by name twin_checkrun twin.yamlhealth contractsEvery tool is annotated
readOnlyHint: true. Answers are bounded, typically 1–3 KB. -
Pre-command hook (
twin preflight --hook <agent>). The agent's own hook system sends each shell command and each file edit made with its own tools (Claude Code Write/Edit/MultiEdit, Codexapply_patch, Geminiwrite_file/replace) to twin before it runs. Cursor has no pre-edit hook, so its file edits are not checked in advance.- go: silent.
- caution: the reasons are added to the agent's context.
- stop: the human is asked to confirm, or the command is blocked where the agent can't
ask. Pass
--on-stop deny|ask|warnto choose.
The hook fails open: if twin is unavailable, your agent keeps working.
-
Skill / AGENTS.md. Plain instructions that teach an agent when to reach for twin, for agents without MCP or hooks.
| Area | Commands |
|---|---|
| Services | systemctl, service, nginx -s, apachectl |
| Containers | docker/podman/nerdctl, docker compose/docker-compose (resolved to the project's containers) |
| Files | rm, unlink, shred, sed -i, perl -i, tee, truncate, mv, cp, chmod, chown, >/>> |
| Packages | apt, apt-get, dnf, yum, zypper, apk, pacman, dpkg, rpm |
| Processes | kill (including -0, -STOP, -HUP), pkill, killall |
| Storage | umount, mount -o remount,ro, mkfs*, wipefs, dd of=/dev/…, parted, sgdisk |
| Network | ip link/addr/route, ifdown, nmcli, ufw, iptables, nft, firewall-cmd |
| System | sysctl -w, /proc/sys writes, swapoff, crontab, userdel, usermod -L, reboot, shutdown |
| Kubernetes | kubectl/oc rollout, delete, scale, apply, drain |
| Scripts | ./deploy.sh, bash x.sh, source x.sh, make TARGET: every line is evaluated |
It sees through sudo/doas, env assignments, nohup/timeout/env, &&/||/;/pipes,
nested bash -c '…' and heredocs. In scripts, only lines that are operations are shown; lines
that depend on shell variables are flagged as not assessable rather than guessed. Direct file
changes can be checked with twin preflight --file PATH --op edit|delete|create.
Examples of what it catches:
- Killing a pid is resolved to its owning service, with that service's dependents.
- Restarting Docker lists every running container that stops (it reads
live-restore). rm -rf /var/lib/dockeris a stop: a running service reads data under that path.- Editing
/etc/nginx/nginx.confreports that nginx reads the file and suggestsnginx -t. - Stopping sshd or NetworkManager is a stop: you can lock yourself out.
ufw deny 5432finds the listener and its connected clients.ip link set eth0 downis a stop when that interface carries the default route.umount /srvormkfson its device names every service using paths on that mount.docker compose downfinds the project's containers from their Compose labels.pacman -Syu/apt upgradeis a caution: twin explains it can't see the package manager's plan, and suggests preflighting specific packages.
Exit codes: 0 go, 3 caution, 4 stop, 1 error. Use it in scripts too:
twin preflight "$cmd" && eval "$cmd".
- Read-only forever. No restarts, kills, writes to the host, firewall changes, installs, or
container/Kubernetes mutations.
twin-safetyscans every production source file for execution primitives (Command::new,kill, Kubernetespatch/delete). Only the preflight parser may name commands such assystemctl restart, and only as text to recognize. - Evidence first. Every relationship cites its observations and an evidence strength; observed facts are kept separate from inferences.
- Risk ≠ confidence. "How bad" and "how sure" are reported separately.
- Unknown is not absent. Permission gaps, unmapped sockets and unavailable collectors are reported as coverage gaps and lower confidence. They never turn into a green result.
- Secrets never stored. Process argument values, environment variables, Kubernetes secret data and log bodies are never persisted.
| Command | Purpose |
|---|---|
twin brief [target] |
one-call host overview or target briefing |
twin preflight "<cmd>" |
predict a command's blast radius |
twin mcp |
MCP server for agents |
twin setup <agent> |
first run: store, scan, visibility check, agent snippets |
twin agent-config <agent> |
print agent setup snippets |
twin scan / twin watch |
collect host state once, or continuously (optionally with eBPF) |
twin graph [target] |
explore the dependency graph (also `--format dot |
twin impact <target> |
dependents of a service, port or package |
| `twin emulate restart | delete |
twin what-changed --since 1h [--target X] |
incident timeline |
twin why <target> |
evidence chains |
twin snapshot / twin diff |
baselines and structural diffs |
twin test run twin.yaml |
health contracts; see operational workflows |
| `twin k8s scan | graph |
twin doctor |
permissions, collectors, eBPF/Docker/Kubernetes availability |
Add --json to any command for machine-readable output. Lists are capped at 100 items with a
<field>_omitted count beside them; add --full to get everything.
read-only collectors (/proc, systemd + D-Bus, sockets, Docker/containerd, K8s, packages, eBPF)
↓ observations (facts, with source + timestamp)
temporal dependency graph (SQLite, retained history)
↓
overlay emulation (hypothetical; never persisted, never executed)
↓
brief · preflight · impact · why · what-changed · contracts
State lives under XDG paths (~/.config/twin/config.toml, ~/.local/share/twin/twin.db).
cargo build && cargo test --workspace && cargo clippy --workspace --all-targets -- -D warnings && cargo fmt --checkTests use fixtures (fake /proc, systemd, Docker, Kubernetes, eBPF streams); no root, Docker
or cluster required. See AGENTS.md for conventions and docs/ for design.
MIT