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:

  1. Cross-Origin Isolation Enforcement via COOP/COEP HTTP response headers
  2. Secure Execution Context requiring HTTPS or localhost origins
  3. 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:// origins
  • localhost (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:

  1. Synchronous Access Handles (SAH) being worker-only APIs
  2. 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:

  1. Send COOP/COEP headers for all responses
  2. Enable SharedArrayBuffer support via:
Cross-Origin-Embedder-Policy: require-corp
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Resource-Policy: same-site
  1. Serve worker scripts with application/javascript MIME type
  2. 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:

  1. Main document
  2. Worker script (worker.sql-wasm.js)
  3. WASM binary (sqlite3.wasm)
  4. 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 be true in about: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:

  1. Develop → Experimental Features → FileSystem API
  2. 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:

  1. Add localhost exception to antivirus web shields
  2. 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

  1. Daily Header Audit:
# Use Mozilla Observatory scan
curl -X POST https://http-observatory.security.mozilla.org/api/v1/analyze?host=localhost:8080
  1. Worker Context Sanity Test:
// In all worker scripts
if (!self.crossOriginIsolated) {
  throw new Error('Worker not cross-origin isolated');
}
  1. 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

ComponentMinimum VersionOPFS Critical Fixes
Chromium103SyncAccessHandle
Firefox104OPFS worker support
Safari16.4Experimental enable
SQLite WASM3.45.0OPFS VFS stability
Emscripten3.1.46OPFS async->sync
althttpd2022-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.

Related Guides

Leave a Reply

Your email address will not be published. Required fields are marked *