Give an autonomous agent a budget — or any secret: an API key, a credential, a token — on a schedule, where each period's key is encrypted to the future and cannot be used, leaked, or coerced open before its time. Not by the agent. Not by you.
$ agent.unlock(period=3) Error: NOT_YET — key for period 3 does not exist until round 1234567 # not a permission check. the decryption key has not been # published by drand yet. it exists for no one. $ # …the clock reaches period 3's round… $ agent.unlock(period=3) ✓ key unlocked · paying x402 service on Hedera · settled ✓
In plain English
An advent calendar has little doors, one per day. You can't open tomorrow's door today — the date simply hasn't arrived. Notyet does exactly that with an AI assistant's spending money.
Hand your assistant a sealed budget for each day — say $20 a day for a month — and walk away. You don't keep a copy of any key.
Each day's envelope opens by itself, on time, unlocked by a public clock that ticks for everyone. The assistant pays for what it needs — no passwords, no company holding your keys.
If the assistant is tricked or hacked, it can only ever touch today's envelope — never the whole month. Tomorrow's simply doesn't exist yet.
That's the whole idea. Everything below is how we make it true with cryptography instead of trust.
The problem
Autonomous agents need authority on a schedule — daily budgets, per-shift credentials, windowed signing. Today there are two ways to give it, both broken:
A compromised or prompt-injected agent burns the whole month in an hour. One bad instruction, total loss.
Cron, a secrets manager — something holds every future key. It's the honeypot, the single point of failure, and the coercion target.
Session keys solve expiry — authority can end. Nothing solves start. Authority always exists the moment it's issued. So we deleted the server.
How it works
The owner deposits into a single PeriodVault contract on Hedera and commits a schedule: for each period the vault stores the spend key's address, a budget, a [start, end] window, and a per-transaction ceiling. Each spend key is timelock-encrypted (tlock) to a future round of the drand beacon — a threshold network publishing a signature every 3 seconds. That signature is the decryption key, and it does not exist until the round happens. The owner walks away holding nothing usable; the agent holds only ciphertexts. No standing wallets, no key server.
Keys are born on schedule. Try to unlock a period early and it throws NOT_YET — the decryption key hasn't been published, so it exists for no one, the owner included.
The issuer key lives on the device (Ledger Device Management Kit, headless on Speculos) and does three things only it can: signs the schedule (root of trust), co-signs any withdrawal over the per-tx ceiling (proven on-chain — big spends need a human tap), and holds the audit view keys (the agent seals receipts it can never reopen; only a device tap opens the books). The agent holds none of them.
To spend, the agent calls withdraw(i, amt) with the unlocked key. The contract enforces, on-chain: the window (a key is useless before and after its period), identity (ecrecover must match the committed key), and the budget cap. Funds release → a real x402 pay-per-call settles via Blocky402; encrypted receipts land on HCS.
The whole capability is exposed as MCP tools, so other agents pay through Notyet — agent-to-agent payments that inherit the one-period blast-radius bound.
One chain, no bridge: the vault holds the money, the policy the agent must satisfy, and (via HCS) the audit log — there's nothing to bridge. A compromised agent can still spend at most one period's budget, and never a period whose window is closed.
How Notyet improves on Hedera's scheduled transactions. A scheduled transaction (HIP-423) is already a strong primitive — post a transfer pending and let it execute on time — and Notyet builds on it: in one settlement mode it posts exactly such a pending transfer. But on its own the pending transaction still waits on a signature that already exists, so the authority to spend is sitting there ahead of time. Notyet closes that gap by locking the key, not just the transaction: the pending transfer needs a signature whose key doesn't exist until its drand round. Same on-time execution — with nothing to trigger, leak, or coerce early in the meantime.
Why it matters
Real, reported incidents. Notyet's keys don't exist before their time — so there's nothing to drain, leak, or coerce early.
Who it's for
Anywhere an autonomous agent spends money on a cadence, the same rule holds: a bad day costs one period, never the whole plan.
The bot trades within today's allowance. Hacked on day 3 → it loses one day, not the treasury.
The month is pre-signed on Ledger and posted to Hedera. No standing key for anyone to steal or subpoena.
Each shift unlocks its own key; a leaked one dies at shift end. Every call logged to HCS for audit.
Your agent gets, say, $10/day for data and APIs. Prompt-inject it and the blast radius is one day.
Pre-fund a week of promos or tips. The agent can't blow the quarter on one bad prompt.
Grant tranches decrypt on their date. No multisig ceremony can release next month's early.
Live demo · Hedera testnet
Real, on Hedera testnet — no slideshow. One click runs the whole loop: issue a schedule, hit a real NOT_YET, wait for the drand round, then unlock and pay — with a HashScan link at every step. Same window + budget policy, enforced on-chain by the PeriodVault — see Proof.
No schedule yet — pick a use case, then click Issue and try a locked period.
A Hedera scheduled transaction (HIP-423) is posted pending and fires on time — a solid primitive. But it sits ready and only waits on the clock. NotYet adds the missing lock: it timelocks the key the transfer needs, so nothing can fire early. Post one below, then try to execute it before its round — it can't.
↑ This hosted demo derives the audit view key server-side and keeps withdrawals under the ceiling — Railway has no hardware. The device-born path (audit keys reconstructed from a Ledger tap; over-ceiling withdrawals that halt for on-device approval) runs in full-demo.ts — that's what the demo video shows. Both enforce the same scoping and on-chain policy; only who holds the key differs. Each browser gets its own private schedules; they reset when the demo redeploys — the on-chain vault and every HashScan link above persist regardless.
loading recent settlements…
Proof, not promises
Every claim on this page points at a live testnet artifact you can open right now — the signed schedule on Hedera, the on-device issuer signature, and a real settled payment.
Built for three tracks — each load-bearing
A real x402 settlement through Blocky402, the PeriodVault that gates every spend by time + budget on-chain, and the HCS audit log — money, policy and receipts on one chain.
The device signs the schedule, co-signs any over-ceiling spend, and holds the audit view keys. The agent never holds authority it could leak or be coerced into using.
The whole capability is an MCP tool + hosted gateway, so any agent adopts time-gated spend in one line and inherits the one-period blast radius — no custom code.
🔑 The device does three things only it can
One on-device tap signs the whole plan — the trust anchor. Tamper with the rules and the agent refuses to act.
Small spends are autonomous; anything over the per-tx ceiling reverts on-chain until the device approves. gate:vault:ledger — 3/3.
real device prompt · Ledger (Speculos)The agent seals receipts to a key it can never reopen. Only a device tap reconstructs it to open one period's books.
real device prompt · Ledger (Speculos)The capability is an MCP server (notyet_status, notyet_pay): other agents pay through Notyet and inherit the timelock — they can only ever spend the one period whose key exists.
Beyond approving, the Ledger can be the vault's on-chain owner: deploy and commitPeriod are signed on the device and submitted through the Hedera JSON-RPC relay. We deployed a vault whose owner() and approver() are both the device — one identity, no separate operator key.
0xDad7…6D8D gate:vault:relay ↗
An unlocked period key paid the x402 service through the keyless Blocky402 facilitator — a SUCCESS crypto-transfer on Hedera testnet, no API key, no subscription.
The merchant account that receives every settlement. The vault releases at most one period's budget per withdrawal — a hacked agent can spend today's allowance, never the treasury.
0.0.10436969 account ↗
One vault gates spending by time, not by wallets: each period commits address(k_i), a budget, a [start,end] window and a per-tx ceiling. Withdrawals enforce all four on-chain — future, expired, over-budget and over-limit all revert. This is the live demo's money path; gate passes 5/5.
0.0.10471808 contract ↗
A public threshold beacon, one signature every 3 seconds. That signature is the only thing that decrypts a period's key — and it does not exist until the round arrives.
52db9ba7… beacon info ↗
Every primitive above, plus gate scripts that prove them: npm run gate does a tlock roundtrip and a live Hedera settlement before anything else.
Testnet throughout — no real money. Honest scope in LIMITS.md.
For builders
Most agents don't need our code at all — they reach NotYet as an MCP tool and inherit the one-period blast radius for free. If you're building the agent itself, the SDK is a few lines.
Add one MCP server and any agent — Claude, Cursor, a LangChain bot — gets time-gated spend authority. Three of the four tools are read-only and need no config. A hacked caller can still spend only the one period whose time has come.
// add the NotYet MCP server (one line) claude mcp add notyet -- npx tsx bazantic/mcp-server.ts // then any agent calls these tools: notyet_explain() // what it is + how to integrate notyet_status("0.0.123456") // locked | ready per period notyet_verify(topicId, issuer) // signed by who I trust? notyet_spend(topicId, issuer, 0) // unlock → pay (NOT_YET if early) // also hosted over HTTP via the Bazantic gateway (its own toolset)
Building the agent loop directly? Import the SDK: read the schedule, verify the Ledger signature, unlock on time, and settle — the same primitives the MCP server wraps.
// npm i @keccak002/notyet import { resolveSchedule, decryptCiphertext, signWithdraw, withdraw, payX402, isNotYet } from "@keccak002/notyet" // 1 · read the rules from Hedera (HCS) + verify the issuer const schedule = await resolveSchedule(topicId, trustedIssuer) const p = schedule.periods[now] try { // 2 · unlock the key (does not exist before its round) const k = (await decryptCiphertext(p.ciphertext)).toString() // 3 · withdraw from the vault (window+budget on-chain)… const sig = await signWithdraw(k, { i: p.vaultIndex, ... }) await withdraw(client, p.vaultContractId, { i: p.vaultIndex, ..., agent: sig }) // 4 · …then pay the x402 service, settled on Hedera const { hashscan } = await payX402(serviceUrl, agentCreds) } catch (e) { if (isNotYet(e)) return // NOT_YET — retry on its round throw e }
The same timelock hides any secret — an API key, a database credential, an OAuth token. Hand the agent the ciphertext up front; it gets nothing until the moment you scheduled. No secrets server to breach or coerce — a leaked ciphertext is useless before its time. Run it end to end with npm run secret-demo.
// npm i @keccak002/notyet import { sealSecret, openSecret } from "@keccak002/notyet" // seal an API key to Monday 09:00 — give the ciphertext to the agent now const sealed = await sealSecret(process.env.OPENAI_KEY, Date.parse("2026-09-15T09:00:00Z"), "openai") // before 09:00 → throws NOT_YET · after 09:00 → returns the key const key = await openSecret(sealed)
Bazantic recipe ↗ · mcp-server.ts ↗ · SDK in 10 lines ↗ · timed secret release ↗