Verification path · no wallet
Check it yourself. Trust nothing we say.
Seven steps, top to bottom. Steps 1 and 2 run a quick check in your browser. Steps 3 to 6 open each recorded call on /verify or name a verification script. Step 7 is optional. If you see something else, the claim fails.
Reading the passive health check…
Where each check comes from: computed in your browserread from NEAR mainnetfetched from OutLayeragainst Intel's root / PCSdata we publish (repo) Only the last one is our word; everything else you can re-run without us.
Step 1: The code running in the enclave is the code in the repo
fetched from OutLayercomputed in your browseragainst Intel's root / PCSread from NEAR mainnetIntel signs a report naming the code that ran, and we compare its fingerprint with the current build, ed0f614a.
What you should seeThe quick check shows Pass. On /verify, all checks are green: the worker downloaded from FastFS hashes to ed0f614a…, the quote's report data matches, and Intel's certificate chain verifies. Badge: Current build. Published with add_versionalt ↗.Proof 3 items
- current build
ed0f614acd83c8dc4851901206f5e5f065a3a6e7731cf8cc8d6a3e2bfd66a1b8- published in
GXZtL6hgscWUAh7SpZLA5SBLyrVe4w9bCxz2Kmb4q1K5Explorer ↗Alt ↗- attested call
ed2c7a62-411f-4f8f-b06d-b214898e0ec4Open
Prefer a terminal? Verification script:
verify.shRebuilds the worker from source with Rust and prints the same hash. Read the script ↗
Step 2: The NFT contract is the one in the repo
read from NEAR mainnetcomputed in your browserNEAR's public RPC reports the code hash of keyhole-devmo.near, and we compare it with the wasm committed in the repo.
What you should seeThe quick check shows Pass. The contract's only minter is an account whose key was generated inside the enclave.Proof 3 items
- expected code hash
3cZsaySYgqZntFaRKEUffQUMGqRjkQ2DuSmFWXwiKGZH- contract
keyhole-devmo.nearExplorer ↗Alt ↗- repo file
contracts/keys/deployed/keyhole_keys.wasmExplorer ↗
Prefer a terminal? Verification script:
1-contract.shSame comparison from a terminal, using curl and sha256sum. Read the script ↗
Step 3: Checks on build a55ebb14: only the real holder gets in
fetched from OutLayerdata we publish (repo)These five calls ran on the previous build, a55ebb14. Each answer, refusals included, was produced inside the enclave and signed by Intel.
- RefusedA limited (function-call) key tries to act for an accountRefused: key_not_owned. Only a full-access key counts.
- AllowedThe account's real full-access key signsAllowed: the vault opens.
- RefusedAn account that already holds a key tries to mint anotherRefused: already_holder. No quota was used.
- AllowedThe holder locks the key for one hourAllowed: locked.
- RefusedA cross-chain signature is asked for while the key is lockedRefused: locked.
What you should seeEach Verify this call opens /verify with the verdict Genuine enclave, previous build (not the current one). That is expected for these calls, because they ran on build a55ebb14.Step 4: Selling the key moves the account; the old holder is locked out
read from NEAR mainnetfetched from OutLayerdata we publish (repo)The key moved from account A to account B on build a985fd87 (not the current build ed0f614a). A and B are team test wallets, not independent users. Before the move A reads its balance. After it, A is refused and B reads the same balance. A replayed request and an early withdrawal by B are refused too.
What you should seeEach call below is labelled build a985fd87, previous. Its attestation names that wasm, not the current ed0f614a. Click Verify this call to check one. The sale is one NEAR transaction, and its receipt shows SuccessValue.- AllowedSeller A's status, before the saleAllowed: A holds the key.
- AllowedSeller A reads the balance, before the saleAllowed: 2000 USDC base units.
- RefusedSeller A reads the balance, after the saleRefused: not_owner.
- AllowedBuyer B reads the balance, after the saleAllowed: the same 2000 USDC base units. The account moved with the key.
- RefusedSeller A replays the request it signed before the saleRefused: not_owner. The owner check runs before the nonce check.
- RefusedBuyer B withdraws right after the transferRefused: cooldown (10 minutes after a transfer).
Transfer 1 item
- sale: nft_transfer, A → B (build a985fd87)
89vSYY4tbeNPuNuhHB8YmkkAWAcjW5VdniQm3ePHuPYzExplorer ↗Alt ↗
The site gateway also refuses a session-scheme withdraw with HTTP 403 session_scope, before any enclave call. It has no call id.
Recorded on earlier builds (10 calls)
Earlier transfers 2 items
- key transfer A → B (earlier build)
G35r5pmCTffvKMShjPcwTkds1z8SuCHdJr32qGGzYktDExplorer ↗Alt ↗- key back to A (earlier build)
HGKe7Dzd3ERMLkWYcVvffV6opweQqN3wbfFevo44uDUnExplorer ↗Alt ↗
- AllowedA withdraws before the transferAllowed: withdrew 0.003 USDC.
- RefusedA signs again after the transferRefused: not_owner.
- AllowedB, the new holder, withdrawsAllowed: withdrew 0.001 USDC.
- RefusedB's signed request is sent twiceRefused: replay.
- AllowedB locks the key for saleAllowed: locked.
- RefusedB tries to withdraw while lockedRefused: locked.
- AllowedA cuts its caps to $0.002 per trade, $0.005 per dayAllowed: applies now.
- AllowedA asks to raise them againAllowed: pending for 24 hours.
- RefusedA asks for a quote above its own capRefused: over_cap_per_tx.
- RefusedA withdraws right after getting the key backRefused: cooldown (10 minutes).
Step 5: Tier caps hold on real trades
fetched from OutLayerdata we publish (repo)A Bronze key may trade at most $1 at a time, and a 2 USDC swap was refused before anything moved.
- Refused2 USDC swap on a Bronze key (earlier build f13efe92)Refused: over_cap_per_tx.
What you should see/verify shows the refusal with green checks. All 30 recorded mainnet steps are in the repo.Prefer a terminal? Verification script:
2-attestations.shChecks each of the 30 recorded steps against its attestation. In the browser instead: open any call id from docs/evidence/mainnet-e2e on /verify. Read the script ↗
Step 6: Each key can sign for other chains
read from NEAR mainnetfetched from OutLayerWith Chain Signatures, NEAR's MPC network signed for the EVM address derived from this key alone.
- Allowedchain_sign for key k5f4750858f328940 (build 09d481a5)Allowed: recovers to 0xe39C…f063.
What you should seeThe signature recovers to 0xe39C947d…f063. The MPC transaction below shows v1.signer.sign called by the minter, and the beat transaction was signed by the limited key: it may only call beat on the contract.Proof 2 items
- MPC sign
7P9VFx4pz5oxyXpXkyDVE6SheDuTqHgQcEM2vgKFUAfKExplorer ↗Alt ↗- beat (limited key)
B1BH9eZycbXqgwod2dqbn6e5UWY8TKTw2ZawyYqLRDtGExplorer ↗Alt ↗
Step 7: Optional: try it with your own wallet
computed in your browserread from NEAR mainnetSteps 1 to 6 need no wallet. With a NEAR wallet or MetaMask you can mint a free key, one signature, and the enclave pays the storage.
What you should seeYour result shows a call id The receipt number of one enclave run. Paste it into /verify to check that run yourself. and a Verify link. It checks out on /verify just like step 1. In your vault, press Read balance.MetaMask may warn “Malicious site”. Its scanner flags every *.vercel.app address, including ones that were never deployed. Details are in research/METAMASK_FLAG.md.
Who sees what
| Party | Sees | Does not see |
|---|---|---|
| NEAR (anyone) | Token id, holder, tier, transfer time, the minter's mint and beat transactions | Balances, trades, history, caps |
| KEYHOLE website gateway | The signed request and the reply it relays to the holder's browser. Bodies are not logged | Anything without the holder's fresh signature |
| OutLayer TDX enclave | Everything for the call it runs, sealed by Intel TDX and attested | Nothing is hidden from the code, which is why the code hash is checked |
| OutLayer custody wallet | The key account's balances and the swaps it settles | Who holds the NFT |
| 1Click solvers | The asset pair and amount of a quote | The key, its holder, its history |
| Next holder of the key | The account, its balances, earlier history rows as hashes | Earlier holders' trade details |
| Previous holder | Only what they did while holding it. After the transfer every request is refused (not_owner) | The new holder's balances and trades |
Who can change things: keyhole-devmo.near owns the contract (owner-only set_minter and set_royalty, royalty capped at 10%) and publishes worker versions with add_version. Every version is on chain, and an attestation always names the exact build that ran, so a new version cannot rewrite an old proof.
What this does not prove
- The attestation proves which code ran on which input and output. It does not prove the code is bug-free. The source is open; the threat model is in THREAT_MODEL.md.
- Trust rests on Intel TDX and the OutLayer operator's quote collateral, like any TEE system.
- Amounts in the recorded runs are tiny on purpose (fractions of a cent to $0.02).