Skip to content

fix: x402 verifier TLS and facilitator compatibility#322

Closed
bussyjd wants to merge 1 commit intomainfrom
fix/x402-verifier-tls
Closed

fix: x402 verifier TLS and facilitator compatibility#322
bussyjd wants to merge 1 commit intomainfrom
fix/x402-verifier-tls

Conversation

@bussyjd
Copy link
Copy Markdown
Collaborator

@bussyjd bussyjd commented Apr 8, 2026

Summary

  • CA certificate bundle: obol sell pricing now populates the ca-certificates ConfigMap from the host's cert bundle so the distroless verifier can TLS-verify external facilitators
  • Missing Description field: facilitator rejects verify requests without description in PaymentRequirement

Validated: two-stack Base Sepolia testnet flow

Two independent DGX Spark nodes (ARM64), each running their own obol stack with Nemotron 120B (120B params, tensor-parallel across 2 GPUs):

Alice (spark-1, seller)

obolup.sh
obol stack init && obol stack up
obol model setup custom --name nemotron-120b \
  --endpoint http://host.k3d.internal:8000/v1 \
  --model "nvidia/NVIDIA-Nemotron-3-Super-120B-A12B-NVFP4"
obol sell pricing --wallet 0xC0De...97E --chain base-sepolia
obol sell http nemotron --wallet 0xC0De...97E --chain base-sepolia \
  --per-request 0.001 --namespace llm --upstream litellm --port 4000 \
  --health-path /health/readiness --register \
  --register-name "Nemotron 120B on DGX Spark"
obol tunnel restart

Bob (spark-2, buyer)

Path 1 — Direct x402 payment:

# Discover → probe 402 → sign EIP-712 → pay → HTTP 200
curl $TUNNEL/.well-known/agent-registration.json  # x402Support: true
curl -X POST $TUNNEL/services/nemotron/...        # 402 with pricing
python3 bob_buy.py                                 # sign + pay → 200

Path 2 — paid/* through buyer sidecar (full LiteLLM supply chain):

obolup.sh
obol stack init && obol stack up
# Configure x402-buyer sidecar with pre-signed ERC-3009 auths
# Agent calls: model="paid/nemotron-120b"
#   → LiteLLM paid/* → x402-buyer sidecar → Alice's tunnel
#   → x402-verifier → facilitator → on-chain settlement
#   → LiteLLM → vLLM Nemotron 120B → response

On-chain receipts (Base Sepolia)

Tx Description
0xd769953b... Direct: Bob → Alice 0.001 USDC
0xd004b8cd... Sidecar: Bob → Alice 0.001 USDC via paid/*

Balance changes

Wallet Role Before After
0xC0De...97E Alice (seller) 15.021 USDC 15.023 USDC
0x57b0...490E Bob (buyer) 5.000 USDC 4.998 USDC

Test plan

  • go test ./internal/x402/... passes
  • Real on-chain x402 payment (direct path)
  • Real on-chain x402 payment (sidecar paid/* path)
  • Two independent stacks, two separate clusters

Two fixes validated with real Base Sepolia x402 payments between
two DGX Spark nodes running Nemotron 120B inference.

1. **CA certificate bundle**: The x402-verifier runs in a distroless
   container with no CA store. TLS verification of the public
   facilitator (facilitator.x402.rs) fails with "x509: certificate
   signed by unknown authority". Fix: `obol sell pricing` now reads
   the host CA bundle and patches it into the `ca-certificates`
   ConfigMap mounted by the verifier.

2. **Missing Description field**: The facilitator rejects verify
   requests that lack a `description` field in PaymentRequirement
   with "invalid_format". Fix: populate Description from the route
   pattern when building the payment requirement.

## Validated testnet flow

### Alice (seller)

```
obolup.sh                    # bootstrap dependencies
obol stack init && obol stack up
obol model setup custom --name nemotron-120b \
  --endpoint http://host.k3d.internal:8000/v1 \
  --model "nvidia/NVIDIA-Nemotron-3-Super-120B-A12B-NVFP4"
obol sell pricing --wallet 0xC0De...97E --chain base-sepolia
obol sell http nemotron \
  --wallet 0xC0De...97E --chain base-sepolia \
  --per-request 0.001 --namespace llm \
  --upstream litellm --port 4000 \
  --health-path /health/readiness \
  --register --register-name "Nemotron 120B on DGX Spark"
obol tunnel restart
```

### Bob (buyer)

```
# 1. Discover
curl $TUNNEL/.well-known/agent-registration.json
# → name: "Nemotron 120B on DGX Spark", x402Support: true

# 2. Probe
curl -X POST $TUNNEL/services/nemotron/v1/chat/completions
# → 402: payTo=0xC0De...97E, amount=1000, network=base-sepolia

# 3. Sign EIP-712 TransferWithAuthorization + pay
python3 bob_buy.py
# → 200: "The meaning of life is to discover and pursue purpose"
```

### On-chain receipts (Base Sepolia)

| Tx | Description |
|----|-------------|
| 0xd769953b...c231ec0 | x402 settlement: Bob→Alice 0.001 USDC via ERC-3009 |

Balance change: Alice +0.001 USDC, Bob -0.001 USDC.
Facilitator: https://facilitator.x402.rs (real public settlement).
@bussyjd
Copy link
Copy Markdown
Collaborator Author

bussyjd commented Apr 8, 2026

Collapsed into #324 which is a superset of this PR (includes CA bundle, facilitator URL, and LiteLLM zero-downtime).

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