Skip to main content
Lit Chipotle is built on three composable layers that each handle a distinct concern. Understanding the separation makes it easier to reason about security, auditability, and where your own code fits in. The throughline: your keys’ authority lives on-chain, and an attested TEE enforces it by reading the chain. We call this Chain Secured — on-chain authority, signing at the speed of an API call.

Video overview

This video explains how Lit works in about four and a half minutes. It covers confidential hardware and how you can check which code is running. It also shows how contracts on Base decide which releases and which Lit Actions can use keys.
Say you’re building an app that holds a private key. It might sign transactions for your users, or for an AI agent. The key has to live on a server somewhere. So how does anyone know what that server does with it? Lit is designed so you can check. I’ll show you how, starting with two ideas: a hash, and confidential hardware.A hash function turns any input into a short output of a fixed size. SHA-256 always gives you 256 bits. Here’s a tiny program. It approves a payment when the amount is 1,000 or less, and this is its hash. Now I’ll change a single character, so the limit is 1,001. About half of the bits flip, and the new hash looks unrelated to the old one. Nobody knows how to find two programs with the same hash. So a hash tells you exactly which program you have.Now the hardware. Lit runs in a trusted execution environment, or TEE, using Intel TDX. A TEE is a virtual machine that the processor walls off from the rest of the computer. The processor encrypts its memory. The host operating system can’t read it, and neither can the cloud provider or the people running the server. That includes us. As the virtual machine boots, each layer of software is hashed before it runs, from the firmware up to the application. The hashes are recorded in registers that software can add to but can’t rewrite. The processor can then sign a report of those hashes, called a quote. The signing key exists only in genuine Intel hardware, and Intel’s certificates vouch for it. So anyone can check that a quote is real. This is called attestation.For Lit, the measurement that matters most is the application. The whole Lit runtime is defined by one file. It lists every container and pins each one by its own hash. The hash of that file identifies a Lit release, and it’s recorded in the quote.Approved release hashes are listed in a smart contract on Base. The Lit multisig approves each new one, and the approval is public. Lit’s keys come from a key management service that also runs in a TEE. When a Lit machine starts, it sends its quote and asks for keys. The service checks Intel’s signature, reads the release hash from the quote, and looks it up on Base. If the hash is approved, the machine gets its keys. If anyone changes the code, even by one character, the hash changes. The lookup fails, and the machine gets no keys. So new code can’t reach the keys until it’s approved on-chain. That applies to code we deploy too.You can run these checks yourself before you send anything private. First, fetch the quote from Lit’s API and check Intel’s signature. Second, hash the published release file and compare it with the quote. Public build records link each container to the open-source code. Third, check that the hash is approved on Base. Fourth, check the TLS certificate. It was generated inside the TEE and it’s bound to a quote, so your HTTPS connection ends inside the TEE.Your own code works the same way. A Lit Action is a JavaScript program that runs inside the TEE. It can fetch data from APIs and blockchains, and sign with a key. Each Action is identified by its IPFS CID, which is a hash of its code. In Chain Secured mode, your wallet owns your account on Base. You create groups that say which Actions can use which keys. On each request, Lit hashes the code to get its CID, and it reads the chain. If the Action and the key aren’t in the same group, nothing gets signed. Actions can’t be edited. To change the logic, you deploy a new version and update the group. The key keeps its address.So there are two layers of checks. The hardware shows which Lit release is running, and the chain says which releases can hold keys. Inside that release, the chain also says which Actions can use each key. Attestation tells you what code is running. It doesn’t prove that the code is free of bugs, and it relies on Intel’s hardware being secure. The source is open, so you can read it. The verification guide has every check as a command you can run. It’s at developer.litprotocol.com.

The Three Layers

TEE Enclave (Phala / dstack) The enclave holds the root key and performs all sensitive operations: key derivation, authorization checking, and sandboxed Lit Action execution. Nothing that touches key material ever leaves the enclave. The TEE also acts as a convenience relay for API-mode accounts — it can sign and submit on-chain management transactions on your behalf after verifying your API key scopes. On-Chain Permissions (Base) All authorization state lives on-chain in a set of smart contracts: an Account contract that registers the owner address, an API Key Registry mapping key hashes to scopes, a PKP Registry of wallet derivation path IDs, and Groups that bind PKPs to permitted action CIDs. The TEE reads these contracts to decide whether to execute a request. You can update them through the TEE relay (API mode) or by submitting transactions directly from the owner’s EOA or multisig (the only way in ChainSecured mode). Lit Actions (IPFS) Lit Actions are immutable JavaScript programs referenced by their IPFS content ID (CID), which is a hash of the code. They are not owned by anyone — they are public, reusable, and content-addressed, similar to npm packages. You can pin an action to IPFS to share it, but the TEE does not fetch actions from IPFS. You send the code with the request (or the CID of code the server has already cached), the TEE hashes it to get the CID, and then it runs the code inside a sandboxed JS environment that has access to the derived key material.

Follow one request

Here is one request from start to finish. Your app holds a usage key with execute permission in group 7. Group 7 lists the action QmABC and the PKP pkp_001. The app sends the action’s code, with {"pkpId": "pkp_001"} as its js_params. 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.
  1. The server hashes the code. The hash is QmABC, so this is the action that group 7 lists.
  2. It checks the account’s rules on Base. The key may execute in group 7, and group 7 lists QmABC, so the code may run.
  3. The TEE runs main(js_params).
  4. The action calls Lit.Actions.getPrivateKey({ pkpId: "pkp_001" }). Group 7 lists both QmABC and pkp_001, and the key can use group 7, so the TEE derives the key and the action signs.
  5. The response carries whatever main returned, such as the signature.
If a check fails, nothing is signed. A key without permission stops at step 2. Code that changed by one character has a different CID, so it also stops at step 2. A PKP that isn’t in the group stops at step 4. The Authentication Model walks through the same flow from the contract’s side.

API mode vs ChainSecured mode

Both modes run the same code, and which one you use is a configuration choice. They differ in who owns the account. In API mode, a Lit-managed credential owns the account and relays your admin writes — the fastest way to start. In ChainSecured mode, a wallet you control (an EOA or Safe) owns the account on-chain and signs every change itself — fully self-custodied, with an on-chain audit trail. Both run the same contracts and the same Lit Actions; only account ownership and how writes are signed differ. See API Mode vs ChainSecured Mode for the side-by-side and the migration path.

Further Reading