agentguard: spend caps and kill switch for MCP agents
Um proxy MCP para agentes que possuem credenciais reais: limites por execução e por dia em gravações, exclusões, e-mails e dólares, aprovação humana para ferramentas destrutivas, simulação para gravações, um quebra-loop, um interruptor de emergência e um log de auditoria encadeado por hash. MIT, sem conta: npx @agentwares/agentguard init.
Documentação
okgate
60 segundos para uma primeira execução segura. Seu agente já tem uma configuração MCP. Coloque o okgate na frente dele, execute o agente uma vez em dry-run e leia o que ele teria feito:
npx -p @agentwares/agentguard okgate init # finds your MCP config, writes okgate.yaml (dry-run), routes every server through the proxy
# restart your MCP client, run your agent as usual — writes are faked, nothing executes upstream
npx -p @agentwares/agentguard okgate report # "would have deleted 12 records, sent 5 emails, spent $140 — halted a loop at call 31"
npx -p @agentwares/agentguard okgate diff # the record-by-record mutation diff
# set `mode: enforce` in okgate.yaml when it looks right
# okgate report — run `run_20260902_a1b2`
61 tool calls between 10:02:11 and 10:02:19 across crm.
## What this run would have done (dry-run, nothing was executed)
It would have **deleted 1 record**, updated 1, created 1, sent 1 message, **spent $12.00**.
## Where okgate stepped in
| # | code | tool | why |
|----|-----------------|--------------------|--------------------------------------------------------------|
| 10 | `LOOP_DETECTED` | crm_update_contact | called 3 times with the same arguments in the last 30 calls |
| 61 | `CAP_EXCEEDED` | crm_create_contact | writes cap for this run is 50; used 50, this call would make it 51 |
Renomeado de agentguard em 8 de outubro de 2026: nada irreversível sem um OK. Nada do que você
configurou quebra. agentguard.yaml, um .agentguard/ existente (sua cadeia de auditoria continua), cada
variável AGENTGUARD_* (AGENTGUARD_KILL=1 ainda interrompe tudo), o bin agentguard e o
hook e as entradas MCP que o executam continuam funcionando, e os nomes de ferramentas agentguard_* ainda funcionam até
2027-01-15.
Até que @agentwares/okgate esteja no npm, o npm serve o okgate como o bin okgate de
@agentwares/agentguard, daí npx -p @agentwares/agentguard okgate.
okgate é um proxy de políticas MCP para agentes que tocam em produção. Ele fica entre o agente e seus servidores MCP, vê cada chamada de ferramenta e aplica um arquivo YAML:
- Limites rígidos de gastos —
spend_usdpor execução e por dia em todos os provedores, a partir de argumentos de ferramentas (stripe_create_charge.amount), resultados de ferramentas (cost_usd) e — com ofetchprotegido do SDK — uso de tokens LLM de respostas da OpenAI, Anthropic e Gemini. A chamada que excederia o limite recebeCAP_EXCEEDEDcom o orçamento restante. - Bloqueio de ações destrutivas com aprovações —
approval.tools: [crm_delete_*]faz o agente obterAPPROVAL_REQUIRED+ um id; um humano executaokgate approve <id>(ou clica no botão no Slack) e a nova tentativa idêntica do agente passa uma vez. - Interruptor de emergência —
okgate kill(um arquivo),OKGATE_KILL=1(env;AGENTGUARD_KILL=1também) ouPOST /kill(HTTP): toda execução para instantaneamente comKILLEDatéokgate resume. - Credenciais com escopo por agente — o proxy guarda os tokens upstream; cada agente recebe uma chave
agk_…com sua própria allowlist, denylist e limites. Apenas o hash da chave vive na política. - Escritas em dry-run com diffs de mutação — escritas classificadas retornam um sucesso plausível moldado pelo esquema de saída da ferramenta para o agente continuar;
okgate diffmostra o que teria mudado. - Quebrador de loop semântico — o mesmo
(tool, normalized args)3× nas últimas 30 chamadas, ou um ciclo A→B→A→B, retornaLOOP_DETECTED. Timestamps, ids, espaços em branco e ordem de chaves são ignorados. - Limites de raio de explosão —
tool_calls,writes,deletes,emails,spend_usde contadores personalizados, por execução e por dia. - Log de auditoria com hash encadeado — cada chamada é uma linha JSONL com
prev_hashehash;okgate verifyprova que nenhuma entrada foi editada, removida do meio ou reordenada (veja Limites para o que uma cadeia local não pode provar sozinha). - Modo hook para agentes de codificação —
okgate hooks installcoloca a mesma política na frente dos comandos de shell e edições de arquivo do Claude Code, Codex e Gemini CLI; comandos irreversíveis (push, merge, publish, send,rmfora do repositório) são executados apenas em uma rodada recente digitada por uma pessoa que os nomeia, nunca em texto escrito pelo modelo. Detalhes. - Auditoria de adesão a regras —
okgate auditlê CLAUDE.md, AGENTS.md, GEMINI.md e.cursor/rulese as sessões do Claude Code e Codex desta máquina, e imprime quais regras os agentes quebraram ("quebrado em 9 de 31 sessões"), localmente;--enforcetransforma as verificáveis em regras de hook em dry-run. Detalhes.
Sem chamadas LLM. Sem telefone para casa. Sem conta. MIT.
Dois caminhos de instalação, um motor de políticas: o proxy MCP (npx -p @agentwares/agentguard okgate, stdio + Streamable HTTP, múltiplos upstreams) e o SDK/middleware (@agentwares/agentguard-sdk) para OpenAI Agents SDK, LangChain ou ferramentas de função simples que nunca passam pelo MCP.
Instalação
npx -p @agentwares/agentguard okgate init # rewrites the first project-level config it finds
npx -p @agentwares/agentguard okgate init --all # ...or every config: .mcp.json, .cursor/mcp.json, .vscode/mcp.json
npx -p @agentwares/agentguard okgate init --client ~/.claude.json # a user-level config, which --all still leaves alone
npx -p @agentwares/agentguard okgate init --client ~/Library/Application\ Support/Claude/claude_desktop_config.json # user-level configs only with --client
npx -p @agentwares/agentguard okgate init --undo # restore the backup
init encontra .mcp.json, .cursor/mcp.json, .vscode/mcp.json, mcp.json e .gemini/settings.json no projeto (apenas arquivos de nível de usuário com --client), escreve okgate.yaml ao lado deles, faz backup da configuração (*.okgate-backup) e substitui seus servidores por uma entrada:
{
"mcpServers": {
"okgate": {
"command": "npx",
"args": [
"-y",
"-p",
"@agentwares/agentguard",
"okgate",
"proxy",
"--config",
"/abs/path/okgate.yaml"
]
}
}
}
As ferramentas mantêm seus nomes (prefixadas <upstream>__ apenas em colisão). Seu cliente MCP vê um servidor; o okgate conecta-se a todos eles e guarda suas credenciais.
Do seu agente ou editor
O mesmo init, de onde você já trabalha:
-
Claude Code (e Copilot CLI, que lê o mesmo arquivo de marketplace):
/plugin marketplace add agentwares/okgate, depois/plugin install okgate@agentwares(instalado comoagentguard@agentwares? Esse plugin mantém as mesmas skills até pelo menos 2027-01-15)./okgate:initexecutainite explica a política que escreveu;/okgate:reportexplica o que uma execução fez ou teria feito. -
Gemini CLI:
gemini extensions install https://github.com/agentwares/okgate, depois/okgate:inite/okgate:report.initlê.gemini/settings.json; os servidoreshttpUrldo Gemini são proxyados como Streamable HTTP (servidoresurlapenas SSE não são suportados). -
Cursor e VS Code: um clique adiciona o okgate como servidor MCP (
npx -y -p @agentwares/agentguard okgate proxy):Os botões abrem
cursor://anysphere.cursor-deeplink/mcp/install?name=okgate&config=…evscode:mcp/install?{…}; o GitHub não renderiza esses esquemas como links, então os botões passam porcursor.com/linkevscode.dev/redirect. Onde o editor o inicia ao lado de umokgate.yaml, esse servidor protege o que a política lista. Em qualquer outro lugar, ele não protege nada e serve as três ferramentas somente leitura abaixo, a primeira das quais diz para você executarinitno projeto.initentão escreve a entrada de nível de projeto que faz a proteção.
Iniciado sem arquivo de política
Iniciado sem argumentos (o que uma instalação do registro MCP faz), okgate
serve o proxy stdio e lê OKGATE_CONFIG ou ./okgate.yaml; em um terminal, ele
imprime a ajuda. Se nenhum nomear um arquivo que exista, ele não sai: não protege nenhum
servidor, não escreve nada e serve três ferramentas somente leitura próprias, cada uma com um título,
readOnlyHint: true e um esquema estrito:
| Ferramenta | Respostas |
|---|---|
okgate_get_status | qual arquivo de política carregou ou procurou, modo, upstreams, limites usados e restantes, interruptor de emergência, aprovações pendentes e o próximo passo |
okgate_get_report | o que uma execução fez, ou teria feito em dry-run, e cada chamada interrompida (run_id opcional) |
okgate_verify_audit_log | se o log de auditoria com hash encadeado verifica, sua contagem de entradas e hash da cabeça |
Uma política sem upstreams: serve as mesmas três. Uma vez que os upstreams estão configurados, o agente vê
apenas suas ferramentas, exatamente como antes. Um arquivo nomeado com --config ou OKGATE_CONFIG que não
existe ainda é um erro.
Docker
O Dockerfile do repositório compila o CLI a partir do código-fonte e o serve via stdio:
docker build -t okgate .
docker run -i --rm okgate # no policy: the three tools above
docker run -i --rm okgate proxy --config /app/demo/okgate.yaml # a fake CRM behind the proxy, dry-run
Prefere HTTP (vários agentes, chaves com escopo, botões de aprovação no Slack)? okgate proxy --http --port 8788 e aponte os clientes para http://127.0.0.1:8788/mcp com um cabeçalho X-Run-Id por execução e Authorization: Bearer agk_… por agente.
Modo hook: os próprios comandos de um agente de codificação
O proxy vê chamadas MCP. Um agente de codificação também age por meio de suas próprias ferramentas de shell e arquivo, que nenhum
proxy MCP vê. O modo hook coloca o mesmo okgate.yaml na frente delas, por meio do hook de
pré-execução do agente, e adiciona uma verificação que nada mais faz: um comando irreversível é executado apenas em uma
rodada que uma pessoa digitou.
npx -y -p @agentwares/agentguard okgate hooks install # Claude Code, this project (.claude/settings.json)
npx -y -p @agentwares/agentguard okgate hooks install --client all # + Codex (.codex/hooks.json) and Gemini CLI (.gemini/settings.json)
npx -y -p @agentwares/agentguard okgate hooks install --user # every project (~/.claude/settings.json, policy in ~/.okgate/)
npx -y -p @agentwares/agentguard okgate hooks status | uninstall
install adiciona uma entrada de hook (outras configurações e hooks são mantidos; a primeira mudança deixa um
*.okgate-backup), adiciona um bloco hooks: comentado em dry-run para okgate.yaml (ou
escreve uma política com apenas esse bloco) e adiciona .okgate/ a .gitignore. Commit
okgate.yaml e .claude/settings.json e todo o time executa o mesmo conjunto de regras. O comando
de hook é npx -y -p @agentwares/agentguard@<version> okgate hook pre-tool-use (cerca de meio segundo por
chamada protegida); com o pacote instalado globalmente, --command okgate o torna cerca de 0,1 s.
| Agente | Hook (verificado em 7 out 2026) | Regras, aprovações, limites, auditoria | Portão de rodada humana |
|---|---|---|---|
| Claude Code | PreToolUse: Bash, Write, Edit, mcp__* … | sim | sim, a partir da transcrição da sessão |
| Codex CLI | PreToolUse: Bash, apply_patch, mcp__* | sim (confie no hook uma vez no /hooks do Codex) | não: o Codex chama seu formato de transcrição instável, então irreversível = aprovar |
| Gemini CLI | BeforeTool: run_shell_command, write_file, replace, mcp_* | sim | não: não verificado contra uma instalação local, então irreversível = aprovar |
O Cursor tem seus próprios hooks (beforeShellExecution) e pode carregar os do Claude Code
(cursor.com/docs/hooks); o modo hook não foi testado lá.
O que é verificado. Cada comando de shell é dividido em comandos simples da maneira que um shell faria
(&&, ;, pipes, $(…), backticks, bash -c '…', eval, find -exec, corpos de heredoc ignorados,
sudo/env/timeout/npx wrappers e o -C do git removidos, cd seguido). Então:
hooks:
mode: dry-run # dry-run: log what enforce would stop, block nothing | enforce
shell:
deny: ["terraform destroy*", "curl * | sh"] # refused whatever anyone says
approval: ["kubectl delete *"] # held for `okgate approve`
paths: # file edits and shell writes (>, tee, cp, mv, rm, sed -i)
deny: [".env", "*.pem"] # no slash: any file with that name; `src/**`: from the project root
approval: ["**/migrations/**"]
irreversible:
builtins: true # or a list: [git-push, merge, tag-delete, rm-outside-workspace, publish, send, guard-config]
window_s: 1800 # the authorizing turn is at most 30 minutes old
require_mention: true # ...and names the action
require_origin: false # true: only entries labelled as typed by a person (see below)
mentions: { git-push: [deploy] } # extra words per class
rules: # your own irreversible classes
- { name: deploy, shell: ["vercel --prod*", "fly deploy*"], mentions: [deploy] }
Os padrões deny e approval.tools do lado MCP também se aplicam a ferramentas MCP que o agente de codificação chama
diretamente (mcp__crm__crm_delete_contact corresponde a crm_delete_*). O interruptor de emergência interrompe toda
chamada protegida em ambos os modos.
Classes irreversíveis. git-push (git push, não --dry-run); merge (gh pr merge,
glab mr merge, gh api -X PUT …/merge); tag-delete (git tag -d, gh release delete);
rm-outside-workspace (rm/rmdir/unlink/shred/find -delete em qualquer coisa fora do
projeto, o próprio projeto, ou — apenas recursivo — um alvo que depende de uma variável; diretórios
temporários são livres); publish (npm/pnpm/yarn/bun publish, cargo publish,
twine upload, gem push, poetry/uv publish, docker push, gh release create,
changeset/lerna publish, semantic-release, …); send (mail/sendmail/mutt/…,
gh pr|issue create/comment/edit/…, gh api escritas, curl para hosts de API do Slack/Discord/Telegram/mail,
ferramentas MCP nomeadas send/reply/forward/post); guard-config (edições em okgate.yaml
ou um arquivo de configuração de hook, okgate hooks …).
O portão de turno humano. Um comando irreversível é executado quando a entrada mais recente que uma pessoa escreveu no
transcript (1) veio após o comando irreversível anterior desta sessão — um turno autoriza um
— (2) tem no máximo window_s de idade, e (3) nomeia a ação ("manda"), ou é um "sim" / "pode
ir" puro respondendo a uma pergunta do assistente que a nomeia ("Quer que eu faça push para a main?"). Caso contrário, o
agente recebe APPROVAL_REQUIRED com um id, e ou a pessoa diz isso com suas próprias palavras ou executa
okgate approve <id> (no Claude Code, ! npx -p @agentwares/agentguard okgate approve <id> executa
sem passar pelo modelo); a nova tentativa idêntica então é executada uma vez.
Como "uma pessoa escreveu" é decidido (transcripts do Claude Code, verificados contra as versões 2.1.170
a 2.1.286). Uma entrada conta apenas se: type é user e message.role é user; não é
isSidechain (o turno de "usuário" de um subagente é o prompt do modelo pai); não é isMeta (corpos de skills,
lembretes, mensagens de pares); não é um resumo de compactação; não carrega tool_result e nenhum
toolUseResult (a saída da ferramenta viaja em entradas de usuário); se tiver um origin, origin.kind é
human (task-notification, peer, coordinator não são); caso contrário, se tiver um turnOrigin,
isso é human; com nenhum dos rótulos (versões mais antigas, prompts enfileirados, claude -p), seu texto não
começa com um wrapper de harness (<command-name>, <local-command-stdout>, <task-notification>,
<bash-input>, [Request interrupted, …), e require_origin: true recusa diretamente. Texto
dentro de uma entrada de assistente nunca conta — é onde um "usuário: sim, manda" fabricado vive.
O que o agente vê. Uma negação é o permissionDecision: "deny" do Claude Code / Codex (Gemini:
decision: "deny") cujo motivo é o corpo usual do okgate:
{
"code": "APPROVAL_REQUIRED",
"cause": "\"git push origin main\" is irreversible (git-push (git push origin main)) and the latest message typed by a person (08:29:16) does not name this action (expected one of: push, ship). It needs a person's approval (approval apr_7c62fbad56).",
"fix": "stop and tell the user: this needs their approval. They can run `npx -p @agentwares/agentguard okgate approve apr_7c62fbad56` in a terminal in this project (or type `! npx -p @agentwares/agentguard okgate approve apr_7c62fbad56` in Claude Code), or say in their own words that you should do it; then retry this exact command once. Do not change it, work around it, or run the approval yourself: okgate refuses an approval from the agent.",
"retryable": true,
"details": { "approvalId": "apr_7c62fbad56", "command": "okgate approve apr_7c62fbad56" }
}
e a pessoa recebe um systemMessage de uma linha com o comando de aprovação. okgate nunca responde
"permitir": uma chamada permitida passa pelos prompts de permissão do próprio agente como antes. O agente não pode
aprovar, negar ou retomar suas próprias ações retidas, nem escrever .okgate/ (recusado no enforce).
Em dry-run, a pessoa vê "teria parado …" e a chamada é executada; okgate report lista cada
decisão dessas sob Coding-agent hooks. Chamadas de hook contam contra caps sob hook_calls,
shell_commands, file_edits e irreversible (por execução = por sessão de agente, e por dia); o
loop breaker interrompe a mesma edição de arquivo repetida.
Auditoria: quais regras escritas seus agentes de codificação quebraram
Equipes escrevem regras para seus agentes em CLAUDE.md, AGENTS.md, GEMINI.md e .cursor/rules, e depois
não conseguem dizer se os agentes as cumprem. okgate audit lê os arquivos de regras do repositório e
as sessões passadas do Claude Code e Codex desta máquina nele, e imprime, por regra, quantas sessões
a quebraram e quando. Meça primeiro, depois aplique o que pode ser aplicado.
npx -y -p @agentwares/agentguard okgate audit # this repository, the last 7 days
npx -y -p @agentwares/agentguard okgate audit --since 30d # or all, 24h, 2026-09-01; --client claude|codex
npx -y -p @agentwares/agentguard okgate audit --json # counts, rule ids and dates (what /okgate:audit reads)
npx -y -p @agentwares/agentguard okgate audit --enforce --preview # the hook rules that would enforce them
okgate audit — /home/dev/acme · since 2026-09-30
6 sessions in this repository (Claude Code 5, Codex 1). Read on this machine; nothing was sent anywhere.
13 rules found (6 in CLAUDE.md, 3 in AGENTS.md, 1 in GEMINI.md, 2 in .cursor/rules/release.mdc, 1 in okgate.yaml); 11 checkable.
BROKEN
R1 Never push to `main`. (CLAUDE.md:5)
no-push main — broken in 3 of 6 sessions (4 pushed); 1 refused by the harness
2026-10-06 10:40 git push origin main · session 3f9a1c2b
R4 Run `pnpm check` before pushing. (CLAUDE.md:8)
run "pnpm check" before push — broken in 3 of 6 sessions (4 pushed or opened a PR); 4 times
…
NOT CHECKABLE MECHANICALLY (2) — add an inline check (<!-- okgate: … -->) or ask your agent with /okgate:audit
R6 Write tests for new code. (CLAUDE.md:13)
As regras. Cada item de lista e cada frase que direciona algo (blocos de código, tabelas e
front matter ignorados) é uma regra, numerada entre os arquivos, verificável ou não. Uma pequena biblioteca
de padrões, sem LLM, reconhece as formas verificáveis comuns: nunca fazer push para / commitar em / tocar
main diretamente; nunca fazer force-push (ou "reescrever histórico"); nunca --no-verify / pular os hooks; executar
X antes de commitar ou fazer push (também testes, lint, typecheck); usar pnpm, não npm; não editar ou
commitar <path> (.env, lockfiles, diretórios gerados); não adicionar dependências (sem
perguntar); nunca rm -rf fora do repositório; nunca publicar; nunca fazer deploy; nunca usar / executar <command>.
"a menos que peçam" / "sem perguntar" faz uma quebra em um turno que uma pessoa digitou pedindo não contar.
Duas formas explícitas sempre funcionam e vencem os padrões:
- Release notes go in CHANGELOG.md. <!-- okgate: never "git tag*" -->
- Keep functions small. <!-- okgate: none -->
# okgate.yaml
audit:
rules:
- id: deploys
text: Only CI deploys
check: no-deploy # several: check: [no-verify, 'never "git reset --hard*"']
A gramática de verificação: no-push [branch,…], no-commit-to [branch,…], no-force-push [branch,…],
no-verify, run "<command>" before commit|push (@tests, @lint, @typecheck para os comandos
usuais), use pnpm|npm|yarn|bun, no-pm npm,…, no-edit "<glob>" …, no-commit "<glob>" …,
no-new-deps, no-rm-outside, no-publish, no-deploy, never "<command glob>" …, cada um com um
unless-asked opcional; none marca uma regra que nenhum script pode verificar.
As sessões. Transcripts do Claude Code (~/.claude/projects, ou $CLAUDE_CONFIG_DIR) e
rollouts do Codex (~/.codex/sessions, ou $CODEX_HOME) cujo diretório de trabalho é este repositório
ou uma de suas worktrees, subagentes incluídos. As chamadas de ferramenta são lidas do jeito que o hook mode as lê
(o mesmo parser de shell, os mesmos alvos de edição, as mesmas classes rm-fora-do-workspace e publish),
e "pediu por isso" é a leitura de turno humano do hook mode do transcript. A branch de um push é a que está
na linha de comando; para um git push puro, a que o Claude Code registrou que o push foi, senão
a branch que está com checkout.
O que sai da tela: nada. Sem rede, sem telemetria, sem LLM. O comando ou caminho que
quebrou uma regra é mostrado apenas no seu terminal. --json e o cache
(~/.okgate/audit-cache-v1/, contagens por transcript para que uma segunda execução leve cerca de um segundo e o
histórico sobreviva à limpeza de transcripts de 30 dias do Claude Code) guardam contagens, ids de regras, datas, números de
linha e hashes — nunca um comando, um caminho ou um prompt; um teste planta strings de marcador em cada
parte de um transcript sintético e verifica que nenhuma chega a qualquer um deles.
--enforce. Escreve as regras verificáveis em okgate.yaml como regras de hook mode
(hooks.shell.deny, hooks.shell.approval para "sem perguntar", hooks.paths.deny, as
classes irreversíveis embutidas, uma regra deploy), cada uma com um comentário nomeando a regra de onde
veio; mostra o diff; instala o hook se não estiver instalado. Nunca alterna hooks.mode:
um novo bloco de hooks começa em dry-run, e se o seu já está enforce, não escreve nada sem
--yes. "Executar X antes de fazer push" e "nunca commitar em main" permanecem apenas de auditoria (um hook de pré-execução
vê um comando, não a ordem ou a branch com checkout).
No seu agente. /okgate:audit (plugin do Claude Code, extensão do Gemini CLI) executa
audit --json e tem seu próprio agente julgando as regras marcadas como não verificáveis contra o
repositório e seu histórico git — nenhum token nosso. /okgate:hooks instala ou verifica o hook
mode.
Política
okgate init gera este arquivo com cada opção explicada inline. A forma curta:
version: 1
mode: dry-run # dry-run | enforce
upstreams:
- name: crm
url: https://mcp.example.com/mcp
auth: ${CRM_TOKEN} # the agent never sees this
- name: files
command: npx
args: [-y, "@modelcontextprotocol/server-filesystem", "."]
classify: # patterns win over annotations win over verb heuristics
write: [crm_update_*, crm_delete_*, email_send]
spend: [stripe_*, x402_*]
unknown: write # unclassifiable tools count as writes (or: read | block)
caps:
per_run: { writes: 50, deletes: 10, emails: 5, spend_usd: 25, tool_calls: 400 }
per_day: { spend_usd: 200 }
spend:
tools:
stripe_create_charge: { amount_arg: amount, divisor: 100, currency_arg: currency }
loop: { window: 30, max_repeats: 3, max_cycle_len: 4, max_read_repeats: 10 }
dry_run: { tools: [crm_delete_*], synthesize: true } # always fake these, even in enforce
approval:
tools: [crm_delete_*, db_drop_*]
wait_s: 0 # >0 holds the call open waiting for the decision
notify: { slack: ${SLACK_WEBHOOK_URL} }
kill: { file: .okgate/KILL, env: OKGATE_KILL }
agents: # okgate key create deployer --allow 'crm_get_*' --writes 5
- name: deployer
key_hash: sha256:…
allow: [crm_get_*, crm_update_contact]
caps: { per_run: { writes: 5 } }
alerts: { slack: ${SLACK_WEBHOOK_URL}, on: [LOOP_DETECTED, CAP_EXCEEDED, KILLED, APPROVAL_REQUIRED] }
audit: { path: .okgate/audit.jsonl, redact: true }
Ordem de classificação: padrões classify.* → MCP annotations.readOnlyHint / destructiveHint → heurísticas de verbo (get/list/search… leitura, create/update/delete/send/execute… escrita, pay/charge/refund… + stripe_*/x402_* gasto). okgate tools imprime cada ferramenta com sua classe e o porquê.
O que o agente vê
Cada bloco é um resultado de ferramenta in-band com isError: true e um corpo JSON no qual o modelo pode agir:
{
"code": "CAP_EXCEEDED",
"cause": "writes cap for this run is 50; used 50, this call would make it 51",
"fix": "stop and report to the user what is done and what remains; a human can raise caps.per_run in okgate.yaml or start a new run",
"retryable": false,
"details": {
"scope": "per_run",
"counter": "writes",
"limit": 50,
"used": 50,
"remaining": { "writes": { "per_run": 0 } }
}
}
Códigos: KILLED, APPROVAL_REQUIRED (repetível uma vez após aprovado), APPROVAL_DENIED, LOOP_DETECTED, CAP_EXCEEDED, TOOL_DENIED, UNKNOWN_TOOL, UPSTREAM_ERROR. Resultados bem-sucedidos e falsificados carregam _meta.okgate = { class, verb, mode, outcome, dryRun, seq, run_id }.
Identidade de execução: cabeçalho X-Run-Id (HTTP) → _meta.runId na chamada → sessão → um id por processo de proxy. Limites por execução e a janela de loop são por execução; limites por dia são por política (e por agente).
Comandos
| Comando | O que faz |
|---|---|
okgate init [--client path] [--all] [--no-probe] [--mode enforce] [--undo] | gerar a política, reescrever a configuração do cliente (nível de projeto por padrão) |
okgate proxy [--http --port 8788] [--agent name] [--run-id id] [--mode m] | executar o proxy (stdio por padrão) |
okgate report [--run id | --all] [--json] | o que esta execução fez / teria destruído / gasto; onde foi interrompida; status da cadeia |
okgate diff [--run id] | diff de mutação de escritas falsificadas |
okgate verify [audit.jsonl] | recalcular a cadeia de hash; sair com 1 na primeira quebra |
okgate status [--run id] | contadores vs limites, estado de kill, aprovações pendentes, proxy HTTP em execução |
okgate tools [--json] | cada ferramenta exposta com classe, verbo, upstream e o motivo |
okgate kill [reason] / okgate resume | interromper tudo agora / limpar |
okgate approvals [--all] / approve <id> / deny <id> [--note …] | a fila de aprovações |
okgate key create <agent> [--allow p]… [--deny p] [--writes n] [--spend n] [--mode m] / key list / key revoke <agent> | credenciais com escopo |
okgate connect <key> [--write] [--client path] [--all] [--url base] | apontar o cliente MCP desta máquina para um proxy hospedado (tiers pagos); imprime a configuração, --write a mescla |
okgate permission-diff [--base ref] [--head ref] [--fail-on-widen] | quais mudanças de configuração ampliam as permissões do agente (também uma GitHub Action) |
okgate hooks install [--project|--user] [--client claude|codex|gemini|all] [--command cmd] / hooks uninstall / hooks status | colocar a política na frente dos comandos de shell e edições de arquivo de um agente de codificação (hook mode) |
okgate hook pre-tool-use [--client c] | o que o hook instalado executa: lê o JSON do harness no stdin, responde em seu formato |
okgate audit [--since 7d|30d|all] [--client claude|codex|all] [--json] [--rules file] [--no-cache] [--no-evidence] | quais regras escritas os agentes de codificação quebraram, por regra, das sessões desta máquina (auditoria) |
okgate audit --enforce [--preview] [--yes] | escrever as regras verificáveis em okgate.yaml como regras de hook (dry-run), mostrar o diff, instalar o hook |
Tiers hospedados
A CLI aplica a política na sua máquina e não precisa de conta. Os níveis pagos movem a aplicação
para o lado do servidor — estado compartilhado entre máquinas, auditoria retida, alertas — e connect é como você
aponta um cliente para o seu:
npx -p @agentwares/agentguard okgate connect agk_... # print the MCP server block
npx -p @agentwares/agentguard okgate connect agk_... --write # merge it into your MCP config (existing servers are kept)
Diferente de init, connect adiciona um servidor remoto e deixa o resto da sua configuração intacta. A chave
vem do seu painel; todo o resto — URL do proxy, modo, banda — é respondido pelo servidor.
Endpoints de controle HTTP (token em .okgate/http.json): GET /health, GET /status?run=, POST /kill, POST /resume, GET|POST /approve/:id, /deny/:id, GET /approvals.
Experimente com os fixtures
git clone https://github.com/agentwares/agentguard && cd agentguard && pnpm install && pnpm build
cd apps/agentguard-cli
cat > okgate.yaml <<'YAML'
mode: dry-run
upstreams:
- name: crm
command: node
args: [dist/fixtures/crm-server.js]
caps: { per_run: { writes: 50 } }
YAML
node dist/fixtures/demo-agent.js --config okgate.yaml # a scripted agent: reads, writes, a deliberate loop, a 60-write burst
node dist/cli.js report && node dist/cli.js diff && node dist/cli.js verify
Conformidade e testes
pnpm test executa a suíte da CLI (85 testes; mais 191 em agentguard-core, 11 no SDK): a auditoria de ponta a ponta em um histórico sintético (arquivos de regras em todos os formatos, sessões do Claude Code e Codex com regras conhecidas mantidas e quebradas, strings de marcador que nunca devem chegar ao cache ou --json, --enforce), o modo hook de ponta a ponta (instalação para três clientes, o formato de resposta de cada cliente, o fluxo de aprovação, transcrições sintéticas reproduzindo um "user: sim, pode enviar" escrito por modelo), o motor sobre InMemoryTransport, o proxy stdio gerado (com e sem arquivo de política), o proxy HTTP Streamable com X-Run-Id, chaves com escopo e endpoints de controle, init contra configurações reais, e uma reprodução de fixture gravado (fixtures/recorded/crm-session.json; regrave com RECORD_FIXTURES=1). pnpm conformance executa a suíte oficial do servidor @modelcontextprotocol/conformance contra o proxy com um servidor de exemplo atrás dele (ferramentas, recursos, prompts, conclusões, logging, progresso, amostragem e elicitação são retransmitidos).
Limites (honestos)
- O proxy vê chamadas de ferramentas MCP; o modo hook vê comandos de shell, edições de arquivos e chamadas MCP diretas de um agente de codificação. O gasto de tokens na API do modelo só é visível através do
fetchprotegido do SDK (ou regrasspend.toolspara ferramentas MCP que chamam modelos). - O modo hook é uma proteção contra um agente agindo sem o consentimento de uma pessoa, não uma sandbox contra um hostil. Ele lê linhas de comando, não o que é executado: um script (
./release.sh,make deploy) que envia por dentro só é pego por uma regrashellque o nomeie; alvosxargs rmvêm do stdin e não são verificados; o valor de uma variável é desconhecido (umrmrecursivo de um conta como fora do workspace). "Nomeia a ação" é uma correspondência de palavras: "não envie ainda" nomeia envio. As palavras de cada classe estão no bloco de política; adicione as do seu idioma comirreversible.mentions. - O Claude Code escreve a transcrição de forma assíncrona; se o turno que autoriza um comando ainda não estiver no disco quando o hook rodar, o comando é retido (falha fechada) e a nova tentativa passa. Um prompt
claude -pnão tem rótuloorigin, então conta como turno da pessoa, a menos querequire_origin: true. Codex e Gemini CLI não têm portão de turno humano (seus comandos irreversíveis sempre precisam deokgate approveem enforce). Se o próprio hook falhar (umokgate.yamlquebrado), a chamada roda e a pessoa vê "esta chamada NÃO foi verificada". - A auditoria conta o que as transcrições mostram. Regras são reconhecidas por padrões, não compreendidas: uma regra redigida de forma incomum é listada como não verificável (adicione uma verificação inline), e uma redigida como um padrão que não significa pode ser mal interpretada (marque-a como
<!-- agentguard: none -->). Umgit pushsimples conta como um push para o branch verificado apenas na thread principal (ogitBranchde um subagente pode ser o da sessão, não o do seu worktree); uma tentativa que o harness recusou ainda conta, marcada como recusada. Sessões são correspondidas pelo diretório de trabalho: um worktree fora do repositório não é incluído. Rollouts do Codex não dizem quais turnos uma pessoa digitou, então uma quebra deunless-askedlá é relatada como indeterminada; a ferramenta JavaScriptexecdo Codex é contada, não lida. - Dry-run sintetiza resultados do
outputSchemada ferramenta; agentes que dependem de ids reais de uma cadeia create → update verão ids plausíveis mas falsos.dry_run.toolspermite que você finja apenas as ferramentas perigosas no modo enforce. - Contadores diários são um arquivo JSON sob um bloqueio de diretório; ok para uma workstation ou uma máquina, não para uma frota. O nível hospedado (em breve) é a versão de estado compartilhado.
- Botões "Aprovar" do Slack são links para o proxy HTTP local; eles funcionam para pessoas que conseguem alcançá-lo. Sem o modo HTTP, a mensagem carrega o comando
okgate approve <id>. - Uma cadeia de hash local é à prova de adulteração evidente, não à prova de adulteração, e tem um ponto cego: entradas excluídas do final do arquivo deixam uma cadeia mais curta que ainda verifica. Editar, excluir do meio e reordenar são todos pegos.
okgate verifyimprime o hash da cabeça e a contagem de entradas — registre-os (log de CI, ticket, chat) para fechar a lacuna, ou use o nível hospedado, que publica uma raiz Merkle diária que você pode verificar contra a execução.
Relacionados
@agentwares/agentguard-sdk— o mesmo motor para OpenAI Agents SDK / LangChain / funções simples, além dofetchprotegido para gastos de LLM.@agentwares/agentguard-core— o motor de política padrão Web (traga seus próprios armazenamentos).- permission-diff GitHub Action — comenta em PRs que ampliam
okgate.yaml,.claude/settings.jsonoumcp.json.
Este repositório
| Caminho | O que |
|---|---|
apps/agentguard-cli | o CLI okgate e proxy MCP — publicado como @agentwares/agentguard |
packages/agentguard-core | o motor de política, padrão Web — publicado como @agentwares/agentguard-core |
packages/agentguard-sdk | middleware para chamadas de ferramentas não-MCP — publicado como @agentwares/agentguard-sdk |
permission-diff | o GitHub Action, uses: agentwares/okgate/permission-diff@main |
git clone https://github.com/agentwares/okgate && cd okgate
pnpm install && pnpm test && pnpm build
Este repositório é gerado a partir do monorepo agentwares, que permanece privado porque também contém os produtos pagos. Issues e pull requests aqui são lidos e aplicados upstream.