8 min de leitura

Verificações de sucesso de comandos SSH: prove que o trabalho remoto terminou

As verificações de sucesso de comandos SSH exigem mais que um código de retorno zero. Aprenda a validar a saída, ler o estado remoto, lidar com timeouts e repetir ações com segurança.

Verificações de sucesso de comandos SSH: prove que o trabalho remoto terminou

Um comando SSH que termina com status 0 concluiu uma única tarefa limitada: o programa remoto informou ao shell que teve sucesso. Isso é uma evidência útil, mas não prova que uma implantação chegou à versão correta, que um serviço permaneceu saudável ou que uma alteração de configuração entrou em vigor.

Já vi muita automação declarar vitória porque ssh host command retornou zero, para depois descobrir que o comando escreveu no diretório errado, colocou na fila um trabalho que falhou mais tarde ou reiniciou um serviço que travou imediatamente. A solução não é adicionar logs mais otimistas. Trate uma alteração remota como concluída somente quando três fatos independentes concordarem: o SSH chegou ao programa remoto, o programa retornou o resultado esperado e uma leitura independente confirmou o estado que você pretendia criar.

Um código de saída SSH informa apenas uma camada da operação

Um status de saída informa sobre a conclusão do processo, não sobre o resultado operacional completo. Um comando remoto está dentro de uma cadeia de suposições: conectividade de DNS e de rede, identidade do host, autenticação, comportamento do shell, análise do comando, dependências, permissões remotas e o estado que você esperava alterar.

O manual do OpenSSH para ssh(1) diz que ssh sai com o status do comando remoto, ou com 255 quando ocorre um erro. Essa distinção importa. Um status 255 normalmente significa que o cliente SSH não conseguiu estabelecer ou manter a sessão como necessário. Um status 1, 2 ou outro valor diferente de zero normalmente veio do comando remoto. O zero também veio desse comando.

Essa definição não diz que zero significa «a alteração em produção está correta». Não poderia dizer. O SSH não sabe se /srv/app/current aponta para a versão pretendida, se um daemon aceita solicitações depois de systemctl restart ou se uma migração de banco confirmou as linhas de que a aplicação precisa.

O POSIX define um status de saída zero como conclusão bem-sucedida de um comando. O ponto útil dessa definição é seu alcance limitado. Ela descreve o contrato do comando. Se o seu contrato diz apenas «execute esta linha de shell», o zero prova muito pouco. Dê ao comando um contrato mais preciso e depois teste o estado fora dele.

Uma forma prática de pensar nas falhas é separá-las em três camadas:

  • Falha do SSH: o cliente não conseguiu conectar, autenticar, verificar o host ou concluir a sessão.
  • Falha do comando: o processo remoto detectou um erro e retornou um status diferente de zero.
  • Falha do resultado: o processo retornou zero, mas o estado remoto pretendido está ausente, errado, incompleto ou foi revertido depois.

As equipes costumam misturar as duas últimas camadas. Isso deixa os relatórios de incidentes vagos e torna as novas tentativas perigosas. Quando você sabe qual camada falhou, sabe se deve verificar credenciais e conectividade, corrigir o comando ou reparar o estado remoto.

O texto de sucesso só é evidência quando é definido com precisão

As verificações da saída detectam erros que o status de saída não consegue perceber, mas somente quando a saída tem um contrato explícito. Procurar palavras como success, complete ou deployed em uma transcrição voltada para pessoas é uma evidência fraca. Muitas ferramentas imprimem essas palavras antes de um comando posterior falhar, e wrappers costumam imprimi-las depois de apenas enviarem um trabalho assíncrono.

Faça o comando remoto emitir um único registro com a identidade e o estado esperados. JSON costuma ser conveniente, mas uma linha com delimitadores fixos também funciona quando você controla os valores. O importante é que o script imprima o registro somente depois de concluir o trabalho que afirma relatar.

Por exemplo, suponha que um script de versão altere um link simbólico para um diretório de release. Este script remoto emite o destino final, em vez de uma mensagem vaga de progresso:

#!/bin/sh
set -eu

release="$1"
base=/srv/example/releases
link=/srv/example/current

[ -d "$base/$release" ]
ln -sfn "$base/$release" "$link"

actual=$(readlink "$link")
[ "$actual" = "$base/$release" ]
printf 'RELEASE_TARGET=%s\n' "$actual"

