chore: rename project from idfoto to relicario
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>
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
# idfoto — Design Specification
|
||||
# relicario — Design Specification
|
||||
|
||||
A git-backed, self-hostable password manager with a Rust core, CLI, and Chrome browser extension. The reference image as a DCT-embedded secret carrier is the core differentiator.
|
||||
|
||||
## Overview
|
||||
|
||||
idfoto is a password manager where vault decryption requires two independent factors: a passphrase the user memorizes and a reference JPEG that carries a 256-bit secret embedded via DCT steganography. The vault lives in a git repository (self-hosted on the user's own Gitea instance), and the server only ever sees opaque ciphertext. Compromise of either factor alone is insufficient to decrypt the vault.
|
||||
relicario is a password manager where vault decryption requires two independent factors: a passphrase the user memorizes and a reference JPEG that carries a 256-bit secret embedded via DCT steganography. The vault lives in a git repository (self-hosted on the user's own Gitea instance), and the server only ever sees opaque ciphertext. Compromise of either factor alone is insufficient to decrypt the vault.
|
||||
|
||||
Primary goals: portfolio project for adlee.work, architectural elegance, legibility-as-security (the README should read as the security proof), learning Rust, and fun to tinker with.
|
||||
|
||||
@@ -23,7 +23,7 @@ A collection of credentials (usernames, passwords, URLs, TOTP seeds, notes) belo
|
||||
| Stolen device | Filesystem: reference image, device key, cached vault | Decrypt vault | Attacker has image_secret but not passphrase. Argon2id makes brute-force expensive. |
|
||||
| Stolen device + weak passphrase | Same + feasible brute-force | Decrypt vault | Enforce minimum passphrase strength at vault creation. Universal worst case. |
|
||||
| Shoulder surfer | Observed passphrase | Decrypt vault (if they also get image) | Passphrase alone insufficient — still need image_secret. |
|
||||
| Credential stuffing | Leaked email/password from other breaches | Access user's accounts | idfoto generates unique passwords per site. Breach of site A doesn't compromise site B. |
|
||||
| Credential stuffing | Leaked email/password from other breaches | Access user's accounts | relicario generates unique passwords per site. Breach of site A doesn't compromise site B. |
|
||||
|
||||
### Out of scope
|
||||
|
||||
@@ -50,7 +50,7 @@ passphrase (user types, UTF-8 encoded)
|
||||
▼
|
||||
Argon2id(
|
||||
password = passphrase_bytes || image_secret_bytes, // concatenated, 32-byte secret appended
|
||||
salt = vault_salt, // 32 bytes, from .idfoto/salt
|
||||
salt = vault_salt, // 32 bytes, from .relicario/salt
|
||||
memory = 64 MiB,
|
||||
iterations = 3,
|
||||
parallelism = 4,
|
||||
@@ -79,7 +79,7 @@ With a 4-word diceware passphrase (~51 bits) and Argon2id at 64 MiB, brute-force
|
||||
Compared to competitors:
|
||||
- LastPass/Bitwarden: server breach exposes ~40-60 bits (master password only)
|
||||
- 1Password: server breach exposes password + 128-bit Secret Key
|
||||
- idfoto: server breach exposes password + 256-bit image_secret
|
||||
- relicario: server breach exposes password + 256-bit image_secret
|
||||
|
||||
### Authenticated encryption
|
||||
|
||||
@@ -103,7 +103,7 @@ Nonce is generated fresh (CSPRNG) on every write. Version byte allows future for
|
||||
|
||||
### KDF parameters
|
||||
|
||||
Stored in `.idfoto/params.json` (plaintext, committed). Configurable per-vault:
|
||||
Stored in `.relicario/params.json` (plaintext, committed). Configurable per-vault:
|
||||
- Default: `argon2_m=65536` (64 MiB), `argon2_t=3`, `argon2_p=4`
|
||||
- Users can increase for CLI-only use on powerful hardware
|
||||
- Enables future parameter upgrades without format changes
|
||||
@@ -177,13 +177,13 @@ Caller must normalize EXIF orientation before passing JPEG to embed/extract. EXI
|
||||
## Vault Format & Repo Layout
|
||||
|
||||
```
|
||||
idfoto-vault/
|
||||
relicario-vault/
|
||||
├── manifest.enc # encrypted JSON: entry index, vault metadata
|
||||
├── entries/
|
||||
│ ├── a1b2c3d4.enc # one encrypted entry per file, random hex ID
|
||||
│ ├── e5f6a7b8.enc
|
||||
│ └── ...
|
||||
└── .idfoto/
|
||||
└── .relicario/
|
||||
├── salt # 32 bytes, plaintext (prevents precomputation)
|
||||
├── params.json # Argon2id parameters, plaintext
|
||||
└── devices.json # authorized device ed25519 public keys, plaintext
|
||||
@@ -226,7 +226,7 @@ Flat schema. No nested objects, no folders, no tags for V1. Entry IDs are random
|
||||
|
||||
### Plaintext metadata
|
||||
|
||||
Stored in `.idfoto/` and committed to the repo:
|
||||
Stored in `.relicario/` and committed to the repo:
|
||||
- `salt`: 32 random bytes, generated once at vault creation
|
||||
- `params.json`: Argon2id tuning knobs (memory, iterations, parallelism, format version)
|
||||
- `devices.json`: list of authorized device ed25519 public keys, used to verify commit signatures
|
||||
@@ -244,20 +244,20 @@ Preserved as-is. Every add/edit/rm is a commit. Provides "when was this password
|
||||
## Crate Layout
|
||||
|
||||
```
|
||||
idfoto/
|
||||
relicario/
|
||||
├── Cargo.toml # workspace root
|
||||
├── crates/
|
||||
│ ├── idfoto-core/ # library: imgsecret, KDF, vault format
|
||||
│ ├── relicario-core/ # library: imgsecret, KDF, vault format
|
||||
│ │ └── src/
|
||||
│ │ ├── lib.rs
|
||||
│ │ ├── imgsecret.rs
|
||||
│ │ ├── kdf.rs
|
||||
│ │ ├── vault.rs
|
||||
│ │ └── entry.rs
|
||||
│ ├── idfoto-cli/ # binary: the `idfoto` CLI
|
||||
│ ├── relicario-cli/ # binary: the `relicario` CLI
|
||||
│ │ └── src/
|
||||
│ │ └── main.rs
|
||||
│ └── idfoto-wasm/ # wasm-bindgen wrapper around core
|
||||
│ └── relicario-wasm/ # wasm-bindgen wrapper around core
|
||||
│ └── src/
|
||||
│ └── lib.rs
|
||||
├── extension/ # TypeScript Chrome MV3 extension
|
||||
@@ -271,14 +271,14 @@ idfoto/
|
||||
|
||||
### Design principles
|
||||
|
||||
- **`idfoto-core` is platform-agnostic.** No filesystem, no git, no network. Takes bytes, returns bytes. This makes it trivially portable to WASM, Android (via JNI), iOS (via Swift bridge).
|
||||
- **`idfoto-cli`** is the platform layer. Handles filesystem, git operations (shells out to `git`), clipboard, terminal I/O.
|
||||
- **`idfoto-wasm`** is a thin wasm-bindgen wrapper exposing core functions to JavaScript.
|
||||
- **`relicario-core` is platform-agnostic.** No filesystem, no git, no network. Takes bytes, returns bytes. This makes it trivially portable to WASM, Android (via JNI), iOS (via Swift bridge).
|
||||
- **`relicario-cli`** is the platform layer. Handles filesystem, git operations (shells out to `git`), clipboard, terminal I/O.
|
||||
- **`relicario-wasm`** is a thin wasm-bindgen wrapper exposing core functions to JavaScript.
|
||||
- **`extension/`** is TypeScript/MV3. Loads the WASM module, runs crypto inline (no native messaging bridge).
|
||||
|
||||
### Rust crate dependencies (expected)
|
||||
|
||||
**idfoto-core:**
|
||||
**relicario-core:**
|
||||
- `argon2` — Argon2id KDF
|
||||
- `chacha20poly1305` — XChaCha20-Poly1305 AEAD
|
||||
- `sha2` — SHA-256 for hashing
|
||||
@@ -290,44 +290,44 @@ idfoto/
|
||||
- `ed25519-dalek` — device key signing (used by CLI, exposed via core)
|
||||
- `thiserror` — error types
|
||||
|
||||
**idfoto-cli:**
|
||||
**relicario-cli:**
|
||||
- `clap` (derive) — argument parsing
|
||||
- `anyhow` — CLI error handling
|
||||
- `rpassword` — passphrase prompt without echo
|
||||
- `arboard` or `cli-clipboard` — clipboard access
|
||||
- `dirs` — platform config/data directories
|
||||
|
||||
**idfoto-wasm:**
|
||||
**relicario-wasm:**
|
||||
- `wasm-bindgen` — JS interop
|
||||
- `js-sys`, `web-sys` — browser APIs
|
||||
|
||||
## CLI Commands
|
||||
|
||||
```
|
||||
idfoto init # Create vault: generate salt, prompt for passphrase,
|
||||
relicario init # Create vault: generate salt, prompt for passphrase,
|
||||
# prompt for carrier image, embed image_secret,
|
||||
# output reference JPEG, git init + first commit
|
||||
|
||||
idfoto add # Prompt for entry fields, encrypt, commit
|
||||
idfoto get <name> # Case-insensitive substring match on name/URL, decrypt, copy password to clipboard (30s TTL)
|
||||
idfoto list # Decrypt manifest, print entry names/URLs
|
||||
idfoto edit <name> # Decrypt entry, prompt for changes, re-encrypt, commit
|
||||
idfoto rm <name> # Remove entry file, update manifest, commit
|
||||
idfoto sync # git pull --rebase && git push
|
||||
idfoto generate # Generate a random password (utility, no vault interaction)
|
||||
relicario add # Prompt for entry fields, encrypt, commit
|
||||
relicario get <name> # Case-insensitive substring match on name/URL, decrypt, copy password to clipboard (30s TTL)
|
||||
relicario list # Decrypt manifest, print entry names/URLs
|
||||
relicario edit <name> # Decrypt entry, prompt for changes, re-encrypt, commit
|
||||
relicario rm <name> # Remove entry file, update manifest, commit
|
||||
relicario sync # git pull --rebase && git push
|
||||
relicario generate # Generate a random password (utility, no vault interaction)
|
||||
|
||||
idfoto device add # Generate ed25519 keypair, add pubkey to devices.json, commit
|
||||
idfoto device list # List authorized devices
|
||||
idfoto device revoke <name> # Remove device from devices.json, commit
|
||||
relicario device add # Generate ed25519 keypair, add pubkey to devices.json, commit
|
||||
relicario device list # List authorized devices
|
||||
relicario device revoke <name> # Remove device from devices.json, commit
|
||||
```
|
||||
|
||||
Unlock flow: on any command that needs the vault, the CLI prompts for the passphrase and the reference image path (or uses a configured default path). Derives master_key, holds it in memory for the duration of the command, then drops it. No persistent daemon for V1 — each invocation re-derives.
|
||||
|
||||
Future: `idfoto unlock` could spawn a background agent (ssh-agent-style) that holds the key for a configurable TTL, so subsequent commands don't re-prompt.
|
||||
Future: `relicario unlock` could spawn a background agent (ssh-agent-style) that holds the key for a configurable TTL, so subsequent commands don't re-prompt.
|
||||
|
||||
## Chrome Extension Architecture
|
||||
|
||||
The Chrome MV3 extension loads `idfoto-wasm` directly — no native messaging bridge.
|
||||
The Chrome MV3 extension loads `relicario-wasm` directly — no native messaging bridge.
|
||||
|
||||
- **Service worker:** initializes the WASM module, holds the master_key in memory after unlock, handles vault operations
|
||||
- **Popup:** passphrase prompt, entry list/search, entry detail view
|
||||
@@ -343,16 +343,16 @@ Extension design details (popup UI, content script heuristics, autofill flow) ar
|
||||
|
||||
Not in V1 scope. Planned approach:
|
||||
|
||||
- `idfoto export-recovery` generates a small encrypted file containing only the `image_secret` (32 bytes + metadata), locked with the passphrase alone (separate Argon2id derivation)
|
||||
- `relicario export-recovery` generates a small encrypted file containing only the `image_secret` (32 bytes + metadata), locked with the passphrase alone (separate Argon2id derivation)
|
||||
- User stores this file offline (USB drive, printed QR, safe deposit box)
|
||||
- Recovery: `idfoto recover --file recovery.enc` + passphrase → recovers image_secret → can decrypt vault from git
|
||||
- Recovery: `relicario recover --file recovery.enc` + passphrase → recovers image_secret → can decrypt vault from git
|
||||
- This is a second backup path alongside the "dead drop" reference JPEG (which can live on social media, personal website, etc.)
|
||||
|
||||
## Post-V1 Ideas
|
||||
|
||||
- **Secure notes:** free-form encrypted text entries (no URL/username/password schema, just a title + body). Same encryption, same repo layout — just a different entry type field.
|
||||
- **Secure document storage:** encrypted file attachments up to 5-10 MB per entry. Stored as separate `.enc` blobs in an `attachments/` directory, referenced by entry ID. Git handles large binary blobs tolerably at this scale; git-lfs is an option if vaults grow beyond ~100 MB total.
|
||||
- **`idfoto unlock` daemon:** ssh-agent-style background process that holds master_key for a configurable TTL, so repeated CLI commands don't re-prompt for passphrase.
|
||||
- **`relicario unlock` daemon:** ssh-agent-style background process that holds master_key for a configurable TTL, so repeated CLI commands don't re-prompt for passphrase.
|
||||
- **Mobile clients (Android/iOS):** Rust core compiles to ARM. Thin native wrappers (Kotlin/Swift) deferred.
|
||||
- **Import from LastPass/Bitwarden/1Password**
|
||||
- **Firefox/Safari extensions**
|
||||
@@ -1,4 +1,4 @@
|
||||
# idfoto — Credential Capture Design
|
||||
# relicario — Credential Capture Design
|
||||
|
||||
Experimental feature that detects login form submissions and prompts the user to save or update credentials in the vault. Configurable prompt style (notification bar or toast). Off by default.
|
||||
|
||||
@@ -60,7 +60,7 @@ A fixed-position bar at the top of the page, injected into the DOM:
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────────┐
|
||||
│ idfoto: Save login for github.com? (alee) [Save] [Never] [✕] │
|
||||
│ relicario: Save login for github.com? (alee) [Save] [Never] [✕] │
|
||||
└──────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
@@ -77,7 +77,7 @@ A floating element in the bottom-right corner:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────┐
|
||||
│ idfoto │
|
||||
│ relicario │
|
||||
│ Save login for github.com? │
|
||||
│ alee │
|
||||
│ [Save] [Never] [✕] │
|
||||
@@ -103,7 +103,7 @@ When user clicks:
|
||||
Stored in `chrome.storage.local` under key `settings`:
|
||||
|
||||
```typescript
|
||||
interface IdfotoSettings {
|
||||
interface RelicarioSettings {
|
||||
captureEnabled: boolean; // default: false
|
||||
captureStyle: 'bar' | 'toast'; // default: 'bar'
|
||||
}
|
||||
@@ -138,7 +138,7 @@ The toggle and style selector write to `chrome.storage.local`. Blacklist entries
|
||||
| { type: 'check_credential'; url: string; username: string; password: string }
|
||||
| { type: 'blacklist_site'; hostname: string }
|
||||
| { type: 'get_settings' }
|
||||
| { type: 'update_settings'; settings: Partial<IdfotoSettings> }
|
||||
| { type: 'update_settings'; settings: Partial<RelicarioSettings> }
|
||||
| { type: 'get_blacklist' }
|
||||
| { type: 'remove_blacklist'; hostname: string }
|
||||
|
||||
@@ -161,7 +161,7 @@ extension/src/popup/components/settings.ts # Settings view
|
||||
extension/src/content/detector.ts # Import and init capture module
|
||||
extension/src/service-worker/index.ts # Handle new message types
|
||||
extension/src/shared/messages.ts # Add new Request/Response types
|
||||
extension/src/shared/types.ts # Add IdfotoSettings interface
|
||||
extension/src/shared/types.ts # Add RelicarioSettings interface
|
||||
extension/src/popup/popup.ts # Add 'settings' view to state machine
|
||||
extension/src/popup/components/unlock.ts # Wire up settings button
|
||||
```
|
||||
@@ -1,4 +1,4 @@
|
||||
# idfoto — Firefox Extension Port Design
|
||||
# relicario — Firefox Extension Port Design
|
||||
|
||||
Port the existing Chrome MV3 extension to Firefox. Shared TypeScript source, separate manifests, separate build outputs. No code changes to components, popup, or content script.
|
||||
|
||||
@@ -40,12 +40,12 @@ Firefox supports the `chrome.*` namespace for WebExtension APIs, so no `browser.
|
||||
```json
|
||||
{
|
||||
"manifest_version": 3,
|
||||
"name": "idfoto",
|
||||
"name": "relicario",
|
||||
"version": "0.1.0",
|
||||
"description": "Two-factor encrypted password manager",
|
||||
"browser_specific_settings": {
|
||||
"gecko": {
|
||||
"id": "idfoto@adlee.work",
|
||||
"id": "relicario@adlee.work",
|
||||
"strict_min_version": "128.0"
|
||||
}
|
||||
},
|
||||
@@ -71,7 +71,7 @@ Firefox supports the `chrome.*` namespace for WebExtension APIs, so no `browser.
|
||||
"extension_pages": "script-src 'self' 'wasm-unsafe-eval'; object-src 'self'"
|
||||
},
|
||||
"web_accessible_resources": [{
|
||||
"resources": ["setup.html", "setup.js", "styles.css", "idfoto_wasm_bg.wasm", "idfoto_wasm.js"]
|
||||
"resources": ["setup.html", "setup.js", "styles.css", "relicario_wasm_bg.wasm", "relicario_wasm.js"]
|
||||
}]
|
||||
}
|
||||
```
|
||||
@@ -94,12 +94,12 @@ async function initWasm(): Promise<WasmModule> {
|
||||
|
||||
if (typeof ServiceWorkerGlobalScope !== 'undefined') {
|
||||
// Chrome MV3: service worker context — use initSync
|
||||
const wasmResponse = await fetch(chrome.runtime.getURL('idfoto_wasm_bg.wasm'));
|
||||
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 context — dynamic import works
|
||||
const wasmUrl = chrome.runtime.getURL('idfoto_wasm_bg.wasm');
|
||||
const wasmUrl = chrome.runtime.getURL('relicario_wasm_bg.wasm');
|
||||
await initDefault(wasmUrl);
|
||||
}
|
||||
|
||||
@@ -130,7 +130,7 @@ Identical to `webpack.config.js` except:
|
||||
"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/idfoto-wasm --target web --out-dir ../../extension/wasm"
|
||||
"build:wasm": "wasm-pack build ../crates/relicario-wasm --target web --out-dir ../../extension/wasm"
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -1,6 +1,6 @@
|
||||
# idfoto — Standalone Vault Initialization Wizard Design
|
||||
# relicario — Standalone Vault Initialization Wizard Design
|
||||
|
||||
A browser-based wizard that guides new users through creating an idfoto vault from scratch. Lives at `extension/setup.html`, uses the same WASM module as the extension, same terminal dark aesthetic. No server, no Rust toolchain required.
|
||||
A browser-based wizard that guides new users through creating an relicario vault from scratch. Lives at `extension/setup.html`, uses the same WASM module as the extension, same terminal dark aesthetic. No server, no Rust toolchain required.
|
||||
|
||||
## Scope
|
||||
|
||||
@@ -37,7 +37,7 @@ Step includes a "Next" button. No validation needed at this step.
|
||||
|
||||
Fields:
|
||||
- Host URL (e.g. `https://git.adlee.work` or `https://github.com`) — pre-filled based on host type selection
|
||||
- Repository path (e.g. `alee/idfoto-vault`)
|
||||
- Repository path (e.g. `alee/relicario-vault`)
|
||||
- API token (password field)
|
||||
|
||||
"Test Connection" button:
|
||||
@@ -58,15 +58,15 @@ Two inputs:
|
||||
|
||||
1. Load WASM module
|
||||
2. Generate random 32-byte `image_secret` via `crypto.getRandomValues()`
|
||||
3. Embed secret into carrier JPEG via WASM `extract_image_secret` — wait, that's extract. We need `embed`. Check: the WASM crate currently only exposes `extract_image_secret`, not `embed`. **We need to add a `embed_image_secret` function to `idfoto-wasm`.**
|
||||
3. Embed secret into carrier JPEG via WASM `extract_image_secret` — wait, that's extract. We need `embed`. Check: the WASM crate currently only exposes `extract_image_secret`, not `embed`. **We need to add a `embed_image_secret` function to `relicario-wasm`.**
|
||||
4. Generate random 32-byte `salt` via `crypto.getRandomValues()`
|
||||
5. Create `params.json` with default KDF params (`{"argon2_m":65536,"argon2_t":3,"argon2_p":4}`)
|
||||
6. Derive `master_key` via WASM `derive_master_key(passphrase, image_secret, salt, params_json)`
|
||||
7. Encrypt empty manifest (`{"entries":{},"version":1}`) via WASM `encrypt_manifest`
|
||||
8. Push files to repo via git API:
|
||||
- `.idfoto/salt` (raw 32 bytes)
|
||||
- `.idfoto/params.json` (JSON string)
|
||||
- `.idfoto/devices.json` (`[]`)
|
||||
- `.relicario/salt` (raw 32 bytes)
|
||||
- `.relicario/params.json` (JSON string)
|
||||
- `.relicario/devices.json` (`[]`)
|
||||
- `manifest.enc` (encrypted manifest bytes)
|
||||
9. Show progress bar during push operations
|
||||
|
||||
@@ -81,20 +81,20 @@ Two things happen:
|
||||
- Show warning: "Keep this image safe. You need it alongside your passphrase to unlock the vault. Store it somewhere you won't lose it."
|
||||
|
||||
**Push config to extension (if available):**
|
||||
- Try to detect the idfoto extension via `chrome.runtime.sendMessage` with a `get_setup_state` message
|
||||
- Try to detect the relicario extension via `chrome.runtime.sendMessage` with a `get_setup_state` message
|
||||
- If extension responds: push `save_setup` message with `{ config: { hostType, hostUrl, repoPath, apiToken }, imageBase64 }`. Show "Extension configured! You can now open the extension and unlock your vault."
|
||||
- If extension not detected: show the config as a copyable JSON blob with instructions: "Install the idfoto extension, then paste this into the setup wizard." (Or just tell them to run through the extension setup manually with the same host/token/repo.)
|
||||
- If extension not detected: show the config as a copyable JSON blob with instructions: "Install the relicario extension, then paste this into the setup wizard." (Or just tell them to run through the extension setup manually with the same host/token/repo.)
|
||||
|
||||
## WASM Crate Change
|
||||
|
||||
The `idfoto-wasm` crate needs one new function:
|
||||
The `relicario-wasm` crate needs one new function:
|
||||
|
||||
```rust
|
||||
#[wasm_bindgen]
|
||||
pub fn embed_image_secret(carrier_jpeg: &[u8], secret: &[u8]) -> Result<Vec<u8>, JsValue>
|
||||
```
|
||||
|
||||
This wraps `idfoto_core::imgsecret::embed`. Currently only `extract_image_secret` is exposed.
|
||||
This wraps `relicario_core::imgsecret::embed`. Currently only `extract_image_secret` is exposed.
|
||||
|
||||
## File Structure
|
||||
|
||||
@@ -154,7 +154,7 @@ Add `setup.html` to the extension so it can be opened as a chrome-extension page
|
||||
```json
|
||||
{
|
||||
"web_accessible_resources": [{
|
||||
"resources": ["setup.html", "setup.js", "styles.css", "idfoto_wasm_bg.wasm", "idfoto_wasm.js"],
|
||||
"resources": ["setup.html", "setup.js", "styles.css", "relicario_wasm_bg.wasm", "relicario_wasm.js"],
|
||||
"matches": ["<all_urls>"]
|
||||
}]
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
# idfoto — WASM + Chrome MV3 Extension Design
|
||||
# relicario — WASM + Chrome MV3 Extension Design
|
||||
|
||||
The browser extension for idfoto. Compiles `idfoto-core` to WASM, wraps it in a Chrome MV3 extension with a terminal-aesthetic popup, conservative autofill, and direct Gitea/GitHub API access. No CLI dependency, no native messaging bridge.
|
||||
The browser extension for relicario. Compiles `relicario-core` to WASM, wraps it in a Chrome MV3 extension with a terminal-aesthetic popup, conservative autofill, and direct Gitea/GitHub API access. No CLI dependency, no native messaging bridge.
|
||||
|
||||
## Scope
|
||||
|
||||
- `idfoto-wasm` crate — wasm-bindgen wrapper around `idfoto-core`
|
||||
- `relicario-wasm` crate — wasm-bindgen wrapper around `relicario-core`
|
||||
- Chrome MV3 extension:
|
||||
- One-time setup wizard (git host + token + repo + reference image)
|
||||
- Service worker — WASM runtime, master_key holder, vault operations, git API
|
||||
@@ -44,9 +44,9 @@ pub struct ManifestEntry {
|
||||
|
||||
The `group` field is a free-form string. No predefined list, no nesting. User types "work" or "family" and entries cluster. Backwards-compatible — existing vaults without `group` deserialize as `None` (ungrouped).
|
||||
|
||||
## WASM Crate (`idfoto-wasm`)
|
||||
## WASM Crate (`relicario-wasm`)
|
||||
|
||||
Thin wasm-bindgen wrapper exposing `idfoto-core` functions to JavaScript. Lives at `crates/idfoto-wasm/`.
|
||||
Thin wasm-bindgen wrapper exposing `relicario-core` functions to JavaScript. Lives at `crates/relicario-wasm/`.
|
||||
|
||||
### Public API
|
||||
|
||||
@@ -94,7 +94,7 @@ pub fn generate_entry_id() -> String
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
idfoto-core = { path = "../idfoto-core" }
|
||||
relicario-core = { path = "../relicario-core" }
|
||||
wasm-bindgen = "0.2"
|
||||
js-sys = "0.3"
|
||||
serde_json = "1"
|
||||
@@ -106,10 +106,10 @@ data-encoding = "2" # base32 decoding for TOTP secrets
|
||||
### WASM build
|
||||
|
||||
```bash
|
||||
wasm-pack build crates/idfoto-wasm --target web --out-dir ../../extension/wasm
|
||||
wasm-pack build crates/relicario-wasm --target web --out-dir ../../extension/wasm
|
||||
```
|
||||
|
||||
Output: `idfoto_wasm.js` (JS glue) + `idfoto_wasm_bg.wasm` (binary). Expected size ~200-500 KB gzipped. The `image` crate's JPEG decoder is the heaviest component — optimize only if measured size is a problem.
|
||||
Output: `relicario_wasm.js` (JS glue) + `relicario_wasm_bg.wasm` (binary). Expected size ~200-500 KB gzipped. The `image` crate's JPEG decoder is the heaviest component — optimize only if measured size is a problem.
|
||||
|
||||
### TOTP implementation
|
||||
|
||||
@@ -147,7 +147,7 @@ interface WorkerState {
|
||||
interface VaultConfig {
|
||||
hostType: "gitea" | "github";
|
||||
hostUrl: string; // e.g. "https://git.adlee.work"
|
||||
repoPath: string; // e.g. "alee/idfoto-vault"
|
||||
repoPath: string; // e.g. "alee/relicario-vault"
|
||||
apiToken: string; // personal access token
|
||||
imageBytes: Uint8Array; // reference JPEG, stored in chrome.storage.local
|
||||
}
|
||||
@@ -190,7 +190,7 @@ Popup and content script communicate with the service worker via typed messages:
|
||||
2. Popup sends `{ type: "unlock", passphrase }` to service worker
|
||||
3. Service worker loads vault config from `chrome.storage.local` (includes image bytes)
|
||||
4. WASM: `extract_image_secret(image_bytes)` → `image_secret`
|
||||
5. Service worker fetches `.idfoto/salt` and `.idfoto/params.json` via git API
|
||||
5. Service worker fetches `.relicario/salt` and `.relicario/params.json` via git API
|
||||
6. WASM: `derive_master_key(passphrase, image_secret, salt, params)` → `master_key`
|
||||
7. Service worker fetches `manifest.enc` via git API
|
||||
8. WASM: `decrypt_manifest(manifest_enc, master_key)` → manifest
|
||||
@@ -330,7 +330,7 @@ No shadow DOM traversal. No heuristic scoring. No iframe inspection. If the form
|
||||
### 2. Field Icon Injection
|
||||
|
||||
When a password field is detected:
|
||||
- Small idfoto icon (16x16, inline SVG) appears at the right edge of the password field
|
||||
- Small relicario icon (16x16, inline SVG) appears at the right edge of the password field
|
||||
- Click triggers: send page URL to service worker → get matching entries
|
||||
- Single match: fill immediately
|
||||
- Multiple matches: show inline picker (small dropdown below the icon)
|
||||
@@ -380,7 +380,7 @@ extension/
|
||||
│ └── shared/
|
||||
│ ├── messages.ts # typed message definitions
|
||||
│ └── types.ts # Entry, ManifestEntry, VaultConfig, etc.
|
||||
├── wasm/ # wasm-pack output (idfoto_wasm.js + .wasm)
|
||||
├── wasm/ # wasm-pack output (relicario_wasm.js + .wasm)
|
||||
├── icons/ # extension icons (16, 48, 128px)
|
||||
└── dist/ # build output → load unpacked into Chrome
|
||||
```
|
||||
@@ -392,7 +392,7 @@ No framework. Vanilla TypeScript + DOM manipulation. The popup is small enough t
|
||||
### WASM build
|
||||
|
||||
```bash
|
||||
wasm-pack build crates/idfoto-wasm --target web --out-dir ../../extension/wasm
|
||||
wasm-pack build crates/relicario-wasm --target web --out-dir ../../extension/wasm
|
||||
```
|
||||
|
||||
### Extension build
|
||||
@@ -414,7 +414,7 @@ Chains wasm-pack then webpack. Dev mode: `npm run dev` watches TypeScript and au
|
||||
```json
|
||||
{
|
||||
"manifest_version": 3,
|
||||
"name": "idfoto",
|
||||
"name": "relicario",
|
||||
"version": "0.1.0",
|
||||
"description": "Two-factor encrypted password manager",
|
||||
"permissions": ["storage", "activeTab", "clipboardWrite"],
|
||||
@@ -476,7 +476,7 @@ The token is stored in `chrome.storage.local`, which is sandboxed per-extension
|
||||
|
||||
- Unit tests: each wrapper function round-trips correctly (`wasm-pack test --node`)
|
||||
- TOTP: test vectors from RFC 6238 appendix B
|
||||
- Integration: derive key + encrypt + decrypt cycle matches `idfoto-core` output
|
||||
- Integration: derive key + encrypt + decrypt cycle matches `relicario-core` output
|
||||
|
||||
### Extension (manual for V1)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# idfoto — Typed Item Data Model Design
|
||||
# relicario — Typed Item Data Model Design
|
||||
|
||||
Foundational data-model rewrite for idfoto. Replaces the single `Entry` type with a polymorphic typed-item system supporting Login, SecureNote, Identity, Card, Key, Document, and TOTP — with sections, custom fields, attachments, password history, soft-delete, and the security architecture needed to support 1Password-style daily-driver UX.
|
||||
Foundational data-model rewrite for relicario. Replaces the single `Entry` type with a polymorphic typed-item system supporting Login, SecureNote, Identity, Card, Key, Document, and TOTP — with sections, custom fields, attachments, password history, soft-delete, and the security architecture needed to support 1Password-style daily-driver UX.
|
||||
|
||||
This is **Phase 1** of the broader 1Password-parity roadmap. Phase 0 (audit remediation) is the precursor implementation pass; Phase 2+ (admin portal, importers, Watchtower checks, etc.) build on top of this model.
|
||||
|
||||
@@ -8,7 +8,7 @@ This is **Phase 1** of the broader 1Password-parity roadmap. Phase 0 (audit reme
|
||||
|
||||
In:
|
||||
|
||||
- New typed-item Rust data model in `idfoto-core` (replaces `Entry`)
|
||||
- New typed-item Rust data model in `relicario-core` (replaces `Entry`)
|
||||
- New on-disk repo layout (items + attachments split, settings file, format version 2)
|
||||
- Cryptographic envelope updates (length-prefixed Argon2 inputs, Zeroize discipline, opaque session-handle WASM bridge)
|
||||
- Security architecture for the extension boundary (split message router, origin-checked autofill, closed Shadow DOM rendering, hardened CLI git shell-out)
|
||||
@@ -69,7 +69,7 @@ Captured during brainstorming so the rationale is preserved:
|
||||
|
||||
```
|
||||
┌────────────────────────────────────────────────────────────────────┐
|
||||
│ idfoto-core (Rust) │
|
||||
│ relicario-core (Rust) │
|
||||
│ - Item, ItemCore (7 variants), Field, Section, Attachment │
|
||||
│ - Manifest, VaultSettings │
|
||||
│ - crypto: KDF (length-prefixed), AEAD, Zeroize discipline │
|
||||
@@ -79,7 +79,7 @@ Captured during brainstorming so the rationale is preserved:
|
||||
│ │
|
||||
▼ ▼
|
||||
┌──────────────────────┐ ┌──────────────────────────────────┐
|
||||
│ idfoto-cli (Rust) │ │ idfoto-wasm (Rust → WASM) │
|
||||
│ relicario-cli (Rust) │ │ relicario-wasm (Rust → WASM) │
|
||||
│ - clap commands │ │ - opaque session handles │
|
||||
│ - hardened git │ │ - typed-item API surface │
|
||||
│ - rpassword 7.x │ │ - master_key never returned to │
|
||||
@@ -141,7 +141,7 @@ pub enum ItemCore {
|
||||
}
|
||||
```
|
||||
|
||||
Each variant struct lives in `crates/idfoto-core/src/item_types/<type>.rs`. Compiler enforces exhaustiveness across the codebase — adding a new variant later means: create the file, add the enum variant, fix the (typically ~5) match-arm sites the compiler points at, register the UI form. No reflection, no registry, no runtime dispatch.
|
||||
Each variant struct lives in `crates/relicario-core/src/item_types/<type>.rs`. Compiler enforces exhaustiveness across the codebase — adding a new variant later means: create the file, add the enum variant, fix the (typically ~5) match-arm sites the compiler points at, register the UI form. No reflection, no registry, no runtime dispatch.
|
||||
|
||||
### Per-type cores
|
||||
|
||||
@@ -296,14 +296,14 @@ pub enum SymbolCharset {
|
||||
}
|
||||
```
|
||||
|
||||
Single canonical generator implementation in `idfoto-core`, exposed via WASM and used by CLI directly. Both paths use `getrandom`-backed `OsRng` and `rand::distributions::Uniform` for unbiased sampling.
|
||||
Single canonical generator implementation in `relicario-core`, exposed via WASM and used by CLI directly. Both paths use `getrandom`-backed `OsRng` and `rand::distributions::Uniform` for unbiased sampling.
|
||||
|
||||
## Storage, Manifest & Sync
|
||||
|
||||
### Repo layout
|
||||
|
||||
```
|
||||
.idfoto/
|
||||
.relicario/
|
||||
salt # 32-byte vault salt (KDF input)
|
||||
params.json # Argon2id parameters, format version
|
||||
devices.json # authorized device ed25519 pubkeys
|
||||
@@ -405,7 +405,7 @@ pub fn derive_master_key(
|
||||
image_secret: &[u8; 32],
|
||||
salt: &[u8; 32],
|
||||
params: &Argon2Params,
|
||||
) -> Result<Zeroizing<[u8; 32]>, IdfotoError> {
|
||||
) -> Result<Zeroizing<[u8; 32]>, RelicarioError> {
|
||||
let passphrase_nfc = passphrase.nfc().collect::<String>(); // normalize once
|
||||
|
||||
let mut password = Zeroizing::new(
|
||||
@@ -461,13 +461,13 @@ Per-encryption layout (item, manifest, settings, each attachment):
|
||||
- `VERSION_BYTE = 0x02` (clean break — no v1 compat).
|
||||
- XChaCha20-Poly1305 (already correct, audit confirmed-safe #1).
|
||||
- Fresh `OsRng`-derived nonce per encryption.
|
||||
- Decrypt failure returns opaque `IdfotoError::Decrypt` regardless of which validation tripped (audit M4).
|
||||
- Decrypt failure returns opaque `RelicarioError::Decrypt` regardless of which validation tripped (audit M4).
|
||||
|
||||
### RNG (audit H5, H6)
|
||||
|
||||
- `idfoto-wasm` uses `getrandom` (with `js` feature) for password generation, item IDs, attachment IDs. **No `Math.random()` anywhere.**
|
||||
- `relicario-wasm` uses `getrandom` (with `js` feature) for password generation, item IDs, attachment IDs. **No `Math.random()` anywhere.**
|
||||
- Modulo-bias eliminated via `rand::distributions::Uniform` for charset sampling — both CLI and WASM paths.
|
||||
- Single canonical `generate_password` and `generate_bip39` in `idfoto-core`, exposed to WASM and called directly by CLI.
|
||||
- Single canonical `generate_password` and `generate_bip39` in `relicario-core`, exposed to WASM and called directly by CLI.
|
||||
|
||||
### ID format (audit M8)
|
||||
|
||||
@@ -476,14 +476,14 @@ Per-encryption layout (item, manifest, settings, each attachment):
|
||||
|
||||
### Per-vault crypto metadata
|
||||
|
||||
`.idfoto/params.json`:
|
||||
`.relicario/params.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"format_version": 2,
|
||||
"kdf": { "algorithm": "argon2id-v0x13", "m": 65536, "t": 3, "p": 4 },
|
||||
"aead": "xchacha20poly1305",
|
||||
"salt_path": ".idfoto/salt"
|
||||
"salt_path": ".relicario/salt"
|
||||
}
|
||||
```
|
||||
|
||||
@@ -493,7 +493,7 @@ Three version fields exist intentionally and evolve independently:
|
||||
|
||||
| Field | Where | Bumps when |
|
||||
|---|---|---|
|
||||
| `format_version` | `.idfoto/params.json` | Overall vault layout changes (file structure, KDF construction, anything cross-cutting) |
|
||||
| `format_version` | `.relicario/params.json` | Overall vault layout changes (file structure, KDF construction, anything cross-cutting) |
|
||||
| `schema_version` | inside `manifest.enc` | Manifest entry shape changes only (e.g., adding a new field to `ManifestEntry`) |
|
||||
| `VERSION_BYTE` | first byte of every AEAD blob | AEAD construction itself changes (cipher, nonce size, tag layout) |
|
||||
|
||||
@@ -505,7 +505,7 @@ All three set to `2` for the initial typed-item release. Future bumps are indepe
|
||||
|
||||
- `setup.html` and `setup.js` **removed from `web_accessible_resources`** in both `extension/manifest.json` and `extension/manifest.firefox.json`.
|
||||
- The popup opens setup via `chrome.tabs.create({ url: chrome.runtime.getURL('setup.html') })` — own-origin extension tabs work without WAR.
|
||||
- WASM artifacts (`idfoto_wasm.js`, `idfoto_wasm_bg.wasm`) removed from WAR — service worker loads them via `import` from extension origin.
|
||||
- WASM artifacts (`relicario_wasm.js`, `relicario_wasm_bg.wasm`) removed from WAR — service worker loads them via `import` from extension origin.
|
||||
|
||||
### Split message router (audit C1, C2, C4)
|
||||
|
||||
@@ -593,7 +593,7 @@ root.appendChild(promptDom);
|
||||
Strict rules for content-script DOM construction:
|
||||
|
||||
1. **No `innerHTML` anywhere in content scripts.** All construction via `document.createElement` + `.textContent =`.
|
||||
2. **Element IDs randomized per-prompt** (no stable `idfoto-save-btn` for page collisions). Use a per-prompt `Map<string, HTMLElement>` to wire up handlers.
|
||||
2. **Element IDs randomized per-prompt** (no stable `relicario-save-btn` for page collisions). Use a per-prompt `Map<string, HTMLElement>` to wire up handlers.
|
||||
3. **Page-derived values bounded** — username field from `findUsernameValue` capped at 256 chars, control characters stripped, then assigned only via `.textContent`.
|
||||
4. **CSS scoped via Shadow DOM** — no leak to/from page CSS.
|
||||
|
||||
@@ -657,7 +657,7 @@ These audit items are bundled into the Phase 0 remediation plan (not this Phase
|
||||
- M7: CLI stdout `Password: ********` by default + `--show` flag.
|
||||
- M11: CLI ISO-8601 timestamp formatting.
|
||||
- L7: `cargo audit` / `cargo deny` CI configuration.
|
||||
- L8: CLI vault-dir detection (refuse to operate outside an `.idfoto/`-marked directory).
|
||||
- L8: CLI vault-dir detection (refuse to operate outside an `.relicario/`-marked directory).
|
||||
|
||||
## WASM API Surface
|
||||
|
||||
@@ -720,38 +720,38 @@ New commands and renamed semantics:
|
||||
|
||||
```bash
|
||||
# Existing (semantics carry forward, terminology updated to "item")
|
||||
idfoto init
|
||||
idfoto unlock # unlocks for next command
|
||||
idfoto lock
|
||||
idfoto sync # git pull --rebase + push, hardened
|
||||
idfoto generate [--length N] [--bip39 [--words N]] [--symbols safe|extended]
|
||||
idfoto device <add|list|revoke>
|
||||
relicario init
|
||||
relicario unlock # unlocks for next command
|
||||
relicario lock
|
||||
relicario sync # git pull --rebase + push, hardened
|
||||
relicario generate [--length N] [--bip39 [--words N]] [--symbols safe|extended]
|
||||
relicario device <add|list|revoke>
|
||||
|
||||
# Updated for typed items
|
||||
idfoto add <type> [--title T] [--group G] [--tags t1,t2] [--favorite]
|
||||
relicario add <type> [--title T] [--group G] [--tags t1,t2] [--favorite]
|
||||
[...type-specific fields, e.g., --username, --url, --password-prompt]
|
||||
idfoto get <id-or-title> # always concealed by default; --show to reveal
|
||||
idfoto list [--type T] [--group G] [--tag T] [--trashed]
|
||||
idfoto edit <id-or-title> # interactive prompts for fields to update
|
||||
relicario get <id-or-title> # always concealed by default; --show to reveal
|
||||
relicario list [--type T] [--group G] [--tag T] [--trashed]
|
||||
relicario edit <id-or-title> # interactive prompts for fields to update
|
||||
# (no $EDITOR with plaintext — temp-file leak risk)
|
||||
idfoto rm <id-or-title> # soft-delete (trash)
|
||||
idfoto restore <id-or-title> # restore from trash
|
||||
idfoto purge <id-or-title> # hard-delete (also purges attachments)
|
||||
idfoto trash empty # hard-delete all past retention
|
||||
relicario rm <id-or-title> # soft-delete (trash)
|
||||
relicario restore <id-or-title> # restore from trash
|
||||
relicario purge <id-or-title> # hard-delete (also purges attachments)
|
||||
relicario trash empty # hard-delete all past retention
|
||||
|
||||
# New for attachments
|
||||
idfoto attach <id-or-title> <file> # adds file as attachment
|
||||
idfoto attachments <id-or-title> # list attachments on item
|
||||
idfoto extract <id-or-title> <aid> [--out path] # decrypt + save to disk
|
||||
relicario attach <id-or-title> <file> # adds file as attachment
|
||||
relicario attachments <id-or-title> # list attachments on item
|
||||
relicario extract <id-or-title> <aid> [--out path] # decrypt + save to disk
|
||||
|
||||
# Settings
|
||||
idfoto settings get [<key>]
|
||||
idfoto settings set <key> <value> # e.g., trash_retention=days:60
|
||||
relicario settings get [<key>]
|
||||
relicario settings set <key> <value> # e.g., trash_retention=days:60
|
||||
```
|
||||
|
||||
`idfoto get` shows password as `********` by default. `--show` is required to print plaintext. Clipboard auto-clear unconditional after 30s with `Zeroizing<String>` wrap (audit M6, M7).
|
||||
`relicario get` shows password as `********` by default. `--show` is required to print plaintext. Clipboard auto-clear unconditional after 30s with `Zeroizing<String>` wrap (audit M6, M7).
|
||||
|
||||
`vault_dir()` detection: traverses up from CWD looking for `.idfoto/`. Refuses to operate without one (audit L8).
|
||||
`vault_dir()` detection: traverses up from CWD looking for `.relicario/`. Refuses to operate without one (audit L8).
|
||||
|
||||
## Browser Extension UI Implications
|
||||
|
||||
@@ -792,7 +792,7 @@ Setup wizard, capture flow, autofill icon, and unlock screen all continue to exi
|
||||
|
||||
- **Service worker router**: mock `chrome.runtime.onMessage` and verify each message type is rejected when sent from the wrong sender (popup-only from content, content-callable from popup, anything from external).
|
||||
- **Origin-bound autofill**: mock `sender.tab.url` and verify cross-origin requests are rejected even when the content script asks nicely.
|
||||
- **Closed Shadow DOM**: render the capture prompt, verify the page-side `document.querySelector('#idfoto-save-btn')` returns null.
|
||||
- **Closed Shadow DOM**: render the capture prompt, verify the page-side `document.querySelector('#relicario-save-btn')` returns null.
|
||||
- **Generator**: verify no `Math.random()` reachable from any extension entry point (lint rule + runtime probe).
|
||||
|
||||
### Manual / observational
|
||||
@@ -826,7 +826,7 @@ Setup wizard, capture flow, autofill icon, and unlock screen all continue to exi
|
||||
| H6 | High | `rand::distributions::Uniform` in CLI generator |
|
||||
| H7 | High | Bump `rpassword` to 7.x (Phase 0) |
|
||||
| H8 | High | Documented in setup wizard + README; fine-grained PAT guidance |
|
||||
| M4 | Medium | Opaque `IdfotoError::Decrypt` for all decrypt failures |
|
||||
| M4 | Medium | Opaque `RelicarioError::Decrypt` for all decrypt failures |
|
||||
| M5 | Medium | Popup captures `(tab.id, tab.url)` at open; verifies on `fill_credentials` |
|
||||
| M8 | Medium | 16-char hex IDs |
|
||||
| M9 | Medium | Item type discriminant validation in deserializer |
|
||||
@@ -839,20 +839,20 @@ Phase 0 implementation handles the remaining items (M3, M6, M7, M11, L7, L8) out
|
||||
New files (Rust):
|
||||
|
||||
```
|
||||
crates/idfoto-core/src/item.rs # Item, Section, Field, FieldKind, FieldValue
|
||||
crates/idfoto-core/src/item_types/mod.rs
|
||||
crates/idfoto-core/src/item_types/login.rs
|
||||
crates/idfoto-core/src/item_types/secure_note.rs
|
||||
crates/idfoto-core/src/item_types/identity.rs
|
||||
crates/idfoto-core/src/item_types/card.rs
|
||||
crates/idfoto-core/src/item_types/key.rs
|
||||
crates/idfoto-core/src/item_types/document.rs
|
||||
crates/idfoto-core/src/item_types/totp.rs
|
||||
crates/idfoto-core/src/manifest.rs # rewritten
|
||||
crates/idfoto-core/src/settings.rs
|
||||
crates/idfoto-core/src/generators.rs
|
||||
crates/idfoto-core/src/attachment.rs
|
||||
crates/idfoto-wasm/src/session.rs
|
||||
crates/relicario-core/src/item.rs # Item, Section, Field, FieldKind, FieldValue
|
||||
crates/relicario-core/src/item_types/mod.rs
|
||||
crates/relicario-core/src/item_types/login.rs
|
||||
crates/relicario-core/src/item_types/secure_note.rs
|
||||
crates/relicario-core/src/item_types/identity.rs
|
||||
crates/relicario-core/src/item_types/card.rs
|
||||
crates/relicario-core/src/item_types/key.rs
|
||||
crates/relicario-core/src/item_types/document.rs
|
||||
crates/relicario-core/src/item_types/totp.rs
|
||||
crates/relicario-core/src/manifest.rs # rewritten
|
||||
crates/relicario-core/src/settings.rs
|
||||
crates/relicario-core/src/generators.rs
|
||||
crates/relicario-core/src/attachment.rs
|
||||
crates/relicario-wasm/src/session.rs
|
||||
```
|
||||
|
||||
New files (extension):
|
||||
@@ -875,15 +875,15 @@ extension/src/popup/components/history.ts
|
||||
Heavily modified (Rust):
|
||||
|
||||
```
|
||||
crates/idfoto-core/src/lib.rs # re-exports + module declarations
|
||||
crates/idfoto-core/src/crypto.rs # length-prefix KDF, Zeroize, NFC
|
||||
crates/idfoto-core/src/entry.rs # DELETED — replaced by item.rs
|
||||
crates/idfoto-core/src/error.rs # opaque Decrypt variant only
|
||||
crates/idfoto-core/Cargo.toml # add zeroize, zxcvbn, bip39, unicode-normalization
|
||||
crates/idfoto-wasm/src/lib.rs # session-handle API, getrandom
|
||||
crates/idfoto-wasm/Cargo.toml # update deps
|
||||
crates/idfoto-cli/src/main.rs # rewritten command handlers
|
||||
crates/idfoto-cli/Cargo.toml # rpassword = "7", clipboard hardening
|
||||
crates/relicario-core/src/lib.rs # re-exports + module declarations
|
||||
crates/relicario-core/src/crypto.rs # length-prefix KDF, Zeroize, NFC
|
||||
crates/relicario-core/src/entry.rs # DELETED — replaced by item.rs
|
||||
crates/relicario-core/src/error.rs # opaque Decrypt variant only
|
||||
crates/relicario-core/Cargo.toml # add zeroize, zxcvbn, bip39, unicode-normalization
|
||||
crates/relicario-wasm/src/lib.rs # session-handle API, getrandom
|
||||
crates/relicario-wasm/Cargo.toml # update deps
|
||||
crates/relicario-cli/src/main.rs # rewritten command handlers
|
||||
crates/relicario-cli/Cargo.toml # rpassword = "7", clipboard hardening
|
||||
```
|
||||
|
||||
Heavily modified (extension):
|
||||
@@ -916,5 +916,5 @@ Documentation:
|
||||
```
|
||||
README.md # update for typed items, security warnings
|
||||
CLAUDE.md # reflect new module structure
|
||||
docs/superpowers/specs/2026-04-11-idfoto-design.md # amend KDF section per H1; note format v2
|
||||
docs/superpowers/specs/2026-04-11-relicario-design.md # amend KDF section per H1; note format v2
|
||||
```
|
||||
Reference in New Issue
Block a user