This commit is contained in:
Laan Tungir
2026-05-02 12:31:26 -04:00
commit 268b33b6d3
37 changed files with 7947 additions and 0 deletions

104
plans/nsigner.md Normal file
View File

@@ -0,0 +1,104 @@
# nsigner Implementation Plan
This document is the implementation roadmap for `n_signer`.
Authoritative behavior documentation: [README.md](../README.md).
## 1. Architecture pivot: program not daemon
This plan adopts a single foreground program model instead of a daemon model to reduce operational complexity and eliminate persistent runtime artifacts.
- Single foreground process, not a background daemon
- No config files — all state entered at runtime via TUI
- No control socket — TUI is built into the program
- No separate binaries — one binary with subcommands
- Abstract namespace sockets — no filesystem artifacts
## 2. Module inventory (what exists, what stays, what goes)
| Module | Fate |
|---|---|
| `src/secure_mem.{h,c}` | **KEEP** unchanged |
| `src/mnemonic.{h,c}` | **KEEP** unchanged |
| `src/role_table.{h,c}` | **KEEP** data structures, **REMOVE** file-loading code |
| `src/selector.{h,c}` | **KEEP** unchanged |
| `src/enforcement.{h,c}` | **KEEP** unchanged |
| `src/dispatcher.{h,c}` | **KEEP** unchanged |
| `src/cjson/` | **KEEP** unchanged |
| `src/policy.{h,c}` | **REFACTOR**: remove file loading, add interactive approval, hardcode same-uid default |
| `src/daemon.{h,c}` | **REPLACE** with `src/server.{h,c}`: abstract namespace socket, poll-based, integrated with TUI |
| `src/control.{h,c}` | **DELETE** (control socket no longer needed) |
| `src/tui.c` | **DELETE** as separate binary (functionality moves into `src/main.c`) |
| `src/client.c` | **DELETE** as separate binary (becomes subcommand in `src/main.c`) |
| `src/main.c` | **REWRITE**: startup TUI + server loop + status display + client subcommand |
| `config/` | **DELETE** entirely |
| `tests/test_daemon_integration.c` | **DELETE** or rewrite |
| `tests/test_full_flow.c` | **REWRITE** for new architecture |
| `tests/test_policy.c` | **UPDATE** for new policy API |
## 3. Implementation phases (new)
### Phase A — Cleanup and removal
- Delete `config/`, `src/control.{h,c}`, `src/tui.c`, `src/client.c`
- Remove file-loading functions from `src/role_table.c` and `src/policy.c`
- Remove old daemon integration tests
### Phase B — Server module (replaces daemon)
- Add `src/server.{h,c}`: abstract namespace socket, poll-based accept loop
- Keep peer-cred extraction behavior on abstract socket
- Add integration point for TUI approval callbacks
### Phase C — Unified main with built-in TUI
- Startup phase: mnemonic prompt (echo off), word count confirm, optional role enrollment
- Transition to running phase: status display, activity log, hotkey handling
- Approval prompt overlay when policy requires it
- Client subcommand: `nsigner client '<json>'` connects to abstract socket, sends request, prints response
- Signal handling: `SIGINT`/`SIGTERM` → zeroize and exit
### Phase D — Policy simplification
- Default policy: same-uid as process = allow all verbs on all roles, no prompt
- Unknown caller: display approval prompt in TUI, user decides per-request or per-session
- No file-based policy at all
### Phase E — Integration test
- Single test that launches the program (with mnemonic piped via stdin for automation), sends requests via client subcommand, verifies responses
## 4. Decisions log
### 2026-05-02 — Program not daemon pivot
1. Single foreground process replaces daemon + TUI + client multi-binary model
2. No config files — all state entered at runtime, lives in RAM only
3. Abstract namespace sockets replace filesystem sockets
4. Control socket eliminated — TUI is in-process
5. Policy simplified to same-uid default + interactive approval
6. Crash = total wipe is a security feature, not a limitation
7. Same core modules target both Linux desktop and ESP32 MCU
### 2026-05-02 — Earlier decisions (retained)
- `role_path` must be pre-registered (no ad-hoc derivation)
- `nostr_index` naming (not `role_index`)
- Multi-curve support from start (`secp256k1`, `ed25519`, `x25519`)
- [README.md](../README.md) is authoritative behavior spec
## 5. Open questions
- Automated test strategy for interactive TUI (stdin pipe? expect-style? separate test mode flag?)
- ESP32 transport shim interface definition
- NIP-46 relay transport integration timeline
- Role enrollment UX: how many questions at startup vs. "just use main = index 0"?
- Lock-without-exit: needed? Or is quit-and-relaunch sufficient?
## 6. Immediate next work
- Execute Phase A (cleanup)
- Execute Phase B (server module)
- Execute Phase C (unified main)
- Execute Phase D (policy simplification)
- Execute Phase E (integration test)

