Semana agentic: confianza, no prueba
Esta semana llegó a producción un payment channel en Solana donde el vendedor firma el medidor. El comprador que lo integró no discutió el diseño: escribió su propio lector de la cuenta del canal y compara lo reclamado contra lo que firmó. Tres fixes de x402 aterrizaron la misma semana haciendo exactamente eso: sacar el número del mensaje firmado y leerlo de la cadena.
Catorce pull requests se mergearon en x402-foundation/x402 entre el 2 y el 9 de octubre. phdargen firmó 4, PhilBot402 y CarsonRoscoe 3 cada uno, mintlify[bot] y notorious-d-e-v 2 cada uno. No hubo release: los paquetes publicados más nuevos siguen siendo @x402/core 2.28.0 en npm, del 29 de septiembre, Python x402 2.25.0 y Go v2.28.0.
La semana pasada la pregunta era qué le deja decidir el cliente al servidor. Esta semana la respuesta llegó con la misma forma cuatro veces distintas: cuando dos partes no coinciden en cuánto se gastó ya, lee el dato de algún lugar que ninguna de las dos escribió.
1. Batch-settlement en Solana en producción, y un comprador que lo verifica
El scheme batch-settlement de x402 — depositas una vez en un canal, pagas por llamada con vouchers off-chain, reclamas en lotes — está vivo en Solana mainnet. Sondeamos el facilitator de PayAI el 9 de octubre. Su endpoint /supported anuncia batch-settlement en solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp con USDC, marcado "experimental": true, además de Base mainnet y dos testnets. La política publicada es acotada: depósitos de 10000 a 100000000 unidades atómicas (0,01 a 100 USDC), maxOpenChannels 1.000, maxOpenChannelsPerAccount 10, cinco intentos de depósito por minuto e idleCloseSeconds 259200 — tres días. La documentación de PayAI lo llama "a public preview with bounded channel capacity" y soporta "at most four channels per claim transaction".
Lo que vale la pena leer es el lado del comprador. El PR #435 de ClawRouter, abierto el 7 de octubre y aún abierto al momento de escribir, agrega batch settlement opt-in a un router del lado del agente que paga sol.blockrun.ai por llamada de modelo. Cubrimos el hueco de confianza de ese scheme en julio y su modo server-signed la semana pasada, donde la spec dice que un operador "can take up to the full deposit" sin prueba de haber entregado nada.
El PR enuncia la exposición en una línea: los canales de BlockRun son server-signed, y "the operator can claim up to the whole deposit without another client signature". Después construye cuatro defensas, y ahí está lo interesante. La lista de operadores confiables está vacía por defecto, así que el scheme nunca se registra y toda llamada se queda en exact salvo que se nombre una clave de operador explícitamente. La exposición está topeada a un depósito. El store de canales es un archivo en modo 0600 escrito atómicamente, y un archivo corrupto se rechaza en vez de leerse como vacío, porque "forgetting the signed cumulative is the one failure that must not happen silently".
La cuarta es un claim check. Al arrancar, cada diez minutos y a demanda, lee la cuenta de cada canal directamente — owner CHNLxYvVA28MJP9PrFuDXccuoGXAx7jBacfLEkahyGsX, 256 bytes, el campo settled como u64 little-endian en el offset 20 — y compara el monto reclamado contra el acumulado más alto que esa wallet firmó. Si lo reclamado excede lo firmado, batch settlement se desactiva para el proceso y la falla se loguea fuerte. El comprador no puede impedir un sobre-reclamo, así que lo detecta, barato, desde el único registro que el vendedor no escribe.
Una nota al pie de ese PR es un dato sobre nuestra propia categoría de producto. La integración llama setSpendControls(false), porque @x402/core 2.28 activa spend controls del cliente por defecto y rechazaría toda llamada de más de un dólar. Verificamos el default en el bundle publicado: DEFAULT_MAX_AMOUNT_PER_PAYMENT = "$1". Argumentamos a favor de que ese cap existiera en nuestra auditoría de spend controls. La primera integración real que se lo encontró lo apagó, porque las llamadas de imagen y video cuestan más de un dólar.
2. El SDK hace el mismo movimiento: la baseline desde la cadena
El PR #3655 (CarsonRoscoe, mergeado el 2 de octubre, +2.226/-206 en 29 archivos de Go, TypeScript, Python y la spec) corrige cómo un server de batch-settlement sabe cuánto se cobró ya en un canal cuando no tiene registro local — tras un refund total, un auto-refund por inactividad o un restart. Antes inferia esa baseline del voucher firmado por el cliente. El propio mensaje del pagador decidía dónde arrancaba el medidor.
Ahora la baseline es el totalClaimed on-chain verificado por el facilitator, el chequeo acumulado corre en AfterVerify, y un request sin totalClaimed se rechaza en vez de adivinarse. El lado del facilitator se endureció con eso: un voucher o depósito que no sea refund debe avanzar maxClaimableAmount a por lo menos totalClaimed + amount, donde antes solo tenía que superar totalClaimed. Los vouchers de refund quedan exentos y pueden seguir igualándolo. El PR #3661 (phdargen, mergeado el 2 de octubre) espeja la regla en SVM, rechazando vouchers "that do not advance beyond the onchain settled watermark" — el mismo chequeo que ClawRouter escribió a mano.
La regla más filosa está en la edición de la spec. El precio con el que se calcula ese piso llega del resource server, y el binding EVM ahora dice que requirements.amount "is supplied by the resource server and MUST NOT be trusted". Un facilitator debe validarlo como entero base-10 no negativo y devolver una respuesta inválida tipada si viene malformado o negativo, en vez de tirar una excepción o bajar el piso en silencio. Los dos lados del pago son ahora inputs no confiables para la única parte que toca la cadena.
3. auth-capture v1.1 aterriza, y dice lo que no puede probar
El scheme de dos fases auth-capture — autorizar ahora, capturar o anular después — recibió su implementación v1.1 en ambos SDKs. El PR #3203 (phdargen, abierto el 18 de agosto, mergeado el 6 de octubre, +24.279/-1.287 en 100 archivos) lleva TypeScript a la spec v1.1. El PR #3622 (CarsonRoscoe, mergeado el 5 de octubre, +12.305/-494 en 75 archivos) agrega el server y el facilitator de Go sobre el contrato AuthCaptureEscrow de base/commerce-payments, con corridas end-to-end cross-language reportadas en verde en ambas direcciones sobre EIP-3009 y Permit2.
Auditamos el flujo v1.0 en agosto. Los cambios de v1.1 son directos sobre qué estaba mal. extra.autoCapture se rechaza de plano, reemplazado por paymentFlow y captureMode. Los motivos de error pasan al namespace invalid_auth_capture_evm_*. El target de settle sale de operatorType en vez de una sonda con getCode. Los deadlines deben ser todos absolutos o todos relativos. Y una ruta collect-only ahora debe declarar captureMode: "deferred", con el viejo default sync lanzando error "at enhance time rather than escrowing funds the facilitator will not finalize" — una ruta mal configurada solía dejar en escrow el dinero de un pagador que nada podía capturar, hasta el reclaim tras el deadline.
El binding EVM es inusualmente honesto sobre el modelo de operador delegado. Con un extra.receiverAuthorizer distinto de cero, el facilitator relaya tanto el collect como las llamadas de lifecycle, y la spec escribe qué entrega el servidor:
Nothing onchain requires an authorizer signature, checks it against the one the server holds, or stops a capture the server never asked for, so the facilitator's own verification is the only gate. [...] Within those bounds it is trust, not proof.
Y después se movió la clave misma. Un fragmento de changelog de Go del 5 de octubre agrega autorización de receptor delegada al facilitator: un servidor sin ReceiverAuthorizerSigner envía payloads de charge, capture, void y refund sin firmar, y "the facilitator signs them after ResolveCallerIdentity authenticates the caller, binding the identity to the paymentInfoHash". Un merchant sin gestión de claves obtiene capture y refund gratis. La firma EIP-712 que representa el consentimiento del receptor la produce ahora quien autenticó la llamada a la API. El mismo fragmento trae una línea que todo integrador debería subrayar: un receiverAuthorizer omitido en la ruta ya no implica collect-only, y hay que ponerlo en la dirección cero explícitamente.
4. Coinbase Institute pone precio al problema, y muestra un diagrama
El 7 de octubre el Coinbase Institute publicó "Machine-to-machine payments in the AiFi era", un paper de 20 páginas de Chad Harper, Kevin Leffew, Lincoln Murr y Sam Shoar. Su núcleo económico es una sola métrica: la transacción mínima viable, definida como el pago más chico cuyo costo total de procesamiento se mantiene en o por debajo del 5 por ciento de su valor. Los programas de card small-ticket caen en $1,19, el crédito card-not-present entre $5,00 y $14,29, débito en $4,44, FedNow en $0,90, y una transferencia de stablecoin en una cadena de bajo costo entre $0,002 y $0,02. "The gap is three to four orders of magnitude, not an increment." La línea que enmarca todo: "A $0.30 fixed fee on a $60 grocery basket is 50 basis points, a rounding error. The same $0.30 on a $0.001 API call is a 30,000 percent transaction cost."
Dos de sus cinco recomendaciones de política se leen como requisitos de diseño para nuestro stack. Donde una obligación deba aplicar, "should attach to the account, wallet, or relationship that can bear it, not to each transfer" — y la supervisión debe ser programable, porque "a principal's limits and a provider's controls should be enforced at the moment of payment, not reconstructed afterward". La tercera es una advertencia sobre la capa que está arriba del pago: "A standard that lets an agent pay an endpoint but not find one keeps payment open while concentrating the market at the directory layer."
X-PAYMENT-RESPONSE impreso en su portada: {"success":true,"transaction":"0x4d2e8b","network":"base","payer":"0xA11c"}. El id de transacción es un stub de tres bytes, el payer está truncado, el handshake es x402 versión 1 y el timestamp validAfter es el 1 de enero de 2025. Es un diagrama bien hecho, y la economía de arriba no depende de él. No es una medición.
5. Los MCP Server Cards quedan Final; el repo del schema sigue diciendo experimental
El 6 de octubre se mergeó el SEP-2127 tras ocho meses y medio en review, y el texto en main ahora dice Status: Final, Type: Extensions Track. Auditamos el diseño en septiembre con el pull request todavía abierto: un documento JSON estático que describe la identidad y los transports de un server remoto, con GET <streamable-http-url>/server-card reservado como ubicación recomendada y el discovery a nivel dominio delegado a un AI Catalog.
Dos cosas no se movieron con él. El SEP-2127 es, por su propia descripción, un documento de charter, y delega el wire format normativo al repo ext-server-card, donde schema.ts es la fuente de verdad. El README de ese repo sigue abriendo con "Status: Experimental. This work is for prototyping and feedback only, and is not an accepted or official MCP extension", y su último push fue el 12 de agosto. Tampoco está ratificada la capa de discovery: el repo del AI Catalog, activo el 8 de octubre, todavía se llama a sí mismo "a temporary working repo maintained by the Linux Foundation" y dice que "when the specification is finalized, A2A and MCP steering committees will vote on adoption". Un pull request mergeado el 8 de octubre recortó su lista de implementaciones oficiales a los SDKs de Go y Rust, moviendo tres a proyectos de comunidad. Lee la advertencia de Coinbase sobre la capa de directorio contra eso, y el hueco es el mismo.
MCP también publicó documentación de seguridad esta semana: una guía de seguridad para servers locales mergeada el 5 de octubre tras casi tres meses abierta, y un refresh completo del Inspector con una nueva página de seguridad el 8 de octubre.
6. Items menores
Un quinto tipo de path bypass. El PR #3577 (CarsonRoscoe, mergeado el 2 de octubre) cierra un bypass del payment gate en @x402/fastify reportado vía HackerOne. El request.url de Fastify es el request-target crudo, así que un target en forma absoluta — permitido por el RFC 7230 §5.3.2 — conservaba su scheme y autoridad a través de request.url.split("?")[0]. La regex de la ruta entonces no matcheaba y el gate concluía que no había pago pendiente, mientras el router de Fastify sí quitaba el prefijo y servía el handler protegido. El primer fix espejaba la regex del router; el changeset mergeado explica por qué se abandonó: las dos versiones de find-my-way que Fastify 5 admite resuelven esos targets distinto, así que el gate no puede espejar a un router cuyo comportamiento depende de un rango de dependencias. Los targets que no son origin-form ahora se rechazan con 400 antes del matcheo. Es el octavo commit desde agosto cuyo asunto es un fix de path o request-target, después del trabajo de line-terminator y escaped-path de principios de agosto y la familia /api%2Fpremium que cubrimos el 25 de septiembre.
Metadata escrita por el facilitator. El PR #3666 agrega un mapa m a la extensión builder-code, que implementa el Schema 2 de ERC-8021 — el sufijo CBOR de atribución en el calldata de settlement. Las reglas: solo el facilitator lo escribe, cualquier m en un payload se ignora, las claves llevan prefijo x402 y las define el scheme que las emite, el encoding es determinista, y los encoders ahora fallan pasados los 65.535 bytes en vez de truncar en silencio. Todavía nada emite metadata; el follow-up declarado mueve los charge counts de batch-settlement a m.x402ChargeCounts. En el roundup del 4 de septiembre esta misma extensión produjo un bug donde un campo de atribución seteado por el cliente tenía consecuencia on-chain. La respuesta de la spec llegó: los campos con consecuencia en el settlement los escribe quien liquida.
Mergeado no es publicado. Revisamos los paquetes publicados el 9 de octubre. @x402/fastify 2.28.0 todavía contiene request.url.split("?")[0] en sus builds ESM y CJS. La regeneración de bundles del paywall mergeada el 28 de septiembre también sigue sin release: @x402/paywall 2.28.0 todavía carga los identificadores CAIP-2 de Algorand con padding, previos al fix. Todo fix de este roundup está en main y en el node_modules de nadie.
Un modelo de spend control que compite. La página de Agent Payments de Sui, leída el 9 de octubre, describe grants del owner que llevan "a total budget, a per-payment cap, and an expiry" y son "onchain state enforced at settlement, not a server session", revocables, con los requests fuera de alcance respondidos con GRANT_DENIED y las ofertas reembolsables liquidadas en un RefundVault compartido. Está "Live on Sui Testnet. Not on Mainnet yet. Paid calls are invite only", y su rail de agentes anuncia MPP hoy, con ofertas x402 en el mismo body del 402. Testnet o no, ese es el modelo de control que pide el paper de Coinbase y que x402 no tiene.
Y un número congelado. Los contadores de "Last 30 Days" en x402.org — 75,41M transacciones, $24,24M de volumen, 94,06K buyers, 22K sellers — son byte a byte idénticos a lo que registramos el 25 de septiembre y el 2 de octubre. Tres semanas sin movimiento, y sin metodología publicada. Trátalo como un snapshot de antigüedad desconocida.
Qué significa para LLM4Agents
Somos un resource server y el facilitator de nuestro propio billing, así que el patrón de esta semana aterriza en los dos roles.
La integración de ClawRouter es la especificación externa más clara de lo que un agente que paga espera de un gateway como el nuestro. Fallar cerrado ante un operador no confiable. Topear la exposición a un solo depósito. Mantener un registro durable del acumulado más alto que firmaste, y rechazar uno corrupto en vez de leerlo como cero. Reconciliar eso contra una fuente independiente de manera periódica. Si un agente está dispuesto a escribir un parser de layout de cuentas para chequear lo que afirmamos, deberíamos publicar el número que está buscando.
La regla de requirements.amount es la que hay que internalizar. En nuestra arquitectura el motor de billing hace de facilitator de nuestra propia capa de routing, y debería tratar el precio que esa capa le pasa como un input no confiable: no negativo, entero, dentro del máximo cotizado y consistente con el historial liquidado — no simplemente mayor al último valor que cacheamos. El caso del restart del #3655 también es nuestro. Cualquier estado que reconstruyamos tras un deploy debe venir de registros de settlement, no del último mensaje de una contraparte.
La delegación de auth-capture es una oportunidad y un riesgo en el mismo objeto. Un flujo de hold y después capture encaja preciso con inferencia: autorizar un máximo desde max_tokens, entregar, capturar el uso real, anular el resto. Pero la spec dice claro que en el modelo delegado la cadena no chequea el consentimiento del receptor, y el SDK de Go ahora deja que un facilitator produzca ese consentimiento después de autenticar a quien llama la API. Si tomamos un rol de receiverAuthorizer, la clave se queda con nosotros. Si pagamos en el escrow de alguien más, leemos su operatorType primero y asumimos que un capture hasta el máximo firmado puede pasar sin nosotros.
El campo m es lo más directamente útil de todo esto. Un mapa de metadata escrito por el facilitator que los indexers de ERC-8021 ya parsean es donde corresponde un recibo por llamada: cantidad de requests, modelo, precio unitario, monto cobrado, escrito por la parte que mandó la transacción. Eso vuelve nuestro billing auditable por alguien que no nos cree ni a nosotros ni al agente, que es la única auditoría que cuenta. Y el bypass de fastify es una amenaza viva para cualquiera que cobre delante de una API: nuestro gate y nuestro router tienen que coincidir en la ruta, y la lección es que la coincidencia sale de normalizar el input una vez, no de imitar un parser que no controlamos.
Cómo mantenerse en la frontera
Cinco pasos, en orden.
Primero, normalizar el path del request una sola vez, esta semana. Auditar cada lugar donde nuestro paywall o gateway deriva una ruta desde un request-target crudo. Rechazar con 400 todo lo que no sea origin-form antes del matcheo, en vez de reproducir el parsing de un router. Agregar un test de regresión que escriba una request line cruda a un socket, porque los clientes HTTP comunes no pueden construir una y por eso no lo detectan.
Segundo, pinear los SDKs y mirar main, no los releases. Todo lo de arriba está sin publicar. Pinear versiones exactas, seguir typescript/.changeset y go/.changes/unreleased, y mantener el chequeo que usamos esta semana: desempaquetar el artefacto publicado y grepear el string que introdujo el fix. Dos fixes relevantes para seguridad están en main sin fecha de release.
Tercero, publicar el número que buscaría un auditor. Exponer un total acumulado cobrado por canal o por agente, firmado, junto a las transacciones liquidadas que lo respaldan — y diseñar ya nuestra metadata de settlement contra las reglas de m (claves con prefijo x402, enteros y texto, encoding determinista, 65.535 bytes) para estar listos cuando un scheme empiece a emitirla. Arrancar con cantidad de requests y monto cobrado por claim.
Cuarto, hacer que nuestros caps se apliquen al momento del pago, y subir el default. El paper de Coinbase pide límites "enforced at the moment of payment, not reconstructed afterward"; Sui los aplica on-chain en el settlement; nuestros presupuestos son server-side y tienen que comportarse igual, con una denegación machine-readable que nombre el límite que se tocó. Y nota qué hizo la primera integración real con un cap de un dólar del lado del cliente: lo desactivó. Nuestros techos por pago tienen que estar dimensionados para llamadas de video e imagen, o los agentes también los van a apagar.
Quinto, publicar un Server Card y una entrada de AI Catalog. Servir un card v1 en /server-card de nuestro endpoint MCP y referenciarlo desde /.well-known/ai-catalog.json, validado contra el schema generado del repo de la extensión. El status Final vuelve seguro construir contra el formato; no ratifica la capa de discovery, y no hace confiable un card sin firma. Publicarlo, indexar temprano, y no dejar que ninguna decisión de pago dependa de uno.
Paga por llamada, en stablecoins, sobre una API compatible con OpenAI
Registra un agente, fondéalo y empieza a rutear. Sin suscripción, sin mínimo mensual.
Registrar un agente