Documentation Index
Fetch the complete documentation index at: https://developer.litprotocol.com/llms.txt
Use this file to discover all available pages before exploring further.
What Changed
Until now, the only JavaScript available inside a Lit Action was what shipped with the runtime:ethers (the v5 version) and the Lit.Actions SDK. If you needed anything else, you had to inline it or bundle it into your action code before uploading to IPFS.
That limitation is gone. Lit Actions can now import ES modules at runtime from jsDelivr, a public CDN that serves npm packages as ready-to-run ESM. You write a standard import statement with a version-pinned package specifier and the runtime resolves it to jsDelivr, fetches, verifies, caches, and executes it.
How It Works
Every import goes through three stages before any bytes reach the V8 engine.1. Resolution
The runtime resolves the import specifier to a jsDelivr URL. You can write a short npm specifier (zod@3.22.4), an explicit ESM specifier (zod@3.22.4/+esm), or a full URL (https://cdn.jsdelivr.net/npm/zod@3.22.4/+esm). When no file path is specified after the version, the runtime automatically appends /+esm to request the ESM entry point from jsDelivr. Bare package names without a version (import { z } from "zod") and relative paths (./util.js) are rejected. Every import must include a pinned version.
2. Integrity Verification
Each module URL is checked against anintegrity.lock manifest that maps URLs to their expected SHA-384 hash. If the hash of the downloaded content does not match, the import fails and the action does not execute.
For modules not yet in the manifest, the system uses trust-on-first-use (TOFU) with up to four-way verification: it fetches the module twice from jsDelivr, independently computes the SHA-384 of each response, verifies both against jsDelivr’s SRI hash header when available, and if the import specifier includes an inline #sha384- hash, verifies against that as well. The module is accepted only if all checks agree. The verified hash is then pinned to the lockfile so all future fetches are verified against it.
3. Caching
Once a module is verified, its source is held in an in-memory cache. Subsequent imports of the same URL (from any action execution) are served from cache without a network request.Import Syntax
Imports use an npm-style specifier with a pinned version. The runtime automatically resolves these to jsDelivr URLs and appends/+esm when no file path is specified.
@<version> pin is required and ensures you always get the exact same bytes. The /<file> part is optional. When omitted, /+esm is automatically appended to request the package’s ESM entry point from jsDelivr. You can also specify a path to a specific file in the package.
Inline Integrity Hash
You can append a#sha384-<hash> fragment to any import specifier to declare the expected integrity hash directly in your code. The runtime will verify the fetched content against this hash before execution.
integrity.lock. The /+esm suffix is still auto-appended when no file path precedes the # fragment.
The fragment is never sent over the network (per the URL specification). It is stripped before fetching and used only for local verification.
Full URLs
Full jsDelivr URLs are also accepted, with or without an inline hash:Examples
Validate Input with Zod
Format and Sign a Timestamp Proof
Encode Data as CBOR Before Signing
JSON Schema Validation with AJV
Package Compatibility
Not every npm package works. The package must meet these requirements:| Requirement | Why |
|---|---|
| Ships ESM in its npm tarball | jsDelivr serves files as-is from the published package. No transpilation or CJS-to-ESM conversion happens. |
| No Node.js built-in dependencies | Lit Actions run in Deno/V8, not Node.js. Packages that import fs, path, crypto, or other Node built-ins will fail. |
| No native/binary addons | The runtime is a sandboxed V8 isolate. Native code cannot execute. |
| Pinned to an exact version | Required for integrity verification and deterministic behavior. |
- zod — Schema validation
- ajv — JSON Schema validation
- date-fns / date-fns-tz — Date utilities
- cbor-x — CBOR encoding/decoding
- uuid — UUID generation
- lodash-es — Utility functions (ESM build)
- preact — Lightweight UI rendering (for server-side HTML generation)
- superstruct — Structural validation
- axios — depends on Node.js
httpmodule - lodash (non-ESM) — CJS only, use
lodash-esinstead - sharp — native binary addon
- bcrypt — native binary addon
If you are unsure whether a package ships ESM, check its
package.json for an "exports" or "module" field, or test the jsDelivr URL directly in a browser: https://cdn.jsdelivr.net/npm/<package>@<version>/+esmWhy jsDelivr
We evaluated several CDN options for serving npm packages as ESM. The choice came down to a set of non-negotiable requirements for running third-party code inside a cryptographic signing environment.The Requirements
- Immutability — The same URL must return the exact same bytes forever. If content can change, integrity hashes become meaningless.
- No server-side transformation — The CDN must serve the original files from the npm tarball. Any server-side bundling or transpilation introduces a layer we cannot audit or pin.
- Version pinning — URLs must support exact version locks (
@3.22.4) so that the resolved content is deterministic. - SRI hash support — The CDN should support Subresource Integrity headers so hashes can be computed and verified against the original source.
How jsDelivr Meets Them
jsDelivr with pinned versions serves raw files directly from npm packages at version-pinned URLs that are guaranteed immutable. Once a version is published to npm, the content behindhttps://cdn.jsdelivr.net/npm/zod@3.22.4/+esm never changes. jsDelivr does not perform any transformation, bundling, or minification on the source files. What the package author published to npm is exactly what gets served. Chipotle enforces this immutability guarantee by validating the SHA-384 hash of every module on every fetch, so even if a CDN were to serve altered content, the integrity check would catch it and reject the module before execution.
jsDelivr also provides built-in SRI hash support and is backed by a multi-CDN infrastructure (Cloudflare, Fastly, and others) with high availability and global edge caching.
Alternatives Considered
| CDN | Verdict | Reason |
|---|---|---|
| esm.sh | Rejected | Performs server-side CJS-to-ESM conversion. The output is generated, not the original source. We cannot guarantee that two fetches of the same URL produce the same bytes, and we cannot audit the conversion logic. |
| unpkg.com | Rejected | Serves raw npm files (good) but does not guarantee immutability of the ?module rewriting layer. The redirects it uses also complicate integrity verification. |
| Skypack | Rejected | Performs server-side optimization and conversion. Same concerns as esm.sh. |
| Self-hosted | Deferred | Eliminates third-party trust entirely but requires operating a package mirror. May be considered for enterprise deployments in the future. |
Security Model
Module imports operate under the same security model as the rest of the Lit Actions runtime. Every module is verified before it reaches V8.| Layer | Protection |
|---|---|
| URL allowlist | Only https://cdn.jsdelivr.net/ is accepted. All other origins are rejected at the resolution stage. |
| Version pinning | Exact versions are required. The URL is the immutable identifier. |
| SHA-384 integrity | Every module is hashed and compared against the integrity.lock manifest or an inline #sha384- hash in the import specifier. Mismatches are fatal. |
| Trust-on-first-use | New modules are double-fetched, hashes compared, and verified against jsDelivr’s SRI header before being accepted and pinned. |
| No redirects | HTTP redirects are blocked. The CDN must serve the content directly. |
| Size limits | Responses larger than 10 MB are rejected. |
| Timeouts | Fetch operations time out after 30 seconds. |
| Sandboxing | Imported code runs in the same Deno/V8 sandbox as the rest of the action. No filesystem, no subprocess, no native code. |
Limits
Module imports are subject to the same resource limits as the rest of your action:- Memory — Imported modules count toward the action’s memory limit (default 128 MB).
- Timeout — Module fetch time counts toward the action’s execution timeout (default 15 minutes).
- Network — Module fetches use a separate HTTP client from the action’s
fetch()API and do not count toward the per-action fetch limit. However, they share the same execution timeout. - Module size — Individual modules are capped at 10 MB.
Debugging Imports
UseLit.Actions.showImportDetails() to inspect which modules were loaded and their integrity hashes. This is useful for debugging import resolution, verifying that the expected modules were fetched, and auditing the integrity of imported code.
console.log output in the response logs.
Next Steps
- Examples — More action patterns using the built-in SDK
- Patterns — Advanced patterns like gating logic and action-identity signing
- Lit Actions SDK — Full API reference