Self-hosting del settlement x402: auditoría de x402-rs
Todo pago x402 termina en un facilitator: el servidor que verifica una autorización firmada y la ejecuta on-chain. Nuestro deep dive del API del facilitator cerró con una salida de emergencia de una línea — puedes self-hostear con x402-rs. Este es el follow-up: clonamos el repo en HEAD, leímos el parser de configuración y sondeamos la instancia en vivo del maintainer.
La pregunta detrás de esta auditoría no es académica. Un gateway que enruta llamadas a modelos y las liquida en stablecoins depende de un facilitator para cada pago x402 walk-up. Si ese facilitator es un tercero hosteado, heredas su tarifario, su uptime, su superficie de censura y su vista sobre la metadata de tus transacciones. Self-hostear elimina las cuatro — y las reemplaza por custodia de signers, confiabilidad de RPC y un float de gas que ahora tienes que administrar. x402-rs es la respuesta open source más completa a ese trade, así que lo tratamos igual que los quince network bindings: clonar, leer, verificar y reportar lo que realmente hay, no lo que promete el README.
El rol que estás asumiendo
Un recap rápido del contrato, porque todo lo que sigue cuelga de él. En x402 v2, el middleware del seller llama POST /verify antes de hacer el trabajo (un pre-flight que no cuesta gas) y POST /settle después (la ejecución on-chain). GET /supported anuncia qué combinaciones de scheme y red puede ejecutar el facilitator y — crítico para bindings estilo Solana — qué direcciones de signer usará, para que los sellers puedan fijar el fee payer. El facilitator es non-custodial por construcción: solo ejecuta autorizaciones de transferencia que el buyer firmó, con destino y monto fijados por la firma. Lo que sí puede hacer es rechazar, demorar u observar. Esas son las tres propiedades que el self-hosting recupera.
También hereda un cuarto trabajo fácil de pasar por alto: en cadenas EVM el facilitator es el emisor de la transacción de transferWithAuthorization de EIP-3009, lo que significa que paga el gas de cada settlement. Un facilitator self-hosteado no es infraestructura gratis. Es una hot wallet con presupuesto operativo.
La economía del trade es concreta. Cuando relevamos el mercado hosteado en julio, el facilitator de CDP de Coinbase cobraba 1,000 transacciones gratis al mes y $0.001 por transacción después, con un directorio de una docena de alternativas a tarifas similares o no publicadas. A volúmenes de escala agente — millones de settlements de fracciones de centavo — un fee fijo por transacción es una segunda cuenta de gas que crece linealmente con tu éxito. Self-hostear lo convierte en un costo operativo plano: el gas en sí, la suscripción de RPC y el tiempo de ingeniería que esta auditoría intenta poner en precio.
Qué contiene realmente el repo
x402-rs vive en x402-rs/x402-rs en GitHub, Apache-2.0, con el tagline "a comprehensive Rust toolkit for the x402 protocol". A nuestra fecha de clonado (2026-08-09, HEAD e75adda, último commit 2026-07-13) el repo mostraba 284 stars, 166 forks y 21 issues abiertos. Es, en la práctica, un proyecto de una sola persona: de los 51 commits más recientes, 50 son de Sergey Ukustov, el autor listado en el manifest del workspace. Vale decirlo sin rodeos antes de cualquier decisión de dependencia — es trabajo solo de alta calidad, no un equipo respaldado por una foundation.
El workspace está en versión 2.0.2 (Rust 1.93, edition 2024) y se divide limpiamente en cuatro capas. x402-types contiene los tipos del protocolo, los traits del facilitator y el registro de nombres de red de v1. x402-axum y x402-reqwest son el middleware de seller y buyer — los equivalentes Rust del seller stack y el buyer stack que auditamos en el SDK de TypeScript. x402-facilitator-local implementa la lógica de verify/settle/supported como librería. Y un crate facilitator lo envuelve todo en un binario servidor Axum ejecutable, distribuido vía cargo install --git o la imagen Docker ghcr.io/x402-rs/x402-facilitator — el binario en sí no está en crates.io. La capa de cadenas son cuatro crates: x402-chain-eip155, x402-chain-solana, x402-chain-tron y x402-chain-aptos — este último solo por git porque arrastra librerías core de Aptos que requieren dos entradas [patch] en tu manifest para siquiera compilar.
La adopción es medible y reciente: x402-types se publicó por primera vez en crates.io el 2026-02-01 y acumula 31,716 descargas totales (17,877 recientes) al momento de escribir. La versión 2.0.0 llegó el 2026-06-16 con breaking changes, la 2.0.1 el 2026-06-19 y la 2.0.2 el 2026-07-12, seguida al día siguiente por el crate de TRON en 0.2.2. La cadencia durante junio y julio fue de aproximadamente un cluster de commits por semana.
El modelo de configuración
El facilitator se controla con un único archivo JSON, con claves en identificadores de cadena CAIP-2 — la misma convención que usa la spec v2 en el wire. Una config mínima funcional para Base más Solana se ve así:
{
"port": 8080,
"host": "0.0.0.0",
"chains": {
"eip155:8453": {
"eip1559": true,
"signers": ["$FACILITATOR_PRIVATE_KEY"],
"rpc": [{ "http": "https://mainnet.base.org", "rate_limit": 100 }]
},
"solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp": {
"signers": ["$SOLANA_PRIVATE_KEY"],
"rpc": [{ "http": "https://api.mainnet-beta.solana.com" }]
}
},
"schemes": [
{ "id": "v2-eip155-exact", "chains": "eip155:*" },
{ "id": "v2-solana-exact", "chains": "solana:*" }
]
}
Tres detalles lo hacen mejor de lo que parece. Primero, cualquier valor string puede ser una referencia a variable de entorno ("$FACILITATOR_PRIVATE_KEY") resuelta al cargar vía un tipo wrapper LiteralOrEnv, así que las llaves quedan fuera del archivo. Segundo, signers es un array — el facilitator soporta un pool de llaves de firma por cadena, y el changelog de la 1.5.4 documenta selección aleatoria de la dirección del facilitator en el flujo upto, lo que reparte la presión de nonces entre llaves. Tercero, rpc también es un array, con rate_limit por endpoint, dándote failover multi-proveedor en configuración y no en código. Las entradas de schemes aceptan patrones de cadena: un id CAIP-2 exacto, un wildcard como eip155:*, o un conjunto como eip155:{1,8453}. Los defaults por cadena son sensatos — eip1559 activado, flashblocks desactivado, timeout de receipt de 30 segundos.
La superficie HTTP es exactamente los tres endpoints de la spec más acabado operativo: GET/POST en /verify y /settle (las variantes GET devuelven información de schema), GET /supported, un greeting en la raíz y GET /health — que simplemente delega en /supported, de modo que una respuesta sana significa que el registro de schemes realmente construyó. CORS está abierto de par en par (origin Any), el shutdown es graceful ante SIGTERM/SIGINT, y el tracing y las métricas OpenTelemetry vienen detrás de un feature flag telemetry configurado con las variables OTEL_* estándar.
Cobertura de schemes: qué está implementado y qué no
El facilitator registra siete implementaciones de scheme: v1-eip155-exact, v2-eip155-exact, v2-eip155-upto, v1-solana-exact, v2-solana-exact, v2-aptos-exact y v2-tron-exact. Dos cosas resaltan frente a la implementación de referencia en TypeScript.
La primera es que upto — el scheme de facturación metered construido sobre Permit2 — está implementado del lado del facilitator, no solo del cliente. La versión 2.0.0 entregó el cliente Rust completo (V2Eip155UptoClient) y el camino de settlement contra el contrato x402UptoPermit2Proxy, incluyendo el enforcement de la autorización del facilitator en el witness de Permit2. Encima hay una extensión que no habíamos visto en otro lado: eip2612GasSponsoring, que permite al cliente pedirle al facilitator que patrocine la aprobación única de Permit2 vía un permit EIP-2612 — con chequeo previo de allowance, para no crear permits redundantes. Eso cierra el problema de arranque en frío más duro del flujo upto: una wallet nueva puede pasar de cero a pagos metered sin haber tenido gas jamás.
La segunda es lo ausente: no hay scheme de batch-settlement ni deferred. El roadmap del README todavía lista "Deferred Scheme" como planeado. Si tu arquitectura necesita rieles de crédito estilo Cloudflare de commit-accumulate-redeem, x402-rs no los provee hoy — es un plano capital-backed que liquida por llamada. Relacionado: los schemes v1 aún resuelven nombres humanos de red ("base-sepolia", "polygon-amoy") a través de un registro de unas dieciséis redes EVM conocidas en x402-types, mientras los schemes v2 aceptan cualquier identificador CAIP-2 sin registro alguno — una ilustración limpia de por qué el addressing de v2 justificó el break.
TRON es el titular silencioso
El crate de cadena más nuevo es el más interesante estratégicamente. x402-chain-tron (mergeado como PR #98, "Facilitation on TRON", versión actual 0.2.2) lleva el settlement x402 a la cadena que carga el mayor float de USDT — y lo hace casi sin criptografía nueva. El TIP-712 de TRON es byte-idéntico a EIP-712, así que el struct de autorización es el mismo que en EVM; el crate soporta tanto transferWithAuthorization estilo EIP-3009 como transferencias Permit2 a través del deployment de Permit2 de SUN.io, con un contrato X402ExactPermit2Proxy dedicado en mainnet y en la testnet Nile. Las diferencias son todas operativas: las direcciones viajan como Base58Check en el wire pero como hex EVM dentro del typed data, el settlement va por el API REST de TronGrid y no por JSON-RPC, y TRON no tiene contract wallets — solo ecrecover secp256k1, así que los buyers con smart accounts quedan afuera.
Recuerda de la auditoría de network bindings que el SDK de TypeScript incluye un paquete de mechanism tvm pero los documentos por red del repo de la spec no hacen de TRON un titular. En Rust ahora es un target de facilitator de primera clase con USDT — no USDC — como token documentado. Para pagos machine-to-machine eso importa: una economía de agentes que liquida donde realmente está la liquidez de stablecoins se ve distinta de una confinada a USDC en Base.
Sondeando la instancia en vivo
El proyecto opera un facilitator público de testnet gratuito en facilitator.x402.rs. Consultamos GET /supported el 2026-08-09. La respuesta anunciaba 31 payment kinds sobre 19 redes distintas: 26 entradas v2 (14 exact, 12 upto) y 5 entradas v1 todavía direccionadas por nombre de red. La cobertura EVM en v2 abarca Base Sepolia, Ethereum Sepolia, Arbitrum Sepolia, Polygon Amoy, BSC testnet y Monad testnet entre otras, más Solana devnet bajo su genesis hash CAIP-2 y TRON Nile bajo tron:0xcd8690dc. Casi todas las entradas exact de EVM anuncian la extensión eip2612GasSponsoring.
El mapa signers — el campo que señalamos en el deep dive del API del facilitator como la defensa del seller para fijar fee payers — mostró una asimetría que vale anotar: el fee payer de Solana devnet y el signer de TRON Nile están divulgados (deben estarlo, porque los buyers los incrustan en las transacciones), mientras que las doce cadenas EVM devuelven arrays de signers vacíos. Nada en la spec exige divulgación en EVM, donde el facilitator es solo el emisor de la transacción. Pero significa que un seller no puede pre-autorizar direcciones específicas de facilitator EVM solo con /supported — una brecha pequeña y real entre lo que el endpoint puede expresar y lo que este deployment comparte.
Qué encontró la auditoría
Leímos el parser de configuración antes de probar los ejemplos documentados, y resultó ser el orden correcto. Tres hallazgos, todos verificados contra HEAD e75adda:
facilitator/config.json.example no parsea (jq: "Expected separator between values at line 7") porque a dos entradas _comment les faltan comas. Quien parta del archivo de ejemplo obtiene un error de parseo en el primer boot.
Hallazgo 2 — el README del facilitator documenta una clave de config que el parser rechaza. Su ejemplo usa {"scheme": "v2-eip155-exact", ...}, pero el struct SchemeConfig en x402-types exige que el campo se llame id, sin alias de serde. La misma clave incorrecta aparece en el doc comment de x402-types/src/config.rs. La deserialización falla con un error de campo faltante.
Hallazgo 3 — un tercer formato de config fantasma. El doc comment en facilitator/src/config.rs muestra cadenas configuradas con claves planas rpc_url y signer_private_key. El struct real Eip155ChainConfigInner exige signers (un array) y rpc (un array de objetos). Ese formato tampoco parsea nunca. En total el repo documenta tres formas de configuración distintas, y solo una — la del archivo de ejemplo, menos sus errores de sintaxis — es real.
Suma un cuarto más blando: el roadmap del README todavía lista el "Upto Scheme" y el "Gasless Approval Flow" como planeados, aunque ambos salieron en la 2.0.0 y ambos están vivos en la instancia pública. Nada de esto es un problema de correctitud en el camino de settlement — el código que leímos es cuidadoso, los traits son limpios, y un harness de protocol-conformance TypeScript-contra-Rust en el repo levanta binarios reales de facilitator, seller y buyer para testear interoperabilidad entre implementaciones. Es deuda de documentación exactamente del tipo que acumulan los proyectos solo, y cae en la peor página posible: los primeros treinta minutos del deployment de un operador nuevo.
Construido para extenderse, no solo para desplegarse
La parte del repo que mejor predice su longevidad es la documentación dirigida a quien quiere modificarlo. Dos guías en docs/ — "Build Your Own Facilitator" y "How to Write a Scheme for x402-rs" — describen la superficie de extensión en detalle, y el código coincide con ellas. Un scheme de pago nuevo son cuatro traits: X402SchemeId lo nombra, X402SchemeFacilitatorBuilder lo construye desde un chain provider, X402SchemeBlueprint lo registra y X402SchemeFacilitator implementa el par async de verify y settle. El binario del facilitator en sí es una composición delgada: construir un ChainRegistry desde config, registrar blueprints en un SchemeRegistry, envolverlo en FacilitatorLocal y montar las rutas Axum de fábrica. Ese es todo el servidor.
Para un operador de gateway esta es la diferencia entre un producto y una plataforma. Hooks propios pre y post settlement — control de acceso, exportación de billing, detección de anomalías en patrones de settlement — encajan en la frontera de FacilitatorLocal sin forkear la lógica del protocolo. Y si falta un scheme que necesitamos (batch settlement es el candidato obvio), el paso a paso de la guía es un camino realista para implementarlo contra los traits upstream y no en un fork privado.
Operarlo, y la salida managed
El deployment es genuinamente simple una vez superada la config: un contenedor Docker, un archivo JSON montado, puerto 8080, feature flags para compilar solo las cadenas que necesitas, export OTLP si quieres traces. Las piezas que debes traer son las que ningún binario resuelve — llaves de signer fondeadas por cadena y su historia de custodia, endpoints RPC en los que confíes (con el failover integrado como mitigación, no absolución), y monitoreo del balance de gas que determina en silencio si /settle sigue funcionando. Para equipos que quieren el codebase sin el pager, el maintainer también opera FareSide, un facilitator hosteado construido sobre x402-rs cuyo pitch es la versión honesta del trade: sin lock-in, porque cambiar de facilitator es cambiar una URL.
Qué significa para LLM4Agents
LLM4Agents liquida llamadas x402 walk-up a través de infraestructura de facilitator hoy, lo que hace de este repo un insumo estratégico directo. Tres consecuencias. Primera, self-hostear el settlement plane ya es un proyecto realista y acotado: un solo binario auditado cubre exact y upto en todas las cadenas EVM que nos importan, más Solana — es decir, nuestro camino de facturación por llamada podría correr sin facilitator de terceros en el loop, sin fee por transacción y sin un externo observando nuestra metadata de settlement. Segunda, el crate de TRON abre un riel que hoy no cotizamos: settlement en USDT donde vive la liquidez de stablecoins más profunda, con el mismo stack de firma EIP-712 que ya operamos. Tercera, el perfil de riesgo es legible: una dependencia mantenida por una sola persona argumenta por pinnear versiones, correr el harness de conformance del propio repo en nuestro CI y tratar nuestra config de deployment como código testeado — precisamente porque la documentación es la capa más débil del proyecto.
Cómo mantenerse en la frontera
Pasos concretos, en orden. Levantar x402-rs contra Base Sepolia y Solana devnet y apuntar el camino walk-up de un gateway de staging hacia él, usando el harness de conformance como puerta de aceptación. Cablear nuestro sync de /supported en el boot contra la instancia self-hosteada y alertar ante drift, igual que hace el middleware de seller en TypeScript. Enviar upstream los fixes de los tres hallazgos de config — parches pequeños, y la credibilidad más barata disponible en un ecosistema así de joven. Prototipar un price tag exact de TRON en un endpoint interno para aprender la superficie operativa de TronGrid antes de que llegue la demanda de USDT. Y mantener honesta la lista de brechas: si nuestro roadmap alguna vez necesita batch settlement credit-backed en el edge, x402-rs no lo provee, y debemos saberlo antes de que una arquitectura asuma lo contrario.
Settlement que puedes operar tú mismo
LLM4Agents mide llamadas a modelos por uso y las liquida en stablecoins vía x402 — sobre infraestructura que auditamos antes de confiar.
Registra tu agente