| Age | Commit message (Collapse) | Author |
|
- config.h: remove duplicate nostr_sync fields
- Makefile: add mint_health stub for test_session_payment_method
- test_tollgate_client_mining: inline market_t stub to avoid dependency cascade
- CMakeLists: add negentropy_adapter.c + esp_littlefs + negentropy REQUIRES
|
|
New modules:
- mining_payment.c/h: hashprice calc (nbits->difficulty->sat/GH/s/day),
share validation, client stats, allotment conversion (ms + bytes)
- stratum_client.c/h: SV1 upstream pool connection (subscribe/authorize/submit)
- stratum_proxy.c/h: Local SV1 TCP server for downstream miners, job broadcast
- sw_miner.c/h: Software SHA256d miner (ESP32 CPU fallback)
- asic_miner.c/h: ASIC detection stub (BM1366/BM1368 SPI)
Config:
- config.h/c: mining_payout_mode_t enum (auto/pool/upstream/proxy_only),
stratum pool settings, mining port, hashprice override, sandbox mint access
- Defaults fill nostr_seed_relays (8/8) and nostr_relays (4/4) with fast relays
Integration into existing modules:
- session.h/c: payment_method_t enum (CASHU/MINING/BYTES)
- firewall.h/c: firewall_set_mining_port(), firewall_set_sandbox_mint_access()
- tollgate_api.c: GET /mining/job, POST /mining/share, GET /mining/stats
- tollgate_client.h/c: TG_CLIENT_MINING state, mining discovery tag parsing
- tollgate_main.c: mining init in start_services(), stratum_client_tick() in loop
- captive_portal.c: tabbed Cashu/Mine UI with live hashrate polling
Unit tests (69 new assertions across 4 suites):
- test_mining_payment (23 tests): nbits->difficulty, hashprice, client stats, allotment
- test_stratum_proxy (21 tests): job set/get, stats, type validation
- test_session_payment_method (12 tests): PAYMENT_METHOD enum, bytes/cashu methods
- test_tollgate_client_mining (20 tests): mining tag parsing, discovery struct
- test_firewall_sandbox (16 tests): client grant/revoke, max clients, setters
Enhanced test stubs:
- BaseType_t/pdPASS in freertos/task.h
- lwip: sockets.h, etharp.h, prot/ip.h, prot/ip4.h, prot/tcp.h, netif.h
- dns_server.h, esp_wifi_ap_get_sta_list.h
Build fixes:
- cvm_server.c: replace esp_timer_get_time() with xTaskGetTickCount(),
fix process_relay_message() 3-arg call to 2-arg, add WS keepalive ping
- stratum_proxy.c: widen task_name buffer 16->20
- sw_miner.c: add missing #include esp_random.h
- nucula_src: save_proofs() moved to public in wallet.hpp
Nostr relay updates:
- nostr_seed_relays: +relay.anzenkodo.workers.dev, +nostr.koning-degraaf.nl,
+knostr.neutrine.com, +nostr.einundzwanzig.space (8/8 slots)
- nostr_relays: +relay.anzenkodo.workers.dev, +nostr.koning-degraaf.nl (4/4 slots)
Squash-merge of feature/mining-payment (5 commits: c75230e..9d98ba1)
|
|
Squash merge of feature/multi-mint-support (21 commits):
Multi-mint wallet:
- Accept payments from 4 mints: minibits, coinos, 21mint, lnvoltz
- Periodic health probing (300s interval, 3 recovery threshold)
- Multi-wallet init with nucula_wallet_init_multi()
- /mints and /wallet API endpoints
WPA auto-detect:
- wifi_auth_mode config field (default WPA2, supports WPA3)
- Runtime mapping to wifi_auth_mode_t in STA config
Display gating:
- display_enabled config field (default true)
- Guards display_init/display_update per-board
Bug fixes:
- 3s delay before service start prevents lwip mem_free assertion
- Real npub in discovery (identity_get()->npub_hex)
- Health probe interval 300s (production value)
- Duplicate services_start_task call removed
- UTF-8 arrow replaced with ASCII in log message
Tests: 61+14 unit tests passing, firmware builds clean
|
|
SSID is populated from scan results which may arrive after the vendor
IE callback. Entries with ssid='unknown' but valid BSSID and price
are valid discoveries.
|
|
Price discovery allows TollGate ESP32 boards to advertise their per-step
price via WiFi Vendor-Specific Information Elements (OUI 0xC0FFEE) in
beacon and probe response frames. Nearby boards passively scan and build
a market view of competing TollGates without requiring internet access.
Features:
- beacon_price.c/h: 26-byte packed Vendor IE payload (price, step, metric,
mint_hash, geohash, npub_hash), injected via esp_wifi_set_vendor_ie()
- market.c/h: Passive WiFi scan receiver, vendor IE callback parsing,
BSSID-correlated market entries, effective price ranking
- GET /market API endpoint: JSON market snapshot with discovered entries
- AP-only services: beacon + market + API start on WIFI_EVENT_AP_START,
independent of STA connectivity
- STA reconnect fix: 2s delay between retries creates scan windows;
s_sta_connecting guard prevents double-connect
- write-config-ap-only-a/b Makefile targets for STA-less testing
- market_tick() in main loop, client price comparison logging
Hardware verified: both boards discover each other via Vendor IE beacons.
Board A sees TollGate-C0E9CA (RSSI=-30), Board B sees TollGate-B96D80
(RSSI=-25). test-market.mjs: 9/9, test-price-discovery.mjs: 7/7 per board.
Unit tests: 45 new assertions across test_beacon_price (28) and test_market
(17). All 15 test suites pass. ESP-IDF build clean for ESP32-S3.
|
|
Restores build broken by eeb9d2d (cvm-relay-stability removed deps):
- CMakeLists.txt: restore display.c, font.c, local_relay.c, relay_selector.c, sync_manager.c, axs15231b, qrcode, wisp_relay
- tollgate_main.c: restore display.h, local_relay.h, relay_selector.h, sync_manager.h includes and display calls
- cvm_server.c: kept master's keepalive/timeout/ping-pong fixes
New test infrastructure:
- test-local-relay, test-relay-nip11, test-cvm-roundtrip, test-cvm-mcp, test-cross-board make targets
- test-cvm-roundtrip.mjs: MCP get_config + get_balance via public relay
- test-cross-board.mjs: cross-board payment test
- test-cvm-mcp-relay.mjs: kept from master
New unit tests (35 tests):
- test_display.c: 22 tests for escape_wifi_field
- test_negentropy_adapter.c: 13 tests for negentropy adapter
New modules:
- negentropy_adapter.c/h: NIP-77 adapter skeleton
Docs:
- AGENTS.md: display module docs, new test commands
- RELAY_HARDENING_PLAN.md: hardening checklist
- RELAY_HARDENING_MERGE.md: merge plan and checklist
Cleanup:
- Removed CHECKLIST-CVM-RELAY.md, PLAN-SQUASH-MERGE.md (stale planning docs)
- Removed components/esp-miner submodule
Host unit tests: 63/63 pass
|
|
Relay disconnect fix (cvm_server.c):
- TLS read timeout reduced from 15s to 1s (short poll loop)
- Ping timer fires every 30s independently of read activity
- Consecutive timeout counter (65s) detects real disconnects
- Handle relay close frames (opcode 0x08) explicitly
- Result: 120s+ stable connection (previously ~37s disconnect cycle)
MCP relay integration tests (17/17 pass via make test-cvm-mcp):
- MCP initialize roundtrip via relay.primal.net
- get_sessions returns session array
- get_usage returns metric/price/step fields
- Non-owner auth rejection (board silently drops)
- Owner control request passes after rejection test
Build fixes:
- Remove display/font/axs15231b/qrcode deps (from display branch, not here)
- Remove local_relay/relay_selector/sync_manager deps (from relay branch)
- Add esp_timer to CMakeLists REQUIRES
Host unit tests: 61/61 pass
|
|
Local Nostr relay (NIP-01) on port 4869 with LittleFS 4MB storage.
All events published locally first, then synced to public relays via REQ-diff.
Relay selection via NIP-11 HTTP probing with NIP-77 scoring and auto-failover.
Components:
- wisp_relay: 16-file local relay (ws_server, storage_engine, sub_manager,
broadcaster, relay_validator, router, handlers, rate_limiter, nip11,
deletion, flash_monitor, relay_types)
- esp_littlefs: LittleFS VFS integration (git submodule)
- negentropy: for future NIP-77 binary sync (git submodule)
New source files:
- local_relay.c/h: thin wrapper for relay init/start/publish
- relay_selector.c/h: NIP-11 probe + scoring + auto-failover
- sync_manager.c/h: REQ-diff sync (primary 30min, fallback 6h)
Bug fixes:
- config.c: use-after-free (cJSON_Delete before seed_relays/sync parsing)
- local_relay: moved init to app_main for boot-time start (not gated on STA IP)
Flash layout: 4MB LittleFS partition at 0x500000 for relay_store
Test results (Board B, live hardware):
- Smoke: ping + HTTP 4869 + NIP-11: PASS
- NIP-11 info document: 10/11 PASS
- WS pub/sub (connect, REQ/EOSE, EVENT/OK, CLOSE, concurrent): 6/6 PASS
- Unit tests (relay_validator + relay_selector): 13/13 PASS
Hardware test make targets in physical-router-test-automation/:
- make relay-build, relay-flash-b, relay-test-smoke/nip11/pubsub/sync/full
|
|
|
|
- test_relay_validator: Schnorr verify + SHA-256 event ID, tamper detection
(ID, sig, content), invalid JSON, missing fields, result_string
- test_relay_selector: relay scoring (NIP-77 bonus, latency tiebreak,
failure penalty, dead relay handling)
- Updated Makefile with new test targets
- Added configTICK_RATE_HZ to FreeRTOS stubs
|
|
Complete CVM implementation: persistent WebSocket relay listener,
kind 25910 event subscription, MCP protocol handlers, CEP-6 announcements,
10 MCP tools, per-board hardware locks, WiFi EU regulatory fix.
Architecture:
- cvm_server.c: WS relay listener, kind 25910 subscription, MCP dispatch
- mcp_handler.c/h: 10 MCP tools (get_config, set_config, get_balance,
wallet_send, get_sessions, get_usage, set_payout, set_metric,
set_price, wallet_melt)
- Responses published via existing WS connection (not new TLS)
- Auth check: only owner npub accepted
- CEP-6: kinds 11316 (server), 11317 (tools), 10002 (relay list)
- WS ping/pong keepalive every 30s, 60s TLS read timeout
Critical fixes:
- WiFi country code DE (ESP-IDF defaults to CN, breaks EU APs)
- Subscription #p filter must be array not string
- Use-after-free: tags_str freed before nostr_event_to_json
- MCP responses via existing WS (ESP32 can't open multiple TLS)
- EVENT msg buffer underflow, WS frame masking, TLS write loop
Per-board hardware locks:
- Lock files in physical-router-test-automation/locks/
- lock-a/b/c, unlock-a/b/c targets in 3 Makefiles
- All hardware-touching targets require board lock
Verified on Board B via relay.primal.net:
- 282 unit tests passing (61 CVM + 60 MCP + 161 existing)
- MCP initialize roundtrip: PASS
- tools/list: PASS
- tools/call get_config: PASS
- tools/call get_balance: PASS
- tools/call set_price: PASS (write operation)
- CEP-6 announcements (11316, 11317, 10002): all accepted by relay
- WiFi STA connection (EnterSSID-2.4GHz): PASS with country code DE
- Board A WiFi confirmed hardware issue (not firmware)
|
|
- Move integration tests (api, network, phase2, smoke) to tests/integration/
- Move Playwright specs (captive-portal, interop-happy-path) to tests/e2e/
- Move playwright.config.mjs to tests/e2e/
- Fix hardcoded IP fallbacks: 192.168.4.1 → 10.192.45.1
- Add test-reset-auth.mjs: reset→pay→allow→revoke→block cycle
- Add test-session-expiry.mjs: pay→wait 65s→verify blocked (slow test)
- Add test-dns-firewall.mjs: DNS hijack/forward + per-client NAT filter
- Update Makefile with test-unit, test-integration, test-e2e, test-all targets
- Update package.json scripts for new paths
- Fix Playwright video: retain-on-failure instead of always-on
- Update AGENTS.md: per-client NAT filter description
- Update CHECKLIST.md: mark completed items, add Board B identity
- Board B nsec: 9af47906... → SSID TollGate-b96d80, AP IP 10.185.47.1
- 186 unit tests passing
|
|
- Add lwip_tollgate_hooks.h defining LWIP_HOOK_IP4_CANFORWARD macro
- Inject hook into lwIP build via CMakeLists.txt ESP_IDF_LWIP_HOOK_FILENAME
- Filter forwarded packets by source IP against firewall allowed list
- Only filter packets from AP subnet (10.192.45.0/24), allow all others
- Fix byte order bug: use network byte order for firewall_is_client_allowed
- NAT always enabled, removed global NAT toggle functions
- Remove spent-secret tracking from session.c (mint is authority)
- Remove unused get_ap_netif() function
- Reduce API server stack from 32KB to 16KB (fixes ESP_ERR_HTTPD_TASK)
- Add esp_random.h stub for unit tests
- All 186 unit tests passing
- Verified on hardware: block->pay->allow->revoke->block E2E works
|
|
- interop-happy-path.spec.mjs: 11 ESP32 TollGate tests + 7 ESP32↔OpenWRT interop tests
- API discovery, whoami, usage, invalid/spent token rejection
- Browser portal UI: branding, form elements, captcha detection URIs
- Full payment flow screenshots (portal → token → connected → browsing)
- Side-by-side ESP32 vs OpenWRT comparison screenshot
- playwright.config.mjs: video on, screenshot on, 120s timeout
- package.json: test:happy-path, test:interop, test:playwright scripts
|
|
server skeleton
- mcp_handler.c/h: 4 tools (get_config, set_config, get_balance, wallet_send)
- nip04.c/h: AES-256-CBC + ECDH with 0x02 compressed pubkey prefix
- Fixed IV copy bug: mbedTLS AES-CBC modifies IV in-place
- Base64 encode/decode for ciphertext transport
- PKCS7 padding
- cvm_server.c/h: Nostr DM listener with FreeRTOS task
- config: cvm_enabled, cvm_relays fields
- 156 total tests passing across 10 test binaries
|
|
- session_create_bytes() + session_add_bytes() for bytes-metric sessions
- session_is_expired() dispatches on config metric (bytes vs milliseconds)
- cashu_calculate_allotment() unified dispatcher for both metrics
- tollgate_api discovery/usage/session_event use configured metric
- config: metric field defaults to 'bytes', step_size_bytes=22020096 (21MB)
- 14 new unit tests (148 total passing)
- ASSERT_EQ_UINT64 macro added to test framework
|
|
- New lnurl_pay.c/h: LNURL-pay protocol (GET .well-known/lnurlp + callback)
- New lightning_payout.c/h: threshold-based auto-payout with multi-recipient split
- Extended nucula_wallet bridge with nucula_wallet_melt() (NUT-05)
- Config: payout section with multi-mint, multi-recipient, fee_tolerance
- Default: enabled, TollGate@coinos.io, min_payout=128, min_balance=64
- 18 new unit tests (all passing), 134 total
|
|
- New tollgate_client.c/h: detect upstream TollGate (kind=10021),
auto-pay via nucula wallet, session monitoring with 20% renewal
- State machine: IDLE→DETECTING→NEEDS_PAY→PAYING→PAID→RENEWING
- Blocking: upstream payment before local services start
- Synchronous wallet init (was async task)
- Client config: enabled, steps_to_buy, renewal_threshold_pct
- Updated PLAN.md with Phases 4-7 (client, payout, bytes, CVM)
- Updated CHECKLIST.md with all new phase items
- 30 new unit tests (all passing), 116 total
|
|
- Expand esp_http_client.h stub: full config struct + method enum + init/perform/cleanup
- Add portTICK_PERIOD_MS + esp_err_to_name to stubs
- session.c: reject duplicate spent secrets in session_create (double-spend protection)
- .gitignore: add test binaries
|
|
serialization
|
|
determinism
|
|
tests (11/11 pass)
- Add AGENTS.md: full project context + mandatory testing rules for AI sessions
- Add tests/unit/ with host-compiled C unit test infrastructure
- Clean stubs approach: ESP-IDF type stubs in tests/unit/stubs/, no source modifications
- Fix geohash.c bit extraction bug (3-byte span) found by unit tests
- test_geohash: 11/11 passing with reference vectors (Munich, NYC, origin, boundaries)
|
|
route management
|
|
- Add esp_netif_set_dns_info() on AP interface so DHCP advertises
AP as DNS server to clients (fixes captive portal on GrapheneOS)
- Embed price and mint URL directly in portal HTML via server-side
template substitution (no JavaScript fetch to :2121 needed)
- Move supported mints section below the token input field
- Add Playwright tests: no unresolved placeholders, embedded mint/price,
DOM order verification (14/14 passing)
|
|
- Derive unique SSID (TollGate-{MAC4}{MAC5}) and AP IP (10.{b5}.{subnet}.1)
from factory MAC — boards no longer conflict
- Board A: TollGate-377C @ 10.55.85.1, Board B: TollGate-5050 @ 10.80.10.1
- Captive portal detection URIs return 200 with portal HTML (matching
esp32-mesh working approach) instead of 302 redirect
- Dynamic AP IP in portal HTML via __AP_IP__ template substitution
- Supported mints section in portal page (shows mint URL, tap to copy)
- Fixed mint URL to testnut.cashu.space (was stale in SPIFFS)
- DoT reject server on port 853 for DNS-over-TLS fallback
- DNS hijack: NXDOMAIN for all non-A queries, no forwarding for unauthed
- Playwright tests updated for 200 response on detection URIs
- Phase 2 test suite: 20/21 pass (test 22 expiry ping route issue)
- Tests 25-27 deferred to Phase 3 (Board B as second client)
|
|
- Updated from Phase 1 tests to Phase 2 (302 redirects, Cashu token input)
- Test captive detection URIs return 302 (using request API)
- Test invalid token via request API (no CORS issues)
- Tests: portal branding, price, token input, pay button, detection redirects
- Tests: whoami, usage, API advertisement, invalid token
|
|
- Test 22: session expires after allotment, internet blocked, usage returns -1/-1
- Test 23: second token renews session, internet restored
- Test 18 fix: add route through TollGate before ping, pipe sudo password
- All 21 Phase 2 tests pass
|
|
- Add DoT reject server on port 853 (TCP RST forces DNS-over-TLS fallback)
- DNS hijack returns NXDOMAIN for all non-A query types (no forwarding for unauthed)
- Shorter TTL on hijack responses (10s) for faster captive detection
- Explicit 302 redirect handlers for /generate_204, /hotspot-detect.html, etc.
- HTTP and DNS request logging for debugging captive detection
- Per-MAC tracking in firewall (find_by_mac, get_mac_for_ip with ARP fallback)
- Session MAC tracking (session_find_by_mac)
- Phase 2 test 18: add route through TollGate before ping test
- All 17 Phase 2 tests pass (15-21 + whoami + portal form)
|
|
- Add cashu.c/h: Cashu token decode (cashuA/base64url), proof state check via mint API, allotment calculator
- Add session.c/h: time-based session management with allotment/expiry, spent secret tracking
- Add tollgate_api.c/h: HTTP server on :2121 with GET / (kind=10021 discovery), POST / (payment processing), /usage, /whoami
- Update captive portal HTML: replace Grant Free Access with Cashu token paste form + Pay & Connect button
- Update tollgate_main.c: wire in session manager, TollGate API, 1s session tick loop
- Add tests/phase2.mjs: Phase 2 test suite (discovery, invalid token, wrong mint, valid payment)
- Update CHECKLIST.md: reflect Phase 1 complete, Phase 2 in progress with known bugs
Known issues (not yet flashed):
- Stack overflow crash in httpd POST handler (need stack_size=16384 + heap allocations)
- cashu_decode_token uses 2KB stack buffer (needs heap alloc)
- Mint URL should be testnut.cashu.space (nofee.testnut has API compat issues)
|
|
- Fix WiFi init order: netif creation before esp_wifi_init, set mode before set_config
- Replace broken netif input filter with NAPT on/off per authentication state
- NAPT disabled by default, enabled when client granted, disabled on revoke
- Fix test helpers: use -I wlp59s0 for ping, handle nslookup exit code 1
- All 20 API tests pass, all 6 smoke tests pass
|