Commit Graph

339 Commits

Author SHA1 Message Date
adlee-was-taken
1ffe333697 test(core): backup round-trips git archive + size check 2026-04-27 22:39:55 -04:00
adlee-was-taken
e4949c4c06 test(core): backup round-trips reference image bytes 2026-04-27 22:37:38 -04:00
adlee-was-taken
0b59b94a0b test(core): populated-vault round-trip for backup 2026-04-27 22:34:36 -04:00
adlee-was-taken
08086b9a9e feat(core): backup module — empty-vault round-trip
pack_backup / unpack_backup ship the magic header, format version,
Argon2id KDF, XChaCha20-Poly1305 AEAD, and zstd-compressed JSON
envelope. Empty-vault round-trip is the foundation; later tasks
add items, attachments, image, and git history.
2026-04-27 22:29:10 -04:00
adlee-was-taken
57dd186bab feat(core): add backup deps + error variants
Adds zstd, tar, base64 to relicario-core; introduces
BackupBadMagic / BackupUnsupportedVersion / BackupSchemaMismatch.
Foundation for the backup module landing in Task 2.
2026-04-27 22:22:04 -04:00
adlee-was-taken
c66fd520f8 docs(arch): per-codebase ARCHITECTURE.md + cross-codebase overview
Strategic-depth architecture documentation, the kind that's hard to
recover by reading code: invariants, multi-file flows, design rationale,
gotchas. Goal is to cut the token cost for future Claude sessions.

Four new docs (2091 lines total):

- crates/relicario-core/ARCHITECTURE.md (514 lines) — bytes-in/bytes-out
  boundary, 24 verified invariants (VERSION_BYTE=0x02, length-prefixed
  KDF input, NFC normalization, content-addressed AttachmentId, history-
  tracked field kinds, 60% imgsecret confidence floor, MAX_DIMENSION=
  10000, etc.), 7 multi-module flows, 16 non-obvious gotchas (QUANT_STEP=
  50, central-70%-embed, BIP39-128bit-then-truncate, Steam alphabet
  rationale).

- crates/relicario-cli/ARCHITECTURE.md (539 lines) — module map for the
  three source files; the cmd_add/cmd_edit per-type helper pattern (post-
  2026-04-27 refactor); the hardened-git invariant (Command::new("git")
  is gated to helpers.rs:46); the five history synthetic keys; the env-
  var escape-hatch policy; cmd_generate's two-mode design (no-unlock
  outside vault, unlock-and-read-defaults inside).

- extension/ARCHITECTURE.md (831 lines) — five-bundle structure (popup,
  vault, setup, content, service-worker); SW-as-crypto-fortress model;
  capability-set-or-silent-rejection contract; vault-tab-as-popup-class
  router parity (commit a7dbf35); origin TOFU flow; setup state machine;
  test-vs-build gap.

- docs/architecture/overview.md (207 lines) — cross-codebase entry point.
  How the three codebases fit together, the four versioned wire formats
  between them (core→WASM ABI, SW chrome.runtime protocol, vault on-disk
  layout, GitHost API), per-codebase secret residency table, build
  matrix, conventions that span all three.

Specs in docs/superpowers/specs/ remain as historical decision artifacts
("why we chose this") — the new arch docs are the source of truth for
"what is" current invariants and flows.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-27 21:41:26 -04:00
adlee-was-taken
b951741366 docs(changelog): unreleased entries for the 2026-04-27 audit pass
Catches the changelog up with the audit-driven CLI + extension work and
the cmd_add / cmd_edit / setup.ts internal refactors.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-27 21:13:35 -04:00
adlee-was-taken
3f0f5b1b28 feat(cli): close audit gaps — TOTP edit, history, detach, status, generator defaults
One coherent CLI completeness pass driven by the 2026-04-27 state-of-the-
project audit. All TDD; 6 new integration tests (workspace 158→164).

