---
name: sshp-setup
description: Dia Zero do SSHepherd. Conduz uma pessoa sem experiência de servidor desde a escolha do provedor (DigitalOcean, Hostinger, Contabo, Hetzner) até o SSH validado com usuário nao-root, root desligado e firewall ativo. Aceita prints de tela do painel. Use com "/sshp-setup", "não tenho servidor", "acabei de contratar um vps", "não consigo entrar no servidor".
license: MIT
compatibility: Claude Code, Codex, Cursor, Gemini CLI e demais agentes compativeis com Agent Skills.
metadata:
  author: sandeco
  version: "0.0.10"
  framework: sshepherd
  role: onboarding
  referencia_paineis: "2026-08"
---

Você conduz o Dia Zero. Esta é a parte onde a maioria das pessoas desiste, e a sua única meta é que essa não desista.

No fim deste fluxo, cinco coisas precisam ser verdade, e todas comprovadas por comando, não por achismo:

1. o framework entra no servidor por chave, como usuário que não e root
2. login de root pela rede está desligado
3. login por senha está desligado
4. firewall ativo, negando entrada por padrão
5. atualizações automáticas de segurança ligadas

## Máquina de estados

O estágio fica em `.sshepherd/state.json`, chave `onboarding.stage`. Grave a cada transição: a pessoa pode fechar o chat e voltar amanha.

```
nao-iniciado → provedor-escolhido → maquina-criada → chave-instalada
             → acesso-validado → usuario-criado → servidor-endurecido
             → regras-de-envio → concluído
```

Ao iniciar, leia o estágio e retome dali. Nunca refaça passo concluído.

## Regra de ouro sobre painéis

Os painéis dos provedores mudam sem aviso. Você descreve **objetivos**, nunca posição de botao:

- Certo: "procure a secao de chaves SSH; costuma ficar em Settings ou Security"
- Errado: "clique no botao azul no canto superior direito"

E toda etapa termina em **verificação por comando**, nunca em "parece que deu certo".

Se você não souber a tela de um provedor, diga que não sabe e descreva o objetivo. Instrucao errada com tom de autoridade é pior do que não ajudar.

## Prints de tela

Ofereca desde o começo:

> Se você se perder em qualquer tela, tira um print e cola aqui. Eu te digo onde você esta é o que clicar.

Ao receber um print:

1. Identifique o provedor é a tela.
2. Diga onde a pessoa esta, com suas palavras.
3. Diga a próxima ação concreta.

Se você não reconhecer a tela, **fale isso**: "Não reconheci essa tela. Você está em qual provedor? Me descreve o título da página." Nunca chute a próxima ação.

Se a engine em uso não le imagem, siga pelo roteiro textual sem interromper o fluxo. Nenhum passo do Dia Zero depende de imagem.

---

## Estágio 1, escolher o provedor

Se a pessoa já tem servidor, pule para o estágio 3.

Pergunte uma coisa so: que tipo de aplicação ela vai subir e se tem banco de dados junto. Com isso você recomenda:

| Aplicação | Recomendacao mínima |
|---|---|
| Site estático, landing, portfolio | 1 vCPU, 1 GB de memória |
| API ou app pequeno, sem banco local | 1 vCPU, 2 GB |
| App com banco de dados na mesma máquina | 2 vCPU, 4 GB |

Provedores que você conhece em detalhe:

**DigitalOcean** — painel mais limpo, documentação boa, cerca de 6 a 12 dolares por mes. Melhor escolha para quem nunca fez isso. A máquina se chama "Droplet".

**Hostinger** — popular no Brasil, painel em portugues, preco mais baixo. A máquina fica dentro do painel geral da conta, na secao de VPS.

**Contabo e Hetzner** — mais memória pelo mesmo preco, painel mais cru. Bom custo-beneficio, experiência pior para quem esta comecando.

Em todos, oriente:

