No new features this stretch, and that's the point. Having just torn out libp2p, I turned the same scrutiny on everything else: a deliberate security-and-correctness audit of the whole thing, carried out the way you'd attack it if you wanted it to misbehave — before anyone is depending on it. It found a lot. That's the good outcome: better me than you, and better now than after a release.
An honest security pass
I went looking for the ways MeshHold could be made to do something it shouldn't, and found real ones — the kind you very much want to catch before 1.0, not after. A representative handful, each now fixed:
- An agent's access key reached too far. The key you hand a friend so they can use your AI agent could, it turned out, also be used to read another agent instance's session, widen that agent's access to your vaults, add "/" as a workspace, or run a command on the host. Now a key opens exactly one instance's own sessions and nothing that administers it — compared in constant time — and the processes an agent spawns talk back to the daemon with a narrow, expiring token scoped to one job, not one all-powerful bearer sitting in every CLI's environment.
- Chat trusted the sender too much. A node that didn't even hold a room's key could still forge a message as another member, or replay someone's real message with a one-millisecond lifetime to delete it for everyone, or with no expiry to keep a "disappearing" message forever. Now every message is sealed with its own random nonce and signed by its sender, and catch-up accepts a message only on that signature.
- "I hold this vault" was taken on trust. A node claiming to hold a vault was simply believed, so a member with no key could be listed as a holder and handed the vault's data and jobs. That claim is now a cryptographic proof, made out from one node to a specific asker and good nowhere else.
- A tunnel exit saw too much of its own host. A tunnel key with no rules was meant to reach the internet; it could also reach the exit machine's own loopback, its link-local addresses (including the cloud-metadata address) and the LAN beside it. Now, without an explicit rule, it reaches the internet and nothing else.
- A replication delete could fire on hearsay. A "you can drop your spare copy" decision counted presence rows — a holder's own day-old word — so a stale or lying peer could let a file slip below its redundancy. It now deletes a copy only on proof that the other keepers really hold it.
There were more; these are the shape of them. Crucially, none of this was ever exposed in the wild — there is no public release yet — which is exactly why now was the moment to go hunting. Everything the pass found and didn't fix in this round is written down in a backlog, with the reasoning, rather than quietly forgotten.
Make the docs tell the truth
A quieter but genuinely important pass: the words had drifted from the code. After the libp2p removal especially, the README, the architecture document, a stack of design docs, the config linter and parts of the site were describing a MeshHold that no longer exists — still recommending libp2p and GossipSub, still marking features "not built" that shipped months ago, still showing a config example whose keys do nothing.
So every one of them got checked against reality. Status lines now match what actually shipped; the architecture doc banners which sections are historical; the config linter names every dead key and every weak vault key; the diagnostics bundle records the exact commit a build came from instead of a hand-typed version; and the whole repo moved to one line-ending convention with a test that keeps formatting honest. A tool that lies to you in its own documentation is a bug like any other.
And calls, because of course
One field call surfaced a scheduling bug worth naming. The traffic scheduler was using a strict global priority, so a single peer pushing back could freeze a LAN call for half a second at a stretch and hold a file transfer past the point the player gave up. It now gives every connection and every class a weighted share instead, so one slow peer can't hold the whole node hostage. Around that, the usual round: proper AIMD rate control, modelled on WebRTC's; a call that started out relayed moving onto a direct path the instant one opens; and an incoming call that finally shows up over your phone's lock screen instead of behind it.
That's the least glamorous and most reassuring kind of pre-release work there is: assume you got things wrong, go looking, and be glad when you're right. 0.8 is close — and closer to trustworthy for this.
Since the last post
- Agent isolation: an instance key now opens only its own sessions (constant-time), can't administer the agent or escalate a session's mode/kind, and an agent's child processes use a per-job, expiring, scoped token instead of one shared full bearer
- Chat authenticity: messages are sealed with a random nonce and signed by their sender; a node without the room key can no longer forge, re-time, re-parent or replay another member's message, and catch-up trusts only the signature
- Vault access proofs: holding a vault is a proof made out to a specific asker, not a peer's word, so a keyless node can't be listed as a holder or handed the data/jobs; jobs and searches that would read a vault you can't prove are refused at submit, leaking nothing
- Scoping fixes: an unscoped tunnel key reaches the internet only (not the exit's loopback/metadata/LAN); SOCKS/forwards bound past loopback need credentials; replication deletes only on proof the other copies are held; a future-dated vault policy can't pin everyone's replication factor
- Honesty pass: README, architecture doc, design-doc status lines, the config linter and the site brought in line with the post-libp2p reality; diagnostics stamp the real build commit; repo-wide LF with a gofmt guard test; the decommissioned VPS address removed from the tree
- Call QoS fix: the scheduler moved from strict global priority to a per-leg, per-class weighted share, so one peer's back-pressure can't freeze a call; plus AIMD rate control, relayed→direct migration, and incoming calls over the Android lock screen
- Backlog, not amnesia: everything the audit found and deferred is recorded in the repo's backlog with its reasoning