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

Listed on mcpservers.org

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_usd por 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 o fetch protegido do SDK — uso de tokens LLM de respostas da OpenAI, Anthropic e Gemini. A chamada que excederia o limite recebe CAP_EXCEEDED com o orçamento restante.
  • Bloqueio de ações destrutivas com aprovações — approval.tools: [crm_delete_*] faz o agente obter APPROVAL_REQUIRED + um id; um humano executa okgate 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=1 também) ou POST /kill (HTTP): toda execução para instantaneamente com KILLED até 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 diff mostra 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, retorna LOOP_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_usd e contadores personalizados, por execução e por dia.
  • Log de auditoria com hash encadeado — cada chamada é uma linha JSONL com prev_hash e hash; okgate verify prova 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 install coloca 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, rm fora 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 audit lê CLAUDE.md, AGENTS.md, GEMINI.md e .cursor/rules e 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; --enforce transforma 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 como agentguard@agentwares? Esse plugin mantém as mesmas skills até pelo menos 2027-01-15). /okgate:init executa init e explica a política que escreveu; /okgate:report explica o que uma execução fez ou teria feito.

  • Gemini CLI: gemini extensions install https://github.com/agentwares/okgate, depois /okgate:init e /okgate:report. init lê .gemini/settings.json; os servidores httpUrl do Gemini são proxyados como Streamable HTTP (servidores url apenas 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):

    Add okgate to Cursor Install okgate in VS Code

    Os botões abrem cursor://anysphere.cursor-deeplink/mcp/install?name=okgate&config=… e vscode:mcp/install?{…}; o GitHub não renderiza esses esquemas como links, então os botões passam por cursor.com/link e vscode.dev/redirect. Onde o editor o inicia ao lado de um okgate.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ê executar init no projeto. init entã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:

FerramentaRespostas
okgate_get_statusqual 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_reporto que uma execução fez, ou teria feito em dry-run, e cada chamada interrompida (run_id opcional)
okgate_verify_audit_logse 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.

AgenteHook (verificado em 7 out 2026)Regras, aprovações, limites, auditoriaPortão de rodada humana
Claude CodePreToolUse: Bash, Write, Edit, mcp__* …simsim, a partir da transcrição da sessão
Codex CLIPreToolUse: 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 CLIBeforeTool: run_shell_command, write_file, replace, mcp_*simnã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

ComandoO 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 resumeinterromper 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 statuscolocar 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 fetch protegido do SDK (ou regras spend.tools para 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 regra shell que o nomeie; alvos xargs rm vêm do stdin e não são verificados; o valor de uma variável é desconhecido (um rm recursivo 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 com irreversible.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 -p não tem rótulo origin, então conta como turno da pessoa, a menos que require_origin: true. Codex e Gemini CLI não têm portão de turno humano (seus comandos irreversíveis sempre precisam de okgate approve em enforce). Se o próprio hook falhar (um okgate.yaml quebrado), 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 -->). Um git push simples conta como um push para o branch verificado apenas na thread principal (o gitBranch de 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 de unless-asked lá é relatada como indeterminada; a ferramenta JavaScript exec do Codex é contada, não lida.
  • Dry-run sintetiza resultados do outputSchema da ferramenta; agentes que dependem de ids reais de uma cadeia create → update verão ids plausíveis mas falsos. dry_run.tools permite 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 verify imprime 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

Este repositório

CaminhoO que
apps/agentguard-clio CLI okgate e proxy MCP — publicado como @agentwares/agentguard
packages/agentguard-coreo motor de política, padrão Web — publicado como @agentwares/agentguard-core
packages/agentguard-sdkmiddleware para chamadas de ferramentas não-MCP — publicado como @agentwares/agentguard-sdk
permission-diffo 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.