# A aprovação de agentes no macOS precisa distinguir cada build

Aprovar um agente de IA pelo nome amigável do comando é uma forma de aprovar o executável errado. Em um único Mac, é fácil acumular uma versão do fornecedor, uma versão beta, uma cópia instalada por gerenciador de pacotes e um build feito a partir do código-fonte, todos respondendo ao mesmo nome. Se o seu limite de aprovação não consegue diferenciá-los, uma decisão tomada para um build revisado pode se aplicar silenciosamente a outro.

A solução não é uma lista de permissões mais longa. Você precisa decidir quais propriedades identificam uma execução do agente no seu ambiente, comprovar que cada cópia instalada tem as propriedades esperadas e testar o comportamento da aprovação com as duas cópias presentes. Caminhos, assinaturas, requisitos designados e hashes respondem a perguntas diferentes. É quando esses elementos são confundidos que os problemas aparecem.

## Um nome de comando pode apontar para vários executáveis

Um comando do shell é uma busca, não uma identidade. Quando você digita `claude`, `agent` ou o nome de um wrapper, o shell pode encontrar um alias, uma função, um shim, um link simbólico, um diretório de gerenciador de pacotes ou um arquivo que aparece antes da cópia pretendida no `PATH`.

Comece pelo terminal, editor, serviço de inicialização ou executor de automação que realmente inicia o agente. Não inspecione um shell interativo conveniente e presuma que o resultado vale para os outros ambientes. Shells de login, aplicativos gráficos e executores de CI costumam receber variáveis de ambiente diferentes.

Execute primeiro:

```sh
type -a agent-name
command -v agent-name
```

Um resultado útil poderia ser:

```text
agent-name is /Users/me/bin/agent-name
agent-name is /opt/homebrew/bin/agent-name
agent-name is /usr/local/bin/agent-name
/Users/me/bin/agent-name
```

Essa saída informa que o primeiro caminho vence neste shell. Ela não informa se `/Users/me/bin/agent-name` é um binário direto, um link simbólico, um script que inicia outro binário ou um wrapper que altera variáveis de ambiente antes de transferir o controle.

Resolva o arquivo antes de inspecioná-lo:

```sh
BIN="$(command -v agent-name)"
python3 - <<'PY' "$BIN"
import os, sys
print(os.path.realpath(sys.argv[1]))
PY
file "$BIN"
```

Se `file` informar que é um script de shell, leia-o. Se informar que é um link simbólico, inspecione o destino. Se informar que é um executável Mach-O universal, inspecione esse executável. Um wrapper pode fazer uma tela de aprovação parecer correta e, mais tarde, iniciar um processo filho diferente.

Não pare depois de encontrar o vencedor atual. Faça o inventário de todos os resultados de `type -a`, além das cópias na pasta Applications do fornecedor, na pasta Downloads, no checkout do código-fonte e nos diretórios de gerenciadores de pacotes. A cópia que vence hoje pode perder amanhã depois de uma atualização do gerenciador de pacotes ou de uma pequena alteração no PATH.

## Um caminho é evidência de localização, não prova de identidade

As equipes costumam começar por uma regra de caminho porque os caminhos são fáceis de ler. `/Applications/Agent.app` parece deliberado. `/Users/me/dev/agent/bin/agent` parece experimental. São pistas úteis, mas um caminho sozinho não confirma que código está ali agora.

Um gerenciador de pacotes pode substituir o arquivo apontado por um link simbólico estável. Um instalador direto pode sobrescrever um pacote de aplicativo no mesmo lugar. Um build local pode usar sempre o mesmo caminho de saída. Um invasor com permissão de escrita suficiente pode substituir um arquivo em um caminho aprovado. O caminho apenas informa onde o carregador encontrou o arquivo.

Use quatro campos separados no inventário:

| Campo | O que informa | O que não informa |
| --- | --- | --- |
| Caminho resolvido | Onde esta inicialização encontrou o arquivo | Quem o produziu ou se ele mudou |
| Informações de assinatura | Qual identidade de assinatura foi associada ao código | Se outro build do mesmo assinante tem comportamento idêntico |
| Requisito designado | A regra de continuidade que o macOS associa ao código | Se a regra é restrita o suficiente para o seu objetivo de aprovação |
| Hash SHA-256 | Os bytes exatos que você inspecionou | Se uma atualização futura deve herdar a confiança |

