# grasp-mirror Plan ## Completed - [x] Fix `push_mirror` in `src/git_mirror.rs` — reuse existing `push_target` remote via `remote_set_url` - [x] Fix `mirror_cycle` in `src/main.rs` — make per-repo errors non-fatal - [x] Add HTTP health endpoint (`/health`, `/api/mirror-health`) on port 7335 - [x] Mirror repos from 3 npubs - [x] Ansible role, playbook, systemd unit, config templates - [x] Dashboard integration on `services.orangesync.tech` - [x] Watchdog integration ## Phase 3: NIP-46 Remote Signing via Amber ### Problem GRASP servers reject unauthenticated git pushes. Authorization requires a `kind:30618` state event signed by a maintainer's key. The daemon needs to sign these events but should never hold raw nsec keys. ### Solution NIP-46 (Nostr Connect) remote signing via Amber on the user's phone. The daemon acts as a NIP-46 client, sends `sign_event:30618` requests to Amber, and waits for approval before pushing. ### Architecture ``` VPS: grasp-mirror ←→ relays ←→ Amber (phone) - discovers repos - NIP-46 kind:24133 - builds state events - sign_event:30618 - queues signing - user approves - publishes event - returns signature - git push services.orangesync.tech - pairing UI + QR codes - status display ``` ### Config ```toml [nip46] relays = ["wss://relay.orangesync.tech", "wss://ngit.orangesync.tech"] signing_timeout_secs = 604800 # 7 days ``` ### Checklist - [ ] Add `nip46_sessions` and `signing_queue` tables to `src/db.rs` - [ ] Build `src/nip46.rs` — NIP-46 client with session management - Generate client keypair per npub, persist to SQLite - Build `nostrconnect://` URIs with `perms=sign_event:30618` - Listen for `kind:24133` events on configured relays - Handle connect/get_public_key/sign_event handshake - `sign_state_event(npub, state_event) -> Result` method - Auto-reconnect on session drop - [ ] Wire NIP-46 into `src/main.rs` daemon startup — connect sessions for all 3 npubs - [ ] Modify `src/git_mirror.rs` — before push, build `kind:30618` state event, request signature via NIP-46 - [ ] Add pairing URI + NIP-46 status to health endpoint (`/api/mirror-health`) - [ ] Update dashboard HTML (`static/services/index.html`) with pairing UI + QR codes - [ ] Update Ansible config template with `[nip46]` section - [ ] Add `health_port` to `[storage]` in config template (already done) - [ ] Rebuild release binary - [ ] Push to all 9 GRASP servers - [ ] Redeploy via Ansible - [ ] End-to-end test: pair npub via Amber, verify signing flow works