From 13512ed9bfbaaadecd8d12b66389e76fea5ecb6b Mon Sep 17 00:00:00 2001 From: adlee-was-taken Date: Fri, 26 Jun 2026 00:39:54 -0400 Subject: [PATCH] docs: record v0.9.0 org-write spike decision + device-key prereq (PM) Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_013Hc6Rvdz3DxLucqNtPE2iP --- STATUS.md | 17 ++++++++++++++++- .../2026-06-20-extension-org-gui-design.md | 10 ++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/STATUS.md b/STATUS.md index 3fe69a2..3705e81 100644 --- a/STATUS.md +++ b/STATUS.md @@ -6,7 +6,22 @@ **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. -**Next track:** **v0.9.0 — extension org GUI + pluggable second factor.** 2 specs + 5 plans committed (`74cee8a`), 0/140 tasks executed — ready for a `mode:"multi"` kickoff. +**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. + +## v0.9.0 lift — in progress (kicked off 2026-06-25) + +Two-track multi-agent lift (5 streams, relay-coordinated; PM + Dev-A..E): +- **Org track (merge order A→B→C):** Dev-A org SW+WASM foundation, Dev-B org read UI, Dev-C org write. +- **Keyfile track (merge order D→E):** Dev-D keyfile core/cli/wasm, Dev-E keyfile extension + positioning pivot. Independent of the org track. + +**Org-write transport decision (2026-06-25) — recorded per the org-GUI spec's spike gate:** +- Spike (Dev-C, `426b82a`; doc `docs/superpowers/spikes/2026-06-20-org-signed-commit-spike.md`): **signature mechanism is GO** — a SW-built SSHSIG commit (raw `sign_for_git` + manual SSHSIG framing, no new WASM export) passes both `git verify-commit` and `relicario-server verify-org-commit`, incl. item+manifest dual-write + grant/slug authz. Hook matches the **signing-key fingerprint** → `members[].ed25519_pubkey` (committer text free-form, not checked). +- **Transport:** Gitea has **no write Git Data API at any version** (research vs 1.26.4 latest / roadmap / `repo_file.go`; Contents API can't carry a caller signature). The route is **native `git-receive-pack` packfile push via isomorphic-git** in the SW — host-agnostic, works at any Gitea version, preserves the SSHSIG byte-for-byte; extension `host_permissions` avoids the CORS proxy. ~68KB gzip dep. +- **Decision (user):** **build now in v0.9.0** as the **single universal write path for both Gitea + GitHub** (GitHub-only Git Data API approach retired). Plan C re-scoped. **Mini-spike PROVEN GO (2026-06-25):** isomorphic-git `commit({onSign})` embeds the SSHSIG (exact-payload, zero canonicalization risk) and passes `verify-commit` + the **real org pre-receive hook over `git-receive-pack`** — owner-signed accepted, non-member rejected; works at any Gitea version + GitHub. Now building the universal `commitSigned` (A/B-independent); org write handlers + UI held until A+B merge; **`/security-review` mandatory** on the push path before merge. Residual: 1 live-creds push (transport-only, confirm at integration). + +**Tracked follow-up bug (out of v0.9.0 scope unless trivial):** `extension/src/service-worker/gitea.ts` `putBlob` large-blob fallback calls nonexistent Gitea `/git` write endpoints → attachments over `BLOB_THRESHOLD_BYTES` 404 on a Gitea host (pre-existing ~v0.8.2). Found during the spike. + +**Org foundation scope addition (2026-06-26):** Dev-A discovered the extension never persists the device ed25519 private key — `DEVICE_STATE` (WASM `device.rs`) is in-memory only, wiped on every SW restart; `chrome.storage.local` stores only `vaultConfig`/`imageBase64`/`device_name`. The org-GUI spec's assumption of `chrome.storage.local.device_private_key` was wrong; the device-auth key lifecycle was never completed (the extension never used `sign_for_git`). Since `org_unwrap_key` (read) **and** `sign_for_git` (Dev-C write) both need it, A is building **device-key persist+restore (Variant Y)** as a foundation prereq (Task 4.5): encrypt the device key under the personal master key at register, restore into `DEVICE_STATE` at unlock (decrypt **inside** WASM — the private key never crosses to JS), and refactor `org_unwrap_key(keys_blob)` to read `DEVICE_STATE`. Expands the org track scope/timeline; keyfile track (D/E) unaffected. ## What landed on main since the v0.5.0 version bump 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 c7791dd..d53b7f7 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 @@ -30,6 +30,16 @@ Therefore org **write** from the extension must construct and push a **signed co The spike result is recorded back into this spec and `STATUS.md` before A3 build work starts. +### Spike result + transport decision (resolved 2026-06-25) + +The spike (Dev-C, commit `426b82a`; evidence in `docs/superpowers/spikes/2026-06-20-org-signed-commit-spike.md`) returned **GO on the signature mechanism**: a SW-built SSHSIG commit (raw `sign_for_git` + manual SSHSIG framing — **no new WASM export**) passes both `git verify-commit` and `relicario-server verify-org-commit`, including the item+manifest dual-write with grant/slug authz. The hook matches the **signing-key fingerprint → `members[].ed25519_pubkey`** (committer/author text is free-form, not checked — this corrects the "committer identity must match" assumption). + +**Transport finding (corrects this section's planned approach):** the GitHub-style Git Data API path is **GitHub-only**. Research (vs Gitea 1.26.4 / roadmap / the `repo_file.go` structs / open issues #30955, #22100) confirms **no Gitea version exposes a write Git Data API or accepts a caller-supplied commit signature** (the Contents API signs with the instance key or not at all), and none is planned. So A3 does **not** use the host REST API. Org write ships via **native `git-receive-pack` packfile push (isomorphic-git in the SW)** — host-agnostic, works at any Gitea version, preserves the SSHSIG byte-for-byte; the extension's `host_permissions` removes the usual CORS-proxy requirement. This becomes the **single universal write path for both Gitea and GitHub** (~68KB gzip dependency). + +**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. + +**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 An org vault is a second git repo alongside the personal vault, cryptographically isolated (org spec § Architecture). The org master key is a random 256-bit key, wrapped per member via ECIES (X25519 + XChaCha20-Poly1305) to their device ed25519 key. The extension mirrors the CLI: unwrap the org key with the device private key, decrypt items exactly as the personal vault does — but with the org key, not the Argon2id-derived personal key.