O chamador pode exigir uma linha de saída exata:

expected="RELEASE_TARGET=/srv/example/releases/2025.06.14"
output=$(ssh deploy@web-01 '/usr/local/sbin/activate-release 2025.06.14' 2>&1)
status=$?

if [ "$status" -ne 0 ]; then
  printf 'remote command failed, status=%s\n%s\n' "$status" "$output" >&2
  exit "$status"
fi

if ! printf '%s\n' "$output" | grep -Fxq "$expected"; then
  printf 'remote command returned unexpected output:\n%s\n' "$output" >&2
  exit 1
fi

grep -Fxq é importante aqui. Ele verifica uma linha fixa completa. Uma expressão genérica como grep deployed aceita lixo, correspondências parciais e logs de progresso enganosos. Se a saída tiver campos dinâmicos, analise um documento estruturado com um analisador adequado, em vez de tentar fazer uma expressão regular entender dados aninhados.

Não transforme a correspondência de saída em uma segunda cópia do comando inteiro. Ela deve responder a uma única pergunta: o programa remoto informou que alcançou o estado identificado? A leitura posterior responde se essa afirmação continua verdadeira onde realmente importa.

As escritas remotas precisam de uma leitura feita pelo proprietário do estado

Uma leitura posterior oferece a confirmação mais forte porque consulta o componente que mantém o estado alterado. A leitura correta depende da alteração e muitas vezes é diferente do comando que a realizou.

Para uma troca de link simbólico, readlink verifica o sistema de arquivos. Para a instalação de um pacote, consulte a versão instalada. Para reiniciar um serviço, peça ao gerenciador de serviços o estado ativo e depois faça uma solicitação ao serviço. Para uma alteração no banco de dados, leia a linha ou use o endpoint de status oferecido pela aplicação. Para uma tarefa em fila, consulte o registro da tarefa até que ela chegue a um estado final.

O padrão problemático costuma ser este:

ssh deploy@web-01 'deploy-release 2025.06.14 && systemctl restart example'

Ele pode retornar zero enquanto o serviço está «ativo», mas aponta para uma versão antiga porque o script de implantação escreveu em outro caminho. Também pode retornar zero quando o gerenciador de serviços aceita o reinício, mas o processo morre alguns instantes depois. O shell viu dois programas retornarem zero. Os usuários continuam com um serviço quebrado.

Use uma leitura que torne a condição desejada observável. Uma verificação de implantação poderia ser assim:

ssh deploy@web-01 '
  test "$(readlink /srv/example/current)" = /srv/example/releases/2025.06.14 &&
  systemctl is-active --quiet example &&
  curl --fail --silent --show-error http://127.0.0.1:8080/healthz
'

Isso é melhor que o primeiro comando porque verifica três afirmações que o comando original apenas pressupunha. Ainda assim, não prova que todos os usuários externos conseguem chegar ao serviço. Se a alteração afetar um endpoint público, faça uma verificação adequada a partir da posição de rede que os usuários realmente ocupam. Verificações locais via loopback detectam uma falha do processo, mas não um problema de firewall ou balanceador de carga.

As verificações de leitura após escrita devem usar um caminho independente sempre que possível. Executar uma segunda função dentro do mesmo script de implantação é melhor que nada, mas ela pode herdar a mesma variável equivocada, o host errado ou uma dependência simulada. Um comando separado que consulte o sistema de arquivos, o gerenciador de serviços, a API ou o banco de dados reduz esse modo de falha compartilhado.

Um comando precisa informar se é síncrono

Muitos comandos remotos retornam zero porque aceitaram um trabalho, não porque o trabalho terminou. Esse é um resultado correto para o envio de uma tarefa para uma fila, um trabalho em segundo plano, uma solicitação de recarga de serviço ou uma API de orquestração. Ele vira um problema quando o chamador trata a aceitação como conclusão.

Separe os dois contratos nos nomes e na saída. submit-backup pode informar um ID de trabalho e retornar zero quando o servidor aceitar a solicitação. wait-backup pode retornar zero somente depois que esse trabalho específico informar que terminou. Não chame as duas ações de backup esperando que o operador se lembre de qual versão é executada em cada host.

Um script remoto que inicia trabalho em segundo plano exige cuidado especial. Esta linha de shell retorna sucesso depois de iniciar um processo, mesmo que esse processo falhe imediatamente:

