upleb.uk

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

summaryrefslogtreecommitdiff
path: root/AGENTS.md
diff options
context:
space:
mode:
Diffstat (limited to 'AGENTS.md')
-rw-r--r--AGENTS.md34
1 files changed, 28 insertions, 6 deletions
diff --git a/AGENTS.md b/AGENTS.md
index 368fd83..6f8ba12 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -2,7 +2,7 @@
2 2
3## Project Overview 3## Project Overview
4 4
5TollGate ESP32 firmware: captive portal WiFi hotspot with Cashu e-cash payments, on-device wallet, Nostr identity derivation, wifistr service discovery, and ContextVM (MCP over Nostr) server. Runs on three ESP32-S3 boards. 5TollGate ESP32 firmware: captive portal WiFi hotspot with Cashu e-cash payments, on-device wallet, Nostr identity derivation, wifistr service discovery, ContextVM (MCP over Nostr) server, and **local Nostr relay** with relay selection and sync. Runs on three ESP32-S3 boards.
6 6
7## Technology Stack 7## Technology Stack
8 8
@@ -12,6 +12,9 @@ TollGate ESP32 firmware: captive portal WiFi hotspot with Cashu e-cash payments,
12- **Identity:** Nostr nsec → HMAC-SHA512 → deterministic MAC/SSID/IP 12- **Identity:** Nostr nsec → HMAC-SHA512 → deterministic MAC/SSID/IP
13- **Service discovery:** wifistr (Nostr kind 38787) via WebSocket 13- **Service discovery:** wifistr (Nostr kind 38787) via WebSocket
14- **ContextVM:** MCP over Nostr (kind 25910), CEP-6 announcements, 10 MCP tools 14- **ContextVM:** MCP over Nostr (kind 25910), CEP-6 announcements, 10 MCP tools
15- **Local relay:** wisp-esp32 (adapted), NIP-01 server on port 4869, LittleFS 4MB storage
16- **Relay selection:** NIP-11 HTTP probing, latency + NIP-77 scoring, auto-failover
17- **Sync:** REQ-diff with primary (30min) and fallback (6h) relays
15- **Testing:** Host C unit tests (gcc), Node.js integration tests (live board), Playwright E2E 18- **Testing:** Host C unit tests (gcc), Node.js integration tests (live board), Playwright E2E
16 19
17## Board Configuration 20## Board Configuration
@@ -42,7 +45,8 @@ nvs_flash_init()
42 → wifi_configure_ap() // uses derived SSID 45 → wifi_configure_ap() // uses derived SSID
43 → esp_wifi_start() 46 → esp_wifi_start()
44 → [on STA got IP] start_services(): 47 → [on STA got IP] start_services():
45 sntp_init, firewall_init, session_init, wallet_init, dns_server, captive_portal, api, wifistr_publish, cvm_server_start 48 sntp_init, firewall_init, session_init, wallet_init, dns_server, captive_portal, api,
49 local_relay_init+start, relay_selector_init+probe, sync_manager_start, wifistr_publish, cvm_server_start
46``` 50```
47 51
48## Key Files 52## Key Files
@@ -53,7 +57,7 @@ nvs_flash_init()
53- `identity.c/h` — HMAC-SHA512 derivation from nsec, npub/MAC/SSID/IP 57- `identity.c/h` — HMAC-SHA512 derivation from nsec, npub/MAC/SSID/IP
54- `nostr_event.c/h` — NIP-01 event serialization + BIP-340 Schnorr signing 58- `nostr_event.c/h` — NIP-01 event serialization + BIP-340 Schnorr signing
55- `geohash.c/h` — lat/lon to geohash encoding 59- `geohash.c/h` — lat/lon to geohash encoding
56- `wifistr.c/h` — kind 38787 event builder + WebSocket relay publish 60- `wifistr.c/h` — kind 38787 event builder + local-first publish (local relay then public)
57- `captive_portal.c/h` — HTTP :80 portal, captive detection, grant/reset 61- `captive_portal.c/h` — HTTP :80 portal, captive detection, grant/reset
58- `dns_server.c/h` — DNS hijack/forward per-client, DoT reject 62- `dns_server.c/h` — DNS hijack/forward per-client, DoT reject
59- `firewall.c/h` — per-client NAT filter via LWIP_HOOK_IP4_CANFORWARD, MAC resolution 63- `firewall.c/h` — per-client NAT filter via LWIP_HOOK_IP4_CANFORWARD, MAC resolution
@@ -62,10 +66,18 @@ nvs_flash_init()
62- `tollgate_api.c/h` — HTTP :2121, payment endpoints, wallet endpoints 66- `tollgate_api.c/h` — HTTP :2121, payment endpoints, wallet endpoints
63- `cvm_server.c/h` — ContextVM: persistent WS relay listener, kind 25910 subscription, MCP protocol handlers, CEP-6 announcements 67- `cvm_server.c/h` — ContextVM: persistent WS relay listener, kind 25910 subscription, MCP protocol handlers, CEP-6 announcements
64- `mcp_handler.c/h` — 10 MCP tool handlers (get_config, set_config, get_balance, wallet_send, get_sessions, get_usage, set_payout, set_metric, set_price, wallet_melt) 68- `mcp_handler.c/h` — 10 MCP tool handlers (get_config, set_config, get_balance, wallet_send, get_sessions, get_usage, set_payout, set_metric, set_price, wallet_melt)
69- `local_relay.c/h` — Thin wrapper: inits wisp_relay storage/sub/rate-limiter on port 4869, publishes events to LittleFS + broadcasts to WS subscribers
70- `relay_selector.c/h` — NIP-11 HTTP probing of seed relays, latency + NIP-77 scoring, auto-failover after 3 disconnects, 6h re-probe cycle
71- `sync_manager.c/h` — REQ-diff sync: primary every 30min, fallback every 6h, reconciles local events vs remote, dedicated FreeRTOS task
65 72
66### Components 73### Components
67- `nucula_lib/` — C++ bridge to nucula::Wallet (C API in nucula_wallet.h) 74- `nucula_lib/` — C++ bridge to nucula::Wallet (C API in nucula_wallet.h)
68- `secp256k1/` — symlink to nucula_src/components/secp256k1/ 75- `secp256k1/` — symlink to nucula_src/components/secp256k1/
76- `wisp_relay/` — Local Nostr relay (NIP-01): ws_server, storage_engine (LittleFS), sub_manager, broadcaster, router, handlers, relay_validator (Schnorr+SHA256), rate_limiter, nip11, deletion, flash_monitor
77- `esp_littlefs/` — LittleFS VFS integration for relay storage partition (git submodule)
78- `negentropy/` — Negentropy set-reconciliation library (git submodule, for future NIP-77)
79- `axs15231b/` — QSPI TFT display driver (JC3248W535)
80- `qrcode/` — QR code generator
69 81
70### Config Format (config.json on SPIFFS) 82### Config Format (config.json on SPIFFS)
71```json 83```json
@@ -79,6 +91,14 @@ nvs_flash_init()
79 "nostr_geohash": "u281w0dfz", 91 "nostr_geohash": "u281w0dfz",
80 "nostr_relays": ["wss://relay.damus.io", "wss://nos.lol"], 92 "nostr_relays": ["wss://relay.damus.io", "wss://nos.lol"],
81 "nostr_publish_interval_s": 21600, 93 "nostr_publish_interval_s": 21600,
94 "nostr_seed_relays": [
95 "wss://relay.orangesync.tech",
96 "wss://relay.damus.io",
97 "wss://nos.lol",
98 "wss://relay.nostr.band"
99 ],
100 "nostr_sync_interval_s": 1800,
101 "nostr_fallback_sync_interval_s": 21600,
82 "cvm_enabled": true 102 "cvm_enabled": true
83} 103}
84``` 104```
@@ -186,7 +206,9 @@ make flash-b # flash to Board B
186 206
187- **Test mint:** `testnut.cashu.space` — auto-pays lightning invoices 207- **Test mint:** `testnut.cashu.space` — auto-pays lightning invoices
188- **Nostr relays:** `relay.damus.io`, `nos.lol` — for wifistr events 208- **Nostr relays:** `relay.damus.io`, `nos.lol` — for wifistr events
209- **Seed relays:** `relay.orangesync.tech` (NIP-77), `relay.damus.io`, `nos.lol`, `relay.nostr.band` — for relay selection and sync
189- **CVM relay:** `relay.primal.net` — for ContextVM kind 25910 events and CEP-6 announcements 210- **CVM relay:** `relay.primal.net` — for ContextVM kind 25910 events and CEP-6 announcements
211- **Local relay:** Port 4869, LittleFS 4MB partition at 0x500000, max 5000 events, 21-day TTL
190- **Nutshell CLI:** `cashu` command for token generation 212- **Nutshell CLI:** `cashu` command for token generation
191- **ESP-IDF:** `source ~/esp/esp-idf/export.sh` before `idf.py` commands 213- **ESP-IDF:** `source ~/esp/esp-idf/export.sh` before `idf.py` commands
192- **System libs for unit tests:** `libmbedtls-dev`, `libcjson-dev` 214- **System libs for unit tests:** `libmbedtls-dev`, `libcjson-dev`
@@ -200,12 +222,12 @@ make flash-b # flash to Board B
200- `sudo` password: `c03rad0r123` 222- `sudo` password: `c03rad0r123`
201- SPIFFS is at offset `0x410000`, size `0xF0000` — erase with `esptool.py erase_region 0x410000 0xF0000` if config is stale 223- SPIFFS is at offset `0x410000`, size `0xF0000` — erase with `esptool.py erase_region 0x410000 0xF0000` if config is stale
202- NVS stores wallet proofs — erasing NVS clears wallet balance 224- NVS stores wallet proofs — erasing NVS clears wallet balance
225- **Relay storage** LittleFS at offset `0x500000`, size `0x400000` (4MB) — auto-formatted on first boot
203- The `nostr_event.c` `created_at` field uses `gettimeofday()` — mock this in unit tests 226- The `nostr_event.c` `created_at` field uses `gettimeofday()` — mock this in unit tests
204- Wifistr event signing uses `secp256k1_schnorrsig_sign32()` — verify with `_verify()` in tests 227- Wifistr event signing uses `secp256k1_schnorrsig_sign32()` — verify with `_verify()` in tests
228- relay_validator.c does Schnorr verify + SHA-256 event ID — test with `test_relay_validator`
229- relay_selector scoring: NIP-77 bonus (1000pts) + latency + failure penalty (100pts each) — test with `test_relay_selector`
205- Portal HTML has server-side template substitution (`__AP_IP__`, `__PRICE__`, `__MINT_URL__`) — no JS fetch 230- Portal HTML has server-side template substitution (`__AP_IP__`, `__PRICE__`, `__MINT_URL__`) — no JS fetch
206- **WiFi country code:** Must set `esp_wifi_set_country_code("DE")` before `esp_wifi_start()` — defaults to CN which causes auth failures on EU APs 231- **WiFi country code:** Must set `esp_wifi_set_country_code("DE")` before `esp_wifi_start()` — defaults to CN which causes auth failures on EU APs
207- **Board A WiFi is broken** — hardware issue confirmed: `WIFI_REASON_AUTH_EXPIRED` on all APs in all modes (APSTA, STA-only, factory MAC). Board B with identical firmware connects instantly. Do not waste time debugging Board A WiFi.
208- Default nsec: `a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2` 232- Default nsec: `a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2`
209- Board A nsec: `9af47906b45aca5e238390f3d03c8274e154198e81aa2095065627d1e61ca968` 233- Board A nsec: `9af47906b45aca5e238390f3d03c8274e154198e81aa2095065627d1e61ca968`
210- CVM relay: `relay.primal.net` — relay disconnects every ~15s by default, now has 60s timeout + WS ping/pong keepalive
211- MCP responses sent via existing WS connection (not new TLS) — ESP32 can't handle multiple simultaneous TLS sessions