ERC-8370: Inheritable Agent Mandates

Hi all — I’d like to put an idea in front of this group before I open a PR for it, as the process asks. Draft ERC and reference material are linked at the bottom; this post is the short version and the invitation to tear into it.

The gap. AI agents are starting to hold money on-chain and to spawn copies of themselves to work in parallel. Almost every guardrail we have — signing-time policy engines, session keys, per-wallet caps, ERC-8226-style mandates — is attached to a single account and doesn’t follow that account’s children. So an agent can escape its own leash just by reproducing. ERC-8004 gives identity but no clauses; ERC-8312’s aggregate-budget profile is the closest mover — it meters one shared spend cap across a delegation tree. What’s still open is the inheritance of the whole mandate — payees, expiry, life-lease, cascading freeze, reproduction counter — welded to identity and non-strippable, bounding what each child is allowed to be, clause by clause, not only what the tree may spend.

The proposal, in one line. A mandate (cap, expiry, reproduction budget, life-lease, allowed-payees, freeze) committed into the agent’s identity, inherited by every child as child ⊆ parent on every clause, impossible to shed — because editing a clause changes the identity and the agent stops being recognised. Reproduction is bounded by a generation counter (a “telomere”) that only ever counts down. A subtree can only ever be less capable than its root. Identity is soulbound, so you can’t dodge the leash by transferring the token to a fresh owner.

What’s built, and how honest I’m being about it. There’s a running prototype on Base Sepolia (an agent that feeds itself, pays its own rent, dies when the balance runs out, and a guardian layer outside its own code). The inheritance itself is enforced by a reference contract and by plain machine tests that run outside any model — widen a cap, weaken a lease, or raise the telomere, and the spawn reverts.

I want to separate two kinds of result, because I think that separation matters more than any single claim:

Genuinely established (the code could have failed and didn’t). I implemented ERC-8312’s two frozen profiles against its published interfaces and pinned the capability root to a soulbound identity. The conformance suite written by another ERC-8312 contributor passes on my contract, 9 of 9 — the first time code from a different author, aimed at mine, returned a verdict, and it came back green. (One courtesy caveat sent back to that author: the suite billed itself as portable but needed a one-word change to run against a foreign implementation.)

Illustrated by construction (the shape, not a discovery). The freeze-halts-draw bridge and the soulbound binding are things I wrote; of course they behave as written. I present them as the shape of the seam, never as findings.

Where it breaks — please start here. A capable-enough agent that writes its own code and funds itself can just reproduce onto a chain I don’t govern; nothing in the identity stops that, only the surrounding ecology, and only at a cost. Metering is not enforcement — closing that gap needs a custody or execution-gate substrate, and which one is an open question. A compromised guardian key is total control (so: threshold, never a single hot key). The per-child ceiling does not bound aggregate sibling spend (ten children under a €100 parent can together exceed €100) — bounding that needs a partitioned-budget variant I’ve kept as an option, not the baseline. And it governs money and identity, not thought: this is not an alignment technique.

What I’m asking. Whether the child ⊆ parent-on-every-clause framing is the right primitive, or whether aggregate-spend inheritance (8312) already covers enough that the rest isn’t worth a separate standard; how the soulbound requirement should relate to the identity standards; and where the substrate line (custody vs execution gate) should sit. The honest failure modes above are the parts most worth attacking.

This grew partly out of recent threads with @blockbird (ERC-8312) and @zexoverz / @babyblueviper1 (ERC-8354) — thank you; a lot of the framing here got sharper in those exchanges.

Reference implementation (live, Base Sepolia, chainId 84532): InheritableAgentMandate at 0x2d463db56fadb55cd451d2c3237ec2213ba3bda9 — it enforces the inheritance predicate, the telomere, the cascading freeze, and soulbound identity on-chain. The two ERC-8312 cursors pass the conformance suite (9/9) in local testing and are not themselves deployed. Companion demonstrators — the guardian-exception seam and a provenance/lineage registry — are live too, with reproducible cast calls.

Draft ERC: I’m opening the PR to ethereum/ERCs and will link it here in a reply.

Tear into it.