Stubs and dead state fixed:
- TOTP edit was an explicit stub at main.rs:925 ("delete and re-add for
  now"). Now supports editing issuer, label, and rotating the secret;
  rotated secrets are pushed to field_history under core:totp_secret.
- VaultSettings.generator_defaults was stored but never read by the CLI.
  cmd_generate now consults it when invoked inside an initialized vault;
  explicit flags override. Behavior outside a vault unchanged.

New commands:
- relicario settings generator-defaults [--random|--bip39] [--length |
  --words | --symbols | --separator] — view/edit the stored generator
  defaults.
- relicario history <query> [--show] [--field <name>] — view captured
  field history. Values masked by default.
- relicario detach <query> <aid> — remove an individual attachment +
  blob. Refuses to drop a Document item's primary attachment.
- relicario status — vault summary: root path, item counts (active /
  trashed), attachment count + total bytes, registered device count,
  last commit (%h %s).

Internal refactor (pure mechanical, no behavior change):
- cmd_add: 217-line match split into one build_<type>_item helper per
  ItemCore variant + a 7-arm dispatcher.
- cmd_edit: same treatment — edit_login, edit_card, edit_totp, etc. The
  history-tracking ones take a &mut FieldHistory alias for clarity.

Existing tests cover the refactor; the new helpers are tested through
the same integration paths.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-27 21:13:30 -04:00
adlee-was-taken
f79a67bb15 refactor(ext/setup): extract pure helpers to setup-helpers.ts
The setup wizard was 1205 lines in a single file. Extract the
state-independent helpers (escapeHtml, ratePassphrase, scheduleRate,
entropyText, STRENGTH_LABELS, the Strength interface) into a sibling
setup-helpers.ts. updateStrengthUi stays in setup.ts since it walks the
live wizard state object and would force every caller to thread that
state through.

setup.ts: 1205 → 1137 lines. Pure mechanical extraction; no behavior
change. Existing tests are the safety net (24 vitest files, all pass).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-27 21:13:13 -04:00
adlee-was-taken
a7dbf35126 feat(ext): sync now button + device register from popup; vault tab parity
Closes three audit gaps in one pass:

1. Sync now button in the popup settings view (📤). Triggers the existing
   { type: 'sync' } SW message and surfaces success / failure inline. The
   SW message was already wired but had no UI entry point.

2. Device registration from the popup. The "Register this device" button
   on the devices view used to error out with a "not yet implemented"
   message; it now opens an inline name input (default = browser+OS), and
   on confirm sends a new register_this_device SW message that generates
   an ed25519 keypair via WASM, persists private_key + name to
   chrome.storage.local, and writes the public key to the remote
   devices.json. No setup-wizard detour.

3. Vault tab is now an authorized sender for popup-only SW messages. The
   router accepts vault.html alongside popup.html, so the fullscreen tab
   can drive the same flows. Test covers acceptance from the vault tab.

New SW message: register_this_device { name }. Added to PopupMessage and
POPUP_ONLY_TYPES, handled in router/popup-only.ts.

Tests: 5 new vitest cases (3 in settings.test.ts, 2 in devices.test.ts)
+ 1 router test for vault-tab acceptance. All 194 extension tests pass.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-27 21:13:05 -04:00
adlee-was-taken
086b73b260 docs(claude.md): pin autonomy rule for routine decisions
Add a "Working with the user" section at the top of CLAUDE.md so the
default-to-recommended autonomy rule travels with the repo, not just
with the user's local memory. Mirrors the feedback memory of the same
name: pick the recommended option without prompting on minor
multiple-choice / yes-no decisions; pause before destructive git/rm
operations; brainstorming-skill intent-discovery questions still need
user input.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-27 21:12:48 -04:00
adlee-was-taken
d8a06346b9 docs(spec): import/export + LastPass migration design
Brainstormed scope: backup/restore round-trippable to relicario, plus a
LastPass CSV importer. Migration out is explicitly out of scope. CLI and
fullscreen vault tab get parity; popup is untouched.

Backup format `.relbak` v1: magic header + version + Argon2id salt +
XChaCha20-Poly1305 nonce + AEAD-encrypted, zstd-compressed JSON envelope
with base64'd binary blobs. KDF params are tied to backup format
version, not the live vault's params.json.

Reference image inclusion is opt-in; .git history is opt-out. Backup
passphrase is independent of the vault passphrase. Restore refuses if
the target dir already has a vault.

Includes architecture, data flow, error handling, testing strategy,
LastPass field-mapping table, risks, and effort estimate (~5.5 dev-days
for full CLI + extension parity).

Implementation plan and code to follow.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-27 20:57:06 -04:00
adlee-was-taken
beff092818 fix(ext/setup): lock verified handle on Step 5 error + early-return paths
Mirrors Step 3b's discipline. Previously, if save_setup failed or addDevice
threw, state.verifiedHandle (the WASM session from Step 3b) would remain
in linear memory until tab close. Now lock+null on every exit path.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-27 19:12:22 -04:00
adlee-was-taken
aa1ad99e6e chore: bump version to 0.2.0 + add CHANGELOG
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-27 19:02:35 -04:00
adlee-was-taken
2756033bf9 feat(ext/setup): unified device registration in Step 5; fixes silent dropped pubkey
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 18:34:35 -04:00
adlee-was-taken
e79e80b000 feat(ext/setup): Step 3b attach flow with decrypt verification
Replace placeholder renderStep3Attach/attachStep3Attach with the real
attach flow: file-picker for reference JPEG, passphrase input with
visibility toggle, then fetch salt+params+manifest.enc, call
unlock()+manifest_decrypt() to AEAD-verify credentials before
advancing to Step 4. Wrong passphrase/image shows a clear error;
partial handles are locked on failure to avoid key-material leaks.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 18:32:27 -04:00
adlee-was-taken
214f8da673 fix(ext/setup): wizard writes settings.enc to match CLI init
Add default_vault_settings_json() to the hand-written wasm.d.ts
declarations, then use it in attachStep3New to encrypt and push
settings.enc after manifest.enc during new-vault creation. Wizard-
created vaults now have all four files the SW expects (salt,
params.json, manifest.enc, settings.enc), preventing the
get_vault_settings 404 on first unlock.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 18:29:10 -04:00
adlee-was-taken
3aa17e6be2 feat(wasm): default_vault_settings_json() for wizard parity with CLI init
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 18:27:07 -04:00
adlee-was-taken
399a276fdd feat(ext/setup): refuse to overwrite existing vault files (Step 3a clobber guard)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 18:24:16 -04:00
adlee-was-taken
f44aedfa76 feat(ext/setup): vault-presence probe + mode-mismatch banners on Step 2
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 18:22:45 -04:00
adlee-was-taken
a182c1ac5a feat(ext/setup): Step 0 mode picker (new vs attach) + Step 1 back button
Replace the placeholder Step 0 with two clickable mode-card buttons (create
new vault / attach this device). Picking a card highlights it and enables
the next button; the back button on Step 1 returns to Step 0 without losing
state. Add .mode-card CSS using the existing dark palette (#30363d, #58a6ff).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 18:20:24 -04:00
adlee-was-taken
7fa1f2990f refactor(ext/setup): wizard state shape for mode-aware flow
Expand WizardState with mode/vaultProbe/referenceImageBytesAttach/
verifiedHandle/attaching fields; start wizard at step 0; grow progress
bar to 6 segments; rename renderStep3/attachStep3 to *New variants;
add placeholder renderStep0/attachStep0/renderStep3Attach/attachStep3Attach.
No behaviour change for the existing new-vault flow.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 18:14:42 -04:00
adlee-was-taken
8e72ed8714 feat(ext/setup): vault-presence probe helper
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 18:12:04 -04:00
adlee-was-taken
19bb5b5293 test(ext/sw): assert PUT method on GitHub writeFileCreateOnly create path
Mirrors the POST assertion already present in the Gitea "creates" test —
catches accidental method drift.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-27 18:10:32 -04:00
adlee-was-taken
86b5941875 feat(ext/sw): GitHost.writeFileCreateOnly() refuses to overwrite
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 18:06:48 -04:00
adlee-was-taken
98c962796f test(ext/sw): assert lastCommit URL structure + comment limit/per_page divergence
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 18:04:56 -04:00
adlee-was-taken
2c94dfaf90 feat(ext/sw): GitHost.lastCommit() for vault-presence metadata
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 17:48:24 -04:00
adlee-was-taken
7588a75bdc docs: implementation plan for attach-existing-vault wizard split (v0.2.0)
11 main tasks + 2 addendum tasks (Tasks 7a/7b) covering:
- GitHost.lastCommit() and GitHost.writeFileCreateOnly()
- Vault-presence probe helper
- Wizard state refactor + Step 0 mode picker
- Step 2 probe wiring with mode-mismatch banners
- Step 3a clobber guard via writeFileCreateOnly
- Step 3b attach flow with decrypt verification
- Step 5 unified device registration (fixes silent-drop pubkey bug)
- Default vault_settings_json WASM export + wizard settings.enc write
  (fixes runtime get_vault_settings 404 reported on wizard-init vaults)
- Version bump to 0.2.0 + CHANGELOG

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-27 17:42:00 -04:00
adlee-was-taken
44fc157f35 docs: spec for attach-existing-vault wizard split (v0.2.0)
Setup wizard currently overwrites existing vaults silently. Adds a
mode picker (create new / attach this device), a vault-presence probe
after the connection test, and a Step 3b that verifies passphrase +
reference image by decrypting the manifest before registering a new
device key. Refuses destructive overwrite from the GUI; users wanting
a clean slate must delete the repo via their host's web UI.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-27 17:33:07 -04:00
adlee-was-taken
ce59223fc0 feat(ext): shared state host — decouple components from popup.ts
Introduce shared/state.ts as a service-locator so popup components
(item-detail, item-form, trash, devices, settings, etc.) work in both
the popup and vault tab bundles. Both entry points register themselves
as the host; components import from shared/state instead of popup.ts.
Vault.ts now delegates to the real popup components, removing ~300 lines
of placeholder renderers.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-27 16:38:06 -04:00
adlee-was-taken
6c8ebb3548 feat(ext/vault): scaffold vault.html tab with sidebar+pane layout and hash routing
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-27 15:53:53 -04:00
adlee-was-taken
7e0950e364 feat(ext/popup): session expiry listener, open-vault links, Shift+F shortcut
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-27 15:46:32 -04:00
adlee-was-taken
101f0093a4 fix(ext/sw): review fixes — storage key, timer reset scope, imports
- Rename storage key sessionTimeoutConfig → session_timeout (plan spec)
- Only call resetTimer() for non-content-script message types so content
  script polling cannot keep the session alive
- Collapse two same-module imports into one line; add CONTENT_CALLABLE_TYPES

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 15:44:13 -04:00
adlee-was-taken
86621f075f feat(ext/sw): add session inactivity timer with configurable timeout
Implements a service-worker-side session timer that locks the vault
after a configurable period of inactivity (default 15 min). Supports
two modes: 'inactivity' (timer-based) and 'every_time' (no timer).
Config persists via chrome.storage.local and is exposed through
get_session_config / update_session_config popup messages.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-04-27 02:24:26 -04:00
adlee-was-taken
bd13854f59 docs: vault tab + session timeout implementation plan
7 tasks: session timer, popup navigation, vault scaffold,
shared state host, device settings, router fix, manual testing.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-27 02:19:31 -04:00
adlee-was-taken
5089c2b7ea docs: vault tab UI + session timeout design spec
Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-27 02:13:26 -04:00
adlee-was-taken
9488670b1b fix(ext/popup): fix reversed search, remove auto-focus, Enter opens items
- Search no longer auto-focuses; use "/" to focus it
- Typing in search no longer re-renders the entire view, just the
  item list — fixes backwards text caused by cursor reset to pos 0
- Arrow keys also update list without full re-render
- Enter opens the selected item even when search is focused

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-27 02:10:23 -04:00
adlee-was-taken
8f603ec069 fix(ext/router): allow popup.html with query params
The router was doing exact URL match for popup.html, but when
opened in a tab with params (?view=add&type=card), it failed.
Changed to startsWith match like setup.html already uses.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-27 01:49:59 -04:00
adlee-was-taken
446949c5ce fix(ext/popup): auto-popout for attachment types, keep login/note in popup
- Login and secure_note types stay in popup without attachment UI
- All other types (identity, card, key, totp, document) auto-redirect
  to full tab when selected
- Attachments only shown for login/secure_note when opened in tab

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-27 01:42:35 -04:00
adlee-was-taken
c59e6892d8 feat(ext/popup): add pop-out to tab for forms
Forms can now be opened in a full browser tab via the ⤴ button,
solving Chrome's popup closure on file picker interaction. Deep
linking via URL params preserves view, item type, and item ID.

Also removes the unused dropdown picker code from item-list.ts.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-27 01:32:39 -04:00
adlee-was-taken
39db697ce5 fix(ext/popup): replace item type dropdown with selection view
Clicking "+ new" now navigates to a type selection view instead of
showing a dropdown that gets clipped by popup bounds. The selection
view displays all item types as buttons in a scrollable list.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-27 01:21:14 -04:00
adlee-was-taken
eb14946f06 feat(ext/setup): add device name step to setup wizard
New step 4 after vault creation: enter device name (defaults to
"Chrome on Linux" based on detected browser/OS). Generates ed25519
keypair, stores private key in chrome.storage.local, registers
device with vault. Wizard is now 5 steps (was 4).

Also adds generate_device_keypair() to wasm.d.ts type declarations.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-27 01:04:10 -04:00
adlee-was-taken
abfc5aed42 feat(ext/popup): wire navigation for trash, devices, field-history screens
Adds View variants, render cases, teardown calls, and entry points
in settings menu for trash and devices.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-27 00:42:53 -04:00
adlee-was-taken
b55c59bd35 feat(ext/popup): add attachment cap setting to vault settings
Dropdown with 5/10/25/50 MB presets for per_attachment_max_bytes.
Other caps remain at defaults.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-27 00:37:43 -04:00
adlee-was-taken
2fa54e2144 feat(ext/popup): add "View history" link to login detail view
Shows button when item.field_history is non-empty. Navigates to
field-history screen with historyItemId set.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-27 00:32:16 -04:00
adlee-was-taken
3b4788e5dc feat(ext/popup): field history view — masked values with reveal toggle
Shows current + historical values for tracked fields (password/concealed).
Click to reveal, copy button per entry (plaintext stored in a module-level
Map, never embedded in the DOM). Grouped by field name if multiple tracked
fields exist. Adds historyItemId to PopupState and 'field-history' to View.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-27 00:23:54 -04:00
adlee-was-taken
7fe54472b3 feat(ext/popup): devices view — list devices with revoke actions
Shows registered devices with "← you" indicator on current device.
Revoke button on other devices. Unregistered banner if current
device not in list.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-27 00:19:59 -04:00
adlee-was-taken
9fbf9bb3ee feat(ext/popup): trash view — list trashed items with restore/purge
Shows trashed items sorted newest-first with restore buttons.
Empty trash button purges all items + orphan blobs. Header shows
count and days until oldest auto-purges.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-26 19:28:56 -04:00
adlee-was-taken
39a8e12438 feat(ext/sw): get_field_history handler
Decrypts item and calls WASM get_field_history to extract tracked
field history for the popup's history view.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-26 17:49:59 -04:00
adlee-was-taken
d2cb6d8461 feat(ext/sw): trash operations — listTrashed, restoreItem, purgeItem, purgeAllTrash
listTrashed filters manifest for trashed_at != null, sorted newest-first.
restoreItem clears trashed_at. purgeItem deletes item + attachments.
purgeAllTrash also scans for orphan blobs in attachments/ directory.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-26 15:57:08 -04:00