A minimal example
ethers.utils.verifyMessage(payload, signature) and compare the recovered address to the address of this CID, obtained through getLitActionWalletAddress from any other action. Swap getLitActionPrivateKey() for getPrivateKey({ pkpId }) if you want the proof bound to a wallet you manage instead.
What is a Lit Action?
A Lit Action is an immutable JavaScript program identified by its IPFS CID and executed inside the Lit network’s TEE. The CID is both the action’s address and a commitment to its code: change one byte and you have a different action. Actions have access to a small SDK that lets them:- Sign data with the private key of a Programmable Key Pair (PKP) or with the action’s own identity key
- Encrypt and decrypt data with a symmetric key derived from a PKP, or with keys derived from the action’s identity
- Make HTTP requests to APIs, oracles, and blockchains
- Return a value to the caller
How a request reaches your code
You run an action by sending its code toPOST /core/v1/lit_action with a usage key. The server passes js_params to your main function as its argument, and whatever main returns comes back as response:
- The source is trusted and
js_paramsis not. The source is fixed by its CID, so anything written in it is part of the action’s identity. Anyone who can call the action can put anything injs_params, so check those values before you act on them. Derived Actions builds on this rule. - Actions are stateless. Each run starts fresh and remembers nothing from earlier calls. To keep state, store it outside the action and verify it when you read it back, as Signed Data in Untrusted Storage shows.
What will this action sign with?
Start from what the signature has to mean. A price proof should mean “this exact code produced this price”, so it should be signed with a key that belongs to the code itself. A treasury should keep its address when you ship a new version of its logic, so it should be signed with a wallet you manage. Those two needs map to the two kinds of keys below.Two kinds of keys
Every action can reach two different kinds of key. Knowing which to use is most of the design work.
A PKP outlives its code. When you ship a new version of an action, you add the new CID to the group and the PKP keeps its address. An identity key dies with its code. Ship a new version and you have a new key, which is exactly what you want when the key’s whole meaning is “this code approved it”.
Because the identity key depends on every byte of the source, changing one constant in an audited template gives you a new immutable action with its own key. One template can then serve many users or secrets. See Derived Actions.
What is a proof?
When an action fetches external data (a price, a weather reading, another chain’s state) and signs the result, that signed output is a proof. Anyone holding the signer’s public key can verify that the specific computation produced it. Smart contracts cannot make HTTP requests; a Lit Action can, and can deliver the result on-chain with a signature the contract checks withecrecover.
- Data from outside the chain. The action fetches the data and signs it, and the contract checks the signature.
- Computation you can check. The signature is tied either to a PKP you registered or to the exact CID that ran.
- No intermediary to trust. Neither the action author nor a single node can forge a result, because the code is immutable and the key never leaves the TEE.
How actions fit into Chipotle
Actions are organized within groups. A group binds PKPs to the action CIDs allowed to use them, and usage API keys are scoped to execute within groups./core/v1/lit_action, the server hashes the submitted code to its CID, checks that the API key may execute in a group containing that CID, and only then runs it. Inside the action, every getPrivateKey / Encrypt / Decrypt call is checked again: the named PKP must be in a group with the running CID. There is no IPFS upload step. You send the source and the server derives the CID.
The usage key appears in the tree above because it points at the group through execute_in_groups. The permission is stored on the key, and the group itself doesn’t list keys. Here is the whole path a request takes:
Every request takes this path. A group answers “Can this action use this PKP?” A usage key’s scopes answer “Can this caller use this group?” A wildcard key (execute_in_groups: [0]) passes the first check for any code, but it still needs a group to use a PKP.
Patterns that make Chipotle easier
These four pages describe the load-bearing design patterns. They are what production apps on Chipotle, including the Lit Agent Keychain, are actually built from.Derived Actions
One audited template, a stamped-in constant, and a new immutable action with its own key per user, secret, or tenant. No PKP to mint.
Signed Data in Untrusted Storage
Have an action sign the records you store. Consumers verify at runtime, so your database can never forge a permission.
Patterns
Gating logic as code, hostname-pinned RPCs, multi-source consensus, action-identity signing, PKP vaults.
Secrets
API keys and credentials inside actions: PKP vaults, encrypt-to-action, storage, rotation.
The action runtime
Inside an action, theLit.Actions namespace exposes the SDK. The most used functions:
ethers v5 is a global. Third-party ESM packages can be imported from jsDelivr with pinned versions. Key operations are limited to ten per execution and outbound fetch calls to fifty; see Limits.
Full reference: Lit Actions SDK.
Next steps
To read this section in order, follow the sidebar: Module Imports, WebAssembly, Examples, Patterns, Derived Actions, Signed Data in Untrusted Storage, Secrets, and Limits. These pages are also useful on their own:- Derived Actions — per-user and per-secret immutable actions from one template
- Signed Data in Untrusted Storage — verifiable off-chain state for stateless actions
- Examples — working code for common patterns and links to full runnable projects
- Module Imports — third-party packages with integrity verification
- Lit Actions SDK — full API reference
- Migration from Naga — mapping deprecated Naga actions to current equivalents