3.5 KiB
Interface: PDP (OPA)
Contrato de dados/consulta do Policy Decision Point. Implementação:
src/emcp_bus/pdp/client.py,
pdp/models.py,
config/policies/entitlement.rego,
config/policies/transaction.rego. Ver
AS-BUILT.md.
Modelo em dois passos
PDPClient.evaluate(client_profile, tool, arguments, customer_classification) consulta, em
ordem, e nunca em paralelo:
emcp.entitlement(entitlement.rego) —allowsetoolestá emdata.emcp.client_profiles[client_profile].allowed_tools.DENYaqui é definitivo (reason_code=NOT_IN_ENTITLEMENT) — nenhum argumento chega a ser avaliado.emcp.transaction(transaction.rego) — só avaliada se o passo 1 permitiu. Restringe por argumento/recurso; nunca pode adicionar uma tool ausente do entitlement. Retornaallowerequire_approvalindependentemente.
Dados versionados no OPA
EntitlementManager.sync_profiles_to_pdp() empurra cada ClientProfile validado para
PUT /v1/data/emcp/client_profiles/<profile_name> no boot — o Rego nunca lê YAML diretamente.
Requisição de avaliação
POST /v1/data/emcp/entitlement/allow
{"input": {"client_profile": "Sales.Write", "tool": "finance.payment.execute"}}
POST /v1/data/emcp/transaction
{"input": {
"client_profile": "Finance.Payments",
"tool": "finance.payment.execute",
"arguments": {"amount": 15000, "region": "BR-SP"},
"customer_classification": "standard"
}}
Resposta combinada (PDPDecision)
{"outcome": "ALLOW" | "DENY" | "REQUIRE_APPROVAL", "policy_version": "a1b2c3d4e5f6", "reason_code": "..."}
Nunca um booleano isolado — policy_version e reason_code são obrigatórios em toda resposta.
reason_code |
outcome |
Quando |
|---|---|---|
NOT_IN_ENTITLEMENT |
DENY |
Passo 1 negou. |
TRANSACTION_POLICY_DENIED |
DENY |
Passo 1 permitiu, passo 2 negou (limite, região, classificação). |
APPROVAL_THRESHOLD_EXCEEDED |
REQUIRE_APPROVAL |
allow=true e amount > restrictions.approval_threshold. |
ALLOWED |
ALLOW |
Ambos os passos permitiram, sem exceder o threshold. |
policy_version
Um hash SHA-256 (12 caracteres) do conteúdo dos arquivos *.rego (exceto *_test.rego) em
config/policies/, computado uma vez no boot do Gateway (compute_policy_version). Propagado em
toda decisão e evento de auditoria — ver audit-events.md. Um deployment de
produção substituiria isso por uma revisão de bundle OPA real (ver
../Production-Recommendations.md); o contrato (PDPDecision.policy_version como string
opaca) não mudaria.
Erros
| Situação | Exceção | Tratamento pelo caller (Gateway) |
|---|---|---|
| OPA inalcançável, timeout, resposta malformada | PDPUnavailableError |
Tratado como DENY (PDP_UNAVAILABLE) — nunca ALLOW implícito (fail-closed, README.md §38). |
Falha ao empurrar um ClientProfile no boot |
PDPUnavailableError |
O Gateway não sobe "saudável" — não há modo degradado sem sincronia de entitlement. |
Como uma política nova entra em vigor
Editar config/policies/*.rego muda o policy_version computado (hash do conteúdo). Não há
hot-reload nesta RI — reiniciar o Gateway (que reinicia o processo OPA associado, ou aponta para
um OPA já rodando com o Rego atualizado via opa run --server config/policies/).