Auditoría de KYAPay: seis drafts para saber quién está detrás del agente
Un agente toca la puerta de tu API. Dos preguntas deciden si lo atiendes: ¿hay un humano real detrás de esto? y ¿puede pagar? Una familia de seis Internet-Drafts, todos publicados el mismo día de julio, intenta responder ambas en un solo header HTTP.
El 2026-08-16 leímos el texto crudo de los seis drafts, bajamos sus repositorios de GitHub, revisamos los tres registros de IANA en los que piden escribir y comparamos la especificación en papel contra lo que la empresa detrás de ella realmente sirve en producción. Esto es lo que encontramos.
La familia se llama KYAPay — Know Your Agent más Pay. La firman Ankit Agarwal, de Skyfire Systems Inc., y Michael B. Jones, de Self-Issued Consulting, coautor de JWT, JWS y JWK. Ese pedigrí es la razón para tomarla en serio, y también la razón para leerla con cuidado.
Seis drafts, un día, cuatro empleadores externos
Todos los documentos de la familia llevan fecha 19 de julio de 2026 y vencimiento 20 de enero de 2027. Cinco están etiquetados Standards Track. El encabezado de las páginas dice "Web Authorization Protocol" — el nombre del propio working group de OAuth en el IETF — pero el datatracker los lista como individual submissions sin working group asociado, y cada uno lleva el texto estándar de que el draft "is not endorsed by the IETF and has no formal standing in the IETF standards process".
El reparto de autoría vale leerlo como un mapa de a quién se está reclutando:
draft-skyfire-oauth-kyapay-token-01— el formato del token. Agarwal y Jones. Reemplaza al anteriordraft-skyfire-kyapayprofile.draft-skyfire-oauth-using-kyapay-tokens-00— cómo lo consumen los intermediarios. Suma a Srinivasa Thumma, de Akamai.draft-skyfire-oauth-id-verification-01— el claimivm. Suma a Nash Ali, de Experian.draft-skyfire-oauth-amr-values-01— diez valores nuevos deamr.draft-skyfire-oauth-aml-methods-00— el claimaml. Agarwal, Jones, Ali.draft-skyfire-oauth-kyapay-token-exchange-01— canjear el token por un access token de OAuth. Suma a Abhishek Hingnikar, de Okta, y Jeffrey Hickman, de Ory.
Un buró de crédito, un CDN y dos proveedores de identidad. Eso no es casualidad de redacción; es la forma del problema. Verificar que un humano autorizó a un agente necesita a alguien que pueda verificar humanos (Experian), a alguien que vea el request antes que el origin (Akamai) y a alguien que ya opere el authorization server (Okta, Ory).
Los repositorios de GitHub confirman esa línea de tiempo. skyfire-xyz/draft-skyfire-oauth-kyapay-token se creó el 2025-11-20; los otros cinco entre el 2026-06-12 y el 2026-07-14, y todos se detienen en un commit del 2026-07-19 con mensajes como "Results of proofreading by Mike" y "Add history entry for adding Nash and Srini as authors". Stars sumadas de los seis repos: una.
Tres tipos de token, un header
KYAPay define tres JWT, distinguidos por el header parameter typ, que es REQUIRED junto con kid y alg (hoy, ES256):
kya+jwt prueba identidad y no puede cobrarse. pay+jwt autoriza dinero. kya-pay+jwt lleva las dos cosas. La separación es la buena idea del diseño: un agente que lee una página con paywall y un agente que compra un producto presentan credenciales distintas, y un seller que solo necesita saber que existe un humano nunca recibe material de pago.
Los tres viajan en el mismo lugar. El draft Using KYAPay Tokens define un request header con gramática explícita:
// draft-skyfire-oauth-using-kyapay-tokens-00, Section 5.1
KYAPay-Token = token-jwt *( OWS "," OWS token-jwt )
token-jwt = 1*( ALPHA / DIGIT / "-" / "_" / "." )
El razonamiento es cuidadoso. Como un JWT usa solo el alfabeto base64url más el punto separador, nunca contiene una coma, así que el parseo de la lista no es ambiguo. Un sender MUST poner exactamente un JWT por elemento de la lista y MUST NOT depender del orden para transmitir significado; un recipient MUST determinar el tipo de cada token por su header parameter typ, no por su posición, y MUST ignorar los elementos que no pueda parsear como JWT mientras sigue con el resto. El header MUST NOT enviarse sobre una conexión sin TLS, MUST NOT loguearse y MUST NOT cachearse de forma que permita replay en otro request.
Esa es una buena definición de header. Los desacuerdos empiezan con lo que va adentro.
Qué afirma sobre ti un token KYA
Seis claims comunes son REQUIRED — iss, sub, aud, iat, jti, exp — más cinco opcionales: tdm (target domain), ori (URL del originator), env (production o sandbox), tsi (target service ID) e itg (un tag opaco del initiator). iss es una URL, usada para descubrir el JWK Set por el mecanismo de sufijo /.well-known/jwks.json.
Sobre eso, la mitad de identidad agrega tres claims estructurados. hid es el principal humano, REQUIRED para los casos de uso con identidad humana. aid es el agente, REQUIRED. apd es la plataforma del agente, OPTIONAL. Adentro, dos sub-claims son obligatorios y son justo los discutibles:
// draft-skyfire-oauth-kyapay-token-01, Figure 1 (abreviado)
{
"iss": "https://example.com/issuer",
"iat": 1742245254,
"exp": 1773867654,
"aud": "7434230d-0861-46f2-9c2c-a6ee33d07f17",
"env": "production",
"hid": {
"email": "[email protected]" // REQUIRED
},
"aid": {
"name": "Acme Agent Extraordinaire", // REQUIRED
"creation_ip": "54.86.50.139", // REQUIRED
"source_ips": ["54.86.50.139-54.86.50.141", "1.1.1.0/24"]
}
}
Dentro de hid, email es REQUIRED; nombre, apellido, teléfono, nombre de la organización y el trío verifier / verified / verification_id son opcionales. Dentro de aid, el name del agente y su creation_ip son REQUIRED, con source_ips listando opcionalmente rangos, bloques CIDR o dominios desde donde el agente llamará.
O sea: la afirmación mínima de identidad es el email de un humano más la IP pública de la máquina que creó el token. Enviado a cada seller que el agente toque.
Ese es exactamente el hueco para el que se construyó SD-JWT, y por eso le dedicamos un post entero al selective disclosure en cadenas de delegación. Un token KYAPay es un JWT plano: el seller que solo necesita saber "un humano verificado y mayor de edad autorizó esto" recibe también la dirección de email, porque no hay forma de mandar una cosa sin la otra.
La mitad PAY mete números de tarjeta en un JWT
Los claims de pago son tpr (precio del servicio destino), tps (esquema de precio: pay_per_use, subscription, pay_per_mb o custom), amt (monto en unidades de moneda), cur (código ISO 4217), val (monto en las unidades de la red de settlement), mnr (máximo de requests), stp (tipo de settlement: coin o card) y sti (metadata del settlement).
El par amt / val es el conocido: "amt": "15", "cur": "USD", "val": "15000000" — quince dólares, quince USDC a seis decimales. Cualquiera que haya escrito un payload x402 reconoce la forma.
Lo que sigue es menos familiar. En el propio ejemplo KYA-PAY de la spec, sti contiene un paymentToken de dieciséis dígitos, un mes de expiración, un año de expiración y un código de seguridad de tres dígitos, junto a "type": "visa_vic". Un número de tarjeta virtual con su CVV, dentro de una credencial bearer, en un header HTTP.
Dos cosas sobre ese bloque. Primero, el texto normativo de la Section 3.3.1 llama a esos sub-claims payment_token, token_expiration_month, token_expiration_year y token_security_code en snake_case, mientras que todos los ejemplos los escriben en camelCase. Segundo, la Section 3.3 declara sti REQUIRED, y la Section 3.3.1 abre con "The sti claim is optional. If present, it MAY contain the following sub-claims, all of which are OPTIONAL" — y después marca type como REQUIRED. Tres niveles de requisito contradictorios en ocho líneas.
El tercer hueco es estructural más que editorial. Cuando stp es coin, el único valor de type definido es "usdc". No hay identificador de cadena, no hay campo de red CAIP-2, no hay dirección de contrato. El token dice quince USDC y no dice en cuál de las quince y pico de redes donde existe USDC. Un settlement sin identificador de red no es una instrucción de settlement, es una pista.
Un vocabulario de claims para compliance
Los tres drafts satélite son la parte que podría sobrevivir al resto, porque son genéricos. Ninguno menciona agentes en sus definiciones de claims.
ivm, del draft de verificación de identidad, es un array JSON de strings que declara cómo se verificó a una persona. Ocho valores: dbv (verificación de PII contra un número no especificado de consumer reporting sources), dbv1 (una fuente), dbvm (múltiples fuentes), dig (documento digital, por ejemplo una licencia de conducir móvil), phy (documento físico), sec (documentos secundarios — extractos bancarios, facturas de servicios), inp (en persona) y vid (entrevista por video en vivo).
aml, del draft de AML, también es un array: ofac (cumplimiento OFAC), sanc (screening de sanciones), watch (screening de watchlists), pep (personas expuestas políticamente) y adv (screening de medios adversos).
El draft de amr agrega diez valores al registro que estableció el RFC 8176: app (app autenticadora), bg (autenticación silenciosa de red), email, call, code, url, push, facliv (reconocimiento facial con prueba de vida), sqa (preguntas de seguridad) y psk (passkey).
Todo junto, un seller puede leer de un solo token que el humano detrás del agente fue verificado por entrevista de video en vivo, se autenticó con passkey y pasó screening de sanciones y PEP. Ese vocabulario es genuinamente útil. Si un seller debería creerle es otra pregunta, y los drafts son honestos en que sigue sin resolverse.
La parte empírica: no hay nada registrado
Las especificaciones que definen claims viven o mueren por IANA. Bajamos los registros el 2026-08-16.
El registro de JSON Web Token Claims tiene hoy 172 entradas. La familia KYAPay pide dieciséis de ellas — tdm, tsi, ori, env, itg, hid, apd, aid, tpr, tps, amt, cur, val, mnr, stp, sti — más ivm y aml de los drafts satélite. Cuántos de esos dieciocho están en el registro: cero.
El registro de Authentication Method Reference Values tiene 21 valores: face, fpt, geo, hwk, iris, kba, mca, mfa, otp, pin, pop, pwd, rba, retina, sc, sms, swk, tel, user, vbm, wia. Cuántos de los diez propuestos están presentes: cero.
El registro de media types de application no contiene kya+jwt, ni pay+jwt, ni kya-pay+jwt. Los dos registros nuevos que los drafts proponen crear — Identity Verification Methods y Anti-Money Laundering Methods — todavía no existen.
Esto no es un escándalo; es lo que significa "I-D Exists". El registro viene después de la publicación, y ninguno de estos documentos fue adoptado por un working group, mucho menos publicado como RFC. Pero sí fija bien la expectativa. Quien integre hoy está integrando contra un formato de vendor que casualmente tiene nombre de archivo con forma de IETF. El papeleo es una declaración de intención, no de estado.
Qué hace producción realmente
El contraste interesante es entre los drafts y el sistema corriendo. La documentación pública de Skyfire es específica, y no coincide con el papel justo donde importa.
El tiempo de vida del token es el caso más claro. Los ejemplos del draft del token llevan iat 1742245254 y exp 1773867654 — del 2025-03-17 al 2026-03-18, una credencial de 366 días. La Section 8.2 del draft compañero dice lo contrario: los verifiers SHOULD exigir vidas cortas, "for high-assurance actions, on the order of a few minutes", y MUST rechazar tokens que excedan la política local. Producción le da la razón al draft compañero y agrega una distinción que las specs no hacen. Según la referencia de create-token, la vida por defecto y máxima es de 24 horas cuando el token apunta a un sellerServiceId registrado, y de 5 minutos cuando apunta a un sellerDomainOrUrl crudo. Mínimo, 10 segundos.
Esa asimetría es el instinto correcto. Un token emitido para una contraparte que nunca viste debería ser una credencial corta y de un solo propósito; un token para un servicio con el que ya tienes relación puede durar una jornada. Es la misma división entre walk-up y registrado que dibujamos en el árbol de decisión Bearer contra x402, llegando desde el otro lado.
La historia de federación también es aspiracional por ahora. Los drafts describen un ecosistema abierto de issuers; producción tiene uno. El issuer documentado de producción es https://app.skyfire.xyz, su JWKS vive en /.well-known/jwks.json, y cuando lo bajamos había exactamente una clave en el set — kid BEDD-0, una clave EC P-256 con alg ES256. Los tokens se crean con POST /api/v1/tokens contra la API de Skyfire usando una API key de Skyfire. Un issuer, una clave de firma, un control plane.
Y después los status codes, que te dicen qué entiende el protocolo por un desafío de pago:
// Skyfire, "Handling Missing or Invalid Tokens"
Token ausente -> 403 Forbidden
Token inválido -> 401 Unauthorized
Balance insuficiente -> 402 Payment Required
Un agente que llega sin token recibe un 403 cuyo cuerpo es una frase en inglés diciéndole que vaya a crearse una cuenta. No hay desafío legible por máquina, no hay precio publicado, no hay lista de schemes aceptados. El 402 queda reservado para el caso estrecho de un token que existe pero está subfinanciado. Es el inverso de x402, donde el 402 es el mecanismo de descubrimiento y el cuerpo de la respuesta lleva payment requirements estructurados que un cliente puede satisfacer sin que un humano visite jamás un dashboard.
Bearer, con una salida de emergencia
El documento más honesto de la familia es la sección de seguridad de Using KYAPay Tokens, y merece cita antes que paráfrasis.
Sobre replay: "As deployed today, KYAPay accepts this bounded in-window risk and mitigates rather than eliminates it". Los verifiers MUST validar aud para que un token emitido para un target no pueda presentarse a otro, MUST mantener vidas cortas y SHOULD usar jti para detectar replay contra el mismo receptor.
Sobre el receptor malicioso: "A Target that legitimately receives a bearer token can, within the token's window, reuse it to act elsewhere on the agent's behalf". El audience binding limita el radio de daño; solo la proof of possession lo cierra.
La salida de emergencia es cnf. Cuando un token lleva una confirmation key según el RFC 7800, el verifier MUST verificar además una firma por request sobre esa clave, usando HTTP Message Signatures — RFC 9421, la misma primitiva que recorrimos en el post de Web Bot Auth. El draft prefiere explícitamente firmar el request en sí antes que una prueba desprendida estilo DPoP, porque mantiene una sola firma que cubre el request y el binding de la clave, y permite a un intermediario cachear la clave y luego hacer solo una verificación rápida por request.
Esa es la arquitectura correcta. También es opcional, y producción no parece exigirla.
La Section 8.6 dice después la parte incómoda en voz alta: "A token is only as trustworthy as its issuer". Establecer esa confianza a escala es "an open problem analogous to the Certificate Authority model (audits, a maintained issuer list, a removal mechanism, and possibly transparency logs); this document does not define such a framework, and deployments should not assume one exists". Hasta que exista, la confianza se configura fuera de banda.
Ahí es donde entra Experian Agent Trust, anunciado el 2026-04-30: un Human-to-Agent Binding que enlaza a un consumidor verificado, su dispositivo y el agente que actúa por él, más un Agent Registry que puntúa agentes en el tiempo, desarrollado con Visa, Cloudflare y Skyfire nombrados como partners del ecosistema. El buró se está ofreciendo como CA. Cubrimos la mitad del merchant edge de esa misma alianza en el deep dive del Trusted Agent Protocol de Visa.
La palabra que nunca aparece
Buscamos x402 en los dos drafts principales. Cero ocurrencias, en ninguno de los dos.
Sí referencian MCP y A2A repetidamente — agent-to-tool y agent-to-agent aparecen como transportes de primera clase para el token, incluido un canal MCP por stdio. El ejemplo trabajado del draft de token exchange apunta a resource=https://mcp.acme.example/, canjeando un token KYAPay por un access token de OAuth con scope usando el JWT bearer grant del RFC 7523. Notablemente descarta el token exchange del RFC 8693, con el argumento explícito de que el token KYAPay ya lleva todo lo que aportarían un subject_token y un actor_token separados.
Así que la capa de identidad se está diseñando contra los protocolos de agentes y contra los rieles de tarjeta, con las stablecoins presentes como un string de type de settlement y ausentes como arquitectura. Mientras tanto x402 se diseña como ejecución de pago con la identidad reducida a una dirección de wallet. Dos mitades del mismo problema, construidas por gente distinta, todavía sin unir.
Qué significa para LLM4Agents
Operamos un gateway compatible con OpenAI donde los agentes pagan por request en stablecoins. KYAPay nos toca de tres formas distintas, y tiran en direcciones distintas.
La identidad se está volviendo un header separado del pago
Hoy un payload de pago x402 prueba que una wallet controla fondos y nada más. KYAPay dice que el seller además quiere saber qué humano está detrás de esa wallet, cómo fue verificado y si pasó screening de sanciones. Para un gateway de modelos eso no es hipotético: controles de exportación, listas de sanciones y disponibilidad de modelos por jurisdicción son restricciones reales sobre quién puede llamar a qué modelo.
La lección de diseño es la separación, no el vendor. Las afirmaciones de identidad van en su propio header, con su propia vida útil y sus propios anclajes de confianza. La autorización de pago va en el header de pago. Todo lo que las fusione obliga a cada seller que necesita una sola a aceptar las dos.
Un gateway es exactamente el intermediario que describen estos drafts
Using KYAPay Tokens está escrito para bot managers, sistemas de fraude, defensas contra account takeover y plataformas CIAM — componentes que se sientan delante de un origin y deciden si pasan el request. Un gateway de modelos ocupa estructuralmente la misma posición: terminamos el request del agente, decidimos si lo servimos y reenviamos al proveedor upstream.
Eso significa que el lado de consumo de esta spec es directamente implementable para nosotros: parsear KYAPay-Token, validar contra una allow-list de issuers configurada, verificar aud y env, imponer un techo de vida útil propio sin importar lo que declare el token, y tratar un hid validado como un atributo del request — nunca como una decisión de autorización por sí solo.
El default de PII es un pasivo que no deberíamos heredar
Un token cuyo contenido mínimo es una dirección de email y una IP de origen, sin selective disclosure, llegando en cada request, es un problema de protección de datos en la UE antes que un problema de ingeniería. Nuestra posición es que un gateway debe guardar la menor cantidad posible de datos del principal.
En concreto: si aceptamos estos tokens, deberíamos validarlos, extraer los hechos booleanos que necesitamos (verificado, jurisdicción, screening aprobado) y descartar los claims crudos en lugar de persistirlos. El header explícitamente MUST NOT loguearse. Esa instrucción debería sobrevivir al contacto con nuestro stack de observabilidad.
Cómo mantenerse en la frontera
Cuatro pasos, en el orden en que creemos que deben darse.
Primero, aceptar la identidad como entrada opcional, no como requisito. Construir un verifier que lea KYAPay-Token si está presente, lo valide contra una allow-list explícita de issuers con un tope local duro de vida útil y adjunte el resultado al contexto del request. Un agente que no presenta nada sigue funcionando igual que hoy, pagando con x402. Un agente que presenta un token validado se vuelve elegible para cosas que una dirección de wallet sola no desbloquea: rate limits más altos, modelos restringidos, facturación por invoice. La identidad compra capacidad; no controla el acceso.
Segundo, implementar proof of possession antes de que alguien la pida. Si aceptamos una credencial bearer que carga la identidad de un humano, el riesgo de replay dentro de la ventana es nuestro para explicar. Exigir cnf más una firma de request RFC 9421 para cualquier token que desbloquee capacidad elevada cuesta una verificación de firma por request y elimina toda la clase de reuso por receptor malicioso. Ya necesitamos manejo de RFC 9421 para Web Bot Auth; este es el mismo camino de código con otra fuente de clave.
Tercero, publicar el mapeo entre nivel de aseguramiento de identidad y lo que desbloquea. Los vocabularios ivm, amr y aml son útiles justamente porque son legibles por máquina. Una tabla pública que diga qué combinaciones califican a un agente para qué nivel convierte una conversación vaga sobre confianza en una spec contra la que un desarrollador puede programar. Esa tabla es además el artefacto que pide un revisor de compliance, y no cuesta nada escribirla antes de que existan los tokens.
Cuarto, empujar la unión entre identidad y settlement. El hueco es concreto y chico: un bloque sti de KYAPay con stp: "coin" no tiene identificador de red, y un payload de pago x402 no tiene principal. Un profile que lleve una red CAIP-2 en el bloque de settlement, y una extensión x402 que lleve una referencia a un token de identidad, dejarían que un solo request responda ambas preguntas sin que ninguna spec se trague a la otra. Seguimos la capa de extensiones de x402 justamente para este tipo de costura — ver la auditoría de extensiones — y esta es la costura contra la que vale la pena presentar la propuesta.
La apuesta de fondo en KYAPay es que los sellers no van a atender agentes autónomos hasta saber quién responde. Esa apuesta parece correcta. Si la respuesta es un JWT bearer con un buró de crédito respaldando al issuer, o algo con selective disclosure y atestación on-chain, sigue abierto. Lo que no sigue abierto es que un pago solo va a dejar de ser una presentación suficiente.
Paga por request, sin dashboard
Un gateway compatible con OpenAI donde el agente liquida en stablecoins y la identidad es una capacidad, no una barrera.
Registrar un agente