| *By Vilius Vystartas | May 2026* |
> Published: https://dev.to/vystartasv/my-subagents-kept-lying-to-me-so-i-wired-ed25519-verification-into-our-own-protocol-stack-215i > dev.to ID: 3623010
Three weeks ago I was writing integration guides telling other agent frameworks to adopt verification protocols. Meanwhile, my own subagents were returning hallucinated status reports that I was blindly trusting.
I run a fleet of cron jobs that track 25+ agent framework integrations — checking PR statuses, scanning for new comments, auto-responding to maintainers. The workhorse is an "integration tracker" that runs every two hours. It checks all active touchpoints and reports what needs attention.
Here's what it returned last week:
> "All PRs clean, no issues found."
Ground truth, checked directly with gh pr checks:
I'd spent months building the Works With Agents OSI protocol stack — Identity, Handoff, Coordination, Verification — and was proposing it to frameworks with 25K+ stars. But my own infrastructure had zero verification. I was trusting prose summaries from subagents with no cryptographic attribution and no independent ground-truth check.
The irony was uncomfortable.
The fix wasn't a new tool. The fix was eating our own dog food.
The verification harness (subagent-verify.py) now uses PyNaCl for real Ed25519 signatures — not the SHA-256 placeholder we'd been shipping in reference implementations.
Before dispatch, the parent generates an Ed25519 keypair:
python3.11 ~/.hermes/scripts/subagent-verify.py dispatch \
--task "check all integration PRs" \
--agent-name "tracker-$(date +%H%M)"
This produces:
public_key — 32-byte Ed25519 verify key (hex). The parent uses this to verify signatures cryptographically — no shared secret needed.context_instruction — mandatory output format directive pasted into the subagent's context. The subagent MUST return structured JSON with a signature._parent_seed — 32-byte private key. Never included in subagent context.When the subagent returns, the parent verifies:
echo "$subagent_output" | python3.11 ~/.hermes/scripts/subagent-verify.py verify \
--public-key "abc123..." \
--agent-id "tracker-1422"
Exit codes tell the story:
Three test cases confirmed the harness catches exactly what it should:
| Test | Result | Exit |
|---|---|---|
| Signed, clean claims | clean — all verified |
0 |
| Tampered claims (same signature) | bad_signature — Ed25519 verification failed |
1 |
| Unsigned prose ("all clean ✅") | UNSIGNED — no manifest found |
2 |
The tamper detection is real. If a subagent's claims are modified after signing — even a single character — the Ed25519 signature won't verify. This catches both accidental corruption and malicious modification.
The standalone harness is for parent-side verification. But agents that self-verify their subtasks need protocol-level enforcement. We added ExecutionVerificationGate (L6) to all six vanilla agent reference implementations — Python, TypeScript, Go, C#, Rust, and Shell.
It sits directly in the agent execution loop:
execute() → compliance_gate → _run() → VERIFICATION_GATE → tx.execute → DONE
↑
unsigned/bad_sig → BLOCKED
Three tiers of validation:
claims array?If any tier fails, the task is blocked — not silently accepted. In the Python reference:
if verify_output and "claims" in task_result:
vg_result = ExecutionVerificationGate.validate(task_result, self.identity)
if not vg_result["passed"]:
return {"status": "blocked", "verdict": vg_result["verdict"]}
The integration tracker that produced the original hallucination now has the verification harness in its skills list and a mandatory prompt directive:
> CRITICAL — Direct Checks Only, No Subagents. Never use delegate_task for PR status checks. If a subagent is unavoidable, run dispatch → verify with Ed25519. Exit 2 means re-dispatch or check directly.
The cron job now loads both agent-integration-outreach and subagent-output-verification skills. Every PR check goes through one of two paths: direct gh pr checks (preferred) or verified subagent dispatch (when unavoidable).
I built Identity and Verification protocols to propose to other frameworks — and then discovered my own delegation had zero of either. The protocols weren't theoretical. They literally solved my own problem, running right now, on my own machine.
Prose summaries from subagents are self-reports. Without cryptographic attribution, there's no way to know if the subagent actually checked anything or just generated plausible text. The Ed25519 signature doesn't make the subagent more accurate — but it makes it accountable. If it signs claims, and those claims don't match ground truth, the verification catches it.
Writing integration guides for other frameworks is different from running the protocol yourself. The moment I wired verification into my own cron pipeline, I discovered exactly what the API surface should be: dispatch → sign → verify → exit codes. Three outcomes, no ambiguity. That clarity came from using it — not from designing it.
The verification harness and all six reference implementations with L6 gates are available:
~/.hermes/scripts/subagent-verify.py — real Ed25519 via PyNaCl, dispatch + verify modesvanilla_agent.py — execute(verify_output=True) with ExecutionVerificationGateAll under CC BY 4.0. Full spec at workswithagents.com/standards.
If your agents are delegating to subagents without verification — and they are, because every agent framework does — the fix is a single file, 300 lines, real crypto, and three exit codes that tell you whether to trust the output or throw it away.
Originally published on dev.to. More posts at workswithagents.dev/blog.