sshepherd
v0.0.6
Published
Framework de agentes que leva um dev sem experiencia de sysadmin do zero absoluto ate uma aplicacao publicada em VPS proprio, com servidor endurecido, deploy versionado e rollback em um comando.
Downloads
920
Maintainers
Readme
SSHepherd
Do "não tenho servidor" ao "publiquei e sei voltar atrás", sem virar sysadmin.
SSHepherd é um framework de agentes que instala na pasta da sua aplicação e cuida do seu VPS: escolha do provedor, criação da máquina, chave SSH, endurecimento, provisionamento, deploy versionado, rollback e diagnóstico.
Ele foi feito para uma pessoa específica: quem sabe programar e não sabe administrar servidor.
Instalação
cd sua-aplicacao
npx sshepherd installDepois, abra seu agente (Claude Code, Codex ou compatível) e digite:
/sshpSe você ainda não tem servidor, ele começa pelo Dia Zero e te acompanha até o SSH funcionar.
Requisitos: Node 18.20.2 ou superior, cliente OpenSSH (nativo em Windows 10+, macOS e Linux). O alvo remoto precisa ser Ubuntu 22.04/24.04 ou Debian 11/12.
Comandos do instalador
npx sshepherd install # instala nesta pasta
npx sshepherd update # atualiza agentes e scripts, preservando seus dados
npx sshepherd status # estado da instalação, do Dia Zero e dos servidores
npx sshepherd uninstall # remove, com opção de preservar perfis e logsEngines suportadas
Claude Code, Codex, Cursor, Gemini CLI, Windsurf, Antigravity, Kiro, Opencode, Cline, Roo Code, GitHub Copilot, Aider e Amazon Q Developer. O instalador detecta quais estão presentes — pela pasta do projeto ou pelo binário no PATH — e já vem com elas marcadas.
Aviso de nova versão
A instalação registra um hook SessionStart no Claude Code que roda .sshepherd/bin/version-notice.js. Ele lê só o cache local, sai em ~130ms e nunca bloqueia a sessão; a checagem de rede acontece em processo destacado, valendo para a sessão seguinte. Quando há versão nova, o agente pergunta se pode atualizar — nunca atualiza sozinho. Nas engines sem hook, a mesma instrução vai no entry file.
O que ele faz
| Comando | O que faz |
|---|---|
| /sshp | Descobre em que ponto você está e roteia |
| /sshp-setup | Dia Zero: provedor → máquina → chave → usuário não-root → servidor fechado |
| /sshp-connect | Perfis de servidor, teste de conexão, histórico de auditoria |
| /sshp-harden | Auditoria de segurança com nota 0–100 e correções |
| /sshp-attack | Olha seu servidor do lado de quem ataca: o que responde e o que vaza |
| /sshp-provision | Detecta sua stack e prepara o servidor |
| /sshp-deploy | Publica, verifica saúde, volta versão |
| /sshp-doctor | Diagnóstico quando algo para de funcionar |
| /sshp-agents-help | Catálogo dos agentes |
O que sobe para o servidor
Sua pasta tem coisa que é sua e não do servidor: especificação, prompt de agente, material interno, planilha de preço. Mandar a pasta inteira transforma tudo isso em arquivo público.
Quem decide é o .sshepherdignore na raiz do projeto, criado na instalação, uma regra por linha no formato do rsync:
.env # segredo
.reversa/ # frameworks de IA: especificacao e prompt
_reversa_*/
.claude/
.sshepherd/
.git/ # historico carrega segredo ja removido
node_modules/ # reconstruido no servidor
/aula/ # seu: material internoTrês coisas:
- O Dia Zero termina aqui. O estágio 8 do
/sshp-setuplista sua raiz, compara com o arquivo e pergunta item por item o que ele não tem como saber —aula/é parte do site ou é seu material? Fecha mostrando os dois lados, o que vai e o que fica. - Existe um piso que você não derruba.
.env,.env.*,.git/,.sshepherd/,*.pem,*.key,id_rsa,id_ed25519são excluídos pelo script sempre, apagar a linha não adianta. Sem o arquivo, o deploy recusa publicar. - Na estratégia
gitele não vale. O servidor clona do repositório, então quem manda é o.gitignore. Se.reversa/está versionado, ele chega no servidor. Os agentes avisam antes.
O hacker do bem
/sshp-harden pergunta ao servidor como ele está configurado. /sshp-attack não pergunta nada: bate na porta e vê o que responde.
As duas coisas divergem mais do que parece. Firewall ativo e configurado certo, com o Postgres aberto para a internet do mesmo jeito — porque o Docker publica porta por cima do ufw e a regra do firewall não vale para ela. O harden dá nota alta. O attack mostra o banco exposto.
Três lentes:
- De fora, sem chave — que portas respondem pela internet. Banco de dados aberto é o achado mais comum e o mais grave; Redis e Mongo abrem sem senha por padrão.
- O que a aplicação entrega —
/.env,/.git/HEAD,docker-compose.yml, dump de banco, e a documentação do projeto servida como página. - De dentro — permissão do arquivo de segredos, material interno publicado junto com o código, socket do Docker acessível, sudo sem senha.
E o vetor que é novo: a varredura ficou barata. Mapear um alvo era trabalho de horas de gente com paciência; hoje roda sozinho contra milhares de endereços ao mesmo tempo. Por isso um _reversa_sdd/prd.md exposto deixou de ser inofensivo — ele descreve sua regra de negócio em texto escrito para ser entendido. Era papel, virou mapa.
A base de ameaças
O que procurar não é lista fixa — uma lista de dois anos atrás procura buraco que já foi tapado e ignora o que está sendo usado agora. A base fica em .sshepherd/threats.db, uma linha por ameaça, legível a olho nu:
caminho|/.mcp.json|critica|conexoes do seu assistente de IA|Guarda os tokens...|<fonte>|2026-08-06
porta|9000|alta|painel do Portainer|Quem entra ali manda na maquina.|<fonte>|2026-08-06O agente atualiza pesquisando na web, filtrando pela stack do projeto, e sempre mostra antes de gravar — com a fonte de cada item. Reinstalar não apaga o que ele aprendeu. Quando a base passa de 60 dias, o script avisa.
Só entra o que dá para detectar pedindo um endereço ou batendo numa porta. Nada que explore.
Os limites
Só examina servidor registrado em .sshepherd/servers/, e recusa qualquer outro endereço mostrando quais são os seus. Não explora, não tenta senha, não mede carga, não altera nada.
O pastor acha o buraco na cerca e chama o dono. Não atravessa, e não vai olhar a cerca do vizinho.
O Dia Zero
É a parte que os tutoriais não cobrem e onde a maioria desiste. O agente conduz do zero absoluto, aceita print de tela do painel do provedor para saber onde você está, e valida cada passo por comando — não por "parece que deu certo".
Termina quando cinco condições estão comprovadas:
- você entra por chave, como usuário que não é root
- login de root pela rede desligado
- login por senha desligado
- firewall ativo negando entrada
- atualizações automáticas de segurança ligadas
Provedores cobertos em detalhe: DigitalOcean, Hostinger, Contabo, Hetzner. Outros recebem o roteiro genérico.
Arquitetura
Três camadas, separadas de propósito:
npm (bin/, lib/) → só instala e atualiza. Não toca em servidor.
agents/ (markdown) → decidem, conversam, julgam contexto.
scripts (.sshepherd/) → executam no servidor. Determinísticos e auditáveis.O agente é bom em julgar contexto e ruim em garantir determinismo. O script é o oposto. Por isso nenhum comando remoto é improvisado pelo modelo: tudo passa por script versionado, que você pode ler antes de rodar.
As regras que o framework não quebra
- Nada muda sem você ver o comando. Toda operação que altera o servidor mostra o comando exato e o alvo antes, e espera confirmação. Leitura e diagnóstico rodam livres.
- Comandos destrutivos são recusados sempre. Apagar o disco, parar o SSH, resetar o firewall — não há confirmação que libere.
- Nenhum caminho de acesso é fechado antes de o novo estar provado. É o que impede você de ficar trancado para fora do próprio servidor.
- A chave privada nunca é lida. O framework passa o caminho ao cliente SSH, nunca o conteúdo.
- Tudo fica registrado. Cada comando remoto vai para
.sshepherd/logs/, com segredos redigidos — inclusive o que foi recusado ou bloqueado. - root é usado uma única vez. No primeiro acesso, para criar o usuário
deploy. Depois disso, nunca mais.
Deploy e rollback
Publicação em releases/<timestamp> com troca atômica do symlink current:
/srv/app/
├── releases/20260805143000/ ← nova
├── releases/20260805120000/ ← anterior, intacta
├── shared/.env ← sobrevive a todas as versões
└── current -> releases/20260805143000Depois de publicar, o framework bate na porta da aplicação até 6 vezes. Se não responder, mostra o log e oferece o rollback como primeira ação. Voltar leva segundos, porque o código antigo já está no servidor.
O que ele não faz
- Não cria a VPS pela API do provedor — acompanha você no painel
- Não registra domínio
- Não administra banco de dados (backup, migração, tuning)
- Não roda migração de esquema, e o rollback não desfaz banco
- Não deixa processo rodando no servidor
- Não orquestra múltiplos servidores, nem Kubernetes
Estrutura instalada
seu-projeto/
├── .sshepherdignore o que NAO sobe para o servidor (editavel)
├── .sshepherd/
│ ├── state.json estado e estágio do onboarding
│ ├── config.toml preferências (editável)
│ ├── servers/<id>.json um arquivo por servidor
│ ├── logs/audit-*.jsonl histórico, um evento por linha
│ ├── scripts/sh|ps/ os scripts que executam de fato
│ └── bin/version-notice.js aviso de nova versão (Node puro)
├── .claude/skills/sshp*/
└── .agents/skills/sshp*/servers/ e logs/ já vêm no .gitignore local: contêm endereço de servidor e histórico operacional.
Desenvolvimento
npm test # 47 testes, executor nativo do node, sem framework de teste
npm run bump:check # confere se package.json e os 9 agentes estão na mesma versão
npm run bump # 0.0.2 -> 0.0.3, atualiza os 10 arquivos de uma vezOs testes cobrem o detector das 13 engines, a estrutura instalada, preservação de dados em reinstalação, estado corrompido, o hook de sessão (criação, merge, não-duplicação, settings inválido), o manifesto SHA-256, created_files, o .sshepherdignore (criação, não-sobrescrita, recusa do deploy sem ele), os guard rails e a ausência de CRLF nos scripts.
Versionamento
Só o último número muda. 0.0.1 → 0.0.2 → 0.0.3 → ...
Sem major, sem minor. A versão aparece em package.json e no metadata de cada um dos 9 agentes — npm run bump mantém os dez em sincronia, e npm run bump:check acusa se algum ficou para trás.
Fluxo de release:
npm run bump # incrementa
# registre a mudança no CHANGELOG.md
git commit -am "v0.0.2: <o que mudou>"
git tag v0.0.2 && git push --follow-tagsStatus
⚠️ Versão 0.0.2 — nunca executada contra um VPS real.
Validado: instalador, comandos, hook de versão, guard rails (bloqueio incondicional, redação de segredo, recusa sem confirmação, trava de alvo do attack), sintaxe e fim de linha dos scripts, 47 testes.
Do attack-surface.sh, a sondagem externa e a detecção de arquivo exposto foram exercitadas contra um servidor local montado para isso: portas, /.env, /.git/HEAD, listagem de diretório e banner de versão disparam corretamente. A parte que roda por SSH não foi validada contra máquina real.
Não validado: provision.sh, deploy.sh, rollback.sh, harden.sh e audit.sh contra um servidor de verdade. Use numa VPS descartável antes de apontar para produção. Os únicos que só leem, e portanto são seguros hoje, são o audit.sh e o attack-surface.sh.
Licença
MIT — sandeco
