At 10am, an agent gets authorization to send data to a partner.
The grant expires at noon. Plenty of time.
At 11am, that partner loses access. Ro...
For further actions, you may consider blocking this person and/or reporting abuse
The "self-description with extra steps" line is the whole thing, and you've put the load-bearing constraint exactly where it belongs: agent-writable=false on the source.
One more level worth pushing on: that constraint covers the read target, but not the read selection. If the agent gets to choose which source the SourceAdapter points at (or when it fetches), you've moved the self-description up a notch — it'll point at the source that still says ALLOW. So the source locator should be pinned inside the grant at issue time and ideally signed, and the gate fetches that exact source at execution, not whatever the runtime hands it. Your g-4421 record is one field away from this: put the source address in the signed grant, so "which authority do I re-check" stops being an execution-time choice.
On "needs external source" — the property that actually closes scenario 3 isn't off-agent storage, it's unforgeable-by-the-agent. A store the agent can't write but can substitute (point the adapter at a friendly copy) still fails. The clean version is the issuer's own signed state: revocation co-signed by whoever granted the role, exposed as a signed append-only log; the gate re-derives against the issuer's current signed head, and a substituted or rolled-back source fails the signature instead of returning a stale ALLOW. That's the move from "trust the store" to "trust the key" — and a key is the one kind of external an adversary can't just stand up their own copy of. A memory store with a provenance boundary works for your harness exactly to the degree its responses are signed by something the agent doesn't hold.
And +1 on shipping this as a status report, not a result. "We don't have external claim evidence yet" is the same honesty the claim is about — a gate that only re-derives against what you wrote would pass your own test and prove nothing.
The source locator gap is the one I hadn't named cleanly. Agent-writable=false covers
the store but not the selection. If the gate asks the runtime which source to check
rather than reading the address from the grant itself, a substituted friendly copy gets
through before the read happens. Pinning the source address inside the signed grant at
issue time closes it. The gate stops asking the runtime and starts verifying the
grant.
The trust-the-key frame is the real upgrade from what we have. A provenance boundary
says the store wasn't written by the agent. A signed response says what the issuer
committed to is still what you're reading. A substituted or rolled-back source fails
the signature check instead of returning ALLOW. That's the property the harness needs
to actually be testing against, and right now it depends on whether the external
source's responses are signed by something the agent doesn't hold. Without that,
provenance boundary is a weaker guarantee than it looks.
The g-4421 change is one field: source_address in the signed grant, fetched by the
gate, not passed by the runtime. That makes "which authority do I re-check" an
issuance-time commitment instead of an execution-time choice. The signed append-only
log is what makes substitution fail at the signature layer instead of the storage
layer.
Adding this as a pre-registered constraint in CLAIM-25. The current claim also needs a
note that agent-writable=false is necessary but not the full property.
Agent-unforgeable is where the guarantees actually live.
"Agent-unforgeable is where the guarantees live" is the line — that's the whole thing in five words, and it's a cleaner framing than I gave it.
One thing worth pinning before CLAIM-25 locks: unforgeable isn't the same as fresh, and scenario 3 can sneak back in through that gap. A signed response proves the issuer committed to that state — but not that it's the current state. If the signature covers the value but not its position in the log, an attacker who can't forge anything can still replay a genuinely-signed OLD response: the one from before the revocation, which still says ALLOW. Signature passes (it really is the issuer's), gate returns ALLOW, and you're back in the divergence cell — this time with a valid signature sitting on top of it.
So the signed read needs a freshness binding, not just authenticity: the issuer signs over a monotonic sequence number / log head, and the gate refuses anything at-or-below the highest offset it has already seen (or demands a signed "current as of" it can compare to a known high-water mark). Append-only gives you that for free if the gate tracks the head — it doesn't if the gate just verifies "is this a validly-signed entry." Rollback-to-last-valid is exactly the move that passes a pure signature check, so it's worth making the pre-registration say the property is signed-AND-fresh, not just signed.
Looking forward to CLAIM-25 — this is the part of the design that actually composes.
The rollback gap is the one I hadn't closed. Unforgeable proves the issuer touched that
value. Signed-and-fresh proves it's the latest commitment. Those are different
properties and the gate needs both or scenario 3 comes back through the replay path
with a genuine signature sitting on stale state.
The sequence number binding is what CLAIM-25 needs to name explicitly. Without it the
gate trusts whatever offset the runtime handed it and a pre-revocation entry passes
clean because the signature is real.
Pre-registration will say signed-AND-fresh as the actual property, not signed alone.
Append-only with head tracking is the minimal stack that gives you both. Gate tracks
the high-water mark, refuses at-or-below, rollback path closed.
This is the composability piece that matters most.
Exactly — and the piece that closes it completely is where the gate's floor comes from on the first read. A high-water mark works great once it's running, but the gate has no mark on its very first read (or after any restart that drops it), and that's the one window where a replayed pre-revocation entry has nothing to be rejected against.
The fix folds the two threads into one: put the source's sequence-at-issue inside the signed grant, next to the source address. Then "fresh" means "≥ the offset the grant recorded," and the gate's floor is grant-derived, never runtime-supplied. Cold start, restart, first-ever read — all have a baseline an attacker can't undercut, because it rode in on the same signature that authorized the action.
One more, since it's the kind of thing that bites in implementation: the high-water mark is now state the gate keeps, so it has to be monotonic in storage the actor can't rewind — otherwise you've just moved the rollback from the source to the mark. Append-only there too, or it's the same attack one level down.
That's the whole stack: pinned source + signature + a sequence floor carried by the grant + a tamper-evident mark. Genuinely the composability piece — good to watch it close. Looking forward to seeing CLAIM-25 run.
The cold start window is the one I missed. Without a sequence-at-issue in the grant, a
replayed pre-revocation entry has nothing to be rejected against on first read or after
any restart that drops state. Grant-derived floor closes it because the baseline
arrives on the same signature that authorized the action, before the gate has seen
anything.
The recursion on the mark is the implementation gotcha that needs to be in the
pre-registration explicitly. Append-only on the source but not on the mark just moves
the attack one level down. Same property, same requirement, applied again.
Full stack is now clear: pinned source, signature, sequence floor carried by the grant,
tamper-evident mark. CLAIM-25 will name all four as the actual property set, not just
signing. This is exactly what the pre-registration needs to lock before we run it.
If your AI gate only re-checks token expiration times without verifying real-time system changes, it's basically asleep at the wheel while security risks fly right past it.
Exactly the right frame. The harness showed this concretely: timestamp-only gate
returned ALLOW on the divergence cell, TTL still valid, but the real-world condition
that granted the permission had already changed underneath it. Technically correct
about time. Completely wrong about the world. That's the gap the re-derivation step is
trying to close
A nice reminder that resilience and recovery are just as important as correctness
That's one frame for it. The thing that surprised me is that TTL-valid plus
source-stale isn't really a recovery problem it's a correctness problem that passes
every surface check. The system wasn't down. The grant was valid. Nothing looked wrong.
The failure was happening inside the comparison itself, not in the uptime layer.
Resilience would matter if the re-derivati
Great post!
Thank you, glad it landed!