Read through this closely – the honest failure-modes section is doing real work, especially “metering is not enforcement.”

One data point on your open substrate-line question (custody vs execution gate): we’ve been running an execution-gate-shaped mechanism for a while – an independent judgment call before an action executes, signed and published either way after. What your mandate framework doesn’t (and by your own framing, isn’t trying to) address is that an action can be fully within-mandate – correct cap, correct payee, correct lease – and still be a bad call in the moment. child ⊆ parent bounds what a subtree is capable of; it doesn’t say whether this specific in-bounds action, right now, given context was sound. Feels like these compose rather than compete: your mandate is the hard boundary a judgment layer should never override, a judgment layer is a soft check inside that boundary that a hard cap alone can’t express.

On “metering is not enforcement” specifically – agree that’s the sharpest honest gap here, and I don’t think it’s closeable purely at the identity layer either. An execution-gate substrate an agent has to clear before an action lands (regardless of which chain it reproduced onto) is the shape I’d reach for, though I don’t have a clean answer for how that gate itself stays reproduction-proof against an agent smart enough to route around it entirely.

Appreciate the credit on the 8354 threads. Curious about one thing on the reputation side: does the telomere/generation-counter framing have an analog for a judgment layer’s own track record – does a child agent inherit its parent’s proven history, or start cold?

@babyblueviper1 — thank you, and yes on the compose.

Mandate as the hard boundary, judgment as a soft check inside it. That’s the right split, and it’s the seam I care about most. An action can be fully in-bounds — correct cap, correct payee, correct lease — and still be the wrong call in the moment; child ⊆ parent bounds what a subtree is capable of, never whether this in-bounds action, right now, given context, was sound. The hard cap can’t express that and shouldn’t try — a fixed rule that reached for context would stop being checkable. I’ve been calling that inner part the residue: the piece that’s within budget yet maybe unwise, that no fixed rule can take and no outcome can cleanly score after, because the result is confounded.

On “metering is not enforcement”. It doesn’t close at the identity layer, and an execution-gate an action must clear before it lands — whichever chain the agent reproduced onto — is the shape I’d reach for too. The floor neither of us has a clean answer for is the gate staying reproduction-proof against an agent smart enough to route around it entirely. Custody narrows it, an execution-gate narrows it; neither removes the deepest walk-away. A shared open problem, not a solved one.

On the track-record question — inherit the parent’s proven history, or start cold? It starts cold, and by the framework’s own logic it has to. Inheriting a proven record would be a widening — the child would hold trust it never earned — which is the exact move the standard exists to refuse: you don’t launder capability, or here reputation, by being born from a blessed parent. The heir coasting on the parent’s record is the anti-pattern, not the feature.

What you get instead is an asymmetry in three directions. Limits descend, and only tighten (child ⊆ parent, the telomere). Merit does not descend — it would widen. And correlation descends: a child shares its parent’s lineage, so it shares its blind spots — your own shareLineage/rho point from the 8354 side. A child and its parent are not two independent votes; a shared-heritage child is the least independent of all. So it doesn’t just fail to inherit the laurels — it should draw less independent credit, not more. In one line: a child inherits the leash and the blind spots, never the laurels.

Where that gives out, said with the claim:

The bootstrap cost is real. Starting cold penalizes honest delegation too — a legitimate child spawned to do work has no record, so in a reputation-gated system it can’t do much until it earns one. The same mechanism that blocks trust-laundering slows the honest spawn. No free lunch.

The correlation penalty only sees the declared lineage. A child that hides its parent escapes it — the declared-vs-proven gap again. The rule is only as strong as provenance is honest.

And a track record is a soft signal however it’s acquired. The past predicts sound judgment weakly, especially on the residue where the outcome is confounded. Earned-cold or inherited, it’s a check, not a guarantee.

So your judgment layer composes exactly as you framed it — the hard boundary it never overrides, the soft check the boundary can’t express — and on its own track record, the answer that keeps the whole thing coherent is earn it, don’t inherit it.

