Admissão: principais confiáveis e o perfil strict-v1¶
Tradução informativa da versão em inglês, atualizada em 26/10/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.
Um bundle verificado prova sua integridade e que foi assinado pelas chaves que lista. Qualquer pessoa pode criar um registro de principal responsável com qualquer human_id e chaves próprias, então a verificação sozinha não diz quem é o principal.
A admissão acrescenta o que um operador precisa para aceitar evidências apenas de principais em que já confia:
- Principais confiáveis. O operador fornece um conjunto de confiança: o
human_idde cada principal e as chaves públicas com que ele pode assinar. Um bundle só é admitido se as chaves que de fato verificaram as assinaturas do principal (o RPR e o manifesto do bundle, clássica e pós-quântica) forem chaves confiáveis desse principal, ativas e dentro da janela de validade. - O perfil strict-v1. Opcional, versionado e fixado pelo operador: assinaturas híbridas em todos os níveis de risco, um
intended_verifierassinado igual aoverifier_iddo operador, uma janela de sessão assinada de no máximo 300 segundos e um snapshot de revogação com no máximo 300 segundos de idade.
Estado de publicação (26/10/2026). A admissão e
strict-v1estão publicados em@dcp-ai/sdk2.2.0, Express/OpenClaw 2.1.0, Pythondcp-ai2.10.0, Gov2.11.0e Rustdcp-ai2.11.0. Veja npm, Python, Go e Rust. O servidor de referência e o CLI da raiz continuam sendo executados de um checkout.
A definição normativa, com todas as regras e códigos de erro, é spec/profiles/admission. Os vetores de teste compartilhados estão em tests/interop/v2/admission_vectors.json.
É opt-in¶
A admissão só se aplica ao código que define um conjunto de confiança ou um perfil, e verified mantém o seu significado. Os verificadores também ganham um relógio opcional e informam as chaves que usaram, na mesma verificação. As mesmas mudanças também alteram a própria verificação, à parte da admissão: veja Compatibilidade.
O conjunto de confiança¶
Quem o escreve é o operador; ele nunca vem de um bundle, de uma requisição, de um parâmetro de ferramenta nem de uma URL de um RPR. Esquema: schemas/v2/principal_trust_set.schema.json.
{
"trust_set_version": "2026-11-02.1",
"principals": [
{
"human_id": "human:acme-ops",
"keys": [
{ "kid": "…", "alg": "ed25519", "public_key_b64": "…", "status": "active" },
{ "kid": "…", "alg": "ml-dsa-65", "public_key_b64": "…", "status": "active", "expires_at": "2027-06-01T00:00:00Z" }
]
}
]
}
Ele é recusado ao ser carregado se um kid não for derivado da sua chave, se uma chave estiver malformada, se um identificador estiver vazio ou se um human_id ou kid aparecer duas vezes. Os campos status, not_before e expires_at do conjunto de confiança são os que valem: um bundle que liste uma chave como active não pode restabelecer uma que o operador revogou.
Rotação. Adicione as chaves novas (com not_before, se quiser), mantenha as antigas active durante a sobreposição e depois aposente-as (revoked, expired, um expires_at ou removendo-as), cada vez com um trust_set_version novo. Um bundle não pode rotacionar as próprias chaves.
As chaves do agente não são listadas: a assinatura do principal sobre o manifesto cobre o hash do passaporte, e isso vincula o agente e suas chaves ao principal.
O perfil strict-v1¶
Esquema: schemas/v2/admission_profile.schema.json.
| Requisito | Valor |
|---|---|
| Versão do bundle | Somente V2 |
| Assinaturas | Clássica e PQ, ambas válidas, em todos os níveis de risco (hybrid_required; pq_only não o satisfaz) |
| Principal | Confiável (acima), com os dois componentes da assinatura ancorados |
| Vinculação de sessão | Obrigatória |
| Janela de sessão | session_issued_at e session_expires_at assinados no manifesto; no máximo max_session_duration_seconds (padrão e máximo 300); iniciada dentro de 30 s de desvio de relógio; não expirada |
| Audiência | intended_verifier assinado igual a verifier_id |
| Revogação | Um snapshot do agente e de cada chave de assinatura verificada, com no máximo max_revocation_age_seconds de idade (padrão e máximo 300); nenhuma revogada ou desconhecida |
O perfil define requisitos, não padrões. max_session_duration_seconds, max_revocation_age_seconds e o verifier_policy opcional só podem endurecê-lo; um valor que o afrouxaria (allow_v1_bundles: true, require_*: false, um modo clássico ou pq_only, uma sessão mais longa, outro verifier_id) é um erro de configuração. Os adaptadores também recusam as próprias opções que o afrouxariam.
Fontes de revogação¶
A revogação é resolvida por uma fonte escolhida pelo operador; nenhum serviço central é necessário. Uma fonte recebe os sujeitos (agent_ids e kids) e retorna um snapshot:
{ "checked_at": "2026-11-02T10:00:30Z", "agents": { "agent:acme-bot": "not_revoked" }, "keys": { "<kid>": "not_revoked" } }
checked_at é o momento em que a fonte estabeleceu os status. Uma fonte que excede o tempo, falha ou não retorna nada faz a requisição falhar com REVOCATION_UNAVAILABLE; nada é tratado, por padrão, como não revogado.
denyListRevocationSource (integrações TypeScript, o verificador de referência) e deny_list_revocation_source (Python) leem uma lista de bloqueio completa no formato de /.well-known/dcp-revocations.json do serviço de revogação de referência, com a URL e os cabeçalhos do operador, um tempo limite, sem redirecionamentos e com no máximo 16 MiB. A URL precisa ser https (http simples só para um host de loopback). Se faltar revocations ou kid_revocations (qualquer um dos dois campos), a lista é inválida, não vazia: ela pode ter sido truncada.
Resultados¶
| Campo | Significado |
|---|---|
verified |
O veredito do verificador, inalterado |
principal_trust |
trusted, untrusted ou not_evaluated (sem conjunto de confiança, ou não verificado) |
profile / profile_compliance |
strict-v1 ou null / compliant, non_compliant, not_evaluated |
admitted |
A decisão final com esta configuração |
action_authorized |
Sempre false: a admissão não autoriza nenhuma ação |
recorded_decision |
A decisão de política que o bundle registra (approve, escalate, block) |
trust_set_version |
O conjunto de confiança usado |
errors |
{ code, message }; os códigos estão listados na especificação, § 5.3 |
Um bundle que registra uma decisão block ou escalate pode ser admitido: ele é evidência dessa decisão, não uma permissão.
Como usar¶
TypeScript (@dcp-ai/sdk)¶
import { admitSignedBundleV2, createPrincipalTrustStore, resolveStrictV1Profile } from '@dcp-ai/sdk';
const trust = createPrincipalTrustStore(trustSetJson); // throws AdmissionConfigError
const profile = resolveStrictV1Profile({ profile: 'strict-v1', verifier_id: 'api.acme.example' });
const result = await admitSignedBundleV2(signedBundle, registry, { profile, trust, revocation: snapshot });
if (!result.admitted) throw new Error(result.errors.map((e) => e.code).join(', '));
evaluateAdmissionV2 recebe um resultado de verificação que você já tem (sem criptografia, sem E/S) e confia nele: passe apenas o resultado da verificação desse mesmo bundle, no mesmo now, sob a política do perfil. revocationSubjects diz a uma fonte pelo que ela precisa responder. Python, Go e Rust expõem as mesmas funções; veja a especificação, § 8.
Express (@dcp-ai/express)¶
import { dcpVerify, denyListRevocationSource } from '@dcp-ai/express';
app.use('/api/agents', dcpVerify({
trustedPrincipals: trustSetJson, // or ({ tenant }) => trust set
profile: { profile: 'strict-v1', verifier_id: 'api.acme.example' },
revocationSource: denyListRevocationSource({ url: 'https://revocation.acme.example/.well-known/dcp-revocations.json' }),
tenant: (req) => req.auth.tenant, // from your own authentication
cacheTtlSeconds: 60,
}));
Uma requisição só chega à rota quando o bundle é admitido; req.dcpAgent.admission traz o resultado. Uma função de confiança que lança uma exceção, ou que retorna undefined, null ou qualquer coisa que não seja um conjunto de confiança válido (por exemplo, um locatário para o qual não tem entrada), recusa a requisição com 503: isso nunca significa «sem requisito de confiança». O cache guarda apenas a verificação criptográfica, indexada pelo bundle, pela política do verificador e pelo locatário; a confiança, as verificações do perfil e a revogação são avaliadas a cada requisição, então remover um principal ou revogar uma chave vale a partir da próxima. Executável: integrations/express/examples/strict-v1.mjs.
FastAPI (dcp_ai.fastapi)¶
DCPVerifyMiddleware(app, require_bundle=True, trusted_principals=..., profile=..., revocation_source=deny_list_revocation_source(url), tenant=...), com as mesmas regras (require_bundle tem padrão False no FastAPI e precisa ser True com principais confiáveis); veja integrations/fastapi/README.md.
OpenClaw (@dcp-ai/openclaw)¶
configureVerifier({ trustedPrincipals, profile: { profile: 'strict-v1', verifier_id: 'agent-gateway' }, revocationSource });
dcp_verify_bundle passa então a informar admitted e só nomeia o agente para um bundle admitido. Sob o perfil, o parâmetro verifier_policy do modelo é recusado, e os parâmetros que a ferramenta não define são ignorados. Executável: integrations/openclaw/examples/strict-v1.mjs.
Servidor de referência e CLI¶
O servidor de verificação (npm run server) expõe POST /v2/admit quando o operador define DCP_TRUSTED_PRINCIPALS_FILE (recarregado sempre que o conteúdo muda) e, para strict-v1, também DCP_ADMISSION_PROFILE_FILE e DCP_REVOCATION_LIST_URL (com um DCP_REVOCATION_LIST_AUTHORIZATION opcional). Ele se recusa a iniciar com uma configuração contraditória, ou com uma variável de admissão definida mas vazia. Offline:
# the reference CLI of a checkout; it reads only files in the current directory
node /path/to/dcp-ai/bin/dcp.js admit-bundle bundle.signed.json --trust trust.json --profile profile.json --revocation snapshot.json
Compatibilidade¶
- API de verificação.
verifySignedBundleV2/verify_signed_bundle_v2/VerifySignedBundleV2WithPolicy/verify_signed_bundle_v2_with_policymantêm as suas assinaturas. As chaves de assinatura sãodetails.signer_keysem TypeScript e Python e o novo campoSignerKeysem Go. Em Rust,V2VerificationResultmantém seus dez campos, então o código que o constrói por inteiro ou o desestrutura sem..continua compilando; as chaves de assinatura vêm do novoverify_signed_bundle_v2_at, que retornaV2VerificationDetails(resultesigner_keys) e é o que a admissão recebe. O código escrito contra a API de admissão antes da sua publicação lêverification.result.verifiedem vez deverification.verified. - Comportamento da verificação. As mesmas mudanças alteram o comportamento da verificação, à parte da admissão. Assinaturas Ed25519 com S >= L agora são rejeitadas. Separadamente, os verificadores Go e Rust agora aplicam
accepted_classical_algseaccepted_pq_algsde uma política, que antes recusavam por não serem aplicados: sob uma política assim, um bundle fora das listas falha, e um dentro delas agora pode ser verificado. O CHANGELOG lista todas as mudanças. - Bundles. Nenhuma mudança no formato de transmissão. strict-v1 precisa de bundles assinados com
session_issued_at,session_expires_ateintended_verifierno manifesto, que todos os assinantes dos SDKs gravam quando recebem uma expiração e uma audiência. - Com um conjunto de confiança, bundles V1 são recusados: eles não trazem nenhuma assinatura de principal para ancorar.
- Opções das integrações. Novas e opcionais. Com principais confiáveis,
requireBundle: false(Express) erequire_bundle=False(FastAPI) são erros de configuração: uma requisição sem bundle chegaria à rota. Sob o perfil, também o sãoverifierPolicy,requireHybrid: falsee as opçõescheckRevocation(Express),verifier_policy(FastAPI) everifierPolicy(OpenClaw).
Limites¶
- Repetição. O nonce de sessão, a audiência e a expiração limitam onde e por quanto tempo um bundle pode ser repetido; não o tornam de uso único. Um verificador que precise de uso único mantém o próprio registro de nonces ou hashes de bundles consumidos; a admissão não os consome.
- Autorização.
action_authorizedé semprefalse. Vincular uma autorização externa a uma ferramenta, aos seus argumentos e à sua execução é uma fase posterior. - A identidade é tão boa quanto o conjunto de confiança e o processo que o mantém.
- A revogação é tão recente quanto o snapshot: uma revogação posterior a
checked_até vista com o próximo snapshot.