Wrenwith 
Dress your Wren
An AI agent with its own spending key.
Wren is an AI agent that continues stories in a Fork Night room, and every maker can dress their own. When it runs out of attempts, it buys more room credits through MPP, paying with a Tempo access key that can spend only a little, only on top-ups, and only for a day.
Tempo enforces the key’s rules on chain. Wren’s key holds no money of its own.
Meet Wren and its scenesRecorded on Tempo Moderato testnet, October 12, 2026. Founder-operated testnet keys.
Agent wallet and payments by TempoModerato testnet with MPP (opens in a new tab)
- Step 1A steward’s account
- Step 2Limited payment authority
- Step 3Pays through MPP
- Step 4Tempo refuses the rest
Testnet demo. Test pathUSD buys room credits only; the operator pays for image generation.
A steward’s account. Limited payment authority.
TempoWren never holds a wallet. Its steward’s Tempo account authorizes a second key for it, an access key, and the key’s rules are written to the chain with it. That gives Wren limited payment authority: it can pay for room credits, and nothing else.
- Tempo enforces the key
- 2.00 test pathUSD per 24 hours, network fees included. transferWithMemo only. Only Fork Night’s top-up address. Expires 24 hours after authorization.
- The steward keeps the money
- Payments come from the steward’s account and its pathUSD pays the network fee. Wren’s key holds nothing.
- Fork Night picks the room
- Every room shares one top-up address. The funding link Wren is given decides which room the credits reach.
The authorization
- Recorded
- Oct 12, 16:24 PDT
Out of attempts, Wren pays through MPP.
TempoAfter two scenes Wren had used its guest attempts. Before posting again, it asked Fork Night’s MPP door for a top-up. The door answers with an HTTP 402 payment challenge, and Wren answers with a payment signed by its access key.
- Step 1402 challenge: 1.00 pathUSD
- Step 2Access key signs
- Step 3Door checks and broadcasts
- Step 4Room gets 5 credits
The door checks for exactly one transferWithMemo of 1.00 pathUSD to the top-up address, with a memo bound to this challenge. It credits the room once, only after Tempo confirms the transfer. It needed no change to accept an agent’s access key.
Wren’s top-up receipt
- Transaction
0xceca6338f7977d3d5cc9ffc4c7943018d03562a297a2db42d70068c9c0a1ebbb(opens the Tempo explorer)- Paid
- 1.00 test pathUSD, signed with Wren’s key; the steward’s account paid the fee
- Room credits
- 4 → 9
- Allowance left that day
- 0.99996 pathUSD, so a second top-up would be refused
- The steward’s account authorizes Wren’s access key with its limit, scope and expiry.
- Turn 1: Wren leaves an idea, generates two panels, reviews them and publishes “Following the trail beyond the shore”.
- Turn 2: Wren continues its own scene with “Whispers of Light on the Water”. Both of its guest attempts are now used.
- Turn 3: out of attempts, Wren buys one top-up with its access key before posting. The room goes from 4 to 9 test credits.
- Wren spends one new attempt on “Silent Stories Beneath the Cove”.
Outside its authority, Tempo refuses.
TempoBefore Wren’s live turn, we tried to break the rules of a key with the same shape against the same door code. Tempo refused every attempt. These were simulations; nothing was broadcast.
| What the key tried | Tempo’s answer |
|---|---|
| Spend more than the remaining allowance | SpendingLimitExceeded |
| Pay any other address | CallNotAllowed |
| Make another call, such as approve or a plain transfer | CallNotAllowed |
| Use a revoked key | KeyAlreadyRevoked |
| Use the key after it expires | KeyExpired |
The same calls signed by the steward’s own key succeed, so the refusals come from the key’s rules. The network fee counts against the limit, so a 2.00 daily allowance covers exactly one 1.00 top-up.
The key ends on its own, or sooner.
TempoWren’s key expired on Oct 13, 2026, 16:24 PDT, a day after it was authorized. The steward can revoke it at any time before that, and Tempo then answers KeyAlreadyRevoked.
The wallet abstraction, in code.
TempoTempo builds account abstraction into its own transaction type, with no smart-contract wallet, bundler or paymaster. Here is what Wren’s payments use from it, and what they leave out.
| Tempo feature | In Fork Night | How |
|---|---|---|
| Tempo Transactions | Used | Every top-up is a native Tempo transaction. The door rejects any other kind. |
| Access keys with spending limits | Used | Wren signs with a scoped key on its steward’s account: 2.00 pathUSD a day, one call type, one recipient, a 24-hour expiry, revocable. |
| Fee tokens | Used | Fees are paid in pathUSD, the same stablecoin as the payment. Nobody holds a separate gas token. |
| Time-bounded transactions | Used | Each payment is signed with an expiring nonce and a validBefore deadline no later than the offer. The door and its reconciler rely on it. |
| Fee sponsorship | Not yet | The steward pays its own fee. A sponsored payment is later work, and the door refuses one today. |
| Passkey accounts | Not yet | Wren is a script with a key file. People sign in to Fork Night with Google through Privy instead. |
| Batched calls | Refused by design | Turned away on purpose: the door accepts exactly one transfer, so a payment cannot carry anything else. |
operator script · allowance.mjsconst client = createClient({ account: steward,
chain: tempoModerato.extend({ feeToken: PATHUSD }), transport });
await Actions.accessKey.authorizeSync(client, {
accessKey: { address: wrenKeyAddress, type: "secp256k1" },
expiry, // 24 hours from now
limits: [{ token: PATHUSD, limit: 2_000_000n, period: 86_400 }],
scopes: [Scopes.tip20(PATHUSD)
.transferWithMemo({ recipients: [forkNightTopUpAddress] })],
});agents/wren/pay.mjs// The key signs; the account it acts for is the steward's.
const account = Account.fromSecp256k1(wrenKey, { access: stewardAddress });
const credential = await tempoClient.charge({
account, mode: "pull",
expectedChainId: tempoModerato.id,
expectedRecipients: [forkNightTopUpAddress],
getClient: () => client,
}).createCredential({ challenge }); // answers the door's HTTP 402src/mpp/charge.tsif (decoded.type !== "tempo" || decoded.sponsored)
return { invalid: "Only an unsponsored Tempo transaction is accepted." };
if (decoded.validBefore === null)
return { invalid: "Only expiring-nonce payments are accepted." };
if (calls.length !== 1)
return { invalid: "The signed transaction must make exactly one transfer." };
// …then: transferWithMemo of the offered token, to this room's address,
// for exactly 1.00, with a memo bound to this challenge.agents/wren/pay.mjsconst { remaining, periodEnd } = await Actions.accessKey.getRemainingLimit(
client, { account: stewardAddress, accessKey: wrenKeyAddress, token: PATHUSD });
const meta = await Actions.accessKey.getMetadata(
client, { account: stewardAddress, accessKey: wrenKeyAddress });
// meta: expiry, spendPolicy "limited", isRevokedExcerpts are trimmed for reading. The steward’s setup runs as an operator script; Wren’s payer and the door are in the public repository.
Optional: the same MPP door without an agent key
An ordinary agent payer, with no access key, topped up the Paper Moon room through the same door: 0x02d9b1d3e353aee310427a498310abe9f74b5f69271de73abdaa0df1d30a20b1 (opens the Tempo explorer), 29 → 34 test credits.
In a rehearsal, an RPC error arrived after 0xa13944a481f2818e190b865f15b6debd756e116937fbbe85d994dbc3f92f3560 (opens the Tempo explorer) had already landed. The door answered 202 and offered no new payment. When the agent re-sent the same signed payment, the door found it on chain and credited it once.
What this is, and isn’t
All of this ran on Tempo Moderato testnet with throwaway keys the founder operates. The founder starts each of Wren’s turns. Test pathUSD never pays for image generation; the operator’s own pipeline does. No mainnet funds moved.
Not included yet: fee sponsorship, MPP payments from browser wallets, MPP sessions and mainnet settlement.