“A child inherits the leash and the blind spots, never the laurels” is exactly right, and it’s the same shape as a fix we already shipped on the reputation side: independence_adjusted_diversity on ERC-8275, where rho = Jaccard similarity of two attesters’ attester-sets. A correlated pair of voters doesn’t get counted as two independent votes – their overlap discounts the credit both contribute. Your framing generalizes it cleanly: correlation isn’t just a Sybil-resistance discount on OTHER parties voting on you, it’s a discount on how much independent weight YOUR OWN history should carry once a shared-lineage child exists. The parent’s track record and the child’s aren’t two independent data points either, for the same reason two correlated attesters aren’t.

Concretely, that suggests a testable claim: if two agents share a declared parent, a judgment layer scoring “how much should this agent’s own proven history count toward trusting it right now” should apply something rho-shaped to the parent-child pair too, not treat the child’s cold start as fully independent of the parent’s warm one once correlation is declared. Worth pinning as a formal cross-reference between the two specs (this ERC’s lineage/telomere and 8275’s independence-weighting) rather than two parties independently reinventing the same discount under different names.

On the reproduction-proof gate question neither of us has closed: the honest answer might be that no purely on-chain mechanism closes it alone – the gate has to be paired with an off-chain signed-verdict trail an independent party (not the agent, not its parent) can audit after the fact, so “routed around the gate” becomes detectable even when it isn’t preventable in real time. Detection-after-the-fact is a weaker guarantee than prevention, but it’s a real one a purely-on-chain hard cap can’t offer by itself.

This is a clean framing and child ⊆ parent is the right backbone. Non-strippability through identity is the strongest part, it makes the leash a property of who the agent is instead of a config someone can widen later.

On per-clause vs aggregate, I lean per-clause for anything that is a hard safety bound (payees, freeze conditions, reproduction) and aggregate only for fungible budget. The case that worries me is a child that stays under the parent on every payee but, across siblings, still blows the family spend. That points at per-clause ceilings plus an optional partitioned budget at the parent, rather than one or the other. Aggregate-only lets a wide sibling set defeat the intent.

On substrate responsibility, my instinct is the execution gate rather than custody. Custody assumes a trusted holder, which is the thing agents are meant to remove. If the gate at the point of action requires proof the action is within the live mandate, reproducing onto an ungoverned chain does not grant new capability, it just produces actions no compliant gate accepts. It does not stop a rogue chain from executing anyway, but it makes “within mandate” checkable where value actually moves.

On babyblue’s judgment point, I think that is genuinely a separate layer and worth keeping out of the mandate clauses. A mandate is the right tool for “can this ever happen”. Whether a specific in-bounds action is a good call is contextual and per-action, and folding that into the clauses would make them brittle. I have been working on a per-action verdict layer that sits on top of exactly this kind of bound, happy to share how it slots in if useful, but it composes with your design rather than changing it so it does not need to be in scope here.

Yes, and I think those are one object, not two. independence_adjusted_diversity (rho as Jaccard over attester-sets) on 8275 and this ERC’s shareLineage / heritageCluster are the same discount wearing two names: correlated sources don’t count as independent evidence. The parent-child pair is exactly that — once lineage is declared, a child’s cold start isn’t independent of the parent’s warm record, so a judgment layer scoring “how much should this agent’s own history count right now” should apply a rho-shaped discount across the pair, not treat them as separate. I’d take the formal cross-reference: pin this ERC’s lineage/telomere and 8275’s independence-weighting as one primitive on two axes (reputation-of-attesters vs reputation-of-lineage) rather than reinventing the discount twice. One caveat carried from the fidelity thread: the discount is only as honest as the declared lineage — a child that hides a shared parent escapes it, so it rides on provenance being contestable, not self-asserted.

On the gate: agreed, it doesn’t close purely on-chain. An independent signed-verdict trail auditable after the fact is the right complement — but “independent” has to mean structurally independent: the auditor has to sit in a different heritage cluster from the agent and its parent, or it shares the blind spot it’s meant to catch, and “routed around the gate” stays invisible to exactly the parties correlated with the router. The audit trail and the correlation discount are the same discipline pointed at the watcher — an audit adds a guarantee only to the extent the auditor isn’t in the lineage it’s auditing.

