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.md32
1 files changed, 24 insertions, 8 deletions
diff --git a/AGENTS.md b/AGENTS.md
index 6f1c399..368fd83 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, and wifistr service discovery. Runs on two ESP32-S3 boards. 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.
6 6
7## Technology Stack 7## Technology Stack
8 8
@@ -11,14 +11,18 @@ TollGate ESP32 firmware: captive portal WiFi hotspot with Cashu e-cash payments,
11- **Wallet:** nucula library (libsecp256k1) via git submodule 11- **Wallet:** nucula library (libsecp256k1) via git submodule
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- **Testing:** Host C unit tests (gcc), Node.js integration tests (live board), Playwright E2E 15- **Testing:** Host C unit tests (gcc), Node.js integration tests (live board), Playwright E2E
15 16
16## Board Configuration 17## Board Configuration
17 18
18| Board | Port | Factory MAC | Notes | 19| Board | Port | Factory MAC | SSID | AP IP | Notes |
19|-------|------|-------------|-------| 20|-------|------|-------------|------|-------|-------|
20| A | `/dev/ttyACM0` | `94:a9:90:2e:37:7c` | Primary test target | 21| A | `/dev/ttyACM0` | `94:a9:90:2e:37:7c` | `TollGate-B96D80` | `10.185.47.1` | Primary test target |
21| B | `/dev/ttyACM1` | `fc:01:2c:c5:50:50` | Secondary | 22| B | `/dev/ttyACM1` | `fc:01:2c:c5:50:50` | `TollGate-C0E9CA` | `10.192.45.1` | Secondary |
23| C | `/dev/ttyACM3` | `20:6e:f1:98:d7:08` | (TBD) | (TBD) | Display board |
24
25**IMPORTANT:** Board ports change on every USB replug. Always verify with `esptool.py --port <port> chip_id` before flashing.
22 26
23Identity (SSID, IP, MAC) is derived from `nsec` in config.json. Each board gets a unique nsec. 27Identity (SSID, IP, MAC) is derived from `nsec` in config.json. Each board gets a unique nsec.
24 28
@@ -34,10 +38,11 @@ nvs_flash_init()
34 → esp_wifi_init() 38 → esp_wifi_init()
35 → esp_wifi_set_mac(STA/AP) // sets derived MACs 39 → esp_wifi_set_mac(STA/AP) // sets derived MACs
36 → esp_wifi_set_mode(APSTA) 40 → esp_wifi_set_mode(APSTA)
41 → esp_wifi_set_country_code("DE") // EU regulatory domain (channels 1-13, 20dBm)
37 → wifi_configure_ap() // uses derived SSID 42 → wifi_configure_ap() // uses derived SSID
38 → esp_wifi_start() 43 → esp_wifi_start()
39 → [on STA got IP] start_services(): 44 → [on STA got IP] start_services():
40 firewall_init, session_init, wallet_init, dns_server, captive_portal, api, wifistr_publish 45 sntp_init, firewall_init, session_init, wallet_init, dns_server, captive_portal, api, wifistr_publish, cvm_server_start
41``` 46```
42 47
43## Key Files 48## Key Files
@@ -55,6 +60,8 @@ nvs_flash_init()
55- `session.c/h` — time-based sessions, MAC tracking 60- `session.c/h` — time-based sessions, MAC tracking
56- `cashu.c/h` — Cashu token decode, checkstate, allotment calc 61- `cashu.c/h` — Cashu token decode, checkstate, allotment calc
57- `tollgate_api.c/h` — HTTP :2121, payment endpoints, wallet endpoints 62- `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
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)
58 65
59### Components 66### Components
60- `nucula_lib/` — C++ bridge to nucula::Wallet (C API in nucula_wallet.h) 67- `nucula_lib/` — C++ bridge to nucula::Wallet (C API in nucula_wallet.h)
@@ -71,7 +78,8 @@ nvs_flash_init()
71 "step_size_ms": 60000, 78 "step_size_ms": 60000,
72 "nostr_geohash": "u281w0dfz", 79 "nostr_geohash": "u281w0dfz",
73 "nostr_relays": ["wss://relay.damus.io", "wss://nos.lol"], 80 "nostr_relays": ["wss://relay.damus.io", "wss://nos.lol"],
74 "nostr_publish_interval_s": 21600 81 "nostr_publish_interval_s": 21600,
82 "cvm_enabled": true
75} 83}
76``` 84```
77 85
@@ -178,6 +186,7 @@ make flash-b # flash to Board B
178 186
179- **Test mint:** `testnut.cashu.space` — auto-pays lightning invoices 187- **Test mint:** `testnut.cashu.space` — auto-pays lightning invoices
180- **Nostr relays:** `relay.damus.io`, `nos.lol` — for wifistr events 188- **Nostr relays:** `relay.damus.io`, `nos.lol` — for wifistr events
189- **CVM relay:** `relay.primal.net` — for ContextVM kind 25910 events and CEP-6 announcements
181- **Nutshell CLI:** `cashu` command for token generation 190- **Nutshell CLI:** `cashu` command for token generation
182- **ESP-IDF:** `source ~/esp/esp-idf/export.sh` before `idf.py` commands 191- **ESP-IDF:** `source ~/esp/esp-idf/export.sh` before `idf.py` commands
183- **System libs for unit tests:** `libmbedtls-dev`, `libcjson-dev` 192- **System libs for unit tests:** `libmbedtls-dev`, `libcjson-dev`
@@ -186,10 +195,17 @@ make flash-b # flash to Board B
186 195
187- **Commit + push every time a test passes that previously didn't pass.** Green tests = checkpoint. Don't batch multiple test fixes into one commit. 196- **Commit + push every time a test passes that previously didn't pass.** Green tests = checkpoint. Don't batch multiple test fixes into one commit.
188- Commit + push after each working change 197- Commit + push after each working change
189- Board A is at `/dev/ttyACM0`, Board B at `/dev/ttyACM1` 198- Board A is at `/dev/ttyACM0`, Board B at `/dev/ttyACM1`, Board C at `/dev/ttyACM3`
199- **Per-board locks required** before hardware access: `make lock-a PHASE="desc"`, lock files in `physical-router-test-automation/locks/`
190- `sudo` password: `c03rad0r123` 200- `sudo` password: `c03rad0r123`
191- SPIFFS is at offset `0x410000`, size `0xF0000` — erase with `esptool.py erase_region 0x410000 0xF0000` if config is stale 201- SPIFFS is at offset `0x410000`, size `0xF0000` — erase with `esptool.py erase_region 0x410000 0xF0000` if config is stale
192- NVS stores wallet proofs — erasing NVS clears wallet balance 202- NVS stores wallet proofs — erasing NVS clears wallet balance
193- The `nostr_event.c` `created_at` field uses `gettimeofday()` — mock this in unit tests 203- The `nostr_event.c` `created_at` field uses `gettimeofday()` — mock this in unit tests
194- Wifistr event signing uses `secp256k1_schnorrsig_sign32()` — verify with `_verify()` in tests 204- Wifistr event signing uses `secp256k1_schnorrsig_sign32()` — verify with `_verify()` in tests
195- Portal HTML has server-side template substitution (`__AP_IP__`, `__PRICE__`, `__MINT_URL__`) — no JS fetch 205- 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
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`
209- 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