28 KiB
AS-BUILT — Enterprise MCP Service Bus (RI)
Referência técnica do que existe hoje na Reference Implementation (RI): responsabilidade de
cada componente, onde o código vive, contratos de entrada/saída em nível alto (o detalhe fino
de cada contrato está em interfaces/), o que foi validado e contra qual
dependência real, e as limitações arquiteturais reais encontradas durante a implementação.
Público-alvo: quem vai integrar com ou estender a RI. Para quem vai rodar a RI, ver
How-To.md. Para quem vai avaliar/adaptar a RI para um cenário real, ver
Assumptions.md e Production-Recommendations.md.
Este documento sucede o backlog de planejamento docs/RI-PLANNING.md (removido do
repositório após a conclusão do marco M4, com histórico preservado no git) — a informação
factual desse backlog que ainda é relevante para quem usa o produto acabado foi migrada para
cá, para Assumptions.md e para Production-Recommendations.md.
Status
Todo o backlog planejado (marcos M0 a M4) está implementado, testado e validado. 261 testes
automatizados (contagem verificada via pytest --collect-only nesta revisão): 254 passam com
pytest --forked (unit + e2e + contract + adversarial), mais 7
testes de aceite de segurança (tests/e2e/test_security_acceptance.py) que exigem Docker real e
são rodados separadamente pelo custo de build+Keycloak por execução. Lógica de produção sem
mocks no núcleo de segurança e orquestração: OPA real (subprocess), Keycloak real (Docker),
LangGraph real com checkpointer SQLite, um LLM real (endpoint OpenAI-compatible), JWKS HTTP real
(stand-in leve para o resto da suíte e2e) com JWT RS256 reais, YAML reais de config/, SQLite
real para o Registry.
Índice de épicos
Cada EP-XX-TXX que aparece em comentários/docstrings do código-fonte referencia um destes
épicos. Esta tabela é o que substitui docs/RI-PLANNING.md §8.6 como fonte de contexto para
essas referências.
| Épico | Título | Módulo/teste principal |
|---|---|---|
| EP-00 | Bootstrap e tooling da RI | pyproject.toml, Makefile, .github/workflows/ci.yml, src/emcp_bus/common/config.py |
| EP-01 | Identidade do MCP Client e autenticação | src/emcp_bus/identity/*.py, deploy/keycloak/realm-export.json |
| EP-02 | Global Capability Registry e publishing pipeline | src/emcp_bus/registry/*.py |
| EP-03 | PDP / Policy Engine (OPA) | src/emcp_bus/pdp/*.py, config/policies/*.rego |
| EP-04 | Entitlement Manager | src/emcp_bus/entitlement/*.py |
| EP-05 | MCP Gateway / PEP | src/emcp_bus/gateway/*.py |
| EP-06 | Enterprise MCP Fabric e servidores de domínio de exemplo | src/emcp_bus/fabric/*.py, services/example_mcp_servers/* |
| EP-07 | Downstream identity (inbound vs outbound) | src/emcp_bus/downstream/*.py |
| EP-08 | Auditoria, correlação e observabilidade | src/emcp_bus/audit/*.py, src/emcp_bus/common/otel.py |
| EP-09 | Profile Intelligence (contrato genérico) | src/emcp_bus/profile_intelligence/*.py |
| EP-10 | Offering Filter | src/emcp_bus/offering_filter/service.py |
| EP-11 | Service Orchestrator (NBA/NBO) | src/emcp_bus/orchestrator/*.py |
| EP-12 | Agent Runtime e channel adapters | src/emcp_bus/agent_runtime/*.py |
| EP-13 | Agente de exemplo e instrumentação Langfuse | agents/example_agent/* |
| EP-14 | Human-in-the-loop / Approval workflow | src/emcp_bus/approval/*.py |
| EP-15 | Testes de segurança e adversariais | tests/contract/, tests/e2e/test_security_acceptance.py, tests/adversarial/, tests/e2e/test_fail_closed.py |
| EP-16 | Deploy final e documentação | docker-compose.yml, docs/adr/ |
Dentro de cada épico, TXX numera as tarefas (ex.: EP-05-T03 = terceira tarefa do épico
EP-05/Gateway — o cache hint de tools/list). Os ADRs referenciados por número (ADR-XXX) são
arquivos individuais em ../../docs/adr/.
Componentes
Identidade do MCP Client (EP-01)
Responsabilidade: autenticar a identidade de workload que faz a chamada, nunca a identidade
autodeclarada em clientInfo ou em qualquer campo do payload. src/emcp_bus/identity/authn.py
(TokenValidator) valida um bearer token OAuth2/OIDC contra o JWKS do IdP (assinatura RS256,
exp, iss, aud); o client_id confiável vem exclusivamente da claim azp/client_id do
token verificado. src/emcp_bus/identity/models.py (ClientRegistration) mapeia esse
client_id a exatamente um ClientProfile (EP-04) via YAML em config/clients/*.yaml.
shared_client_lint.py (EP-01-T05) valida em CI que dois agentes distintos nunca compartilham
o mesmo client_id sob profiles diferentes.
Validado contra: Keycloak 26.7.3 real via Docker (tests/e2e/test_real_keycloak.py) — realm
emcp importado de deploy/keycloak/realm-export.json, 3 clients (sales-read-agent,
sales-write-agent, finance-payments-agent) com grant client_credentials; e um servidor JWKS
HTTP local mais leve como stand-in para o resto da suíte e2e (válido para a lógica de validação,
não para a integração de protocolo real com o IdP).
Extensão de produção documentada, não implementada: mTLS/workload identity —
docs/adr/ADR-019-identity-extension.md.
PDP / Policy Engine (EP-03)
Responsabilidade: decidir ALLOW/DENY/REQUIRE_APPROVAL de forma determinística e auditável.
Modelo em dois passos (src/emcp_bus/pdp/client.py, PDPClient.evaluate): (1) entitlement.rego
decide se a tool está no ClientProfile.allowed_tools — DENY aqui é definitivo, nenhum argumento
é avaliado; (2) transaction.rego restringe por argumento/recurso (limite de valor, região,
classificação do cliente) e pode rebaixar um ALLOW para REQUIRE_APPROVAL acima de
approval_threshold. Toda resposta é um PDPDecision (outcome, policy_version,
reason_code) — nunca um booleano. policy_version é um hash SHA-256 (12 chars) do conteúdo
dos arquivos .rego em disco, computado uma vez no boot do Gateway.
Validado contra: OPA real via subprocess (binário opa run --server), 9 testes Rego
(opa test) + 6 testes Python de integração.
Global Capability Registry (EP-02)
Responsabilidade: control-plane asset com autoridade e ciclo de vida próprios sobre o
catálogo de capabilities, separado da visão de execução que o Gateway expõe a cada client.
registry/models.py define CapabilityManifest (nome canônico <domínio>.<capability>,
risk_tier R0–R4, data_classification, side_effects, idempotent, entitlements,
backend, approval, observability, lifecycle). registry/pipeline.py
(PublishingPipeline) é o único caminho de entrada: valida gates de negócio (owner obrigatório,
backend declarado, tiers R3/R4 exigem approval.required=true) e avança
NOMINATE → REVIEW → REGISTER; um publish() separado avança REGISTER → ACTIVE, quando a
capability se torna resolvível pelo Fabric. registry/lifecycle.py garante mecanicamente que
nada alcança ACTIVE sem passar por REVIEW. registry/store.py persiste em SQLite;
registry/seed.py popula o Registry a partir de config/capabilities/*.yaml no boot do Gateway
(um arquivo temporário por processo, não compartilhado entre execuções).
Validado contra: SQLite real, 9 manifestos de exemplo (6 Sales + 3 Finance) passando pelo pipeline completo sem intervenção manual.
PEP / MCP Gateway (EP-05)
Responsabilidade: único Policy Enforcement Point. src/emcp_bus/gateway/server.py
implementa dois invariantes: tools/list retorna a interseção do catálogo dos backends
entitled com o MaximumEntitlement do client autenticado (discovery filtrado); tools/call é
reautorizado de forma totalmente independente do que tools/list retornou — o PDP é consultado
em toda chamada. Sem token válido: tools/list vazio, tools/call sempre Access denied: UNAUTHENTICATED. PDP inalcançável é DENY (PDP_UNAVAILABLE), nunca ALLOW implícito. Falha do
AuditSink nunca vira erro 500 numa operação já decidida (_emit_audit, engole exceção em
todos os 8 pontos de emissão). Roteamento por capability é resolvido a cada chamada via
CapabilityRouter (EP-06) contra o Registry — um client pode ver tools de mais de um backend
(ex.: Sales + Finance). Ver interfaces/mcp-gateway.md para o
contrato completo (headers, códigos de negação, cache hint).
Validado contra: OPA real, Keycloak real (Docker), JWT RS256 reais, e um cliente MCP real do
host contra o Gateway containerizado (docker compose --profile core [--profile identity] up).
Enterprise MCP Fabric e servidores de domínio de exemplo (EP-06)
Responsabilidade: src/emcp_bus/fabric/router.py (CapabilityRouter) resolve um nome de
tool <domínio>.<capability> para o backend que a serve, usando apenas manifestos ACTIVE do
Registry — nunca um mapeamento estático no Gateway. services/example_mcp_servers/sales_domain/
(6 tools, R1+R2) e finance_domain/ (3 tools: invoice.get R1, payment.create R2,
payment.execute R3 com ciclo de aprovação completo) são os dois domínios de exemplo,
protegidos por BackendCredentialGate (EP-05-T07) — uma requisição sem a credencial de saída do
Gateway nunca alcança um handler de tool. fabric/adapters/rest_adapter.py (EP-06-T04)
implementa um adapter REST genérico, testado de forma standalone (httpx.MockTransport); nenhum
manifesto seed usa backend.type: "rest", então não há caminho ao vivo através do Gateway
exercitando-o.
Validado contra: roteamento real Sales→sales-domain / Finance→finance-domain nunca cruzado, inclusive cross-container via Docker Compose real (segmentação de rede confirmada: nenhum dos domain servers ou o OPA publica porta de host).
Downstream / outbound identity (EP-07)
Responsabilidade: a credencial que o Fabric apresenta ao backend nunca é o token inbound do
MCP Client (src/emcp_bus/downstream/identity.py, BackendCredentialProvider). Dois modos
declarados por backend em config/backends/*.yaml (BackendConfig.credential_mode):
service_account (credencial estática lida de uma env var no momento da chamada — o modo
padrão dos backends seed) e token_exchange (RFC 8693 via Keycloak Standard Token Exchange,
TokenExchangeClient — dois round-trips HTTP por troca nova, com cache por backend até pouco
antes do expires_in). Ver interfaces/downstream-identity.md.
Validado contra: Keycloak 26.7.3 real — audience do exchange precisa ser um client id
realmente registrado (uma string livre falha com "Audience not found"); pedir audience sem um
scope que efetivamente adiciona essa audience falha com "Requested audience not available".
Realm ganhou o client gateway-token-exchange e client scopes com oidc-audience-mapper por
backend. 7 testes (4 unit com httpx.MockTransport, 3 e2e contra Keycloak real, incluindo um
tools/call completo pelo Gateway usando uma credencial obtida por token exchange).
Auditoria, correlação e observabilidade (EP-08)
Responsabilidade: audit/models.py define EventEnvelope com uma taxonomia de 3 categorias
adotada da referência Agent Platform OCI (ADR-022): IC (Interação de Cliente/negócio-jornada,
ex. client_authenticated, tool_call_allowed), NOC (operacional/erro de disponibilidade,
ex. pdp_unavailable), GRL (guardrail/governança, ex. tool_call_denied,
tools_list_filtered, bypass_attempt_detected). Todo evento sanitiza recursivamente qualquer
chave cujo nome contenha token/secret/password/authorization/credential, substituindo o
valor por sha256:<hex> — nunca um valor de segredo chega a um sink. audit/correlation.py
propaga decision_id/policy_decision_id/entitlement_version/policy_version/
mcp_request_id como campos de primeira classe em todo evento e como atributos de span OTel
(ADR-017). audit/sink.py (JSONLFileAuditSink) grava em arquivo JSONL append-only.
common/otel.py instala tracing automático (OpenTelemetryMiddleware) sem código manual de
span no Gateway. Ver interfaces/audit-events.md.
Validado contra: correlação de trace_id real entre containers Docker distintos (Gateway ↔
domain server) para uma chamada bem-sucedida. Stack local otel-collector + Jaeger
(EP-08-T04, perfil observability): configuração validada (docker compose config), não
exercitada com tráfego OTLP real por um teste automatizado — Langfuse (EP-13-T02) é quem recebeu
tráfego OTLP real de fato, validado manualmente (ver componente "Agente de exemplo" abaixo).
Entitlement Manager (EP-04)
Responsabilidade: entitlement/manager.py (EntitlementManager) resolve o
MaximumEntitlement combinando o ClientProfile (YAML validado, entitlement/models.py) com a
decisão de entitlement do PDP, produzindo um ResolvedEntitlement versionado
(entitlement_version) com allowed_tools/risk_ceiling/profile_name. sync_profiles_to_pdp
empurra os profiles validados para o data document do OPA no boot — falha aqui impede o Gateway
de subir "saudável" (fail-closed, README.md §38). Revogação invalida o cache de entitlement e
reavalia a partir da próxima chamada.
Validado contra: OPA real + YAML reais; revogação de política ao vivo contra um Gateway
rodando (tests/adversarial/test_protocol_identity.py).
Approval workflow (EP-14)
Responsabilidade: approval/service.py (ApprovalService) gate para decisões
REQUIRE_APPROVAL: check_and_consume (chamado pelo Gateway a cada tentativa) só retorna
verdadeiro para uma aprovação concedida, não expirada e ainda não consumida — single-use, nunca
"enfileira e libera". Um ApprovalRequest é vinculado a um hash SHA-256 do triplo exato
(client_profile, tool, arguments) (compute_operation_hash), não apenas ao nome da tool: uma
aprovação para uma chamada nunca satisfaz uma chamada diferente com os mesmos tool/profile.
check_and_consume é protegido por threading.Lock (achado real de concorrência, ver
"Limitações" abaixo). Ver interfaces/approval-workflow.md.
Validado contra: ciclo completo e2e para Sales e Finance (nega → aprova externamente →
executa → replay nega); replay sob concorrência real (ThreadPoolExecutor, 20 threads).
Profile Intelligence (EP-09)
Responsabilidade: profile_intelligence/models.py define ProfileView (subject_ref
pseudonimizado, profile_version, segments, attributes, reason_codes, expires_at) como
um contrato genérico e substituível — qualquer ProfileIntelligenceProvider (o stub
determinístico rule-based em rule_based_provider.py, ou um modelo de clustering real numa
implantação de produção) produz o mesmo contrato. Um ProfileView nunca é consultado pelo PDP e
nunca pode conceder uma capability fora do MaximumEntitlement — nada aqui está conectado ao
caminho de autorização. governance.py implementa os guardrails de governança: pseudonimização,
log de linhagem, stub de monitor de drift.
Validado contra: determinismo (mesmo subject_ref + janela ISO → mesmo profileVersion).
Offering Filter (EP-10)
Responsabilidade: offering_filter/service.py implementa
FilteredOfferings = ActiveCatalog ∩ MaximumEntitlement ∩ ConsentContext. A ordem da interseção
é deliberada: allowed_tools (MaximumEntitlement, já resolvido pelo EP-04) é o único termo
tratado como relevante para segurança e é aplicado primeiro; um ConsentContext (aqui, apenas
allow_restricted_data sobre CapabilityManifest.data_classification) só pode reduzir o que já
sobreviveu a essa interseção, nunca reintroduzir uma capability fora do entitlement.
Validado contra: invariante de subconjunto provada com Hypothesis, 2000 casos gerados
(tests/unit/test_offering_filter_invariants.py).
Service Orchestrator (EP-11)
Responsabilidade: orchestrator/graph.py (build_journey_graph) é um StateGraph real do
LangGraph com nós fixos resolve_profile → resolve_entitlement → filter_offerings → decide_nba → approval_gate, mais uma reentrada dedicada para o loop de recálculo após DENY
(handle_denial_node, que nunca chama o Fabric diretamente — apenas recalcula a NBA sobre as
offerings restantes). orchestrator/nba_model.py define NBADecision
(decision_id, action, capability_version, subject_ref, channel, agent,
reason_codes, policy_context, expires_at) e o protocolo NBADecisionModel — trocar a
implementação de referência (decisioning.RuleBasedNBADecisionModel) por um modelo de
ranking/IA não muda nada no contrato de entrada/saída nem no que EP-03/04/05 autorizam, porque o
modelo só ordena/seleciona entre DecisionContext.filtered_offerings, já intersectado pelo
EP-10 antes de chegar aqui. orchestrator/hitl_node.py usa langgraph.types.interrupt/
Command(resume=...) para pausar de verdade o grafo em aprovação — sem timeout que vire ALLOW
implícito.
Validado contra: OPA real + LangGraph real + checkpointer SQLite real, incluindo prova de que retomar de um checkpoint pausado não reprocessa nós já executados.
Agent Runtime (EP-12)
Responsabilidade: agent_runtime/dispatcher.py (AgentDispatcher) consome uma NBADecision
e a despacha contra o Gateway real, autenticado como o MCP Client já registrado para o agente
alvo — nunca gera ou armazena credencial própria (AgentTokenProvider, tipicamente um
client_credentials real contra o mesmo IdP de qualquer outro MCP Client). handoff_graph.py
(EP-12-T03, execute_handoff/build_handoff_payload) implementa handoff entre agentes com
minimização de contexto e resolução de entitlement sempre independente por agente — ver
"Limitações" abaixo para o achado de que nem o Modo A nem o Modo B (EP-13-T05/T06) acabaram
usando esse primitivo. identity_resolver.py (EP-12-T04, IdentityResolver) normaliza nomes de
campo entre canais (config/orchestrator/identity.yaml) e nunca é consultado pelo PDP — checado
mecanicamente via análise de AST no teste unitário correspondente, a mesma disciplina que
global_supervisor.py usa para provar que nunca importa emcp_bus.orchestrator.
Validado contra: Gateway + Keycloak reais — AgentDispatcher alcançando o backend Sales
real e sendo negado no cross-domain; dois agentes reais obtendo tokens client_credentials
independentes.
Agente de exemplo (EP-13)
Responsabilidade: agents/example_agent/agent.py é um agente conversacional com
tool-calling real contra um endpoint OpenAI-compatible, cujo catálogo é restrito ao próprio
tools/list do client. common/pii_redaction.py redige PII/segredos antes de qualquer export.
prompts/store.py implementa versionamento local de prompt (nunca sobrescreve uma versão
anterior) como núcleo testável — sincronização com a API de Prompt Management do Langfuse é um
adapter fino documentado, não implementado.
Validado contra: um LLM real (meta.llama-3.3-70b-instruct via endpoint OpenAI-compatible da
OCI Generative AI), com tool-calling confirmado empiricamente; o cenário de prompt injection do
README.md §29 (pedir uma tool fora do entitlement) reproduzido de verdade — a chamada nunca
executa, seja porque o modelo nunca viu a tool no próprio catálogo, seja porque o Gateway/PDP
nega. Langfuse self-hosted real (6 containers: web/worker/postgres/clickhouse/redis/minio,
deploy/langfuse/docker-compose.override.yml) recebendo tráfego OTLP real do agente —
spans de MCP e de "generation" (com gen_ai.request.model/tokens reais) confirmados fisicamente
na tabela events_core do ClickHouse. Essa validação é manual, não um teste e2e automatizado
(custo de subir 6 containers a cada execução da suíte).
Modo A — Global Supervisor externo (EP-13-T05, ADR-024)
Responsabilidade: agent_runtime/global_supervisor.py (GlobalSupervisor) é o stand-in
deliberadamente simples (roteamento por palavra-chave) para uma plataforma de orquestração
externa que decide qual agente de backend atende uma conversa — nunca é, ou pretende ser, a
fronteira de segurança; nunca importa emcp_bus.orchestrator (checado via AST). A propriedade de
segurança real não está neste módulo: está no fato de que cada BackendAgent
(agents/example_agent/backends/{sales_agent,finance_agent}/) resolve seu próprio MCP
Client/entitlement de forma completamente independente — token, tools/list e reautorização
próprios — e o GlobalSupervisor nunca lê, guarda ou repassa a credencial de um backend para
outro.
Validado contra: LLM real + Gateway real (tests/e2e/test_multi_agent_mode_a.py, 2 testes):
handoff de sales_agent para finance_agent com token completamente distinto; injeção de
prompt via handoff tentando finance.payment.execute continua negada pelo Gateway/PDP reais.
Modo B — Orchestrator como único cérebro de NBA/NBO (EP-13-T06, could, ADR-024)
Responsabilidade: agents/example_agent/mode_b_orchestrator_driven/orchestrator.py
(ModeBOrchestrator) invoca o journey StateGraph real do EP-11-T02 (não modificado) uma vez
por client_id relevante para o mesmo subject, combina os NBADecisions resultantes (regra de
referência: pendência de Finance sempre vence oferta discricionária de Sales) e despacha o
vencedor via AgentDispatcher real — nunca cria um caminho de autorização próprio. Deliberadamente
não estende JourneyState/build_journey_graph para múltiplos client_ids nativamente; a
combinação vive uma camada acima do grafo (decisão de design registrada no próprio módulo).
Validado contra: Keycloak real + OPA real + Sales/Finance reais
(tests/e2e/test_real_keycloak.py::test_mode_b_orchestrator_combines_offerings_and_dispatches_through_the_real_chain):
candidatos de ambos os domínios realmente combinados, decisão final do nosso Orchestrator,
dispatch ALLOW de ponta a ponta.
O que foi validado, por dependência real
| Dependência real | O que exercita | Onde |
|---|---|---|
| OPA (subprocess) | entitlement.rego/transaction.rego, PDPClient |
tests/unit/policies/, toda a suíte e2e |
| Keycloak (Docker, 26.7.3) | client_credentials, JWKS real, token exchange RFC 8693, Modo B | tests/e2e/test_real_keycloak.py |
| LangGraph 0.6 + checkpointer SQLite | journey graph, pausa/retomada de aprovação | tests/unit/test_orchestrator_graph.py, e2e do M3 |
| LLM real (OpenAI-compatible) | tool-calling do agente de exemplo, prompt injection | tests/e2e/test_example_agent.py, test_multi_agent_mode_a.py |
| Docker Compose completo | build+up+fail-closed+trace cross-container+segmentação de rede | tests/e2e/test_security_acceptance.py, validação manual documentada |
| Langfuse self-hosted (6 containers) | export OTLP real, spans de generation | Validação manual (não automatizada) |
Limitações e achados arquiteturais reais
ttl_msdo cache hint detools/list(SEP-2549, EP-05-T03): implementado corretamente emgateway/cache.py, mas o campo pertence ao protocolo 2026-07-28, que só existe no modo stateless per-request do SDKmcp(sem handshakeinitialize). Esta RI usa o handshake de sessão clássico em toda parte (Gateway↔Client e Gateway↔backends), entãottl_msnunca chega ao wire nesta arquitetura — o SDK descarta o campo na serialização para versões de protocolo anteriores a 2026-07-28 (verificado empiricamente).cache_scope="private"é sempre definido, mas por não depender do wire-mode também não é algo que um teste e2e consiga verificar de forma discriminante. A propriedade de segurança real (nunca vazar catálogo entre clientes) é garantida de forma independente: o Gateway nunca cacheia o catálogo filtrado no servidor.- Consistência header/body (EP-05-T08): implementada como um gate próprio
(
gateway/canonical_request.py) reaproveitando as constantes de campo do SDK, não como o ladder de validação nativo do SDK (que só se aplica ao mesmo modo stateless per-request de 2026-07-28 acima) — pela mesma razão, esta RI não o alcança pelo caminho nativo. Cabeçalhos ausentes (um client que não implementa esse SEP) não são um erro. - Adapter REST (EP-06-T04): implementado e testado de forma standalone
(
fabric/adapters/rest_adapter.py); nenhum backend de exemplo desta RI é REST (ambos são MCP-nativos), então o roteamento ao vivo através do Gateway para um backendtype: "rest"nunca foi exercitado end-to-end. - Handoff entre agentes (EP-12-T03,
execute_handoff/build_handoff_payload): continua implementado e testado (tests/unit/test_agent_runtime_handoff.py), mas nem o Modo A (EP-13-T05) nem o Modo B (EP-13-T06) acabaram usando esse primitivo. O Modo A precisa de roteamento conversacional (qual backend responde à próxima mensagem), não de despacho de umaNBADecisionespecífica —GlobalSupervisorchamarun_turn()diretamente nos backends. O Modo B já combinaNBADecisions de múltiplos clients num único passo e despacha o vencedor viaAgentDispatcher.dispatchpuro — não há um agente "de origem" cedendo controle a outro paraexecute_handoffinterceptar. O primitivo permanece correto e reutilizável para um cenário futuro de handoff dentro de uma jornada Orchestrator-driven, mas essa forma específica de uso não existe nesta RI. - Replay de requisição: sem nonce/anti-replay nesta RI — uma requisição capturada com um
token ainda válido pode ser reenviada com sucesso. Mitigado por tokens de vida curta
(
credential_policy.short_livedem todoClientProfileseed) e por auditoria completa (cada tentativa, mesmo idêntica, geradecision_idpróprio). Vertests/adversarial/test_protocol_identity.py. - Rate limiting / bulk exfiltration: sem limitação de taxa de requisições no Gateway — N leituras legítimas consecutivas são todas permitidas. Mitigado apenas por detectabilidade a posteriori via auditoria (cada chamada gera evento correlacionado), nunca por prevenção em tempo real.
- Race condition real corrigida em
ApprovalService:check_and_consumefazia check-then-set em duas operações não atômicas — sob concorrência real de threads, duas tentativas podiam ambas observar "granted" antes de qualquer uma escrever "consumed" (double-spend de um grant single-use). Corrigido comthreading.Lockdedicado, provado comThreadPoolExecutor(20 threads) emtests/adversarial/test_protocol_identity.py. - Falha de audit sink podia virar erro 500 real, corrigida: nenhuma das 8 chamadas
audit_sink.emit(...)no Gateway estava protegida — uma falha no sink (disco cheio, fila indisponível) podia transformar uma operação jáALLOWed num erro 500 para o caller, violando a política de não bloquear operações de baixo risco. Corrigido com o helper_emit_audit(gateway/server.py), que engole qualquer exceção do sink nos 8 pontos de emissão, para ALLOW e para DENY. Uma falha do sink em produção precisa de observabilidade própria (verProduction-Recommendations.md) — este comportamento é correto por design, mas silencioso. - Roteamento do
GlobalSupervisor(Modo A): por palavra-chave simples, deliberadamente não é a fronteira de segurança nem uma reivindicação de qualidade de roteamento — apenas um stand-in honesto para o que uma plataforma externa real faria. - Regra de combinação do
ModeBOrchestrator(Modo B): "Finance sempre vence Sales" é uma regra de referência simples, não uma reivindicação de otimalidade — mesma postura deRuleBasedNBADecisionModel(EP-11-T03). - Stack de observabilidade (EP-08-T04): configuração validada (
docker compose config), não exercitada com tráfego real por um teste automatizado (a subida completa com o perfilobservabilityfoi validada manualmente).