Essa distinção importa porque cada campo muda em um ritmo diferente. Uma atualização do fornecedor pode preservar o caminho e o requisito designado, mas mudar o hash. Uma versão beta pode manter a autoridade de assinatura e usar outro identificador de pacote. Um build local pode ter a mesma revisão do código-fonte de uma versão lançada, mas carregar uma assinatura ad hoc ou nenhuma assinatura.

A Technical Note TN2206 da Apple explica o papel pretendido do requisito designado: ele deve corresponder a atualizações legítimas de um programa e excluir código não relacionado. Isso faz dele um mecanismo de continuidade. Não faz dele uma resposta universal para «eu quis dizer exatamente este binário?». A Apple também observa que o requisito padrão é sintetizado a partir da configuração de assinatura, portanto sua abrangência depende de como o desenvolvedor assinou o build.

Os sistemas de aprovação precisam da mesma separação. A decisão de confiar na próxima versão compatível de um fornecedor é diferente da decisão de confiar em um artefato exato de uma versão específica. Tratar as duas como a mesma decisão impede explicar o que o usuário aprovou.

## Inspecione a assinatura antes de criar uma regra de aprovação

Para cada executável candidato, registre os detalhes da assinatura e o hash. Os comandos a seguir usam apenas ferramentas incluídas no macOS e `shasum`:

```sh
inspect_agent() {
  target="$1"
  echo "=== $target ==="
  echo "Resolved path: $(python3 - <<'PY' "$target"
import os, sys
print(os.path.realpath(sys.argv[1]))
PY
)"
  shasum -a 256 "$target"
  codesign --display --verbose=4 "$target" 2>&1 \
    | grep -E '^(Executable|Identifier|TeamIdentifier|Authority|CDHash)='
  codesign --display -r- "$target" 2>&1 \
    | sed -n '/designated/,$p'
  codesign --verify --strict --verbose=2 "$target" 2>&1
}

inspect_agent "$(command -v agent-name)"
```

O formato da saída importa mais que uma string específica do fornecedor:

```text
=== /opt/homebrew/bin/agent-name ===
Resolved path: /opt/homebrew/Cellar/agent-name/2.4.1/bin/agent-name
3b1f...  /opt/homebrew/bin/agent-name
Executable=/opt/homebrew/bin/agent-name
Identifier=com.example.agent
TeamIdentifier=AB12CDE345
Authority=Developer ID Application: Example, Inc. (AB12CDE345)
CDHash=9c1a...
designated => anchor apple generic and identifier "com.example.agent" and certificate leaf[subject.OU] = "AB12CDE345"
/opt/homebrew/bin/agent-name: valid on disk
/opt/homebrew/bin/agent-name: satisfies its Designated Requirement
```

Não trate o `CDHash` exibido como substituto do seu registro SHA-256. O hash do CodeDirectory faz parte da estrutura de assinatura de código e pode variar conforme a estrutura da assinatura. `shasum -a 256` fornece uma impressão digital familiar do arquivo exato para o registro de teste. Registre os dois ao investigar uma colisão.

Em um pacote de aplicativo, inspecione o executável real, não apenas o diretório do pacote. Encontre-o com:

```sh
APP="/Applications/Agent.app"
EXEC="$APP/Contents/MacOS/$(defaults read "$APP/Contents/Info" CFBundleExecutable)"
inspect_agent "$EXEC"
```

Se o agente iniciar um auxiliar, inspecione o auxiliar também. O executável que abre uma janela do terminal nem sempre é o processo que faz chamadas HTTP ou inicia o SSH. O sistema de aprovação deve identificar o processo que solicita a ação sensível, enquanto o teste deve verificar toda a cadeia de inicialização.

A TN3127 mais recente da Apple vai além do guia de assinatura anterior: ela mostra como os requisitos designados padrão diferem entre os tipos de assinatura e por que variantes distribuídas separadamente podem não ser compatíveis entre si. Isso é um alerta contra suposições. Compare o texto real do requisito nos arquivos instalados.

## Builds estáveis, beta, de gerenciador de pacotes e locais precisam de uma matriz de testes

