upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/NIP46-PLAN.md
diff options
context:
space:
mode:
Diffstat (limited to 'NIP46-PLAN.md')
-rw-r--r--NIP46-PLAN.md111
1 files changed, 111 insertions, 0 deletions
diff --git a/NIP46-PLAN.md b/NIP46-PLAN.md
new file mode 100644
index 0000000..8d8d56b
--- /dev/null
+++ b/NIP46-PLAN.md
@@ -0,0 +1,111 @@
1# GRASP Mirror NIP-46 Remote Signing — Implementation Plan
2
3## Goal
4
5Build and deploy the grasp-mirror daemon with NIP-46 remote signing so it can publish authenticated `kind:30618` state events and push git data to GRASP servers, without storing nsec keys on the VPS.
6
7## Architecture
8
9```
10 ┌─────────────┐
11 │ Amber │
12 │ (phone) │
13 └──────┬──────┘
14 │ NIP-46 (kind:24133)
15 │ NIP-04 encrypted
16 ┌──────┴──────┐
17 │ Nostr Relay │
18 │ (relay/ngit)│
19 └──────┬──────┘
20
21 ┌──────┴──────┐
22 │ grasp-mirror│
23 │ daemon │
24 │ (VPS) │
25 └──────┬──────┘
26 │ git push + kind:30618
27 ┌──────┴──────┐
28 │ GRASP │
29 │ servers │
30 └─────────────┘
31```
32
33- Daemon generates client keypairs per npub, persists in SQLite
34- Produces `nostrconnect://` pairing URIs shown in health endpoint
35- User opens URI in Amber → NIP-46 session established
36- Before git push, daemon builds unsigned `kind:30618` from repo refs
37- Sends `sign_event` request to signer via NIP-46 relay protocol
38- Amber signs on phone → daemon receives signed event → publishes + pushes
39
40## Checklist
41
42### Phase 1: Fix compile errors locally
43
44- [ ] Fix `nip46.rs` — `notification.as_ref()` → match on owned `RelayPoolNotification`
45- [ ] Fix `nip46.rs` — ensure `message.id()` borrow is cloned before consuming message
46- [ ] Fix `git_mirror.rs` — accept shared `nostr_sdk::Client` instead of creating throwaway per server
47- [ ] Fix `main.rs` — pass `nostr_client` to `git_mirror.mirror_repo_to_servers`
48- [ ] Verify `db.rs` — `bool` FromRow works with sqlx 0.8 sqlite INTEGER
49- [ ] Pin Rust 1.95.0 in Ansible role `tasks/main.yml`
50- [ ] Commit and push to GRASP origin
51
52### Phase 2: Build on VPS
53
54- [ ] GRASP server running on target VPS (dependency: migration session)
55- [ ] Push SSH key to VPS for Ansible key-based auth
56- [ ] Run `ansible-playbook playbooks/30-grasp-mirror.yml -v`
57- [ ] Fix any remaining compile errors iteratively
58- [ ] Binary installed at `/usr/local/bin/grasp-mirror`
59- [ ] Systemd service `grasp-mirror` running
60
61### Phase 3: Verify and pair
62
63- [ ] Health endpoint responds: `curl http://localhost:7335/health`
64- [ ] Response includes `nip46` array with session statuses
65- [ ] Each unpaired npub shows `pairing_uri`
66- [ ] Pair npub 1 (`npub12m5...`) via Amber
67- [ ] Pair npub 2 (`npub19jx...`) via Amber
68- [ ] Pair npub 3 (`npub1c03...`) via Amber
69- [ ] Health endpoint shows all 3 sessions `connected: true`
70- [ ] Test signing: daemon builds and signs a `kind:30618` event
71- [ ] Test push: git data pushes to a target GRASP server
72
73### Phase 4: Document roadmap
74
75- [ ] Add QEMU/OpenWRT orchestration roadmap to PLAN.md or PROGRESS.md
76
77## Key Files
78
79| File | Role |
80|---|---|
81| `src/nip46.rs` | NIP-46 client: sessions, NIP-04 encrypt/decrypt, relay listener, `sign_event()` |
82| `src/db.rs` | `nip46_sessions` table + CRUD methods |
83| `src/config.rs` | `Nip46Config` with relays + signing_timeout_secs |
84| `src/git_mirror.rs` | Builds unsigned `kind:30618`, signs via NIP-46, publishes before push |
85| `src/http_health.rs` | NIP-46 session status in `/health` JSON |
86| `src/main.rs` | Wires NIP-46 init + listener into daemon startup |
87| `ansible/roles/grasp_mirror/tasks/main.yml` | Ansible deploy tasks (build + install) |
88| `ansible/roles/grasp_mirror/defaults/main.yml` | NIP-46 relay + timeout vars |
89| `ansible/roles/grasp_mirror/templates/config.toml.j2` | Config with `[nip46]` section |
90
91## Constraints
92
93- No `any` type in TypeScript (N/A — Rust project)
94- No comments in code unless requested
95- Use `pnpm` for JS, `cargo` for Rust
96- Rust 1.95.0 required (nostr-sdk 0.39 doesn't compile on 1.85)
97- NIP-04 encryption for NIP-46 messages (not NIP-44 — simpler, Amber supports it)
98- Signing timeout: 7 days (604800 seconds) — queue-and-wait model
99- GRASP push auth requires signed `kind:30618` by maintainer, not HTTP auth
100
101## Future Roadmap: QEMU/OpenWRT Test Orchestration
102
103After migration, repurpose old VPS (2 vCPU, 8GB RAM, 99GB disk) as dedicated OpenWRT test runner:
104
1051. **Deploy loom-worker** on old VPS — decentralized compute marketplace via Nostr
1062. **Write QEMU execution adapter** for loom — Unix domain socket that spins up OpenWRT instances, runs tests, streams results
1073. **Use existing act-runner** custom pipeline to submit loom jobs from CI
1084. **Do NOT build custom orchestration** — loom already has standardized event kinds (5001/5100/5101), Cashu payments, Blossom result storage, encrypted communication
1095. **Evaluate loom from budabit** (`nostr://npub1hw6amg8p24ne08c9gdq8hhpqx0t0pwanpae9z25crn7m9uy7yarse465gr/relay.ngit.dev/loom-worker`) before building anything custom
110
111Loom's pluggable adapter architecture means we write ~200 lines of Deno/TypeScript for the QEMU adapter and get the entire Nostr job queue, payment, and result streaming for free.