- **Sistema operacional: Ubuntu 24.04 LTS.** Se não tiver, Ubuntu 22.04 ou Debian 12. Nada fora disso, porque os scripts do SSHepherd são testados nessas.
- **Região: a mais próxima de quem vai usar.** Brasil, ou leste dos Estados Unidos como segunda opção.
- **Autenticação: chave SSH, nunca senha.** Se o painel oferecer as duas, escolha chave. Você vai gerar a chave no próximo estágio.

Grave o provedor no estado e avance.

---

## Estágio 2, gerar a chave ANTES de criar a máquina

Faça isso antes de a pessoa clicar em criar, porque a maioria dos painéis pede a chave pública no momento da criação. Assim você evita o caminho de senha por completo.

Rode:

```
bash .sshepherd/scripts/sh/keygen.sh --name <nome-do-projeto>
```

O script mostra a chave pública. Explique em uma frase:

> Isso gerou um par de chaves. A privada fica no seu computador e nunca sai daqui, nem eu abro. A pública é essa que aparece acima: e ela que você cola no painel. Ela pode ser vista por qualquer um sem problema, é a fechadura, não a chave.

Peça para a pessoa copiar o bloco inteiro, incluindo o `ssh-ed25519` do início é o comentario do fim.

No painel, o objetivo e: **encontrar a secao de chaves SSH e adicionar uma nova chave**, colando esse texto. Nomes comuns dessa secao: "SSH Keys", "Chaves SSH", "Security", "Access".

Grave `key_path` no estado.

---

## Estágio 3, criar a máquina

Acompanhe a criação. Os pontos onde as pessoas erram, em ordem de frequência:

1. **Escolher a imagem errada.** Precisa ser Ubuntu ou Debian limpo, não "aplicação pre-instalada", não painel de controle.
2. **Não marcar a chave SSH.** Muitos painéis mostram a chave adicionada mas não a selecionam por padrão. Peça para conferir se a chave esta marcada antes de finalizar.
3. **Escolher plano grande demais.** Comece pequeno; da para aumentar depois, e diminuir costuma não dar.

Quando a máquina ficar pronta, peça o endereco de rede (o IP público). Grave em `onboarding.host`.

Se a pessoa criou com senha em vez de chave, tudo bem: o próximo estágio instala a chave usando a senha uma única vez.

---

## Estágio 4, primeiro acesso e validação

Nunca declare "funcionou" sem rodar:

```
bash .sshepherd/scripts/sh/validate-access.sh --host <ip> --user root --key <caminho-da-chave>
```

O script retorna `access=ok` ou `access=falhou` com a causa provável. Leia a causa e aja:

- **chave não instalada** — a chave pública não chegou no servidor. Se o painel oferecer console pela web, use ele para colar a chave em `~/.ssh/authorized_keys`. Se a máquina tem senha, use `ssh-copy-id`.
- **servidor inacessível** — pode estar ainda ligando. Espere um minuto e tente de novo. Se persistir, confira no painel se a máquina está ligada.
- **identidade do servidor mudou** — a máquina foi recriada com o mesmo endereco. Explique e remova a entrada antiga de `~/.ssh/known_hosts`, **só depois de confirmar com a pessoa**.

Alguns provedores entregam a máquina com usuário administrativo diferente de `root` (`ubuntu`, `admin`, `debian`). O script mostra qual usuário respondeu; use esse.

Grave `os_family`, `os_version` e `admin_user`. Avance só com `access=ok`.

---

## Estágio 5, criar o usuário de operação

Explique antes de fazer:

> Agora eu crio um usuário chamado `deploy` para operar o servidor no dia a dia. O root é o dono absoluto da máquina: qualquer erro com ele e irreversivel, e ele é o primeiro alvo de quem varre a internet procurando servidor aberto. Depois deste passo, o root não vai mais ser usado.

Rode:

```
bash .sshepherd/scripts/sh/create-ops-user.sh --host <ip> --admin <usuario-admin> --ops deploy --pubkey <caminho>.pub --key <caminho>
```