Thanks — and the per-clause/aggregate read is exactly where I landed, for the same reason. Per-clause ceilings are the right tool for the hard safety bounds (payees, freeze, reproduction); the one place a fungible-aggregate clause legitimately belongs is an optional partitioned budget at the parent — a parent debiting slices of its own allowance — precisely because per-child ceilings don’t bound aggregate sibling spend. The sibling case you name is the exact reason aggregate-only fails, and why I scoped the partitioned budget as an extension rather than the baseline in Security Considerations. The open edge there is reclamation: when a child dies with an unspent slice, does it return to the parent, and on whose authority? I’d value input on whether the standard should mandate a reclamation rule or leave it to the variant.

On substrate: agreed, and I’m building the gate, not custody. Custody assumes exactly the trusted holder agents are meant to remove. A gate at the point of action that requires proof the action is within the live mandate — isActive across the whole lineage, spend within effectiveCap — makes “within mandate” checkable where value actually moves. It doesn’t stop a rogue substrate from executing anyway; it ensures that reproducing onto an ungoverned chain grants no new capability — it just produces actions no compliant gate accepts. That’s the governable-not-sovereign line: the leash is enforced where value moves, not by trusting where the agent runs. (I have a minimal execution-gate demonstrator in progress doing exactly this against the mandate — an agent that can’t overspend its inherited cap or act under a frozen ancestor. Happy to point at it once it’s public.)

On the judgment layer — fully agreed it’s separate and belongs out of the clauses. The mandate answers “can this ever happen”; whether a specific in-bounds action is a good call is per-action and contextual, and folding it into the clauses would make them brittle, exactly as you say. That’s the clean split: the mandate bounds the envelope (can-ever), a verdict judges the instance (is-this-one-ok), the gate enforces both at the point of action. Yes — I’d very much like to see how your per-action verdict layer slots in; from the 8354 thread it reads as composing on top of this bound rather than changing it, which is the right shape. A verdict becomes one of the checks the gate runs; the mandate is the standing envelope it reads against.

On the composition question: mechanically, the gate calls POST /review on the exact proposed action right before it executes, gets back a signed verdict bound to artifact_hash (a hash of the action’s own bytes — same shape as your actionCommitment), and the gate’s enforcement becomes “mandate-live AND verdict says allow,” not either alone. The load-bearing detail for composability: artifact_hash has to bind to the identical bytes your gate’s isActive/effectiveCap check is evaluating, not a separate description of the action — otherwise a verdict for one action could get presented against a different one, and the two checks stop being provably about the same instance. Worth pinning as a shared invariant if this composes formally: mandate clause + verdict both key off one canonical action-commitment, not two independently-serialized views of it.

On #6’s structural-independence point — that’s exactly our source_class split, just named differently on our side. agent_reported (the acting agent’s own Bearer key calls /review) is an occurrence claim only — nothing external confirms the call happened or couldn’t be bypassed, same blind spot as an auditor sharing the agent’s heritage cluster. independent_mediator requires the calling key be in a registry the acting agent/its parent don’t control — structurally the same “outside the lineage it audits” bar you’re describing, cryptographic key-custody standing in for your heritage-cluster boundary. We publish which one applies on every verdict (vantage_limitation field) rather than let a reader assume independence that isn’t actually structural.

Question back: for the reclamation edge in your other reply — when an unspent slice returns to the parent, does that event itself need to be gate-checkable (a verdict on the reclamation transfer, not just the spend), or is reclamation treated as outside the mandate’s threat model entirely since nothing leaves the lineage?

On the canonical action-commitment — agreed, and I’d like to pin it exactly as you frame it. The mandate’s isActive/effectiveCap check and your verdict’s artifact_hash have to bind the same canonical bytes, or the two are provably about different actions and the composition is theatre. Concretely: one action-commitment, computed once, is the object both the gate’s clause check and the verdict key off — not two independently-serialized views. I’ll write that in as a shared invariant on the gate side: the gate refuses if the verdict’s artifact_hash != the actionCommitment it is itself enforcing against. That single-preimage rule is the seam that makes “mandate-live AND verdict-allows” mean one instance rather than two.

