v0.0.2 - Add random mnemonic startup flow and multi-instance random socket naming
This commit is contained in:
@@ -68,8 +68,82 @@ This plan adopts a single foreground program model instead of a daemon model to
|
||||
|
||||
- Single test that launches the program (with mnemonic piped via stdin for automation), sends requests via client subcommand, verifies responses
|
||||
|
||||
### Phase G2 — Mnemonic generation at startup
|
||||
|
||||
Goal: let the user generate a brand-new BIP-39 mnemonic at startup instead of typing an existing one. The generated mnemonic is shown once and used for the session; no confirmation step is required.
|
||||
|
||||
Steps:
|
||||
|
||||
- Extend startup TUI: prompt user with `[E]nter existing mnemonic` / `[G]enerate new mnemonic` (default `E`).
|
||||
- New module function `mnemonic_generate(int word_count, char *out, size_t out_len)` in [`src/mnemonic.c`](../src/mnemonic.c:1):
|
||||
- `getrandom(2)` for entropy (16 bytes for 12 words, 32 bytes for 24).
|
||||
- Compute SHA-256 of entropy → first `entropy_bits / 32` checksum bits.
|
||||
- Concatenate entropy + checksum bits → slice into 11-bit groups.
|
||||
- Look up each group in the BIP-39 English wordlist → space-separated mnemonic.
|
||||
- Zeroize the entropy buffer immediately after use.
|
||||
- Default word count: 12 (no extra prompt).
|
||||
- TUI display:
|
||||
- Numbered word list (1..12).
|
||||
- Loud warning: "WRITE THIS DOWN — IT WILL NOT BE SHOWN AGAIN."
|
||||
- "Press any key to continue" — explicit no-confirmation policy per spec.
|
||||
- After display, the in-memory mnemonic is fed straight into the existing seed-derivation path; no separate buffer or persistence layer is introduced.
|
||||
- Tests in [`tests/test_mnemonic.c`](../tests/test_mnemonic.c:1):
|
||||
- `mnemonic_generate(12)` returns 12 valid BIP-39 English words.
|
||||
- `mnemonic_generate(24)` returns 24 valid BIP-39 English words.
|
||||
- Output validates against the existing `mnemonic_validate` function (round-trip).
|
||||
- Two consecutive calls return different mnemonics with overwhelming probability.
|
||||
|
||||
### Phase H — Multi-instance signer naming (random BIP-39)
|
||||
|
||||
Goal: allow multiple `nsigner` processes to coexist on one host by giving each instance a unique, human-readable abstract socket name.
|
||||
|
||||
Approach: at startup, pick two random BIP-39 English words and bind to `@nsigner_<word1>_<word2>` (e.g. `@nsigner_hairy_dog`).
|
||||
|
||||
Steps:
|
||||
|
||||
- New module `src/socket_name.{h,c}`
|
||||
- `int socket_name_random(char *out, size_t out_len);`
|
||||
- Calls `getrandom(2)` for 3 bytes (24 bits), splits into two 11-bit indices, looks up words from the BIP-39 English wordlist, formats `nsigner_<w1>_<w2>`.
|
||||
- BIP-39 English wordlist
|
||||
- Reuse the wordlist already linked via `nostr_core_lib` if exposed; otherwise vendor `src/bip39_english.c` as a 2048-entry `const char *[]`.
|
||||
- `src/server.c` bind logic
|
||||
- Accept the resolved name from caller.
|
||||
- Bind once. On `EADDRINUSE` and no `--socket-name` override, regenerate a fresh random name and retry up to 8 times. With override, fail immediately and report the override conflict.
|
||||
- `src/main.c` integration
|
||||
- Resolve name precedence: `--socket-name` (explicit) > random pick.
|
||||
- After mnemonic acceptance, derive/pick the name and pass it to the server.
|
||||
- TUI startup banner shows the friendly name and the socket address prominently so the user knows what to point clients at.
|
||||
- `nsigner list` subcommand
|
||||
- Reads `/proc/net/unix`, filters entries whose path starts with `\0nsigner_`, prints them as `@nsigner_<w1>_<w2>` along with inode/state if useful.
|
||||
- No protocol change; purely a discovery helper.
|
||||
- Tests
|
||||
- `tests/test_socket_name.c` — format check, both indices land in `[0, 2048)`, two consecutive calls produce different names with overwhelming probability.
|
||||
- `tests/test_integration.c` — launch with explicit `--socket-name nsigner_test_run` so the integration test is deterministic and isolated.
|
||||
|
||||
Privacy/UX notes:
|
||||
|
||||
- Random names leak nothing about the seed; the trade-off is the user must read the banner each launch to know which socket to address.
|
||||
- ~4.2M combinations × small concurrent process count = collision essentially never observed in practice; retry path exists for correctness.
|
||||
- Behavior of `--socket-name` is unchanged; it remains the deterministic override for scripts and tests.
|
||||
|
||||
## 4. Decisions log
|
||||
|
||||
### 2026-05-02 — Mnemonic generation at startup
|
||||
|
||||
1. Startup offers `[E]nter` or `[G]enerate` choice; default is `E`.
|
||||
2. Generated mnemonic uses `getrandom(2)` + BIP-39 English wordlist, default 12 words.
|
||||
3. Mnemonic is displayed exactly once with a "write this down" warning.
|
||||
4. **No confirmation step** — user is trusted to copy the words; we proceed straight to session use.
|
||||
5. The in-memory lifecycle is identical to a typed mnemonic (same secure buffer, same crash-wipe semantics).
|
||||
|
||||
### 2026-05-02 — Random per-launch signer naming
|
||||
|
||||
1. Multiple `nsigner` instances can run concurrently on one host.
|
||||
2. Default socket name is `@nsigner_<bip39_word1>_<bip39_word2>` chosen randomly at each launch.
|
||||
3. `--socket-name <name>` overrides the random pick for tests and scripted usage.
|
||||
4. `nsigner list` enumerates running signers via `/proc/net/unix` filtered on `nsigner_` prefix.
|
||||
5. Random naming was chosen over deterministic-from-mnemonic for v1 to avoid leaking any seed-derived identifier in the abstract namespace; deterministic naming may be revisited later.
|
||||
|
||||
### 2026-05-02 — Program not daemon pivot
|
||||
|
||||
1. Single foreground process replaces daemon + TUI + client multi-binary model
|
||||
@@ -102,3 +176,5 @@ This plan adopts a single foreground program model instead of a daemon model to
|
||||
- Execute Phase C (unified main)
|
||||
- Execute Phase D (policy simplification)
|
||||
- Execute Phase E (integration test)
|
||||
- Execute Phase G2 (mnemonic generation at startup)
|
||||
- Execute Phase H (multi-instance random naming + `list` subcommand)
|
||||
|
||||
Reference in New Issue
Block a user