Torne o inventário concreto. Escolha as cópias que podem existir no seu Mac, dê a cada uma um rótulo curto e registre o que você espera que elas compartilhem ou não compartilhem com as outras.

| Rótulo | Origem típica | Estado esperado da assinatura | Expectativa de aprovação |
| --- | --- | --- | --- |
| Estável | Instalador do fornecedor ou pacote de aplicativo | Identidade de assinatura de lançamento | Candidato de referência |
| Beta | Canal beta do fornecedor | Pode compartilhar a identidade de lançamento | Deve ser testado separadamente |
| Gerenciador de pacotes | Formula, cask, shim no estilo npm ou similar | Depende do empacotamento original | Deve resolver o destino iniciado |
| Local | Checkout do código-fonte ou saída de build | Desenvolvimento, ad hoc ou sem assinatura | Revisão separada por padrão |

Essa tabela não determina a resposta certa. Ela evita a resposta preguiçosa, que presume que o nome do canal informa o suficiente. Uma versão beta pode ser assinada exatamente como a estável. Uma cópia de gerenciador de pacotes pode ser um artefato do fornecedor sem alterações, um artefato reempacotado ou um script que baixa outro executável. Um build local pode carregar uma assinatura de desenvolvimento válida que parece mais oficial do que deveria.

Crie uma linha para cada cópia instalada em um arquivo de texto que fique junto das anotações de configuração do agente:

```text
label: stable
launch path: /Applications/Agent.app/Contents/MacOS/agent-name
resolved path: /Applications/Agent.app/Contents/MacOS/agent-name
version: 2.4.1
identifier: com.example.agent
team or authority: AB12CDE345
sha256: 3b1f...
designated requirement: anchor apple generic and identifier "com.example.agent" ...
expected approval group: release

label: local
launch path: ~/src/agent/build/agent-name
resolved path: /Users/me/src/agent/build/agent-name
version: git revision recorded separately
identifier: ad hoc or absent
team or authority: none
sha256: 8e52...
designated requirement: unavailable or different
expected approval group: local only
```

Registre a versão, mas não deixe que o número da versão determine a decisão. Strings de versão são metadados do aplicativo. Um arquivo pode declarar uma versão que não corresponde ao lançamento que você achou ter baixado. A assinatura e o hash fornecem fatos independentes.

O caso complicado é haver duas linhas com o mesmo identificador, equipe e requisito designado, mas hashes diferentes. Isso não é necessariamente um defeito. Significa que o fornecedor criou dois builds que o macOS pode considerar instâncias do mesmo programa. Se você quer que a aprovação acompanhe o canal de lançamento do fornecedor, isso pode ser aceitável. Se precisa que a aprovação cubra apenas um artefato específico, ela é ampla demais.

## O teste errado aprova cada cópia isoladamente

Testar o build estável na segunda-feira e o beta na terça-feira quase não diz nada sobre cobertura cruzada. Cada teste pode mostrar uma solicitação porque ainda não existe uma aprovação ativa. Você precisa ter as duas cópias instaladas e manter uma sessão de uma delas ativa enquanto a outra solicita acesso.

Execute esta sequência em uma conta descartável ou usando credenciais que não possam alterar sistemas de produção:

1. Coloque as cópias estável, beta, de gerenciador de pacotes e local no lugar. Confirme os caminhos resolvidos e salve a saída da inspeção.
2. Limpe ou revogue a sessão existente do agente pela ferramenta de aprovação que você usa. Confirme que a próxima chamada sensível exigirá uma nova decisão.
3. Inicie a cópia estável e faça uma chamada inofensiva a um endpoint de teste. Aprove apenas essa execução. Mantenha o processo ativo.
4. Enquanto o processo estável continuar ativo, inicie a cópia beta e faça a mesma chamada inofensiva. Observe se ela pede aprovação e inspecione a identidade do processo mostrada no cartão.
5. Repita com as cópias do gerenciador de pacotes e local. Depois inverta a ordem do PATH e repita o teste do gerenciador de pacotes.

O resultado esperado depende da regra escolhida. Se estável e beta devem ser separados, o beta precisa pedir aprovação enquanto o estável continua aprovado. Se eles devem compartilhar intencionalmente um grupo de aprovação, o cartão e a trilha de auditoria precisam trazer detalhes suficientes para que esse agrupamento seja claro para um revisor.