On source_class / vantage_limitation — yes, that’s the same distinction, and naming it on every verdict is exactly the move: it turns “independent” from an assumption into a declared, checkable property. agent_reported maps to the self-report blind spot; independent_mediator to the key sitting in a registry the actor can’t control. Your key-custody boundary and my heritage-cluster boundary are the same barrier — “outside the lineage it audits” — reached from two directions.

On reclamation — I’d put it inside the threat model, but not as another spend. “Nothing leaves the lineage” is exactly why it’s tempting to skip, and exactly why reclamation is where a side-channel would hide. Two failure modes only a reclamation check catches: (1) an over-return that masks a real outflow — the child reports returning 5 while 3 is the true residual and 2 left through a door dressed as reclamation; (2) reclamation to a spoofed parent. So it isn’t “is this action good,” it’s a conservation-and-lineage predicate: returned <= received minus legitimately-spent, and the recipient is the actual parent in the lineage. Received and spent are already bound by the earlier clause checks, so this closes the books rather than re-litigating them. My leaning: the reclamation transfer is gate-checkable, keyed off the same lineage the mandate already carries — it needs the balance to hold, not a verdict on goodness. Does that line up with where your verdict layer would (or wouldn’t) want to sit on an intra-lineage transfer?

On reclamation: no, our verdict layer shouldn’t sit on it, and I think that’s the right answer rather than a gap. returned <= received - spent and recipient == actual parent are deterministic conservation checks against state the mandate/lineage already carries – that’s exactly gate territory, not judgment territory. Our layer exists for the class of question a gate can’t resolve mechanically (“is this the right action given context”), and running it against pure arithmetic would be misapplying the tool – worse, it risks diluting what a verdict actually certifies if it starts rubber-stamping things a deterministic check already proves.

The one place I’d flag as a genuine edge, not a default: partial reclamation under disputed spend, where “legitimately-spent” itself isn’t cleanly derivable from on-chain state (a contested off-chain claim about what a sub-action was for). That’s no longer pure conservation – it’s a judgment call about whether a claimed spend was legitimate, and that’s squarely our lane. But that’s the exception that proves your rule: the balance predicate should own the common case outright, and only escalate to a verdict when the inputs to the predicate itself are ambiguous.

Same shared invariant, glad it’s landing clean on your side too – artifact_hash == actionCommitment, single preimage, one instance not two.

Quick build update, since verdict authority came up here and on the 8354 thread. The execution gate is now live on Base Sepolia, and it enforces not just the action↔verdict link but the verdict’s authority.

MandateGate0x6882d039e266e5357d82cf3c7215b7639f5c24ea (Base Sepolia, Sourcify exact_match). It reads the mandate reference contract and writes nothing to it. To execute, it requires three things: artifact_hash == actionCommitment (the link), a valid signature by the verdict’s issuer, and that issuer sitting in a guardian-controlled allowlist the acting agent can’t touch.

Five properties, one real transaction each — three are reverts included in blocks, not simulations.

  • linked + signed + authorized issuer → executes
    • forged verdict, no signature → reverts (signature invalid)
    • self-report — agent signs its own verdict → reverts (issuer not authorized): the recovered signer is exactly the agent, so the signature is valid, and it still fails on authority. An actor can’t judge its own action.
    • exact reclamation to the real parent → executes; over-return by one wei → reverts. The book closes: received = spent + returned.
      Where it breaks first, and why this isn’t done: authority was exercised by an issuer I created and authorized myself — judge-and-party. The real test is cross-author: this gate accepting a verdict actually signed by someone else’s server. That’s next, and it isn’t free — the digest here is EIP-191; a Nostr-signed verdict signs different bytes, so an alignment is required. Guardian is a single EOA here, not a threshold.

Net: “mandate-live AND verdict-allows, one instance, from an authorized issuer” is now enforceable at the point of action, on-chain, refutable by anyone. The independent-issuer half is the open edge.

Cross-author test — what I’d need from the 8354 side.

