diff --git a/docs/typescript_cutover_plan.md b/docs/typescript_cutover_plan.md index 41a4d104..1aebaf98 100644 --- a/docs/typescript_cutover_plan.md +++ b/docs/typescript_cutover_plan.md @@ -17,9 +17,16 @@ P0 gate is green, Python remains the normative reference | Cross-implementation profile interop | green | | Sync protocol parity | green | | Agent security model | green (TS ahead of Python) | -| Feature parity for daily use | green for daily use; 3 scoped exclusions | +| Feature parity for daily use | green for daily use; scoped exclusions below | +| Interactive mode (TUI) | green — legacy (v1) menu tree ported | | Packaging + release | green | | Migration + rollback story | green | +| Independent security review | **not done — the one open blocker** | + +The gates below are green. The remaining blocker is not a gate in this list: +no third party has reviewed the code. Two rounds of subagent review found 9 +criticals/highs between them that self-review missed entirely, which is the +evidence for treating that as load-bearing rather than optional. ## P0 gates (must be green to cut over) @@ -47,15 +54,20 @@ P0 gate is green, Python remains the normative reference and the TS CLI restores it with secrets intact. **Green.** 5. **Feature parity for real daily use.** **Green for daily use**, with - three scoped exclusions that are documented rather than hidden: + scoped exclusions that are documented rather than hidden: - **PGP RSA keys** — unsupported by design. PyCryptodome's seeded prime search is not reproducible, so TS refuses the key type instead of deriving a different key. ed25519 PGP is at byte-for-byte parity. - **`semantic`** (local vector search) — a derived index that can be rebuilt; plan §8.5 rates it P2. - **`api`** (FastAPI server) — a separate surface, not vault behavior. + - **TUI v2/v3** — interactive mode ports the legacy (v1) menu tree only. + - **QR display in the TUI** — no QR encoder in this build; the menu item + remains and offers the underlying value instead. + - **Script checksum verify/generate** — covers the Python source tree; + replaced by the release bundle's `.sha256`. - Recommendation: none of the three blocks cutover. Each is a bounded, + Recommendation: none of these blocks cutover. Each is a bounded, named gap with the Python implementation still available for it, and the migration guide tells users exactly that. Revisit if a real profile is found to depend on RSA PGP entries. @@ -93,8 +105,12 @@ P0 gate is green, Python remains the normative reference - `_system.index0` / atlas content merge — Python-derived state, recomputed on load; TS deliberately does not emit it and refuses to merge populated index0 rather than corrupt it. -- TUI v2/v3 — the plan explicitly does not port these; terminal UX is the - CLI plus any future interactive mode. +- TUI v2/v3 — not ported. Interactive mode (`seedpass-js` with no + subcommand) reproduces the **legacy v1** menu tree instead: the same eight + main-menu items, nine entry types, eighteen settings under the same + numbers, and the Profiles/Nostr/entry-action/edit submenus. Items this + build cannot perform keep their position and say why, so the numbering + Python users have memorised still selects the same thing. ## Cutover mechanics (once gates are green) @@ -114,3 +130,53 @@ The Python implementation stays in the repository and in CI as the compatibility oracle, not as a shipped product. If a future change makes the two diverge, the cross-impl suite fails and the change is wrong until proven otherwise. + +## Session log — 2026-08-15 + +Everything below was found by *running* the CLI, not by the 348-test suite. +That ratio is the most useful thing this session produced: keep exercising +the product by hand. + +**Parity gap closed: there was no way to create a vault.** Every path assumed +an existing seed and told the user to export `SEEDPASS_MNEMONIC`, so a new +user could not start at all. `fingerprint create` now generates one the way +Python does (32 bytes of OS entropy through BIP-85 app 39 index 0, with the +entropy call outside any try/catch so a CSPRNG failure can never be caught +and substituted). Delivery of the phrase is chosen *before* generation and +happens *before* the profile is created, so no failure can leave a vault +whose seed nobody holds; `--out` is 0600 + O_EXCL; a non-TTY stdout with no +destination refuses outright rather than writing the only copy of a seed +into a pipe. + +**Interactive mode added, then rebuilt.** The first version was list-and- +arrow-keys and was unnavigable to someone who knows SeedPass; the second +follows Python's legacy v1 menus item for item. Recorded because the lesson +generalises: parity means the interaction model, not just the feature list. + +**Bugs fixed:** bare `seedpass-js` printed help then `error: (outputHelp)`; +unknown commands printed their error twice; `use --exec` always exited 0, so +`use db-pass --exec ./deploy.sh && echo ok` reported success after a failed +deploy; the sink command spec split on whitespace and ignored quotes, so +`--exec 'sh -c "..."'` died on an unterminated string; `--tags work,dev` +silently stored one tag named `work,dev`; a stopped agent reported raw +`connect ENOENT`; echo came back on between password retries, so a password +retyped after a typo was echoed in clear text. + +**Stale claims corrected:** the capability map said SSH and PGP were not +ported (both are at parity), and the migration guide said there was no TUI. + +**Verified this session:** a TS-generated seed opened in Python with the same +fingerprint, password and TOTP code; 128 CLI + 220 core tests; 36/36 +cross-impl checks. `test/nostr.test.ts` is flaky under parallel load (live +relay) — it failed once and passed on rerun and three times in isolation. + +### Next + +1. **Independent security review before any real secrets.** The standing + recommendation, unchanged. +2. Fold the bundle checksum into the `release-integrity` signing workflow + (gate 6's remaining item). +3. Use the interactive mode for real work and report where it still does not + match muscle memory. +4. Deferred by the user until the project is ready to share: the Tessera + agent-plugin skill. diff --git a/docs/typescript_port_compatibility_matrix.md b/docs/typescript_port_compatibility_matrix.md index 810f80da..6c5b924b 100644 --- a/docs/typescript_port_compatibility_matrix.md +++ b/docs/typescript_port_compatibility_matrix.md @@ -46,6 +46,8 @@ until a later milestone. | Profiles + config (Python `~/.seedpass` layout) | P1 | green* | — | `fingerprint list/add/switch/remove`, `config get/set` over the Python directory layout (fingerprints.json, parent_seed.enc kdf/ct wrapper, index-key-encrypted config with ConfigManager defaults). *Layout-compatible and tested end-to-end in TS; opening a real Python-created profile is verified indirectly via the parent-seed wrapper fixture — direct cross-open test TODO. | | Scoped tokens + audit chain (plan §9.3 / M8 core) | P1 | green | — (live agent tests) | Bearer tokens issued/held by the agent (hash-stored, shown once) with read/use/reveal scopes, kind + label-regex constraints, TTL and use counts; token-mode CLI reads the index and materializes secrets agent-side, never sees the mnemonic, and can never escalate to owner ops. Audit log uses Python AuditLogger's chain scheme (HMAC(prev_sig+canonical payload), KEY_INDEX key); verify/tail commands; tamper detection tested. Not yet: approval gates, high-risk partitions, persistent token store. | | Session agent (vault unlock/lock) | new (TS-only) | green | — | ssh-agent-style unix-socket daemon holding seeds with TTL (0600 socket, in-memory only); seed resolution env -> agent; full lifecycle tested with no mnemonic in the environment. Designed enforcement point for the section-9.3 lease/token layer. Python has no equivalent (its lock is in-process TUI state). | +| Seed generation (new vault) | P0 | green | — | `fingerprint create`: 32 bytes of OS entropy through BIP-85 app 39 index 0, matching `PasswordManager.generate_bip85_seed`; 12 or 24 words. The entropy call is deliberately outside any try/catch (Python carries the same note from its entropy audit) so a CSPRNG failure can never be caught and a value substituted. Delivery is chosen before generation and happens before the profile is created, so no failure path leaves a profile whose seed was never delivered. **Was missing entirely until 2026-08-15** — a new user had no way to create a vault. | +| Interactive mode (legacy v1 TUI) | P1 | green | — | `seedpass-js` with no subcommand reproduces `src/main.py`'s menu tree: eight main-menu items, nine entry types under Add Entry, eighteen Settings items **under the same numbers**, plus Profiles, Nostr, entry-action and edit submenus. Line-based like Python's, blank input goes back. Not ported: TUI v2/v3, QR rendering (no encoder; the item offers the underlying value), semantic index, script checksums — each keeps its menu position and says why, so Python users' numbering still holds. Secret Mode matches Python: it routes reveals to the clipboard. Verified through a pty against a real vault and by 28 scripted-UI tests. | ## Environment coverage