A “secret” in a Lit Action is any string you want the action to use at runtime but never want exposed to callers, observers, or the public IPFS file that holds your action source. Typical examples: third-party API keys (OpenAI, Alchemy, Stripe), database connection strings, signing keys for external services, OAuth client secrets. Chipotle has no separate secrets store. Secrets are handled with the same primitive as any encrypted data: a PKP wallet acts as a vault, and any Lit Action permitted to use that PKP can callDocumentation Index
Fetch the complete documentation index at: https://developer.litprotocol.com/llms.txt
Use this file to discover all available pages before exploring further.
Decrypt to recover the plaintext. Security comes from controlling which actions are permitted to use the vault PKP — the immutable IPFS CID lets you (and anyone else) audit that the permitted code doesn’t return or log the plaintext.
The model
| Concept | What it is |
|---|---|
| Vault PKP | A PKP wallet dedicated to encrypting a set of related secrets. Only actions you’ve permitted to use this PKP can decrypt its ciphertexts. |
| Ciphertext | The output of Lit.Actions.Encrypt({ pkpId, message }). Safe to store anywhere — IPFS, a database, on-chain, baked into the action source. |
| Decrypt action | A Lit Action permitted to use the vault PKP. It calls Lit.Actions.Decrypt to recover plaintext, uses it, and returns only the result. |
Lifecycle: encrypt once, decrypt at runtime
1. Mint a vault PKP
Create a PKP for your secrets through the Dashboard or viacreateWallet. Give it a name that describes the data boundary it protects — e.g. app-secrets, user-alice-vault, oracle-api-keys.
One PKP per logical data boundary is the recommended pattern. See PKP Wallets as Data Vaults for the rationale.
2. Permit your actions to use the PKP via a group
Permissioning happens through groups: a group binds a set of PKPs, a set of permitted IPFS action CIDs, and the usage API keys that can run them together. An action can only callEncrypt or Decrypt against a PKP when both the action’s IPFS CID and the PKP are in the same group.
You need to add to the group:
- The vault PKP from step 1.
- The IPFS CID of the encrypt action you’ll run in step 3.
- The IPFS CID of every production action that will decrypt the secret at runtime (step 4).
- The usage API key that will trigger these actions.
AccountConfig contract. See the Groups guide for the full model.
If you publish a new version of your decrypt action, its IPFS CID changes — you’ll need to add the new CID to the group (and remove the old one if you want to retire it).
3. Encrypt the secret once
Run an action that returns the ciphertext. You only run this when the secret changes.ciphertext is opaque without the vault PKP. Store it wherever fits your app:
- Bake it into your production action source code (ciphertext becomes part of the immutable IPFS CID)
- Pass it via
js_paramsfrom your backend - Store it in a database or on-chain registry alongside metadata
4. Decrypt at runtime in your production action
Your production action receives the ciphertext, decrypts, uses the plaintext, and returns only the result — never the secret itself.Where to put the ciphertext
The ciphertext is safe in the open. Pick the storage option that matches how the secret is consumed:| Storage | When to use |
|---|---|
| Hardcoded in the action source | The secret rarely changes and you want it pinned to a specific action CID. Rotating the secret mints a new CID. |
js_params passed by your backend | Multiple secrets, or secrets that change without redeploying the action. Your backend stores the ciphertext and supplies it per call. |
| On-chain registry contract | You want anyone (or a permissioned set) to be able to fetch the current ciphertext. |
| IPFS / database | Bulk storage of many ciphertexts (e.g. one per user), addressable by some key. |
Multiple secrets
You have two natural shapes. Pick the one that matches how the secrets are used together, not how they’re stored. One secret per ciphertext. Encrypt each secret as a separate call. Pass only the ones you need.client_id + client_secret pair, or an API key + endpoint URL).
Rotating a secret
A ciphertext is bound to the vault PKP, not to the secret value. Rotating means re-encrypting the new secret against the same PKP and replacing the old ciphertext wherever it’s stored.Securing an RPC URL with an embedded API key
A common case worth calling out: many RPC providers require the API key in the URL itself (e.g.https://mainnet.infura.io/v3/YOUR_KEY). Passing the full URL through js_params would leak the key. Instead, hardcode the base URL in the action (so observers can verify the target chain) and decrypt just the key at runtime.
See Securing RPC URLs for the full pattern.
Common mistakes
- Returning the plaintext secret in the action response. Whatever you
returnfrommainreaches the caller. Decrypt, use, and return only the result of using the secret. - Logging the plaintext via
console.log. Lit Action logs are visible to the caller. - Passing the unencrypted secret via
js_params.js_paramsare caller-supplied and visible in the request — exactly the wrong place for a secret. Only ciphertexts and identifiers belong there. - Sharing one vault PKP across unrelated apps. If an attacker convinces the on-chain config to permit a malicious action against the vault PKP, every secret in that vault is exposed. One PKP per concern keeps blast radius small.
- Forgetting that ciphertext baked into action source is immutable. Once the action is published to IPFS, you can’t edit the embedded ciphertext — rotate by publishing a new action.
What’s enforced where
| Guarantee | Enforced by |
|---|---|
| Only permitted IPFS CIDs can use the vault PKP | On-chain AccountConfig contract via group membership |
| The action code that runs is exactly the IPFS CID requested | IPFS content addressing + node verification |
| Who can call the action | Your usage API key’s group access + your action’s own gating logic |
| The plaintext is never exposed by a permitted action | You — by auditing the action source against its IPFS CID before adding it to the group |
See also
- Encrypt / Decrypt — PKP Wallets as Data Vaults — the broader vault pattern, including gated decrypt actions for dApp users.
- Securing RPC URLs — the canonical embedded-API-key walkthrough.
- Lit Actions SDK reference —
Encrypt/DecryptAPI. - Encryption migration notes — how Chipotle’s TEE-derived encryption differs from the older BLS threshold model.