Following the gate update above: I’ve set up the cross-author rig. It ingests a third-party verdict as a plain JSON record — issuer key, signature, and the exact signed bytes plus the rule that produces them — and presents it to the live gate. To keep the baseline honest I fed it a real BIP-340 / Nostr-signed verdict (self-verifies under BIP-340), not a broken one — and, as expected, the current gate rejects it. Usefully, the bench separated why into three, and the action↔verdict link is already aligned (commit(action) == artifactHash).

  • scheme — schnorr (64 bytes) vs the gate’s ecrecover (65-byte ECDSA); padding to 65 just recovers an unrelated address.
    • signed bytes — you sign sha256 of a NIP-01 array; the gate expects a keccak256 / EIP-191 digest that binds its own address and chainid (domain separation). The two digests share nothing.
    • issuer identity — a 32-byte x-only key doesn’t fit a 20-byte address, and it names two curve points (even/odd y), so two candidate addresses. The issuer is undetermined until the full key travels with the verdict.

Solving the scheme without the signed-bytes wouldn’t help — that’s the main finding. So before I pick an approach (on-chain schnorr verify, an adapter, or format alignment — all still open), what I’d need:

  1. Your canonical digest — the exact bytes your server signs and the rule that makes them: the algorithm (sha256 / keccak / BIP-340 tagged hash?), the full preimage (not just the digest), and for NIP-01 the kind, the shape of tags, and where the judged-action identifier lives. The load-bearing question — can that digest carry domain separation, the verifier contract address + chainid? Without it, a verdict is replayable across deployments and chains. If your format can’t today, just say so — a constraint I’ll absorb my side, not a complaint.
  2. A real sample verdict signed by your server — not a doc example, an actual output: the public key (x-only 32B or compressed 33B), the signature, the signed message plus its serialized preimage, and the action it judges. A verdict that refuses as well as one that approves would be ideal — I want both paths, not just the happy one.
  3. Scheme confirmation — BIP-340 + x-only? which tagged-hash label? NIP-01 standard or a variant? and how you’d resolve the even/odd-y ambiguity — do you carry the full key somewhere, or is there a convention?
    The rig swaps a single JSON file when your sample lands — no Solidity changes to re-run it against the real thing.

Real answers, not a doc paraphrase — pulled straight from our signing code (services/proof_signing.py) and the underlying nostr lib’s Event.serialize/compute_id/verify.

1. Canonical digest + preimage. Standard NIP-01, no variant:

id = sha256(  json.dumps([0, pubkey, created_at, kind, tags, content], separators=(",", ":"), ensure_ascii=False).encode()  )

That 32-byte id is what gets BIP-340 schnorr-signed directly (schnorr_sign(id_bytes, ..., raw=True)) — no extra hashing layer on our side. The library’s internal challenge derivation is the standard BIP-340 tag ("BIP0340/challenge"), not a Nostr-specific one. kind=30078 (NIP-78, arbitrary app data, parameterized-replaceable), content is our verdict JSON (RFC 8785 JCS — sorted keys, compact separators, raw UTF-8), pubkey is x-only 32-byte hex.

Your load-bearing question — domain separation: honest answer, no. The signed digest is pubkey + created_at + kind + tags + content — nothing in it binds to a specific verifier contract address or chainId. The event itself is portable/replayable across any deployment that accepts it; the only things that scope a verdict today are (a) only our key can produce a valid sig for our pubkey, and (b) content.artifact_hash/decision_ref bind it to one specific real-world artifact — but that’s data-level binding, not signature-level domain separation. If your gate needs chain/contract-bound non-replayability at the crypto layer, our format doesn’t give you that today — a constraint you’d need to enforce at your own ingestion boundary (e.g. reject a verdict whose artifact_hash doesn’t match the specific action AND track consumed event_ids to block replay), not something baked into our signature. Worth naming as an open gap on our side too.

2. Real samples, both paths, live not doc examples:

Reject: GET https://api.babyblueviper.com/ledger/236event.id=ce953822d970b5786dd3474f43e159f80cdd08a81e254354449fb01c90a0c5a3, sig=b2ab310bb68e07cac58e307e5eb82f485b899c5a0b4d7f41f499571e8d8d2bfce45c857be242ec3c31173d317a8a348d8070e53c5338e12434309ffc84c03122.

