Sweeping rename across crates, CLI binary, WASM bindings, extension, docs,
and vault metadata paths. Git remote updated to relicario.git.
- crates/idfoto-{core,cli,wasm} -> crates/relicario-{core,cli,wasm}
- IdfotoError -> RelicarioError
- IDFOTO_IMAGE env var -> RELICARIO_IMAGE
- ~/.config/idfoto -> ~/.config/relicario
- .idfoto/ vault metadata dir -> .relicario/ (breaking; pre-release)
- Binary name idfoto -> relicario
- Extension wasm module idfoto_wasm -> relicario_wasm
- Storage key idfotoSettings -> relicarioSettings
- All doc filenames and content references updated
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
324 lines
8.9 KiB
Markdown
324 lines
8.9 KiB
Markdown
# 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": ["<all_urls>"],
|
|
"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": ["<all_urls>"],
|
|
"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<WasmModule> {
|
|
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.
|