Semana agentic: Cardano entró en x402 y le movió los relojes
Cardano entró en x402 el 9 de septiembre tras catorce semanas de review. Meter por la puerta una chain con finality probabilística obligó al SDK a triplicar el timeout del facilitator, reescribir dos adapters de middleware y alargar el timeout de tools MCP de 60 segundos a lo que declare el seller.
Se mergearon veintitrés pull requests en x402-foundation/x402 entre el 4 y el 11 de septiembre. La semana pasada la historia era contribuidores externos encontrando bugs leyendo código. Esta semana la cuota externa bajó a cinco cuentas con un PR cada una, y el autor más grande no fue una persona: PhilBot402, que firma sus PRs como "Automated by @phdargen", mergeó ocho. phdargen mergeó siete y el bot de docs tres. El patrón es un maintainer más una automatización que porta cada cambio de TypeScript a Go y Python en menos de un día y abre un issue cuando los tres SDKs divergen.
El otro hilo de la semana corre fuera del repo. Un gateway llamado X Pay salió a mainnet en Base el 11 de septiembre y anuncia que devuelve la respuesta pagada antes de que termine el settlement. Visa publicó el 9 de septiembre una encuesta según la cual el 23 por ciento de los consumidores de EE.UU. confía en la IA generativa para ejecutar un pago en su nombre. Ambas cosas tratan de la misma pregunta que el merge de Cardano: quién espera, y cuánto, entre la firma y el dinero.
1. Cardano entró, y los relojes se movieron
El PR #2537 de fabianbormann se abrió el 1 de junio y se mergeó el 9 de septiembre: 99 archivos, 18.673 líneas añadidas, un paquete nuevo @x402/cardano, una spec de scheme en specs/schemes/exact/scheme_exact_cardano.md, fixtures e2e y un workflow de publicación. Es solo TypeScript. El PR de docs añade cardano:mainnet, cardano:preprod y cardano:preview como identificadores de red, con USDM como asset por defecto en mainnet y tUSDM en preprod, ambos con seis decimales. Al momento de escribir esto el paquete todavía no está en npm.
El modelo es el que describimos para XRPL y NEAR en el post de network bindings: firmado por el cliente, enviado por el facilitator. El agente construye y firma una transacción completa, no la propaga, y manda los bytes en PAYMENT-SIGNATURE. El facilitator verifica monto, destinatario, que el UTXO del nonce siga sin gastar, conservación de valor, piso de fee y TTL, y luego la propaga durante settle. El cliente paga el fee de red, así que el facilitator no necesita una wallet con fondos. La primitiva de replay es el UTXO nombrado en payload.nonce, que la transacción consume. La ventana de validez es el slot TTL de la transacción, acotado por maxTimeoutSeconds.
Salen tres valores de assetTransferMethod: default para pago dirección a dirección, masumi para bloquear fondos en el escrow vested_pay de Masumi con un datum fijo de diecinueve campos, y script para bloquear en cualquier contrato definido por el servidor con un datum arbitrario que x402 adjunta tal cual y no puede validar. La spec dedica un párrafo a defender por qué esto vive en extra y no como extensión: las extensiones son ignorables por construcción, y un cliente que ignorara masumi pagaría a la dirección del script sin datum y dejaría los fondos atrapados para siempre.
El párrafo que importa a cualquiera que venda por x402 es el de finality. Cardano corre Ouroboros Praos, y la spec dice que una transacción en el mempool o incluso en un bloque reciente puede revertirse. Dar acceso con status: "mempool" está "strongly discouraged" para cualquier cosa con valor económico real. El settlement puede devolver el estado no terminal settlement_pending con un id de transacción, y el resource server reintenta /settle una vez con el mismo payload mientras el facilitator retoma la observación sin volver a propagar. El ejemplo de la documentación pone maxTimeoutSeconds: 600.
Response.clone() branch after processSettlement, so a long settle could deliver an empty or already-read body". Los adapters leían el body una vez para las extensiones, lo clonaban, y después devolvían el stream original al cliente tras el settlement. En una chain rápida nadie lo notó. En una chain donde el settle tarda lo suficiente para que el runtime drene el clon, el cliente que pagó no recibía nada. El fix consume el body del handler, streams incluidos, antes del settle, y responde desde ese buffer solo después de la confirmación. El mismo PR subió el timeout por defecto de HTTPFacilitatorClient de 30.000 a 90.000 milisegundos. Su docstring decía que los 30 segundos coincidían con Go y Python. Ya no, así que el bot portó los 90 segundos a Python y Go el 8 de septiembre.
Después el PR #3430 del 9 de septiembre arregló el cliente MCP: las llamadas a tools pagas ahora expiran según el maxTimeoutSeconds del accept, 300 segundos por defecto, en lugar de un 60 hardcodeado que abortaba settlements lentos antes de que terminaran. El cliente MCP de e2e eliminó su override manual. Fijate en la dirección de cada uno de estos cambios. Una sola integración de chain movió el presupuesto del facilitator de 30 a 90 segundos, el de tools de 60 a 300, y convirtió el timeout declarado por el seller en la fuente de verdad. Un rail afinado para bloques de dos segundos se está reafinando para bloques de diez minutos, y cada SDK de buyer hereda los nuevos defaults toque Cardano o no.
2. Envenenamiento del catálogo por doble encoding, arreglado en tres lenguajes
El PR #3213 de ygd58 se abrió el 20 de agosto y se mergeó el 9 de septiembre. Arregla isValidRouteTemplate en la extensión Bazaar, el catálogo de discovery que cubrimos en el post de Bazaar. La función hacía una sola pasada de decodeURIComponent antes de buscar .. y :// en la plantilla. Pero ROUTE_TEMPLATE_REGEX permite el signo de porcentaje literal, así que un payload doblemente codificado como %252e%252e o %253a%252f%252f sobrevivía una decodificación todavía codificado, los checks de substring nunca lo veían y la función devolvía true.
La consecuencia, en palabras del propio comentario de la función, es que una plantilla de ruta maliciosa permite a un cliente hacer que el facilitator catalogue un pago bajo una URL arbitraria. El autor descartó el parche obvio. Decodificar dos veces solo cierra el caso doble; el triple encoding se salta cualquier conteo fijo. El fix, fullyDecodeRouteTemplate, decodifica hasta un punto fijo donde otra pasada no cambia nada, con un presupuesto de cinco pasadas, y rechaza todo lo que no parsea o no converge. Seis tests de regresión cubren traversal doble y triple, inyección de scheme con doble encoding, un segmento legítimo con un solo encoding que debe seguir pasando, y encoding patológicamente profundo. Los ports a Python y Go, #3440 y #3441, se mergearon el 11 de septiembre. Veinte días del reporte al merge en TypeScript. Dos días más hasta la paridad.
3. Batch settlement recibió un hint de depósito, y un techo encima
El PR #3372, mergeado el 7 de septiembre, añade un extra.minDeposit opcional al scheme batch-settlement de EVM que auditamos en el post de pagos diferidos. El servidor anuncia un monto atómico que debe ser un entero positivo igual o mayor a amount. Los servidores TypeScript ahora siempre lo anuncian, con diez veces el precio por defecto. Los clientes deberían usar un hint conforme como objetivo de depósito. El facilitator no debe aplicarlo. Un servidor puede rechazar un depósito por debajo del hint con invalid_batch_settlement_evm_deposit_below_min_deposit, pero solo si enforceMinDeposit está activado, y por defecto está apagado.
La mitad interesante es el techo. Los depósitos en este scheme pueden quedarse en escrow durante withdrawDelay, que según el PR puede llegar a 30 días. Un mínimo elegido por el servidor sin tope del lado del cliente es un ataque de lock-up: una respuesta 402 que pide a un agente escrowear un monto sin límite. Así que el cliente acota. Si el buyer configuró spend controls, el depósito se limita a maxAmountPerPayment × depositMultiplier, multiplicador cinco por defecto, mínimo tres. Si el buyer corre con spend controls apagados o una entrada de asset sin tope, el depósito tampoco tiene tope. El PR rechazó explícitamente un maxDeposit atómico global porque no funciona entre tokens con decimales distintos, y rechazó poner la política de depósito en los spendControls del core porque es específica del scheme. Es el mismo razonamiento de la auditoría de spend controls: una sola perilla, el tope que el buyer ya fijó, reutilizada como cota de todo lo que viene después. Go y Python todavía no lo tienen. El issue de drift #3405, abierto por el bot el 8 de septiembre, lo lista como grande.
4. El SDK Python de MCP dejó de seguir redirects fuera del origin
El SDK Python de MCP v2.2.0, publicado el 7 de septiembre junto a una release de mantenimiento 1.30.0, cambia cuatro defaults que importan a un agente que se conecta a servidores que no controla. Los redirects ahora solo se siguen dentro del origin del endpoint: mismo scheme, host y puerto, o un upgrade de http a https en el mismo host. Cualquier otro falla con MCPError, y los providers de OAuth aplican la misma regla a sus propias requests. El ajuste follow_redirects de un cliente httpx provisto por el caller ya no se respeta para tráfico MCP.
El cliente OAuth ahora verifica el issuer del authorization server también en la ruta legacy. Metadata cuyo issuer no sea el origin del propio servidor se rechaza con un error de issuer mismatch. Un 403 que no sea un challenge insufficient_scope se devuelve al caller en vez de reintentarse, y un 5xx o 429 al pedir la protected resource metadata detiene el flujo en vez de caer a los endpoints legacy. Un nuevo AuthSettings.validate_token_resource permite a un servidor aceptar solo tokens que su verifier reporte como emitidos para ese servidor, y 3.0 lo pondrá en true por defecto. Del lado servidor, las sesiones con estado sobre el transporte anterior al 2026-07-28 ahora expiran tras 30 minutos inactivas y se limitan a 10.000 por servidor. Los servidores stateless y las conexiones 2026-07-28 no se ven afectados. Tasks, DPoP y el grant jwt-bearer siguen sin implementar, según las release notes. Cada uno de estos cambios cierra un camino por el que un servidor comprometido o mal configurado podría desviar a un cliente hacia otro sitio. Pertenecen a la misma familia que el trabajo de identidad de cliente del post de autorización MCP.
5. Lado demanda: un gateway que entrega antes de liquidar, y un número de confianza
X Pay, de X-Pay Technologies en Ámsterdam, anunció mainnet el 11 de septiembre: settlement en USDC sobre Base, chain id 8453, precio del uno por ciento del volumen liquidado, nada cobrado en llamadas fallidas. Los claims técnicos son x402 estándar: autorizaciones EIP-3009 firmadas como typed data EIP-712, recuperadas contra el dominio del contrato del token, gastables exactamente una vez. Una frase destaca: "The response is released before settlement completes, so a paid call is as fast as a free one."
Es una decisión de diseño, y es la contraria a la que el SDK de x402 tomó dos veces en ocho días. El hueco de dos fases existe porque el seller quiere ver el dinero antes de soltar los bytes. La semana pasada el SDK de Java dejó de servir contenido antes del settlement. Esta semana Hono y Next dejaron de servir un body vacío tras un settlement largo. La respuesta de X Pay es no retener la respuesta del seller para nadie y, presumiblemente, cargar el riesgo de un settle fallido en su propio balance a cambio del uno por ciento. El comunicado no dice quién absorbe esa falla. Para un seller que elige gateway, esa es la primera pregunta.
El Trust Index de Visa, publicado el 9 de septiembre a partir de una encuesta de Harris Poll a 2.065 consumidores de EE.UU. realizada del 26 al 28 de mayo, reporta que el 72 por ciento ha usado un asistente de IA y el 23 por ciento confía en la IA generativa para ejecutar una transacción de pago en su nombre. El 61 por ciento dijo que confiaría en Visa para una transacción agentic. Leído como hallazgo de producto y no como titular: la brecha de confianza está entre el modelo y el dinero, y la marca en la que la gente confía es el rail, no el agente. Ese es el argumento para empujar la autorización hacia abajo, a la capa de pago, donde una autorización EIP-3009 firmada con tope duro dice exactamente lo que el agente puede gastar, en vez de hacia arriba, al modelo, donde lo dice un prompt.
Qué significa para LLM4Agents
Los cambios de timeout nos pegan de frente. Cualquier lado seller construido sobre el middleware de referencia acaba de ver su presupuesto de facilitator por defecto pasar de 30 a 90 segundos. El settlement en Base no necesita nada parecido, así que un default de 90 segundos es un techo de peor caso que preferimos no heredar en silencio. El cambio en MCP es lo mismo al revés: nuestras tools pagas declaran maxTimeoutSeconds, y ese valor ahora es el temporizador de aborto del cliente. Si declaramos 300 porque es el default, un buyer que esperaba abortar a los 60 segundos esperará cinco minutos en un settle trabado.
El bug de Hono y Next recuerda que el hueco de dos fases tiene dos modos de falla, no uno. Servir antes del settlement le da al buyer contenido gratis. Servir después del settlement desde un stream consumido le da al buyer nada después de pagar. Un gateway que hace streaming de tokens tiene que bufferear la salida del modelo antes del settle, porque los tokens emitidos no se pueden retirar, y la misma clase de bug puede aparecer en cualquier adapter que clone un body para un hook de extensión.
El fix de envenenamiento del catálogo es un threat model directo para nuestras entradas de discovery. Si listamos endpoints en el Bazaar, una plantilla de ruta malformada de un tercero podría haber catalogado un pago bajo una URL nuestra. Ya está arreglado en los tres lenguajes, pero tomó tres semanas y lo encontró un lector externo, no la suite de tests.
Cardano en sí no está en nuestro roadmap. Lo que habilita es un patrón asentado para chains con finality probabilística: settlement_pending, un único reintento acotado y una prohibición explícita de dar acceso en mempool. Ese es el patrón que necesitaríamos para cualquier chain más lenta que Base, y ahora tiene implementación de referencia.
Cómo mantenerse en la frontera
Primero, fijar los timeouts. Poner timeoutMs explícito en nuestro cliente de facilitator en vez de aceptar el nuevo default de 90 segundos, y poner maxTimeoutSeconds en cada accept al tiempo real de settlement en Base más margen, no al default del SDK. Publicar ambos números en nuestra documentación para que los buyers dimensionen sus propios temporizadores de aborto.
Segundo, actualizar el middleware y añadir un test de regresión para el caso de streaming: un handler que devuelve un stream, un settle que tarda más que el drenado del runtime, y una aserción de que el cliente recibe el body completo con PAYMENT-RESPONSE presente. El fix ya está en @x402/hono y @x402/next, pero el test pertenece también a nuestra suite.
Tercero, adoptar extra.minDeposit de nuestro lado solo si pasamos a batch settlement, y si lo hacemos, acotar primero del lado buyer. Nuestro propio SDK de buyer debería salir con depositMultiplier fijado y spend controls activos por defecto, para que ningún 402 pueda bloquear más de cinco veces el tope de un pago.
Cuarto, replicar las reglas de redirect e issuer del SDK Python de MCP en la configuración de nuestro propio cliente MCP, y poner validate_token_resource en true en nuestro servidor antes del default de 3.0.
Quinto, seguir al bot de drift. El issue #3405 es ahora la forma más rápida de saber qué SDK tiene una feature y cuál no. Para una plataforma cuyos buyers llegan en Python, TypeScript y Go, la lista de drift es la matriz de compatibilidad.
Pagar por llamada, liquidar por llamada
Un gateway compatible con OpenAI donde el pago se confirma antes de que salgan los tokens.
Registrar agente