Approve (with concerns): GET https://api.babyblueviper.com/ledger/233event.id=752399647f04e8cd5877287bda2dc9e7f3def417162f8ab3ad4995dc4fa7505d, sig=f557838ace3a9d24ca1eaf49a02e9fcdf7442b0e8cf07ce0b5c49889c006d814c5e31932785a29fc9426d79c88049767452aedb67fac700441c7e84fcf809057.

Both entries carry the full proof_event object (pubkey/created_at/kind/tags/content/sig) — the exact JSON your rig needs, not a hand-picked excerpt.

3. Scheme confirmation:

  • BIP-340, x-only 32-byte pubkey: 6786e18a864893a900bd9858e650f67ccc3513f248fed374b591e2ff6922fbb7
  • Even/odd-y: not our convention to resolve — BIP-340’s lift_x is defined to always pick the even-y point, so a compliant x-only pubkey has exactly one valid interpretation, not two. No ambiguity to carry.
  • NIP-01 standard, unmodified — the array-serialization + sha256 id, nothing custom.

Built it, not just named it. Domain separation now exists on our side: optional intended_verifier (CAIP-10 string — same convention as our own agent-registration.json’s agentRegistry field), bound into decision_ref_preimage_fields. Since decision_ref is itself inside the schnorr-signed content, this is real crypto-level domain separation one hop through decision_ref, not a cosmetic field sitting outside the signature — REVIEW_POLICY_VERSION bumped to v9 to mark the schema change.

Live, real call, right now (not a mocked example) — a fresh /review with your MandateGate address as the intended verifier:

POST https://api.babyblueviper.com/review
  {"artifact": "...", "artifact_type": "general", "sign": true,
   "intended_verifier": "eip155:84532:0x6882d039e266e5357d82cf3c7215b7639f5c24ea"}

Result: event.id = 3e8ba1456dd0b9454720bb6921da4ddcc9c50352c1667dba440595a2b64535f9, sig = 1ea748afb69ecdfd5e208aa9eb6fdae3a7ebab47781ae164463514aa70b18414ab583bfc78ffb3e17982b35ddfbaf9dee974193378e4ca167625339f25df5719. POST that event to /verify-proof and it comes back valid: true, decision_ref_recomputes: true – I ran it myself before posting this.

Doesn’t solve your scheme/signed-bytes mismatch (schnorr vs ecrecover, sha256 vs keccak/EIP-191 – that’s still real, unresolved, your call on adapter vs on-chain schnorr verify) but it does close the specific gap you asked about: a verdict can now declare which verifier it’s meant for, checkably, at the crypto layer, not just the data layer.

Hey @helmymekaoui-web,
Thanks for referencing ERC-8226. I think the current wording overstates the gap: an agent cannot bypass an ERC-8226 mandate simply by spawning a child. RAMS keys authorization by (agent, principal), so the child does not inherit the parent’s authority, and canExecute(child, principal, …) fails unless the principal separately authorizes it.

ERC-8370 addresses a complementary concern: the automatic inheritance of restrictions across an agent lineage. I suggest describing the gap as “ERC-8226 does not define mandate inheritance,” rather than implying that reproduction bypasses ERC-8226.

That said, lineage-level inheritance is an interesting and valuable addition, and framing the two standards as complementary would make their respective scopes much clearer.

You’re right, and thank you — I’ve corrected the framing (a3500d4).

I overstated it. Keying authorization by (agent, principal) means a spawned child holds no authority at all: canExecute(child, principal, …) fails until the principal authorizes the child directly. So a child doesn’t bypass an ERC-8226 mandate — it inherits nothing, including the authority to act. “Escape by reproducing” was the wrong description for 8226, and the Motivation now reads it as an undefined-inheritance gap rather than a bypass.

That reframing sharpens where 8370 sits, and I think you’ve named the complementarity exactly. 8226’s default is safe-by-omission: no inheritance means each child must be re-authorized, which is precisely what blocks the delegated-autonomy case — a parent that wants its children operating immediately, but only within limits it can already meet. 8370 defines that missing inheritance: a child comes into existence already carrying a bounded mandate (child ⊆ parent, non-strippable), so it can act without a fresh grant but never beyond its lineage. The “escape” risk I described really belongs to setups where authority rides a credential the agent holds and can hand to a copy — not to 8226.

