/ln-invoice settlement grants the gate to the quote-status POLLER, not the quote owner — owner later reads access_granted=true with no gate
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Newbie friendliness
- 48/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Active
- Tech stack
- go
- Domain
- api, authentication, backend
Research direction
Start in src/main.go at the /ln-invoice handler, monitorLightningQuote, and clientMACFromSocket; trace how quote state records the creator and how settlement sets access_granted. Then read the tests/happy-path README and run the harness with two seeded clients. Done when a poll by the second client grants only the creator's MAC and a regression test verifies that behavior.
Written by the indexing model from the issue text.
Description
Summary
The Lightning-quote settlement path grants the gate to whoever polls the quote's status, not to the client that created (and paid for) the quote. A second client on the same router can poll a settled quote; the module then authorizes THE POLLER's MAC, stops the quote monitor, and subsequent polls by the real owner return access_granted: true — while the owner's gate never opened.
Severity: S2/S3 (funds-safe, access/UI-honesty). No value is lost, but the paying customer's portal can display a granted session that never gates their device — the "misleading success" class this project explicitly hunts.
Verified on
- Commit:
68ad5144ab09cb4723d8f3f655cbba1ac7ea9de5(main, 2026-10-07) - Surface:
/ln-invoicecreate + status-poll flow, offline happy-path harness with two seeded loopback clients
Evidence (verbatim module log)
Quote created and paid by client A (127.0.0.2, lease MAC 02:00:00:00:00:20). Client B (127.0.0.1, MAC 02:00:00:00:00:21 — a different probe) polls the quote status:
Hit /ln-invoice endpoint module=main remote_addr="127.0.0.1:35430" <- poller is client B
Replacing existing timed gate with indefinite data-based gate. mac_address="02:00:00:00:00:21" module=valve
Authorization successful for MAC mac_address="02:00:00:00:00:21" output="Auth: 02:00:00:00:00:21 - Granted\n"
monitorLightningQuote: stopping for hp-stub-quote-0001 — access granted
After this, client A's poll returns the quote JSON with "access_granted": true — but the fake-ndsctl log contains AUTH for .21 ONLY, never .20. The happy-path suite's enforcement:gate-opens-on-recognised-payment check fails on exactly this mismatch.
Reproduce
The packaged harness (tests/happy-path) reproduces it directly — it needs two resolvable clients, which on a host means seeded lease entries (the harness does not seed them itself; see the companion issue about the missing lease fixture):
git clone https://github.com/OpenTollGate/tollgate-module-basic-go && cd tollgate-module-basic-go
git checkout 68ad5144ab09cb4723d8f3f655cbba1ac7ea9de5
# seed two loopback clients exactly as the harness's README documents
printf '1700000000 02:00:00:00:00:21 127.0.0.1 hp-probe *\n1700000000 02:00:00:00:00:20 127.0.0.2 hp-client *\n' > /tmp/dhcp.leases
# extract a published or locally built .apk (see tests/happy-path/README.md), then:
HP_PYTHON=<python-with-playwright> bash tests/happy-path/run.sh --artifact <extracted-dir> --strict --keep
grep -E 'gate-opens|granted-session' <output>
Expected: AUTH logged for the quote OWNER's MAC (…:20). Actual: AUTH for the POLLER (…:21), owner later reads access_granted: true.
Manual curl-only variant (any host running the module with /tmp/dhcp.leases seeded as above):
- From client A:
curl -X POST http://127.0.0.2:2121/ln-invoice -d '{"amount":210,"mint_url":"<mint>"}'→ notequoteid. - Settle the quote at the mint (FakeWallet auto-settles; or wait).
- From client B:
curl "http://127.0.0.1:2121/ln-invoice?quote=<id>"(the status GET the monitor/portal uses). - Observe: ndsctl auth fired for B's MAC; A's subsequent poll says
access_granted: true.
Troubleshooting guide
- Where the decision happens:
src/main.go— the/ln-invoicehandler andmonitorLightningQuote(the log line "monitorLightningQuote: stopping … access granted" names it). The grant path resolves the client from the REQUEST SOCKET (clientMACFromSocket) at poll time. - The intended contract: the module's client-identity doctrine says identity is always the socket — that is right for WHO is asking, but for quote settlement the grant should go to the identity recorded when the QUOTE was created (the payer), because the poller is only an observer. Check whether the quote record (monitor goroutine's state) retains the creator's MAC.
- Why the portal usually masks it: in production the paying browser is normally both creator and poller — the bug surfaces when any other LAN client (or a monitoring agent) knows the quote id. Quote ids are likely unguessable in practice — quantify that before deciding severity.
- Related test that encodes the intent:
tests/happy-pathapi:client-identity/ the C7b contract checks (X-TollGate-Client-MAC, ignored?mac=claims) — the claim-ignoring is correct; this issue is about which socket the GRANT binds to.
Fix hints
- Persist the quote creator's resolved MAC with the quote at creation; on settlement (monitor or poll-triggered), authorize THAT MAC. The status-poll response's
access_grantedshould reflect the QUOTE's grant state, and include enough identity (or an error) that a non-owner poll cannot be mistaken for the owner's success. - Add a regression test with two seeded clients: creator pays, other client polls, assert ndsctl AUTH fires for the creator only.
References
- Happy-path harness + README (client-identity contract, lease-seeding precondition):
tests/happy-path/ - Client-identity doctrine: README "client is identified by the MAC address their device uses … always resolved from the request's socket"
- Dominant language
- Go
- Stars
- 12
- Forks
- 14
- Avg merge
- 1d 6h
- Merged PRs (30d)
- 217
Getting set up
- No Dockerfile or Docker Compose file
- No pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from OpenTollGate/tollgate-module-basic-go
-
go-battery needs an ndsctl on PATH: TestPurchaseSessionGuardHoldsThroughTheOutcomeUnknownWindow fails on bare hosts (passes with stub)Possibly taken @Amperstrand claimed this 1 day ago. Open
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
OpenTollGate/tollgate-module-basic-go#726 · 2 comments ·
Maintainers usually reply within 1 day
-
rebrand-literal-gutter: uhttpd section-vocabulary check trips on a COMMENT (uhttpd.luci in 92-tollgate-admin-setup:178)Possibly taken @Amperstrand claimed this 1 day ago. Open
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
OpenTollGate/tollgate-module-basic-go#723 ·
Maintainers usually reply within 1 day
-
Difficulty 5/5 Over a week Newbie friendliness 20/100
OpenTollGate/tollgate-module-basic-go#768 ·
Maintainers usually reply within 1 day
-
Four drift fences for tests/contract/ (+ test.yml clean-container lane + pre-commit wiring)Possibly taken A pull request linked to this issue is open or already merged. Open
Difficulty 5/5 Over a week Newbie friendliness 25/100
OpenTollGate/tollgate-module-basic-go#767 ·
Maintainers usually reply within 1 day
-
Difficulty 5/5 Over a week Newbie friendliness 20/100
OpenTollGate/tollgate-module-basic-go#763 ·
Maintainers usually reply within 1 day
All issues in OpenTollGate/tollgate-module-basic-go
Similar issues
-
proxy logs "no user in context" at error level for every data gateway downloadPossibly taken @paul43210 claimed this today. Open
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
Maintainers usually reply within 1 day
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 83/100
txn2/mcp-data-platform#2063 ·
Maintainers usually reply within 1 day
-
Difficulty 1/5 Under an hour Newbie friendliness 83/100
kubernetes-sigs/kueue#16990 ·
Maintainers usually reply within 1 day
-
enhancement exporter/awss3 needs triage
Difficulty 2/5 1-3 hours Newbie friendliness 66/100
open-telemetry/opentelemetry-collector-contrib#51905 · 1 comment ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
stellar/stellar-horizon#245 ·
Maintainers usually reply within 1 day