long-task >/var/log/long-task.log 2>&1 &
printf 'started\n'

O código de saída posterior do processo não fica disponível para o chamador SSH. Grave esse resultado em algum lugar persistente e consulte-o depois, ou mantenha a sessão aberta até que a tarefa alcance um estado relevante. Se você separar a tarefa, grave um ID de operação em um arquivo ou banco de dados e retorne esse ID. O chamador poderá então consultar ou aguardar o registro da operação.

Um registro de conclusão útil tem informações suficientes para reconciliar o trabalho:

operation=4f2c1a status=accepted release=2025.06.14

O chamador não deve aceitar esse registro como uma implantação concluída. Deve consultar operation=4f2c1a e exigir um status final como completed, além de verificar o estado produzido. Isso pode parecer excesso de cuidado para um script simples. É muito menos trabalhoso que tentar adivinhar se é seguro executar o script novamente depois de um timeout.

Os timeouts exigem a mesma distinção. Um timeout local informa que o chamador parou de esperar. Não informa que o comando remoto parou. A rede pode ter caído depois que o host remoto confirmou uma alteração. Antes de tentar novamente, inspecione o estado remoto ou consulte o ID da operação. Repetir a ativação de uma versão pode ser inofensivo se ela for idempotente. Repetir um pagamento, uma rotação de segredo ou o envio de um e-mail pode aumentar o dano.

A composição do shell esconde falhas se você não escrever pensando nelas

Retorne resultados, não credenciais
Deixe o Sallyport devolver os resultados dos comandos ao agente, enquanto os segredos de API e SSH permanecem no cofre criptografado.

A sintaxe do shell remoto pode transformar uma falha real em um status de saída final bem-sucedido. Essa é uma das formas mais antigas de uma execução SSH parecer limpa enquanto deixa uma máquina danificada.

Considere este comando:

ssh ops@db-01 'backup-db; upload-backup; prune-old-backups'

O shell remoto retorna o status de prune-old-backups, seu último comando. Se o backup falhar, mas a limpeza for bem-sucedida, o comando SSH geral retorna zero. A transcrição pode conter um erro perto do início, enquanto uma automação que lê apenas o status marca a execução como bem-sucedida.

Use set -e em um script que você controla ou conecte comandos dependentes com && quando uma linha compacta continuar legível:

ssh ops@db-01 'backup-db && upload-backup && prune-old-backups'

set -e não é mágico. Os shells têm exceções em condicionais, substituições de comandos e algumas construções compostas. Não escreva uma linha remota longa supondo que uma única opção tornará toda falha visível. Coloque o trabalho mais complexo em um script remoto, dê a cada operação uma condição clara de sucesso e teste seu comportamento em caso de falha.

Os pipelines introduzem outra armadilha conhecida. Em muitos shells POSIX, isto tem sucesso se o programa final tiver sucesso, mesmo que um produtor anterior falhe:

collect-metrics | format-report > /var/tmp/report.txt

Alguns shells oferecem set -o pipefail, mas /bin/sh pode não oferecer. Se o ambiente remoto precisar de portabilidade POSIX, não use um pipeline como único limite de erro. Grave os dados intermediários em um arquivo temporário, verifique o status do produtor e só então consuma os dados. Outra opção é executar o script com um shell cujo comportamento de pipefail você exija explicitamente.

Evite também terminar um comando remoto com uma saída de diagnóstico que possa ter sucesso depois que o trabalho real falhar:

apply-config
printf 'finished\n'

Sem set -e ou uma verificação explícita, printf se torna o status de saída. Esse erro aparece em comandos escritos à mão durante incidentes porque alguém quer uma mensagem final amigável. Imprima a mensagem somente depois de verificar a condição ou deixe o comando que falhou encerrar o script.

Erros de aspas podem fazer você verificar a máquina errada

Um shell local expande variáveis sem aspas antes que o SSH envie o comando. Isso pode fazer um comando usar a versão errada, comparar a saída local em vez da remota ou expor valores na lista de processos e nos logs locais.

Isto está errado quando você quer que o host remoto avalie $release:

ssh deploy@web-01 "test \"$(readlink /srv/example/current)\" = \"$release\""

O shell local avalia $(readlink ...) antes de iniciar o SSH. Agora você comparou o /srv/example/current da sua estação de trabalho, se ele existir, com uma variável local e enviou o texto resultante ao shell remoto. O status de saída pode ser zero. A verificação não consultou o host de destino.

