Skip to content

Add static paid x402 gateway and per-MTok pricing support#262

Merged
OisinKyne merged 5 commits intomainfrom
codex/feat-issue-258-static-paid-gateway
Mar 6, 2026
Merged

Add static paid x402 gateway and per-MTok pricing support#262
OisinKyne merged 5 commits intomainfrom
codex/feat-issue-258-static-paid-gateway

Conversation

@bussyjd
Copy link
Collaborator

@bussyjd bussyjd commented Mar 6, 2026

Summary

  • add phase-1 per-mtok pricing support with explicit perRequest = perMTok / 1000 approximation and surface the source pricing metadata in sell flows
  • move buy-side paid inference to a static vanilla LiteLLM route paid/* -> openai/* -> x402-buyer with buyer-side metrics, hot reload, and buy.py maintain
  • add verifier metrics, buyer PodMonitor/ServiceMonitor wiring, phase-2 metering design docs, and integration coverage for the qwen3.5:9b paid commerce loop

Closes #258.

Testing

  • go test ./...
  • python3 -m py_compile internal/embed/skills/buy-inference/scripts/buy.py internal/embed/skills/sell/scripts/monetize.py
  • OBOL_DEVELOPMENT=true OBOL_CONFIG_DIR=$(pwd)/.workspace/config OBOL_BIN_DIR=$(pwd)/.workspace/bin OBOL_DATA_DIR=$(pwd)/.workspace/data go test -tags integration -v -run TestIntegration_Tunnel_SellDiscoverBuySidecar_QuotaAndBalance -timeout 30m ./internal/openclaw

bussyjd added 5 commits March 6, 2026 00:28
Adds the full sell→discover→buy BDD test suite and the upstream auth
injection mechanism, rebased cleanly on current main.

## BDD Integration Tests (godog/Gherkin)

7 scenarios, 75 steps, following the real user journey:
  1. Operator sells inference via CLI + agent reconciles
  2. Unpaid request returns 402 with pricing
  3. Paid request returns real inference (EIP-712 → verify → Ollama)
  4. Full discovery-to-payment cycle
  5. Paid request through Cloudflare tunnel
  6. Agent discovers registered service through tunnel
  7. Operator deletes ServiceOffer + cleanup

TestMain bootstrap: obol stack init/up → model setup → sell pricing →
agent init → sell http → wait for reconciliation. No kubectl shortcuts.

## Upstream Auth Injection

x402-verifier now injects Authorization header on paid requests:
  - RouteRule.UpstreamAuth field in pricing config
  - Verifier sets header in 200 response → Traefik copies via authResponseHeaders
  - monetize.py reads LiteLLM master key → writes upstreamAuth to route
  - Eliminates manual HTTPRoute RequestHeaderModifier patches

## Tunnel URL Injection

`obol tunnel status` auto-sets AGENT_BASE_URL on the obol-agent deployment.
monetize.py reads it to publish the tunnel URL in registration JSON.

Files:
  - internal/x402/features/integration_payment_flow.feature (new)
  - internal/x402/bdd_integration_test.go (new)
  - internal/x402/bdd_integration_steps_test.go (new)
  - internal/x402/config.go (UpstreamAuth field)
  - internal/x402/verifier.go (inject Authorization on 200)
  - internal/embed/skills/sell/scripts/monetize.py (read master key, upstreamAuth)
  - internal/tunnel/tunnel.go (InjectBaseURL, auto-inject on status)
  - internal/embed/infrastructure/.../obol-agent-monetize-rbac.yaml (secrets:get)
The previous commit added secrets:get to the cluster-wide
openclaw-monetize-workload ClusterRole, which gave the agent
read access to ALL secrets in ALL namespaces.

Fix: remove secrets from ClusterRole and add a namespaced Role
in the llm namespace scoped to litellm-secrets only via
resourceNames restriction. Same pattern as the existing
openclaw-x402-pricing Role in the x402 namespace.

Verified:
  - Agent can read litellm-secrets in llm namespace (200 OK)
  - Agent cannot list kube-system secrets (403 Forbidden)
  - All 7 BDD scenarios pass with scoped RBAC
…ering

Enriches agent registration with machine-readable metadata for discovery:

## Off-chain (Registration JSON)
- Add OASF service entry with skills[] and domains[] taxonomy paths
  Default for inference: skills=["natural_language_processing/text_generation/chat_completion"]
  Default for inference: domains=["technology/artificial_intelligence"]
- Build richer description from spec (model name, price, type)
- Always emit supportedTrust field (empty array if none)
- CLI flags: --register-skills, --register-domains for custom taxonomy

## On-chain (setMetadata)
- New _set_metadata_on_chain() in monetize.py — ABI-encodes and signs
  setMetadata(uint256, string, bytes) via remote-signer + eRPC
- Sets "x402.supported" = 0x01 and "service.type" = "inference"|"http"
  after successful register() — indexed MetadataSet events enable
  eth_getLogs topic-based filtering without fetching every JSON

## Discovery
- discovery.py search --x402-only: filter by MetadataSet events where
  indexedMetadataKey == keccak256("x402.supported")
- discovery.py search --filter <key>: generic metadata key filter

## Types
- ServiceDef gains Skills/Domains fields (json:"skills/domains,omitempty")

## BDD
- Discover scenario now validates OASF skills and domains in registration

7 scenarios, 77 steps all pass.
@bussyjd bussyjd requested a review from OisinKyne March 6, 2026 13:17
@OisinKyne OisinKyne merged commit 4e8b116 into main Mar 6, 2026
6 checks passed
@OisinKyne OisinKyne deleted the codex/feat-issue-258-static-paid-gateway branch March 6, 2026 18:45
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.

Per-token pricing ($X/1M tokens) is broken — metering layer missing

2 participants