# relicario Firefox Extension Port Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Port the Chrome extension to Firefox with shared TypeScript source, a Firefox-specific manifest, and a separate webpack build target. **Architecture:** All TypeScript source is shared. The only code change is an environment check in `index.ts` for WASM loading (service worker vs background script). A second webpack config produces `dist-firefox/` with the Firefox manifest. **Tech Stack:** TypeScript, webpack, Firefox WebExtensions MV3 **Spec:** `docs/superpowers/specs/2026-04-12-relicario-firefox-extension-design.md` --- ## File Structure ### New files ``` extension/manifest.firefox.json # Firefox-specific manifest extension/webpack.firefox.config.js # Webpack config for Firefox build ``` ### Modified files ``` extension/src/service-worker/index.ts # Environment-aware WASM loading extension/package.json # Add Firefox build scripts .gitignore # Add extension/dist-firefox/ ``` --- ## Task 1: Firefox Manifest and Webpack Config **Files:** - Create: `extension/manifest.firefox.json` - Create: `extension/webpack.firefox.config.js` - Modify: `extension/package.json` - Modify: `.gitignore` - [ ] **Step 1: Create Firefox manifest** Create `extension/manifest.firefox.json`: ```json { "manifest_version": 3, "name": "relicario", "version": "0.1.0", "description": "Two-factor encrypted password manager", "browser_specific_settings": { "gecko": { "id": "relicario@adlee.work", "strict_min_version": "128.0" } }, "permissions": ["storage", "activeTab", "clipboardWrite"], "host_permissions": [""], "background": { "scripts": ["service-worker.js"] }, "action": { "default_popup": "popup.html", "default_icon": { "16": "icons/icon-16.png", "48": "icons/icon-48.png", "128": "icons/icon-128.png" } }, "content_scripts": [ { "matches": [""], "js": ["content.js"], "run_at": "document_idle" } ], "content_security_policy": { "extension_pages": "script-src 'self' 'wasm-unsafe-eval'; object-src 'self'" }, "web_accessible_resources": [ { "resources": [ "setup.html", "setup.js", "styles.css", "relicario_wasm_bg.wasm", "relicario_wasm.js" ] } ] } ``` - [ ] **Step 2: Create Firefox webpack config** Create `extension/webpack.firefox.config.js`: ```javascript const path = require('path'); const CopyPlugin = require('copy-webpack-plugin'); module.exports = { entry: { 'service-worker': './src/service-worker/index.ts', popup: './src/popup/popup.ts', content: './src/content/detector.ts', setup: './src/setup/setup.ts', }, output: { path: path.resolve(__dirname, 'dist-firefox'), filename: '[name].js', clean: true, }, resolve: { extensions: ['.ts', '.js'], }, module: { rules: [{ test: /\.ts$/, use: 'ts-loader', exclude: /node_modules/ }], }, plugins: [ new CopyPlugin({ patterns: [ { from: 'manifest.firefox.json', to: 'manifest.json' }, { from: 'src/popup/index.html', to: 'popup.html' }, { from: 'src/popup/styles.css', to: 'styles.css' }, { from: 'setup.html', to: '.' }, { from: 'icons', to: 'icons' }, { from: 'wasm/relicario_wasm_bg.wasm', to: '.' }, { from: 'wasm/relicario_wasm.js', to: '.' }, ], }), ], experiments: { asyncWebAssembly: true }, }; ``` - [ ] **Step 3: Add Firefox build scripts to package.json** In `extension/package.json`, update the `scripts` section: ```json { "scripts": { "build": "webpack --mode production", "build:firefox": "webpack --config webpack.firefox.config.js --mode production", "build:all": "npm run build:wasm && npm run build && npm run build:firefox", "dev": "webpack --mode development --watch", "dev:firefox": "webpack --config webpack.firefox.config.js --mode development --watch", "build:wasm": "wasm-pack build ../crates/relicario-wasm --target web --out-dir ../../extension/wasm" } } ``` - [ ] **Step 4: Add `dist-firefox/` to `.gitignore`** Append to the root `.gitignore`: ``` extension/dist-firefox/ ``` - [ ] **Step 5: Commit** ```bash git add extension/manifest.firefox.json extension/webpack.firefox.config.js extension/package.json .gitignore git commit -m "feat: add Firefox manifest and webpack config" ``` --- ## Task 2: Environment-Aware WASM Loading **Files:** - Modify: `extension/src/service-worker/index.ts` - [ ] **Step 1: Update the WASM init function** In `extension/src/service-worker/index.ts`, replace the current `initWasm` function and its surrounding comments (lines 23-49) with: ```typescript // --- WASM initialization --- // Chrome MV3 uses service workers which do NOT support dynamic import(). // Firefox MV3 uses background scripts which DO support dynamic import(). // We detect the environment at runtime and use the appropriate loading strategy. // // The JS glue is imported statically so webpack bundles it. Both initSync // (Chrome) and the default export (Firefox) are available. // @ts-ignore TS2307 — resolved by webpack alias / copy import initDefault, { initSync } from '../../wasm/relicario_wasm.js'; // @ts-ignore TS2307 import * as wasmBindings from '../../wasm/relicario_wasm.js'; type WasmModule = typeof wasmBindings; let wasm: WasmModule | null = null; async function initWasm(): Promise { if (wasm) return wasm; const isServiceWorker = typeof ServiceWorkerGlobalScope !== 'undefined' && self instanceof ServiceWorkerGlobalScope; if (isServiceWorker) { // Chrome: fetch WASM binary and instantiate synchronously const wasmResponse = await fetch(chrome.runtime.getURL('relicario_wasm_bg.wasm')); const wasmBytes = await wasmResponse.arrayBuffer(); initSync({ module: new WebAssembly.Module(wasmBytes) }); } else { // Firefox: background script — dynamic init works const wasmUrl = chrome.runtime.getURL('relicario_wasm_bg.wasm'); await initDefault(wasmUrl); } vault.setWasm(wasmBindings); wasm = wasmBindings; wasmReady = true; return wasm; } ``` - [ ] **Step 2: Update the module doc comment** Change the doc comment at the top of the file (line 1) from: ```typescript /// Service worker entry point for the relicario Chrome extension. ``` To: ```typescript /// Background script entry point for the relicario browser extension. /// /// In Chrome this runs as a service worker (MV3). In Firefox this runs /// as a persistent background script. WASM loading adapts automatically. ``` - [ ] **Step 3: Build both targets** ```bash cd extension && bun run build && bun run build:firefox ``` Expected: Both builds succeed with 0 errors. - [ ] **Step 4: Commit** ```bash git add extension/src/service-worker/index.ts git commit -m "feat: add environment-aware WASM loading for Chrome/Firefox" ``` --- ## Task 3: Build and Manual Test **Files:** None (integration testing) - [ ] **Step 1: Verify Chrome build still works** ```bash cd extension && bun run build ``` Expected: `dist/` output, 0 errors. Reload in Chrome — unlock, list, autofill all work. - [ ] **Step 2: Build Firefox** ```bash bun run build:firefox ``` Expected: `dist-firefox/` output with `manifest.json` (Firefox version), all JS bundles, WASM files, icons. - [ ] **Step 3: Verify Firefox manifest** ```bash cat dist-firefox/manifest.json | grep -E "gecko|background" ``` Expected: `browser_specific_settings.gecko.id` present, `background.scripts` (not `service_worker`). - [ ] **Step 4: Load in Firefox** 1. Open Firefox 2. Navigate to `about:debugging#/runtime/this-firefox` 3. Click "Load Temporary Add-on..." 4. Select `extension/dist-firefox/manifest.json` 5. Extension icon appears in toolbar - [ ] **Step 5: Test basic flow** 1. Click extension icon — popup opens, shows setup prompt (or unlock if already configured) 2. Open `setup.html` via the setup button — full-page wizard loads 3. Configure vault (or verify it's already configured from Chrome) 4. Unlock with passphrase — entry list appears 5. Navigate entries, check TOTP countdown works 6. Visit a login page — field icon appears 7. Test autofill 8. If credential capture is enabled, test save prompt appears on form submit - [ ] **Step 6: Fix any Firefox-specific issues** - [ ] **Step 7: Final commit** ```bash git add -A git commit -m "feat: complete Firefox extension port" ``` --- ## Task Summary | Task | Description | Dependencies | |------|-------------|--------------| | 1 | Firefox manifest + webpack config + scripts | None | | 2 | Environment-aware WASM loading | None | | 3 | Build and manual test | Tasks 1, 2 | Tasks 1 and 2 are independent and can run in parallel.