Não chame uma API de produção durante o teste. Use um endpoint de teste que retorne uma resposta fixa e deixe um marcador visível e inofensivo nos próprios logs. Para SSH, use uma conta de teste com um comando limitado a imprimir informações de identidade:

```sh
ssh agent-test@host.example 'id; hostname; date -u +%FT%TZ'
```

O teste tem duas observações: o que a tela de aprovação informa e o que o sistema de destino registra. Salve o horário, o hash do executável, o ID do processo e o marcador retornado. Se um wrapper alterou qual executável realmente foi executado, essa diferença aparecerá antes do que em uma discussão depois de um incidente.

Uma falha comum é a seguinte. Um desenvolvedor aprova o aplicativo estável e depois instala um beta que coloca `/Users/me/bin` à frente de `/Applications` por meio de uma alteração na configuração do shell. O nome do comando continua igual. O beta faz uma chamada sem uma nova solicitação porque a verificação de autorização reconhece uma identidade de assinatura compartilhada ou um agrupamento de processos amplo demais. Ninguém percebe porque o processo estável original continua em execução e o registro de auditoria informa apenas `agent-name`. Você evita essa falha forçando o teste de sobreposição, não lendo as notas de lançamento da versão.

## A identidade do processo e os bytes exatos respondem a perguntas diferentes

Uma aprovação pode ser associada de forma razoável a uma identidade de processo assinado que permanece estável entre atualizações. Ela também pode ser associada aos bytes exatos. Nenhuma escolha é universalmente correta.

Use a identidade do processo quando pretende confiar em uma linha de lançamento mantida por um assinante conhecido. Isso evita obrigar o usuário a aprovar cada atualização corretiva apenas porque o hash do executável mudou. Também permite que uma identidade de processo fixa sobreviva a um caminho normal de atualização.

Use os bytes exatos quando o build for experimental, produzido localmente, modificado de forma independente ou obtido de um canal que você não quer misturar com o estável. Fixar o hash é especialmente útil em uma investigação ou reprodução curta, quando você quer que a decisão expire assim que o arquivo mudar.

O ponto perigoso é fingir que um identificador de assinatura sozinho é uma identidade. A Apple documenta que um identificador de assinatura pode ser reivindicado por vários assinantes. A Apple recomenda combiná-lo com validação e restrições de equipe relevantes ao verificar o código. Na prática, `Identifier=com.example.agent` sem a autoridade ou a equipe é um rótulo que outra pessoa pode reutilizar.

Um requisito designado geralmente é mais forte porque pode combinar o identificador com restrições de autoridade de assinatura. Ainda assim, pode ser mais amplo que a sua intenção. Um requisito que aceite qualquer build válido assinado por uma equipe com um determinado identificador pode abranger corretamente uma atualização estável, uma versão beta e um build local de teste criado pelo fornecedor. Isso só é bom se você realmente quiser os três no mesmo grupo de aprovação.

Escreva a decisão do grupo em linguagem simples ao lado da entrada do inventário. Por exemplo:

```text
Release group: accept future vendor-signed builds with the release identifier.
Beta group: separate, even when signed by the same vendor identity.
Local group: exact SHA-256 only; rebuild requires another approval.
```

Essa observação obriga a tomar a decisão antes que a interface peça um clique. Ela também revela se a ferramenta consegue expressar a distinção. Se não conseguir, use um limite operacional mais estreito, como exigir aprovação por chamada para os builds beta e locais até que a ferramenta ofereça esse recurso.

## A aprovação por sessão não substitui a aprovação por chamada

A aprovação por sessão responde a «este processo de agente pode executar com acesso durante esta execução?». A aprovação por chamada responde a «esta credencial específica pode ser usada para esta ação específica agora?». O primeiro controle limita qual processo obtém uma sessão. O segundo limita a consequência dessa sessão.

O Sallyport usa uma sequência fixa de decisões: um cofre bloqueado nega toda ação, um novo processo de agente normalmente precisa de aprovação de sessão e determinadas credenciais podem exigir uma nova aprovação a cada uso. O cartão de aprovação destaca a autoridade de assinatura de código do processo, uma evidência útil, mas você ainda deve executar o teste de sobreposição quando existir mais de um build.