Mantenha o programa remoto entre aspas simples quando ele contiver sintaxe de shell que precisa ser executada remotamente. Passe valores não confiáveis ou dinâmicos como parâmetros posicionais, em vez de montar texto de shell. Por exemplo:

release='2025.06.14'
ssh deploy@web-01 'sh -s -- "$1"' sh "$release" <<'REMOTE'
set -eu
release=$1
target=$(readlink /srv/example/current)
[ "$target" = "/srv/example/releases/$release" ]
printf 'verified=%s\n' "$target"
REMOTE

O delimitador entre aspas do heredoc impede que o shell local expanda o corpo do script. O valor da versão viaja como um argumento do shell, onde o shell remoto pode colocá-lo entre aspas corretamente. Isso, por si só, não transforma uma entrada arbitrária em um nome de versão seguro. Valide os caracteres permitidos e os formatos esperados antes de usar valores controlados pelo usuário em caminhos, comandos ou consultas ao banco de dados.

Na automação, prefira um script remoto com parâmetros a uma string crescente de aspas aninhadas. Falhas de aspas são difíceis de identificar em uma revisão porque o comando pode parecer plausível à primeira vista. Elas ficam muito mais fáceis de diagnosticar quando os logs mostram a versão exata do script remoto, os valores de parâmetros que podem ser registrados com segurança e a própria saída do comando de verificação.

Um contrato SSH útil tem três resultados separados

Execute SSH sem expor a chave
Use o auxiliar sp-ssh integrado para executar comandos SSH sem expor as credenciais ao agente.

Trate toda ação SSH importante como um pequeno protocolo com campos separados para transporte, resultado do comando e estado observado. O chamador precisa dos três para decidir se pode prosseguir, tentar novamente ou pedir ajuda.

Esta função Bash demonstra o formato. Ela captura stderr junto com stdout para que uma operação malsucedida deixe evidências de diagnóstico, enquanto informa a falha de transporte SSH separadamente de uma resposta bem-sucedida inesperada.

run_remote_check() {
  local host=$1
  local expected=$2
  shift 2

  local output status
  output=$(ssh "$host" "$@" 2>&1)
  status=$?

  if [ "$status" -eq 255 ]; then
    printf 'ssh_transport=failed host=%s\n%s\n' "$host" "$output" >&2
    return 255
  fi

  if [ "$status" -ne 0 ]; then
    printf 'remote_command=failed host=%s status=%s\n%s\n' \
      "$host" "$status" "$output" >&2
    return "$status"
  fi

  if ! printf '%s\n' "$output" | grep -Fxq "$expected"; then
    printf 'remote_result=unexpected host=%s expected=%s\n%s\n' \
      "$host" "$expected" "$output" >&2
    return 1
  fi

  printf 'remote_result=confirmed host=%s\n' "$host"
}

Chame-a com um comando que emita somente um registro de contrato depois das próprias verificações internas:

run_remote_check \
  deploy@web-01 \
  'RELEASE_TARGET=/srv/example/releases/2025.06.14' \
  '/usr/local/sbin/activate-release 2025.06.14'

Depois, faça uma leitura posterior como uma ação separada. Mantenha-a separada nos logs e nos relatórios de status. Se a ativação for bem-sucedida, mas a verificação do serviço falhar, os operadores devem ver exatamente esse limite. Um único resultado opaco como «implantação falhou» obriga a equipe a executar os comandos novamente apenas para descobrir o que já aconteceu.

Para comandos que retornam dados estruturados, retorne um pequeno objeto JSON e analise-o com um analisador JSON. Não use grep contra JSON, a menos que a saída seja deliberadamente um marcador de uma linha e você não precise interpretar seus campos. A correspondência de strings contra JSON arbitrário quebra quando o espaçamento, a ordem ou o conteúdo escapado muda.

O contrato também deve identificar o objeto de destino. status=ok sozinho não diferencia a versão 2025.06.14 da versão de ontem. Inclua o identificador da implantação, o hostname quando relevante, a versão do objeto ou o ID da operação. Esse pequeno detalhe evita um falso positivo surpreendentemente comum: a verificação confirma que existe algo saudável, mas não que seja aquilo que a execução acabou de alterar.

A observabilidade precisa preservar o resultado da verificação

