learnaiwithrafa
ClaudeSetup

O settings.json que acaba com os pedidos de permissão

As regras de permissão são a única configuração do Claude Code que soma entre arquivos em vez de substituir. Entendendo isso, dá pra montar uma allowlist que se sustenta — e parar de aprovar o mesmo comando quarenta vezes por dia.

5 min de leitura2 fontes
  • #claude-code
  • #settings
  • #permissions

A maioria das pessoas conhece o settings.json tentando calar um pedido de permissão: adiciona uma linha e nunca abre o arquivo de novo. Só que existe um detalhe que faz esse arquivo finalmente fazer sentido, e ele está escondido na documentação: toda configuração do Claude Code substitui a de escopo menor, menos as permissões, que se somam.

Mude o model em três arquivos e só o de maior precedência vale. Adicione regras de allow em três arquivos e você fica com todas. Essa assimetria é exatamente o motivo pelo qual vale investir numa allowlist: o que você escreve nas suas configurações de usuário continua valendo dentro de todo projeto, e um projeto pode acrescentar regras sem apagar as suas.

A ordem de precedência, na versão curta

Do mais forte pro mais fraco: managed (instalado pelo TI, não tem como sobrepor) → flags de linha de comando.claude/settings.local.json (seu, fora do git) → .claude/settings.json (do time, versionado) → ~/.claude/settings.json (seu, valendo em todos os projetos).

Três regras de bolso que saem daí:

  • Convenção que o time todo precisa → arquivo do projeto, versionado.
  • Caminho que só existe na sua máquina → settings.local.json.
  • Hábito que você quer em tudo → ~/.claude/settings.json.

Rode /status pra ver quais arquivos realmente carregaram. Permissões, hooks e env recarregam na hora que você salva; model e outputStyle só valem na próxima sessão ou depois de um /clear.

Escreva regras que casam com o que você digita de verdade

As regras têm o formato Tool(padrão). A pegadinha que rouba mais tempo é o alcance do curinga:

  • Bash(npm run test) — só a string exata. npm run test unit não casa.
  • Bash(npm run test *) — casa com npm run test unit, mas não com npm run test unit/advanced. Um * sozinho não atravessa barra.
  • Bash(npm run *) — casa com tudo abaixo de npm run, barras incluídas.

Ou seja: a regra que quase todo mundo escreve, Bash(npm run test), é justamente a que nunca mais dispara depois do primeiro teste com parâmetro. Abra o curinga de propósito.

Uma allowlist inicial larga o bastante pra ser útil e estreita o bastante pra ser segura:

{
  "permissions": {
    "allow": [
      "Bash(pnpm lint)",
      "Bash(pnpm typecheck)",
      "Bash(pnpm --filter * test *)",
      "Bash(git status)",
      "Bash(git diff *)",
      "Bash(git log *)",
      "Read(./**)"
    ],
    "deny": [
      "Read(./.env)",
      "Read(./.env.*)",
      "Read(./secrets/**)",
      "Bash(git push --force *)"
    ]
  }
}

Repare no que está no deny: ler o .env. Liberar Read(./**) sem esse par é exatamente como credencial vai parar num histórico de conversa. deny sempre ganha de allow, em qualquer escopo — então a lista de deny é sua rede de segurança real. Coloque ela nas configurações de usuário e ela protege todo repositório em que você encostar.

A allowlist não te salva de tudo

Essa parte eu aprendi na marra, e ela não está em nenhuma cola. O matcher quebra o comando inteiro em token e compara a sequência de argumentos — o que significa que certos formatos de comando vão pedir permissão por mais que você libere, porque o que você digitou não é o formato que a sua regra descreve.

Dois que me pegaram várias vezes:

  • cd no começo junto com redirecionamento. cd apps/web && node script.ts > out.json não é o comando que a sua regra Bash(node script.ts *) reconhece. Use caminho absoluto e sem cd na frente.
  • Expansão de variável de shell. test -n "$MEU_TOKEN" precisa ser resolvido pelo shell antes de alguém saber o que ele faz, então não vira um padrão estável.

Minha regra de bolso depois de meses nisso: escreva comandos pensando em serem reconhecíveis. Caminho absoluto, sem cd, sem $VAR, um comando por chamada. Se você está aprovando a mesma coisa sem parar, quase sempre a correção é reescrever o comando, não afrouxar a regra.

Quatro outras chaves que valem no primeiro dia

  • permissions.defaultMode — o padrão é "ask". "auto" entrega cada chamada a um classificador em vez de você, e combina bem com uma lista de deny sólida. "deny" é um bom modo pra um repositório que você só está lendo.
  • fallbackModel — até três modelos pra tentar quando o principal está fora, tipo ["claude-sonnet-5", "claude-haiku-4-5"]. Não custa nada até o dia em que salva sua sessão. Atenção: esse não se soma entre escopos.
  • env — variáveis aplicadas em toda sessão. Pare de exportar as mesmas três coisas na mão.
  • cleanupPeriodDays — os arquivos de sessão ficam guardados 30 dias por padrão. Se você mexe com coisa sensível, diminua.

E uma pra conhecer mais do que pra configurar: claudeMdExcludes. Num monorepo, o Claude carrega todo CLAUDE.md das pastas acima, inclusive os de outros times. Exclua por glob no settings.local.json e você recupera contexto.

Faça isso hoje

Abra o ~/.claude/settings.json — o de usuário, não o de um projeto — e adicione só o bloco de deny aí de cima. Quatro linhas protegendo todo repositório da sua máquina, pra sempre. Depois, no projeto atual, rode /status, veja quais arquivos carregaram e olhe os três últimos comandos que você aprovou. Se algum começa com cd, reescreva com caminho absoluto antes de criar uma regra de allow que nunca ia casar.

Fontes