Additive conflict in extension/src/wasm.d.ts resolved by keeping BOTH the
org_* declarations (org_unwrap_key/org_manifest_decrypt/org_manifest_encrypt)
and the keyfile_* declarations (unlock_with_secret/keyfile_encode/keyfile_decode).
Rust lib.rs (core + wasm) auto-merged cleanly with both feature sets.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014s527M917W47LDrfQ4t47g
Introduces OrgConfig/OrgConfigSummary types, chrome.storage.local backing
via org-config.ts, and the first org SW message (org_list_configs) wired in
all three required places: PopupMessage union, POPUP_ONLY_TYPES set, and a
case arm in popup-only.ts. Handler projects to OrgConfigSummary — apiToken,
hostUrl, repoPath, memberId never leave the SW. 3 new TDD tests (RED→GREEN).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014s527M917W47LDrfQ4t47g
Add `--key-file <path>` to `relicario init` as a mutually exclusive
alternative to `--image`/`--output`. When given, a 32-byte secret is
generated with OsRng, armored via `keyfile_encode`, written to the
supplied path (gitignored, never committed), and `params.json` records
`"second_factor": "keyfile"`. The `--image` arg is now Option<PathBuf>;
existing image-vault callers are unchanged. `ParamsFile::for_new_vault`
now takes the second-factor variant as an explicit argument.
Tests: un-ignore init_keyfile_then_unlock_keyfile_round_trips (full
round-trip), strengthen its assertion to check for distinctive username
"octocat", and add init_keyfile_writes_relkey_and_keyfile_params
(armor, params parse, gitignore). Full workspace green (0 failures),
clippy clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01McMF5pUJbCMricmFEnf2sG
clearAll is the security-zero path: each .free() in its own try/catch so a
single throwing handle can't leave the others un-zeroed; state is fully reset
regardless. setOrg frees any prior handle for the same org before overwriting
so a re-unlock can't orphan an un-zeroed WASM key. Tests updated to the
best-effort contract (session.test.ts) plus independent-mock, throwing-free,
and re-unlock cases (org-session.test.ts).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014s527M917W47LDrfQ4t47g
Adds three WASM bindings for the pluggable second-factor work:
- unlock_with_secret: mirrors unlock() but accepts a raw 32-byte secret
instead of a carrier image, skipping imgsecret::extract
- keyfile_encode: armors a 32-byte secret into relicario-keyfile-v1 format
- keyfile_decode: decodes keyfile armor back to the raw 32-byte secret
Adds native equivalence test proving that unlock_with_secret and unlock
derive an identical Argon2id master key when given the same secret,
passphrase, salt, and KDF params. Comparison is direct (master-key bytes),
not cross-decrypt, so the test is both native-safe and unambiguous.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01McMF5pUJbCMricmFEnf2sG
Review fixes for Task 1 (org_unwrap_key). Public signature unchanged.
- Add device::extract_ed25519_seed -> Zeroizing<[u8;32]>: length-checks the
slice then copy_from_slice straight into the Zeroizing buffer, so the raw
seed never lands in a plain [u8;32] (Important #1).
- Refactor device::sign() to call the new helper (pure refactor; device/sign
roundtrip tests unchanged and green) (Important #2).
- org_unwrap_key now calls relicario_core::device::extract_ed25519_seed
directly; drop the duplicated local decode_device_seed helper and remove the
redundant ssh-key dep from relicario-wasm (core already pins it) (Important #2).
- Device-key-form finding (OpenSSH PEM, not base64; spec name matches reality)
moved onto the core helper, condensed copy kept on org_unwrap_key.
- Test helper keeps the private key as Zeroizing<String> and passes &priv
(derefs to &str) — no plain-String copy (Minor #3).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014s527M917W47LDrfQ4t47g
Expose org_unwrap_key(keys_blob, device_private_openssh) to let the
extension SW unwrap a member's ECIES-wrapped org master key into an
ordinary Zeroizing session handle. The returned handle is key-agnostic
and works with the existing item_decrypt/manifest_decrypt AEAD.
Key-form finding: device private keys are OpenSSH PEM blobs produced by
relicario_core::device::generate_keypair(), not base64 raw bytes. The
plan's "device_private_key_base64" parameter name was misleading; all
code and the .d.ts declaration use "device_private_openssh" to match
the spec and reality.
Adds ssh-key dep to relicario-wasm for PEM parsing in decode_device_seed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014s527M917W47LDrfQ4t47g
Add SecondFactor enum (Image | Keyfile, serde lowercase) to relicario-core::crypto,
re-export from lib.rs alongside KdfParams. Add #[serde(default)] second_factor field
to ParamsFile in relicario-cli::session so existing params.json files (no field) still
parse as Image. for_new_vault() initialises the field to Image. No unlock logic
changed; that is deferred to Task 4. TDD: tests written RED first, then GREEN.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01McMF5pUJbCMricmFEnf2sG
Extension-only patch: fixes vault create/attach in the browser extension, where
chrome.runtime.sendMessage silently dropped ArrayBuffer payloads (carrier JPEG →
0 bytes → opaque "no SOF marker found in JPEG"). See CHANGELOG for the full set.
- Version bumps: extension 0.7.0→0.8.2 (it had no changes in 0.8.0/0.8.1);
relicario-core/-cli/-wasm 0.8.1→0.8.2 for unified-tag consistency. The Rust
core/CLI are functionally unchanged from v0.8.1. relicario-server stays 0.1.1
(independent version line; no functional change).
- CHANGELOG.md: v0.8.2 section. STATUS.md / ROADMAP.md: v0.8.2 landed, v0.9.0
flagged as ready to kick off.
Verify: cargo build/test/clippy -D warnings all green (35 test binaries);
extension 435/435 vitest, build:all clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pe8qw5KePDqAEBsAxnVQuJ
clippy 1.93.0 added/tightened several lints that broke `cargo clippy -D warnings`
(the release gate). All behavior-preserving:
- unnecessary_map_or: `signer_parent.map_or(false, |r| …)` → `is_some_and`
(relicario-server/src/main.rs).
- needless_question_mark: drop redundant `Ok(… ?)` wrappers around
`serde_json::from_str(…).context(…)` in org_session.rs load_meta/load_members/
load_collections.
- print_literal: inline the trailing "FLAG" header literal in `org audit`.
- items_after_test_module: move `configure_git_signing` above the
`seed_helper_tests` module in device.rs so the test module is last.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pe8qw5KePDqAEBsAxnVQuJ
- Closed vault drawer now negates its 440px via margin-right so it reserves no
layout space — the list pane and its empty state center in the full
post-sidebar width instead of being shifted left by the off-screen drawer.
- .vault-lock-screen gets flex:1 so the locked vault tab grows to full viewport
width instead of collapsing to content width and pinning to the left.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pe8qw5KePDqAEBsAxnVQuJ
chrome.runtime.sendMessage serializes with a JSON-like algorithm that drops
ArrayBuffer/TypedArray payloads — they arrive as empty objects, so a carrier
JPEG reached the service worker as 0 bytes and failed with the opaque WASM
error "no SOF marker found in JPEG". This broke extension vault creation.
- shared/message-binary.ts: encodeBinary/decodeBinary deep-walk a message tree
and base64-envelope every binary buffer so it survives the channel (chunked
base64 to avoid stack overflow on multi-MB buffers); plain number[] untouched.
- Wire encode/decode at all four message boundaries: popup, service-worker
onMessage listener (decode request, encode response), setup swSend, vault
postToServiceWorker.
- handleCreateVault: magic-byte guard classifies empty / non-JPEG carriers into
actionable errors (carrier_image_empty / carrier_image_not_jpeg) instead of
the opaque WASM error; error-copy.ts adds user-facing text; setup errors now
route through lookupErrorCopy.
- router SETUP_ALLOWED: add create_vault / attach_vault / generate_recovery_qr —
the Phase-3 setup wizard sends these from the setup page and they were being
rejected as unauthorized_sender.
- Tests: message-binary round-trip suite (7) + carrier-guard (2) + setup
allowlist (3). 435/435 vitest, build:all clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pe8qw5KePDqAEBsAxnVQuJ
Product audit (product-expert skill) recommended two priority items; this
lands the audit record plus the two approved design specs that will drive
the v0.9.0 multi-agent train.
- reviews/2026-06-20-product-audit.md — the roadmap audit (reality check,
recommendations, PM brief) that drove the two items.
- specs/2026-06-20-extension-org-gui-design.md — bring the org vault to the
extension at read+write parity. Org write is gated on a Day-1 signing
spike (the org hook rejects unsigned commits; the extension pushes
unsigned today; sign_for_git exists in WASM but is unused). Spike-fail
degrades to read-only + write follow-up.
- specs/2026-06-20-pluggable-second-factor-design.md — key file as an
alternative second factor (same 32-byte secret, same KDF; crypto-light),
chosen at setup via a non-secret params hint, plus the positioning pivot.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VQbgrP6KQW5pibjbPEoTSs
A standalone, self-triggering skill that acts as Relicario's product
strategist: audits the roadmap and reviews freshly-brainstormed release
specs for product/market fit, emitting PM-ready relay directive blocks.
Advisory only — the user stays the decision-maker.
- Two modes: roadmap audit (default) and spec review (verdict:
PROCEED / RESCOPE / CUT / PIVOT).
- Four-lens engine run as parallel subagents: ground-truth (verify
claims vs code/git, distinguishing an in-flight lift from real drift),
jobs-to-be-done, market/competitive, and strategy synthesis.
- Fast by default; `deep` adds live competitive web research.
- Durable by design: lenses read living docs (README/ROADMAP/STATUS/
CHANGELOG/specs) at runtime, so new surfaces/segments/features are
picked up automatically. The one static asset, competitive-landscape.md,
carries a last-reviewed date + freshness protocol.
- Wires a post-brainstorm product gate into CLAUDE.md's Planning section.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VQbgrP6KQW5pibjbPEoTSs
All four v0.8.1 streams merged (main 4c0a289): org add now supports all 7 item
types (card/key/totp/document) and org edit is interactive. Flip the two
high-level org sections from "coming" to shipped, grounded in the real merged
`relicario org add <type> --collection …` surface; remove the rebase TODO markers.
Mark v0.8.1 shipped (all four streams merged on 4c0a289, verified against
source): org add/edit parity for all 7 item types (Card/Key/Totp + Document),
collection-scoped attachment storage, and the grant-scoped attachment-write
pre-receive hook. Move org item-type parity from deferred to shipped; relabel
the org-vault row as v0.8.0; reference the new extension-cli parity gap analysis
as the forward plan for deferred extension org read/write. Scope: STATUS.md +
ROADMAP.md only (CHANGELOG + version bumps owned by PM).
Document the attachments/<slug>/<item-id>/<att-id>.enc layout (exactly 3
segments, slug-authorized by the pre-receive hook, never decrypted
server-side) and DEFAULT_ORG_ATTACHMENT_MAX_BYTES = 10 MiB, citing
org_session.rs:24 and the mirrored personal default settings.rs:116.
A friendly, task-oriented guide for non-technical users: README index,
getting-started, concepts, items, passwords-and-generators, totp,
attachments-and-documents, organizing, sync-and-backup, the-browser-extension,
recovery, faq. Every command/flag derived from the actual CLI surface
(`relicario --help` tree) and real extension behavior — no invented flags.
Org item-type parity is covered high-level pending the v0.8.1 B/C merge
(two TODO markers left for the rebase).