Um log que registra apenas o comando remoto e o status de saída deixa sem resposta a pergunta mais importante: o chamador observou de forma independente o estado pretendido? Registre a ação de verificação e seu resultado ao lado da alteração.

Para uma implantação, um registro útil pode incluir o host de destino, o identificador da versão, o status do SSH, o status do comando, o registro exato do contrato, o status do comando posterior e um resumo curto da verificação de saúde ou do estado. Não registre segredos, cabeçalhos de autorização completos ou argumentos privados apenas porque eles ajudam na depuração. Projete a interface do comando para que as evidências úteis possam ser armazenadas com segurança.

O Sallyport mantém um diário de atividades para ações individuais e um diário de sessões para execuções de agentes, ambos projetados a partir de um log de auditoria criptografado e encadeado por hash. Isso oferece ao operador um lugar para distinguir uma ação SSH que foi executada de uma ação de verificação que confirmou um resultado, em vez de tratar uma única chamada bem-sucedida como toda a história.

A evidência de adulteração ajuda a responder se uma ação registrada foi alterada posteriormente. Ela não transforma um contrato de comando fraco em prova de um bom resultado. sp audit verify pode verificar a cadeia do log offline sobre o texto cifrado, mas o desenho da ação ainda precisa de uma verificação explícita por leitura posterior.

Mantenha a verificação perto o suficiente da escrita para que outro ator não consiga substituir silenciosamente o estado desejado nesse intervalo. Nem sempre é possível eliminar disputas em um sistema compartilhado. Você pode reduzi-las usando IDs de versões imutáveis, IDs de operação, verificações de versão e APIs que aceitem atualizações condicionais. Se o estado puder mudar novamente, registre a versão ou o timestamp observado e faça a automação posterior verificá-lo antes de agir.

As novas tentativas precisam de reconciliação antes da repetição

Mantenha as ações de verificação separadas
Registre a alteração e a leitura de confirmação separadamente como ações individuais no diário de atividades.

Um timeout, uma conexão interrompida ou um trabalho de CI interrompido cria um resultado desconhecido. O host remoto pode ter concluído a alteração, ainda pode estar executando-a ou pode ter falhado no meio. O chamador não sabe disso apenas porque parou de receber saída.

Não resolva essa incerteza com uma nova tentativa automática. Primeiro, execute um comando de reconciliação somente leitura que classifique o estado remoto. Para uma ativação de versão, inspecione o link simbólico atual e a saúde do serviço. Para uma migração, consulte uma tabela de migrações. Para um recurso criado, consulte pelo ID de operação gerado pelo chamador. Para uma rotação de segredo, verifique qual versão os consumidores realmente usam antes de gerar outra.

Um bom comando de reconciliação retorna um conjunto pequeno de resultados explícitos:

  • completed: o estado de destino corresponde à operação solicitada.
  • running: a operação ainda controla um trabalho e o chamador deve aguardar.
  • absent: não há evidências da operação, portanto uma nova tentativa pode ser adequada.
  • conflict: existe um estado diferente e uma pessoa ou um controlador de nível superior precisa decidir.

Não force todo resultado a ser sucesso ou falha. running e conflict são resultados úteis. Um sistema que os chama de falhas costuma repetir um trabalho que deveria ter sido deixado como está.

A idempotência reduz o custo de novas tentativas legítimas, mas o termo é usado de forma incorreta. Um comando é idempotente quando repetir a mesma solicitação deixa o estado desejado inalterado depois da primeira aplicação bem-sucedida. ln -sfn pode ser idempotente para um destino específico de link. «Criar um novo backup com a hora atual» não é. «Enviar um e-mail» também não é, a menos que o sistema de destino elimine duplicatas usando um identificador de mensagem estável.

Crie a verificação por leitura posterior antes de adicionar novas tentativas. Se você não consegue explicar como identificar uma operação concluída, não pode automatizar com segurança a repetição depois de uma incerteza. É nesse ponto que a automação SSH deixa de ser apenas uma conveniência do shell e passa a precisar de um modelo de operações.

O primeiro reparo é auditar suas execuções que aparecem como bem-sucedidas

Comece pelos comandos remotos que alteram o estado de produção e atualmente informam apenas um código de saída positivo. Para cada um, anote o objeto remoto que precisa mudar, o estado exato que conta como sucesso, o componente que consegue ler esse estado e o que permanece desconhecido depois de um timeout.

