Skip to content

feat: direct Lexe gateway client — remove lexe-sidecar and L402 hop (v2) - #2

Merged
vincenzopalazzo merged 2 commits into
mainfrom
feat/direct-gateway-client
Sep 16, 2026
Merged

vincenzopalazzo merged 2 commits into
mainfrom
feat/direct-gateway-client

Conversation

@vincenzopalazzo

Copy link
Copy Markdown
Owner

Summary

  • Replace the lexe-sidecar HTTP dependency with a direct Lexe gateway client in pure Go stdlib, mirroring lexe-node-client from lexe-public@bcabbd3: outer TLS pinned to the embedded Lexe root CA (prod/staging, valid to 2034), CONNECT run.lexe.app:443 with the credentials' long-lived Proxy-Authorization token, inner mTLS presenting the revocable client certificate and chaining the node certificate to the credentials' ephemeral CA
  • Node commands are JSON REST with sats amounts; lexe.pay polls payments/id / payments/updated (250 ms → 4 s backoff, ~150 s cap) to terminal status, mints BOLT12 payer proofs after settled offer pays; post-submission poll errors never surface as retryable bare failures (double-pay guard)
  • lexe.analyze is now a local decode (prefix classification + BOLT11 hrpart amount, overflow-guarded); LNURL/Lightning-address inputs are refused with guidance
  • Removed: lexe.my_account, the L402/AgenticMail hop, stateDir persistence, /webhook, lexe-sidecar.service; config reduced to lexeNetwork (mainnet|testnet3), credentials, port/host, payMaxSats, offer defaults
  • Hermetic fake-gateway + fake-node TLS integration tests (gateway_test.go) pin the full wire protocol: CA pinning, CONNECT auth, inner mTLS (incl. cross-credential rejection), JSON shapes, polling

Review

Production-safety review (multi-model panel degraded → two independent reviewer passes): all 5 findings fixed in 4127512 (double-pay window, invoice matching, server read timeouts + 1 MiB body cap, body-read truncation, BOLT11 overflow); verification pass returned SHIP.

Test plan

  • go vet . && go test -race . && go build — green locally (14 tests incl. hermetic TLS stack)
  • CI smoke unchanged and passing: /health, legacy initialize, OPTIONS 204, tools/list, lexe.pay identity guard
  • Live check against gateway.lexe.app with real SDK credentials before deploy (manual, on host)

vincenzopalazzo and others added 2 commits September 15, 2026 17:01
- outer TLS pinned to the embedded Lexe root CA (prod/staging)
- CONNECT run.lexe.app with the credentials' long-lived proxy token
- inner mTLS: revocable client cert, node cert chained to the eph CA
- node JSON REST commands, amounts in sats; pay polls to terminal state
- local analyze (prefix classify + BOLT11 hrpart amount), LNURL refused
- drop lexe.my_account, agenticmail/L402 machinery, stateDir, webhook
- hermetic fake-gateway+node TLS tests pin the wire protocol
- docs/config/systemd updated; sidecar unit removed

Co-Authored-By: goose <goose@aaif.ai>
- waitPayment: tolerate transient poll errors (submitted payments must
  not surface as retryable bare failures — double-pay guard); surface
  index + check_payment guidance on unknown status
- invoice matching tightened: created_at + outbound + invoice kind + amount
- typed errPaymentNotFound sentinel replaces string matching
- http.Server read timeouts + 1 MiB body cap (slowloris)
- nodeCall: body-read errors on 2xx fail instead of parsing truncation
- bolt11 amount: overflow-safe bounds (21M BTC cap, portable ints)

Co-Authored-By: goose <goose@aaif.ai>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 16, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-16T00:28:44.037529Z 4127512 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@vincenzopalazzo
vincenzopalazzo merged commit 8657538 into main Sep 16, 2026
2 checks passed

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4127512cd9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread server.go
Comment on lines +978 to +979
m := asMap(out)
for _, item := range asSlice(m["payments"]) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Read the bare updated-payments array

The direct API contract recorded in docs/plans/2026-09-15-direct-gateway-client.md:24 returns a bare [BasicPaymentV2…] array, but this casts the response to a map and looks for a payments property. Consequently every BOLT11 payment is submitted but never found by the poller, eventually returning an error after roughly 150 seconds and potentially prompting a duplicate payment; iterate over asSlice(out) instead.

AGENTS.md reference: AGENTS.md:L174-L181

Useful? React with 👍 / 👎.

Comment thread server.go
Comment on lines +533 to 535
body := map[string]any{
"description": description,
"min_amount": strconv.Itoa(minAmountSats),
"expiration_secs": ttl,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Send the offer TTL as expiry_secs

The direct-node create_offer contract documented in docs/plans/2026-09-15-direct-gateway-client.md:20 names this field expiry_secs, not the sidecar-era expiration_secs. As written, the node can reject every create request as malformed or ignore the field, so configured offerTtlSecs values do not control the generated offer's lifetime.

AGENTS.md reference: AGENTS.md:L203-L210

Useful? React with 👍 / 👎.

Comment thread server.go
Comment on lines +816 to +817
case 'p':
return 0, false // sub-sat precision — treat as amountless

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Recognize whole-sat pico-BTC invoice amounts

The p multiplier does not always imply a sub-sat amount: for example, 10000p is exactly one sat. Returning false for every pico-denominated BOLT11 invoice makes valid fixed-amount invoices appear amountless, so lexe.pay rejects them unless the caller redundantly supplies amount_sats and lexe.analyze omits their amount; accept p values representing whole sats and reject only non-integral sat values.

AGENTS.md reference: AGENTS.md:L168-L180

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant