Lit.Actions.Encrypt seals data with an AES key derived — inside the TEE — from a PKP you own, and that key never exists outside the enclave. So “who can decrypt this?” reduces to “which action code is permitted to use that PKP?”, which is on-chain configuration you control.
If PKPs, groups, or usage keys are new to you, keep Core Concepts open in a second tab. Coming from Lit V1 / Naga access-control conditions? Read the model comparison first — this is a different (and simpler) approach.
What you’ll build
- A vault PKP — the key your ciphertexts are sealed under.
- An action that encrypts a secret and returns the ciphertext.
- An action that decrypts it — and only runs because you’ve authorized it.
1
Create an account and add funds
If you haven’t yet, follow the Quick Start through funding: create an account (store the account API key — it’s shown once) and add at least $5.00 of credits. Metered calls without credits fail with
402 Payment Required.2
Mint a vault PKP
wallet_address is your pkpId. Treat one PKP as one trust boundary: everything encrypted under it is decryptable by any action allowed to use it. Separate app secrets from user data by minting separate PKPs — see PKP wallets as data vaults.3
Encrypt a secret
- JavaScript (Core SDK)
- cURL
4
Store the ciphertext — anywhere
The ciphertext is safe to store in public: a database, IPFS, a smart contract, an environment variable, even hardcoded in client code. Its confidentiality comes from the PKP-derived key in the TEE, not from where it sits. Storage trade-offs are covered in Secrets → where to put the ciphertext.
5
Decrypt it back
6
Production posture: lock down who can decrypt
Right now your account key can run any code against the vault PKP. Locking it down is on-chain configuration:
- Pin the decrypt action. Publish your decrypting action to IPFS, register the CID in a group, and add the vault PKP to the same group. Now only that exact code can ever produce the plaintext.
- Scope a usage key to
execute_in_groups: [thatGroup]for whatever service calls it — it can trigger the decrypt flow but can’t run arbitrary code against your vault. - Gate inside the action if you need caller-level rules: token balance, NFT ownership, a caller signature. The gate is plain JavaScript — see gating logic.
The pattern you’ll actually use: encrypted secrets inside actions
Most encryption use on Lit isn’t “encrypt user files” — it’s giving your action a secret without giving it to anyone else. Encrypt an API key once, pass the ciphertext as ajs_params value (or hardcode it in the action), and decrypt at runtime inside the TEE:
Where encryption goes from here
Secrets lifecycle
The full vault model: minting, permitting, storing, bundling multiple secrets, and rotation.
Token-gated decryption
Decrypt only for callers who hold a token or NFT, or who sign a challenge — gates written as plain JS.
Coming from Lit V1?
How PKP-derived encryption differs from BLS access-control conditions, and when to use each.
SDK reference
Exact signatures for Lit.Actions.Encrypt and Lit.Actions.Decrypt.
Encrypted state at scale
A dark pool that stores every order as ciphertext in Postgres and matches batches inside the enclave.
Combine with signing
Decrypt a credential, act on it, sign the result — the two primitives compose in one action.
Administrative checklist for an encryption app
- Separate vault PKPs per trust boundary (app secrets vs. user data) — Patterns
- Decrypt action published to IPFS, CID pinned in a group with the vault PKP — Dashboard
- Usage keys scoped to that group only; account key never ships — API Keys
- No action returns or logs plaintext secrets — Common mistakes
- Rotation plan: re-encrypt, replace ciphertext, invalidate the old secret upstream — Secrets
- Credit balance monitored; decrypt calls are metered like any action — Pricing