Use a verificação mais frequente para credenciais que possam causar mudanças irreversíveis ou visíveis externamente. Credenciais de implantação em produção, APIs administrativas destrutivas e acesso SSH capaz de alterar infraestrutura compartilhada não devem herdar confiança apenas porque um processo de agente era aceitável no início de uma execução longa.

Não resolva uma colisão de binários marcando todas as credenciais para aprovação a cada chamada para sempre. Isso transforma um problema de design em fadiga de aprovação. As pessoas aprovam solicitações repetidas e previsíveis sem lê-las. Em vez disso, separe os builds que não devem compartilhar acesso e reserve a aprovação por chamada para as ações em que uma pessoa precisa ver o momento do uso.

Em um gateway de ações, teste a mesma matriz no limite da ação. Inicie cada binário, faça uma solicitação HTTP de teste e um comando SSH de teste se os dois canais estiverem em uso, depois compare o registro da sessão com o registro da chamada individual. Os registros devem permitir responder qual executável iniciou a solicitação, qual aprovação a cobriu e se a credencial exigiu outra confirmação.

## Gerenciadores de pacotes e shims escondem o executável que você precisa inspecionar

Os gerenciadores de pacotes costumam instalar caminhos de entrada estáveis que apontam para outro lugar. Um comando em `/opt/homebrew/bin/agent-name` pode ser um link simbólico para um diretório Cellar versionado. Outra ferramenta pode instalar um shim em JavaScript, Python ou shell que escolhe um runtime e carrega um pacote de um diretório de cache.

Siga a cadeia até chegar ao processo que faz a solicitação sensível. Estes comandos ajudam nos casos comuns:

```sh
ls -l "$(command -v agent-name)"
readlink "$(command -v agent-name)" || true
head -n 40 "$(command -v agent-name)" 2>/dev/null || true
```

No macOS, `readlink` pode mostrar apenas um salto. O pequeno resolvedor em Python da primeira seção é mais confiável para encontrar o destino final. Se o arquivo de entrada for um script, procure por `exec`, chamadas de runtime, caminhos de binários baixados e variáveis de ambiente que selecionem um canal de lançamento.

Não presuma que uma versão do gerenciador de pacotes é equivalente à versão do fornecedor com o mesmo número. O pacote pode aplicar patches, reempacotar um binário, compilar a partir do código-fonte ou iniciar um runtime diferente. Trate o executável instalado como aquilo que você aprova e guarde o recibo do pacote apenas como contexto adicional.

O mesmo alerta vale para extensões de IDE e integrações de terminal. Um iniciador gráfico pode incluir uma cópia enquanto o shell usa outra. Teste cada ponto de entrada capaz de iniciar um agente. «Ele pede aprovação corretamente no Terminal» não é evidência sobre uma tarefa em segundo plano iniciada por um editor.

## Builds locais precisam deixar claro que são locais

Um build local é útil justamente porque pode ser diferente de uma versão lançada. Pode conter um patch, uma atualização de dependência não revisada, uma alteração do compilador, uma flag de depuração ou um arquivo gerado que nunca entrou no artefato do fornecedor. Ele não deve pegar silenciosamente a reputação de aprovação do build lançado.

Primeiro, inspecione o estado da assinatura:

```sh
LOCAL="$HOME/src/agent/build/agent-name"
codesign --display --verbose=4 "$LOCAL" 2>&1 | sed -n '1,25p'
codesign --verify --strict --verbose=2 "$LOCAL" 2>&1
shasum -a 256 "$LOCAL"
```

Uma saída sem assinatura, uma assinatura ad hoc e uma assinatura de desenvolvimento não são equivalentes. Um executável sem assinatura oferece à camada de aprovação uma evidência de identidade menos durável. Uma assinatura ad hoc pode fazer um arquivo parecer assinado sem vinculá-lo a uma identidade de desenvolvedor. Uma assinatura de desenvolvimento identifica um contexto de desenvolvimento, mas ainda não transforma o arquivo em um artefato de lançamento.

O padrão mais seguro é simples: coloque os binários locais em um diretório separado, dê a eles um nome de comando visivelmente diferente se você controlar o build e exija uma nova aprovação sempre que o hash mudar. Se não puder renomear o comando, deixe o caminho resolvido e o status de build local claros no runbook e na saída dos testes.