Depois, altere a interface do comando para que ela emita um registro de resultado exato depois das próprias verificações. Adicione uma ação separada de leitura posterior. Preserve os dois resultados no registro da execução. Você encontrará comandos que nunca foram síncronos, scripts cujo printf final escondeu uma falha anterior e verificações de implantação que consultaram a máquina que iniciou o comando, em vez da máquina que o recebeu.

Um status de saída zero ainda tem seu lugar. Ele é o primeiro portão, não o veredito final. Tratá-lo dessa forma produz uma automação capaz de explicar o que aconteceu quando o sinal verde aparentemente simples se revela errado.

FAQ

O código de saída 0 significa que um comando SSH funcionou?

Não. Um status de saída zero diz que o processo remoto informou sucesso ao shell. Isso não prova que o processo alterou o recurso correto, alcançou todas as dependências ou deixou o sistema remoto no estado desejado.

Qual código de saída o SSH retorna quando o comando remoto falha?

O SSH normalmente retorna o status de saída do comando remoto. O manual do ssh(1) também reserva 255 para erros do próprio SSH, como falhas de conexão ou autenticação. Portanto, trate 255 como uma falha de transporte ou do cliente, e não como um resultado da aplicação.

Quando devo validar a saída de um comando SSH?

Use verificações de saída quando o comando tiver um resultado estável e legível por máquina que forneça evidências além do código de retorno. Não procure frases vagas como «concluído». Emita um marcador explícito ou um registro estruturado que identifique o objeto e o estado esperados.

Por que devo fazer uma leitura posterior depois de uma alteração remota?

Uma leitura posterior é a verificação correta quando o comando altera um estado persistente, como uma versão implantada, o estado de um serviço, uma linha no banco de dados, o conteúdo de um arquivo ou uma configuração remota. Consulte o sistema que mantém esse estado depois da alteração, em vez de confiar na própria descrição do comando de escrita.

Como diferencio um erro de transporte SSH de um erro da aplicação?

Verifique primeiro o status do próprio SSH antes de interpretar a saída. Se o ssh retornar 255, informe uma falha de conexão, verificação do host, autenticação ou do cliente. Não a classifique como uma implantação ou um comando remoto malsucedido.

Posso repetir um comando SSH com segurança depois de um timeout?

Sim, desde que o comando remoto tenha sido projetado para ser seguro quando repetido. Use uma operação idempotente, confirme o estado final desejado e evite repetir comandos que criem pagamentos, enviem mensagens, façam rotação de segredos ou executem alterações não idempotentes no banco de dados, a menos que exista um identificador de operação e um caminho de reconciliação.

A saída do comando é mais confiável que um código de saída?

Um comando pode imprimir uma mensagem tranquilizadora antes de uma operação posterior falhar, ou informar sucesso apenas por ter colocado o trabalho em uma fila. Capture a saída para diagnóstico, mas exija um marcador exato, um valor estruturado ou uma leitura independente do estado antes de aceitá-la como evidência.

Devo colocar comandos SSH complexos em um script remoto?

Use um script remoto quando aspas, configuração do ambiente, limpeza ou várias verificações tornarem a linha única difícil de revisar. Um script pequeno com set -eu, saída explícita e comandos de verificação separados é mais seguro que um trecho de shell cheio de aspas aninhadas que ninguém consegue inspecionar corretamente durante um incidente.

Como agentes de IA devem validar ações SSH?

Sim. Um agente de programação pode confundir uma chamada SSH local bem-sucedida com uma prova de que a ação remota alcançou seu objetivo, especialmente quando não consegue inspecionar o estado final. Dê ao agente um contrato que exija código de retorno, resultado esperado e uma leitura posterior para alterações de estado.

Os logs de auditoria SSH provam que uma alteração remota foi bem-sucedida?

Os logs ajudam a reconstruir o que foi executado, mas não provam que um comando alcançou o efeito operacional ou de negócio desejado. Registre o comando, o host, o status, o resumo criptográfico da saída e o resultado da verificação para que um operador consiga identificar qual camada falhou.

Sallyport

O Sallyport executa chamadas de API e comandos SSH pelo seu agente de IA. As chaves ficam em um cofre local no seu Mac; você aprova cada execução e toda ação vai para um registro selado.

© 2026 Sallyport · Código aberto sob Apache-2.0 · Oleg Sotnikov