With M2 working, Eran asked what we had overbuilt, without dropping a feature. We measured first: 2,592 lines of code in src, not counting blank lines, comments, docstrings or tests. The fixes were plain. The conn command line had a two-to-five line wrapper per command; it is now one table of one-line handlers. Long word lists, such as the commands that can run anything and the flags the read-only list accepts, were one word per line and are now one string each. The target file's list of allowed keys now comes from the Target fields, so the two can't drift. Small pieces of copied logic got one home: picking a live target, running a short script on either OS, asking yes or no, turning a command into the words policy checks. Dead code went: a function only tests called, a transport method whose answer was always rc == 255, and two fields nothing read. That left 2,407 lines. Then two clients joined conn mcp install. opencode reads MCP servers and permission rules from one JSON file. pi supports MCP servers now, but it has no permission rules, so installing there adds the server and says the agent can still run ssh itself (D45). The total after both is 2,435. Removing whole features is a separate conversation, and the candidates are listed in the PR.
The story so far
Seven short chapters. The dated entries are in the log.
The harness was in the wrong place
The day before, Eran had shipped a side project: a small pi-style agent living inside a test CLI on a lab VM. It worked. Getting there took a wheel build, a sync, a module load, a credentials wizard and a reverse SSH tunnel, because the lab firewall wouldn't let the VM reach the LLM.
Looking back, the redundancy was obvious. Every developer already has a capable agent on their own machine. Why rebuild one on every VM?
Instead of having the CLI on the local remote machine, developers usually prefer to work with their own setup and dev machine. … The interface is 100% agent to agent, so it needs to work in a good, efficient way.Eran, the first message
Five voices, told to argue
Eran asked for the plan to go to sub-agents and for them to fight about it. Four got the same brief with different jobs. A product lead owned the first five minutes. A security skeptic owned the threat model. A protocol architect owned every token. A contrarian argued, in the spirit of pi, that none of this needed MCP at all. A fifth agent went off to survey open source.
Everyone gave something up
Round two was rebuttals, and it was where the design was decided. Each voice read the other three and changed its mind in public. Security dropped its encrypted vault file after the contrarian argued that a homemade vault is how projects end up with CVEs. The contrarian dropped "MCP later" once product pointed out that approvals and long jobs need a long-lived process anyway. Product gave up its fifth tool and its favourite word, "rig". Protocol dropped server-side shell sessions as hidden state.
What was left is small: a CLI and an MCP server on one core, four agent tools, secrets as references, and OpenSSH under a config Conning generates itself.
Learning from open source
Open source got us here, and the best ideas in Conning come from people who solved these problems first. We still want to build our own, because Conning should be one small, lightweight MCP server we can read end to end. That means few dependencies, nothing hidden, and easy to trust with your keys.Eran
The survey found nothing that combines a vault that keeps secrets out of the model, a token-lean exec API, and Windows plus Linux targets. It did find good parts. From 1Password and HashiCorp Boundary we took "access without exposure", from Teleport deny-first policy, from OpenHands the timeout that hands control back instead of killing, and from Desktop Commander and E2B job handles with tail reads. One SSH MCP server measured its own 37 tools at about 43.5k tokens, which became our warning.
Locking it in, one question at a time
Eran asked for a walkthrough instead of a document: a summary of each section, then the questions that close it. The first answers pulled the scope in. Conning is a tool for one developer on their own machine, so the team-shareable target file went away. Agents with only bash get the CLI as well as MCP. And the agent should know its own helpers well enough to enrol a new target.
That last answer pushed on a rule all four voices had agreed on: the agent can never add a target. The compromise is that the agent proposes the target, it waits as pending, and Eran confirms it with one keystroke. Eran also cut a round trip: cache the CLI help in the README, so the agent doesn't have to call --help every session.
The sharpest moment was about approvals. Pressing "always allow" on systemctl status must not also allow systemctl stop. Eran's answer was to remember two words, not one, the same lesson as a read-only mode in that side project, which leaked through the test CLI's subcommands.
Remember 2 commands prefix. You see why?Eran
First contact
M1 is where Conning started touching machines. It still shells out to plain OpenSSH, but never through your ~/.ssh/config. Each target gets a generated config: the host key pinned under its own alias, strict checking, every kind of forwarding off, and one reused connection so a burst of commands costs one handshake. A password never appears on a command line. ssh asks a tiny helper for it, and the helper reads it from the ssh process's environment.
The hard part was long jobs. A test run can outlive an SSH connection, so a command doesn't run inside the session. It runs detached on the target, in its own process group, writing to a file under ~/.conning/jobs. The agent's read asks for bytes from an offset, so it only ever sees new lines. A signal reaches the whole process tree. Old images without setsid or timeout still work, with less protection. The first version had a bug: TERM killed the wrapper before it could record the exit code. The fix keeps the wrapper alive and signals only the command's group.
Eran's rule for this milestone was blunt: mock as much as possible and keep coverage above 95%. The trick that made it honest is a fake transport that runs Conning's real remote scripts with the local sh against a throwaway home directory. The job logic gets tested for real without a server. Getting to 100% found four bugs a happy-path demo would have missed. A failed fan-out start was listed as a job handle. An agent could save a password target with no secret, which then broke the whole target file. Typing "e" on a command that has no rule to remember approved it anyway. A single giant line of output, such as minified JSON, crashed the output cap.
What's next
M2 brings the approval to where you already are: a prompt in the agent client, a live conn watch view, and a desktop notification. It also adds the audit log, conn doctor, conn mcp install with rules that stop the agent going around Conning with raw ssh, and detached jobs on Windows. The goal hasn't moved: your own agent, on your own machine, working on a lab VM as easily as on localhost, without ever holding its keys.