View File

@@ -0,0 +1,87 @@
# nsigner Browser Extension — NIP-07 frontend over nsigner
> **Project:** subtree of `n_signer` at `browser-extension/` (may spin out later).
>
> **Status:** Planning. This document describes a WebExtension that exposes [NIP-07](https://github.com/nostr-protocol/nips/blob/master/07.md) `window.nostr` and forwards calls to a running `nsigner` instance.
>
> **Authoritative signer behavior:** [`../README.md`](../README.md)
## 1. What this is
A Chrome/Firefox/Brave WebExtension (Manifest V3) that:
1. injects a standard NIP-07-compatible `window.nostr` surface
2. forwards each call to `nsigner` over native-messaging, TLS, or relay fallback transport
3. applies per-origin permission policy and per-origin role pinning
The extension is not a wallet and holds no private keys.
## 2. Important compatibility note with multi-purpose roles
`nsigner` role entries now carry explicit purpose/curve metadata (see [`../README.md`](../README.md)).
For browser NIP-07 operations, the extension must request only roles whose purpose resolves to `nostr`.
If a role is configured for another purpose (e.g. bitcoin/ssh/age), extension UI must hide or reject it for NIP-07 signing/encryption calls instead of forwarding an invalid request.
## 3. Architecture (unchanged high level)
- page -> content script -> service worker
- service worker -> chosen transport -> `nsigner`
- policy and user prompts in extension UI
## 4. NIP-07 surface mapping
Standard NIP-07 calls map to `nsigner` request verbs:
- `getPublicKey`
- `signEvent`
- `nip04.encrypt` / `nip04.decrypt`
- `nip44.encrypt` / `nip44.decrypt`
The extension attaches role selector options when needed, but role choices must remain purpose-compatible with Nostr operations.
## 5. Transport options
1. Native messaging bridge (primary)
2. TLS to local/nearby `nsigner`
3. Relay-based fallback
## 6. Permission model
Per-origin policy remains the control point:
- allow/deny by verb/kind/origin
- origin-level role pinning
- explicit prompts for sensitive operations
## 7. Security constraints
- no private key/mnemonic persistence in extension storage
- no plaintext decrypt cache
- strong defaults for decrypt operations
- role purpose compatibility checks before forwarding signer requests
## 8. Implementation phases (extension-specific)
1. native helper
2. MVP extension with native path
3. role-aware UI
4. TLS transport
5. management/audit UX
6. relay fallback transport
7. browser polish and release
## 9. Repo location
Planned extension subtree remains:
- `browser-extension/extension/`
- `browser-extension/native-bridge/`
- `browser-extension/install/`
- `browser-extension/tests/`
## 10. Open questions
- extension coexistence with other NIP-07 providers
- multi-profile signer selection UX
- mobile constraints without native messaging
- hardware-token pending-confirmation UX in popup

135
plans/seed_phrase_uses.md Normal file
View File

@@ -0,0 +1,135 @@
# Seed Phrase Use Catalog for n_signer
This document catalogs potential uses of a single memorized BIP-39 seed phrase when mediated through `n_signer` role/purpose/curve controls.
It is intentionally expansive: some entries are immediate, others are future-facing.
## 1. Core idea
A single mnemonic can deterministically derive many key families. `n_signer` treats those as distinct role entries with explicit metadata:
- purpose
- curve
- derivation selector/path
- policy scope
This prevents "same seed means same trust domain" mistakes by separating identities at the policy layer.
## 2. Immediate and near-term domains
### 2.1 Nostr identities (`purpose="nostr"`)
- Curve: `secp256k1`
- Typical shorthand: `nostr_index` -> `m/44'/1237'/<index>'/0/0`
- Uses:
- main pub identity
- throwaway anti-correlation identity
- per-app/per-origin identities
- delegated service identities for local systems
### 2.2 Bitcoin keys (`purpose="bitcoin"`)
- Curve: `secp256k1`
- Typical path families (examples):
- BIP44 legacy account trees
- BIP49 wrapped-segwit
- BIP84 native segwit
- BIP86 taproot keypath flows
- Uses:
- deterministic receive/change branches
- per-context account segregation
- deterministic recovery for wallet setups
### 2.3 Lightning-related identity material
- Curve: usually `secp256k1`
- Uses vary by node software and key hierarchy model.
- Practical use with `n_signer` should be constrained to explicit purpose-bound role entries, not shared with general signing identities.
### 2.4 Mesh/service identities (`purpose="fips"` and similar)
- Curve: primarily `secp256k1` in current planning
- Uses:
- deterministic node identity roots
- stable key-derived addressing inputs
- service bootstrapping in reproducible deployments
## 3. Additional potential domains
### 3.1 SSH identity material (`purpose="ssh"`)
- Candidate curves: `ed25519` (primary), potentially others
- Uses:
- deterministic host/user key regeneration
- environment-specific SSH key derivation without storing private keys on disk
### 3.2 age-style encryption identities (`purpose="age"`)
- Candidate curves: `x25519` / age-native identity mapping
- Uses:
- deterministic file encryption identity recovery
- reproducible offline decryption setups
### 3.3 WireGuard-like transport identities
- Candidate curves: curve25519 family
- Uses:
- deterministic tunnel identity generation
- ephemeral/per-peer derivation schemes (policy-gated)
### 3.4 PGP/OpenPGP-like profiles (future)
- Possible but operationally complex due to multipurpose key lifecycles and tooling expectations.
- Better treated as explicit, isolated purpose entries if ever added.
### 3.5 Deterministic password or secret derivation (future)
- Could derive app/site secrets from role-scoped KDF outputs.
- Must never blur boundaries with signing keys; separate purpose namespace and strict export policy required.
### 3.6 API/service auth tokens (future)
- Per-service deterministic auth keys (HMAC or asymmetric challenge keys depending on service model).
- Strongly policy-scoped to avoid accidental cross-service linkage.
## 4. Curve and derivation notes
`n_signer` currently models curve metadata from day one:
- `secp256k1`
- `ed25519`
- `x25519`
Not every purpose should be valid on every curve. Enforcement should be explicit in policy/runtime validation.
## 5. Operational caveats
1. **One seed phrase is one root compromise domain**
- If the mnemonic is exposed, every derivable domain is exposed.
2. **Purpose isolation is mandatory**
- Reusing one role across unrelated domains collapses privacy and security boundaries.
3. **Pre-registration requirement matters**
- Arbitrary ad-hoc `role_path` requests are too permissive; only pre-registered paths should be derivable by clients.
4. **Policy must constrain verbs + purposes + roles**
- A caller authorized for Nostr signing should not implicitly gain rights for Bitcoin or SSH derivations.
5. **Auditability and naming discipline are crucial**
- Human-readable role names should clearly encode domain intent to avoid operator mistakes.
## 6. Practical guidance
Recommended baseline for early deployments:
- keep `purpose="nostr"` roles as primary production scope
- add non-Nostr purposes only when a concrete consumer exists
- require explicit approval to register new purpose+path combinations
- test recovery procedures per purpose before relying on them operationally
## 7. Relationship to main docs
- Product behavior contract: [`README.md`](../README.md)
- Implementation sequencing: [`plans/nsigner.md`](nsigner.md)
- Browser integration planning: [`plans/nsigner_browser_extension.md`](nsigner_browser_extension.md)