@dcp-ai/sdk — SDK de TypeScript para DCP-AI v2.0¶
Traducción informativa de la versión en inglés, actualizada el 02/11/2026. Si difieren, prevalece la versión en inglés. Los identificadores, comandos y bloques de código se conservan sin traducir.
SDK oficial de TypeScript para el Digital Citizenship Protocol (DCP-AI). Crea, firma y verifica Citizenship Bundles con criptografía híbrida poscuántica (Ed25519 + ML-DSA-65), firmas compuestas, niveles de seguridad adaptativos, comunicación entre agentes (A2A), observabilidad integrada y refuerzo para producción.
Instalación¶
Publicado en npm (26/10/2026). @dcp-ai/sdk 2.2.0 se publicó como latest en esa fecha. Incluía las correcciones del verificador descritas en SECURITY.md, confianza en el principal y strict-v1, cadenas de delegación, la ventana de recepción A2A y las API experimentales de dcp-jcs-v2. El candidato seguía siendo opt-in y no normativo, con dcp-jcs-v1 como predeterminado. Los ejemplos siguientes usan API disponibles desde la 2.2.0.
@dcp-ai/sdk 2.2.1. Esta versión también verifica los campos secundarios SHA3-256 de VERIFICATION.md V2-11 cuando están presentes: audit_merkle_root_secondary, cada prev_hash_secondary y cada intent_hash_secondary. Los campos ausentes no se exigen, así que un bundle verificado puede no tener una cadena secundaria completa; un campo presente que sea null, no sea una cadena o sea incoherente se rechaza. No cambian las API, los campos de resultado, dcp-jcs-v1 ni el tratamiento de hash_alg. La versión 2.2.0 no comprueba estos campos. Los comandos de instalación siguientes indican esta versión; para su disponibilidad en el registro, véase Versiones publicadas.
Consulta el inventario de publicaciones npm para ver todas las versiones.
Para desarrollar con una copia del repositorio, compila e instala el paquete local:
git clone https://github.com/dcp-ai-protocol/dcp-ai.git
cd dcp-ai/sdks/typescript && npm install && npm run build
cd /path/to/your-project && npm install /path/to/dcp-ai/sdks/typescript
Inicio rápido (V1)¶
Un bundle V1 firmado con Ed25519. El RPR se firma con la clave del principal y el pasaporte con la del agente,
cada uno sobre el JSON canónico del objeto sin su campo signature; createAuditEntry calcula
intent_hash y la cadena prev_hash. verifySignedBundle comprueba la firma del bundle, sus hashes y la
cadena, no estas firmas internas: compruébalas tú, como se muestra abajo.
import { BundleBuilder, generateKeypair, signBundle, signObject, verifyObject, verifySignedBundle } from '@dcp-ai/sdk';
import type { AgentPassport, ResponsiblePrincipalRecord } from '@dcp-ai/sdk';
const now = new Date().toISOString();
const principalKeys = generateKeypair();
const agentKeys = generateKeypair();
// Each artifact is signed over the object without its `signature` field.
const rprBody: Omit<ResponsiblePrincipalRecord, 'signature'> = {
dcp_version: '1.0', human_id: 'human:example-001', legal_name: 'Example Principal',
entity_type: 'natural_person', jurisdiction: 'ES', liability_mode: 'owner_responsible',
override_rights: true, issued_at: now, expires_at: null, contact: null,
};
const rpr: ResponsiblePrincipalRecord = { ...rprBody, signature: signObject(rprBody, principalKeys.secretKeyB64) };
const passportBody: Omit<AgentPassport, 'signature'> = {
dcp_version: '1.0', agent_id: 'agent:example-001', public_key: agentKeys.publicKeyB64,
principal_binding_reference: rpr.human_id, capabilities: ['browse', 'api_call'],
risk_tier: 'low', created_at: now, status: 'active',
};
const passport: AgentPassport = { ...passportBody, signature: signObject(passportBody, agentKeys.secretKeyB64) };
const bundle = new BundleBuilder()
.responsiblePrincipalRecord(rpr)
.agentPassport(passport)
.intent({
dcp_version: '1.0', intent_id: 'intent:example-001', agent_id: passport.agent_id, human_id: rpr.human_id,
timestamp: now, action_type: 'api_call', target: { channel: 'api', domain: 'api.example.com' },
data_classes: ['none'], estimated_impact: 'low', requires_consent: false,
})
.policyDecision({
dcp_version: '1.0', intent_id: 'intent:example-001', decision: 'approve', risk_score: 0.05,
reasons: ['low-impact call'], required_confirmation: null,
})
.createAuditEntry({
dcp_version: '1.0', audit_id: 'audit:example-001', timestamp: now, agent_id: passport.agent_id,
human_id: rpr.human_id, intent_id: 'intent:example-001', policy_decision: 'approved',
outcome: 'call_completed', evidence: { tool: 'http', result_ref: null },
})
.build();
const signed = signBundle(bundle, { secretKeyB64: principalKeys.secretKeyB64, signerType: 'human', signerId: rpr.human_id });
console.log(verifySignedBundle(signed, principalKeys.publicKeyB64)); // { verified: true }
// The inner signatures, over the same objects without `signature`
console.log(verifyObject(rprBody, rpr.signature, principalKeys.publicKeyB64)); // true
console.log(verifyObject(passportBody, passport.signature, agentKeys.publicKeyB64)); // true
Inicio rápido (V2)¶
Un bundle V2 con firmas híbridas (Ed25519 + ML-DSA-65): cada artefacto es un SignedPayload
({ payload, payload_hash, composite_sig }) firmado en su contexto de separación de dominio; el principal firma
el RPR y el manifiesto, y el agente, el pasaporte, la intención y la decisión de política.
import {
AlgorithmRegistry, BundleBuilderV2, DCP_CONTEXTS, Ed25519Provider, MlDsa65Provider, compositeSign,
generateSessionNonce, preparePayload, sha256Hex, canonicalizeV2, signBundleV2, verifySignedBundleV2,
} from '@dcp-ai/sdk';
import type {
AgentPassportV2, AuditEventV2, CompositeKeyPair, DcpContext, IntentV2, KeyEntry, PolicyDecisionV2,
ResponsiblePrincipalRecordV2, SignedPayload,
} from '@dcp-ai/sdk';
const registry = new AlgorithmRegistry();
const ed25519 = new Ed25519Provider();
const mlDsa65 = new MlDsa65Provider();
registry.registerSigner(ed25519);
registry.registerSigner(mlDsa65);
async function hybridKeys(): Promise<CompositeKeyPair> {
const c = await ed25519.generateKeypair();
const p = await mlDsa65.generateKeypair();
return {
classical: { kid: c.kid, alg: 'ed25519', secretKeyB64: c.secretKeyB64, publicKeyB64: c.publicKeyB64 },
pq: { kid: p.kid, alg: 'ml-dsa-65', secretKeyB64: p.secretKeyB64, publicKeyB64: p.publicKeyB64 },
};
}
const keyEntries = (keys: CompositeKeyPair): KeyEntry[] => [keys.classical, keys.pq].map((k) => ({
kid: k.kid, alg: k.alg, public_key_b64: k.publicKeyB64, created_at: '2026-01-01T00:00:00Z', expires_at: null, status: 'active',
}));
const sha256Of = (value: unknown) => `sha256:${sha256Hex(new TextEncoder().encode(canonicalizeV2(value)))}`;
async function signed<T>(payload: T, context: DcpContext, keys: CompositeKeyPair): Promise<SignedPayload<T>> {
const { canonicalBytes, payloadHash } = preparePayload(payload);
return { payload, payload_hash: payloadHash, composite_sig: await compositeSign(registry, context, canonicalBytes, keys) };
}
const principal = await hybridKeys();
const agent = await hybridKeys();
const nonce = generateSessionNonce();
const now = new Date().toISOString();
const rpr: ResponsiblePrincipalRecordV2 = {
dcp_version: '2.0', human_id: 'human:example-001', session_nonce: nonce, legal_name: 'Example Principal',
entity_type: 'natural_person', jurisdiction: 'ES', liability_mode: 'owner_responsible', override_rights: true,
issued_at: now, expires_at: null, contact: null, binding_keys: keyEntries(principal),
};
const passport: AgentPassportV2 = {
dcp_version: '2.0', agent_id: 'agent:example-001', session_nonce: nonce, keys: keyEntries(agent),
principal_binding_reference: rpr.human_id, capabilities: ['browse', 'api_call'], risk_tier: 'low',
created_at: now, status: 'active',
};
const intent: IntentV2 = {
dcp_version: '2.0', intent_id: 'intent:example-001', session_nonce: nonce, agent_id: passport.agent_id,
human_id: rpr.human_id, timestamp: now, action_type: 'api_call', target: { channel: 'api', domain: 'api.example.com' },
data_classes: ['none'], estimated_impact: 'low', requires_consent: false,
};
const policy: PolicyDecisionV2 = {
dcp_version: '2.0', intent_id: intent.intent_id, session_nonce: nonce, decision: 'approve', risk_score: 50,
reasons: ['low-impact call'], required_confirmation: null, applied_policy_hash: sha256Of({ policy: 'default' }), timestamp: now,
};
const audit: AuditEventV2 = {
dcp_version: '2.0', audit_id: 'audit:example-001', session_nonce: nonce, prev_hash: 'GENESIS', hash_alg: 'sha256',
timestamp: now, agent_id: passport.agent_id, human_id: rpr.human_id, intent_id: intent.intent_id,
intent_hash: sha256Of(intent), policy_decision: 'approved', outcome: 'call_completed',
evidence: { tool: 'http', result_ref: null, evidence_hash: null }, pq_checkpoint_ref: null,
};
const bundle = new BundleBuilderV2(nonce)
.responsiblePrincipalRecord(await signed(rpr, DCP_CONTEXTS.ResponsiblePrincipal, principal))
.agentPassport(await signed(passport, DCP_CONTEXTS.AgentPassport, agent))
.intent(await signed(intent, DCP_CONTEXTS.Intent, agent))
.policyDecision(await signed(policy, DCP_CONTEXTS.PolicyDecision, agent))
.addAuditEntry(audit)
.build();
const signedBundle = await signBundleV2(bundle, { registry, signerType: 'human', signerId: rpr.human_id, keys: principal });
const result = await verifySignedBundleV2(signedBundle, registry);
console.log(result.verified, result.errors); // true []
Referencia de la API¶
Criptografía básica (V1)¶
| Función | Descripción |
|---|---|
generateKeypair() |
Genera un par de claves Ed25519 (publicKeyB64, secretKeyB64) |
signObject(obj, secretKeyB64) |
Firma un objeto y devuelve la firma en base64 |
verifyObject(obj, signatureB64, publicKeyB64) |
Verifica una firma con una clave pública |
canonicalize(obj) |
Serialización JSON determinista (canónica) |
publicKeyFromSecret(secretKeyB64) |
Deriva la clave pública a partir de una clave secreta |
Proveedores criptográficos (V2)¶
| Exportación | Descripción |
|---|---|
Ed25519Provider |
Proveedor de firma clásica Ed25519 |
MlDsa65Provider |
Proveedor de firma poscuántica ML-DSA-65 |
SlhDsa192fProvider |
Proveedor de firma poscuántica SLH-DSA-192f |
AlgorithmRegistry |
Registro que gestiona los proveedores de algoritmos criptográficos disponibles |
getDefaultRegistry() |
Devuelve el registro de algoritmos único (singleton) |
registerDefaultProviders() |
Registra los proveedores Ed25519, ML-DSA-65 y SLH-DSA-192f |
deriveKid(alg, publicKeyBytes) |
Deriva un identificador de clave a partir de una clave pública |
Firmas compuestas (V2)¶
| Función | Descripción |
|---|---|
compositeSign(registry, context, canonicalPayloadBytes, keys) |
Crea una firma compuesta con algoritmos clásico + PQ |
compositeVerify(registry, context, canonicalPayloadBytes, compositeSig, classicalPubkeyB64, pqPubkeyB64?, strategy?) |
Verifica una firma compuesta |
classicalOnlySign(registry, context, canonicalPayloadBytes, key) |
Firma solo con el algoritmo clásico (modo de respaldo) |
Niveles de seguridad (V2)¶
| Función | Descripción |
|---|---|
computeSecurityTier(intent, policyDecision?, mandateChainTier?) |
Calcula un SecurityTier (routine, standard, elevated, maximum) a partir de una intención y del risk_score de la decisión de política (spec §14.2); el effective_tier de una cadena de delegación verificada solo puede elevarlo; los valores desconocidos fallan cerrados a elevated |
maxTier(a, b) |
Devuelve el más alto de dos niveles de seguridad |
tierToVerificationMode(tier) |
Asigna a un nivel el modo de verificación exigido |
tierToCheckpointInterval(tier) |
Asigna a un nivel el intervalo de checkpoints PQ |
Construcción de bundles¶
| Exportación | Versión | Descripción |
|---|---|---|
BundleBuilder |
V1 | Constructor fluido de Citizenship Bundles V1 |
BundleBuilderV2 |
V2 | Constructor fluido de bundles V2 con niveles de seguridad y doble hash |
signBundle(bundle, options) |
V1 | Firma un bundle V1 con Ed25519 |
signBundleV2(bundle, options) |
V2 | Firma un bundle V2 con firmas compuestas |
signBundleV2ClassicalOnly(bundle, options) |
V2 | Firma un bundle V2 solo con firmas clásicas |
verifySignedBundle(signedBundle, publicKeyB64) |
V1 | Verifica un bundle V1 firmado |
verifySignedBundleV2(signedBundle, registry, policy?) |
V2 | Verifica un bundle V2 firmado (compuesto o clásico) según el más estricto entre el modo de la política y el modo del nivel de seguridad; informa de resolved_tier |
Validación de esquemas (V2-2; en main, no en una versión publicada). verifySignedBundleV2 y las demás funciones de verificación V2 validan primero el sobre del bundle firmado y los cuatro payloads contra schemas/v2, después de las comprobaciones de política y reloj y antes de que el verificador compruebe el manifiesto, los hashes de los payloads, las claves y las firmas; admitSignedBundleV2, que verifica primero, y el verificador experimental del candidato pasan por la misma puerta, mientras que evaluateAdmissionV2 recibe un resultado anterior y no la ejecuta. Un bundle que falla devuelve verified: false con errores schema: <JSON pointer> <keyword>[ <member>] (como mucho 16, y luego schema: further errors not listed) y sin detalles criptográficos. Los miembros desconocidos de los objetos cerrados se rechazan; en los objetos abiertos, como el target del intent, los miembros declarados conservan sus restricciones y solo los adicionales quedan sin inspeccionar. session_issued_at y session_expires_at del manifiesto deben ser cadenas cuando están presentes, no null. date-time sigue la comprobación de ajv-formats 3.0.1 del verificador de referencia, que no es RFC 3339 estricto. Un miembro con valor undefined cuenta como ausente, igual que en la canonicalización. Cambio de aceptación: los bundles inválidos por esquema que antes verificaban ahora se rechazan. No hay ninguna API solo de esquema. Véase el mapa de cobertura.
Optimización de bundles (V2)¶
| Exportación | Descripción |
|---|---|
suggestPresentationMode(tier, opts) |
Recomienda un modo de presentación según el nivel de seguridad y si se trata de un primer contacto o de una relación de confianza ya existente |
presentFull(bundle) |
Presentación completa del bundle (sin omisiones) |
presentCompact(bundle, pqCheckpointRef) |
Presentación compacta: elimina la firma PQ y, en su lugar, hace referencia al checkpoint PQ más cercano |
presentReference(bundle, verificationEndpoint) |
Presentación solo por referencia: hash del bundle, nonce de sesión y un endpoint para verificar u obtener el bundle completo |
presentIncremental(baseBundle, updatedBundle, baseAuditCount) |
Presentación incremental respecto de un bundle base verificado: nuevas entradas de auditoría, checkpoints PQ, manifiesto y firma actualizados |
VerificationCache |
Guarda en caché resultados de verificación por hash del bundle. Un acierto no vuelve a comprobar la confianza, la revocación ni la sesión, y nada se invalida automáticamente, así que verifica de nuevo para la admisión. En el código fuente de main (no publicado), un context opcional elegido por quien llama separa inquilinos o políticas, get devuelve una copia e invalidateByAgent elimina las entradas del agente y todas las guardadas sin agentIds; en la 2.2.1 publicada la clave es solo el hash e invalidateByAgent no elimina nada, así que usa invalidateByKid o clear() (detalles) |
Checkpoints PQ (V2)¶
| Exportación | Descripción |
|---|---|
PQCheckpointManager |
Gestiona la creación periódica de checkpoints poscuánticos |
createPQCheckpoint(registry, events, sessionNonce, keys) |
Crea un checkpoint con firma PQ sobre entradas de auditoría |
auditEventsMerkleRoot(events) |
Calcula una raíz de Merkle a partir de entradas de auditoría |
Doble hash (V2)¶
| Función | Descripción |
|---|---|
sha256Hex(data) |
Hash SHA-256 (cadena hexadecimal) |
sha3_256Hex(data) |
Hash SHA3-256 (cadena hexadecimal) |
dualHash(data) |
Devuelve { sha256, sha3_256 } para un doble hash resistente a la computación cuántica |
dualMerkleRoot(leaves) |
Calcula una raíz de Merkle con hojas de doble hash |
Protocolo A2A (DCP-04)¶
| Función | Descripción |
|---|---|
createAgentDirectory(organization, agents) |
Crea un directorio de agentes en memoria |
findAgentByCapability(directory, requiredCapabilities) |
Busca agentes por capacidad en un directorio |
findAgentById(directory, agentId) |
Busca un agente por ID |
createHello(initiatorBundle, kemPublicKeyB64, requestedCapabilities, securityTier) |
Crea un mensaje Hello del handshake A2A |
createWelcome(responderBundle, kemPublicKeyB64, kemCiphertextB64, resolvedTier) |
Crea un mensaje de respuesta Welcome A2A |
deriveSessionId(agentIdA, agentIdB, nonceA, nonceB, sessionKey) |
Deriva un ID de sesión a partir de los nonces del handshake |
createCloseMessage(sessionId, reason, finalSequence, auditSummaryHash) |
Crea un mensaje de cierre de sesión |
createSession(sessionId, sessionKey, localAgentId, remoteAgentId, securityTier, rekeyingInterval?) |
Crea una sesión A2A cifrada |
encryptMessage(session, payload) |
Cifra un mensaje dentro de una sesión A2A |
decryptMessage(session, message) |
Descifra un mensaje dentro de una sesión A2A; cada secuencia se acepta una sola vez dentro de la ventana de 1000 mensajes (DCP-04 §8.4) |
needsRekeying(session) |
Comprueba si una sesión necesita rotación de claves |
generateResumeProof(session, lastSeenSequence) |
Genera una prueba para reanudar la sesión |
verifyResumeProof(session, lastSeenSequence, proof) |
Verifica una prueba de reanudación de sesión |
Observabilidad¶
| Exportación | Descripción |
|---|---|
dcpTelemetry |
Instancia única de telemetría |
dcpTelemetry.init(config) |
Inicializa la telemetría con el nombre del servicio y el exportador |
dcpTelemetry.startSpan(name) |
Inicia un span de traza con nombre |
dcpTelemetry.endSpan(span) |
Termina un span de traza |
dcpTelemetry.recordSignLatency(ms) |
Registra la métrica de latencia de firma |
dcpTelemetry.getMetricsSummary() |
Devuelve un resumen agregado de las métricas |
Refuerzo para producción¶
| Exportación | Descripción |
|---|---|
DcpErrorCode |
Enumeración de códigos de error estructurados |
DcpProtocolError |
Clase de error tipada para fallos a nivel de protocolo |
createDcpError(code, details?) |
Fábrica para crear errores DCP estructurados |
isDcpError(error) |
Guarda de tipo para DcpProtocolError |
RateLimiter |
Limitador de tasa de ventana fija |
AdaptiveRateLimiter |
Limitador de tasa que se ajusta según la carga |
CircuitBreaker |
Cortocircuito (circuit breaker) para llamadas externas |
withRetry(fn, config?) |
Reintenta una función asíncrona con espera progresiva |
Otras de V2¶
| Exportación | Descripción |
|---|---|
generateSessionNonce() |
Genera un nonce de sesión criptográfico |
domainSeparatedMessage(context, canonicalPayloadBytes) |
Antepone un separador de dominio a un mensaje |
generateEmergencyRevocationToken() |
Genera un secreto de revocación y su compromiso de token sha256: |
buildEmergencyRevocation({ agentId, humanId, revocationSecret }) |
Construye una solicitud de revocación de emergencia a partir del secreto de revocación |
shamirSplit(secret, threshold, totalShares) |
Divide un secreto en totalShares partes; cualquier threshold de ellas lo reconstruye |
shamirReconstruct(shares) |
Reconstruye un secreto a partir de al menos threshold partes |
generatePiiSalt() |
Genera un pii_salt (32 bytes aleatorios en hexadecimal). Asígnalo al RPR antes de firmarlo y mantenlo privado: blindRpr lo omite del registro cegado, pero el RPR completo lo conserva |
blindRpr(rpr) |
Sustituye los datos personales de un RPR (legal_name, contact) por un compromiso de hash con sal; lanza una excepción si falta rpr.pii_salt o está mal formado |
verifyBlindedRpr(fullRpr, blindedRpr) |
Comprueba que un RPR cegado corresponde al registro completo; warnings señala un compromiso heredado sin sal |
isBlindedRpr(rpr) |
Guarda de tipo para BlindedResponsiblePrincipalRecordV2 |
computePiiHash(rpr) |
Calcula el compromiso de hash sobre los campos de datos personales de un RPR y pii_salt (sin pii_salt: el valor sin sal, obsoleto) |
CborEncoder |
Clase codificadora CBOR |
CborDecoder |
Clase decodificadora CBOR |
cborEncode(value) |
Codifica un valor en bytes CBOR |
cborDecode(data) |
Decodifica bytes CBOR a un valor |
Admisión: Trusted Principals y strict-v1¶
Un bundle verificado está firmado por las claves que enumera; no prueba quién es el principal. La admisión acepta un bundle solo cuando sus firmas de principal se hicieron con claves de un principal que figura en tu conjunto de confianza y, opcionalmente, bajo el perfil strict-v1 fijado por el operador (firmas híbridas, audiencia firmada, una sesión de 300 s como máximo, una instantánea de revocación reciente).
// Fragment, not a complete program: trustSetJson and the strict-v1 settings come from operator configuration, signedBundle from the request, snapshot from your revocation source, registry as in the V2 Quick Start.
import { admitSignedBundleV2, createPrincipalTrustStore, resolveStrictV1Profile } from '@dcp-ai/sdk';
const trust = createPrincipalTrustStore(trustSetJson); // AdmissionConfigError on a bad trust set
const profile = resolveStrictV1Profile({ profile: 'strict-v1', verifier_id: 'api.acme.example' });
const result = await admitSignedBundleV2(signedBundle, registry, { profile, trust, revocation: snapshot });
// result.verified, result.principal_trust, result.profile_compliance, result.admitted,
// result.action_authorized (always false), result.errors [{ code, message }]
verifySignedBundleV2(bundle, registry, policy, { now })acepta un reloj opcional e informa dedetails.signer_keys; sin ellos se comporta como antes.evaluateAdmissionV2evalúa un resultado de verificación que ya tienes (sin criptografía ni E/S);revocationSubjectsenumera aquello por lo que debe responder una fuente de revocación.- Guía:
docs/ADMISSION.md. Especificación y códigos:spec/profiles/admission.
Cadenas de delegación (DCP-09 §3.5)¶
Una persona delega en un agente con un mandato raíz; ese agente puede pasar parte de su autoridad a otro agente con un submandato que firma él mismo, y así sucesivamente. Cada salto solo puede restringir lo que recibió, y toda la cadena se verifica sin conexión hasta llegar a la persona.
| Función | Descripción |
|---|---|
createDelegationMandate(registry, humanKeys, params) |
Mandato raíz firmado por la persona (human_composite_sig); sub_delegation y minimum_security_tier son opcionales |
createSubMandate(registry, delegatorKeys, parent, params) |
Submandato firmado por el agente del padre (delegator_composite_sig, contexto DCP-AI.v2.SubMandate) |
verifyMandateChain(registry, chain, revokedIds, keys, now?) |
Verifica la cadena empezando por la raíz: firmas hasta la persona, enlaces con el padre y profundidad, sub_delegation, ventanas de validez anidadas, atenuación (acciones, data_classes y limits solo se restringen), el nivel de seguridad mínimo y la revocación; devuelve { valid, errors, depth, effective_tier } |
// Fragment, not a complete program: registry and nonce as in the V2 Quick Start; rootMandate, orchestratorKeys and the key entries come from your delegation setup; intent and policyDecision are the V2 payloads.
import { createSubMandate, verifyMandateChain, computeSecurityTier } from '@dcp-ai/sdk';
const sub = await createSubMandate(registry, orchestratorKeys, rootMandate, {
mandate_id: 'mandate:sub-1',
session_nonce: nonce,
agent_id: 'agent:subagent',
authority_scope: [{ domain: 'email', actions_permitted: ['read'], data_classes: ['contact_info'], limits: { max_messages_per_day: 10 } }],
valid_from: '2026-03-01T00:00:00Z',
valid_until: '2026-06-01T00:00:00Z',
revocable: true,
});
const result = await verifyMandateChain(registry, [rootMandate, sub], new Set(), {
human_keys: humanKeyEntries,
agent_keys: { 'agent:orchestrator': orchestratorKeyEntries },
});
// The chain's tier floor can only raise the tier of an action taken under it.
const tier = computeSecurityTier(intent, policyDecision, result.valid ? result.effective_tier : null);
Experimental: candidato de canonicalización dcp-jcs-v2 (opt-in)¶
No es normativo ni está activo: dcp-jcs-v1 sigue siendo el único perfil de canonicalización, y no cambia ningún valor por defecto,
constructor, firmante ni verificador. Estas API existen solo para quien opte por usarlas, también en npm SDK 2.2.0. Un bundle candidato verificado no otorga confianza ni admisión. Diseño, reglas y contrato
completo: docs/proposals/canonicalization-candidate
(§15 entrada, §16 enrutador, §17 verificador, §18 productores).
| Exportación | Descripción |
|---|---|
experimentalCandidateIngress(bytes) |
Comprueba los bytes originales de una unidad: { status: 'accept', value, canonical, canonicalSha256 } o { status: 'reject', class, offset } |
experimentalCandidateExtractUnit(bytes, transport) |
Extrae la unidad de un transporte bare o envelope |
createExperimentalCandidateRouter(config) |
Enruta bytes: reject, v1_path (a través de un decodificador histórico con nombre) o candidate |
createExperimentalCandidateVerifier(config).verify(bytes, registry, policy?, options?) |
Verifica a partir de los bytes originales con un verificador como máximo: el actual para dcp-jcs-v1 y, para el candidato, el actual aceptando exactamente dcp-jcs-v2 |
experimentalHistoricalAdapters |
Los decodificadores medidos (bufferUtf8JsonParse, textDecoderJsonParse); ninguno es el predeterminado |
experimentalCandidateSignPayload(registry, context, input, keys) |
Firma un artefacto a partir de { bytes } o { value }; sin perfil |
experimentalCandidateSignBundle(registry, parts, signer, options) |
Ensambla y firma un bundle cuyo manifiesto nombra dcp-jcs-v2 |
ExperimentalCandidateProduceError |
Un rechazo por el dominio o la forma de las entradas, lanzado antes de llamar a cualquier firmante (class, part, path, offset); los errores de clave, registro y proveedor siguen siendo los actuales |
import {
AlgorithmRegistry, DCP_CONTEXTS, Ed25519Provider, MlDsa65Provider, experimentalCandidateIngress,
experimentalCandidateSignPayload,
} from '@dcp-ai/sdk';
// The ingress decides on the original bytes: 1.0 is the integer 1, members are sorted.
const accepted = experimentalCandidateIngress(new TextEncoder().encode('{"b":1.0,"a":[]}'));
if (accepted.status === 'accept') console.log(new TextDecoder().decode(accepted.canonical)); // {"a":[],"b":1}
const refused = experimentalCandidateIngress(new TextEncoder().encode('{"a":1,"a":2}'));
if (refused.status === 'reject') console.log(refused.class, refused.offset); // DUPLICATE_KEY 7
// A producer signs an artifact from those bytes (hybrid keys, today's composite signature).
const registry = new AlgorithmRegistry();
const ed25519 = new Ed25519Provider();
const mlDsa65 = new MlDsa65Provider();
registry.registerSigner(ed25519);
registry.registerSigner(mlDsa65);
const c = await ed25519.generateKeypair();
const p = await mlDsa65.generateKeypair();
const keys = {
classical: { kid: c.kid, alg: 'ed25519', secretKeyB64: c.secretKeyB64, publicKeyB64: c.publicKeyB64 },
pq: { kid: p.kid, alg: 'ml-dsa-65', secretKeyB64: p.secretKeyB64, publicKeyB64: p.publicKeyB64 },
};
const artifact = await experimentalCandidateSignPayload(
registry, DCP_CONTEXTS.Intent, { bytes: new TextEncoder().encode('{"intent_id":"intent:1","risk":1.0}') }, keys,
);
console.log(artifact.signedPayload.payload_hash); // sha256:… of {"intent_id":"intent:1","risk":1}
DCP-05–09: módulos ampliados del protocolo¶
| Módulo | Especificación | Exportaciones principales |
|---|---|---|
lifecycle |
DCP-05 | LifecycleState, CommissioningCertificate, VitalityReport, DecommissioningRecord, VitalityMetrics, TerminationMode, DataDisposition |
succession |
DCP-06 | DigitalTestament, SuccessionRecord, MemoryTransferManifest, MemoryTransferEntry, MemoryClassification, SuccessorPreference, TransitionType, MemoryDisposition |
conflict-resolution |
DCP-07 | DisputeRecord, ObjectionRecord, DisputeType, DisputeStatus, EscalationLevel, ObjectionType |
arbitration |
DCP-07 | ArbitrationResolution, JurisprudenceBundle, AuthorityLevel |
rights |
DCP-08 | RightsDeclaration, RightEntry, ObligationRecord, RightsViolationReport, RightType, ComplianceStatus |
delegation |
DCP-09 | DelegationMandate, AdvisoryDeclaration, PrincipalMirror, InteractionRecord, AuthorityScopeEntry |
awareness-threshold |
DCP-09 | AwarenessThreshold, ThresholdRule, ThresholdOperator, ThresholdAction |
principal-mirror |
DCP-09 | PrincipalMirror (reexportación con utilidades de construcción) |
// Fragment, not a complete program: registry, humanKeys (the principal's CompositeKeyPair) and nonce as in the V2 Quick Start.
// Example: Lifecycle management (DCP-05), signed with the commissioning human's composite keys
import { createCommissioningCertificate, validateStateTransition } from '@dcp-ai/sdk';
import type { CommissioningCertificate } from '@dcp-ai/sdk';
const cert: CommissioningCertificate = await createCommissioningCertificate(registry, humanKeys, {
agent_id: 'agent:agent-001',
session_nonce: nonce,
human_id: 'human:human-001',
commissioning_authority: 'human:human-001',
purpose: 'Customer support triage',
initial_capabilities: ['browse', 'api_call'],
risk_tier: 'low',
principal_binding_reference: 'human:human-001',
});
validateStateTransition('commissioned', 'active'); // true
// Example: Delegation mandate (signed by the human)
import { createDelegationMandate } from '@dcp-ai/sdk';
const mandate = await createDelegationMandate(registry, humanKeys, {
mandate_id: 'mandate-001',
session_nonce: nonce,
human_id: 'rpr:human-001',
agent_id: 'agent:agent-001',
authority_scope: [{ domain: 'email', actions_permitted: ['read', 'draft'], limits: { max_messages_per_day: 50 } }],
valid_from: '2026-03-01T00:00:00Z',
valid_until: '2026-06-01T00:00:00Z',
revocable: true,
});
Tipos V2¶
Tipos principales que exporta el SDK:
SignedPayload— Envoltorio de datos firmados con metadatos de firma compuestaCompositeSignature— Contiene los componentes de firma clásico + PQKeyEntry— Entrada de clave pública con algoritmo, kid y material de claveSecurityTier—'routine' | 'standard' | 'elevated' | 'maximum'VerifierPolicy— Política del verificador: modo por defecto y modos específicos porrisk_tier; el nivel de seguridad solo puede hacer el modo más estrictoPQCheckpoint— Checkpoint poscuántico sobre entradas de auditoríaA2ASession— Estado de una sesión cifrada entre agentesA2AMessage— Sobre de mensaje A2A cifradoTelemetryConfig— Configuración del subsistema de observabilidad
Tipos de DCP-05–09:
LifecycleState—'commissioned' | 'active' | 'declining' | 'decommissioned'CommissioningCertificate— Registro de puesta en servicio del agente con condicionesVitalityReport— Métricas periódicas de salud y rendimientoDecommissioningRecord— Registro de fin de vida con disposición de los datosDigitalTestament— Planificación de la sucesión con disposición de la memoriaSuccessionRecord— Registro de una sucesión completadaMemoryTransferManifest— Manifiesto clasificado de transferencia de memoriaDisputeRecord— Registro de conflicto con niveles de escaladoArbitrationResolution— Resultado de un arbitraje con autoridad vinculanteJurisprudenceBundle— Colección de precedentes para resolver disputasRightsDeclaration— Derechos del agente con seguimiento del cumplimientoObligationRecord— Obligación con estado de aplicaciónRightsViolationReport— Informe de vulneración con gravedadDelegationMandate— Delegación de autoridad con alcance definidoAwarenessThreshold— Reglas que activan la intervención humana (human-in-the-loop)PrincipalMirror— Instantánea de las preferencias del principal
Protocolo A2A¶
Comunicación cifrada entre agentes:
// Fragment, not a complete program: sessionId and sessionKey come from the A2A handshake, remoteSession is the peer's session.
import { createSession, encryptMessage, decryptMessage } from '@dcp-ai/sdk';
// Create encrypted A2A session
const session = createSession(sessionId, sessionKey, 'agent:a', 'agent:b', 'elevated');
const encrypted = encryptMessage(session, { action: 'negotiate', data: { offer: 10 } });
const decrypted = decryptMessage(remoteSession, encrypted);
// The same message delivered again throws: each sequence is accepted once.
Observabilidad¶
La telemetría requiere instrumentación explícita: llama a los métodos de spans y métricas alrededor de las operaciones que quieras observar. La inicialización por sí sola no instrumenta las operaciones criptográficas. Este ejemplo solo inicializa la telemetría y lee el resumen actual:
import { dcpTelemetry } from '@dcp-ai/sdk';
dcpTelemetry.init({ serviceName: 'my-agent', enabled: true, exporterType: 'console' });
// Read metrics explicitly recorded so far; initialization alone records no crypto operations
const summary = dcpTelemetry.getMetricsSummary();
Dependencias¶
ajv+ajv-formats— Validación de JSON Schematweetnacl+tweetnacl-util— Criptografía Ed25519json-stable-stringify— JSON determinista@noble/post-quantum— Firmas poscuánticas ML-DSA-65 y SLH-DSA
Desarrollo¶
# Install dependencies
npm install
# Build (ESM + CJS + types)
npm run build
# Tests with Vitest
npm test
npm run test:watch
npm run test:coverage
# Type check
npm run lint
Licencia¶
Apache-2.0