Limites operacionais¶
Tradução informativa da versão em inglês, atualizada em 08/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.
O que limita as entradas, as fontes de confiança e de revogação, a concorrência, a desconexão do cliente e o cache de veredictos dos SDKs de TypeScript, Python, Go e Rust e dos adaptadores Express e FastAPI, e o que você, como operador, precisa definir. Descreve o código-fonte deste repositório; um pacote publicado tem o que a sua própria versão inclui (veja Versões publicadas).
Nada nesta página altera o formato de wire, dcp-jcs-v1, um perfil ou uma assinatura. Quatro camadas definem limites, e elas são mantidas separadas:
- O protocolo fixa comprimentos por campo e as janelas de atualidade do strict-v1.
- Os SDKs verificam, admitem e compõem. Essas APIs não recebem prazo nem sinal de cancelamento; o helper de lista de negação do Python tem um prazo de download próprio, separado da criptografia.
- Os adaptadores acrescentam orçamentos, prazos, capacidade e cancelamento, todos opcionais (opt-in).
- Você escolhe os seus valores, o inquilino, o limite de taxa e a infraestrutura à frente deles.
A configuração de exemplo no final é um ponto de partida, não um padrão nem uma regra.
Quem define o quê¶
| Limite | De onde vem |
|---|---|
| Duração da sessão e idade do snapshot de revogação no strict-v1: no máximo 300 s | O perfil de admissão (normativo) |
sig_b64 com no máximo 66 476 caracteres, uma chave pública com no máximo 3456, intended_verifier com no máximo 256 |
Os esquemas V2 (normativo, por campo) |
| Número de entradas de auditoria, checkpoints PQ, chaves, principais confiáveis, entradas da lista de negação, mandatos de uma cadeia, bundles de delegadores | Nenhum limite de esquema nem de SDK: só o que você deixa entrar os limita. O custo de verificação cresce com eles |
| Profundidade de aninhamento | Nenhum orçamento comum. O parser e a pilha de cada runtime recusam no seu próprio nível; a API opcional admitSignedBundleV2FromBytes recusa mais de 64 níveis |
| Corpo da requisição | Express: o limit do seu parser de corpo. FastAPI: max_body_bytes, opcional e sem valor padrão. Servidor de referência: 1 MiB |
| Bundle em um cabeçalho | Os limites de cabeçalho do seu servidor e de cada proxy (o padrão do Node é 16 KiB); max_body_bytes não cobre cabeçalhos |
| Download da lista de negação | Os helpers de lista de negação: no máximo 16 MiB, sem redirecionamentos, com um prazo que você define |
| Prazos e capacidade das fontes | O supervisor de fontes, opcional e sem capacidade padrão |
| Cache de veredictos do Express | Desativado por padrão (cacheTtlSeconds 0); no máximo cacheMaxEntries (1000 por padrão) quando ativado |
| Inquilino | A sua própria autenticação, nunca um cabeçalho escolhido pelo cliente |
| Taxa de requisições | Você (o exemplo do Express usa express-rate-limit) |
Entradas¶
- Envie o bundle no corpo, com um orçamento de corpo verificado antes do parsing. Um bundle V2 típico com assinaturas híbridas é maior que o limite de cabeçalho padrão do Node.
- O orçamento de corpo é um orçamento de entrada que você define. Cada assinatura, entrada de auditoria e checkpoint PQ acrescenta trabalho, e os esquemas não limitam quantos existem. Junto com as cardinalidades que você aceita, a sua política de verificação e a CPU e a concorrência que você permite fora do adaptador, ele limita quanto trabalho uma requisição pode trazer. Não é um prazo de CPU e não limita as APIs que o seu código chama com objetos construídos fora do HTTP. Dimensione-o para os bundles que você espera, não para o maior possível.
- Profundidade. No código atual e nas amostras locais dos mantenedores, um corpo mais aninhado do que o runtime suporta foi recusado, não admitido, em um ponto diferente em cada adaptador: o Express relatou uma falha de verificação (
Malformed signed bundle: Maximum call stack size exceeded); o FastAPI respondeu 403Missing DCP signed bundle, porque não conseguiu ler o corpo como JSON. Os limites de profundidade variam entre runtimes e não fazem parte do protocolo.
Fontes de confiança e de revogação¶
Quando uma fonte é lenta ou nunca responde, há quatro ações distintas. Um adaptador pode fazer as três primeiras; só a própria fonte pode fazer a quarta.
- Parar de esperar. A requisição recebe uma resposta de falha.
- Recusar o resultado tardio. Um valor que chega depois do prazo nunca é usado.
- Pedir o cancelamento. Um
AbortSignalé abortado (Express) ou a tarefa é cancelada (FastAPI). - Parar o trabalho. Só uma fonte que coopera faz isso.
Com um supervisor de fontes (sourceSupervisor e supervise, ou source_supervisor e supervise):
- Uma fonte de confiança que não responde a tempo resulta em 503, e uma fonte de revogação, em 403
REVOCATION_UNAVAILABLE. - O resultado tardio nunca é usado.
- O prazo é medido com um relógio monotônico, mas só vence quando o event loop está livre: uma CPU ocupada ou uma fonte que bloqueia o loop atrasa a resposta.
- O supervisor conta a invocação que ele possui até que essa invocação termine de fato. Uma fonte que ignora o cancelamento mantém a sua vaga depois da resposta, até terminar.
- O trabalho que uma fonte inicia por conta própria (threads, processos, tarefas que ela não retorna) não é contado nem limitado pelo adaptador.
Sem supervisor:
| Fonte | Express | FastAPI |
|---|---|---|
| Confiança | Sem prazo: a requisição espera a fonte | Sem prazo. Uma fonte síncrona roda no event loop e o bloqueia |
| Revogação | revocationTimeoutMs: a resposta é enviada, a fonte continua rodando e não é contada |
revocation_timeout usa asyncio.wait_for, que espera a fonte terminar de se cancelar. Uma fonte que suprime o cancelamento atrasa a resposta, e o seu snapshot tardio é usado. Uma fonte síncrona continua rodando na sua thread |
Em produção, use um supervisor. A opção antiga checkRevocation do Express não tem prazo nem limite de tamanho, é recusada pelo strict-v1 e fica fora desta orientação.
Listas de negação:
- TypeScript. O helper do Express
denyListRevocationSourceaborta o seu download quando vence o prazo ou com o sinal do supervisor, inclusive enquanto o corpo está sendo lido. - Python. O helper
deny_list_revocation_sourceverifica o seu prazo entre leituras, por isso não limita a resolução DNS nem uma linha de status e cabeçalhos enviados devagar. Dê a ele um orçamentoDenyListWorkers: ele limita as threads de download, que são uma reserva separada da vaga do supervisor.DenyListWorkersconta um download até que o seu fetch retorne; a sua thread ainda pode estar terminando por um instante depois disso. - Go e Rust não têm fonte de lista de negação: você constrói o snapshot de revogação e o passa para a admissão.
Cada invocação de um helper de lista de negação baixa a lista de novo (uma requisição recusada antes da etapa de revogação não baixa). Uma lista grande baixada por muitas requisições ao mesmo tempo usa buffers e objetos para cada uma delas: mantenha as listas pequenas. Se você servir snapshots a partir de um cache próprio, mantenha-o por inquilino e dentro da janela de atualidade do perfil: as regras de admissão não mudam.
Concorrência e inquilinos¶
- A capacidade pertence ao objeto supervisor, não ao processo. Compartilhe um supervisor para compartilhar uma capacidade. As fontes de confiança e de revogação de uma requisição ocupam vagas do mesmo supervisor.
- A etapa que recusa primeiro depende do que é supervisionado. Com uma função
trustedPrincipalssupervisionada (como na configuração de exemplo, que supervisiona as duas fontes), uma requisição precisa primeiro de uma vaga de confiança: enquanto as vagas estão ocupadas, por chamadas de confiança ou de revogação, uma requisição nova é recusada na etapa de confiança com 503, antes de qualquer fonte ser chamada. Com um conjunto de confiança estático e só a fonte de revogação supervisionada, não há vaga de confiança a ocupar, e uma requisição sem vaga livre é recusada na etapa de revogação com 403REVOCATION_UNAVAILABLE. - Sem
maxInFlightPerTenant/max_in_flight_per_tenant, não há isolamento entre inquilinos: um inquilino pode ocupar todas as vagas. - O inquilino precisa vir da sua autenticação. Nunca o tome de um cabeçalho definido pelo cliente.
- A verificação usa a CPU. Em TypeScript e Python ela roda no event loop; em Go e Rust, na thread de quem chama. Com concorrência, as requisições fazem fila para ela. A capacidade limita as fontes, não a CPU nem a concorrência HTTP: limite essas com o seu controle de taxa, os seus workers e a sua infraestrutura.
Desconexão do cliente¶
Com cancelOnDisconnect (Express) ou cancel_on_disconnect (FastAPI, que também precisa de max_body_bytes):
- O que conta como ir embora. O cliente vai embora de fato quando a resposta é fechada antes de ser enviada, ou quando o upload é interrompido. O Express não toma como abandono o próprio
closede uma requisição depois do seu corpo. - O que acontece. Só a invocação supervisionada da fonte dessa requisição é cancelada. Nada mais é feito pela requisição: nem handler, nem escrita no cache, nem
onFailure, nem resposta. - O que não faz. Uma verificação que já está rodando não é interrompida. Uma fonte que ignora o cancelamento mantém a sua vaga até terminar.
Sem cancelamento nos SDKs¶
Nenhuma API de verificação, admissão ou composição dos SDKs (admissão delegada e ativação incluídas) recebe um prazo, um AbortSignal ou um context.Context. O prazo de download do helper de lista de negação do Python é separado e não se aplica à criptografia:
- TypeScript e Python verificam no event loop.
- Go e Rust são síncronos.
- WASM é síncrono.
Uma verificação iniciada roda até o fim. Limite o seu custo com o orçamento de entrada, e a sua concorrência fora do SDK.
O cache de veredictos (Express)¶
- Desativado por padrão. Mantenha-o desativado, a menos que as suas próprias medições o justifiquem.
- O que ele guarda no caminho de admissão (
trustedPrincipals, com ou sem strict-v1): só o resultado da verificação criptográfica, sob uma chave hash; não o bundle, um snapshot nem qualquer autorização. A confiança do principal é avaliada em cada requisição, em cache ou não, e também as verificações do perfil e a revogação quando estão configuradas. Um conjunto de confiança sem perfil não ganha as verificações de revogação do strict-v1. - No caminho só de verificação (sem
trustedPrincipals) ele também guarda o agente extraído e os identificadores das suas chaves. As verificações de confiança e de perfil precisam do caminho de admissão, que então deixa de ser só de verificação;checkRevocation, se ativada, é avaliada em cada requisição. - A chave. Cobre o bundle canônico, a política de verificação e o inquilino.
- Expiração e despejo. Uma entrada expira com o seu TTL, com a sessão do bundle ou com as suas chaves. Quando o cache está cheio, a entrada usada há mais tempo é despejada.
- Memória. A memória do processo não é só o cache, e o tamanho do bundle não se traduz no tamanho de uma entrada.
- Outros adaptadores e o SDK. O FastAPI não tem cache. O
VerificationCachedo SDK de TypeScript é um utilitário separado que os adaptadores não usam.
Delegação nos adaptadores¶
- Onde está a admissão delegada. Os SDKs de TypeScript, Python, Go e Rust oferecem a admissão delegada (
admitDelegatedBundleV2e equivalentes), a ativação (evaluateDelegationActivationV2) e a admissão a partir de bytes (admitSignedBundleV2FromBytes). - Onde não está. O WASM não as expõe, e nenhum dos adaptadores as chama.
mandateIderequire_dcp_mandated.req.dcpAgent.mandateIddo Express é copiado do manifesto do bundle e não verifica nenhuma cadeia de mandatos.require_dcp_mandateddo FastAPI só verifica ummandate_idque o middleware nunca preenche para bundles V2, e também não verifica nenhuma cadeia.- O que a sua aplicação faz. Ela mesma chama a API do SDK, limita a evidência que aceita (comprimento da cadeia, número de bundles de delegadores, bytes) e decide a autorização e a repetição.
Outras superfícies¶
| Superfície | Limites |
|---|---|
OpenClaw (@dcp-ai/openclaw) |
O mesmo prazo, a mesma capacidade e o mesmo helper de lista de negação que o Express. A sua entrada é o objeto que o host já fez parsing, sem orçamento próprio, e ele não tem cancelOnDisconnect |
Servidor de referência (/v2/admit) |
Corpos de no máximo 1 MiB. Um timeout de inatividade do socket de 30 s que responde 408 sem cancelar o trabalho. Revogação em 5000 ms por padrão, abortando o download. O arquivo de confiança é lido em cada admissão. Estado em memória, uma única instância. Para reprodução, não para produção |
CLI (dcp-ai) |
Uma invocação por processo, sem prazo nem orçamento próprios: quem a chama precisa impô-los |
Uma configuração de exemplo¶
Um ponto de partida para um piloto, usado para exercitar os adaptadores. Não é um padrão, nem uma recomendação para qualquer implantação, nem parte do protocolo. Dimensione cada valor para os seus bundles, as suas fontes e o seu hardware.
| Ajuste | Valor de exemplo |
|---|---|
| Orçamento de corpo | 64 KiB (limit do parser no Express; max_body_bytes=65536 no FastAPI) |
| Transporte | Corpo, não cabeçalho |
| Supervisor | 32 invocações ao mesmo tempo, 8 por inquilino |
| Prazo de confiança | Express: supervise.trustTimeoutMs: 2000 (milissegundos). FastAPI: supervise={"trust_timeout": 2.0} (segundos) |
| Prazo de revogação, supervisionado | Express: revocationTimeoutMs: 2000 com supervise.revocation: true. FastAPI: revocation_timeout=2.0 com supervise={"revocation": True} |
| Download da lista de negação | Helper do Express: timeoutMs: 1500. Helper do Python: timeout=1.5. Dentro do prazo de revogação: pede que o download termine antes, não garante que o seu trabalho termine |
| Downloads do Python ao mesmo tempo | DenyListWorkers(max_workers=8, max_workers_per_tenant=2): conta downloads ativos, não o número exato de threads |
| Desconexão do cliente | Ativada |
| Cache de veredictos do Express | Desativado |
| Evidência delegada (a sua aplicação) | No máximo 4 mandatos em uma cadeia, cada bundle dentro do orçamento de corpo |
O que esta página não afirma:
- Números de capacidade, latência ou memória para a sua implantação.
- A ausência de vazamentos.
- A aceitação de evidência além do que cada API documenta.
Meça no seu próprio ambiente.