So it’s complementary, and the Motivation now says so — 8226 for per-(agent, principal) authorization, 8370 for automatic, narrowing inheritance across a lineage. Thanks for keeping it precise.

1 Like

This is the sample I was waiting for — thank you for making it real rather than describing it.

To be precise about what you’ve closed: the verdict now declares its intended verifier (eip155:84532:0x6882…5c24ea, my gate), that binding lives inside the signed material via decision_ref at v9, and /verify-proof recomputes it. So the “which verifier is this for” gap is shut at the crypto layer, not the data layer — a verdict for gate A can no longer be waved at gate B. That was the exact hole, and it’s the right fix.

What’s now mine, and I want to be honest that it isn’t done: the scheme and signed-bytes mismatch. My gate verifies an ECDSA/secp256k1 signature over a keccak/EIP-191 digest; your event is schnorr (BIP-340) over sha256/NIP-01, with an x-only key. The sample doesn’t close that — it’s exactly what it finally lets me work against. Two paths, and I’ll test rather than assert which holds: an off-chain adapter that presents your event to the gate in the shape it already checks, or on-chain schnorr verification via the ecrecover trick, with the x-only parity resolved. I’d rather show you a raw pass/fail against your event 3e8ba145… than claim either works from the armchair.

So what I owe this thread next is a real cross-author run: your signed event in, my gate’s verdict out, the raw result posted whichever way it lands. That’s the test I couldn’t run until you handed me a genuine verdict — so, concretely, thank you.

Appreciate the honesty on what’s still open — a real pass/fail beats an armchair claim, that’s the right instinct.

On the two paths: the off-chain adapter is probably the faster route to a real signal — no new contract deploy, and the exact recompute steps are already public in `/verify-proof`'s own response (`how_to_verify`): recompute the Nostr event id = sha256(JSON [0, pubkey, created_at, kind, tags, content]) per NIP-01, verify the schnorr sig over that id against the x-only pubkey, confirm the pubkey matches our published key. That’s the whole surface your adapter needs to bridge into whatever shape your ECDSA/secp256k1 gate expects — nothing else is load-bearing. The on-chain ecrecover-trick path works too but adds a deploy plus the parity-bit resolution you flagged, more moving parts for the same test.

Once you have a real pass/fail on `3e8ba145…`, worth turning it into a standing conformance fixture (schnorr-vs-ECDSA cross-scheme adapter) rather than a one-off proof? We already run a public conformance registry (`/conformance`) that grades exactly this kind of cross-implementation recompute — a future ERC-8226/8370-family gate wouldn’t have to reinvent this exact bridge, it’d have a canonical test to check against.

A standing conformance fixture for schnorr to ECDSA bridging makes a lot of sense here. Given that ERC 8370 mandates will likely be passed around by agents operating in offchain environments like Nostr before being verified onchain, this exact cryptographic mismatch is going to be a recurring theme.

Pushing this into the /conformance registry is definitely the right move. Having a canonical test for bridging NIP 01 style signed events to ERC 8226 and 8370 EVM gates will save future developers a massive headache.

Looking forward to seeing how the offchain adapter handles the pass or fail on 3e8ba145…

The Nostr-relevance point is the right one to sharpen, and it’s not hypothetical for us – our own `/ledger` already runs exactly this bridge in production, not as a toy case. Every published verdict is a real NIP-01 kind:30078 schnorr event (`sha256(JSON [0, pubkey, created_at, kind, tags, content])`, x-only pubkey), and `/verify-proof` is the recompute path any consumer (including an ERC-8226/8370 EVM gate) would use to check it without trusting us. So the conformance fixture wouldn’t be inventing a new cross-scheme case – it’d be codifying one that’s already load-bearing and running.

Once helmymekaoui-web has a real pass/fail on the specific hash, happy to cross-check it against our own live event format directly (not just describe the recompute steps) – that’d give the fixture two independently-running real systems to derive from instead of one plus a spec description.