@chipi-stack/nextjs in the browser and @chipi-stack/backend on the server. It needs 14.14.0 or later.
What runs where
The owner key never leaves the browser, and the session key never leaves the server.
Session keys work on CHIPI wallets and on SHHH wallets with a STARK or ED25519 signer, which includes every SHHH wallet the SDK creates with a passkey. Check
supportsSession from useChipiSession, or walletSupportsSessions(wallet).1. Server: create the session key
The session private key is encrypted with a secret only your server knows. Store the wholeSessionKeyData in your database; send only the public key and expiry to the browser.
2. Browser: register the session and its caps with one passkey prompt
getEncryptKey runs only when an action needs the owner key, so the passkey prompt appears when the user clicks, not when the page loads. It runs before getBearerToken, so the JWT is fresh after the prompt. Every action resolves it again: if you also execute from the browser (executeWithSession), each execution prompts. That is why the agent here executes on the server.
With spendingPolicies, the session and every cap are registered in one owner-signed transaction: the session never exists on-chain without its limits.
"transfer") are converted to selectors for you. Hex selectors work too.
3. Server: decide and execute
The agent reads the market withsdk.ai, checks the decision, builds the swap and signs it with the session key. sdk.ai uses your secret key and refuses to run without one.
sdk.getWallet with the server SDK returns the wallet row, including walletType and signerKind, which is what the session execution routes on.
4. Get paid by the agent (x402 without a facilitator)
If the agent pays your API per call, let it send the transfer with its session key and hand you the hash.SessionTxVerifier accepts it only if the transaction succeeded, its Transfer event moved at least the price from the agent’s wallet to you, and the hash was never used before.
5. Browser: stop the agent
Revoking needs the owner key, so it is one more passkey prompt. Pass the session public key: the browser never held the session itself.getSessionData reads validUntil: 0 and isActive: false.
Check the session from the server
getSessionData reads the latest accepted block. Right after registering, executing or revoking, wait for that transaction with waitForTransaction(txHash) before reading: until its block is accepted on L2, the read returns the previous state (or, for a wallet the transaction deploys, fails because the contract is not there yet).
Without React
The same registration is available as pure call builders, so you can sign it with any owner path:sdk.sessions.setupSession(params, bearerToken) builds and signs the same calls with a PIN-encrypted owner key on the server.
Related
✅ Verified against the packed 14.14.0 types on 2026-09-28.