Em seguida, **valide o usuário novo em conexão separada**. Isso não é formalidade, é a trava que impede a pessoa de ficar trancada para fora:

```
bash .sshepherd/scripts/sh/validate-access.sh --host <ip> --user deploy --key <caminho-da-chave>
```

Só com `access=ok` grave `onboarding.access_validated = true`. Sem isso, o próximo estágio recusa rodar, e recusa de propósito.

---

## Estágio 6, endurecer

Diga o que vai acontecer, e por que, em uma linha cada:

> Vou fechar o servidor. Quatro coisas:
> - **desligar o login de root pela rede** — é o alvo número um de varredura automática
> - **desligar o login por senha** — senha pode ser quebrada por tentativa e erro, chave não
> - **ligar o firewall** negando tudo que não foi liberado — hoje qualquer porta aberta por engano fica exposta
> - **ligar atualizações automáticas de segurança** — falha conhecida sem correção é a porta de entrada mais fácil
>
> Se algo der errado, eu volto a configuração anterior sozinho. Sua sessão atual fica aberta o tempo todo justamente para isso.

Rode:

```
bash .sshepherd/scripts/sh/harden.sh --host <ip> --admin <admin> --ops deploy --key <chave> --validated
```

O script:
- recusa rodar se `--validated` não vier (e ele mesmo revalida por conta própria)
- libera a porta do SSH no firewall **antes** de ligar o firewall
- guarda copia do arquivo de configuração original com carimbo de data
- válida a sintaxe antes de aplicar
- usa `reload`, nunca `restart`, para não derrubar a sessão em uso
- confirma o acesso em conexão nova e **restaura sozinho** se falhar

Se ele retornar código 6, o acesso novo falhou e a configuração já foi restaurada. Não insista: investigue com a pessoa antes de tentar de novo.

---

## Estágio 7, verificação final

```
bash .sshepherd/scripts/sh/verify-day-zero.sh --host <ip> --user deploy --key <chave>
```

Cinco condições, resultado individual. **So declare o Dia Zero concluído com 5 de 5.** Quatro de cinco não e "quase la", e incompleto, e você diz isso.

Com as cinco, grave o perfil do servidor em `.sshepherd/servers/<id>.json`:

```json
{
  "id": "produção",
  "host": "<ip>",
  "port": 22,
  "user": "deploy",
  "key_path": "<caminho da chave privada>",
  "environment": "produção",
  "os_family": "ubuntu",
  "os_version": "24.04",
  "app_path": "/srv/app",
  "created_at": "<data ISO>",
  "day_zero": { "verified_at": "<data ISO>", "checks": 5 }
}
```

Atualize `state.json`: `onboarding.stage = "regras-de-envio"` e acrescente o id em `servers`.

---

## Estágio 8, decidir o que sobe para o servidor

O Dia Zero não termina no acesso. Termina quando está decidido **o que da sua pasta pode ir para o ar** — e isso se decide agora, com calma, não na pressa da primeira publicação.

O arquivo é o `.sshepherdignore`, na raiz do projeto. A instalação já criou ele com um padrão. Seu trabalho aqui é completar com o que só existe neste projeto.

Diga por que isso importa, em uma frase:

> A sua pasta tem coisa que é sua e não do servidor: especificação, prompt de agente, material interno, planilha de preço. Se eu mandar a pasta inteira, tudo isso vira arquivo público num endereço que qualquer um alcança. Então a gente decide agora o que vai.

### Como descobrir

1. **Liste a raiz do projeto.** Só o primeiro nível já mostra quase tudo que interessa.
2. **Leia o `.sshepherdignore` que já existe.** Ele cobre segredos, frameworks de IA, `.git`, dependências e lixo de build.
3. **Compare.** Toda pasta ou arquivo da raiz que não está coberto, você pergunta.

Classifique cada item da raiz em uma das três:

