@dcp-ai/sdk — SDK TypeScript para o DCP-AI v2.0¶
Tradução informativa da versão em inglês, atualizada em 02/11/2026. Em caso de divergência, prevalece a versão em inglês. Identificadores, comandos e blocos de código são mantidos sem tradução.
SDK oficial em TypeScript para o Digital Citizenship Protocol (DCP-AI). Crie, assine e verifique Citizenship Bundles com criptografia híbrida pós-quântica (Ed25519 + ML-DSA-65), assinaturas compostas, níveis de segurança adaptativos, comunicação entre agentes (A2A), observabilidade integrada e reforço para produção.
Instalação¶
Publicado no npm (26/10/2026). @dcp-ai/sdk 2.2.0 foi publicado como latest nessa data. Incluía as correções do verificador descritas em SECURITY.md, confiança no principal e strict-v1, cadeias de delegação, a janela de recepção A2A e as APIs experimentais de dcp-jcs-v2. O candidato continuava opt-in e não normativo, com dcp-jcs-v1 como padrão. Os exemplos abaixo usam APIs disponíveis desde a 2.2.0.
@dcp-ai/sdk 2.2.1. Esta versão também verifica os campos secundários SHA3-256 do VERIFICATION.md V2-11 quando estão presentes: audit_merkle_root_secondary, cada prev_hash_secondary e cada intent_hash_secondary. Os campos ausentes não são exigidos, então um bundle verificado pode não ter uma cadeia secundária completa; um campo presente que seja null, não seja uma string ou seja inconsistente é rejeitado. Não mudam as APIs, os campos de resultado, dcp-jcs-v1 nem o tratamento de hash_alg. A versão 2.2.0 não verifica esses campos. Os comandos de instalação abaixo indicam esta versão; para a disponibilidade dela no registro, veja Versões publicadas.
Veja o inventário de publicações npm para todas as versões.
Para desenvolver a partir de uma cópia do repositório, compile e instale o pacote 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
Início rápido (V1)¶
Um bundle V1 assinado com Ed25519. O RPR é assinado com a chave do principal e o passaporte com a do agente,
cada um sobre o JSON canônico do objeto sem o campo signature; createAuditEntry calcula
intent_hash e a cadeia prev_hash. verifySignedBundle verifica a assinatura do bundle, seus hashes e a
cadeia, não essas assinaturas internas: verifique-as você mesmo, como abaixo.
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
Início rápido (V2)¶
Um bundle V2 com assinaturas híbridas (Ed25519 + ML-DSA-65): cada artefato é um SignedPayload
({ payload, payload_hash, composite_sig }) assinado no seu contexto de separação de domínio; o principal assina
o RPR e o manifesto, e o agente, o passaporte, a intenção e a decisão 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 []
Referência da API¶
Criptografia básica (V1)¶
| Função | Descrição |
|---|---|
generateKeypair() |
Gera um par de chaves Ed25519 (publicKeyB64, secretKeyB64) |
signObject(obj, secretKeyB64) |
Assina um objeto e retorna a assinatura em base64 |
verifyObject(obj, signatureB64, publicKeyB64) |
Verifica uma assinatura com uma chave pública |
canonicalize(obj) |
Serialização JSON determinística (canônica) |
publicKeyFromSecret(secretKeyB64) |
Deriva a chave pública a partir de uma chave secreta |
Provedores criptográficos (V2)¶
| Exportação | Descrição |
|---|---|
Ed25519Provider |
Provedor de assinatura clássica Ed25519 |
MlDsa65Provider |
Provedor de assinatura pós-quântica ML-DSA-65 |
SlhDsa192fProvider |
Provedor de assinatura pós-quântica SLH-DSA-192f |
AlgorithmRegistry |
Registro que gerencia os provedores de algoritmos criptográficos disponíveis |
getDefaultRegistry() |
Retorna o registro de algoritmos único (singleton) |
registerDefaultProviders() |
Registra os provedores Ed25519, ML-DSA-65 e SLH-DSA-192f |
deriveKid(alg, publicKeyBytes) |
Deriva um identificador de chave a partir de uma chave pública |
Assinaturas compostas (V2)¶
| Função | Descrição |
|---|---|
compositeSign(registry, context, canonicalPayloadBytes, keys) |
Cria uma assinatura composta com algoritmos clássico + PQ |
compositeVerify(registry, context, canonicalPayloadBytes, compositeSig, classicalPubkeyB64, pqPubkeyB64?, strategy?) |
Verifica uma assinatura composta |
classicalOnlySign(registry, context, canonicalPayloadBytes, key) |
Assina somente com o algoritmo clássico (modo de contingência) |
Níveis de segurança (V2)¶
| Função | Descrição |
|---|---|
computeSecurityTier(intent, policyDecision?, mandateChainTier?) |
Calcula um SecurityTier (routine, standard, elevated, maximum) a partir de uma intenção e do risk_score da decisão de política (spec §14.2); o effective_tier de uma cadeia de delegação verificada só pode elevá-lo; valores desconhecidos falham fechados para elevated |
maxTier(a, b) |
Retorna o mais alto de dois níveis de segurança |
tierToVerificationMode(tier) |
Mapeia um nível para o modo de verificação exigido |
tierToCheckpointInterval(tier) |
Mapeia um nível para o intervalo de checkpoints PQ |
Construção de bundles¶
| Exportação | Versão | Descrição |
|---|---|---|
BundleBuilder |
V1 | Construtor fluente de Citizenship Bundles V1 |
BundleBuilderV2 |
V2 | Construtor fluente de bundles V2 com níveis de segurança e hash duplo |
signBundle(bundle, options) |
V1 | Assina um bundle V1 com Ed25519 |
signBundleV2(bundle, options) |
V2 | Assina um bundle V2 com assinaturas compostas |
signBundleV2ClassicalOnly(bundle, options) |
V2 | Assina um bundle V2 somente com assinaturas clássicas |
verifySignedBundle(signedBundle, publicKeyB64) |
V1 | Verifica um bundle V1 assinado |
verifySignedBundleV2(signedBundle, registry, policy?) |
V2 | Verifica um bundle V2 assinado (composto ou clássico) pelo mais estrito entre o modo da política e o modo do nível de segurança; informa resolved_tier |
Validação de esquemas (V2-2; na main, não em uma versão publicada). verifySignedBundleV2 e as demais funções de verificação V2 validam primeiro o envelope do bundle assinado e os quatro payloads contra schemas/v2, depois das verificações de política e de relógio e antes de o verificador conferir o manifesto, os hashes dos payloads, as chaves e as assinaturas; admitSignedBundleV2, que verifica primeiro, e o verificador experimental do candidato passam pela mesma barreira, enquanto evaluateAdmissionV2 recebe um resultado anterior e não a executa. Um bundle que falha retorna verified: false com erros schema: <JSON pointer> <keyword>[ <member>] (no máximo 16, depois schema: further errors not listed) e sem detalhes criptográficos. Membros desconhecidos de objetos fechados são rejeitados; em objetos abertos, como o target do intent, os membros declarados mantêm suas restrições e apenas os adicionais não são inspecionados. session_issued_at e session_expires_at do manifesto devem ser strings quando presentes, não null. date-time segue a verificação do ajv-formats 3.0.1 do verificador de referência, que não é RFC 3339 estrito. Um membro com valor undefined conta como ausente, como na canonicalização. Mudança de aceitação: bundles inválidos pelo esquema que antes eram verificados agora são rejeitados. Não há API só de esquema. Veja o mapa de cobertura.
Otimização de bundles (V2)¶
| Exportação | Descrição |
|---|---|
suggestPresentationMode(tier, opts) |
Recomenda um modo de apresentação conforme o nível de segurança e se é um primeiro contato ou uma relação de confiança já existente |
presentFull(bundle) |
Apresentação completa do bundle (sem omissões) |
presentCompact(bundle, pqCheckpointRef) |
Apresentação compacta: remove a assinatura PQ e, no lugar dela, referencia o checkpoint PQ mais próximo |
presentReference(bundle, verificationEndpoint) |
Apresentação só por referência: hash do bundle, nonce de sessão e um endpoint para verificar ou obter o bundle completo |
presentIncremental(baseBundle, updatedBundle, baseAuditCount) |
Apresentação incremental em relação a um bundle base verificado: novas entradas de auditoria, checkpoints PQ, manifesto e assinatura atualizados |
VerificationCache |
Armazena em cache resultados de verificação pelo hash do bundle. Um acerto não verifica de novo a confiança, a revogação nem a sessão, e nada é invalidado automaticamente; portanto, verifique novamente para a admissão. No código-fonte de main (não publicado), um context opcional escolhido por quem chama separa locatários ou políticas, get retorna uma cópia e invalidateByAgent remove as entradas do agente e todas as armazenadas sem agentIds; na 2.2.1 publicada a chave é apenas o hash e invalidateByAgent não remove nada; portanto, use invalidateByKid ou clear() (detalhes) |
Checkpoints PQ (V2)¶
| Exportação | Descrição |
|---|---|
PQCheckpointManager |
Gerencia a criação periódica de checkpoints pós-quânticos |
createPQCheckpoint(registry, events, sessionNonce, keys) |
Cria um checkpoint com assinatura PQ sobre entradas de auditoria |
auditEventsMerkleRoot(events) |
Calcula uma raiz de Merkle a partir de entradas de auditoria |
Hash duplo (V2)¶
| Função | Descrição |
|---|---|
sha256Hex(data) |
Hash SHA-256 (string hexadecimal) |
sha3_256Hex(data) |
Hash SHA3-256 (string hexadecimal) |
dualHash(data) |
Retorna { sha256, sha3_256 } para um hash duplo resistente à computação quântica |
dualMerkleRoot(leaves) |
Calcula uma raiz de Merkle usando folhas de hash duplo |
Protocolo A2A (DCP-04)¶
| Função | Descrição |
|---|---|
createAgentDirectory(organization, agents) |
Cria um diretório de agentes em memória |
findAgentByCapability(directory, requiredCapabilities) |
Encontra agentes por capacidade num diretório |
findAgentById(directory, agentId) |
Encontra um agente pelo ID |
createHello(initiatorBundle, kemPublicKeyB64, requestedCapabilities, securityTier) |
Cria uma mensagem Hello do handshake A2A |
createWelcome(responderBundle, kemPublicKeyB64, kemCiphertextB64, resolvedTier) |
Cria uma mensagem de resposta Welcome A2A |
deriveSessionId(agentIdA, agentIdB, nonceA, nonceB, sessionKey) |
Deriva um ID de sessão a partir dos nonces do handshake |
createCloseMessage(sessionId, reason, finalSequence, auditSummaryHash) |
Cria uma mensagem de encerramento de sessão |
createSession(sessionId, sessionKey, localAgentId, remoteAgentId, securityTier, rekeyingInterval?) |
Cria uma sessão A2A cifrada |
encryptMessage(session, payload) |
Cifra uma mensagem dentro de uma sessão A2A |
decryptMessage(session, message) |
Decifra uma mensagem dentro de uma sessão A2A; cada sequência é aceita uma única vez dentro da janela de 1000 mensagens (DCP-04 §8.4) |
needsRekeying(session) |
Verifica se uma sessão precisa de rotação de chaves |
generateResumeProof(session, lastSeenSequence) |
Gera uma prova para retomar a sessão |
verifyResumeProof(session, lastSeenSequence, proof) |
Verifica uma prova de retomada de sessão |
Observabilidade¶
| Exportação | Descrição |
|---|---|
dcpTelemetry |
Instância única de telemetria |
dcpTelemetry.init(config) |
Inicializa a telemetria com o nome do serviço e o exportador |
dcpTelemetry.startSpan(name) |
Inicia um span de rastreamento nomeado |
dcpTelemetry.endSpan(span) |
Encerra um span de rastreamento |
dcpTelemetry.recordSignLatency(ms) |
Registra a métrica de latência de assinatura |
dcpTelemetry.getMetricsSummary() |
Retorna um resumo agregado das métricas |
Reforço para produção¶
| Exportação | Descrição |
|---|---|
DcpErrorCode |
Enumeração de códigos de erro estruturados |
DcpProtocolError |
Classe de erro tipada para falhas no nível do protocolo |
createDcpError(code, details?) |
Fábrica para criar erros DCP estruturados |
isDcpError(error) |
Type guard para DcpProtocolError |
RateLimiter |
Limitador de taxa de janela fixa |
AdaptiveRateLimiter |
Limitador de taxa que se ajusta conforme a carga |
CircuitBreaker |
Disjuntor (circuit breaker) para chamadas externas |
withRetry(fn, config?) |
Tenta novamente uma função assíncrona com espera progressiva |
Outras da V2¶
| Exportação | Descrição |
|---|---|
generateSessionNonce() |
Gera um nonce de sessão criptográfico |
domainSeparatedMessage(context, canonicalPayloadBytes) |
Prefixa uma mensagem com um separador de domínio |
generateEmergencyRevocationToken() |
Gera um segredo de revogação e o seu compromisso de token sha256: |
buildEmergencyRevocation({ agentId, humanId, revocationSecret }) |
Monta uma solicitação de revogação de emergência a partir do segredo de revogação |
shamirSplit(secret, threshold, totalShares) |
Divide um segredo em totalShares partes; quaisquer threshold delas o reconstroem |
shamirReconstruct(shares) |
Reconstrói um segredo a partir de pelo menos threshold partes |
generatePiiSalt() |
Gera um pii_salt (32 bytes aleatórios em hexadecimal). Defina-o no RPR antes de assiná-lo e mantenha-o privado: blindRpr o omite do registro cegado, mas o RPR completo o mantém |
blindRpr(rpr) |
Substitui os dados pessoais de um RPR (legal_name, contact) por um compromisso de hash com sal; lança uma exceção se rpr.pii_salt estiver ausente ou malformado |
verifyBlindedRpr(fullRpr, blindedRpr) |
Verifica se um RPR ocultado corresponde ao registro completo; warnings sinaliza um compromisso legado sem sal |
isBlindedRpr(rpr) |
Type guard para BlindedResponsiblePrincipalRecordV2 |
computePiiHash(rpr) |
Calcula o compromisso de hash sobre os campos de dados pessoais de um RPR e o pii_salt (sem pii_salt: o valor sem sal, obsoleto) |
CborEncoder |
Classe codificadora CBOR |
CborDecoder |
Classe decodificadora CBOR |
cborEncode(value) |
Codifica um valor em bytes CBOR |
cborDecode(data) |
Decodifica bytes CBOR para um valor |
Admissão: Trusted Principals e strict-v1¶
Um bundle verificado é assinado pelas chaves que ele lista; isso não prova quem é o principal. A admissão aceita um bundle somente quando as assinaturas de principal foram feitas com chaves de um principal que consta no seu conjunto de confiança e, opcionalmente, sob o perfil strict-v1 fixado pelo operador (assinaturas híbridas, audiência assinada, uma sessão de no máximo 300 s, um snapshot de revogação recente).
// 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 })aceita um relógio opcional e informadetails.signer_keys; sem eles, comporta-se como antes.evaluateAdmissionV2avalia um resultado de verificação que você já tem (sem criptografia, sem E/S);revocationSubjectslista aquilo por que uma fonte de revogação precisa responder.- Guia:
docs/ADMISSION.md. Especificação e códigos:spec/profiles/admission.
Cadeias de delegação (DCP-09 §3.5)¶
Uma pessoa delega a um agente com um mandato raiz; esse agente pode repassar parte da sua autoridade a outro agente com um submandato que ele mesmo assina, e assim por diante. Cada salto só pode restringir o que recebeu, e toda a cadeia é verificada offline até chegar à pessoa.
| Função | Descrição |
|---|---|
createDelegationMandate(registry, humanKeys, params) |
Mandato raiz assinado pela pessoa (human_composite_sig); sub_delegation e minimum_security_tier são opcionais |
createSubMandate(registry, delegatorKeys, parent, params) |
Submandato assinado pelo agente do pai (delegator_composite_sig, contexto DCP-AI.v2.SubMandate) |
verifyMandateChain(registry, chain, revokedIds, keys, now?) |
Verifica a cadeia a partir da raiz: assinaturas até a pessoa, vínculos com o pai e profundidade, sub_delegation, janelas de validade aninhadas, atenuação (ações, data_classes e limits só se restringem), o nível de segurança mínimo e a revogação; retorna { 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 canonicalização dcp-jcs-v2 (opt-in)¶
Não é normativo nem está ativo: dcp-jcs-v1 continua sendo o único perfil de canonicalização, e nenhum padrão,
construtor, assinante ou verificador muda. Estas APIs existem apenas para quem optar por usá-las, inclusive no npm SDK 2.2.0. Um bundle candidato verificado não concede confiança nem admissão. Projeto, regras e contrato
completo: docs/proposals/canonicalization-candidate
(§15 entrada, §16 roteador, §17 verificador, §18 produtores).
| Exportação | Descrição |
|---|---|
experimentalCandidateIngress(bytes) |
Verifica os bytes originais de uma unidade: { status: 'accept', value, canonical, canonicalSha256 } ou { status: 'reject', class, offset } |
experimentalCandidateExtractUnit(bytes, transport) |
Extrai a unidade de um transporte bare ou envelope |
createExperimentalCandidateRouter(config) |
Roteia bytes: reject, v1_path (por um decodificador histórico nomeado) ou candidate |
createExperimentalCandidateVerifier(config).verify(bytes, registry, policy?, options?) |
Verifica a partir dos bytes originais com no máximo um verificador: o atual para dcp-jcs-v1 e, para o candidato, o atual aceitando exatamente dcp-jcs-v2 |
experimentalHistoricalAdapters |
Os decodificadores medidos (bufferUtf8JsonParse, textDecoderJsonParse); nenhum é o padrão |
experimentalCandidateSignPayload(registry, context, input, keys) |
Assina um artefato a partir de { bytes } ou { value }; sem perfil |
experimentalCandidateSignBundle(registry, parts, signer, options) |
Monta e assina um bundle cujo manifesto nomeia dcp-jcs-v2 |
ExperimentalCandidateProduceError |
Uma rejeição pelo domínio ou pela forma das entradas, lançada antes de chamar qualquer assinante (class, part, path, offset); os erros de chave, registro e provedor continuam os atuais |
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 estendidos do protocolo¶
| Módulo | Especificação | Exportações principais |
|---|---|---|
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 (reexportação com utilitários de construção) |
// 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¶
Principais tipos exportados pelo SDK:
SignedPayload— Invólucro de dados assinados com metadados de assinatura compostaCompositeSignature— Contém os componentes de assinatura clássico + PQKeyEntry— Entrada de chave pública com algoritmo, kid e material de chaveSecurityTier—'routine' | 'standard' | 'elevated' | 'maximum'VerifierPolicy— Política do verificador: modo padrão e modos específicos porrisk_tier; o nível de segurança só pode tornar o modo mais estritoPQCheckpoint— Checkpoint pós-quântico sobre entradas de auditoriaA2ASession— Estado de uma sessão cifrada entre agentesA2AMessage— Envelope de mensagem A2A cifradaTelemetryConfig— Configuração do subsistema de observabilidade
Tipos do DCP-05–09:
LifecycleState—'commissioned' | 'active' | 'declining' | 'decommissioned'CommissioningCertificate— Registro de comissionamento do agente com condiçõesVitalityReport— Métricas periódicas de saúde e desempenhoDecommissioningRecord— Registro de fim de vida com destinação dos dadosDigitalTestament— Planejamento da sucessão com destinação da memóriaSuccessionRecord— Registro de uma sucessão concluídaMemoryTransferManifest— Manifesto classificado de transferência de memóriaDisputeRecord— Registro de conflito com níveis de escalonamentoArbitrationResolution— Resultado de uma arbitragem com autoridade vinculanteJurisprudenceBundle— Coleção de precedentes para resolver disputasRightsDeclaration— Direitos do agente com acompanhamento da conformidadeObligationRecord— Obrigação com status de cumprimentoRightsViolationReport— Relatório de violação com gravidadeDelegationMandate— Delegação de autoridade com escopo definidoAwarenessThreshold— Regras que acionam a intervenção humana (human-in-the-loop)PrincipalMirror— Snapshot das preferências do principal
Protocolo A2A¶
Comunicação 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.
Observabilidade¶
A telemetria exige instrumentação explícita: chame os métodos de spans e métricas ao redor das operações que deseja observar. A inicialização por si só não instrumenta operações criptográficas. Este exemplo apenas inicializa a telemetria e lê o resumo atual:
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();
Dependências¶
ajv+ajv-formats— Validação de JSON Schematweetnacl+tweetnacl-util— Criptografia Ed25519json-stable-stringify— JSON determinístico@noble/post-quantum— Assinaturas pós-quânticas ML-DSA-65 e SLH-DSA
Desenvolvimento¶
# 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
Licença¶
Apache-2.0