Slice 4 spec review caught: router.test.ts's narrow chrome.* shim
triggered 4 TS errors in webpack's ts-loader pass during production
builds (partial mocks don't match the full chrome.* type surface).
Plan's verbatim test body assumes tests aren't part of the build
compile. Add src/**/__tests__/** to tsconfig exclude — tests still
compile under Vitest's independent ts pipeline (42/42 passing).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Non-functional tightening flagged in the slice-3 code review:
- session.ts: document future multi-vault refactor (β+) so the module-
scope singleton is explicitly "deliberately simple," not an oversight.
- vault.ts: move findByHostname doc comment above the function; note
α's intentionally-coarse hostname match (no www-stripping, no
public-suffix matching) and that tighter matching is a β/γ concern.
- index.ts: expand the passphrase scope-clearing comment to make
the theatre explicit rather than leaving it looking like real defense.
- index.ts: TODO(slice-4) marker on delete_item's non-atomic two-write
path — consider manifest-first ordering or retry/rollback at router-
split time.
- index.ts: cross-reference comment on itemToManifestEntry pointing at
the Rust-side ManifestEntry::from_item derivation it must mirror.
No behavior change; build still compiles with 2 bundle-size warnings.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add initSync named export (Chrome MV3 service worker path — can't use
dynamic import()), and correct TotpCode.expires_at from number to bigint
to match the u64 wasm-bindgen output.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Verified against the Plan 1A Rust sources:
- ItemType / ItemCore use snake_case with tag="type" internal tagging
(not the external tagging I initially wrote)
- TotpKind is default-externally-tagged (no tag attr), so it serializes
as bare "totp"/"steam" for unit variants and { hotp: { counter } }
- GeneratorRequest uses tag="kind" internal tagging
- FieldValue / TrashRetention / HistoryRetention / SymbolCharset use
adjacent tagging { tag: "kind", content: "value" }
- Fix Login form TOTP parse example and "gen" button payload
No scope change — this is a bookkeeping correction so the plan
author references the correct wire shapes.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Foundation slice of the browser-extension migration onto the typed-item
core from Plans 1A+1B. Scope: WASM artifact rebuild, typed-item shared
types, SessionHandle-based service worker, split router with sender
checks, full security architecture (origin-bound autofill, TOFU ack,
closed Shadow DOM, popup captured-tab verification), zxcvbn setup gate,
Login-parity popup. Other 6 item types land in 1C-β; attachments/trash/
history/device UI in 1C-γ.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
A Task 6 implementer subagent ran `relicario init` inside the worktree
root during manual testing and committed the resulting vault skeleton
(.relicario/, manifest.enc, settings.enc) plus overwrote .gitignore.
None of these should be in the source repo.
Restores the original .gitignore (adds reference.jpg and ref.jpg to it)
and checks in the Plan 1B design doc that describes the work just merged.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds RELICARIO_TEST_ITEM_SECRET env hatch for rpassword calls in
cmd_add / cmd_edit so piped-stdin tests can exercise the password
prompt paths without a TTY.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Uses assert_cmd + tempfile to spin up a fresh vault per test.
Covers init layout, add/list/get mask semantics, rm/restore/purge cycle,
and generate smoke. Adds RELICARIO_TEST_PASSPHRASE env-var hatch in
unlock_interactive and cmd_init so tests don't need a TTY.
Also fixes read_params in session.rs to correctly parse the nested
params.json format (kdf sub-object) rather than trying to deserialize
the whole file as KdfParams.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Maps serialize as JS objects, not Maps — what the extension popup
expects. Also ships hand-written TS declarations for the bridge
(consumed by Plan 1C).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Also ports TOTP RFC 6238 compute to relicario-core::item_types::totp
so native + CLI + WASM share one implementation (audit H5: CSPRNG
via core's Uniform-sampling generator).
Adds hmac = "0.12" and sha1 = "0.10" to relicario-core deps to support
HOTP/TOTP HMAC with Sha1/Sha256/Sha512. RFC 6238 test vector (t=59,
SHA-1, 8 digits) passes: "94287082".
Adds six #[wasm_bindgen] functions (manifest_encrypt/decrypt,
item_encrypt/decrypt, settings_encrypt/decrypt) plus a native
round-trip test that verifies encrypt→core_decrypt and nonce
uniqueness without calling js-sys (serde_wasm_bindgen::from_value
is wasm32-only; documented in test comment).
Master key never leaves WASM linear memory. Held in Zeroizing<[u8;32]>
inside a thread_local HashMap keyed by u32. lock() removes + zeroizes.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
CLI no longer has its own charset-sampling path — uses the CSPRNG
generate_password / generate_passphrase in relicario-core, which use
rand::distributions::Uniform internally.
Extracted purge_item helper so cmd_trash_empty loops over it without
re-prompting for passphrase per item. Single git commit per trash empty
summarizing the count. Caught in Task 12 review.
Soft-delete sets trashed_at via Item::soft_delete; restore clears it.
Purge deletes item + attachment dir and removes manifest entry.
Trash empty scans for items past settings.trash_retention.
Title/group/tags always optional. Per-type prompts for core secret
fields (Login.password, Card.number, Key.material, SecureNote.body)
push the old value to field_history via a synthetic core:<key>
FieldId so rotation is audit-traceable.
Secrets masked by default (audit M7). --show reveals plaintext.
--copy writes to clipboard and spawns a detached 30s auto-clear
thread holding a Zeroizing copy that wipes on drop (audit M6).
SecureNote, Identity, Card, Key, Document (with inline attachment),
and Totp with base32 secret decoding. Document widens the commit
to include the attachment blob path.
Unlocks vault, builds LoginCore from flags (password via rpassword if
--password-prompt), saves item + manifest, commits via hardened git.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1. Add .relicario/salt to the initial git commit so it syncs across
devices (Argon2 salt must match at unlock time).
2. Return a proper error instead of panicking when --output has no
filename component (e.g., trailing ..).
3. Wrap the generated 32-byte image_secret in Zeroizing for
consistency with the passphrase + master_key handling in Task 4.
Caught in Task 6 review.
Prompts for a strong passphrase (zxcvbn gate via core), generates a
32-byte image secret, embeds it in the carrier JPEG, writes the
standard vault skeleton, and makes an initial git commit via the
hardened git_command helper.
Every subcommand from the Plan 1B CLI spec present; bodies return
'not yet implemented' so subsequent tasks land one command at a time.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
atomic_write now appends .tmp instead of replacing the extension
(manifest.enc.tmp, not manifest.tmp). image_secret is wrapped in
Zeroizing so both KDF inputs wipe on drop. Caught in Task 4 review.
Provides load/save helpers for Manifest/Settings/Item; atomic_write keeps
vault files consistent across crashes. main.rs is transiently broken
against the old Entry API — Task 5+ rewrites the command handlers.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
peek_jpeg_dimensions reads jpeg[i+8] as the last byte, so the guard
should be \`i + 8 >= jpeg.len()\`, not \`i + 9 >= jpeg.len()\`. The old
guard would reject a valid SOF marker ending exactly at len()-1.
Caught in Task 2 code-quality review.