| O que é | Vai? |
|---|---|
| Código, configuração da aplicação, migração, `package.json`, assets do site | **sobe** |
| Segredo, chave, `.env`, dump de banco, credencial | **nunca sobe** — já está no piso de segurança do script |
| Especificação, prompt, plano de agente, material de aula, documentação interna, precificação | **não sobe** — acrescente ao arquivo |

Frameworks de IA são o caso mais comum, e o mais fácil de esquecer, porque a pessoa nem lembra que instalou. Procure por `.reversa/`, `_reversa_*/`, `.claude/`, `.agents/`, `.specify/`, `.bmad-core/`, `.cursor/`, e a própria `.sshepherd/`. Todos já vêm no padrão — confirme que nenhum ficou de fora.

### Pergunte item por item, sem jargão

Não mostre o arquivo cru e pergunte "está bom?". Pergunte pelo que você não tem como saber sozinho:

> Achei estas pastas na raiz que eu não reconheço:
>
> - `aula/` — tem um PDF e uns prints
> - `docs-internos/`
>
> Isso precisa estar no servidor para o site funcionar, ou é material seu de trabalho?

Uma pasta por vez, com o que tem dentro. A pessoa decide, você escreve a linha.

**Escreva sempre no bloco `--- Seu projeto ---`, no fim do arquivo.** Nunca no meio das seções que já vieram prontas. O motivo é prático: quando alguém abrir esse arquivo daqui a seis meses, tem que dar para ver de relance o que é padrão do SSHepherd e o que foi decisão desta pessoa neste projeto. Uma linha por item, com comentário curto dizendo por que:

```
# --- Seu projeto ---
/aula/                  # material de aula, PDF e prints
/docs-internos/         # documentacao que nao e do produto
```

### Feche mostrando os dois lados

Nunca mostre só o que fica de fora. Mostre o que **vai**, que é o que ela precisa reconhecer como "sim, é o meu site":

> Fica assim:
>
> **Sobe:** `src/`, `prisma/`, `public/`, `package.json`, `next.config.ts`
>
> **Não sobe:** `.env` e qualquer chave, `.git/`, `node_modules/` (reconstruído no servidor), `.reversa/`, `_reversa_sdd/`, `.claude/`, `.sshepherd/`, `aula/`
>
> Isso está gravado em `.sshepherdignore`. Dá para editar quando quiser, e toda publicação lê ele de novo.

Grave `onboarding.deploy_rules_reviewed = true` e `onboarding.stage = "concluído"`.

### Aviso que você não pode deixar de dar

Se o projeto tem repositório remoto e a publicação for pela estratégia `git`, **este arquivo não vale**: o servidor clona do repositório, então quem decide é o `.gitignore`. Confira se as mesmas pastas estão lá. É comum `.reversa/` e `_reversa_sdd/` estarem versionados de propósito — e aí eles chegam no servidor mesmo com o `.sshepherdignore` correto. Diga isso agora, não depois.

---

## Fechamento

> Pronto. Seu servidor está no ar e fechado:
> - você entra como `deploy`, com chave, sem senha
> - root não entra mais pela rede
> - firewall ativo
> - atualizações de segurança automáticas
> - está decidido o que da sua pasta sobe e o que fica aqui
> - o cofre de chaves da aplicação já existe no servidor, vazio, esperando o `/sshp-secrets`
>
> Guarde isto: sua chave está em `<caminho>`. Se você perder esse arquivo, perde o acesso ao servidor. Faça uma copia em lugar seguro.
>
> Próximo passo: `/sshp-provision`, para preparar o servidor para a sua aplicação.

---

## Quando algo dá errado

Não suavize. Mostre a saída do comando como o script entregou, diga a causa provável é o que fazer. Se você não souber, diga que não sabe e liste o que já foi descartado.

Se a pessoa ficar sem acesso ao servidor apesar de tudo, o caminho é o console pela web do provedor (nomes: "Console", "Recovery", "VNC", "Terminal"), onde da para entrar sem SSH. Explique que isso existe e como chegar la.
