upleb.uk

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

summaryrefslogtreecommitdiff
path: root/PLAN.md
diff options
context:
space:
mode:
Diffstat (limited to 'PLAN.md')
-rw-r--r--PLAN.md75
1 files changed, 71 insertions, 4 deletions
diff --git a/PLAN.md b/PLAN.md
index 2a0ed2b..5690c1b 100644
--- a/PLAN.md
+++ b/PLAN.md
@@ -286,7 +286,7 @@ Publishes TollGate node to Nostr as kind 38787 (wifistr):
286| 37 | 5 consecutive payments | Loop | All authenticated | TODO | 286| 37 | 5 consecutive payments | Loop | All authenticated | TODO |
287| 38 | Stress: rapid pay/expire | Loop with short sessions | No crash/leak | TODO | 287| 38 | Stress: rapid pay/expire | Loop with short sessions | No crash/leak | TODO |
288 288
289### Phase 4: ESP32 TollGate Client Detection + Auto-Payment — IN PROGRESS 289### Phase 4: ESP32 TollGate Client Detection + Auto-Payment — COMPLETE
290 290
291**Goal:** ESP32 detects upstream TollGate when connected as STA, automatically pays for internet access using on-device wallet. Enables ESP32→OpenWRT (Scenario 4) and ESP32→ESP32 (Scenario 5) auto-payment. 291**Goal:** ESP32 detects upstream TollGate when connected as STA, automatically pays for internet access using on-device wallet. Enables ESP32→OpenWRT (Scenario 4) and ESP32→ESP32 (Scenario 5) auto-payment.
292 292
@@ -354,7 +354,7 @@ IDLE → [STA got IP] → DETECTING → [kind=10021 found] → NEEDS_PAY
354 354
355Pre-association price discovery via Wi-Fi Vendor IE beacons (OUI `0x54:0x47`) is deferred to a future phase. The client currently uses HTTP-based discovery after connection. 355Pre-association price discovery via Wi-Fi Vendor IE beacons (OUI `0x54:0x47`) is deferred to a future phase. The client currently uses HTTP-based discovery after connection.
356 356
357### Phase 5: Lightning Auto-Payout — NOT STARTED 357### Phase 5: Lightning Auto-Payout — COMPLETE
358 358
359**Goal:** When wallet balance exceeds a configurable threshold, automatically pay out to Lightning addresses via LNURL-pay + Cashu NUT-05 melt. 359**Goal:** When wallet balance exceeds a configurable threshold, automatically pay out to Lightning addresses via LNURL-pay + Cashu NUT-05 melt.
360 360
@@ -420,7 +420,7 @@ Wraps `Wallet::request_melt_quote()` + `Wallet::melt_tokens()` (NUT-05).
420| 47 | Melt with fee tolerance | Integration | Invoice paid, change received | TODO | 420| 47 | Melt with fee tolerance | Integration | Invoice paid, change received | TODO |
421| 48 | Full payout cycle | E2E | Wallet drains to min_balance | TODO | 421| 48 | Full payout cycle | E2E | Wallet drains to min_balance | TODO |
422 422
423### Phase 6: Bytes-Based Billing — NOT STARTED 423### Phase 6: Bytes-Based Billing — COMPLETE
424 424
425**Goal:** Support both time-based (milliseconds) and data-based (bytes) billing metrics. Mirrors the Go implementation's dual-metric system. 425**Goal:** Support both time-based (milliseconds) and data-based (bytes) billing metrics. Mirrors the Go implementation's dual-metric system.
426 426
@@ -473,7 +473,7 @@ uint64_t bytes_consumed;
473| 51 | NAPT byte counting | Integration | Counters match actual traffic | TODO | 473| 51 | NAPT byte counting | Integration | Counters match actual traffic | TODO |
474| 52 | Bytes metric end-to-end | E2E | Client disconnected after data cap | TODO | 474| 52 | Bytes metric end-to-end | E2E | Client disconnected after data cap | TODO |
475 475
476### Phase 7: ContextVM Server (MCP over Nostr) — NOT STARTED 476### Phase 7: ContextVM Server (MCP over Nostr) — COMPLETE
477 477
478**Goal:** Remote configuration of ESP32 TollGate via ContextVM — services communicate over Nostr using public keys as addresses. Exposes configuration as MCP tools accessible by both humans and AI agents. 478**Goal:** Remote configuration of ESP32 TollGate via ContextVM — services communicate over Nostr using public keys as addresses. Exposes configuration as MCP tools accessible by both humans and AI agents.
479 479
@@ -524,6 +524,73 @@ Only accept commands from owner npub (derived from nsec in config.json).
524 524
525## Total: 56 Tests across 7 phases 525## Total: 56 Tests across 7 phases
526 526
527## Post-Phase 7: Bug Fixes & Architecture Improvements
528
529### Per-Client NAT Filtering (Multi-Client Isolation Fix)
530
531**Problem:** When client A's session expires but client B is still active, NAPT stays enabled globally. Client A's existing TCP/UDP connections in the NAT table continue routing — their traffic reaches the internet even though they're unauthenticated.
532
533**Solution:** Use lwIP's `LWIP_HOOK_IP4_CANFORWARD` hook to filter forwarded packets by source IP. This fires in `ip4_forward()` **before** NAPT translation, so unauthorized clients are blocked at the IP layer.
534
535**Architecture Change:**
536- **Before:** NAT is a global toggle — ON when any client is auth'd, OFF when none are
537- **After:** NAT stays always ON. Per-client filtering happens in the lwIP forwarding path via `LWIP_HOOK_IP4_CANFORWARD`
538
539**Files:**
540
541| File | Action | Description |
542|------|--------|-------------|
543| `main/lwip_tollgate_hooks.h` | Create | Defines `LWIP_HOOK_IP4_CANFORWARD` macro |
544| `CMakeLists.txt` | Modify | Inject hook header into lwIP compilation |
545| `main/firewall.c` | Modify | Add filter function, remove global NAT toggle |
546| `main/firewall.h` | Modify | Expose filter function declaration |
547| `main/tollgate_main.c` | Modify | Remove `firewall_disable_nat()` from stop_services |
548
549**Implementation:**
550
5511. `lwip_tollgate_hooks.h` — hook header included by lwIP:
552```c
553#define LWIP_HOOK_IP4_CANFORWARD(p, addr) tollgate_ip4_canforward_filter(p, addr)
554```
555
5562. `CMakeLists.txt` — inject into lwIP build (follows ESP-IDF vlan example pattern):
557```cmake
558idf_component_get_property(lwip lwip COMPONENT_LIB)
559target_compile_options(${lwip} PRIVATE "-I${PROJECT_DIR}/main")
560target_compile_definitions(${lwip} PRIVATE "-DESP_IDF_LWIP_HOOK_FILENAME=\"lwip_tollgate_hooks.h\"")
561```
562
5633. `firewall.c` — filter function checks source IP against allowed client list:
564```c
565int tollgate_ip4_canforward_filter(struct pbuf *p, u32_t dest_addr_hostorder) {
566 struct ip_hdr *iphdr = (struct ip_hdr *)p->payload;
567 uint32_t src_ip = lwip_ntohl(iphdr->src.addr);
568 return firewall_is_client_allowed(src_ip) ? 1 : 0;
569}
570```
571
5724. NAT management simplified: enable once during `firewall_init()`, never disable. Remove `update_nat()`, `firewall_enable_nat()`, `firewall_disable_nat()`.
573
574**Key properties:**
575- Hook fires only for **forwarded** packets (AP client → internet), not packets to ESP32 itself
576- Captive portal (port 80) and API (port 2121) remain accessible to all clients
577- DNS hijack continues independently — both layers enforce auth
578- Return traffic: NAPT only creates DNAT entries for successfully forwarded outbound packets, so blocked clients don't get return traffic either
579- Performance: linear scan of ≤10 clients per packet — negligible cost
580
581### Spent-Secret Cleanup
582
583**Problem:** `session.c` tracks spent Cashu secrets locally in a static array (`s_spent_secrets[100][65]`). This is redundant — the mint is the authority on spent state, and `nucula_wallet_receive()` already swaps proofs (registering them as spent with the mint).
584
585**Changes:**
586- Remove `s_spent_secrets[]`, `s_spent_count`, `session_is_secret_spent()` from `session.c`
587- Remove `spent_secrets` and `spent_secret_count` from `session_t` struct
588- Remove `spent_secrets` params from `session_create()` / `session_create_bytes()`
589- Remove local spent-secret check in `tollgate_api.c` — keep only mint `check_proof_states` check
590- Update unit tests
591
592**Rationale:** The mint's `cashu_check_proof_states()` already catches double-spends over HTTP. `nucula_wallet_receive()` → `swap()` registers proofs as spent and replaces them. After a successful receive, the old token is useless. Local tracking adds no security, wastes 6.5KB RAM, and is lost on reboot anyway.
593
527## Testing Infrastructure 594## Testing Infrastructure
528 595
529### Three-Layer Test Architecture 596### Three-Layer Test Architecture