Wraps ssh-key's PublicKey::fingerprint(HashAlg::Sha256). Output format
matches ssh-keygen -lf and git verify-commit --raw stderr
(SHA256:<43-char base64>). Used by the upcoming relicario-server
verify-commit rewrite (audit S1).
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
- register_device() generates signing + deploy keypairs via core device
module, stores them in DEVICE_STATE (once_cell Lazy<Mutex>), and
returns only public keys to JS
- sign_for_git() signs data using the internal signing key
- get_device_info() returns name and public keys; returns null if not
registered
- clear_device() zeroes and drops device state (logout / re-registration)
- Removed generate_device_keypair() which exposed raw private key bytes
Fixes audit I5: private key material no longer crosses the WASM boundary.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove device from devices.json
- Append to revoked.json with timestamp and revoked_by
- Delete Gitea deploy key (best-effort, warns if env vars missing)
- Always commit both devices.json and revoked.json together
- Print revoked signing public key for audit confirmation
- Guard against revoking the current device (would lose push access)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- verify-commit command checks signature against devices.json
- generate-hook outputs installable pre-receive script
- Foundation for server-side enforcement
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
OpenSSH-format keypair generation, signing, and verification.
Foundation for device authentication.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
per_vault_soft_cap_bytes and per_vault_hard_cap_bytes were defined in
VaultSettings but never checked. Now enforced in cmd_attach with
warning at soft cap, error at hard cap.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Control characters (newlines, tabs) in item titles corrupted git log
output. Now strips control chars and truncates to 50 chars.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Crafted .relbak files with IDs like "../../.bashrc" could escape the
target directory. Now validates that item/attachment IDs are hex-only
via is_valid() before any fs::write.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
RELICARIO_TEST_PASSPHRASE and friends were checked in production code,
exposing the passphrase via /proc/<pid>/environ and shell history.
Now only compiled into debug binaries via cfg(debug_assertions) helper
functions. Release builds compile the helpers to return None, so the
env var names are absent from the release binary (verified via strings).
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
HOTP requires incrementing and persisting the counter after each use.
Without vault-save machinery in compute_totp_code, HOTP would desync
immediately. Now returns HotpNotSupported error.
TOTP and Steam codes continue to work.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- AttachmentId now uses 16 bytes of SHA-256 (128 bits) instead of 8,
requiring ~2^64 work for birthday collision instead of ~2^32.
- Added is_valid() to ItemId and AttachmentId for path traversal
prevention during backup restore.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Backup KDF was passing raw passphrase bytes to Argon2id without NFC
normalization, causing cross-platform restore failures for non-ASCII
passphrases (macOS NFD vs Linux NFC).
Now matches derive_master_key behavior from crypto.rs.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Brand name uses capital R in user-facing text — extension UI strings,
CLI clap help / descriptions / error prose, markdown docs. Lowercase
preserved for the binary command, crate names, npm package, file
paths, env vars, and code identifiers.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- import_lastpass.rs: note that password and extra are intentionally
not trimmed (leading/trailing whitespace is significant for both).
- cmd_import_lastpass: document the coupling between the
ImportWarning message strings and the CLI summary's "skipped"
filter — partial-import warnings (TOTP/URL) must not contain
the word "skipped".
Comment-only; no behavior change. Catches I1 and M5 from the
final code review without taking on the cross-cut WarningKind
enum refactor (deferred to a follow-up if it ever ships).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Returns { items: [Item], warnings: [ImportWarning] } as a JSON
string. The items already have fresh IDs + timestamps; the SW
caller encrypts and writes them through the existing
item_encrypt + manifest_encrypt bridges.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Unlocks the vault, parses the CSV, encrypts each item, writes
items/<id>.enc and manifest.enc, then a single
`git add … && git commit` covers all of them. Stderr progress
every 50 items + final summary. Exit non-zero only when zero
items imported.
Adds the Import command group with a Lastpass subcommand.
Stub returns `not implemented` so the help text is reachable
ahead of the body landing in Task 8.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Bad URLs in login rows downgrade to url: None with a warning
rather than skipping the row. Header mismatches (extra columns,
wrong order) surface ImportCsvHeader. Quoted commas, multi-line
extra, unicode all parse cleanly via the csv crate's defaults.
Rows with url == "http://sn" map to SecureNoteCore with extra
copied verbatim into the body. LastPass-packed structured data
(credit cards, addresses) flows through unparsed — users can
re-categorize manually post-import.
SecureNote rows skip the password-required check that applies
to Logins.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Successful base32 decode attaches a SHA1/6/30s Totp config to
LoginCore.totp. Bad base32 emits a warning and imports the login
without TOTP rather than skipping the row entirely.
Refactors map_row to return (Option<Item>, Option<ImportWarning>)
so a single row can produce both an item and a warning.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Map LastPass grouping/fav/extra columns to relicario item metadata.
Grouping becomes item.group, fav="1" sets item.favorite, extra becomes item.notes.
Multi-line extra via CSV quoting round-trips correctly.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Pins the parse_lastpass_csv signature and ImportWarning shape.
A single LastPass row with name/url/username/password round-trips
to a Login item with a freshly-minted ID. Header validation
rejects shape mismatches with a clear message.
TOTP, grouping, fav, SecureNote rows, and error paths land in
Tasks 3-6.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds csv = "1" to relicario-core; introduces
ImportCsvHeader and ImportCsvFormat. Foundation for the
import_lastpass module landing in Task 2.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Locks the singular vs plural transition (1 minute ago vs 2 minutes
ago) and each bucket boundary (59→60s minutes, 3599→3600s hours,
86400→86400×2 days, etc.) so future tweaks can't silently regress
the user-facing labels.
Drop the dead `stdout.contains("last export:")` + `.to_lowercase()` fallback
in status_shows_last_backup_line and status_shows_recent_backup_after_export;
assert `stdout.contains("Last export:")` verbatim instead.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Reads .relicario/last_backup (written by cmd_backup_export). Format:
'never' for fresh vaults, '4 days ago' otherwise. Closes the
'is my backup stale?' question without leaving the terminal.
Refuses non-empty target, prompts for backup passphrase, writes the
full vault layout, untars .git/ when bundled or git-inits a fresh
'restore from backup <iso8601>' commit otherwise.
Also tightens error context on tar_directory's builder.finish().
Reads the vault layout from disk, prompts for backup passphrase
(zxcvbn-gated, independent of the live vault key), tars .git/
unless --no-history, optionally bundles the reference JPEG, and
atomic-writes the .relbak. Leaves .relicario/last_backup marker
for cmd_status.
Adds 'relicario backup' as a subcommand wrapping export and restore.
Stubs return 'not yet implemented' — handlers land in Tasks 8 and 9.
The existing top-level 'relicario restore <query>' (un-trash) is
untouched.
Covers bad magic, unsupported version, wrong passphrase, truncation,
and tampered ciphertext. The wrong-passphrase / tampered-tag pair both
collapse to RelicarioError::Decrypt — same opaque-failure contract as
the live vault.
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.
Adds zstd, tar, base64 to relicario-core; introduces
BackupBadMagic / BackupUnsupportedVersion / BackupSchemaMismatch.
Foundation for the backup module landing in Task 2.
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>
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>
generate_device_keypair returns an ed25519 keypair as JSON with hex pubkey
and base64 private key. get_field_history extracts tracked field history
from a decrypted item for the popup's history view.
Co-Authored-By: Claude <noreply@anthropic.com>