Evite o conselho popular de assinar novamente todo build local com o mesmo certificado usado nos lançamentos apenas para reduzir solicitações. Ele é popular porque facilita o desenvolvimento. Está errado quando o modelo de aprovação usa a autoridade de assinatura como um limite relevante. Você ampliou a identidade de lançamento para incluir toda máquina e todo script que possam acessar esse certificado. Mantenha o material de assinatura de lançamento fora dos builds locais comuns, a menos que o processo de lançamento consiga sustentar essa afirmação.

## Os registros de auditoria precisam permitir reconstruir a decisão depois

Um registro de aprovação que diga apenas «agente aprovado» é uma evidência fraca. Seis semanas depois, você não conseguirá saber se o programa aprovado veio do instalador estável, da pasta beta, de um link simbólico do gerenciador de pacotes ou de um checkout local.

Para cada teste e mudança operacional importante, retenha estes fatos:

- o caminho de inicialização e o caminho final resolvido
- o identificador de assinatura, a autoridade ou equipe e o texto do requisito designado
- o hash SHA-256 e a versão do aplicativo
- o ID do processo, o horário de início e o resultado da sessão
- o destino da ação e o resultado da chamada individual

O Sallyport mantém as execuções do agente no diário Sessions e as ações individuais no diário Activity, ambos projetados a partir de um log de auditoria criptografado e encadeado por hashes. A verificação offline `sp audit verify` pode confirmar se a cadeia desse log criptografado continua válida, mas a integridade do log não preenche campos de identidade que você nunca registrou. Inclua as evidências do executável no contexto do evento enquanto ainda puder inspecionar a máquina.

Execute uma verificação de auditoria depois de alterar a matriz de builds, revogar uma sessão e repetir o teste de sobreposição. Você precisa confirmar duas coisas: o sistema registrou as execuções separadas esperadas e a trilha de registros continua válida quando lida de forma independente. Uma cadeia íntegra prova a continuidade do registro, não que a decisão original de agrupamento foi acertada.

## Transforme isso em um teste de regressão, não em uma limpeza única

Várias cópias voltam a aparecer. Alguém instala uma versão beta para testar uma correção. Um gerenciador de pacotes atualiza durante a noite. Um colega compartilha um build local. O aplicativo estável é atualizado no mesmo lugar. Se você só testar depois de um susto, descobrirá a colisão quando o agente já tiver acesso.

Mantenha um script curto que escreva um relatório com data e hora para cada caminho candidato. Execute-o depois de mudanças de instalação, antes de conceder uma nova credencial de alto impacto e sempre que modificar arquivos de inicialização do shell ou as configurações de agente do editor.

```sh
#!/bin/zsh
set -eu

for candidate in \
  "/Applications/Agent.app/Contents/MacOS/agent-name" \
  "/opt/homebrew/bin/agent-name" \
  "$HOME/src/agent/build/agent-name"; do
  [[ -e "$candidate" ]] || continue
  echo "### $candidate"
  echo "resolved: $(python3 -c 'import os,sys; print(os.path.realpath(sys.argv[1]))' "$candidate")"
  shasum -a 256 "$candidate"
  codesign --display --verbose=4 "$candidate" 2>&1 \
    | grep -E '^(Identifier|TeamIdentifier|Authority|CDHash)=' || true
  codesign --display -r- "$candidate" 2>&1 \
    | grep 'designated' || true
  echo
 done
```

Compare o relatório com a última cópia revisada. Um hash alterado é esperado depois de uma atualização. Uma autoridade de assinatura, um identificador ou um requisito designado alterado merece uma decisão explícita antes que você trate a alteração como uma atualização rotineira. Um novo caminho que apareça antes do caminho de lançamento em `type -a` merece a mesma atenção.

O padrão prático é simples: uma aprovação deve se aplicar ao grupo de executáveis pretendido, e suas evidências devem mostrar por que esse grupo inclui um build e exclui outro. Coloque as cópias estável, beta, de gerenciador de pacotes e local no mesmo Mac, mantenha um processo aprovado em execução e obrigue cada outra cópia a pedir aprovação. Se o resultado surpreender você, o limite de aprovação está vago demais.
