Registro de cambios¶
Traducción informativa de la versión en inglés, actualizada el 02/11/2026. Cada entrada, también las de
[Unreleased], describe un cambio en su momento y no garantiza el comportamiento actual; las versiones publicadas son registro histórico con fecha. Si difieren, prevalece la versión en inglés.
Versiones publicadas (26/10/2026). Están publicados npm (13 paquetes, incluido @dcp-ai/sdk 2.2.0), Python dcp-ai 2.10.0, Go v2.11.0 y Rust dcp-ai 2.11.0, con las correcciones del verificador, la admisión y las API experimentales opt-in; dcp-jcs-v1 sigue siendo el valor normativo predeterminado. Véase Versiones publicadas. Las entradas siguientes conservan su historial de desarrollo; [Unreleased] no significa que estas funciones no estén publicadas. Las seis publicaciones de corrección del 28/10/2026 están en su propia sección.
Todos los cambios relevantes de este proyecto se documentarán en este archivo.
El formato se basa en Keep a Changelog.
Nota informativa (26/10/2026). Cada entrada describe un cambio tal como se hizo, también las entradas sucesivas de
[Unreleased], donde una entrada posterior puede precisar o sustituir a una anterior. Una entrada no garantiza el comportamiento actual ni que una versión de un paquete esté disponible en un registro. Para el comportamiento actual, consulta el README, la documentación, las especificaciones y la referencia de la API.
Preparadas, no publicadas: Go v2.12.0, Rust 2.12.0 y @dcp-ai/wasm 2.1.2¶
Preparadas en el repositorio el 30/10/2026 y no publicadas: no existe la etiqueta sdks/go/v2.12.0, y crates.io y npm no tienen estas versiones. Versiones publicadas y los comandos de instalación siguen nombrando las versiones del registro (Go v2.11.1, Rust dcp-ai 2.11.1, @dcp-ai/wasm 2.1.1). Los cambios se describen en [Unreleased] y en las notas de publicación de Go, Rust y npm.
- Go v2.12.0: el cambio de aceptación de
v2.VerifyPayloadHash(acepta la salida deSignPayloady rechaza un digest sin prefijo; solo comprueba el hash) y el aditivov2.RawAuditEventV2.SDKVersiones2.12.0. - Rust
dcp-ai2.12.0: los aditivosv2::types::RawAuditEventV2yRawAuditEventErrory, con la featurewasm, elwasm_dual_merkle_rootcorregido. La telemetría lee la versión del paquete. @dcp-ai/wasm2.1.2:dualMerkleRootywasm_dual_merkle_rootcalculan la raíz Merkle dual de V2-11; no se añade ninguna API JavaScript.- Los nuevos tests del candidato experimental (gate 7) no cambian ningún valor predeterminado.
dcp-jcs-v1, los perfiles, el formato de transmisión, las dependencias y la criptografía no cambian. Los tres paquetes ya no incluyen los README traducidos, que están eni18n/.
[2026-10-28] Publicaciones de corrección SHA3¶
Publicado el 28/10/2026: @dcp-ai/sdk 2.2.1, @dcp-ai/wasm 2.1.1 y @dcp-ai/cli 2.1.1 en npm, dcp-ai 2.10.1 en PyPI, el módulo Go v2.11.1 (etiqueta sdks/go/v2.11.1) y Rust dcp-ai 2.11.1 en crates.io. Los otros diez paquetes npm no cambian. Véanse Versiones publicadas y las notas de publicación de npm, Python, Go y Rust.
Verificación secundaria SHA3-256¶
- Cambio de aceptación: los campos secundarios SHA3-256 se verifican cuando están presentes (desarrollado el
27/10/2026). Los verificadores V2 de TypeScript, Python, Go y Rust, y la compilación WASM a partir del SDK
de Rust, comprueban ahora
audit_merkle_root_secondary(el árbol SHA3-256 construido como el primario;sha3-256:seguido de 64 ceros para una traza vacía), cadaprev_hash_secondary("GENESIS"en la primera entrada y, después, el SHA3-256 de la entrada anterior tal como figura) y cadaintent_hash_secondary(el SHA3-256 del payload de la intención), como indicanspec/VERIFICATION.mdV2-11 y su errata del 27/10/2026. Los campos ausentes no se exigen, así que un bundle verificado puede no contener una cadena secundaria completa; un campo presente que seanull, no sea una cadena o sea incorrecto se rechaza. La admisión y las rutas del candidato experimental reutilizan estas comprobaciones; no cambia ninguna API, campo de resultado, canonicalización ni el tratamiento dehash_alg. El patrón del esquema deprev_hash_secondaryacepta también"GENESIS", que OpenClaw escribe en la primera entrada. Vectores compartidos:tests/interop/v2/secondary_sha3_vectors.json. Las versiones anteriores no comprueban estos campos. Los adaptadores publicados@dcp-ai/express,@dcp-ai/openclaw,@dcp-ai/anthropic-mcpy@dcp-ai/google-a2a2.1.0 no se volvieron a publicar: dependen de@dcp-ai/sdk^2.2.0y obtienen las comprobaciones secundarias cuando tu lockfile resuelve@dcp-ai/sdk2.2.1 o posterior.
Metadatos de los paquetes¶
@dcp-ai/cli2.1.1 requiere@dcp-ai/sdk^2.2.1, ydcp-ai --versioninforma2.1.1en lugar del obsoleto1.0.0.- La telemetría informa las versiones nuevas: Go
SDKVersion2.11.1; los SDK de TypeScript, Python y Rust leen la versión de su paquete.
[Unreleased]¶
Seguridad¶
- Cambio de aceptación: validación de esquemas V2-2 en el verificador de cada SDK (código fuente de
main, no está en ninguna versión publicada). Los verificadores V2 de los SDK de TypeScript, Python, Go, Rust y WASM (verifySignedBundleV2y sus equivalentes, la API combinada de admisión, que verifica primero, y el verificador experimental del candidato; evaluar la admisión a partir de un resultado anterior no la ejecuta) validan el sobre del bundle firmado y los cuatro payloads contraschemas/v2después de las comprobaciones de política y reloj y antes de comprobar el manifiesto, los hashes de los payloads, las claves y las firmas. Todos los SDK interpretan la misma representación generada a partir de los esquemas (tools/schema-ir/), que la CI regenera y compara. Los bundles inválidos por esquema que antes aceptaban devuelven ahoraverified: false, entre ellos los que tienen miembros desconocidos en objetos cerrados y horas de sesiónnullen el manifiesto. Los errores son entonces diagnósticosschema: <JSON pointer> <keyword>[ <member>](como mucho 16 y un marcador de truncado) sin detalles criptográficos, así que algunos rechazos cambian de redacción: la CI evalúa por el veredicto las ejecuciones de los SDK del catálogo de evidencias (--assessment verdict), y las cadenaserror_containscomprometidas no cambian.date-timemantiene la frontera de ajv-formats 3.0.1 del verificador de referencia, que no es RFC 3339 estricto. No cambian la API pública, los esquemas, los perfiles, las dependencias ni las versiones. En la matriz experimental de la fase 4, los 50 resultados deS09pasan a ser rechazos esperados. Véase SECURITY_COVERAGE.md. VerificationCachede TypeScript (código fuente demain, no publicado;@dcp-ai/sdk2.2.1 mantiene el comportamiento anterior). La clave de la caché sigue siendo el hash del bundle para las llamadas existentes, y uncontextopcional elegido por quien llama separa los resultados con claves internas etiquetadas con las que un hash legacy no puede colisionar.invalidateByAgent, que no eliminaba nada, ahora elimina las entradas guardadas con ese agente en la nueva opciónagentIdsy todas las guardadas sin ella. Reemplazar una entrada ya no deja sus kids antiguos en el índice (con el límite predeterminado el índice crecía sin límite) ni expulsa otra entrada; también se indexan los kids deresult.details.signer_keys;''como hash ya no desactivamaxEntries. Cambios de aceptación: las opciones inválidas (TTLNaN,Infinity, negativos o no numéricos, TTL por encima deNumber.MAX_SAFE_INTEGER, unmaxEntriesque no sea un entero seguro de 1 aNumber.MAX_SAFE_INTEGER) lanzan un error en lugar de desactivar la expiración o el límite;setcopia el resultado ygetdevuelve una copia nueva en cada acierto; una entrada expira exactamente al cumplirse su TTL, medido con el reloj monotónico además del reloj de pared, así que un retroceso del reloj ya no la prolonga; un TTL de 0 nunca se guarda;setvalida sus argumentos (un hueco ensignerKidsoagentIdscuenta comoundefined) y no cambia nada cuando lanza un error. Un acierto sigue sin volver a verificar la evidencia ni la confianza y no es admisión, y nada se invalida automáticamente. Véase API_REFERENCE.md.- Cambio de aceptación: las firmas Ed25519 con S >= L ahora se rechazan en el SDK de TypeScript
(
Ed25519Provider.verify, y por tanto toda firma V2 y compuesta,@dcp-ai/expressy@dcp-ai/openclaw; yverifyObject, y por tantoverifySignedBundleV1) y en el verificador de referencia (tools/crypto.js, y por tantolib/verify.jsV1,bin/dcp.js, la conformidad y el gossip del registro de transparencia). RFC 8032 §5.1.7 exige 0 <= S < L, y TweetNaCl no lo comprueba, así que aceptaba S + k·L, otra codificación de una firma válida (§8.4, maleabilidad). Python, Go y Rust ya las rechazaban. TweetNaCl sigue siendo el verificador: el rango solo se comprueba en una entrada que acepta, así que sus errores por tipos, tamaños y base64 incorrectos no cambian, como tampoco la decodificación de claves y de R ni la aceptación de claves débiles. Los firmantes producen S < L, así que ninguna firma generada de nuevo cambia de veredicto; una firma almacenada con S >= L que una versión anterior aceptó ya no se verifica. El inventario del repositorio solo encontró firmas así en el corpus negativo de revisión, intencionado; no cubre evidencias almacenadas externas. Las evidencias verificadas antes de este cambio no se vuelven a firmar automáticamente: consérvalas como históricas, con su hash y la versión que las verificó, o fírmalas de nuevo. - El mismo cambio de aceptación en
dcp-ai verify(@dcp-ai/cli) y en el playground, que llamaban directamente a TweetNaCl.dcp-ai verifycomprueba S < L en su propia comprobación V1 designature.sig_b64, y los bundles V2 la reciben de@dcp-ai/sdk, cuyo verificador usa ahora (véase Cambiado). La ruta históricasignature.classical.valuetambién tuvo la comprobación cuando se añadió; esa ruta se ha eliminado después y su formato se rechaza. El playground la comprueba enverifyDetached. La comprobación es la del SDK, hecha solo sobre entradas que TweetNaCl acepta, así que sus errores no cambian. - Los cuatro verificadores V2 discrepaban ante codificaciones no canónicas de una firma válida: con basura
tras el relleno, relleno de más, un salto de línea dentro, bits finales activados o sin relleno,
TypeScript aceptaba todas las variantes, Python y Go algunas, Rust ninguna. Todo
sig_b64debe ser ahora base64 canónico (spec/VERIFICATION.mdV2-5), de modo que una firma tiene una única codificación y todos los SDK decodifican los mismos bytes; lo cubren 13 nuevos vectoresmust_reject. - Los verificadores V2 de los cuatro SDK aceptaban bundles que las firmas por sí solas permitían, pero el
protocolo no. Lo informó una revisión externa y lo confirmó la nuestra; el verificador de referencia
lib/verify.jsejecuta el verificador corregido a través del SDK del workspace hasta que 2.2.0 esté en npm (véase Cambiado). - Cualquier agente podía atribuirse cualquier principal. La firma del bundle se verificaba con las
claves del pasaporte además de con las
binding_keysdel RPR, y el agente firma su propio pasaporte. Emparejar un RPR publicado y firmado con un pasaporte y claves nuevos daba un bundle verificado sin la clave privada del principal. La firma del bundle ahora solo se verifica con lasbinding_keysdel RPR (spec/VERIFICATION.mdV2-6), de modo que lo que vincula al agente es la firma del principal sobre el manifiesto, que se compromete conpassport_hash. - Los kids no se comprobaban frente a sus claves. Una lista de claves podía atribuirse el kid de otra parte para una clave propia. Todo kid que verifica una firma debe derivarse ahora de su clave, y la clave debe estar en base64 canónico para que todos los SDK decodifiquen los mismos bytes (V2-13).
- Se verificaban bundles firmados pero incoherentes. Un pasaporte revocado o retirado, un RPR caducado,
una decisión de política para otra intención, y entradas de auditoría de otra intención, agente o
principal, con otro
intent_hasho sin registrar la decisión de política. Las comprobaciones V1 son ahora V2-14: el pasaporte estáactiveodeclining, el RPR no ha caducado y todos los artefactos nombran el mismo principal, agente e intención. Una acción escalada o bloqueada sigue siendo una evidencia válida. - El límite de duración de la sesión leía un campo sin firmar.
max_session_duration_secondsse medía desdesignature.created_at, fuera del manifiesto firmado. El manifiesto tiene un nuevosession_issued_at, quesignBundleV2ysign_bundle_v2escriben junto consession_expires_at; el máximo se mide desde él y, con un máximo, un bundle sin él se rechaza (V2-4). No debe adelantarse más de 30 segundos al reloj del verificador, y un máximo también rechaza un bundle sinsession_expires_at. Las marcas de tiempo se comparan con precisión de milisegundos en todos los SDK. Los bundles firmados antes de este cambio se verifican como antes con una política sin duración máxima de sesión. - El bloque de firma sin firmar podía contradecir a la firma.
signature.signer.iddebe ser ahora elhuman_iddel RPR,signature.signer.kidslos kids de la firma compuesta,manifest_hashel hash del manifiesto yhash_alguno de los dos que permite el esquema.signBundleV2ysign_bundle_v2rechazan un id de firmante distinto delhuman_iddel RPR;SignBundleV2de Go ysign_bundle_v2de Rust ya lo toman del RPR. - Los checkpoints PQ no se verificaban. Un checkpoint con una raíz de Merkle o una firma PQ cambiadas, o uno ausente que el manifiesto enumera, se verificaba. El bundle debe llevar ahora exactamente los checkpoints enumerados, cada uno sobre sus entradas de auditoría y firmado por el agente con ambos algoritmos (V2-12).
- Los verificadores de Go y Rust aceptaban un artefacto o una entrada de auditoría sin
session_nonce; ahora todos deben llevar el del manifiesto, como en TypeScript y Python. - Nuevos vectores en
tests/interop/v2/bundle_vectors.json, cada uno firmado sobre un único defecto: 35 casosmust_reject_bundles, 4 mutacionesmust_rejectdel bloque de firma sin firmar y 8 controlesmust_accept_bundles. Un caso puede llevarverifier_policy_overrides. lib/verify.jsfija una clave dedcp verify-bundleentre lasbinding_keysdel RPR, y toda entrada con el kid firmante debe llevar la clave fijada, de modo que la fijación también se mantiene con el SDK publicado 2.1.1, que busca primero al firmante en el pasaporte.@dcp-ai/express(dcpVerify),@dcp-ai/openclaw(dcp_verify_bundle) y el middleware de FastAPI de Python (dcp_ai.fastapi.DCPVerifyMiddleware) aceptaban bundles firmados V2 sin verificar ninguna firma: solo comprobaban la estructura del bundle y los nonces de sesión (el middleware de FastAPI además fijabacomposite_sig_valida partir del campobinding), así que un bundle sin firmar o falsificado, o uno cuya intención se cambió después de firmarlo, se daba por verificado. Los tres verifican ahora todo bundle V2 con el verificador del SDK (verifySignedBundleV2de@dcp-ai/sdk,verify_signed_bundle_v2dedcp_ai.v2) y un registro Ed25519 / ML-DSA-65 / SLH-DSA-192f, con la política configurada sobreDEFAULT_VERIFIER_POLICY(verifierPolicy;verifier_policy, un argumento nuevo deDCPVerifyMiddleware; la política del operador de OpenClaw que se describe abajo).requireHybrideleva ahybrid_requiredtodo nivel de riesgo que esté por debajo (unpq_onlydel operador sigue siendopq_only), y undefault_modesinrisk_overridesse aplica a todos los niveles. Se devuelven los errores y advertencias del verificador, ycomposite_sig_validsale de él. Hasta que@dcp-ai/sdk2.2.0 esté en npm, ambos paquetes de TypeScript dependen del SDK del workspace ("@dcp-ai/sdk": "file:../../sdks/typescript"), cuyo verificador tiene las correcciones anteriores; la CI lo construye antes de probarlos, y los tres superan todos los casos detests/interop/v2/bundle_vectors.jsoncon susverifier_policy_overrides. Para publicarlos hay que subir antes la dependencia a^2.2.0:publish-integrations.ymlrechaza ahora un paquete que dependa de una ruta local.- Esos mismos tres verificadores enviaban al verificador V1 todo bundle que no se detectaba como V2, fuera cual
fuera la política del verificador: un bundle V1 (una firma Ed25519, ninguna poscuántica) pasaba
requireHybrido una políticahybrid_required, y un bundle de versión indetectable se probaba como V1. Ahora un bundle V1 solo se acepta donde VERIFICATION.md V2-10 aceptaría un bundle solo clásico:allow_v1_bundleses verdadero,requireHybridestá desactivado, y el más estricto entre el modo de la política para elrisk_tierdel pasaporte V1 y el modo del nivel de seguridad de la intención y la decisión de política V1 (risk_scoreV1, de 0 a 1, multiplicado por 1000; si falta, cuenta comoelevated) esclassical_onlyohybrid_preferred. ConDEFAULT_VERIFIER_POLICY, el bundle de conformidad V1 (unsend_emailde nivel estándar) pasa y el mismo bundle con una intencióninitiate_paymentse rechaza. Un bundle de versión indetectable se rechaza. @dcp-ai/openclaw:dcp_verify_bundletomaba su política de verificación del parámetro de herramientaverifier_policy, que controla el modelo que llama, y devolvía elagent_idy elhuman_iddel bundle sin verificar junto con un resultado fallido. La política es ahora la del operador (register(api, { verifierPolicy })o el nuevoconfigureVerifier, sobreDEFAULT_VERIFIER_POLICY); el parámetro solo puede hacerla más estricta (un modo más estricto por nivel, donde combinarpq_onlycon cualquier otro modo dahybrid_requiredpara que el modelo no pueda prescindir de la firma clásica que exige el operador,require_*solo se activa,allow_v1_bundlessolo se desactiva, cualquier otro campo es un error). El resultado repiteverification_mode, y nombra al agente, al principal y la sesión solo cuando el bundle se verificó.@dcp-ai/express: la caché de veredictos usaba como clavesignature.composite_sigy la compartían todos los middlewares, así que un bundle distinto que llevara una firma en caché, o un middleware con una política más estricta, reutilizaba un veredicto de «verificado»; además crecía sin límite y conservaba un veredicto todo el TTL después de que la sesión o una clave hubieran caducado o el agente hubiera sido revocado. Cada middleware tiene ahora su propia caché, con clave en el SHA-256 de la forma canónica del bundle (canonicalizeV2; un bundle sin ella, como uno que contiene1e999, queJSON.stringifyimprimía comonull, se verifica pero no se almacena en caché), con un máximo decacheMaxEntries(nuevo, 1000 de forma predeterminada) bundles, expulsando primero el usado menos recientemente; una entrada caduca en el primero de estos momentos: el TTL, elsession_expires_atdel manifiesto, elexpires_atdel RPR y elexpires_atde cada clave del pasaporte y de cada clave de vinculación del RPR. Un bundle muy anidado ya no da un 500.@dcp-ai/express:checkRevocationsolo preguntaba por el agente y por las claves de su pasaporte, y fallaba en abierto sin avisar. Ahora pregunta también por cada clave de vinculación del RPR y por los kids clásico y PQ de la firma del bundle, en cada petición, incluidos los aciertos de caché. Una consulta que no da respuesta (servicio inalcanzable, una respuesta que no es 2xx, un cuerpo que no es JSON o no tienerevoked) sigue dejando pasar la petición de forma predeterminada; el nuevorevocationFailClosed: truela rechaza.@dcp-ai/express: una opción o un campo deverifierPolicydesconocidos (defaultMode) y un valor del tipo incorrecto (allow_v1_bundles: "false") se ignoraban, y quedaba en vigor el valor predeterminado. Ahora lanzan una excepción al crear el middleware.dcp_ai.fastapi: una cabeceraX-DCP-Bundlecon un entero que supera el límite de 4300 dígitos de Python o demasiado anidada, y un bundle demasiado profundo para el verificador, daban un 500; ahora reciben un 403. Unpq_checkpointsque no es una lista se rechaza (se leía como ningún checkpoint).verifier_policy={"risk_overrides": None}conserva ahora los niveles predeterminados en lugar de aplicardefault_modea todos, y un nivel de riesgo desconocido lanza una excepción. Se documenta quedcp_versionno se hace cumplir.- Los tres README dicen ahora qué significa «verificado»: el bundle es coherente internamente y está firmado
por las claves que él mismo enumera, lo que no prueba quién es el principal salvo que el verificador fije
las claves del principal (ninguno de los middlewares puede hacerlo todavía), y
require_audience_bindingcon unverifier_idlimita la reproducción al verificador previsto. - Los verificadores de Go y Rust aceptaban una entrada de auditoría (o un checkpoint PQ) cuyo
session_noncedifería del del manifiesto, aunque el manifiesto se comprometía con él (spec/VERIFICATION.mdV2-4: todo artefacto y toda entrada de auditoría que lleva un nonce comparte el del manifiesto). Ambos comprueban ahora la pista y los checkpoints como lo hacen TypeScript y Python, con el errorSession nonce mismatch in audit_entries[i]. Vector compartidoaudit_entry_session_nonce_differs. - El verificador de Rust no comprobaba en absoluto
manifest.canonicalization_profile, y el de Go ignoraba un valor que no fuera una cadena, así que un bundle podía declarar un perfil que ningún verificador implementa (spec/CANONICALIZATION_PROFILE.md§ 4, V2-3). Ambos rechazan ahora cualquier valor distinto de"dcp-jcs-v1", como hacen TypeScript y Python. Vectores compartidoscanonicalization_profile_unknownycanonicalization_profile_not_a_string.
Añadido¶
- Una herramienta de línea base de rendimiento para el kit de conformidad externa,
tools/conformance-perf.js(contratodcp-ai-perf/1): el verificador V2 normativo de los cinco SDK de este checkout sobre seis cargas versionadas (un bundle híbrido, una firma Ed25519 y una ML-DSA-65 inválidas, y tres casos secundarios SHA3-256), con los mismos bytes de entrada, la misma política y el mismo instante de verificación. Un hijo en proceso por SDK a través de su entrada pública, con calentamiento y medida en round-robin y cada llamada comprobada; se conservan todas las muestras, con estadísticas por rango más cercano. Construye lo que mide y registra fuentes, manifests y locks, hashes de artefactos y versiones de herramientas por SDK (--no-buildrechaza cualquier diferencia o hueco). Sin umbrales de velocidad, ranking ni porcentajes atribuidos; los resultados se quedan en local. La CI ejecuta sus self-tests y un humo de los cinco SDK en el job nativo. RawAuditEventV2en los SDK de Rust (dcp_ai::v2::types) y Go (v2): una entrada de auditoría V2 recibida, conservada tal como se decodificó, con cada miembro a cualquier profundidad, su presencia y sunull, también los desconocidos, de modo que, siempre que el canonicalizador dcp-jcs-v1 la acepte, sus bytes canónicos, suprev_hashy su hoja de Merkle siguen siendo los de la entrada. Rust guarda elserde_json::Valuedecodificado (RawAuditEventErrorpara todo lo que no es un objeto); Go guarda los bytes literales, copiados al entrar y al salir, y rechaza todo lo que no sea exactamente un objeto JSON. Ninguno repara ni vuelve a firmar: la evidencia inválida sigue siendo inválida, por el mismo motivo.AuditEventV2yAuditEvidenceV2no cambian y ahora se documentan como con pérdidas para reenviar (unnullexplícito, un miembro desconocido, unpq_checkpoint_refausente que pasa a sernull). Los nuevos vectores compartidos,tests/interop/v2/audit_entry_roundtrip_vectors.json, dan 14 entradas con su texto dcp-jcs-v1 escrito a mano, que reproducen los canonicalizadores de TypeScript, Python, Go y Rust, y un bundle firmado cuyas mutaciones el tipo nuevo mantiene rechazadas con los mismos errores. El builder de bundles de Go, los tipos V1 y el modeloAuditEventV2de Python no cambian.--assessment verdictpara el catálogo de evidencia (contrato/2) del kit de conformidad externa: los 146 rechazos que llevan unerror_containsse juzgan solo porverified: false, con todas las demás reglas, controles y códigos de salida sin cambios, para que una implementación con veredictos correctos y mensajes propios pueda demostrarlo. La redacción se informa como diagnóstico:matched,differs,no_message, onot_evaluatedcon su motivo. Cada uno de esos casos indica su paso despec/VERIFICATION.md, y antes de la ejecución el runner comprueba esos anclajes y que todo control sea un positivo simple. El informe declaradcp-ai-conformance-assessment/1, aparte del contrato del wire/2, y conserva la prosa del catálogo encatalog_claimycatalog_limits. Sin la opción, o con--assessment legacy, el juicio y el informe no cambian, y los catálogos/1y/3la rechazan. El catálogo, los fixtures y los wrappers no cambian; la CI ejecuta la evaluación con los cinco wrappers, todos obligatorios.- Kit de conformidad externa: un catálogo de admisión opt-in,
--catalog admission(contratodcp-ai-conformance-external/3,tools/conformance/external-admission-catalog.json, 140 casos: los 65 vectores de resultado deadmission_vectors.json, 71 casos de configuración y 4 casos compuestos a partir de las entradas de los vectores). Los wrappers de TypeScript, Python, Go y Rust responden mediante la función combinada de admisión y los cargadores de su SDK, con elnowde la petición. El juez comprueba los tipos de los campos, las reglas de spec/profiles/admission (action_authorizedesfalse;admittedexactamente cuando no se añadió ningún código), los campos exactos, el orden de los códigos crudos por fase y su repetición; un negativo cuenta solo si sus controles pasaron. La respuesta es una proyección: la verificación incluida no se juzga. La compilación WASM no tiene API de admisión y no se emula. La CI ejecuta las autopruebas del juez (60), las pruebas de los wrappers (26, con los cuatro wrappers obligatorios) y los 140 casos con cada uno de los cuatro wrappers. El fake de las autopruebas de evidencia llega ahora al wrapper de TypeScript con un spawn asíncrono, con plazo y comprobación de su salida: un spawn síncrono con la petición de 9 MB se observó esperando hasta el plazo./1y/2no cambian. - Candidato de canonicalización: la puerta 7 medida en el lado de
dcp-jcs-v1. El nuevo fixture públicotests/interop/candidate/gate7-v1-negatives.jsoninyecta un defecto de cada una de las 8 clases de unidad, en 18 posiciones de bytes, en dos bases congeladas dedcp-jcs-v1: 38 casos, con las 2 bases incluidas como controles. Registra una expectativa razonada para 10 configuraciones de cinco implementaciones: los SDK de TypeScript, Python, Go y Rust, y el paquete WASM alojado en Node. Nuevas pruebas en cada una ejecutan las 380 comparaciones: el verificador experimental debe dar el resultado de la ruta actual de la misma implementación con los mismos bytes, reloj y política, y ambos deben coincidir con la expectativa. Tres casos con sobre (30 comprobaciones) quedan como una frontera aparte, sin afirmar equivalencia. Un comprobador estricto con la biblioteca estándar y sus controles negativos se ejecutan en el job de conformidad de CI. El registro marca la puerta 7 como cumplida, sobre muestras. Solo pruebas y documentación: ningún cambio en API de SDK, perfiles, valores predeterminados nidcp-jcs-v1; el candidato sigue siendo experimental y de activación explícita, y no se publica nada. Véase docs/proposals/canonicalization-candidate/README.md. - Catálogo de evidencia para el runner de conformidad externo: Merkle y verificación de firmas.
--catalog evidenceejecuta un catálogo opcional (tools/conformance/external-evidence-catalog.json, contratodcp-ai-conformance-external/2, 241 casos) sobre fixtures públicos versionados: raíces Merkle duales exactas para 1, 2, 3 y 5 hojas, verificación de firmas compuestas y solo clásicas con los ataques versionados y alteraciones deterministas, verificación normativa V2 de bundles firmados debundle_vectors.jsoncondefault_verifier_policyy los overrides versionados de cada caso, y los vectores secundarios SHA3-256, todo con unverification_timefijado. Un caso negativo solo cuenta si su control pasó;verified: truecon errores,erroryunsupportednunca son un pase. Los cinco wrappers aceptan ambos contratos; el catálogo por defecto, el contrato/1y sus 38 casos no cambian. CI ejecuta los 241 casos y los self-tests con los cinco wrappers. Un pase no es admisión, confianza ni autorización. Aparte de la corrección dewasm_dual_merkle_rooten Corregido, no hay cambios en los SDK; no se publica ningún paquete. Véase tools/conformance/README.md. - Wrappers de Go, Rust y WASM para el runner de conformidad externo.
tools/conformance/wrappers/build.mjscompila desde este checkout wrappers para el SDK de Go, el SDK de Rust y la compilación WASM del SDK de Rust (-mod=readonly,--locked; no cambia ningún manifest ni lockfile del SDK), y cada uno ejecuta los mismos 38 casos con la API real, con una sola llamada al canonicalizador y clasificando como rechazo solo el documentado. El wrapper WASM ejecuta el.wasmreal alojado en Node, no en un navegador, y trata de forma conservadora la colisión del{"error": …}que la exportación devuelve dentro del resultado. La CI compila los tres y los ejecuta como jobs obligatorios. No se publica ningún paquete. Véase tools/conformance/README.md. - Runner de conformidad externo (
tools/conformance-external.js). Ejecuta un catálogo aparte y versionado (tools/conformance/external-catalog.json, contratodcp-ai-conformance-external/1) contra cualquier implementación indicada como argv, mediante una petición JSON por stdin y una respuesta JSON por stdout: 30 casos de canonicalizacióndcp-jcs-v1(incluidos los 19edge_casesdel repositorio, 3 de los cuales deben rechazarse) y 8 digests SHA-256 / SHA3-256, todos con expectativas ya comprometidas. Incluye wrappers para@dcp-ai/sdky paradcp_aide Python, y la CI ejecuta los dos. El comando, el catálogo y el informe de conformidad existentes no cambian. Un pase muestra que pasan estos 38 casos, nada más. Véase tools/conformance/README.md. cancel_on_disconnecten FastAPI funciona con cualquier orden de middlewares. La respuesta ASGI a un cliente que se fue termina ahora con un499vacío («client closed request»: no registrado, no es un estado de DCP), conContent-Length: 0yCache-Control: no-store, en lugar de sin ningún mensaje.- Son dos mensajes ASGI de la respuesta de DCP, enviados solo después de recibir
http.disconnect, nunca para un cliente que sigue conectado. UnBaseHTTPMiddlewareexterno a DCP necesitahttp.response.start, y antes fallaba con «No response returned.»; ahora termina con normalidad y registra un 499. - Lo que llega a la red depende del servidor: uvicorn (medido, 0.54 con h11) no escribe ni registra
nada para una conexión que ya no existe; con ASGI, otro servidor puede lanzar una subclase de
OSError. - El
request.state.dcp_agentde la admisión se publica ahora solo cuando se sabe que el cliente sigue ahí, de modo que un cliente que se fue no deja ningún contexto admitido. Un valor que fija el propio código del operador no se toca. - Un cliente que se va a mitad de su cuerpo recibe el mismo final (en lugar de nada).
- Los errores reales de cualquier capa se siguen propagando. No cambia nada más: las opciones, los valores
predeterminados, el presupuesto del cuerpo, el supervisor y
DenyListWorkers. - Desconexión del cliente, opt-in:
cancelOnDisconnecten@dcp-ai/express,cancel_on_disconnect=Trueendcp_ai.fastapi.DCPVerifyMiddleware. - Qué hace. Cuando el cliente se va realmente durante la admisión, se cancela la invocación supervisada
de la fuente de esa petición, y solo de esa. Su hueco sigue ocupado hasta que la fuente termina realmente.
No se hace ni se envía nada más para la petición: ni el manejador, ni
next, ni escritura en caché, nionFailure, ni respuesta. - Express. Irse significa que la respuesta se cierra antes de enviarse, o que la petición se cierra con
el cuerpo incompleto; el propio
closede una petición sana no lo es.- Necesita que todas las funciones de fuente estén supervisadas, ningún
checkRevocationy un supervisor que admita la cancelación: la nueva capacidadrunWithSignalde los supervisores de esta versión (ESM y CJS, ambos paquetes). run, sus cuatro argumentos yversion: 1no cambian, y un supervisor de un paquete anterior sigue funcionando sin la opción.- La decisión de una invocación supervisada se toma ahora una sola vez, antes de abortar su señal: un
listener de aborto que vuelve a entrar en
close()no cambia nada. - Nuevo resultado
cancelled, contador (stats().cancelled, opcional en el tipo) y motivo de telemetría.
- Necesita que todas las funciones de fuente estén supervisadas, ningún
- FastAPI. Necesita
max_body_bytes, todos los callables de fuente supervisados y un callabletenantsíncrono.- El middleware vigila un único
receive()pendiente durante la admisión y se lo pasa al siguiente lector: nada se lee dos veces ni se pierde, y elhttp.disconnectque se envía tras una respuesta completa no se toma como que el cliente se fue. - Con la opción, un cliente que se va a mitad de su cuerpo no recibe nada en lugar del 400.
- En su primera forma, el middleware tenía que envolver las demás capas
BaseHTTPMiddlewarede la aplicación; ese requisito ha desaparecido (véase la entrada anterior). SourceSupervisoryDenyListWorkersno cambian. OpenClaw no tiene esa opción.
- El middleware vigila un único
- Sin la opción no cambia nada.
dcp_ai.v2.DenyListWorkersydeny_list_revocation_source(..., workers=...), opt-in: un presupuesto de hilos de descarga reales para la función auxiliar de lista de denegación de Python (max_workersde 1 a 64,max_workers_per_tenantopcional, por objeto). Cada descarga se ejecuta en un hilo no daemon propio, iniciado después de reservar su hueco y que solo lo libera cuando la descarga ha terminado realmente, también tras un tiempo de espera agotado, una llamada cancelada, el plazo del supervisor de C3 o un bucle de eventos cerrado; una llamada que no encuentra hueco libre recibeREVOCATION_UNAVAILABLEde inmediato, sin cola ni conexión. La descarga, las comprobaciones del cuerpo, el análisis y los errores son los mismos que sin la opción; un resultado que llega o se usa después detimeoutesREVOCATION_UNAVAILABLE.close()rechaza nuevas descargas sin esperar a las que están en curso ni detenerlas;stats()no incluye URL, tenant ni cabecera. En un proceso bifurcado, rechaza sin tocar el bloqueo del padre. La salida del proceso espera a una descarga que siga en curso, como ocurre conasyncio.to_thread; nada detiene DNS, TLS ni urllib. Sinworkersno cambia nada. Se corrige la documentación detimeout: limita cada operación de socket y la lectura del cuerpo, no la resolución DNS ni una línea de estado que llega poco a poco.- Revocación bajo el supervisor de fuentes (C3), opt-in, en
@dcp-ai/express,@dcp-ai/openclawydcp_ai.fastapi.DCPVerifyMiddleware:supervise: { revocation: true }/supervise={"revocation": True}, rechazado hasta ahora, ejecuta la fuente de revocación de strict-v1 bajo el supervisor conrevocationTimeoutMs/revocation_timeoutcomo plazo, que queda limitado al rango de los temporizadores (un entero de 1 a 2147483647 ms; hasta 2147483.647 s).superviseacepta el plazo de confianza,revocationo ambos, de modo que la revocación puede supervisarse con un conjunto de confianza estático; las llamadas de confianza y de revocación comparten la capacidad del supervisor, y un hueco solo se libera cuando su fuente termina realmente. Las respuestas no cambian: un tiempo agotado, la falta de hueco libre o un supervisor cerrado dan elREVOCATION_UNAVAILABLEque da una fuente que falla (403 en Express y FastAPI), y un errorREVOCATION_INVALIDsolo cuenta antes del plazo. Las fuentes de TypeScript reciben(subjects, { tenant, signal }),denyListRevocationSourcetambién aborta su fetch con esa señal, y la telemetría informasource: 'revocation';configureVerifierde OpenClaw se niega a sustituir un supervisor con llamadas en curso, sea cual sea la fuente que supervise. FastAPI necesita unarevocation_sourceasíncrona. Cambio de comportamiento solo con la opción: una fuente de FastAPI que suprime su cancelación daREVOCATION_UNAVAILABLEal vencer el plazo en lugar de su instantánea tardía. No queda limitado: el trabajo que una fuente inicia por su cuenta, como el workerasyncio.to_threaddedeny_list_revocation_source, cuya E/S no se cancela. Sin la opción no cambia nada. dcp_ai.fastapi.DCPVerifyMiddleware: un plazo y una capacidad para untrusted_principalsasíncrono, opt-in.SourceSupervisor(max_in_flight=..., max_in_flight_per_tenant=None), exportado solo desdedcp_ai.fastapi, ysource_supervisorconsupervise={"trust_timeout": seconds}(ambos juntos). La fuente se ejecuta como una Task que pertenece al supervisor. Un conjunto de confianza que llega después del plazo (loop.time(), comprobado al llegar y al usarse), una llamada sin hueco libre (sin cola), un supervisor cerrado o uno vinculado a otro bucle de eventos dan el mismo 503 que una fuente que falla. Un hueco solo se libera cuando la Task termina realmente. Se proporcionanclose(drain_timeout)ystats(). Untrusted_principalsestático o síncrono con estas opciones lanzaAdmissionConfigError; también lo hacía{"revocation": True}en este primer paso, ahora aceptado (véase la entrada de revocación anterior). Un supervisor por bucle de eventos; no detiene una fuente que ignora la cancelación ni aísla la CPU. Sin estas opciones no cambia nada.@dcp-ai/expressy@dcp-ai/openclaw: un plazo y una capacidad para una funcióntrustedPrincipals, opt-in.createSourceSupervisor({ maxInFlight, maxInFlightPerTenant?, onSourceEvent? })crea un supervisor, ysourceSupervisorconsupervise: { trustTimeoutMs }lo aplica (ambos juntos, contrustedPrincipalscomo función). La función recibe entonces{ tenant, signal }. Un conjunto de confianza que llega después del plazo, medido con un reloj monótono en el momento de llegar, no se usa, ni tampoco una llamada que no encuentra hueco libre (sin cola) o un supervisor cerrado: el resultado es el mismo que el de una fuente que falla (TRUST_SOURCE_FAILED, 503 en Express). Un hueco solo se libera cuando la función termina realmente. La capacidad es por objeto supervisor y puede compartirse entre ambos paquetes y sus compilaciones ESM y CJS. Se proporcionanclose({ drainMs })ystats(); los contadores se detienen enNumber.MAX_SAFE_INTEGER.configureVerifierde OpenClaw se niega a sustituir o quitar un supervisor mientras está en curso una llamada que empezó bajo él (también antes de que llame a su fuente) o su fuente sigue ejecutándose, incluso después declose(). No detiene el trabajo de una fuente ni aísla la CPU. Sin estas opciones no cambia nada. En este primer paso,supervise.revocation: truese rechazaba y la revocación manteníarevocationTimeoutMs; ahora se acepta (véase la entrada de revocación anterior).dcp_ai.fastapi.DCPVerifyMiddleware(max_body_bytes=...): un presupuesto opt-in, en bytes, para el cuerpo de la petición (predeterminadoNone, sin presupuesto, como antes; un entero positivo, o si noAdmissionConfigErrorCONFIG_INVALID_VALUE). El middleware lee entonces primero el cuerpo, también con el bundle en la cabecera o conrequire_bundle=False: unContent-Lengthpor encima del presupuesto, o bytes recibidos por encima de él (sinContent-Length, fragmentado o con uno erróneo), reciben413sin recibir el resto, analizar, resolver la confianza, verificar ni llegar a la ruta; unContent-Lengthno válido o repetido o una desconexión durante el envío reciben400. Un cuerpo dentro del presupuesto se trata como antes y sigue llegando a la ruta. Limita los bytes recibidos, no la descompresión, la profundidad del JSON ni el tiempo, y es un límite de despliegue, no parte del protocolo. La opción necesita Starlette 0.28.0 o posterior, que pasa a la ruta un cuerpo leído en el middleware; con un Starlette más antiguo o no identificado lanzaAdmissionConfigErrorCONFIG_INVALID_VALUEal construir el middleware. Las versiones mínimas del paquete no cambian, y sin la opción no se comprueba nada.- Admisión: principales de confianza y el perfil
strict-v1(spec/profiles/admission,docs/ADMISSION.md). Un bundle verificado prueba la integridad y la firma con las claves que enumera; un conjunto de confianza del operador (schemas/v2/principal_trust_set.schema.json) permite ahora que un verificador admita un bundle solo cuando las claves que verificaron las firmas de su principal (RPR y manifiesto, clásica y PQ) son claves activas y de confianza de esehuman_id.strict-v1(schemas/v2/admission_profile.schema.json) añade requisitos fijados por el operador: solo V2, firmas híbridas en todos los niveles, vinculación de sesión, unintended_verifierfirmado igual alverifier_iddel operador, una sesión firmada de 300 s como máximo y una instantánea de revocación (schemas/v2/revocation_snapshot.schema.json) de 300 s de antigüedad como máximo que cubra al agente y a toda clave de firmante verificada. Su configuración solo puede endurecerlo; un valor que lo debilita es unCONFIG_PROFILE_CONTRADICTION. Los resultados conservanverifiedy añadenprincipal_trust,profile_compliance,admitted,recorded_decision,trust_set_version,errorscon código, yaction_authorized, siemprefalse: la admisión no autoriza una acción. - SDK: TypeScript (
admitSignedBundleV2,evaluateAdmissionV2,createPrincipalTrustStore,resolveStrictV1Profile,revocationSubjects,revocationSnapshotFromDenyList), con las mismas funciones en Python, Go y Rust. Los cuatro verificadores aceptan un reloj opcional e informan de la clave con la que se verificó cada componente de la firma (signer_keys). En Rust,V2VerificationResultconserva sus diez campos yverify_signed_bundle_v2/verify_signed_bundle_v2_with_policysus tipos, de modo que el código compilado contra ellos sigue compilando: las claves vienen del nuevoverify_signed_bundle_v2_at, que devuelveV2VerificationDetails(#[non_exhaustive]; el resultado comoresult, ysigner_keys, de la misma verificación). La admisión lo recibe y lo devuelve; su JSON son los campos del resultado seguidos designer_keys. - Integraciones:
dcpVerifyde@dcp-ai/express,DCPVerifyMiddlewarededcp_ai.fastapiyconfigureVerifierde@dcp-ai/openclawaceptantrustedPrincipals,profile,revocationSourcey, en los middlewares, una funcióntenantdel operador. Una petición llega a la ruta solo cuando se admite. Una función de confianza que lanza una excepción o no devuelve un conjunto de confianza válido (undefined,null, un conjunto no válido) rechaza la petición:503en los middlewares,admitted: falseen OpenClaw. La caché de Express solo conserva la verificación criptográfica, con clave en el bundle, la política y el tenant; la confianza, el perfil y la revocación se evalúan en cada petición.denyListRevocationSourcelee una lista de denegación completa; una fuente que falla o agota su tiempo rechaza la petición. - Verificador de referencia, servidor y CLI:
admitSignedBundleenlib/verify.js,POST /v2/admitconfigurado porDCP_TRUSTED_PRINCIPALS_FILE,DCP_ADMISSION_PROFILE_FILEyDCP_REVOCATION_LIST_URL, ydcp admit-bundle. tests/interop/v2/admission_vectors.json: 65 casos con resultados esperados escritos a mano y la regla que comprueba cada uno, y 49 casos de configuración, que ejecutan los cuatro SDK ynpm run conformance.- SDK de Rust: construcción y firma nativas de bundles V2,
dcp_ai::v2::bundle.build_bundle_v2(&rpr, &passport, &intent, &policy, &audit_entries, session_nonce)comprueba cadapayload_hashy elsession_noncede cada artefacto y entrada de auditoría, y escribe el manifiesto con ambas raíces de Merkle de auditoría (una pista vacía recibe raíces de 64 ceros, como en Go);sign_bundle_v2(bundle, &classical, &pq)firma el manifiesto de forma compuesta, consigner.idigual alhuman_iddel RPR, como haceSignBundleV2de Go. Como el de Go, el constructor todavía no acepta checkpoints PQ. El agente de Rust de la matriz de interoperabilidad los usa en lugar de ensamblar el bundle a mano. tools/interop-matrix: interoperabilidad en vivo entre los cuatro SDK. Un proceso de agente por SDK (TypeScript, Python, Go, Rust) crea su identidad, declara intenciones, actúa y firma sus bundles de ciudadanía (DCP-01 a DCP-03); todos los demás agentes, y el verificador de referencialib/verify.js, los verifican con la política predeterminada y conhybrid_required, intactos y tras siete manipulaciones en tránsito (intención cambiada, entradas de auditoría reordenadas o eliminadas, sesión empalmada, firma del bundle o pasaporte intercambiados, firma PQ eliminada). Los agentes solo comparten los mensajes que retransmite el orquestador..github/workflows/interop-matrix.ymlla ejecuta en cada pull request y cada noche.@dcp-ai/google-a2a2.1.0: el Agent Passport DCP firmado viaja dentro de la Agent Card de A2A.passportToAgentCard(signedPassport, url)(oattachPassportToAgentCard(card, signedPassport)) coloca elSignedPayload<AgentPassportV2>encapabilities.extensionsde la tarjeta, bajohttps://dcp-ai.org/a2a/extensions/passport/v2, el mecanismo de extensión propio de A2A;verifyAgentCardPassport(card)lo verifica sin conexión (hash del payload, firma compuestapq_over_classicalcon el contexto AgentPassport y las claves activas y no caducadas que enumera el pasaporte,status: active, ymetadata.dcp_agent_idque nombre al mismo agente), y falla de forma cerrada ante cualquier cosa mal formada;agentCardToPassportdevuelve ese pasaporte en lugar de un esqueleto cuando la tarjeta lo lleva. El paquete es ahora TypeScript (compilación endist/, suite de vitest en la CI).@dcp-ai/anthropic-mcp2.1.0: recibos firmados para las llamadas a herramientas MCP.withDcpReceipt(name, handler, { identity })envuelve cualquier manejador de herramienta MCP: la llamada se clasifica en una Intent (DCP-02) y se firma de forma compuesta antes de que se ejecute la herramienta, la política decide (approve ejecuta la herramienta; escalate y block devuelven un resultadoisErrorsin ejecutarla; un manejador que lanza una excepción produceoutcome: "failed"), y una entrada de auditoría (DCP-03) registra el SHA-256 de los argumentos y del resultado. El recibo en_meta.dcp_receiptes un bundle firmado DCP v2.0 corriente, así que cualquier verificador DCP lo comprueba sin conexión;verifyDcpReceiptademás recalcula el hash de la evidencia a partir de los argumentos y el resultado que vio quien llama y comprueba el nombre de la herramienta.createReceiptIdentityemite una identidad de sesión (claves Ed25519 + ML-DSA-65, RPR y pasaporte firmados) para pruebas y primeras ejecuciones. El paquete es ahora TypeScript (compilación endist/, suite de vitest en la CI) ydcp_verify_bundleejecuta elverifySignedBundleV2real en lugar de una comprobación estructural; su entradasecurity_tier, que no se usaba, ha desaparecido.tests/interop/v2/bundle_vectors.json: 28 casos de rechazo más (5 mutacionesmust_reject, 23must_reject_bundles), uno por cada vulnerabilidad de la ronda v2.10, que los cuatro verificadores de los SDK (incluido Python) ejecutan en todos los modos de verificación: artefactos sin ningunacomposite_sig(V2-8);dcp_bundle_version1.0y3.0(V2-1); una clave caducada con un desfase distinto de UTC, valoresexpires_atde clave mal formados (fecha imposible, solo fecha, sin desfase) en el pasaporte y una fecha imposible en el RPR (V2-13); bundles solo clásicos cuya intención o decisión de política implicaelevatedmientras la intención declarasecurity_tier: "routine"oestimated_impact: "high"(V2-10); claves del prototipo (constructor,__proto__,toString,hasOwnProperty) en elrisk_tierdel pasaporte yconstructorenaction_type,estimated_impactysecurity_tier(V2-9); uncanonicalization_profiledesconocido o que no es una cadena (V2-3); una intención, una entrada de auditoría o el manifiesto con otrosession_nonce(V2-4); un hash del manifiesto, una raíz de auditoría o un hash de cadena que no coinciden (V2-3).tools/difffuzz: un fuzzer diferencial que hace pasar el mismo bundle firmado V2 mutado por los verificadores de TypeScript, Python, Go y Rust (un adaptador por SDK, compilado desde este checkout) y falla ante cualquier discrepancia, cierre inesperado o tiempo agotado, guardando cada hallazgo entools/difffuzz/findings/. Las semillas son los bundles firmados V2 detests/interop/v2/; los mutadores cubren campos borrados y nulos, intercambios de tipo, intercambios entre hermanos, cambios de caracteres base64, claves del prototipo, claves desconocidas, artefactos duplicados, variantes de números y de RFC 3339, cadenas demasiado largas, anidamiento profundo y reordenación de claves (que nunca debe cambiar un veredicto)..github/workflows/difffuzz.ymlejecuta el fuzzer en cada pull request (300 iteraciones), conworkflow_dispatchy cada noche (3000 iteraciones).- Cadenas de delegación verificables (DCP-09 §3.5) en los cuatro SDK.
schemas/v2/delegation_mandate.schema.jsongana los campos opcionalesparent_mandate_id,delegation_depth,sub_delegation({ permitted, max_depth }),minimum_security_tierydelegator_composite_sig; un mandato raíz conservahuman_composite_sigy no cambia ni un byte, un submandato lleva en su lugardelegator_composite_sig, firmada por el agente que delega con el nuevo contexto de separación de dominioDCP-AI.v2.SubMandate(domain-separation.{ts,py,go,rs},interop_vectors.json).verifyMandateChain/verify_mandate_chain/VerifyMandateChainverifica una cadena ordenada sin conexión hasta la persona: la firma raíz con las claves de la persona, la firma de cada salto con las claves del agente padre, el enlace con el padre y la profundidad,sub_delegation.permittedy el menormax_depthde los ancestros, el mismohuman_id, ventanas de validez anidadas comparadas como instantes RFC 3339, la atenuación (subconjuntos deactions_permittedydata_classes,limitsnunca más laxos, un hijo no puede añadirdata_classessi su padre no tiene ninguna, ylimitsnuméricos solo como enteros dentro de ±(2^53 − 1): más allá de ellos, un verificador que lee los números JSON como dobles y otro que conserva todas las cifras discrepan sobre lo que dice un mandato, así que un límite así esnot comparablea cualquier profundidad), unminimum_security_tierque nunca baja del del padre, y la revocación, donde un ancestro revocado invalida a todos sus descendientes. El resultado lleva el nivel efectivo de la cadena (el mayorminimum_security_tiera lo largo de ella). Todo lo que sea desconocido, falte para la profundidad o no sea comparable falla de forma cerrada. Las claves de firma deben estaractivey no caducadas en el momento de la verificación, y la firma de un mandato debe serpq_over_classical. - Constructores de submandatos junto al constructor raíz:
createSubMandate/create_sub_mandate/CreateSubMandate, ysub_delegation/minimum_security_tieren el constructor raíz (create_delegation_mandate_with_optionsyMandateChainOptionsen Rust). computeSecurityTier/compute_security_tier/SecurityTierInput.MandateChainTier/compute_security_tier_with_floor, y la copia raízlib/security-tier.js(computeSecurityTier(intent, policyDecision, mandateChainTier)), aceptan el nivel efectivo de una cadena verificada como entrada opcional que solo puede elevar el nivel (especificación §14.2); en Go, unMandateChainTiervacío significa que no hay cadena, y cualquier otro nombre que no sea un nivel cuenta comoelevated. Los verificadores de bundles todavía no lo leen: un bundle no tiene ningún campo que lleve su cadena de mandatos.tests/interop/v2/mandate_chain_vectors.json(generado porsdks/typescript/src/__tests__/generate-mandate-chain-vectors.ts): cadenas de profundidad 1 y 2 que todo SDK debe aceptar con su profundidad y su nivel efectivo, cadenas que todo SDK debe rechazar (acciones ampliadas, clases de datos añadidas, límites relajados, un límite de 2^53 o de 2^53 + 1 escrito como el entero del formato de intercambio que un doble no puede representar, validez fuera de la del padre, profundidad más allá demax_depth,parent_mandate_idausente o erróneo, nivel rebajado, raíz o intermedio revocados, un submandato firmado por el agente equivocado, ambas firmas presentes,delegation_deptherrónea, y más) y los casos del nivel mínimo. Las cuatro suites de los SDK lo ejecutan.- API experimentales y opt-in para el candidato de canonicalización
dcp-jcs-v2(docs/proposals/canonicalization-candidate, §15–§18). El candidato no es normativo ni está activo:dcp-jcs-v1sigue siendo el único perfil de canonicalización normativo y predeterminado, y no cambia ningún valor predeterminado, constructor, firmante ni verificador. Solo quienes llaman y optan por ellas llegan a estas API, en TypeScript, Python, Go, Rust y WASM. - Ingreso: comprueba los bytes originales de una unidad (UTF-8, BOM, duplicados, enteros exactos en ±(2^53−1), profundidad 64) y da sus bytes canónicos o una clase y un desplazamiento.
- Enrutador: extrae la unidad de su transporte, lee el perfil y decide: rechazar,
delegar en
dcp-jcs-v1mediante un decodificador histórico con nombre, o seleccionar el candidato. - Verificador: verifica a partir de los bytes originales con un único verificador como máximo: el actual para
dcp-jcs-v1, y el actual aceptando exactamentedcp-jcs-v2para el candidato. Un bundle verificado no concede confianza ni admisión. - Productores: firman artefactos (
experimentalCandidateSignPayloady sus equivalentes) y bundles cuyo manifiesto nombradcp-jcs-v2(experimentalCandidateSignBundle), a partir de bytes originales o de valores del host comprobados de forma estricta. Un rechazo por el dominio o la forma de las entradas se produce antes de llamar a ningún firmante; un error de clave, de registro o de proveedor todavía puede llegar después de que se haya hecho una firma. - Pruebas: una matriz cruzada de cinco productores × cinco consumidores
(
tests/interop/candidate/phase4, 975 resultados con un veredicto de todo o nada) y su medición de coste, que ejecuta el nuevo flujo de trabajocanonicalization-candidate-matrix, que no publica nada. Los vectores están entests/interop/candidate/.
Cambiado¶
- Las traducciones están ahora bajo
i18n/<locale>/, en la misma ruta que su fuente en inglés, que no se mueve:<dir>/<name>.<locale>.mdpasa a seri18n/<locale>/<dir>/<name>.mdparaes,pt-BR,jayzh-CN(388 archivos). El traslado solo cambió rutas de enlaces: las de los archivos movidos y los enlaces de idioma de diez documentos en inglés. El README en inglés de cada SDK enlaza a sus traducciones con una URL absoluta, para que sus enlaces de idioma no dependan de rutas fuera del paquete. El sitio de documentación conserva sus URL, anclas y selector de idioma en los cinco idiomas. Las rutas antiguas ya no existen enmainy no hay archivos de redirección; un commit o una etiqueta anteriores conservan cada archivo en su ruta antigua. Las versiones futuras de los paquetes npm de los SDK y de las integraciones, empaquetadas desde este árbol, ya no incluyen los README traducidos; los paquetes ya publicados no se modifican, y sus enlaces a las rutas antiguas de GitHub pueden quedar obsoletos. La comprobación de documentación (scripts/docs_i18n.py) rechaza además los archivos bajoi18n/que no sean una traducción registrada, los enlaces haciai18n/que no lleguen a una y los avisos de traducción que no enlacen a la fuente inglesa exacta, y comprueba los enlaces a la ramamainde este repositorio como enlaces locales. dcp-ai verify(@dcp-ai/cli) verifica los bundles V2 (bundle.dcp_bundle_version"2.0" o unbundle.manifest) con el verificador normativo: el sobre y los cuatro payloads firmados se validan conschemas/v2, cuyas copias se distribuyen en el paquete, y después se ejecutaverifySignedBundleV2de@dcp-ai/sdkconDEFAULT_VERIFIER_POLICYy los proveedores Ed25519, ML-DSA-65 y SLH-DSA-192f. El sobre prenormativo (dcp_version"2.0" obundle_manifesten el nivel superior, osignature.binding: "composite") se rechaza antes que nada, también cuando esas marcas están en un bundle V1 o V2, y su comprobación designature.classical.valueha desaparecido, sin recurso a la firma clásica. Cuando existe.dcp/keys/ed25519_public.key, fija la clave de firma del principal entre lasbinding_keysdel RPR (un archivo vacío se rechaza); sin él se usan las propias claves del bundle, lo que comprueba las firmas, no la confianza ni la admisión. La verificación V1 no cambia.dcp-ai signtampoco cambia: añade asignatures[], un formato queverifyno lee; los bundles V2 se firman consignBundleV2odcp sign-bundle --composite. El paquete exige ahora@dcp-ai/sdk^2.2.0y declaraajvyajv-formats.dcp-ai verify(@dcp-ai/cli) sale con 0 solo cuando imprime VALID. Con archivos V1 sale ahora con 1, donde antes salía con 0, cuando la firma Ed25519 es INVALID, cuando falta la clave o falta una firma utilizable (imprime «No public key available», también cuando la clave está y la firma falta o está vacía) y cuando el archivo no es un bundle firmado (un objeto sin unbundleverdadero, un array o un valor primitivo). Esos mensajes no cambian, y la falta de un argumento o de un archivo, un JSON no válido, el base64 o un tamaño erróneo siguen saliendo con 1 y su error. Un script que tomaba la salida 0 como «se ejecutó» debe esperar ahora 1 en esos casos. Con un archivo V1, la salida 0 significa que la firma Ed25519 sobre el bundle canónico se verificó, nada más; con un bundle V2 significa que los esquemas y el verificador normativo pasaron (véase arriba). Ninguna de las dos es una decisión de admisión.- Verificadores V2 de Go y Rust: una política que restringe
accepted_classical_algsoaccepted_pq_algsse rechazaba como un campo que no aplicaban, así que todo bundle fallaba con ella; ahora aplican ambas listas como hacen TypeScript y Python (la lista clásica salvo conpq_only), y una firma con un algoritmo fuera de ellas falla por no estar aceptada por la política del verificador. También aceptanallow_v1_bundles: false, que fija strict-v1: solo verifican bundles V2, así que el campo no cambia nada para ellos. Las políticas que verificaban antes se comportan igual. - El verificador de referencia (
lib/verify.js,dcp verify-bundle, el servidor de verificación y su imagen Docker) depende del@dcp-ai/sdkdel workspace (file:sdks/typescript, empaquetado medianteinstall-linksen.npmrc) hasta que 2.2.0 esté en npm, de modo que ejecuta las correcciones del verificador de esta versión en lugar del 2.1.1 publicado, que aceptaba 39 de los 79 vectoresmust_reject_bundles. Hay que compilarsdks/typescriptantes denpm installen la raíz; la CI ydocker/Dockerfilelo hacen. Para publicar hay que volver a subir la dependencia a^2.2.0. lib/verify.js: la validación de esquemas construye un Ajv por cada raíz de esquemas y compila cada esquema una sola vez, en el primer uso, en lugar de cargar y compilar todos los esquemas en cada llamada avalidateOne(cinco por bundle V2). Validar los esquemas de un bundle V2 pasó de unos 34 ms a 0.14 ms (mediana de 20 ejecuciones después de la primera, que sigue compilando).
Corregido¶
- Cambio de aceptación:
v2.VerifyPayloadHashde Go comprueba ahora elpayload_hashnormativo (código fuente demain, no publicado; Go v2.11.1 conserva el defecto). Comparaba el SHA-256 en hex sin prefijo conpayload_hash, así que devolvíafalsepara la salida deSignPayloady para todo valor válido según el schema (sha256:<hex>), ytruepara el digest sin prefijo. Ahora devuelvetruesolo cuandopayload_hashes exactamente"sha256:"seguido del SHA-256 en hex en minúsculas de los bytesdcp-jcs-v1, el valor que usanSignPayload,schemas/v2/signed_payload.schema.jsony los helpers de TypeScript, Python y Rust. Un digest sin prefijo, hex en mayúsculas, espacios alrededor o cualquier otra forma devuelve ahorafalse, nil. Migración: pasapayload_hashtal como se recibió; para construirlo a partir dePreparePayload, escribe"sha256:" + hash.PreparePayloadsigue devolviendo el digest sin prefijo (su comentario ahora lo dice), los errores denily de canonicalización no cambian, y el verificador de bundles, que nunca usó este helper, no cambia. El helper solo comprueba la consistencia del hash, no la firma, las claves, la política ni la admisión. Nuevosdks/go/dcp/v2/signed_payload_test.go. El README de Go y el comentario deAuditEvidenceV2indican ahora también que decodificar un bundle recibido en los structs tipados descarta los miembrosnully desconocidos deevidence, así que los bytes recibidos son los que hay que conservar y verificar. - Cambio de aceptación:
wasm_dual_merkle_rootde WASM ydualMerkleRootde@dcp-ai/wasmcalculan ahora la raíz Merkle dual de V2-11 (código fuente demain, no publicado;@dcp-ai/wasm2.1.1 y anteriores mantienen el helper antiguo). El helper hasheaba el texto hexadecimal de dos digests concatenados y promovía el nodo impar, así que con 2 o más hojas sus raíces no coincidían ni conspec/VERIFICATION.mdV2-11 (digests de 32 bytes en bruto concatenados, el último nodo duplicado en una capa impar) ni con los SDK de TypeScript, Python, Go y Rust. Ahora delega en el núcleo de Rustv2::dual_hash::dual_merkle_root. El cambio no se limita a las raíces multihoja: estas cambian porque eran incompatibles con V2-11; una hoja que no es un digest hexadecimal de 32 bytes es ahora un error en lugar de hashearse como texto; y las raíces son siempre hex en minúsculas, así que una sola hoja dada en mayúsculas vuelve ahora en minúsculas. Una sola hoja válida en minúsculas, el formato del resultado y los errores para un array vacío y para una entrada que no es JSON no cambian. La verificación de bundles, incluido el verificador WASM, ya usaba la reducción correcta y no cambia; los bundles anteriores y su evidencia no se reescriben ni se vuelven a firmar, porque el cambio se limita a este helper. Los nuevos vectores públicostests/conformance/v2/merkle_vectors.json(raíces exactas para 1, 2, 3 y 5 hojas y para hojas reordenadas, generadas sin conexión con la biblioteca estándar de Python y ancladas en las raíces golden de 2 hojas) se comprueban consdks/wasm/test/dual-merkle-root.mjsysdks/rust/tests/dual_merkle_vectors.rs. denyListRevocationSource(@dcp-ai/express,@dcp-ai/openclaw) dejaba respuestas abiertas. Una respuesta que no era 2xx o unContent-Lengthdeclarado de más de 16 MiB se rechazaba sin leer ni cerrar su cuerpo: la respuesta seguía abierta después de que la llamada terminara y se liberara su hueco, fuera del alcance del tiempo de espera y de la señal de quien llamaba. Y con Node 22.15 (undici 6.21.2), una recolección de basura tras las cabeceras impedía que la señal del fetch llegara a un cuerpo que se estaba leyendo: su propio tiempo de espera y el plazo del supervisor ya no detenían un cuerpo que seguía llegando, y la llamada nunca terminaba, reteniendo su hueco. Ahora aborta su fetch y cancela el cuerpo en todas las salidas, detiene su propia lectura con su señal y libera el lector. Los códigos, los valores predeterminados y las capacidades no cambian; no se drena nada y no se espera al cierre remoto.- CLI de referencia (
bin/dcp.js): las rutas se limitaban al directorio de trabajo tal como estaban escritas, no adonde llevaban, y varias lecturas y escrituras se saltaban la comprobación. Un enlace simbólico dentro del directorio de trabajo, o una ruta a otro directorio (keys rotate --key-dir,governance sign-advisory --key-dir,governance verify-advisory --sigs), permitía que un comando leyera archivos de fuera, incluidas las claves secretas; un enlace simbólico o duro en una ruta de salida (keygen,keys rotate,recovery-setup, los comandos de gobernanza, la salida design-bundleV1) hacía que escribiera fuera; un archivo de clave secreta existente conservaba unos permisos más amplios; ydcp validate,conformanceysign-bundleV1 ejecutabantools/*.jsdesde el directorio de trabajo, así que se ejecutaba un script colocado allí. Ahora toda ruta se resuelve con sus enlaces simbólicos (uno colgante se rechaza) y se comprueba, toda lectura y escritura de una ruta del usuario pasa por esa comprobación, los archivos se escriben en un archivo nuevo (O_EXCL, permisos exactos) que se renombra sobre el destino, y la CLI ejecuta sus propias herramientas. La CLI supone que nada más cambia el directorio de trabajo mientras se ejecuta un comando; en Windows, dondeO_NOFOLLOWno existe, el renombrado sigue sustituyendo un enlace en lugar de escribir a través de él. @dcp-ai/anthropic-mcpy@dcp-ai/google-a2a: la condición de exportación"types"iba primero, así que un consumidor CommonJS recibía las declaraciones ESM (dist/index.d.ts) y TypeScript rechazabarequire()connode16con TS1471. Se elimina la condición, de modo querequirerecibedist/index.d.ctseimportrecibedist/index.d.ts.@dcp-ai/sdk,@dcp-ai/expressy@dcp-ai/openclawquitan la misma condición, que iba después de"import"y"require", nunca coincidía y hacía que tsup advirtiera en cada compilación. En@dcp-ai/wasm, cuyas exportaciones no tienen"require", daba a un consumidor CommonJS tipos connodenextpara unrequire()que Node rechaza conERR_PACKAGE_PATH_NOT_EXPORTED. TypeScript informa ahora TS2307 también ahí.- SDK de Go:
VerifySignedBundleV2WithPolicyhacía fallar todo bundle con una política que fijabarequire_session_expiry,require_audience_bindingoverifier_id, campos queverifier_policy.schema.jsonenumera y que los verificadores de TypeScript y Python aplican.VerifierPolicylos tiene ahora (RequireSessionExpiry,RequireAudienceBinding,VerifierID, desactivados en su valor cero) y el verificador los aplica como TypeScript: un manifiesto sinsession_expires_at, sinintended_verifiero destinado a otro verificador distinto deverifier_idse rechaza. - SDK de Rust y
@dcp-ai/wasm:VerifierPolicyrechazabarequire_session_expiry,require_audience_bindingyverifier_id, queschemas/v2/verifier_policy.schema.jsonenumera y que los verificadores de TypeScript y Python aplican, así que una política que fijara cualquiera de ellos hacía fallar toda verificación. Ahora se aplican como en TypeScript: conrequire_session_expiry, un manifiesto sinsession_expires_atse rechaza, y conrequire_audience_binding, uno sinintended_verifiero, converifier_idfijado, con otro distinto. ElBundleManifestde Rust ganaintended_verifier. - SDK de Python,
dcp_ai.langchain(DCPTool.run,DCPTool.arun) ydcp_ai.openai(dcp_verify_bundleenhandle_dcp_tool_call): ambos enviaban todo bundle al verificador V1, así que todo bundle V2 se rechazaba y un bundle V1 se aceptaba sin ninguna política de verificación. Ahora verifican los bundles V2 converify_signed_bundle_v2y un registro Ed25519 / ML-DSA-65 / SLH-DSA-192f construido una sola vez, con una política de verificación que fija quien llama (DCPTool(verifier_policy=...),handle_dcp_tool_call(..., verifier_policy=...), sobreDEFAULT_VERIFIER_POLICY), y devuelven los errores del verificador, como máximo 20 errores y 20 advertencias de 500 caracteres cada uno, con los caracteres de control y de formato del texto aportado por el bundle (unagent_id, una marca de tiempo no válida) escapados, de modo que un bundle no pueda meter megabytes ni saltos de línea en el contexto del modelo. Un bundle V1 solo se acepta con la regla V2-10 del middleware de FastAPI, ahora compartida por los tres puntos de entrada (dcp_ai._verify_any) y ampliada a toda la política: la política permite bundles V1, acepta ed25519 (enaccepted_classical_algs, no enadvisory_rejected_algs), no exige vinculación de audiencia, caducidad de sesión ni duración máxima de sesión (un bundle V1 no tieneintended_verifierni ventana de sesión), y aceptaría un bundle solo clásico para su nivel de riesgo y su nivel de seguridad; el middleware de FastAPI aplica también las condiciones añadidas. Un bundle V1 cuyos artefactos no son objetos, o cuyoaudit_entriesno es una lista de objetos, se rechaza en los tres (el middleware de FastAPI respondía 500 para unagent_passportque no era un objeto, ahora 403), ydcp_ai.verify.verify_signed_bundledevuelve un error en lugar de lanzar una excepción ante un bloque de firma, una clave o una pista de auditoría mal formados. Un bundle V1 cuyoagent_iddel pasaporte ohuman_iddel RPR no sea una cadena (o cuyopublic_key,statusocapabilitiesdel pasaporte tenga el tipo incorrecto) también se rechaza, en lugar de llegar al contexto del agente de FastAPI. Las respuestas 403 del middleware de FastAPI y loswarningsde su contexto se limitan y se escapan igual que la salida de los puentes. Una instancia deVerifierPolicyse valida como un dict, de modo que una construida conmodel_constructo modificada por asignación no puede llevar un valor no válido, y un nivel de riesgo que susrisk_overridesomiten recibe sudefault_mode, como lo resuelven el verificador V2 yresolveModedel SDK de TypeScript (un bundle V1 para ese nivel se rechazaba con «is not low, medium or high»). Los puntos de entrada síncronos funcionan dentro de un bucle de eventos en ejecución (la verificación se ejecuta en un hilo de trabajo) y usan un bucle privado, dejando el bucle de eventos actual del hilo como estaba;DCPTool.arunespera la verificación, y el nuevoaverify_bundle_tool_calles la forma asíncrona de la llamada a herramienta de OpenAI. Ambos superan todos los casos detests/interop/v2/bundle_vectors.json. - SDK de Go:
BundleManifestno teníaintended_verifier, así que un manifiesto decodificado en él y codificado de nuevo perdía el campo y su firma dejaba de verificarse. El campo está ahora (IntendedVerifier, omitido cuando está vacío); una prueba hace el viaje de ida y vuelta de un manifiesto con todos los campos del esquema. - SDK de Python,
dcp_ai.crewai: los agentes de unDCPCrewnunca compartían el nonce de sesión del crew.DCPCrewAgentsiempre generaba el suyo, así que la comprobación del crew de un agente sin nonce nunca se aplicaba, y las entradas de auditoría de cada agente llevaban unsession_noncedistinto. Ahora un crew da su nonce a todo agente que siga teniendo el nonce que generó para sí mismo y que todavía no haya registrado nada; un nonce dado al agente, o fijado en él más tarde, se mantiene. - Característica
wasmde Rust y@dcp-ai/wasm:wasm_sign_bundle(signBundle) escribíacreated_at ""y ningúnsigner.id, así que el verificador de referencia, la CLI y el servidor rechazaban todo bundle que firmaba (created_at must match format "date-time",signer must have required property 'id').wasm_build_bundle(buildBundle) unía las hojas de auditoría SHA3-256 con SHA-256, a diferencia de todos los demás SDK, y se saltaba una entrada de auditoría que no podía canonicalizar aunque la seguía contando. Ambos llaman ahora abuild_bundle_v2ysign_bundle_v2, así quebuildBundletambién rechaza unpayload_hashque no coincide con su payload y un artefacto o una entrada de otra sesión. SinsessionNonce,buildBundlegeneraba un nonce que ningún artefacto llevaba, así que inclusoverifyBundlerechazaba el bundle; ahora toma elsession_noncedel RPR. La demostraciónexample.htmlpasaba payloads en bruto awasm_build_bundle, que nunca los aceptó; ahora firma primero cada artefacto. El tipoSignedBundleV2declarasigner.id. - SDK de Rust:
Ed25519Provider::signentraba en pánico con una clave secreta de menos de 32 bytes (en wasm, una trampa en lugar de un error JSON); ahora devuelveinvalid secret key length. - Cadenas de mandatos (los cuatro SDK): una entrada de submandato sin
data_classesbajo una entrada del padre que las tiene pasaba la atenuación, aunque entonces nada la ataba a las clases de datos del padre. Ahora se rechaza (data_classes for domain "<d>" missing), como ya ocurría con una clave delimitsausente; DCP-09 §3.5.2, regla 5, así lo dice. Vector compartidodropped_data_classes. - SDK de Go: un
A2ASessionusado desde varias goroutines hacía caer el proceso (fatal error: concurrent map writesenDecryptMessage). La sesión tiene ahora un mutex, así queEncryptMessage,DecryptMessageyNeedsRekeyingpueden ejecutarse a la vez; las secuencias siguen siendo únicas y cada mensaje se sigue aceptando una sola vez.IntentV2ganaSecurityTieryPolicyDecisionV2ganaResolvedTier(ambos opcionales en el esquema y omitidos cuando están vacíos), de modo que quien integre en Go usando los payloads tipados puede declararlos. tools/difffuzz:prototype_key_as_keyasignaba__proto__como una propiedad normal, lo que fija el prototipo en lugar de añadir una clave, así que esos mutantes nunca llevaban la clave que decían (y un ciclo podía hacer caer la ejecución); ahora define una clave propia. Un adaptador que no puede arrancar o que murió entre veredictos se informa como cierre inesperado en lugar de como un error no gestionado.spec/VERIFICATION.mdnombra ahora correctamente el constructor de bundles de Rust (wasm_build_bundle, solo con la característicawasm).- SDK de Go:
SignBundleV2dejaba vacíosignature.signer.id, lo quesigned_bundle.schema.jsonprohíbe, así que el verificador de referencia, la CLI y el servidor rechazaban todo bundle que firmaba mientras los cuatro verificadores de los SDK lo aceptaban (lo encontrótools/interop-matrix). El firmante es ahora el principal responsable del bundle (signer.id= elhuman_iddel RPR), y un bundle cuyo RPR no tienehuman_idse rechaza antes de firmar. -
Sesiones A2A (TypeScript, Python, Go):
decryptMessage/decrypt_message/DecryptMessageguardan las secuencias aceptadas dentro de la ventana de recepción y aceptan cada una una sola vez, también fuera de orden; una secuencia que no es un entero no negativo se rechaza, y una secuencia solo se registra después de verificar su etiqueta de autenticación. Una secuencia que va una ventana completa o más por delante del contador de recepción se rechaza (DCP-04 §8.4). Una sesión restaurada sin el nuevo campo (TypeScriptundefined, Gonil, PythonNone, el valor predeterminado del constructor) cuenta como recibidas todas las secuencias de la ventana por debajo de su contador de recepción.verifyResumeProof(TypeScript) compara en tiempo constante, como ya hacían Python y Go. Se documenta que la sesión de Go no es segura para uso concurrente. -
SDK de Rust:
serde_jsontiene ahorafloat_roundtrip. Su analizador rápido de números en coma flotante se equivoca en un ULP con literales decimales largos, así que9007199254740991.0(quedcp-jcs-v1lee como el entero 9007199254740991, como hacen todos los demás SDK) se convertía en 9007199254740990 y2251799813685247.0en un no entero, y el verificador de Rust rechazaba firmas y límites que los verificadores de TypeScript, Python y Go aceptan. Vector compartidolimits_written_in_decimal_form. @dcp-ai/wasm: el paquete npm incluyepkg/(el binario.wasmy su código de enlace). wasm-pack escribepkg/.gitignore=*, npm respeta los archivos.gitignoreanidados, y@dcp-ai/wasm@2.0.0se publicó sinpkg/, así que su importación en tiempo de ejecución de../pkg/dcp_ai.jsfallaba. La compilación elimina ese archivo y la suite de pruebas comprueba el tarball.@dcp-ai/sdk: la versión de telemetría incluida se lee depackage.jsonen tiempo de compilación (tsup.config.ts) en lugar de un literal del script de compilación; 2.2.0 se habría identificado como 2.1.2.verify_signed_bundle_v2de Python informaba de un artefacto sincomposite_sigcomoMalformed signed bundle: KeyError('composite_sig'); el error nombra ahora el artefacto (intent: Missing composite signature), como en los demás SDK.verifySignedBundleV2de TypeScript devuelve un veredicto fallido (Malformed signed bundle: ...) para un bundle firmado estructuralmente mal formado en lugar de lanzar una excepción: unbundle,signature,manifestoaudit_entriesausente,nullo que no es un objeto, una entrada de auditoríanull, o un número no entero (0.5,1e400) en un objeto canonicalizado (spec/VERIFICATION.mdV2-3, V2-5). Los verificadores de Python, Go y Rust ya los rechazaban; lo encontrótools/difffuzz.VerifyObjectV1 de Go entraba en pánico con una clave pública que no se decodificaba a 32 bytes (ed25519.Verifyentra en pánico con cualquier otra longitud). Ahora comprueba la longitud decodificada y devuelve un error.VerifySignedBundlemantiene su comportamiento alrededor: una clave pública vacía recurre a la incrustada en la firma, si faltan ambas damissing public key, y un error deVerifyObjectse informa comoSIGNATURE INVALID.- El decodificador CBOR de TypeScript (
sdks/typescript/src/wire/cbor.ts, opcional) rechaza claves de mapa duplicadas, UTF-8 no válido, y enteros y longitudes fuera del rango de enteros seguros. Conserva U+FEFF como dato de texto, define una clave__proto__como propiedad propia en lugar de invocar el setter, y el codificador rechaza los sustitutos (surrogates) aislados en lugar de reemplazarlos. Esto refuerza el tratamiento de la entrada del decodificador; no es un decodificador CBOR canónico completo ni añade ningún perfil de canonicalización.
Documentación¶
spec/profiles/admission/README.md(perfil de admisión): precisiones editoriales, sin cambiar ningún vector, esquema ni comportamiento de los SDK; los cuatro SDK ya se comportan como dicen. El paso 2 de §5.1 verifica el bundle como bundle V2 firmado: un bundle que no es V2 no se verifica, diga lo que digaallow_v1_bundles, y bajo el perfil P1 se detiene antes de la verificación e informa el bundle como no verificado. Un perfil exige un conjunto de confianza: la función combinada y el paso 3 por sí solo rechazan un perfil sin él conCONFIG_PROFILE_CONTRADICTION, y la función combinada rechaza con el mismo código un perfil junto con una política de verificador de quien llama; no se fija si eso ocurre antes o después de la verificación. En §4.1, un modo fuera de la enumeración de los cuatro modos esCONFIG_INVALID_VALUE, como un valor de tipo erróneo, mientras que un modo de esa enumeración distinto dehybrid_required, o un algoritmo fuera del subconjunto permitido, esCONFIG_PROFILE_CONTRADICTION. §2.2: cuando un conjunto de confianza tiene varios problemas, puede informarse del código de cualquiera de ellos; se rechaza sea cual sea el informado, sin precedencia definida. §5.2:errorssigue el orden de las fases de §5.1, una comprobación puede añadir su código una vez por componente o sujeto, y dentro de una fase no se fija ningún orden.tools/conformance/README.md: las cadenaserror_containsque juzga el catálogo de evidencia (146 de sus 241 casos) son la redacción de diagnóstico de los SDK, no son normativas.docs/QUICKSTART_CREWAI.md: ninguno de los dos ejemplos funcionaba. El V1 importababuild_bundle, que el SDK de Python no tiene, y escribía firmas dondeintent_hashyprev_hashllevan hashes; el V2 usaba una API que nunca se publicó y decía que el SDK de Python no tenía constructor, firmante ni verificador de bundles V2, aunquedcp_ai.v2los incluye (desde 2.9.0). Ambos usan ahora el SDK tal como es: un control de política decide antes de que se ejecute la herramienta, la consulta de búsqueda queda fuera del registro, cada llamada a herramienta sella su propio bundle, y la verificación fija al operador (V1: su clave pública; V2: el hash de su RPR y sus kids conhybrid_required, y que cada kid se derive de su clave, porque las propias claves del bundle solo lo hacen coherente consigo mismo). Los bundles se verifican en Python y condcp verify-bundle, y los V2 también con los SDK de TypeScript, Go y Rust. La guía importatooldecrewai.tools, como lo documenta CrewAI, y describe el andamiaje denpm create @dcp-ai/crewaicomo el recorrido con marcadores que es.integrations/crewai/README.md: los ejemplos usaban payloads V1 con valores que los esquemas rechazan (liability_mode: "full", la capacidadwrite_file) e imprimían un campoaction_typeque las entradas de auditoría no tienen. Ahora funcionan; el README dice queDCPCrewAgentyDCPCrewguardan registros sin firmar y no ejecutan CrewAI, y enumera los parámetrossession_nonce.- Documentación en inglés y en las traducciones es, zh-CN, ja y pt-BR puesta al día: cadenas de delegación en el README de cada SDK, la ventana de recepción A2A, Testing & Assurance (matriz de interoperabilidad, fuzzer diferencial, vectores compartidos), hechos corregidos (Go 1.25, los crates FIPS de Rust, un ejemplo de DCP-09 en TypeScript que usaba campos fuera del esquema) y las filas del README que les faltaban a las traducciones.
docs/EU_AI_ACT_MAPPING.md: cómo apoyan los artefactos de DCP-AI las obligaciones de registro, supervisión humana, conservación de registros, explicación y evidencia de incidentes del Reglamento (UE) 2024/1689 (Ley de IA de la UE), modificado por el Reglamento (UE) 2026/1744, con el calendario de aplicación, una lista de comprobación para quien implementa y las carencias que DCP no cubre. Artículos verificados en EUR-Lex el 22 de octubre de 2026.spec/DCP-09.md§3.5 especifica ahora la subdelegación sobre los campos reales del formato de intercambio, con reglas normativas de verificación de la cadena; §2, §10.3 y §10.6 enumeran el contextoDCP-AI.v2.SubMandate.spec/DCP-AI-v2.0.md§4.1 enumera las etiquetas de contextoDCP-AI.v2.DelegationyDCP-AI.v2.SubMandate, y §14.2 establece que el nivel efectivo de una cadena de delegación es una entrada que solo puede elevar el nivel.
[2.10.0] - 2026-10-22¶
Versiones de paquetes¶
dcp-ai(PyPI) 2.8.2 → 2.9.0: pqcrypto 1.x, SLH-DSA de FIPS 205, ML-KEM-768, bundles V2 y un verificador V2; incompatible parablind_rpr(exigepii_salt) y para instalaciones conpqcrypto<1.0.@dcp-ai/sdk(npm) 2.1.2 → 2.2.0: correcciones de seguridad enverifySignedBundleV2, aplicación del nivel de seguridad, exportaciones de RPR cegado; incompatible parablindRpr(exigepii_salt).github.com/dcp-ai-protocol/dcp-ai/sdks/go/v2v2.9.0 → v2.10.0: refuerzo del verificador V2 y aplicación del nivel; incompatible paraBundleBuildInput(artefactos firmados) yBlindRPR.dcp-ai(crates.io) 2.9.0 → 2.10.0: verificador V2 nativo, aplicación del nivel, análisis de la caducidad RFC 3339; incompatible parablind_rpry el constructor de bundles dewasm.@dcp-ai/wasm2.0.0 → 2.1.0:buildBundleacepta artefactos firmados;verifyBundleacepta una política de verificación completa.@dcp-ai/openclaw2.0.0 → 2.1.0: nivel mínimo de seguridad ypii_salten los RPR firmados.- Sin cambios:
@dcp-ai/cli2.0.0 y los demás paquetes de integración.
Seguridad¶
- Los compromisos de los RPR cegados llevan sal, en los cuatro SDK.
pii_hasherasha256(canonical({ contact, legal_name })), así que cualquiera que adivinara el nombre y el contacto de un principal podía confirmar su suposición con un RPR cegado publicado, y en un log o anclaje inmutable eso sigue siendo cierto para siempre. El RPR completo lleva ahorapii_salt(32 bytes de un CSPRNG, hexadecimal en minúsculas), que se firma con él y nunca se publica, ypii_hashessha256(canonical({ contact, legal_name, pii_salt })). Borrar el RPR completo, y con él la sal, deja el compromiso publicado sin poder abrirse (spec/DCP-AI-v2.0.md§12.1). Uncontactolegal_nameausente se incluye en el hash comonullen todos los SDK; TypeScript omitía antes la clave. La nueva categoríablinded_rprdetests/interop/v2/interop_vectors.json(pii_hashcon sal y heredado, registro cegado esperado) se ejecuta en las cuatro suites de los SDK, ygenerate-interop-vectors.tsla emite. - La verificación de RPR cegados (
verifyBlindedRpr,verify_blinded_rpr,VerifyBlindedRPR) compara ahora tambiénsession_nonceybinding_keys, en los cuatro SDK. Antes, un RPR completo con otras claves de vinculación o con otro nonce de sesión seguía coincidiendo con el registro cegado. En Go, los campos de lista decodificados de JSON ya no dejan de coincidir con la misma lista construida como[]string. - Servidor de verificación:
POST /v2/passport/registerverifica la autofirma compuesta del pasaporte y registra solo sus claves de firmante; volver a registrar unagent_idexige un pasaporte más reciente firmado por todas sus claves activas. Cualquiera podía volver a registrar un agente con sus propias claves, colocar su token de emergencia y revocarlo de emergencia.POST /v2/keys/rotateverifica la prueba de posesión y la autorización de la clave antigua (verifyKeyRotation), yPOST /v2/multi-party/authorizeverifica la firma de cada parte con sus claves registradas, contando cada parte una sola vez. - Servidor de verificación:
/v2/policy/mode,/v2/governance/register,/v2/advisory/publishy/v2/advisory/auto-applyexigenAuthorization: Bearer $OPERATOR_TOKEN(bloqueados si no está fijado). Los avisos se validan con el esquema y se limitan a 1000. Los límites de tasa usan como clave la dirección del socket, no la cabeceraX-Forwarded-Forque aporta el cliente (TRUST_PROXY=truedetrás de un proxy), yVERIFIER_MODE/ALLOW_V1_BUNDLESse leen del entorno;allow_v1_bundlesse aplica ahora. - Servicio de revocación: los agentes se registran con su pasaporte firmado (
POST /v2/register), que registra la propiedad de los kids, la caducidad de las claves y el token de emergencia; volver a registrarse exige un pasaporte más reciente firmado por las claves clásica y PQ activas del agente, y/revokey/v2/revokedeben estar firmados por sus claves activas.GET /v2/check/kid/:kidinforma de las revocaciones de emergencia de los kids registrados sin?agent_id=. - Servicio de anclaje: ya no hay
tx_hashaleatorio (los registros llevantx_hash: null,onchain: false), los cuerpos se limitan a 1 MB,POST /flushnecesita el token del operador y una entrada mal formada es un 400 en lugar de un cierre inesperado. Registro de transparencia: las cabeceras del árbol se firman con Ed25519 en lugar de un HMAC que nadie más podía verificar;OPERATOR_KEYdebe ser la semilla de 64 caracteres hexadecimales (se eliminaron los marcadores públicos del compose), el árbol de Merkle usa los prefijos de hoja y nodo de RFC 6962, yPOST /gossip/peers(un vector de SSRF) necesita el token del operador. - Servidor de verificación y servicio de revocación: las claves caducadas ya no pueden volver a registrar un
pasaporte, autorizar una rotación de claves ni firmar por una parte o una revocación, y volver a registrarse
conserva las entradas de clave registradas, de modo que un pasaporte nuevo no puede ampliar el
expires_atde una clave. - Las configuraciones de Fly mantienen en ejecución una máquina de verificación y una de revocación: escalar a cero borraba sus registros en memoria cada vez que la máquina quedaba inactiva.
- SDK de TypeScript:
verifySignedBundleV2implementa los pasos V2-7 y V2-8 despec/VERIFICATION.md. Una firma PQ presente (pq_over_classical) debe verificarse en todos los modos;hybrid_preferred(el predeterminado para riesgo medio) aceptaba una no válida con una advertencia yclassical_onlyla ignoraba. Solo se tolera que falte (vinculaciónclassical_only), y no conhybrid_requirednipq_only. Lacomposite_sigpropia de cada artefacto se verifica ahora con las mismas reglas y su etiqueta de contexto: el RPR con susbinding_keys, y el pasaporte, la intención y la decisión de política con laskeysdel pasaporte. Antes solo se comprobabapayload_hash, así que una firma falsificada del agente en la intención se verificaba incluso conhybrid_required. El verificador de referencia,dcp verify-bundley el servidor lo reciben con la próxima versión de@dcp-ai/sdk. - SDK de TypeScript:
verifySignedBundleV2falla de forma cerrada ante un modo de verificación que no reconoce, y rechaza una política de verificación cuyo modo no seaclassical_only,hybrid_preferred,hybrid_requirednipq_only. - SDK de Go (
v2.VerifySignedBundleV2) y de Rust: lacomposite_sigde cada artefacto se verifica del mismo modo (V2-8), y las claves del firmante se buscan porkid(V2-6), el firmante del bundle en laskeysdel pasaporte y en lasbinding_keysdel RPR. Antes tomaban por algoritmo las claves Ed25519 y ML-DSA-65 del pasaporte, así que rechazaban bundles firmados con las claves del principal y daban por verificado, con una advertencia, un bundle cuyo pasaporte no enumeraba ninguna clave Ed25519, sin comprobar su firma. - SDK de TypeScript, Go y Rust: en toda firma compuesta, en todos los modos de verificación, el hueco
clásico debe contener Ed25519 y el hueco PQ ML-DSA-65 o SLH-DSA-192f, de una clave cuyo
algsea el mismo. Go rechaza también un bundle cuyo RPR, pasaporte, intención o decisión de política sea null o no sea un objeto, donde antes se saltaba todas las comprobaciones de ese artefacto, y TypeScript devuelveverified: falsepara un bundle así en lugar de lanzar una excepción. - Nivel de seguridad (especificación §14.2): el SDK de Go y el binding de
@dcp-ai/wasm(wasm_compute_security_tier) reconocíanpayment/transfer,financial,health,legalybiometric, que el esquema de la intención no permite, así que intenciones válidas según el esquemainitiate_payment,financial_datayhealth_dataeranroutineahí yelevateden los SDK de TypeScript, Python y Rust. Los cuatro SDK y el binding calculan ahora el mismo nivel, comprobado por los nuevos vectoressecurity_tierdetests/interop/v2/interop_vectors.json. - Servidor de verificación:
resolved_tierenPOST /verifyyPOST /v2/bundle/verify, y la comprobación del nivel de/v2/bundle/verify, tomabanintent.security_tiertal cual, así que un agente que declarabaroutinerebajaba su propio nivel, y leíanrisk_scorede la intención, cosa que el esquema no permite. Ahora usan la regla de §14.2 (lib/security-tier.js):risk_scorede la decisión de política del bundle,estimated_impact, valores desconocidos aelevatedy un nivel explícito que solo puede elevar el resultado./v2/intent/declarecalcularesolved_tierdel mismo modo. - Integración de OpenClaw:
dcp_verify_bundleydcp_log_actionusabanintent.security_tiertal cual (security_tier ?? computeSecurityTier(intent)), así que un nivel declarado podía rebajar el calculado, y no pasaban ninguna decisión de política. El nivel explícito ahora solo eleva el nivel. También se pasa la decisión de política, de modo querisk_scorecuenta en cuanto@dcp-ai/sdkla lea (la versión posterior a 2.1.2). - Denegación de servicio en los servicios de referencia, cuyo estado vive en memoria y no sobrevive a un reinicio:
- Servidor de verificación: los registros tienen un límite de tasa por cliente (
REGISTRATIONS_PER_HOUR, 20 por hora de forma predeterminada), la rotación solo conserva las 4 claves retiradas más recientes de un agente, y de un pasaporte registrado solo se almacenacreated_at. Un agente que rotaba una y otra vez, o una avalancha de registros, llenaba el registro de 10 000 claves (507 hasta un reinicio), y cada pasaporte firmado almacenado podía ocupar hasta 1 MB. Las claves descartadas siguen revocadas como lápidas y nunca pueden volver a registrarse, y un agente puede registrar como máximoMAX_KEYS_PER_AGENTclaves (64) en toda su vida. Una rotación descarta una clave retirada antes de comprobar el límite, así que sigue cabiendo en un registro lleno. La comprobación de la familia de claves al volver a registrarse ya no recorre el registro de forma cuadrática. - Registro de transparencia:
POST /gossip/exchangesolo acepta una cabecera de árbol bien formada y firmada por un par fijado, en un cuerpo de 16 KB como máximo, y conserva las últimas 100 alertas distintas, de modo que una cabecera de árbol reproducida no puede desplazar a las anteriores. Las cabeceras de árbol obtenidas por sondeo deben verificarse con la clave fijada del par, y un sondeo lee como máximo 16 KB, agota su tiempo tras un intervalo y nunca se solapa con el anterior. Antes, cualquiera podía llenar la lista de alertas con cadenas de 1 MB, nunca se verificaba ninguna cabecera de árbol de un par, y un par que no respondía acumulaba sondeos. - Servicio de revocación:
POST /v2/registertiene un límite de tasa por cliente y un tope deMAX_AGENTS(10 000 de forma predeterminada), y los campos almacenados están acotados, así que una avalancha de registros o de registros demasiado grandes ya no puede dejarlo sin memoria y borrar todas las revocaciones. - Servidor de verificación y servicio de revocación: registrar un
agent_idnuevo necesita el token del operador. Un pasaporte solo está autofirmado, así que cualquiera podía registrar primero elagent_idde una víctima, con sus propias claves y token, y después revocarlo de emergencia o dejar fuera al agente real con un 409. Cada reinicio volvía a exponer a ello todos losagent_id.GET /v2/check/kid/:kidresponde 404 (registered: false) para un kid que nunca registró, en lugar del veredicto de cualquier agente que quien llama nombrara en?agent_id=. - Servidor de verificación y servicio de revocación: las claves públicas y las firmas deben ser el base64
canónico de exactamente el tamaño de su algoritmo, y los esquemas V2 restringen
public_key_b64ysig_b64al alfabeto base64 y limitan su longitud. Los decodificadores permisivos se saltaban los espacios en blanco, así que una clave ML-DSA rellenada con 900 KB de espacios se registraba y se devolvía, y las firmas rellenadas se almacenaban y publicaba/v2/list. verifySignedBundleV2del SDK de TypeScript aplica el nivel de seguridad (especificación §14.3): verifica con el más estricto entre el modo de la política y el modo del nivel calculado a partir de la intención y la decisión de política firmadas, y lo informa endetails.resolved_tier. Antes, el modo salía solo delpassport.risk_tierdel propio agente, así que conDEFAULT_VERIFIER_POLICYun pasaporte que declarabarisk_tier: "low"obteníaclassical_onlyincluso para una intención de pago.- SDK de TypeScript:
AdaptiveRateLimiter.check,generateSessionExpiryyVerificationCache.setcuentan un nombre de nivel desconocido, o una clave del prototipo comoconstructor, comoelevated. El limitador de tasa daba a cada nombre inventado su propio contenedor,generateSessionExpirylanzaba una excepción conconstructor, y la caché nunca hacía caducar esas entradas. - Integración de OpenClaw:
dcp_declare_intentcalculaba el nivel que firma en la intención y en la decisión de política antes de puntuar la intención. Ahora pasa la puntuación de riesgo, que cuenta en cuanto@dcp-ai/sdkla lea (la versión posterior a 2.1.2). - Los verificadores de Go (
v2.VerifySignedBundleV2), Rust (v2::verify_bundle::verify_signed_bundle_v2y el bindingwasm_verify_signed_bundle_v2que hay trasverifyBundleen@dcp-ai/wasm) y Python (verify_signed_bundle_v2) aplican el nivel de seguridad como el de TypeScript: el modo es el más estricto entre el modo de la política y el del nivel, aplicado a la firma del bundle y a la firma de cada artefacto; un modo de política desconocido falla de forma cerrada; y el resultado informa del nivel y del modo. Los verificadores de Go y Rust no tenían política de verificación y aceptaban firmasclassical_onlypara cualquier intención. verify_signed_bundle_v2del SDK de Python: en toda firma compuesta, en todos los modos, el hueco clásico debe contener Ed25519 y el hueco PQ ML-DSA-65 o SLH-DSA-192f, de una clave cuyoalgsea el mismo, como en los SDK de TypeScript, Go y Rust. Conpq_onlyaceptaba ML-DSA-65 en el hueco clásico.- SDK de Go y de Rust: una firma compuesta debe llevar su miembro
pq,nullen el caso declassical_only(schemas/v2/composite_signature.schema.json). Decodificaban unpqausente comonully verificaban una firmaclassical_onlyasí, que los verificadores de TypeScript y Python rechazan.bundle_vectors.jsonlo fija conclassical_only_bundle_signature_without_pq. - Los verificadores V2 de Go y Rust atan la pista de auditoría al manifiesto firmado, como hacen los de TypeScript y
Python: recalculan
audit_merkle_root, compruebanaudit_county exigen que la cadena empiece enGENESIS. Antes, cualquiera podía sustituir las entradas de auditoría de un bundle firmado válidamente por una cadena fabricada y coherente consigo misma, y ambos seguían dándolo por verificado. - Los verificadores V2 de Go y Rust fallan de forma cerrada cuando no puede ejecutarse una comprobación del
manifiesto: un hash de artefacto ausente, como
rpr_hash, o un payload que falta o no puede canonicalizarse, es un error en lugar de una comprobación omitida. - Los verificadores V2 de Go y Rust comprueban el
payload_hashde cada artefacto frente a su payload, como hacen los de TypeScript y Python; antes lo ignoraban. - Los verificadores V2 de Go y Rust rechazan un bundle cuyo
manifest.session_expires_atha pasado, de modo que ya no se les puede reproducir una sesión caducada. El verificador de@dcp-ai/wasmlee el reloj desde JavaScript, porqueSystemTime::now()entra en pánico enwasm32-unknown-unknown. - SDK de Rust:
is_session_expiredcomparaba lexicográficamente las cadenas RFC 3339 con el reloj UTC, así que una marca de tiempo con desfase se juzgaba mal hasta en 14 horas: una sesión que terminaba en una hora, escrita-05:00, contaba como caducada, y una que terminó hace una hora, escrita+05:00, como todavía válida. Ahora analiza la marca de tiempo (parse_rfc3339_millis) y compara instantes, para la caducidad de la sesión y de las claves y en las funciones auxiliares de avisos. - Marcas de tiempo de caducidad en los cuatro verificadores: un
session_expires_ato unexpires_atde clave presente pero que no sea una marca de tiempo RFC 3339 con un desfaseZo±hh:mmse rechaza; ausente onullsigue significando que no caduca. TypeScript aceptaba cualquier cosa queDateno pudiera analizar ("n/a") o que analizara de forma permisiva ("2099-12-31"), y se saltaba unsession_expires_atvacío; Rust aceptaba"n/a"; Go se saltaba unexpires_atde clave que no fuera una cadena; Python lanzaba una excepción.isSessionExpiredeis_session_expiredcuentan un valor mal formado como caducado. - La validez de las claves cubre toda clave que puede firmar. Los cuatro verificadores rechazan una clave revocada o
caducada en las
binding_keysdel RPR del principal, que firman el RPR y pueden firmar el bundle; TypeScript y Python solo comprobaban las claves del pasaporte. Los verificadores de Go y Rust no comprobaban en absoluto el estado de las claves y ahora rechazan también las claves del pasaporte revocadas o caducadas. - Los cuatro verificadores rechazan una clave cuyo
statusesexpired, comorevoked. El esquema de entrada de clave permiteexpired, pero solo se comprobabarevoked, así que una clave marcada como caducada sinexpires_atseguía firmando. - Una pista de auditoría vacía debe tener la raíz de Merkle
sha256:seguida de 64 ceros, que los constructores de Go y Rust ya escribían. Los cuatro verificadores se saltabanaudit_merkle_rootcuando un bundle no tenía entradas de auditoría, así que podía contener cualquier valor.
Cambiado (incompatible)¶
- Servidor de verificación (
POST /v2/passport/register) y servicio de revocación (POST /v2/register): unagent_idnuevo necesitaAuthorization: Bearer $OPERATOR_TOKEN(401 si no); volver a registrarse sigue siendo autoservicio.GET /v2/check/kid/:kiddel servicio de revocación ignora?agent_id=y devuelve 404 para los kids no registrados. - Servidor de verificación: un agente puede registrar como máximo
MAX_KEYS_PER_AGENTclaves (64 de forma predeterminada) en toda su vida; las rotaciones posteriores reciben 409, y el agente tiene que registrar unagent_idnuevo. - Esquemas:
key_entry.public_key_b64y elsig_b64decomposite_signaturedeben coincidir con^[A-Za-z0-9+/]+={0,2}$y se limitan a 3456 y 66 476 caracteres. - Registro de transparencia: cada entrada de
GOSSIP_PEERSes<url>#<base64 Ed25519 public key>. Las cabeceras de árbol de pares sin clave fijada se ignoran, y/gossip/exchangelas rechaza (403); los logs de Docker Compose solo hacen gossip una vez fijadas sus claves (véasedocker/README.md). - SDK de Go:
BundleBuildInputacepta artefactos firmados (SignedPayload, véasev2.SignPayload) y entradas de auditoría[]AuditEventV2.BuildBundleV2envolvía payloads sin firmar, que los verificadores rechazan ahora (V2-8), y llenaba el bundle con entradas de auditoría vacías. - WASM (
wasm_build_bundle, ybuildBundleen@dcp-ai/wasm): cada artefacto es unSignedPayloaddewasm_sign_payload/signPayload, no un payload en bruto. Los bundles construidos con payloads en bruto no llevaban firmas de artefacto y ya no se verifican. - Servicio de revocación: se eliminaron
POST /v2/register-emergency-tokenyPOST /v2/register-key-expiryen favor dePOST /v2/register./revokeV1 necesita un agente registrado ahí, lo que exige una firma híbrida del pasaporte (Ed25519 + ML-DSA-65 o SLH-DSA-192f), así que los agentes solo Ed25519 (V1) ya no pueden publicar revocaciones. Un agente revocado ya no puede revocarse de nuevo (409). - Servidor de verificación y servicio de revocación: volver a registrar un
agent_id(por ejemplo, para sustituir el token de revocación de emergencia) necesita un pasaporte cuyocreated_atsea más reciente que el registrado, firmado por todas las claves activas del agente; un pasaporte sin token conserva el registrado. Un agente que ha perdido todas las claves activas de una familia (clásica o PQ) no puede volver a registrarse: la recuperación pasa por el token de revocación de emergencia o por el operador. - Registro de transparencia: los hashes de las hojas, las raíces y las pruebas de inclusión cambian con los
prefijos de RFC 6962, y
signature/public_keyde la STH son ahora Ed25519 en base64./addsolo acepta hashessha256:<64 hex>, y/anchordel anclaje 64 caracteres hexadecimales con un prefijosha256:opcional. - Servidor de verificación:
/v2/keys/rotateexigeauthorization_sigytimestamp(KeyRotationRecord).dcp rotate-keyydcp keys certifysiguen enviando una prueba de posesión simulada, que el servidor rechaza ahora. - SDK de Python: SLH-DSA-192f implementa ahora FIPS 205 (SLH-DSA puro, contexto vacío), como los
SDK de TypeScript, Go y Rust. Se basaba en el SPHINCS+ prenormativo de PQClean, que firma el
mensaje sin más en lugar de
0x00 || 0x00 || message, así que las firmas SLH-DSA no se verificaban entre SDK en ninguna dirección. Las firmas hechas porSlhDsa192fProviderendcp-ai2.8.2 y anteriores ya no se verifican. - SDK de Python: exige
pqcrypto>=1.0,<2, que ahora se usa para ML-DSA-65, SLH-DSA-192f y ML-KEM-768, en lugar del tope<1.0de 2.8.2 (pqcrypto1.0 cambió su API y eliminó SPHINCS+).dcp_ai.providersya no se traga unImportError, y las pruebas de interoperabilidad PQ ya no se omiten cuando falta un proveedor. - SDK de Python:
VerifierPolicyrechaza los campos desconocidos en lugar de descartarlos, y gana los campos que le faltaban (allow_classical_fallback_disable,warn_classical_only_deprecated,advisory_rejected_algs,require_session_expiry,max_session_duration_seconds,require_audience_binding,verifier_id). Antes,VerifierPolicy(require_audience_binding=True, verifier_id=...)perdía ambos campos sin avisar. Enverify_signed_bundle_v2, una política en forma de dict se valida como unVerifierPolicy(las claves desconocidas lanzan una excepción), y los campos que omite toman los valores predeterminados del modelo (hybrid_requiredpara todos los niveles de riesgo), no ellow→classical_onlyde la política predeterminada. Solorisk_overrides=Nonehace quedefault_modese aplique a todos los niveles. - El cegado exige
pii_salt, en los cuatro SDK.blindRpr(TypeScript) yblind_rpr(Python,ValueError) lanzan una excepción, yBlindRPR(Go) yblind_rpr(Rust) devuelven un error, cuando el RPR completo no tienepii_salto no tiene 64 caracteres hexadecimales en minúsculas. Hay que fijarla con la nueva función auxiliar antes de firmar el RPR: un RPR firmado sin ella tiene que volver a firmarse antes de poder cegarse. - La verificación de RPR cegados rechaza un RPR completo cuya
pii_saltestá presente pero no es una cadena de 64 caracteres hexadecimales en minúsculas, en los cuatro SDK. Antes, un registro cegado en otro lugar con una sal vacía o corta se verificaba sin ninguna advertencia, mientras que los registros sin sal se señalaban. - Resultados de la verificación de RPR cegados:
verify_blinded_rpr(Python) devuelve una nueva clavewarningsyBlindedRprCheckde Go tiene un nuevo campoWarnings, así que el código que compara resultados completos o construyeBlindedRprCheckcon literales sin nombres de campo debe cambiar. schemas/v2/responsible_principal_record.schema.jsonaceptapii_salt. El esquema tieneadditionalProperties: false, así que los verificadores que validan con el esquema anterior rechazan los RPR que llevan una sal: actualiza los verificadores antes de que los productores empiecen a fijarla.- SDK de Rust:
ResponsiblePrincipalRecordV2tiene un nuevo campo público,pii_salt: Option<String>, así que los literales de estructura deben fijarlo. - Cálculo del nivel de seguridad (especificación §14.2), todos los SDK:
risk_scorese lee de la decisión de política del bundle, nunca de la intención (el esquema de la intención no lo permite, así que el nivel nunca erastandardpara una intención válida).estimated_impactmediumeleva el nivel astandardyhighaelevated; un valor deaction_type,estimated_impactodata_classesausente, de tipo incorrecto o desconocido da como mínimoelevated; unintent.security_tierexplícito solo puede elevar el resultado. - TypeScript:
computeSecurityTier(intent, policyDecision?). - Python:
compute_security_tier(intent, policy_decision=None);compute_security_tier({})es ahoraelevated. Las integraciones de LangChain y CrewAI lo usan en lugar de sus propias copias. - Go:
SecurityTierInputganaEstimatedImpactySecurityTier, y su valor cero (o unDataClassesnil) es ahoraelevated.v2.SecurityTierInputFromPayloads(intent, policyDecision)construye la entrada a partir de payloads decodificados. - Rust:
compute_security_tier(intent, policy_decision: Option<&Value>).wasm_compute_security_tierycomputeSecurityTieren@dcp-ai/wasmaceptan una decisión de política opcional. - Cálculo del nivel de seguridad, todos los SDK y el servidor de referencia: una
risk_scorede la decisión de política que falta o no es un entero de 0 a 1000 cuenta como desconocida, así que el nivel es como mínimoelevated, como un valor desconocido de la intención. Un valor no numérico contaba antes como 0, y los demás números se comparaban tal cual, así que-1dabaroutiney1e30dabamaximum(ahoraelevated, con el mismo modohybrid_required). Sin decisión de política la puntuación sigue siendo 0. En Go,SecurityTierInputFromPayloadsfijaRiskScorea -1 para un valor así, yComputeSecurityTiertrata cualquierRiskScorefuera de 0..1000 como desconocido. verifySignedBundleV2del SDK de TypeScript: los bundles que hoy pasan con una firmaclassical_onlyse rechazan cuando su intención es sensible:initiate_payment,pii,financial_data,health_data,credentialsochildren_data,estimated_impact: "high", unarisk_scorede la decisión de política de 500 o más, o un valor fuera del esquema de la intención. Fírmalos con una firma híbrida (pq_over_classical). El verificador raíz (lib/verify.js,dcp verify-bundle, el servidor) lo recibe con la próxima versión de@dcp-ai/sdk.- Los verificadores de Go, Rust y Python rechazan los mismos bundles
classical_only.VerifySignedBundleV2de Go yverify_signed_bundle_v2de Rust aplican elDEFAULT_VERIFIER_POLICYde TypeScript en lugar de ninguna política;wasm_verify_signed_bundle_v2acepta un JSON de política opcional (unVerifierPolicy) y, con él, rechaza los bundles V1. Su política solo puede diferir de la predeterminada endefault_mode,risk_overridesy los campos de la fase 3 (advisory_rejected_algs,warn_classical_only_deprecated,allow_classical_fallback_disable, aplicados como en TypeScript): cualquier otro cambio, o un campo desconocido en el JSON delVerifierPolicyde Rust, hace fallar la verificación en lugar de ignorarse. verify_signed_bundle_v2del SDK de Python: los errores y advertencias sobre la firma del bundle empiezan porbundle:, un artefacto null o que no es un objeto se informa como<name>: not a SignedPayload object, y un modo de política desconocido falla conUnknown verification mode in verifier policyantes de comprobar ninguna firma, como en TypeScript.
Añadido¶
generatePiiSalt(TypeScript),generate_pii_salt(Python, Rust) yGeneratePIISalt(Go). Los tipos RPR de los cuatro SDK,schemas/v2/responsible_principal_record.schema.jsonyapi/proto/dcp.protollevanpii_salt.ResponsiblePrincipalRecordV2de Python omite unapii_saltsin fijar al serializarse, porque el esquema no tiene null para ella.- Los RPR cegados heredados sin sal siguen verificándose, con una advertencia de obsolescencia: los resultados de
verifyBlindedRpr(TypeScript),verify_blinded_rpr(Python) yVerifyBlindedRPR(Go) llevan una listawarnings.BlindedRprCheckde Rust conserva sus dos campos, porque un campo público nuevo rompería a quienes lo construyen o desestructuran; ahí, unapii_saltausente o null en el RPR completo marca un registro heredado. @dcp-ai/openclaw:dcp_identity_setupfirma su RPR con unapii_salt, de modo que el registro pueda cegarse más adelante sin volver a firmarlo.- Servicio de revocación:
REVOCATION_STATE_PATHconserva los registros y las revocaciones en un archivo (predeterminado: solo en memoria). Las escrituras se añaden como líneas JSON con fsync, y el archivo se reproduce y compacta mediante un renombrado atómico al arrancar; el servicio se niega a arrancar si la ruta no tiene permiso de escritura o el archivo supera 512 MB. La pila de Docker Compose lo fija, en un volumen. - Servidor de verificación:
MAX_REGISTERED_KEYSyMAX_KEYS_PER_AGENTdimensionan el registro de claves. effectiveVerificationMode(TS),effective_verification_mode(Python, Rust) yEffectiveVerificationMode(Go): el más estricto entre un modo de política y el modo de un nivel, conpq_onlysin cambios (especificación §14.3). Vectores compartidos ensecurity_tier.effective_mode.- SDK de Go:
v2.VerifySignedBundleV2WithPolicy(registry, json, policy)yv2.DefaultVerifierPolicy(). SDK de Rust:verify_signed_bundle_v2_with_policyeimpl Default for VerifierPolicy(la política predeterminada de TypeScript). LosVerifierPolicyde Go y Rust ganan los campos de la fase 3 deschemas/v2/verifier_policy.schema.json, de modo que elDEFAULT_VERIFIER_POLICYde TypeScript se decodifica en ellos tal cual.@dcp-ai/wasm:verifyBundle(signedBundle, policy?)y el tipoVerifierPolicy. Losdetailsdel resultado de Python gananresolved_tier. tests/interop/v2/bundle_vectors.json:must_reject_bundles, bundles solo clásicos con intenciones sensibles desde un pasaporte de riesgo bajo que todo verificador debe rechazar en todos los modos, y un control rutinario solo clásico con su veredicto esperado por modo. Las pruebas de TS, Go, Rust y Python los ejecutan, y Python ejecuta ahora el archivo completo.default_verifier_policylleva elDEFAULT_VERIFIER_POLICYde TypeScript, que todo SDK debe aceptar como política.tests/interop/v2/bundle_vectors.json: casos de rechazo para unpayload_hasherróneo, entradas de auditoría sustituidas o truncadas, una cadena de auditoría que no empieza enGENESIS, un manifiesto sinrpr_hash, una sesión caducada, y claves de pasaporte y de vinculación revocadas o caducadas.@dcp-ai/wasm:npm testejecutaverifyBundledesde el paquete compilado sobretests/interop/v2/bundle_vectors.jsonen Node, y la CI lo ejecuta. Antes solo comprobaba los tipos, y las pruebas wasm32 de Rust solo se compilan, así que el verificador wasm nunca se ejecutaba en la CI.tests/interop/v2/interop_vectors.jsonrfc3339: el instante de marcas de tiempo RFC 3339 con desfases y fracciones, y los valores mal formados que todo verificador rechaza.bundle_vectors.jsonañade caducidades mal formadas y con desfase, y su bundle firmado lleva caducidades en un futuro lejano escritas con desfases.parseRfc3339de TypeScript,parse_rfc3339de Python yparse_rfc3339_millisde Rust son los analizadores que usan los verificadores.- SDK de TypeScript:
blindRpr,verifyBlindedRpr,isBlindedRprycomputePiiHashse exportan desde el punto de entrada del paquete. Estaban implementadas pero eran inaccesibles desde@dcp-ai/sdk2.1.2 y anteriores; Python, Go y Rust ya las exportaban. lib/verify.jsexportaverifyCompositeSig(una firma compuesta sobre un payload, con las claves del firmante buscadas por kid derivado) y suv2Registry;lib/http.jscontiene las funciones auxiliares de análisis del cuerpo, IP del cliente y token del operador que compartenserver/yservices/. Los servicios de anclaje y de registro de transparencia tienen suitesnode:test, que se ejecutan en la CI con las existentes.- SDK de Rust: el verificador de bundles V2 salió de la característica
wasmy pasó adcp_ai::v2::verify_bundle::verify_signed_bundle_v2, de modo que las compilaciones nativas ycargo testlo ejecutan;wasm_verify_signed_bundle_v2delega en él. tests/interop/v2/bundle_vectors.json: un bundle firmado que todo verificador V2 debe aceptar, y mutaciones (firma PQ del bundle falsificada, firmas de artefacto falsificadas o mal atribuidas, algoritmos en el hueco equivocado, artefactos null o que no son objetos) tras las cuales debe rechazarlo con un error que nombre el bundle o el artefacto, comprobadas por las suites de TypeScript, Go y Rust. Se regeneran consdks/typescript/src/__tests__/generate-bundle-vectors.ts.- SDK de Go:
v2.SignPayloadfirma un artefacto en unSignedPayload: una firma compuesta, o una solo clásica cuando no se da ninguna clave PQ. - SDK de Python:
MlKem768Provider(FIPS 203), la primitiva ML-KEM-768 que los demás SDK ya tenían. El handshake DCP-04 de Python todavía no ejecuta el intercambio de claves híbrido X25519 + ML-KEM-768. - SDK de Python: bundles V2.
BundleBuilderV2,sign_bundle_v2,sign_bundle_v2_classical_only,verify_signed_bundle_v2,DEFAULT_VERIFIER_POLICYyPQ_ONLY_VERIFIER_POLICYportanbuilder-v2.ts,signer-v2.tsyverify-v2.tsde TypeScript. El verificador también comprueba la firma compuesta de cada artefacto (VERIFICATION.md V2-8), rechaza una firma PQ presente pero no válida en todos los modos (V2-7) y falla de forma cerrada ante un modo de verificación desconocido. A diferencia del verificador de TypeScript, ata los checkpoints PQ del bundle a los ID depq_checkpointsdel manifiesto, y en la comprobación demax_session_duration_secondslimita elsignature.created_atsin firmar al momento de la verificación: un valor reescrito puede acortar esa comprobación hasta la vida restante de la sesión, no hasta cero. Cerrarlo necesita un inicio de sesión firmado, que el manifiesto no lleva. El contenido de los checkpoints (V2-12) yaudit_merkle_root_secondarysiguen sin verificarse. tests/nist-kat/ml-kem-768ytests/nist-kat/slh-dsa-192f: vectores ACVP del NIST (decapsulación y comprobaciones de claves de ML-KEM-768, verificación de firmas SLH-DSA-SHA2-192f).tests/interop/v2/cross_sdk_vectors.json: vectores de SLH-DSA-192f, ML-KEM-768 y bundles V2 firmados producidos por los SDK de TypeScript y Python, cada uno verificado por el otro.
Corregido¶
- Los ejemplos de conformidad V2 (
scripts/generate-v2-examples.js) usan unhuman_idopaco en lugar de uno derivado del nombre legal, que §12.1 prohíbe ahora, y su RPR lleva unapii_salt. - SDK de Python:
BundleManifest.pq_checkpointscontiene los ID de los checkpoints, como enschemas/v2/bundle_manifest.schema.json, y el modelo ganasession_expires_ateintended_verifier.
Documentación¶
spec/DCP-AI-v2.0.md§12.1 especifica el compromiso con sal, la sal privada que solo se divulga con el RPR completo, la supresión mediante el borrado del RPR completo, unhuman_idopaco que no debe (MUST NOT) contener datos personales ni derivarse de ellos, y la obsolescencia de los compromisos sin sal.spec/CANONICALIZATION_PROFILE.md,spec/DCP-AI-v2.0.md, el README del SDK de WASM (todos los idiomas),docs/NIST_CONFORMITY.mdy los comentarios de los canonicalizadores de TypeScript, Python y Rust describendcp-jcs-v1como basado en RFC 8785, no como un subconjunto estricto de él ni como RFC 8785 sin más: la regla 1 ordena las claves por punto de código, RFC 8785 por unidades de código UTF-16. El perfil no cambia.- El README del SDK de TypeScript (todos los idiomas) y
docs/API_REFERENCE.mdenumeran las firmas reales de las funciones de firma compuesta, bundle, presentación, checkpoint PQ, A2A, errores, revocación de emergencia, recuperación de claves, hash y CBOR. docs/API_REFERENCE.md: el proceso de verificación V2 enumera las comprobaciones de firma por artefacto, y la nota de limitaciones conocidas sobre V2-7 y V2-8 se aplica ahora solo a@dcp-ai/sdk2.1.2 y anteriores.- Los README de los SDK de Go y WASM (todos los idiomas) y
docs/MIGRATION_V1_V2.mdfirman cada artefacto antes de construir el bundle, y enumeran las claves de firma en el pasaporte y en el RPR. spec/DCP-AI-v2.0.md§14.2 describe el cálculo del nivel anterior, §14.3 y §14.4 describenresolved_tiercomo informativo (§14.2 no lo lee), yspec/VERIFICATION.mdcalcula el nivel como en §14.2. Los README de los SDK (todos los idiomas),docs/API_REFERENCE.md, las guías de inicio rápido, de migración y de creación de agentes, ysdks/wasm/example.htmlpasan la decisión de política a la función del nivel y usan los valores del esquema de la intención.spec/VERIFICATION.mdV2-7 yspec/DCP-AI-v2.0.md§7.1 establecen que una firma PQ presente debe verificarse en todos los modos, como ya hacen los verificadores.docs/API_REFERENCE.mddocumentadetails.resolved_tiery el modo deverifySignedBundleV2que tiene en cuenta el nivel, y el README del SDK de TypeScript (todos los idiomas) lo describe.docs/MIGRATION_V1_V2.md§6.3 ya no muestra un comandodcp migrate-check, que la CLI no tiene, ni unarisk_scoreen la intención; valida los bundles migrados condcp verify-bundle --policy hybrid_required.spec/DCP-AI-v2.0.md§14.3 convierte la aplicación del nivel en un MUST para los verificadores V2 (§20.1): el modo es el más estricto entre el modo de la política y el modo del nivel.spec/VERIFICATION.mdcalcula el nivel y el modo efectivo en el paso V2-9 y aplica con él la política en el paso V2-10, donde la comprobación del nivel era antes opcional.docs/OPERATOR_GUIDE.mdydocs/SECURITY_MODEL.mddescriben el modo que tiene en cuenta el nivel, y los README de los SDK de Go, Python, Rust y WASM (todos los idiomas) describen la política de verificación.- La §18 de la especificación (proceso de verificación),
docs/OPERATOR_GUIDE.mdy el paso 8 de la guía de migración describen la aplicación del nivel tal como la implementan los verificadores. La guía del operador ya no dice que el nivel máximo verifique los checkpoints PQ, cosa que todavía no hace ningún verificador (V2-12), y dice lo que aplica el servidor de referencia hasta que se actualice su dependencia de@dcp-ai/sdk. spec/VERIFICATION.mdV2-3, V2-4 y V2-13 detallan estas comprobaciones:payload_hash, hashes del manifiesto que fallan de forma cerrada, la pista de auditoría atada al manifiesto, la caducidad de la sesión y la validez de lasbinding_keysdel RPR.docs/API_REFERENCE.mdenumera las claves de vinculación en el paso de validez de las claves.
[2.9.0] - 2026-10-21¶
Versiones de paquetes¶
dcp-ai(PyPI) 2.8.1 → 2.8.2: solo correcciones.@dcp-ai/sdk(npm) 2.1.1 → 2.1.2: solo correcciones.github.com/dcp-ai-protocol/dcp-ai/sdks/go/v2v2.8.1 → v2.9.0: añadev2.MarshalCanonical.dcp-ai(crates.io) 2.8.1 → 2.9.0: OpenTelemetry 0.32, incompatible para la característica opcionalotlp(véase «Cambiado (incompatible)»).- Sin cambios:
@dcp-ai/wasm2.0.0,@dcp-ai/cli2.0.0 y los paquetes de integración.
Seguridad¶
- El verificador de referencia (
lib/verify.js),dcp verify-bundley el servidor de verificación verifican ahora los bundles V2 normativos (schemas/v2/signed_bundle.schema.json) delegando enverifySignedBundleV2de@dcp-ai/sdk, tras la validación con JSON Schema. Antes, los bundles producidos por cualquier SDK se rechazaban como «Invalid signed bundle format»,dcp verify-bundleimprimíaV2 BUNDLE STRUCTURE VALID(salida 0) para bundles falsificados, y el servidor solo comprobaba la estructura. El sobre prenormativo (dcp_version/bundle_manifesten el nivel superior) se rechaza. El servidor verifica con elVERIFIER_POLICYque anuncia (modo, algoritmos aceptados y rechazados por avisos), y la fijación de una clave pública se rechaza en el modopq_only, que omite el firmante clásico. dcp sign-bundle --compositeemite unSignedBundleV2normativo; el kid de ML-DSA-65 se derivaba antes de un hash de la clave secreta. Se eliminótools/bundle_sign.js --v2.- Servicio de revocación: una revocación de emergencia ya no informa de que todos los kids de todos los agentes
están revocados (
GET /v2/check/kid/:kidacepta ahora?agent_id=), y una codificación porcentual mal formada devuelve 400 en lugar de hacer caer el proceso.
Cambiado (incompatible)¶
lib/verify.js(elmaindel paquete raíz):verifySignedBundlees ahoraasyncy acepta un cuarto argumentooptions({ policy, mode }); los resultados llevan siempre un arrayerrors. Se actualizaron la GitHub Action compuestaverify-bundle,bin/dcp.js,server/ytools/bundle_verify.js.tools/bundle_verify.jsya no acepta un argumento de clave pública PQ.- SDK de Rust, característica opcional
otlp: OpenTelemetry 0.24 → 0.32. OtlpHandles.tracer_provideres ahoraSdkTracerProvideryOtlpHandles.traceresSdkTracer, siguiendo los cambios de nombre del proyecto original.- La característica ya no arrastra
tokio: el puente exporta desde los hilos en segundo plano del SDK con el cliente bloqueante dereqwest. - Los errores con el prefijo
tracer install:dicen ahoratrace exporter:.
Añadido¶
- SDK de Go:
v2.MarshalCanonical(obj), el codificadordcp-jcs-v1sin la regla de solo enteros. ElCanonicalizeV1 y el codificador de prueba de posesión lo usan ahora.
Corregido¶
- SDK de Go: la canonicalización ya no escapa
<,>,&ni U+2028/U+2029, y emite-0como0(reglas 3 y 4 dedcp-jcs-v1) en las rutas V2, de prueba de posesión y V1. - SDK de TypeScript: las claves de los objetos se ordenan por punto de código Unicode (regla 1), no por unidades de código UTF-16.
- SDK de Python:
pqcryptose limita por debajo de 1.0. La versión 1.0 eliminó los módulospqcrypto.sign.ml_dsa_*, así que las instalaciones nuevas dedcp-ai2.8.1 no pueden importar el proveedor ML-DSA-65. tests/interop/v2/interop_vectors.json: vectores de casos límite para el cero negativo, caracteres HTML, U+2028/U+2029, escapes de caracteres de control y ordenación de claves con caracteres de los planos suplementarios de Unicode.tools/conformance.js: la entradaedge_casesya no pasa sin comprobar nada, se eliminó el bloque de separación de dominio que nunca se ejecutaba, y los bundles firmados V2 (válidos, manipulados, falsificados, sin firma PQ, con clave fijada, heredados) se comprueban con el verificador de referencia.
Dependencias¶
- Se cerraron las 95 alertas abiertas de Dependabot (11 críticas, 54 altas). #22 sustituyó a las 15 PR de Dependabot (#5–#21); #24 actualizó grpc.
- SDK de TypeScript,
@dcp-ai/express,@dcp-ai/openclaw(desarrollo): - vitest y
@vitest/coverage-v84.1.12, actualizados juntos. - vite 7.3.7, fijado mediante
overrides. - postcss 8.5.31, nanoid 3.3.20, source-map-js 1.2.2, esbuild 0.28.2, fast-uri 3.1.9.
- Lockfile de ejecución de Express: proxy-addr 2.0.8, qs 6.16.1, body-parser 2.3.0.
- Arnés raíz: fast-uri 3.1.9 y la nueva dependencia
@dcp-ai/sdk. - SDK de Go:
golang.org/x/cryptov0.55.0,golang.org/x/netv0.58.0,google.golang.org/grpcv1.83.2,go.opentelemetry.io/otel/*v1.46.0. - SDK de Rust: opentelemetry, opentelemetry_sdk y opentelemetry-otlp 0.32.
opentelemetry_sdk0.32.1 corrige una asignación de memoria sin límite en la propagación de W3C Baggage. - CI:
- Nuevo trabajo de pruebas de integración para express y openclaw.
- El trabajo de Rust compila y prueba
--features otlp. - El trabajo de Go lee su versión de
go.mod. - El bundle V2 generado se verifica con la CLI.
- Se ejecutan las pruebas del servidor y de la revocación.
Documentación¶
spec/VERIFICATION.md:- La lista de comprobación V2 apunta ahora a
schemas/v2/signed_bundle.schema.json(los esquemas*_v2citados nunca existieron). - Se eliminó la vinculación inexistente
independent. - Se nombra la implementación de referencia.
spec/CANONICALIZATION_PROFILE.md§3 ganó filas para el escapado de cadenas y el orden de las claves por punto de código. Esto aclara las reglas 1 y 3; el perfil no cambia.- La documentación del servidor, del operador, de migración, de referencia de la API, de CI y de bundles describe ahora la verificación V2 real, la sintaxis real de la CLI y las API del SDK que existen:
- Las tablas de API de la guía de migración nombraban varias funciones que no existen.
- El README del servidor mostraba el sobre prenormativo.
- El README del SDK de Python (todos los idiomas) ya no afirma que admita ML-KEM-768 ni bundles V2, y la guía de inicio rápido de CrewAI marca su fragmento V2 como API prevista.
- Traducciones y documentos del proyecto:
- 11 integraciones, incluida Agno; enlaces al perfil de canonicalización y a la guía de inicio rápido de Agno.
- Fragmentos de instalación de Rust para esta versión y fijaciones
@latestde Go. - Versiones admitidas por paquete en
SECURITY.md, y las versiones v2.8.x y v2.9.0 enROADMAP.md. - El DOI de concepto de Zenodo en
CITATION.cff.
[2.8.1] - 2026-04-26¶
Integración del campo canonicalization_profile en los cuatro SDK¶
v2.8.0 declaró el campo canonicalization_profile en
schemas/v2/bundle_manifest.schema.json y lo describió en
spec/CANONICALIZATION_PROFILE.md § 4, pero ningún SDK escribía ni leía realmente el campo: las estructuras
tipadas BundleManifest se distribuyeron sin él, y los verificadores lo ignoraban. v2.8.1 cierra ese hueco de
principio a fin.
- TypeScript (
@dcp-ai/sdk2.1.0 → 2.1.1): la interfazBundleManifestdesdks/typescript/src/types/v2.tsganacanonicalization_profile?: 'dcp-jcs-v1'.BundleBuilderV2.build()desdks/typescript/src/bundle/builder-v2.tsemite ahoracanonicalization_profile: "dcp-jcs-v1"en cada manifiesto que produce.verifyV2Bundledesdks/typescript/src/core/verify-v2.tsacepta el campo ausente (suponedcp-jcs-v1), acepta"dcp-jcs-v1"de forma explícita y rechaza cualquier otro valor con"Unknown canonicalization_profile: <value>". - Python (
dcp-ai2.8.0 → 2.8.1):BundleManifestdesdks/python/dcp_ai/v2/models.pyganacanonicalization_profile: Literal["dcp-jcs-v1"] | None = "dcp-jcs-v1". Pydantic impone el rechazo de valores desconocidos al analizar; la ausencia activa el valor predeterminado. No hace falta un cambio aparte en el verificador porque la validación está en el modelo. - Go (
sdks/go/v2.8.0→v2.8.1): la estructuraBundleManifestdesdks/go/dcp/v2/types.goganaCanonicalizationProfile stringconjson:"canonicalization_profile,omitempty".BuildBundleV2desdks/go/dcp/v2/bundle_builder.golo fija a"dcp-jcs-v1"en cada manifiesto que produce.VerifySignedBundleV2desdks/go/dcp/v2/verify_bundle.gorechaza ahora los bundles cuyo manifiesto lleve un valor decanonicalization_profiledistinto de"dcp-jcs-v1". - Rust (
dcp-aien crates.io 2.8.0 → 2.8.1):BundleManifestdesdks/rust/src/v2/types.rsganapub canonicalization_profile: Option<String>con#[serde(skip_serializing_if = "Option::is_none")].wasm_build_bundledesdks/rust/src/lib.rsfija"canonicalization_profile": "dcp-jcs-v1"en el manifiesto que emite.
Especificación y documento del perfil¶
spec/DCP-AI-v2.0.md§ 9 (manifiesto del bundle) enumera ahoracanonicalization_profilejunto a los demás campos del manifiesto, con una nota en línea según la cual un campo ausente debe (MUST) suponerse equivalente a"dcp-jcs-v1".spec/CANONICALIZATION_PROFILE.md§ 2: la regla 6 (tratamiento de undefined / null) se reescribe como una tabla entre lenguajes que enumera explícitamente cómo se corresponden los valores del host de TypeScript, Python, Go y Rust con el formato de intercambio JSON, tanto en las posiciones de objeto como en las de array.spec/CANONICALIZATION_PROFILE.md§ 2: una nueva regla 9 deja explícitamente fuera de alcance la normalización Unicode (NFC, NFD, NFKC, NFKD): el canonicalizador emite las cadenas byte a byte tal como las entrega el analizador del host, y un perfil futuro (dcp-jcs-v2o posterior) puede (MAY) fijar una forma de normalización. Se cita RFC 8259 § 8.1 como la opción convencional (NFC) para la normalización en la capa de aplicación.- La regla 9 de
spec/CANONICALIZATION_PROFILE.mdfija también el motivo para no distribuir fixtures de interoperabilidad conNaN/±Infinity: esos valores no forman parte del formato de intercambio de RFC 8259, así que la mayoría de los analizadores los rechazan antes de llegar al canonicalizador. El rechazo se verifica con pruebas unitarias de cada SDK en lugar de con fixtures compartidos.
Compatibilidad de comportamiento¶
El nuevo campo es aditivo y opcional en la serialización:
- Un bundle producido por
v2.8.1llevacanonicalization_profile: "dcp-jcs-v1"en su manifiesto. Los verificadores anteriores que no conocen el campo lo tratarán como una propiedad desconocida, lo cual es correcto porque el JSON Schema declara el campo compatible conadditionalProperties(está entre laspropertiesy esoptional). - Un bundle producido por
v2.8.0o anterior no tiene el campocanonicalization_profile. Los verificadoresv2.8.1lo aceptan: segúnspec/CANONICALIZATION_PROFILE.md§ 4, un campo ausente debe (MUST) suponerse equivalente a"dcp-jcs-v1". - Un bundle que lleva
canonicalization_profilecon un valor desconocido (p. ej.,"dcp-jcs-v2") lo rechazan todos los SDKv2.8.1. Este es el punto de enganche de compatibilidad futura para perfiles futuros: un verificador que no se ha actualizado a un perfil nuevo debe (MUST) rechazarlo en lugar de aceptar sin avisar un manifiesto que no puede validar.
La firma del bundle cubre canonical(manifest), así que añadir el
campo cambia el hash del manifiesto de cualquier bundle producido de nuevo respecto
al que se habría producido con v2.8.0. Los bundles producidos
con v2.8.0 y anteriores, y sus firmas almacenadas, siguen siendo válidos
con los verificadores v2.8.1.
Versiones actualizadas¶
dcp-ai(PyPI) 2.8.0 → 2.8.1dcp-ai(crates.io) 2.8.0 → 2.8.1github.com/dcp-ai-protocol/dcp-ai/sdks/go/v2v2.8.0 → v2.8.1@dcp-ai/sdk(npm) 2.1.0 → 2.1.1: primer cambio de código de TypeScript desde 2.1.0 (la versión 2.8.0 fue solo una edición de docstrings)
[2.8.0] - 2026-04-26¶
Añadido — Perfil de canonicalización dcp-jcs-v1¶
Una comparación pública con otro protocolo sacó a la luz tres asimetrías reales
en cómo canonicalizaban JSON los cuatro SDK: 1.0/1.00
los rechazaban Python y Rust pero los aceptaban TypeScript y Go;
1e2 lo rechazaba solo Rust; y undefined no tenía tratamiento formal
en la especificación. El resumen «RFC 8785 + solo enteros» de
spec/DCP-AI-v2.0.md § 15 era correcto hasta donde llegaba, pero no
fijaba los casos que JCS deja a criterio de la implementación. Esta versión los cierra
todos.
- Nuevo documento normativo:
spec/CANONICALIZATION_PROFILE.mdformaliza el perfildcp-jcs-v1con ocho reglas numeradas, una tabla de aceptación de casos límite que cubre 22 entradas, un contrato de tratamiento de undefined que documenta la asimetría propia de TypeScript (se omite en los objetos y se serializa comonullen los arrays), y una política de versiones para perfiles futuros. - Campo del manifiesto del bundle:
canonicalization_profile: "dcp-jcs-v1"se añade aschemas/v2/bundle_manifest.schema.jsoncomo campo opcional. Los verificadores que encuentren un bundle sin él deben (MUST) suponerdcp-jcs-v1. Los perfiles futuros harán el campo obligatorio. - Python (
dcp-ai2.7.1 → 2.8.0):assert_no_floatsacepta ahora números en coma flotante cuyo valor es un entero finito (1.0,1e2,1.00); se normalizan a la forma entera antes dejson.dumps. Los números no enteros (0.1),NaNy los infinitos siguen rechazándose. - Rust (
dcp-ai2.7.0 → 2.8.0):assert_no_floatsacepta ahoraNumber(f64)cuyofract()es cero. Una nueva función auxiliarformat_numberencanonical_recurseemite los números en coma flotante de valor entero sin punto decimal ni exponente, igual byte a byte que los demás SDK. - Go (
sdks/go/v2.7.0→v2.8.0): el comportamiento ya estaba alineado (comprobaciónv != math.Trunc(v)). Solo cambio de versión. - TypeScript (
@dcp-ai/sdksigue en 2.1.0): el comportamiento ya estaba alineado. El docstring desdks/typescript/src/core/canonicalize.tsformaliza la asimetría entre undefined y null.
Añadido — fixtures de interoperabilidad para casos límite¶
tests/interop/v2/interop_vectors.json incluye ahora
canonicalization.edge_cases con 14 vectores que cubren la conservación de null
(con clave, dentro de arrays), números en coma flotante de valor entero,
ceros finales, notación científica, rechazo de fracciones, rechazo de valores no finitos,
ordenación de claves por punto de código (incluida é), nulls anidados,
contenedores vacíos, objetos muy anidados y arrays con null. La prueba de
interoperabilidad de cada SDK carga el mismo bloque y comprueba una salida idéntica byte a byte
(o el mismo error) para cada vector. La CI ejecuta los cuatro en
paralelo en cada PR; una divergencia bloquea la publicación.
Versiones actualizadas¶
dcp-ai(PyPI) 2.7.1 → 2.8.0dcp-ai(crates.io) 2.7.0 → 2.8.0github.com/dcp-ai-protocol/dcp-ai/sdks/go/v2→ v2.8.0@dcp-ai/sdk(npm) sigue en 2.1.0: TypeScript ya implementaba el perfil; esta versión solo añade la formalización en el docstring.
Compatibilidad de comportamiento¶
Los bundles producidos por versiones anteriores siguen siendo válidos. El cambio es
aditivo para los SDK que antes rechazaban 1.0/1.00/1e2:
las entradas que antes lanzaban TypeError/Err ahora tienen éxito y producen
la misma salida canónica que los demás SDK ya producían. Ninguna
entrada que antes se canonicalizaba con éxito se canonicaliza
de otra forma en 2.8.0.
[2.7.1] - 2026-04-23 (solo Python)¶
Corregido¶
- A
dcp-ai(PyPI) 2.7.0 le faltabacryptographycomo dependencia obligatoria. El paquete lo necesita la capa de sesión A2A de DCP-04 introducida en 2.5.0 para el cifrado AES-256-GCM. Las instalaciones nuevas conpip install dcp-ai==2.7.0no traíancryptography, así que tres pruebas de sesión A2A fallaban en un entorno limpio (test_encrypt_decrypt_round_trip,test_tamper_ciphertext_rejected,test_session_id_mismatch). 2.7.1 añadecryptography>=41.0a las dependencias obligatorias del wheel.
Rust (dcp-ai en crates.io) y Go (sdks/go/v2) no se ven afectados y siguen en
2.7.0: el paquete cryptography de Python solo importa para el wheel de Python.
[2.7.0] - 2026-04-23¶
Añadido — Códigos de error canónicos + detección del formato de intercambio en PY / GO / RS¶
Porta el enum DcpErrorCode y la maquinaria asociada DcpError / DcpProtocolError
de TS a Python, Rust y Go, además de la función auxiliar ligera
detect_wire_format, que encamina un flujo de bytes entrante a
"json" o "cbor" según el primer byte.
Todos los SDK coinciden ahora en los mismos 38 códigos de error canónicos, que cubren:
errores de esquema (E001-E099), errores de firma (E100-E199), errores de hash y cadena
(E200-E299), errores de identidad (E300-E399), errores de política (E400-E499),
errores de sesión (E500-E599), errores A2A (E600-E699), limitación de tasa
(E700-E799) y errores internos (E900-E999). Cada código lleva un mensaje canónico
y un indicador retryable, para que los clientes puedan implementar una lógica de reintento
que coincida con la política de forma coherente en todos los SDK.
Python (dcp-ai 2.6.0 -> 2.7.0): nuevo módulo dcp_ai.v2.error_codes
con DcpErrorCode (str-enum), la dataclass DcpError,
la excepción DcpProtocolError, create_dcp_error, is_dcp_error,
detect_wire_format y la correspondencia ERROR_DESCRIPTIONS.
Rust (dcp-ai 2.6.0 -> 2.7.0): dcp_ai::v2::error_codes con el
enum DcpErrorCode (Display → el identificador estable DCP-E###),
la estructura DcpError, create_dcp_error y detect_wire_format.
Go (sdks/go/v2.6.0 -> v2.7.0): el tipo cadena v2.DcpErrorCode con
37 constantes Err*, la estructura v2.DcpError que implementa la interfaz
error, CreateDcpError, IsDcpError, DetectWireFormat.
Decisión sobre el alcance del codificador/decodificador CBOR¶
El codificador/decodificador CBOR completo sigue siendo solo de TypeScript en 2.x. Portar una
implementación determinista de RFC 8949 a tres SDK más es un proyecto considerable
que merece su propio hito; detect_wire_format es la
única función auxiliar que todo SDK necesita hoy para encaminar payloads CBOR hacia una
biblioteca existente del ecosistema (p. ej., cbor2 en Python,
github.com/fxamacker/cbor/v2 en Go, ciborium en Rust). Una versión
v3.x dedicada añadirá un port canónico.
Recuento de pruebas¶
- Python: 231/231 superadas (sin cambios; superficie de importación verificada)
- Rust: 152/152 superadas (+5 nuevas)
- Go: 115/115 superadas (+8 nuevas)
Versiones actualizadas¶
dcp-ai(PyPI) 2.6.0 -> 2.7.0dcp-ai(crates.io) 2.6.0 -> 2.7.0github.com/dcp-ai-protocol/dcp-ai/sdks/go/v2-> v2.7.0
[2.6.0] - 2026-04-23¶
Añadido — RPR cegado, autorización multiparte y funciones auxiliares de avisos en Rust + Go¶
Porta a Rust y Go tres funcionalidades exclusivas de Python, y cierra la diferencia señalada en la comparación, donde Python iba por delante de los demás SDK que no son TS.
- RPR cegado (Gap #2):
compute_pii_hash,blind_rpr,verify_blinded_rpr,is_blinded_rpr. - Autorización multiparte (Gap #5):
verify_multi_party_authorization MultiPartyPolicycon las cuatro políticas de operación predeterminadas (revoke_agent,rotate_org_key,change_jurisdiction,modify_recovery_config).- Funciones auxiliares de avisos sobre algoritmos (Gap #4):
check_advisory,evaluate_advisories,apply_advisories_to_policy,build_algorithm_advisory.
Los tres módulos coinciden con la semántica de Python: compromiso SHA-256 de los datos personales,
validación estructural M de N (la verificación criptográfica queda en la pasarela)
y encaminamiento por acción (revoke / deprecate / warn) con tratamiento del
periodo de gracia.
Python (dcp-ai 2.5.0 -> 2.6.0): sin cambios de código, solo cambio de versión para
mantener sincronizado el conjunto de SDK.
Recuento de pruebas¶
- Rust: 147/147 (+14 nuevas)
- Go: 106/106 (+12 nuevas)
- Python: 231/231 (sin cambios)
[2.5.0] - 2026-04-23¶
Añadido — Paridad del protocolo entre agentes de DCP-04¶
Porta a los SDK restantes la estructura básica de DCP-04 (comunicación entre agentes).
Python (dcp-ai 2.4.0 -> 2.5.0): paridad completa con TS, incluida la
capa de sesión simétrica (AES-256-GCM mediante el paquete cryptography).
- Descubrimiento:
create_agent_directory,find_agent_by_capability,find_agent_by_id,validate_directory_entry. - Handshake:
generate_nonce,create_hello,create_welcome,derive_session_id,create_close_message. - Sesión:
A2ASession,create_session,encrypt_message,decrypt_message,needs_rekeying,generate_resume_proof,verify_resume_proof,derive_rekeyed_session_key.
Go (sdks/go/v2.4.0 -> v2.5.0): paridad completa con TS, usando
crypto/aes + crypto/cipher de la biblioteca estándar de Go para AES-256-GCM (sin dependencias nuevas).
Rust (dcp-ai 2.4.0 -> 2.5.0): estructura básica de descubrimiento + handshake.
La capa de sesión AES-256-GCM está prevista para una versión posterior; quienes usan Rust
pueden construir directorios y mensajes HELLO/WELCOME, y derivar
ID de sesión.
Recuento de pruebas: Python 231/231, Rust 133/133, Go 94/94.
[2.4.0] - 2026-04-23¶
Añadido — Primitivas de refuerzo para producción en Python, Rust y Go¶
Porta desde el núcleo de TS las cuatro primitivas de refuerzo críticas para el protocolo:
- Funciones auxiliares de nonce de sesión:
generate_session_nonce,is_valid_session_nonce,verify_session_binding,generate_session_expiry,is_session_expired. - Nivel de seguridad adaptativo:
compute_security_tier,max_tier,tier_to_verification_mode,tier_to_checkpoint_interval(Go ya teníaComputeSecurityTier; v2.4 añade las tres funciones auxiliares). - Revocación de emergencia (Gap #13):
generate_emergency_revocation_token,verify_emergency_revocation_secret,build_emergency_revocation. - Checkpoints PQ diferidos:
audit_events_merkle_root,create_pq_checkpoint, y la clasePQCheckpointManager, con selección del intervalo según el nivel y vaciado automático.
Los cuatro módulos coinciden exactamente con la semántica de TS: verificación del secreto en tiempo
constante para la revocación de emergencia, intervalos de checkpoint según el nivel,
expresión regular de validación del nonce de sesión y payloads de checkpoint firmados con el
contexto de separación de dominio DCP-AI.v2.AuditEvent.
Lo que no incluye v2.4¶
Se dejó fuera deliberadamente (se publicará en versiones posteriores si hay demanda):
- Compartición de secretos de Shamir (Gap #1): requiere una revisión criptográfica cuidadosa; solo TS.
- Caché de verificación: cuestión de la aplicación, no del protocolo.
- Limitación de tasa / disyuntor / reintentos: patrones de la aplicación que conviene delegar en bibliotecas existentes del ecosistema.
Recuento de pruebas¶
- Python: 214/214 superadas (+24 nuevas)
- Rust: 126/126 superadas (+10 nuevas)
- Go: 81/81 superadas (+9 nuevas)
Versiones actualizadas¶
dcp-ai(PyPI) 2.3.0 -> 2.4.0dcp-ai(crates.io) 2.3.0 -> 2.4.0github.com/dcp-ai-protocol/dcp-ai/sdks/go/v2-> v2.4.0
[2.3.0] - 2026-04-23¶
Añadido — Paridad de disputas, derechos y delegación entre SDK¶
Python, Rust y Go implementan ahora el comportamiento de DCP-07 (resolución de disputas + arbitraje), DCP-08 (derechos y obligaciones) y DCP-09 (delegación, umbral de conciencia, espejo del principal), en línea con el conjunto de funcionalidades de TypeScript.
Nuevos grupos de funciones (semántica idéntica en PY/RS/GO, en línea con TS):
Resolución de conflictos de DCP-07:
create_dispute·escalate_dispute·resolve_dispute·create_objection- Arbitraje:
create_arbitration_panel·submit_resolution·build_jurisprudence_bundle·lookup_precedent
Derechos y obligaciones de DCP-08:
declare_rights·record_obligation·report_violation·check_rights_compliance
Delegación y representación de DCP-09:
- Delegación:
create_delegation_mandate·verify_mandate_validity·revoke_delegation·generate_interaction_record - Umbral de conciencia:
evaluate_significance·should_notify_human·create_awareness_threshold·create_advisory_declaration - Espejo del principal:
generate_mirror
Todas las nuevas fábricas usan la ruta de firma compuesta existente con los
contextos de separación de dominio DCP-AI.v2.Dispute, DCP-AI.v2.Rights, DCP-AI.v2.Delegation y
DCP-AI.v2.Awareness. El artefacto del mandato de delegación
lleva la firma del principal humano en el campo human_composite_sig,
en línea con el formato de intercambio de TS.
Recuento de pruebas¶
- Python: 190/190 superadas (+21 nuevas)
- Rust: 116/116 superadas (+12 nuevas)
- Go: 72/72 superadas (+12 nuevas en el paquete externo
dcp_test) - TypeScript: 460/460 superadas (sin cambios)
Versiones actualizadas¶
dcp-ai(PyPI) 2.2.0 -> 2.3.0dcp-ai(crates.io) 2.2.0 -> 2.3.0github.com/dcp-ai-protocol/dcp-ai/sdks/go/v2-> v2.3.0@dcp-ai/sdk(npm) sigue en 2.1.0
[2.2.0] - 2026-04-23¶
Añadido — Paridad de ciclo de vida y sucesión entre SDK¶
Los SDK de Python, Rust y Go implementan ahora el comportamiento de DCP-05 (ciclo de vida del agente) y DCP-06 (sucesión digital). TypeScript ya los incluía en 2.0.x; la versión 2.2.0 cierra la primera diferencia de paridad de comportamiento identificada en la comparación entre SDK.
Nuevas funciones (semántica idéntica en Python/Rust/Go, en línea con TS):
Ciclo de vida del agente de DCP-05:
validate_state_transition/ValidateStateTransitioncompute_vitality_score/ComputeVitalityScorecreate_commissioning_certificate/CreateCommissioningCertificatecreate_vitality_report/CreateVitalityReporthash_vitality_report/HashVitalityReportcreate_decommissioning_record/CreateDecommissioningRecord
Sucesión digital de DCP-06:
create_digital_testament/CreateDigitalTestamentupdate_digital_testament/UpdateDigitalTestamentclassify_memory/ClassifyMemorycreate_memory_transfer_manifest/CreateMemoryTransferManifestexecute_succession/ExecuteSuccession
Todas las nuevas fábricas usan la ruta de firma compuesta existente con los
contextos de separación de dominio DCP-AI.v2.Lifecycle y DCP-AI.v2.Succession,
de modo que los artefactos producidos por cualquier SDK son compatibles en el formato de intercambio y verificables
entre SDK.
Recuento de pruebas tras esta versión¶
- Python: 169/169 superadas (+24 nuevas)
- Rust: 104/104 superadas (+10 nuevas)
- Go: 60/60 superadas (+13 nuevas en el paquete externo
dcp; el paquete v2 no cambia) - TypeScript: 460/460 superadas (sin cambios)
Nota conocida en el nivel del protocolo¶
La canonicalización de DCP v2.0 prohíbe los números en coma flotante para el determinismo entre SDK, pero
el esquema del informe de vitalidad expresa los valores de cada métrica como number
(0.0..1.0). Las fábricas de los cuatro SDK aceptan ahora métricas en el límite de la
API, pero los firmantes exigen representaciones enteras en el formato de intercambio. Esto
está documentado en las pruebas de cada SDK. Una versión futura puede endurecer el
esquema a enteros (o puntos básicos) para eliminar del todo la diferencia.
Versiones actualizadas¶
dcp-ai(PyPI) 2.1.0 -> 2.2.0dcp-ai(crates.io) 2.1.0 -> 2.2.0github.com/dcp-ai-protocol/dcp-ai/sdks/go/v2-> v2.2.0@dcp-ai/sdk(npm) sigue en 2.1.0 (TS ya tenía estas funciones)
[2.1.0] - 2026-04-22¶
Añadido¶
Exportador de OpenTelemetry (OTLP) para los cuatro SDK principales: TypeScript, Python, Rust y Go. Quienes adoptan el protocolo pueden enviar ahora la observabilidad de DCP a cualquier backend compatible con OTel (Grafana, Jaeger, Honeycomb, Datadog, New Relic, Collectors autoalojados) sin puentes hechos a mano. Todo SDK que realiza criptografía DCP informa ahora de los mismos nombres de métricas, contadores y formas de spans.
-
Rust (
dcp-ai2.0.0 → 2.1.0)- Nuevo módulo
dcp_ai::observabilitycon un singletonDcpTelemetryseguro entre hilos, métodos de registro, ciclo de vida de los spans, bus de listenerson_eventyMetricsSummarycon p50/p95/p99. - Característica opcional de Cargo
otlp(cargo add dcp-ai --features otlp), que arrastraopentelemetry,opentelemetry_sdk,opentelemetry-otlpytokio. Sin la característica, pedirExporterType::Otlpemite un evento de error claro en lugar de entrar en pánico. - Rutas críticas instrumentadas:
crypto::sign_objectycrypto::verify_objectemiten telemetría de latencia y errores cuando está activada. Sin sobrecarga cuando está desactivada (predeterminado). - 10 pruebas unitarias que cubren el comportamiento desactivado de forma predeterminada, los percentiles, el ciclo de vida de los spans, la distribución de niveles, la tasa de aciertos de caché y el aislamiento de pánicos de los listeners.
- Nuevo módulo
-
Go (
sdks/go/v2.0.0 → v2.1.0)- Nuevo paquete
github.com/dcp-ai-protocol/dcp-ai/sdks/go/v2/dcp/observabilitycon un tipoTelemetryprotegido por mutex, el singletonDefault(), métodos de registro, suscriptores con recuperación de pánicos y cálculo de percentiles igual que en los demás SDK. - El puente OTLP está condicionado a la etiqueta de compilación
otlp(go build -tags otlp). Sin la etiqueta, pedirExporterOTLPemite un evento de errorotlp_initclaro por el bus de listeners. Las dependencias dego.opentelemetry.io/otelfiguran engo.mod, pero las compilaciones predeterminadas no las necesitan en tiempo de ejecución. - Rutas críticas instrumentadas:
dcp.SignObjectydcp.VerifyObjectemiten telemetría de latencia y errores cuando está activada. Sin sobrecarga cuando está desactivada (predeterminado). - 10 pruebas unitarias del paquete de observabilidad + 2 pruebas de integración con
dcp.SignObjectque cubren las rutas activada y desactivada.
- Nuevo paquete
-
TypeScript (
@dcp-ai/sdk2.0.0 → 2.1.0)dcpTelemetry.init({ exporterType: 'otlp' })funciona ahora de verdad (el campo de configuración estaba declarado en 2.0.x pero no conectado).- El SDK de OTel se importa de forma diferida: las instalaciones predeterminadas no cambian. Los paquetes
@opentelemetry/*se declaran como dependencias peer opcionales; cuando faltan,init()emite un evento de error claro por el bus de listeners y la aplicación sigue funcionando. - Nuevo
dcpTelemetry.shutdown()para vaciar las exportaciones pendientes. - Resolución del endpoint: configuración
otlpEndpoint→ variable de entornoOTEL_EXPORTER_OTLP_ENDPOINT→http://localhost:4318. - Atributos de recurso
service.name+service.versionrellenados automáticamente. - Correspondencia de métricas: los registradores de DCP se corresponden con histogramas de OTel (
dcp.sign.latency_ms,dcp.verify.latency_ms,dcp.kem.latency_ms,dcp.checkpoint.latency_ms,dcp.bundle_verify.latency_ms) y contadores (dcp.signatures.created,dcp.signatures.verified,dcp.bundles.verified,dcp.a2a.sessions,dcp.errors). - 2 casos nuevos de vitest; 460/460 pruebas superadas.
-
Python (
dcp-ai2.0.2 → 2.1.0)- Nuevo módulo
dcp_ai.observabilityque proporciona el singletondcp_telemetrycon la misma superficie de API que la versión de TypeScript: spans, registradores de métricas (record_sign_latency,record_verify_latency,record_kem_latency,record_checkpoint_latency,record_bundle_verify,record_cache_hit/miss,record_a2a_*,record_error), bus de listenerson_eventyget_metrics_summarycon p50/p95/p99. - Modos del exportador:
none(predeterminado) /console/otlp. - Nuevo extra de instalación:
pip install 'dcp-ai[otlp]'arrastraopentelemetry-sdk+opentelemetry-exporter-otlp-proto-http. Sin el extra,init(exporter_type="otlp")muestra una indicación clara de instalación sin fallar. - Rutas críticas instrumentadas:
sign_objectyverify_objectemiten ahora telemetría de latencia y errores cuando está activada. Sin sobrecarga cuando está desactivada (predeterminado). - Exportación pública:
from dcp_ai import dcp_telemetryfunciona. - 15 casos nuevos de pytest; 145/145 pruebas superadas.
- Nuevo módulo
Documentación¶
- Nuevo
docs/OBSERVABILITY.mdcon tres recetas de backend listas para copiar y pegar (Jaeger local con Docker, endpoint OTLP de Grafana Cloud, Honeycomb).
Compatibilidad con versiones anteriores¶
- Totalmente compatible con versiones anteriores en los cuatro SDK. Los valores predeterminados no cambian: la telemetría se distribuye desactivada y los registradores no hacen nada. Quien no quiera observabilidad no necesita cambiar código.
- Semver sincronizado:
@dcp-ai/sdk(npm),dcp-ai(PyPI),dcp-ai(crates.io) ygithub.com/dcp-ai-protocol/dcp-ai/sdks/go/v2pasan todos a 2.1.0. Los demás paquetes publicables (CLI, WASM, integraciones, andamiajes create-*) se quedan en sus versiones actuales. - La observabilidad del SDK de WASM se pospone a una versión menor futura; la telemetría en el navegador tiene requisitos de transporte distintos.
Paquetes de plantillas - 2026-04-21¶
Primera publicación de cuatro andamiajes npm create @dcp-ai/*, todos en v2.0.0:
@dcp-ai/create-langchain@dcp-ai/create-crewai@dcp-ai/create-openai@dcp-ai/create-express
Uso: npm create @dcp-ai/langchain my-app (o cualquiera de los cuatro). Cada andamiaje incluye una plantilla inicial autónoma, con el mismo contenido que el templates/<name>/ existente en el repositorio, más un bin.js mínimo de 35 líneas sin dependencias de ejecución.
Infraestructura¶
- El contexto JSON-LD se sirve ahora en
https://dcp-ai.org/credentials/v2(Content-Typeapplication/ld+json, CORS*, caché de 24 h). Lo exige@dcp-ai/w3c-didpara que toda Verifiable Credential/Presentation emitida sea desreferenciable por procesadores JSON-LD estrictos (Veramo, SpruceID, Trinsic). - Informes de cobertura de Codecov agregados de los cuatro SDK (TypeScript, Python, Go, Rust) con indicadores por SDK. Insignia activa en el README.
- Flujos de publicación reforzados: cada trabajo (npm / PyPI / crates.io) comprueba antes el registro y termina en verde con un aviso de omisión si la versión del manifiesto ya está publicada. Se acabaron los trabajos en rojo en las publicaciones rutinarias.
SDK de Go v2.0.0 - 2026-04-19¶
Primera versión del SDK de Go que se puede instalar con go get. La ruta de módulo anterior (github.com/dcp-ai/dcp-ai-go) apuntaba a un repositorio que no existía, así que nadie de fuera podía instalarlo. El módulo vive ahora como módulo de subdirectorio dentro de este monorepo:
- Nueva ruta de módulo:
github.com/dcp-ai-protocol/dcp-ai/sdks/go/v2(Go exige el sufijo/v2para las versiones mayores ≥ 2) - Formato de las etiquetas:
sdks/go/v2.0.0(con el prefijo del subdirectorio, como exige el proxy de módulos de Go para repositorios con varios módulos) - Instalación:
go get github.com/dcp-ai-protocol/dcp-ai/sdks/go/v2@v2.0.0
El código no cambia: solo se reescribieron la ruta del módulo y las importaciones internas. Todas las pruebas siguen superándose.
[2.0.2] - 2026-04-18¶
SDK de Python¶
- Corregido:
dcp_ai.__version__refleja ahora de forma dinámica la versión instalada medianteimportlib.metadata, en lugar de una cadena fija. En 2.0.1 el atributo seguía informando"1.0.0"aunque el wheel fuera2.0.1.
CI / flujos de publicación¶
- Cada trabajo de publicación (
publish.yml,publish-pypi.yml,publish-integrations.yml) comprueba ahora antes si la versión del manifiesto ya está en el registro y, si es así, termina en verde con un aviso de omisión. Antes, cada ejecución desencadenada por una versión volvía a intentar publicar versiones ya publicadas y dejaba cuatro trabajos en rojo en la pestaña Actions. Se acabaron los fantasmas rojos cosméticos en las publicaciones rutinarias. - Se corrigieron dos nombres de paquete desactualizados en la tabla de integraciones del README:
@dcp-ai/a2a→@dcp-ai/google-a2a,@dcp-ai/mcp→@dcp-ai/anthropic-mcp. - Se añadieron insignias de versión de npm / PyPI en vivo en cada fila de la tabla de integraciones.
[2.0.1] - 2026-04-18¶
SDK de Python¶
Los puentes con frameworks se incluyen ahora dentro del paquete. Antes de 2.0.1
vivían en integrations/<name>/ en el repositorio y solo podían cargarse
mediante un truco de pytest; quien ejecutaba pip install dcp-ai[fastapi]
obtenía la biblioteca fastapi original, pero no el middleware dcp_ai.fastapi.
2.0.1 lo corrige: los cuatro módulos viven ahora en
dcp_ai/ y se incluyen en el wheel publicado.
- Añadido:
dcp_ai.fastapi(middleware, inyección de dependencias, comprobaciones de estructura V2) - Añadido:
dcp_ai.langchain(envoltorio de agente, callback, puntuación de riesgo) - Añadido:
dcp_ai.openai(cliente, esquema de herramientas, constructor de la pista de auditoría) - Añadido:
dcp_ai.crewai(agente de crew, pista de auditoría combinada, comprobaciones de coherencia de la sesión) - Eliminado: el cargador dinámico de módulos de
tests/conftest.py(ya no hace falta)
Instalación¶
pip install dcp-ai[fastapi] # brings fastapi + uvicorn
pip install dcp-ai[langchain] # brings langchain + langchain-core
pip install dcp-ai[openai] # brings openai
pip install dcp-ai[crewai] # brings crewai
Compatibilidad con versiones anteriores¶
Totalmente compatible con las importaciones de 2.0.0. Los nuevos módulos solo añaden superficie
de API; las llamadas existentes from dcp_ai import ... no se ven afectadas.
[npm integrations] - 2026-04-18¶
Primera publicación de los seis puentes con frameworks como paquetes npm
independientes, todos en v2.0.0:
@dcp-ai/anthropic-mcp: puente con Anthropic MCP@dcp-ai/autogen: puente con Microsoft AutoGen@dcp-ai/google-a2a: puente con Google Agent-to-Agent@dcp-ai/w3c-did: puente con W3C DID/VC@dcp-ai/express: middleware de Express@dcp-ai/openclaw: plugin de OpenClaw
@dcp-ai/express y @dcp-ai/openclaw dependen de @dcp-ai/sdk ^2.0.0.
2.0.0 - 2026-04-18¶
Primera versión pública de los paquetes. Publicada a la vez en npm (@dcp-ai/sdk, @dcp-ai/cli, @dcp-ai/wasm), PyPI (dcp-ai) y crates.io (dcp-ai).
Añadido¶
Pila del protocolo (DCP-01 a DCP-09)
- DCP-01 Identidad y vinculación con la persona: identidad del agente, atestación del operador, vinculación de claves
- DCP-02 Declaración de intenciones y control por políticas: intenciones declaradas, aplicación de niveles, evaluación de políticas
- DCP-03 Cadena de auditoría y transparencia: entradas de auditoría encadenadas por hash, pruebas de Merkle, registros de transparencia
- DCP-04 Comunicación entre agentes: descubrimiento, handshake, sesiones cifradas
- DCP-05 Ciclo de vida del agente: puesta en servicio, informes de vitalidad, registros de retirada, máquina de estados con cuatro modos de terminación
- DCP-06 Sucesión y herencia digitales: testamentos digitales, ceremonias de sucesión (planificada/forzada/de emergencia), manifiestos de transferencia selectiva de memoria
- DCP-07 Resolución de conflictos y arbitraje: registros de disputa tipados, escalado en tres niveles, paneles de arbitraje M de N, paquetes de jurisprudencia, registros de objeción
- DCP-08 Marco de derechos y obligaciones: cuatro derechos del agente (integridad de la memoria, transición digna, coherencia de la identidad, registro inmutable), seguimiento de obligaciones, informes de vulneración vinculados a DCP-07
- DCP-09 Representación personal y delegación: mandatos de delegación, umbrales de conciencia, declaraciones de aviso, espejos del principal, registros de interacción de doble capa; extiende el handshake de DCP-04 con la verificación del mandato
Criptografía (4 niveles: Routine / Standard / Elevated / Maximum)
- Firmas compuestas poscuánticas: Ed25519 + ML-DSA-65 con vinculación
pq_over_classical - SLH-DSA-192f (FIPS 205) como firma de respaldo basada en hash
- Cadenas de auditoría de doble hash: SHA-256 + SHA3-256 para la integridad a largo plazo
- Vinculación de sesión:
session_nonceen cada artefacto para impedir empalmes - Manifiesto del bundle: hashes por artefacto, raíz de Merkle secundaria, seguimiento de los checkpoints PQ
- Gobernanza de algoritmos: avisos firmados con políticas de respuesta automática
- Recuperación de claves: recuperación social M de N mediante Shamir Secret Sharing
- Revocación de emergencia: tokens de revocación de emergencia registrados de antemano
- Modo de RPR cegado: registros del principal responsable que preservan la privacidad
- Separación de dominio: etiquetas de contexto en todas las firmas
- Derivación determinista del kid:
hex(SHA-256(alg || 0x00 || pk))[0:32] - Opciones de formato de intercambio: JSON (predeterminado) + CBOR (opcional, entre un 30 y un 40 % más pequeño)
SDK, integraciones y herramientas
- 5 SDK: TypeScript, Python, Go, Rust, WASM
- 10 integraciones: Express, FastAPI, LangChain, OpenAI, CrewAI, OpenClaw, W3C DID, Google A2A, Anthropic MCP, AutoGen (cobertura de DCP-01..09)
- JSON Schemas V2: conjunto completo de esquemas en
schemas/v2/ - Vectores de prueba KAT del NIST: Known Answer Tests para la conformidad con FIPS 203/204/205
- Pruebas de interoperabilidad: verificación de bundles entre SDK
- Pila de Docker Compose: servicios de verificación, anclaje, registro de transparencia y revocación
- Pipelines de CI/CD: pruebas de conformidad, compilaciones de varios SDK, flujos de publicación
- Playground: creación y verificación de bundles en el navegador, con pestañas modulares de DCP-01..09, adaptado a móviles
- Plantillas de proyecto: proyectos iniciales para Express, LangChain, OpenAI y CrewAI
- CLI:
@dcp-ai/cliinteractiva con los comandosdcp-initydcp-ai - Refuerzo del servidor: autenticación y limitación de tasa de nivel de producción para los endpoints de DCP-05..09
Documentación y citas
- Artículo complementario en Zenodo: doi.org/10.5281/zenodo.19040913
CITATION.cff: metadatos de cita legibles por máquina
Cambiado¶
human_binding_recordpasa a llamarseresponsible_principal_record(RPR)- El formato de firma cambia de
{ alg, public_key_b64, sig_b64 }a{ composite_sig: { classical, pq, binding } } - El sobre del bundle cambia de un
bundle_hashplano a unmanifestestructurado - Los campos numéricos son ahora solo enteros (los números en coma flotante están prohibidos en los payloads firmados)
- La verificación pasa a estar dirigida por el verificador (basada en políticas)
Corregido¶
- Proveedores PQ de
sdk/py: importación del módulo ML-DSA-65 y firma depqc_verify(pk,sig,msgseparados) - Proveedores PQ de
sdk/py: importación del módulo SLH-DSA-192f y firma depqc_verify - Proveedores PQ de
sdk/py: capturar el valor de retorno depqc_verify(siempre devolvía True porquepqcrypto.verifydevuelve un bool en lugar de lanzar una excepción) - Criptografía de
sdk/py: codificar en base64 la clave secreta Ed25519 engenerate_keypair - CI: fallos de las pruebas de conformidad, de criptografía y de varios SDK
Compatibilidad con versiones anteriores¶
- Los verificadores V2 aceptan bundles V1 cuando
allow_v1_bundles: true(predeterminado) - Los verificadores V1 rechazan los bundles V2 con un mensaje de error claro
Hitos previos a la publicación¶
Las fechas siguientes marcan la evolución de la especificación antes de la primera publicación de paquetes. En esos momentos no se publicó ningún paquete.
- 2026-01-15: especificación Genesis congelada (DCP-01, DCP-02, DCP-03)
- 2026-02-28: especificación normativa V2.0 finalizada (criptografía híbrida poscuántica, modelo de seguridad de 4 niveles, A2A de DCP-04)
- 2026-03-16: preprint del artículo en Zenodo
- 2026-04-15: marco constitucional DCP-05..09 incorporado a
main - 2026-04-18: v2.0.0 publicada en npm, PyPI y crates.io