diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b15157..804fc87 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,47 @@ # Changelog +## v0.9.0 — 2026-06-27 — org vault in the extension + pluggable second factor + +Two headline features, plus a positioning re-lead on the durable thesis: two +independent secrets into the KDF, self-hostable, a server that holds only opaque +ciphertext, and a git-backed audit log. + +### Added — Pluggable second factor (key file) +- The vault's 256-bit second factor can now be carried by a plain **key file** + (`.relkey`), chosen at creation, as an alternative to the steganographic + reference image. Same secret, same Argon2id KDF, same AEAD — only the container + changes (proven by a byte-identical master-key equivalence test). +- **CLI:** `relicario init --key-file ` generates the `.relkey`; unlock, + `recovery-qr`, and backup resolve the container from a non-secret + `second_factor` hint in `params.json` (`RELICARIO_KEYFILE` mirrors + `RELICARIO_IMAGE`). +- **Extension:** setup-wizard container choice (Reference Image | Key File), + `.relkey` download at creation, and unlock + attach via a key-file picker. +- Threat model: the `.relkey` / `keyfileBase64` holds the secret **in the clear** + — the same posture as the reference JPEG (gitignored / local-only, never pushed; + the server only ever sees ciphertext). Documented as equivalent, not weaker. + +### Added — Org (enterprise) vault in the browser extension (read + write) +- The enterprise org vault (CLI since v0.8.0/v0.8.1) now has full browser + presence: a **Personal / <org>** context switcher, grant-filtered browse + + view of all 7 item types, an **add-org** config flow, **add / edit / delete** of + org items, and an offline read-only indicator. Org admin + (members/collections/grants/rotate/audit) stays CLI-only by design. +- Org **writes** go through ed25519-**signed commits** the org pre-receive hook + accepts, pushed via **native git (`git-receive-pack`) with an in-browser + packfile encoder (isomorphic-git)** — host-agnostic, working on Gitea *and* + GitHub at any version (Gitea exposes no signature-carrying write REST API). The + org master key never touches persistent storage; org data never persists outside + the Zeroizing WASM session. +- **Device-key lifecycle completed:** the device's ed25519 private key is now + persisted encrypted under the vault master key and restored into the WASM + session at unlock — it never crosses into JS. + +### Versions +- extension + `relicario-core` / `relicario-cli` / `relicario-wasm` → `0.9.0`; + `relicario-server` stays `0.1.1` (independent line). `manifest.firefox.json` + corrected from a stale `0.7.0`. + ## v0.8.2 — 2026-06-25 — extension vault-creation fix (binary transport) Fixes a bug that broke **vault creation and attach in the browser extension**: diff --git a/Cargo.lock b/Cargo.lock index d352788..8d10055 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2156,7 +2156,7 @@ checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" [[package]] name = "relicario-cli" -version = "0.8.2" +version = "0.9.0" dependencies = [ "anyhow", "arboard", @@ -2188,7 +2188,7 @@ dependencies = [ [[package]] name = "relicario-core" -version = "0.8.2" +version = "0.9.0" dependencies = [ "argon2", "base64", @@ -2235,7 +2235,7 @@ dependencies = [ [[package]] name = "relicario-wasm" -version = "0.8.2" +version = "0.9.0" dependencies = [ "base64", "ed25519-dalek", diff --git a/ROADMAP.md b/ROADMAP.md index eda6f74..16d8601 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -7,6 +7,7 @@ | Version | Highlights | |---|---| +| **v0.9.0** *(2026-06-27)* | **Org vault in the browser extension (read + write) + pluggable second factor** (`6d6e9f8`, 5 streams under PM/relay coordination). **Org GUI:** Personal/<org> context switcher, grant-filtered browse + view of all 7 item types, add-org config flow, and add/edit/delete via ed25519-**signed commits** pushed over native git (`git-receive-pack` + an in-browser isomorphic-git packfile encoder — host-agnostic, Gitea *and* GitHub at any version; Gitea has no signature-carrying write REST API); org admin stays CLI-only. **Pluggable second factor:** a plain key file (`.relkey`) as an alternative to the stego reference image — same 256-bit secret + Argon2id KDF; CLI (`init --key-file`, `RELICARIO_KEYFILE`) + extension (wizard/unlock/attach); positioning re-led on the two-factor-KDF thesis. **Device-key lifecycle completed** (device private key persisted encrypted under the master key, restored into the WASM session at unlock, never crosses to JS). Extension 584/584; core/cli/wasm + extension → 0.9.0, `relicario-server` 0.1.1. | | **v0.8.2** *(2026-06-25)* | **Extension vault-creation fix** (`938174b`): binary-safe `chrome.runtime.sendMessage` transport (`message-binary.ts` — base64-envelopes `ArrayBuffer`/`TypedArray` payloads the channel otherwise drops, which had reduced carrier JPEGs to 0 bytes → opaque "no SOF marker found in JPEG", breaking vault create/attach) wired at all four message boundaries; carrier-image magic-byte guard with actionable errors; setup-wizard router allowlist (`create_vault`/`attach_vault`/`generate_recovery_qr`); vault-tab drawer/lock layout fixes. Extension-only (core/cli/wasm bumped 0.8.1→0.8.2 for tag consistency, `relicario-server` stays 0.1.1); 435/435 vitest. | | **v0.8.1** *(2026-06-20)* | **Org item-type parity + collection-scoped attachments + grant-scoped hook** (`4c0a289`, four parallel streams): `relicario org add`/`edit` now cover **all 7 item types** — Card/Key/Totp (Dev-B `6e73c5e`) and Document (Dev-C `4c0a289`) on the shared `item_build` foundation (Dev-A `b09e0ce`); org attachments stored collection-scoped at `attachments///.enc` with a default cap (Dev-C); `relicario-server` `classify_path` grant-scopes those attachment writes (Dev-D `db4e05a`, server `0.1.1` — **requires pre-receive hook redeploy**). **Still deferred:** extension org read/write (forward plan: `docs/superpowers/specs/2026-06-20-extension-cli-parity-gap-analysis.md`); org phase 2. | | v0.8.0 *(2026-06-20)* | **Enterprise org vault — backend complete** (`7392795`): relicario-core `org` module (ECIES X25519 key wrap/unwrap, `OrgRole`/`OrgMember`/`OrgManifest` types, `filter_for_member`, `schema_version: 1`); relicario-server org hook (`verify-org-commit`: signature verification, path-scoped authz, `enforce_owner_only_elevation` on parent role, `enforce_schema_monotonicity`, `generate-org-hook`, new `[lib]` target); relicario-cli — all 19 `relicario org` subcommands: init, add-member/remove-member/set-role, create-collection/grant/revoke, rotate-key (re-encrypts all blobs), transfer-ownership, delete-org, status, audit, and item CRUD (add/get/list/edit/rm/restore/purge). Org item-type parity for Card/Key/Document/Totp shipped subsequently in v0.8.1; extension org parity + phase 2 (SSO/LDAP, read audit, per-collection subkeys, HTTP plane) remain deferred. | @@ -18,10 +19,9 @@ See `CHANGELOG.md` for tagged-release detail and `STATUS.md` for the per-train c ## Up next -All three 2026-05-04 architecture-review specs are shipped; the enterprise org vault backend (v0.8.0) and org item-type parity + collection-scoped attachments (v0.8.1) are shipped. Forward plan for extension parity: `docs/superpowers/specs/2026-06-20-extension-cli-parity-gap-analysis.md`. Pending items in rough priority order: +All three 2026-05-04 architecture-review specs are shipped; the enterprise org vault (backend v0.8.0/v0.8.1, **extension read + write in v0.9.0**) and the **pluggable second factor (v0.9.0)** are shipped. Pending items in rough priority order: -- **Extension org parity — read** — org switch + collection-filtered browse in the popup/vault tab (Dev-D, deferred) -- **Extension org parity — write** — `org add`/`edit`/`rm` from the extension (Plan B-2; the CLI side reached all-7-type org write in v0.8.1, so this is unblocked CLI-side) +- **v0.9.0 follow-ups** — `backup --include-keyfile` (key-file vault backups); the `gitea.ts putBlob` latent bug (large attachments 404 on Gitea); Dev-A's M1 (one-time device-migration user notice) + M2 (corrupt `device_key_enc` recovery path); org-write m2 (TOCTOU manifest dual-read) + m5 (zero-grant `+new` button / push-rejection error copy) - **Personal-side extension gaps** — favorites UI, group/tag/filter editing across all type forms, attachment-remove router wire + per-item purge UI, autofill registrable-domain matching (per the parity gap analysis) - **Phase 4: command palette** — ⌘K global search + action dispatch across the vault tab (no spec yet) diff --git a/STATUS.md b/STATUS.md index 1ad9212..1ab0955 100644 --- a/STATUS.md +++ b/STATUS.md @@ -4,9 +4,8 @@ ## Version -**Last release tagged:** v0.8.1 (`2fa4d68`, 2026-06-20) — org item-type parity + collection-scoped attachments, on top of v0.8.0 enterprise org vault. (v0.7.0 extension restructure; v0.6.0 rolled up Phase 2B / v0.5.1 / 1C-γ / Plan B / management-surfaces / doc-structure.) -**Cutting now:** **v0.8.2 — extension vault-creation fix.** Binary-safe `chrome.runtime.sendMessage` transport (`message-binary.ts`) + carrier-image magic-byte guard + setup-wizard router allowlist + vault-tab drawer/lock layout. Extension-only; core/cli/wasm bumped 0.8.1→0.8.2 for tag consistency, `relicario-server` stays 0.1.1. Merged to main `938174b`; `release: v0.8.2` doc/tag commit pending. -**In flight:** **v0.9.0 — extension org GUI + pluggable second factor.** Multi-agent lift kicked off 2026-06-25 (5 streams, relay-coordinated). **Org-write scope decision (2026-06-25):** the signed-commit spike proved the signature mechanism (GO), but research found Gitea has **no write Git Data API at any version** (1.26.4 latest; none on roadmap; Contents API can't carry a caller signature) — so org-write ships via **native `git-receive-pack` packfile push (isomorphic-git in the SW), a single universal write path for both Gitea + GitHub**. User chose **build-now in v0.9.0** (not defer). Details in the v0.9.0 lift block below. +**Last release tagged:** v0.8.2 (2026-06-25) — extension vault-creation fix (binary-safe `chrome.runtime.sendMessage` transport + carrier guard + setup-router allowlist + drawer/lock layout); core/cli/wasm bumped for tag consistency, `relicario-server` stays 0.1.1. (v0.8.1 org item-type parity + collection-scoped attachments; v0.8.0 enterprise org vault backend.) +**Cutting now:** **v0.9.0 — org vault in the extension + pluggable second factor. FEATURE-COMPLETE.** All 5 streams merged to main (`6d6e9f8`): D keyfile core/cli/wasm + minors + E keyfile extension; A org foundation + B org read-UI/add-org + C org signed-write. Full gate green (cargo 0-failed, clippy `-D warnings`, extension **584/584**, build:all). Versions bumped to **0.9.0** (extension manifests + core/cli/wasm; server stays 0.1.1; `manifest.firefox.json` 0.7.0-drift fixed), CHANGELOG written, Cargo.lock regenerated. **Pending tag:** user smoke-test + explicit approval. Pre/post-tag follow-ups tracked: Dev-A M1 (device-migration user notice) + M2 (corrupt `device_key_enc` recovery); `backup --include-keyfile`; `gitea.ts putBlob` latent bug; worktree cleanup (stale v0.8.1 + review worktrees). Lift detail in the block below. ## v0.9.0 lift — in progress (kicked off 2026-06-25) @@ -16,7 +15,7 @@ Two-track multi-agent lift (5 streams, relay-coordinated; PM + Dev-A..E): **Merge progress (as of 2026-06-27):** - **Keyfile track ✅ COMPLETE** — Dev-D keyfile core/cli/wasm (`588495f`), Dev-D security-review minors (`2262272`: `keyfile_encode`→`Zeroizing` + a backup-keyfile-error test + the no-oracle malformed-vs-wrong pair), Dev-E keyfile extension + positioning docs (`2ff5e5b`). All merged + verified, each two-review-clean (PM diff read + independent security subagent for D; E's mandatory `/security-review` no-findings + opus whole-branch + PM SECURITY.md read). The **pluggable second factor ships end-to-end** (CLI `init --key-file`/unlock/recovery-qr + extension wizard/unlock/attach), documented honestly (secret in the clear but gitignored/local-only and never pushed; not weaker than stego). -- **Org track — A ✅ + B ✅ MERGED; C in progress (final stream):** Dev-A org foundation merged (`33dd018`) — device-key persist/restore (Variant Y, key never to JS), multi-context session zero-all, ECIES org unwrap, grant-filtered read; security PASS-WITH-MINOR with **I1** (free the orphaned org-key handle on the `openOrg` error path) + the error-copy coverage gap both fixed pre-merge. Dev-B org read UI + the **added config-write flow** (`org_add_config` + add-org form — `org_list_configs` read `orgConfigs` but nothing wrote them) merged (`a530e44`); its opus review caught + fixed a **CRITICAL read-only misroute** (org detail/drawer showed edit/trash firing *personal* update/delete with the org id) — read-only now enforced across list/detail/drawer/sidebar. Dev-C (org write) building Tasks 3-5 (`org_add/update/delete` via the universal `commitSigned`, honoring the manifest landmine + client grant check; write UI on B's surfaces; acceptance) + the mandatory `/security-review` → its REVIEW-READY is the org track's final merge. +- **Org track — ✅ COMPLETE (A → B → C all merged):** Dev-A org foundation merged (`33dd018`) — device-key persist/restore (Variant Y, key never to JS), multi-context session zero-all, ECIES org unwrap, grant-filtered read; security PASS-WITH-MINOR with **I1** (free the orphaned org-key handle on the `openOrg` error path) + the error-copy coverage gap both fixed pre-merge. Dev-B org read UI + the **added config-write flow** (`org_add_config` + add-org form — `org_list_configs` read `orgConfigs` but nothing wrote them) merged (`a530e44`); its opus review caught + fixed a **CRITICAL read-only misroute** (org detail/drawer showed edit/trash firing *personal* update/delete with the org id) — read-only now enforced across list/detail/drawer/sidebar. Dev-C org signed-write merged (`6d6e9f8`): `org_add/update/delete` via the universal `commitSigned`, manifest landmine honored, client grant check, write UI on B's surfaces; **the C1 empty-id data-loss bug was caught by the independent security-review subagent and fixed pre-merge** (the dev's own `/security-review` AND the PM diff read both missed it — it was masked by a test that hard-coded the item id; the third independent pass earned its keep); `/security-review` clean. All three org streams merged; full gate green (extension 584/584). - **Pre-tag follow-ups:** Dev-A M1 (one-time user notice that the device-key migration mints a new device identity) + M2 (recovery path for a corrupt `device_key_enc`); `backup --include-keyfile` sibling; the `gitea.ts putBlob` latent bug; stale-worktree cleanup (3 v0.8.1 worktrees + 2 leftover review worktrees in scratchpad). **Org-write transport decision (2026-06-25) — recorded per the org-GUI spec's spike gate:** diff --git a/crates/relicario-cli/Cargo.toml b/crates/relicario-cli/Cargo.toml index 1ec2330..024b203 100644 --- a/crates/relicario-cli/Cargo.toml +++ b/crates/relicario-cli/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "relicario-cli" -version = "0.8.2" +version = "0.9.0" edition = "2021" description = "CLI for relicario password manager" license = "GPL-3.0-or-later" diff --git a/crates/relicario-core/Cargo.toml b/crates/relicario-core/Cargo.toml index e8274ea..e14eec7 100644 --- a/crates/relicario-core/Cargo.toml +++ b/crates/relicario-core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "relicario-core" -version = "0.8.2" +version = "0.9.0" edition = "2021" description = "Core library for relicario password manager" license = "GPL-3.0-or-later" diff --git a/crates/relicario-wasm/Cargo.toml b/crates/relicario-wasm/Cargo.toml index 08065cc..4648c84 100644 --- a/crates/relicario-wasm/Cargo.toml +++ b/crates/relicario-wasm/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "relicario-wasm" -version = "0.8.2" +version = "0.9.0" edition = "2021" description = "WASM bindings for relicario password manager" license = "GPL-3.0-or-later" diff --git a/docs/superpowers/specs/2026-06-20-extension-org-gui-design.md b/docs/superpowers/specs/2026-06-20-extension-org-gui-design.md index d53b7f7..0942cd9 100644 --- a/docs/superpowers/specs/2026-06-20-extension-org-gui-design.md +++ b/docs/superpowers/specs/2026-06-20-extension-org-gui-design.md @@ -38,6 +38,8 @@ The spike (Dev-C, commit `426b82a`; evidence in `docs/superpowers/spikes/2026-06 **Decision (user, 2026-06-25): build the universal packfile-push write path now, in v0.9.0** — not the deferred fallback. A3 (Plan C) is re-scoped: a small SSHSIG-through-isomorphic-git mini-spike, then the universal `commitSigned`, then the write handlers + UI (held until A0–A2 land). A focused `/security-review` on the push path is mandatory before merge. +**SHIPPED in v0.9.0 (merged `6d6e9f8`, 2026-06-27):** org read (A0–A2 + A4-read) and org write (A3) both landed end-to-end. The mini-spike confirmed SSHSIG through isomorphic-git `onSign` against the real pre-receive hook (member accepted / non-member rejected); the write handlers honor the manifest-no-wipe landmine + a client-side grant check; the mandatory `/security-review` passed, and the independent merge-time review additionally caught + fixed a critical empty-id data-loss bug on org add (`new_item_id` is now minted server-side-style in the handler). An "add-org config" write flow was added (Dev-B) since `org_list_configs` read `orgConfigs` but nothing wrote them. Org admin remains CLI-only per scope. + **Latent bug noted (separate follow-up):** `extension/src/service-worker/gitea.ts` `putBlob` calls nonexistent Gitea `/git` write endpoints — large attachments would 404 on a Gitea host (pre-existing ~v0.8.2). ## Architecture diff --git a/extension/manifest.firefox.json b/extension/manifest.firefox.json index 984788b..7a7c55c 100644 --- a/extension/manifest.firefox.json +++ b/extension/manifest.firefox.json @@ -1,7 +1,7 @@ { "manifest_version": 3, "name": "Relicario", - "version": "0.7.0", + "version": "0.9.0", "description": "Two-factor encrypted password manager", "icons": { "16": "icons/icon-16.png", diff --git a/extension/manifest.json b/extension/manifest.json index 468bf03..9be7f9f 100644 --- a/extension/manifest.json +++ b/extension/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 3, "name": "Relicario", - "version": "0.8.2", + "version": "0.9.0", "description": "Two-factor encrypted password manager", "icons": { "16": "icons/icon-16.png", diff --git a/extension/package.json b/extension/package.json index 053af6d..22f60cf 100644 --- a/extension/package.json +++ b/extension/package.json @@ -1,6 +1,6 @@ { "name": "relicario-extension", - "version": "0.8.2", + "version": "0.9.0", "private": true, "scripts": { "build": "webpack --mode production",