Persistent OPFS VFS Unavailable in SQLite WASM Browser Tests
Core Requirements for Enabling OPFS VFS in SQLite WASM Environments
Understanding OPFS Availability Failures in Browser-Based SQLite
The inability to access Origin-Private File System (OPFS) APIs via SQLite’s WebAssembly (WASM) build manifests as skipped test groups when running browser-based test suites like test1-worker.html
. This occurs when the JavaScript environment fails to meet three critical prerequisites for OPFS integration:
- Cross-Origin Isolation Enforcement via COOP/COEP HTTP response headers
- Secure Execution Context requiring HTTPS or localhost origins
- Web Worker Execution mandating OPFS API calls from dedicated worker threads
The SQLite WASM build’s OPFS virtual file system (VFS) layer requires explicit browser security permissions due to OPFS being a high-privilege storage API. When any prerequisite isn’t met, the runtime environment hides OPFS APIs from JavaScript – a security measure enforced at the browser engine level. This results in skipped tests due to missing VFS components, even when the underlying browser supports OPFS in theory.
Common Failure Modes Blocking OPFS VFS Initialization
Cross-Origin Resource Policy Configuration Errors
The #1 root cause of OPFS unavailability stems from incorrect delivery of Cross-Origin-Opener-Policy (COOP) and Cross-Origin-Embedder-Policy (COEP) HTTP headers. These headers must be set with exact values:
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
Deviation from these exact header values (including case variations) or omission of either header disables cross-origin isolation. Servers often fail to propagate these headers for all required resources – not just the main document. This includes worker scripts, WASM binaries, and any imported JavaScript modules.
Insecure Transport Layer Contexts
Browsers restrict OPFS access to secure contexts defined as:
https://
originslocalhost
(IPv4 127.0.0.1, IPv6 ::1)file://
URLs (with limitations)
Developers testing via non-localhost HTTP (e.g., http://192.168.1.5:8080
) trigger secure context violations. Firefox enforces this more strictly than Chromium in some cases.
Web Worker Execution Chain Failures
The OPFS VFS requires initialization exclusively within Web Workers due to:
- Synchronous Access Handles (SAH) being worker-only APIs
- Main thread OPFS operations being asynchronous and incompatible with SQLite’s synchronous VFS expectations
Attempts to access OPFS from the main browser thread will fail silently. Common pitfalls include:
- Loading SQLite WASM in the main document instead of a worker
- Worker scripts not properly initialized with COOP/COEP headers
- Race conditions in worker initialization sequencing
Server Configuration Deficiencies
HTTP servers must be explicitly configured to:
- Send COOP/COEP headers for all responses
- Enable SharedArrayBuffer support via:
Cross-Origin-Embedder-Policy: require-corp
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Resource-Policy: same-site
- Serve worker scripts with
application/javascript
MIME type - Allow CORS preflight OPTIONS requests for worker resources
The simple-http-server
Rust crate and Python’s http.server
often require middleware extensions to meet these requirements. Outdated althttpd
versions (< Sept 2022) lack the -enable-sab
flag needed for SharedArrayBuffer/COOP/COEP support.
Browser Feature Detection False Negatives
Modern browsers implement OPFS behind runtime feature checks that consider:
- User activation status (e.g., click events)
- Permission policies
- Storage access API grants
- iframe sandboxing flags
Automated test suites running without user gestures may encounter false negatives. Browser extensions like ad blockers or privacy tools sometimes interfere with OPFS availability checks.
Comprehensive Validation Protocol for OPFS Readiness
Phase 1: Transport Layer Security Audit
Step 1.1 – Confirm Secure Context
// In browser console:
console.log('Secure context:', window.isSecureContext);
If false
, check:
- Page loaded over HTTPS or localhost
- No mixed content (HTTP resources on HTTPS page)
- Certificate validity (for self-signed certs)
Step 1.2 – Validate Origin Privacy
(async () => {
try {
const root = await navigator.storage.getDirectory();
console.log('OPFS available:', root instanceof FileSystemDirectoryHandle);
} catch(e) {
console.error('OPFS check failed:', e);
}
})();
Failure here indicates missing COOP/COEP or insecure context.
Phase 2: HTTP Header Verification
Step 2.1 – Full Header Inspection
Use browser devtools Network tab to check headers for:
- Main document
- Worker script (
worker.sql-wasm.js
) - WASM binary (
sqlite3.wasm
) - Any imported ES modules
Required headers:
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
Cross-Origin-Resource-Policy: same-site
Step 2.2 – Command-Line Header Check
For local servers on port 8080:
curl -I http://localhost:8080/ --header "Origin: http://example.com"
Validate presence of:
access-control-allow-origin: *
timing-allow-origin: *
cross-origin-opener-policy: same-origin
cross-origin-embedder-policy: require-corp
Phase 3: Web Worker Configuration Testing
Step 3.1 – Worker Instantiation Protocol
Proper worker initialization requires:
// Main thread:
const worker = new Worker('/sql-worker.js', {
type: 'module', // If using ES modules
credentials: 'same-origin'
});
Step 3.2 – Worker Internal Checks
Within the worker script:
console.log('Worker secure context:', self.isSecureContext);
(async () => {
try {
const root = await navigator.storage.getDirectory();
postMessage({opfsAvailable: true});
} catch(e) {
postMessage({opfsAvailable: false, error: e.message});
}
})();
Phase 4: Server Software Configuration
For althttpd:
# Requires version ≥ 85b7ee71fa (Sep 2022)
althttpd -root /var/www -enable-sab -page index.html -port 8080
Flags:
-enable-sab
: Enables COOP/COEP headers-page
: Sets default document-user
: Run as unprivileged user
For simple-http-server (Rust):
// Add headers middleware in main.rs
fn add_security_headers(headers: &mut HeaderMap) {
headers.insert("Cross-Origin-Opener-Policy", "same-origin".parse().unwrap());
headers.insert("Cross-Origin-Embedder-Policy", "require-corp".parse().unwrap());
headers.insert("Access-Control-Allow-Origin", "*".parse().unwrap());
headers.insert("Timing-Allow-Origin", "*".parse().unwrap());
}
Phase 5: SQLite WASM Build Compatibility
Step 5.1 – VFS Registration Order
Confirm OPFS VFS initialization occurs before any database connections:
import sqlite3InitModule from './sqlite3.mjs';
const sqlite3 = await sqlite3InitModule({
print: console.log,
printErr: console.error
});
// Must occur before db open
await sqlite3.initializeOPFS();
Step 5.2 – Build Feature Flags
The SQLite WASM build must include:
-DSQLITE_ENABLE_OPFS
compile-time flag- Linked Emscripten OPFS VFS implementation
- SharedArrayBuffer polyfills for legacy browsers
Validate via:
console.log('OPFS VFS available:',
sqlite3.capi.sqlite3_vfs_find('opfs') !== null);
Phase 6: Browser-Specific Workarounds
Firefox Considerations:
dom.workers.serialized-sab-access
must betrue
inabout:config
- Enable
dom.storage_access.enabled
- Disable privacy.firstparty.isolate for localhost
Chromium Flags:
chromium --enable-features=SharedArrayBuffer
Safari Limitations:
As of March 2024, Safari requires experimental features enabled:
- Develop → Experimental Features → FileSystem API
webkitStoragePolicy
meta tag:
<meta name="webkit-storage-policy" content="allow">
Resolution Workflow for Persistent OPFS Access
Scenario 1: Missing COOP/COEP Headers
Symptom:
window.crossOriginIsolated
===false
- DevTools Network tab shows missing headers
Fix:
Configure server middleware to inject headers for all responses:
// Node.js Express example
app.use((req, res, next) => {
res.set({
'Cross-Origin-Opener-Policy': 'same-origin',
'Cross-Origin-Embedder-Policy': 'require-corp',
'Cross-Origin-Resource-Policy': 'same-site'
});
next();
});
Scenario 2: Worker Context Mismanagement
Symptom:
- OPFS available in main thread but not worker
- Worker fails
navigator.storage.getDirectory()
Fix:
Re-architect initialization flow:
// Main thread
const worker = new Worker('./opfs-worker.js', {name: 'sqlite-opfs'});
// opfs-worker.js
import {default as sqlite3Init} from './sqlite3-opfs.mjs';
self.addEventListener('message', async ({data}) => {
const sqlite3 = await sqlite3Init();
sqlite3.init_opfs_vfs();
const db = new sqlite3.oo1.OpfsDb('/test.db');
// ...queries...
});
Scenario 3: Antivirus/Firewall Interference
Symptom:
- Inconsistent OPFS availability across reloads
- Security software blocking SharedArrayBuffer
Mitigation:
- Add localhost exception to antivirus web shields
- Configure Windows Defender to exclude dev ports:
New-NetFirewallRule -DisplayName "Allow 8080" -Direction Inbound -LocalPort 8080 -Protocol TCP -Action Allow
Scenario 4: Incorrect SQLite WASM Loading
Symptom:
sqlite3.initializeOPFS is not a function
- Runtime errors about missing VFS methods
Resolution:
Use the official OPFS-enabled build from:
<script src="https://sqlite.org/.../sqlite3-opfs.mjs"></script>
And verify initialization order:
// Correct sequence
await sqlite3InitModule();
await sqlite3.initOPFS();
// Now open databases
Maintenance Protocol for OPFS-SQLite Deployments
Continuous Validation Checklist
- Daily Header Audit:
# Use Mozilla Observatory scan
curl -X POST https://http-observatory.security.mozilla.org/api/v1/analyze?host=localhost:8080
- Worker Context Sanity Test:
// In all worker scripts
if (!self.crossOriginIsolated) {
throw new Error('Worker not cross-origin isolated');
}
- Automated Feature Detection:
// During app startup
const opfsSupported = (
typeof SharedArrayBuffer !== 'undefined' &&
'storage' in navigator &&
'getDirectory' in navigator.storage
);
if (!opfsSupported) {
showWarning('OPFS unavailable - check browser support');
}
Version Compatibility Matrix
Component | Minimum Version | OPFS Critical Fixes |
---|---|---|
Chromium | 103 | SyncAccessHandle |
Firefox | 104 | OPFS worker support |
Safari | 16.4 | Experimental enable |
SQLite WASM | 3.45.0 | OPFS VFS stability |
Emscripten | 3.1.46 | OPFS async->sync |
althttpd | 2022-09-30 | -enable-sab flag |
Performance Tuning Parameters
OPFS VFS Configuration Options:
sqlite3.initOPFS({
durability: 'relaxed', // vs 'strict'
purgeOnStartup: false,
maxJournalSize: 8192,
sectorSize: 4096
});
IndexedDB Fallback Strategy:
try {
await sqlite3.initOPFS();
} catch (e) {
console.warn('OPFS failed, falling back to IDB');
sqlite3.vfs('idb');
}
Conclusion: Achieving Persistent Browser Storage via OPFS VFS
Successful integration of SQLite’s OPFS VFS requires meticulous attention to browser security policies, server header configurations, and worker thread management. By systematically validating each layer of the stack – from transport security to SQLite WASM initialization sequences – developers can unlock persistent, high-performance database storage in modern browsers. Regular audits using the provided verification protocols help maintain compatibility across browser updates and evolving web standards.