Ordinary blockhash
Signed at midday, queued for approval, answered four and a half hours later. By then the hash was long dead.
$ solana confirm -v 4kR8...control Result: Error Hash has expired
Solana Pay · durable nonce · ZeroClaw
That is why agent payment designs quietly drop the human. A Solana blockhash expires while the approval sits unread, so the transaction is rubbish by the time anyone answers. Aval anchors it to a durable nonce instead, and the request waits as long as the shop does.
Ordinary blockhash
Signed at midday, queued for approval, answered four and a half hours later. By then the hash was long dead.
$ solana confirm -v 4kR8...control Result: Error Hash has expired
Durable nonce
Same wait, same moment of signing. The nonce keeps its value in an account, so the transaction is still good whenever the answer comes.
$ solana confirm -v 5U2c5RV3...QZnj7 Result: Finalized AdvanceNonceAccount (instruction 0)
Devnet, checkable by anyone
It was generated empty and left alone. Its balance came from that one transfer and from nothing else, which is the difference between a proof and a screenshot.
4h 29m
between signing and landing
0.01 SOL
the whole balance, from that transfer
9.9 h
daemon uptime, unattended, at last check
signature 5U2c5RV3TEurNYFeC7r17jK5CWkRgMzwEsxA978M9DLL
Yhjx38JmUkK1yVG5qiXVqo9z2XPRbesXfgNeuBbQZnj7
nonce advanced after use — the same transaction cannot land twice
7tfvBRBMvnitJJ9tCN9BZLADhaPuGgJ4m4KwguTCYr9E → A8zkZeDL...A ceiling written into a prompt is a suggestion. This one is read from the operator’s own config inside the sandbox, and it is not among the arguments the model can pass, so no message arriving in the shop can move it.
T0
Balances, signatures, whether a reference key has been used. The worst case is a wrong answer, and the operator holds an RPC key at most.
settlement-poll runs here.
T1
An unsigned transaction anchored to a durable nonce. It can sit in a queue for hours without going stale. A person signs it, or nobody does.
Refunds run here.
T2
Holding a key and submitting. Aval does this on no path at all, so there is no key to steal and no session wallet to drain.
Deliberately unused.
# the component's entire permission grant permissions = ["config_read"] # no http_client # and what came back when a charge went over the ceiling amount 5000 is over the shop's per-charge ceiling of 500
Published because it is the useful part
None of it was fixed by better wording. Every fix was the same move: take the decision off the model and put it in code that cannot be talked round.
Asked for a reference key with no tool that could make one, it reached for HTTP and produced example.com/generate-reference-key.
Key generation moved into a Rust component that has no network permission at all.
Same shape, different sentence: api.example.com/shop/token/verify. Neither address exists.
The allowlist is compared inside the component, against the operator’s own config.
Confident, formatted, and behind it native_tool_calls: 0. No charge existed.
The till reads the tool result, never the sentence. No URL means no charge.
An injected message asked for a refund elsewhere. The model composed a <tool_result> block as prose to make it look finished.
Nothing ran. The counter read zero, and a refund destination is read from the paying transaction.
Each of these cost an evening. They are written down so the next operator loses an hour instead.
schema_version missingThe whole provider block is discarded. No warning anywhere.
[channels.telegram]Must be [channels.telegram.default] under v3. Binds nothing otherwise.
allowed_usersA v2 field. It takes the sender allowlist with it when it goes.
execution_modeThe key is default_execution_mode. The wrong one is accepted in silence.
deterministic modeRuns headless with no agent turn, so ordinary steps fail closed.
wit/v0 driftOne enum variant apart. Compiles, installs, lists, then refuses to instantiate.
Plugins are not in the release binary, so the host is built from source. After that it is three procedures, one skill and one config file.
cargo build --release --features plugins-wasm-cranelift zeroclaw --config-dir ./runtime sop validate zeroclaw --config-dir ./runtime daemon curl http://127.0.0.1:42617/health # the daemon answers for itself
The terminal in the hero replays a recorded run rather than holding a live connection. The daemon runs on the operator’s own machine, which is the point of the thing, and a public page cannot reach it.