Extension sandbox
Extension UI code runs in a dedicated Web Worker inside a sandboxed runtime iframe. Every extension shares the runtime's origin, so any browser API scoped to that origin would let one extension read or overwrite another extension's data. The platform therefore removes these APIs before your bundle is evaluated.
Blocked APIs
indexedDB, caches, BroadcastChannel and the other Worker-global APIs listed below are blocked in the extension Worker. Code or third-party libraries that use them throw a SecurityError on first access. Move that state to useExtensionStorage.
Worker global
Reading or assigning any of these throws a SecurityError ("<name> is not available in the Lumio extension sandbox"). Even typeof indexedDB throws, because it reads the property.
| API | Why it is blocked |
|---|---|
indexedDB | Origin-scoped database, shared by all extensions |
caches (CacheStorage) | Origin-scoped cache, shared by all extensions |
BroadcastChannel | Messages reach every context on the shared origin |
navigator.storage | Quota, persistence and the origin-private file system |
navigator.locks | Web Locks are shared by every context on the origin |
navigator.serviceWorker | Service workers are registered per origin |
navigator.storageBuckets | Each Storage Bucket opens its own indexedDB, caches and file system, shared across the origin |
Worker, SharedWorker | A nested worker would start with an unrestricted global |
webkitRequestFileSystem*, webkitResolveLocalFileSystem*URL | Legacy origin-scoped file systems |
The blocks cannot be undone: the properties are non-configurable, and the original accessors on the prototypes are replaced too.
Network
The Worker's Content Security Policy blocks network requests from extension UI code: fetch, XMLHttpRequest, WebSocket, EventSource and importScripts from other origins all fail. Read data through server functions, which can call declared egress hosts with ctx.fetch().
Runtime frame
The runtime iframe that hosts the Worker also blocks localStorage, sessionStorage, indexedDB, caches and BroadcastChannel, and document.cookie always reads as "" (writes are dropped). Extension code does not run in that frame, but the restriction is listed here for completeness.
What to use instead
| You need | Use |
|---|---|
| State that survives a reload, shared by all surfaces of your extension | useExtensionStorage |
| Data your server functions own | Server functions (query() / mutation()) |
| Values configured by the streamer | useLumioConfig |
| Short-lived state in one surface | React state (useState, useReducer) or module variables |
Detecting the sandbox in libraries
A library that probes for storage with typeof indexedDB !== "undefined" throws instead of falling back. Wrap the probe:
function hasIndexedDb(): boolean {
try {
return typeof indexedDB !== "undefined";
} catch {
return false;
}
}
If you cannot change the library, configure it for in-memory storage or pick one without a persistence layer.