diff options
Diffstat (limited to 'PLAN.md')
| -rw-r--r-- | PLAN.md | 86 |
1 files changed, 61 insertions, 25 deletions
| @@ -1,25 +1,61 @@ | |||
| 1 | # grasp-mirror Bugfix Plan | 1 | # grasp-mirror Plan |
| 2 | 2 | ||
| 3 | ## Bugs Identified from VPS Logs | 3 | ## Completed |
| 4 | 4 | ||
| 5 | ### Bug 1: `remote 'push_target' already exists` | 5 | - [x] Fix `push_mirror` in `src/git_mirror.rs` — reuse existing `push_target` remote via `remote_set_url` |
| 6 | - **Location**: `src/git_mirror.rs:137` | 6 | - [x] Fix `mirror_cycle` in `src/main.rs` — make per-repo errors non-fatal |
| 7 | - **Cause**: `repo.remote("push_target", target_url)` creates a new remote every cycle. On subsequent cycles, the bare repo already has a `push_target` remote, so `git2` returns `Exists (-4)`. | 7 | - [x] Add HTTP health endpoint (`/health`, `/api/mirror-health`) on port 7335 |
| 8 | - **Fix**: Use `repo.find_remote("push_target")` first. If it exists, call `repo.remote_set_url()` to update the URL. If not, create it. | 8 | - [x] Mirror repos from 3 npubs |
| 9 | 9 | - [x] Ansible role, playbook, systemd unit, config templates | |
| 10 | ### Bug 2: Single repo failure aborts entire cycle | 10 | - [x] Dashboard integration on `services.orangesync.tech` |
| 11 | - **Location**: `src/main.rs:mirror_cycle()` — the `mirror.mirror_repo_to_servers()` call uses `?` which propagates the error and aborts the loop. | 11 | - [x] Watchdog integration |
| 12 | - **Cause**: When one repo (e.g. `market`) fails to clone from any server, the `?` operator returns early, skipping the remaining ~87 repos. | 12 | |
| 13 | - **Fix**: Replace `?` with a `match` that logs the error and continues to the next repo. Only the `discover_repos_from_relays` and `persist_discovered_repos` calls should be fatal (if relay queries fail, nothing works). | 13 | ## Phase 3: NIP-46 Remote Signing via Amber |
| 14 | 14 | ||
| 15 | ## Checklist | 15 | ### Problem |
| 16 | 16 | 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. | |
| 17 | - [ ] Fix `push_mirror` in `src/git_mirror.rs` — reuse existing `push_target` remote | 17 | |
| 18 | - [ ] Fix `mirror_cycle` in `src/main.rs` — make per-repo errors non-fatal | 18 | ### Solution |
| 19 | - [ ] Rebuild release binary (`cargo build --release`) | 19 | 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. |
| 20 | - [ ] Push updated source to all 9 GRASP servers (`git push origin master`) | 20 | |
| 21 | - [ ] Redeploy via Ansible (`ansible-playbook playbooks/30-grasp-mirror.yml`) | 21 | ### Architecture |
| 22 | - [ ] Verify: `grasp-mirror status` shows repos tracked and sync records | 22 | ``` |
| 23 | - [ ] Verify: `journalctl -u grasp-mirror` shows successful mirror cycles without `already exists` errors | 23 | VPS: grasp-mirror ←→ relays ←→ Amber (phone) |
| 24 | - [ ] Verify: spot-check `git ls-remote` against a few GRASP servers for repos from all 3 npubs | 24 | - discovers repos - NIP-46 kind:24133 |
| 25 | - [ ] Verify: `https://git.orangesync.tech/api/mirror-health` returns `"status": "ok"` | 25 | - builds state events - sign_event:30618 |
| 26 | - queues signing - user approves | ||
| 27 | - publishes event - returns signature | ||
| 28 | - git push | ||
| 29 | |||
| 30 | services.orangesync.tech | ||
| 31 | - pairing UI + QR codes | ||
| 32 | - status display | ||
| 33 | ``` | ||
| 34 | |||
| 35 | ### Config | ||
| 36 | ```toml | ||
| 37 | [nip46] | ||
| 38 | relays = ["wss://relay.orangesync.tech", "wss://ngit.orangesync.tech"] | ||
| 39 | signing_timeout_secs = 604800 # 7 days | ||
| 40 | ``` | ||
| 41 | |||
| 42 | ### Checklist | ||
| 43 | |||
| 44 | - [ ] Add `nip46_sessions` and `signing_queue` tables to `src/db.rs` | ||
| 45 | - [ ] Build `src/nip46.rs` — NIP-46 client with session management | ||
| 46 | - Generate client keypair per npub, persist to SQLite | ||
| 47 | - Build `nostrconnect://` URIs with `perms=sign_event:30618` | ||
| 48 | - Listen for `kind:24133` events on configured relays | ||
| 49 | - Handle connect/get_public_key/sign_event handshake | ||
| 50 | - `sign_state_event(npub, state_event) -> Result<SignedEvent>` method | ||
| 51 | - Auto-reconnect on session drop | ||
| 52 | - [ ] Wire NIP-46 into `src/main.rs` daemon startup — connect sessions for all 3 npubs | ||
| 53 | - [ ] Modify `src/git_mirror.rs` — before push, build `kind:30618` state event, request signature via NIP-46 | ||
| 54 | - [ ] Add pairing URI + NIP-46 status to health endpoint (`/api/mirror-health`) | ||
| 55 | - [ ] Update dashboard HTML (`static/services/index.html`) with pairing UI + QR codes | ||
| 56 | - [ ] Update Ansible config template with `[nip46]` section | ||
| 57 | - [ ] Add `health_port` to `[storage]` in config template (already done) | ||
| 58 | - [ ] Rebuild release binary | ||
| 59 | - [ ] Push to all 9 GRASP servers | ||
| 60 | - [ ] Redeploy via Ansible | ||
| 61 | - [ ] End-to-end test: pair npub via Amber, verify signing flow works | ||