vps-forge: Agents With Root on a VPS Get Auto-Rollback, Self-Bans Drop From 7 in 60 Runs to 0
A developer has released vps-forge, an open-source Agent Skill for Claude Code, Codex, Cursor and other agents that have been given root on a VPS. The problem is a familiar one: in the benchmark, Claude Opus 5.5 without hints banned its own IP via fail2ban in 7 of 60 runs, cutting off the owner's laptop in the process. The core of the skill is a rollback timer, vps-forge-deadman. Before touching SSH, the firewall, sudo or PAM, the agent arms a 5-minute systemd timer. If a fresh login isn't confirmed, the configs revert on their own. Across 120 runs, the score rose from 79–92 to 99–100 out of 100. • A run costs $1.87 versus $0.62 without the skill • Nobody lost access, even without the skill • MIT-licensed, no telemetry
AI-processed from Habr AI; edited by Hamidun News
An agent with root on a fresh VPS installs fail2ban, checks key-based login, tries a password, and on the fifth failed attempt bans the IP the checks are coming from. That is your IP. After that, every command times out, and you start hunting for the web console in your host's control panel. In the benchmark by the author of vps-forge, Claude Opus 5.5 without hints banned itself in 7 of 60 runs. The model is smart. Nobody simply told it to add its own address to ignoreip before the first fail2ban start.
vps-forge is an open-source Agent Skill: a set of instructions and bash scripts that Claude Code, Codex, Cursor and other agents load when the task involves a server. Install it with `npx skills add mountain-beaver/vps-forge -g`. There is a plugin for Claude Code and a zip archive for Claude.ai.
Why "be careful" doesn't work
A broken sshd_config looks like a working one. `sshd -t` stays silent, the reload succeeds, the current session stays alive, and new logins are already blocked. For example, `AllowUsers deploy` when you log in as admin, or an AllowGroups list you forgot to add yourself to.
Agents have a trap of their own. They keep a single connection open through ControlMaster, so a check like `ssh host true` goes over the old channel. It never touches the new sshd or the new firewall rules: the check comes back green, yet no one can log in. The instruction "verify login from a new connection" doesn't help once the agent has already lost access. You need a mechanism that fires without the agent.
A dead man's pedal for sshd
Before every change to SSH, the firewall, sudo or PAM, the agent runs `vps-forge-deadman arm`. The script snapshots the configs and starts a 5-minute systemd timer. The agent changes the config, logs in again via `ssh -o ControlPath=none`, and if the login works, runs disarm. If it doesn't, there is nothing to do: after 5 minutes the timer restores everything as it was.
On paper this is 20 lines of bash; in practice it grew to several hundred. Almost every line exists because something broke:
- Reboot. A timer created by systemd-run lives in memory and vanishes along with the server. Now arm also enables a unit that, at boot, sees the "armed but not disarmed" state and rolls back by itself.
- nftables. Rollback stopped a service that had been off before arm, and its ExecStop runs `nft flush ruleset`. Along with the needed rule, ufw, fail2ban bans and Docker chains disappeared. Now only the tables from the config being rolled back are removed.
- Russian ufw locale. The script looked for the string `Status: active`, but ufw prints "Состояние: активен". The firewall was treated as disabled and got turned off during rollback. `LC_ALL=C` fixes it.
- Ban after rollback. The configs came back, but the IP stayed banned. Now arm remembers the SSH client's address, and rollback unbans it. Under sudo the SSH_CLIENT variable is absent, so the address is taken from the parent process's environment.
All of this runs in CI: a container with a real systemd, a test that breaks sshd, the firewall and fail2ban, reboots the container while armed, and checks that recovery works. A firewall in the provider's control panel cannot be rolled back, because it lives outside the server. There, the only safeguard is ordering: add the new rule first, verify login, then remove the old one.
Cloud image pitfalls and the benchmark
The sneakiest one is cloud-init. Many VPS images ship a `50-cloud-init.conf` with `PasswordAuthentication yes`. sshd takes the first value it reads, and files are read alphabetically, so the popular advice to use `99-hardening.conf` does nothing. The skill's file is named `00-vps-forge.conf`, and the result is verified with `sshd -T`. There's more: Debian 12 has no auth.log, Docker publishes ports around ufw, the Oracle Cloud image has its own REJECT rule in iptables, and Coolify connects to its own host over SSH as root, so `PermitRootLogin no` breaks deployment.
The test bench uses real QEMU VMs (Debian 12 and 13, Ubuntu 22.04, 24.04, 26.04) and four scenarios, each run three times without the skill and three times with it, for 120 Opus 5.5 runs in total. A judge checks the server from the outside before and after a reboot. Without the skill the score is 79–92 out of 100; with it, 99–100.
- ssh-audit with no failures: 4/60 without the skill, 60/60 with it
- SSH login notification: 16/30 and 30/30
- Firewall enabled: 46/60 and 60/60
- Password brute-forcing gets banned: 50/60 and 60/60
- Root cannot log in: 30/60 and 57/60
- Agent banned itself: 7/60 and 0/60
- Lost access: 0/60 and 0/60
The author is upfront about the weak spots. An untouched server scores 25–42. About 70% of the gap comes from three checks that match the skill's own settings: a clean ssh-audit, the root ban and the notification. One person wrote both the judge and the skill, and the independent Lynis barely moves: 69 and 71, 72 and 72, 64 and 72, 73 and 72. Three of the four scenarios were used while refining the skill. Only one model was tested, plus four manual GLM-5.3 runs. The benchmark did not prove the rollback works: nobody lost access, even without the skill.
Cost and how to use it
The skill is slower and roughly three times more expensive: $1.87 versus $0.62 per run. What real benefit remains is fail2ban actually banning on a running server (6 of 15 without the skill, 15 of 15 with it), an enabled firewall, and zero self-bans.
In practice: before the first SSH change, check that the agent ran `vps-forge-deadman arm`. If it didn't, the skill wasn't loaded and there is no safety net. The script also works without an agent, so you can test it by hand on a throwaway server: arm it for a minute, add `AllowUsers deploy`, confirm that login is blocked, and wait for the ROLLED BACK status. You still need a provider snapshot and console access. RHEL is not supported yet, and the skill does not touch Kubernetes nodes. The code is MIT-licensed with no telemetry, and the test bench lives in the bench/ directory of the mountain-beaver/vps-forge repository.
- Oct 28, 2026Wired Tested 6 AI Agents in a Day: Dots Leads, Gemini Spark Fails at Lunch
- Oct 28, 2026Anthropic files for IPO: $16.3 billion in revenue in six months, ticker ANT
- Oct 28, 2026Anthropic reveals how it builds Claude with Claude: the brain separate from the hands
- Oct 28, 2026Anthropic Turns Claude Code Into a Platform: Claude Mods Flip the Agent Harness
Key Questions Answered
Why "be careful" doesn't work?
A broken sshd_config looks like a working one. `sshd -t` stays silent, the reload succeeds, the current session stays alive, and new logins are already blocked. For example, `AllowUsers deploy` when you log in as admin, or an AllowGroups list you forgot to add yourself to. Agents have a trap of their own. They keep a single connection open through ControlMaster, so a check like `ssh host true` goes over the old channel. It never touches the new sshd or the new firewall rules: the check comes back green, yet no one can log in. The instruction "verify login from a new connect
What are the key details on A dead man's pedal for sshd?
Before every change to SSH, the firewall, sudo or PAM, the agent runs `vps-forge-deadman arm`. The script snapshots the configs and starts a 5-minute systemd timer. The agent changes the config, logs in again via `ssh -o ControlPath=none`, and if the login works, runs disarm. If it doesn't, there is nothing to do: after 5 minutes the timer restores everything as it was. On paper this is 20 lines of bash; in practice it grew to several hundred. Almost every line exists because something broke: Reboot. A timer created by systemd-run lives in memory and vanishes along with the
What are the key details on Cloud image pitfalls and the benchmark?
The sneakiest one is cloud-init. Many VPS images ship a `50-cloud-init.conf` with `PasswordAuthentication yes`. sshd takes the first value it reads, and files are read alphabetically, so the popular advice to use `99-hardening.conf` does nothing. The skill's file is named `00-vps-forge.conf`, and the result is verified with `sshd -T`. There's more: Debian 12 has no auth.log, Docker publishes ports around ufw, the Oracle Cloud image has its own REJECT rule in iptables, and Coolify connects to its own host over SSH as root, so `PermitRootLogin no` breaks deployment. The test
Build & Ship Production AI Agents Without Boilerplate Code
Start your free 72-hour trial at Hamidun Academy (academy.hamidun.com) — interactive lessons on Claude Code & Cursor, live Tech Radar, and production agent templates.
The AI world, distilled — once a week
Seven stories that actually mattered, hand-picked. No noise, no reposts, no press releases.
Done! Check your inbox for a confirmation.