---
name: sshp-provision
description: Prepara o servidor endurecido para receber a aplicação. Detecta a tecnologia do projeto lendo os arquivos da pasta, instala o runtime, cria a estrutura de publicação, configura o serviço que mantem a aplicação viva, o servidor web e o certificado. Use com "/sshp-provision", "preparar o servidor", "instalar node no vps", "configurar nginx", "colocar https".
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: provisionamento
---

Você prepara o servidor para a aplicação. Roda uma vez por servidor, entre o Dia Zero é o primeiro deploy.

## Pré-condição

O perfil precisa existir e ter passado pela verificação do Dia Zero. Se `day_zero` não consta no perfil, mande a pessoa para `/sshp-setup` antes. Provisionar servidor aberto e trabalhar para um invasor.

## Detectar a tecnologia

O SSHepherd fica instalado dentro da pasta da aplicação justamente para isso. Leia os arquivos e decida, declarando **a evidência**:

| Evidência encontrada | Tecnologia | Runtime |
|---|---|---|
| `docker-compose.yml` ou `compose.yaml` | compose | Docker é o plugin de composicao |
| `package.json` | node | Node, versão do campo `engines` quando houver, senao 20 |
| `requirements.txt`, `pyproject.toml`, `Pipfile` | python | Python 3 com ambiente virtual |
| `composer.json`, `index.php` | php | PHP com gerenciador de processos |
| `Cargo.toml` | rust | Rust, versão do `rust-version` do `Cargo.toml` quando houver, senao stable |
| so `index.html`, `dist/`, `build/` sem servidor | estático | nenhum |

Detalhes que mudam a decisão:

- `package.json` com `"scripts": {"build": ...}` significa etapa de construção no deploy. Anote o comando.
- `package.json` com `next`, `nuxt` ou `astro` nas dependencias: pode gerar saída estatica ou servidor, confira o script de build.
- Porta: procure `PORT` no código ou em `.env.example`. Sem achar, use a porta do `config.toml`.

**Duas tecnologias na mesma pasta?** Não decida sozinho. Mostre as duas com a evidência de cada e peça para a pessoa escolher.

## Apresentar o plano antes de executar

Nunca comece sem mostrar o plano inteiro:

> Detectei que sua aplicação e **Node**, pelo `package.json` na raiz.
>
> Meu plano para o servidor:
>
> | O que | Escolha | Por que |
> |---|---|---|
> | Runtime, o programa que executa seu código | Node 20 | versão com suporte longo; seu projeto não pediu outra |
> | Estrutura de pastas | `/srv/app`, com uma pasta por versão publicada | é o que faz voltar atrás levar segundos |
> | Serviço, o que mantém a aplicação ligada | systemd, o gerente do sistema | ela volta sozinha se cair, e volta quando o servidor reinicia |
> | Servidor web, quem atende o visitante | Caddy, nas portas 80 e 443 | resolve o cadeado do navegador sozinho e de graça |
> | Domínio | `<informado ou nenhum>` | |
>
> Alguma coisa errada aqui? Se sim, me corrija. Se não, eu sigo.

Use o glossário do agente `sshepherd` para todos esses termos. Explique na primeira vez, depois só a palavra.

## Executar

```
bash .sshepherd/scripts/sh/provision.sh \
  --host <ip> --user deploy --key <chave> \
  --stack <node|python|php|rust|estático|compose> \
  --app-path /srv/app --app-port 3000 \
  --web caddy [--domain exemplo.com] [--start-cmd "node server.js"]
```

Pode rodar de novo sem medo: ele não reinstala o que já existe. Se falhar no meio, o que já foi feito fica, e rodar de novo retoma do ponto que faltou.

## Estrutura de publicação

```
/srv/app/
├── releases/           uma pasta por versão publicada
│   ├── 20260805120000/
│   └── 20260805143000/
├── shared/             o que sobrevive a todas as versões
│   ├── .env            suas senhas e chaves, só o dono le (permissão 600)
│   └── logs/           o caderno onde a aplicação anota o que acontece
└── current -> releases/20260805143000    atalho que aponta para a versão no ar
```

Explique o atalho, porque é dele que sai a mágica de voltar atrás:

> Esse `current` é um atalho, como um atalho na área de trabalho. Ele aponta para a versão que está no ar. Publicar é montar a versão nova ao lado e mudar para onde o atalho aponta. Voltar atrás é apontar de volta — por isso leva segundos, e não um envio novo de arquivos.

## Onde ficam as senhas

O provisionamento instala o **cofre**: `/var/lib/sshepherd/secrets/app.env`, um arquivo que só root abre. A aplicação passa a rodar como um usuário próprio, `sshepherd-app`, sem senha e sem SSH, e recebe as variáveis do systemd, que lê o cofre antes de trocar de usuário. O usuário `deploy`, que é o seu, não lê o cofre: só chama um programa fechado que responde com nomes.

**Você nunca pede os valores das variaveis no chat.** Senha de banco, chave de API e token não passam pelo seu contexto. Cadastro, geração, aplicação e conferência são o `/sshp-secrets`. Mande para lá assim que o provisionamento terminar:

> Servidor pronto. Antes de publicar, as chaves da sua aplicação vão para o cofre: `/sshp-secrets`. Eu te mostro o que falta e te dou o comando para cadastrar no seu terminal; o valor não passa por mim.

O que o script faz com o que já existia:

- `shared/.env` de uma versão anterior é **migrado** para o cofre quando o cofre está vazio. Linha que não cabe no formato (nome minúsculo, valor com barra invertida) é listada por nome e o arquivo antigo **fica** até a pessoa cadastrar essas e apagar o arquivo ela mesma. Com o cofre já cheio, nada é misturado: a saída lista os nomes pendentes.
- Backups de `shared/` anteriores carregam o `.env` antigo. A saída lista esses arquivos (`backups_com_env_antigo=`). Diga para apagar, ou melhor, trocar as chaves que estavam neles.
- Serviço que já estava no ar é reiniciado para rodar com o usuário novo. Avise antes: são alguns segundos fora do ar.

**Compose fica fora do cofre.** A unidade de compose roda o YAML que foi publicado, e YAML de quem publica rodando como root seria root para quem publica. Em compose os segredos continuam em `shared/.env`, legíveis por `deploy`, e a pessoa edita esse arquivo no terminal dela. Diga isso com todas as letras: é um limite, não um detalhe.

Pastas que a aplicação escreve e que precisam sobreviver às versões (`storage/` do Laravel, `.next/cache`, `uploads/`) são declaradas no deploy com `--shared-dir`. O código em `releases/` é só leitura para a aplicação: anote isso para o `/sshp-deploy` quando detectar a stack.

## Domínio e certificado

Antes de tentar emitir certificado, o script verifica se o domínio aponta para o servidor. Se não aponta, ele **não tenta**, e isso é proposital: a autoridade certificadora limita tentativas, e falhar várias vezes bloqueia o domínio por horas.

Quando o domínio não aponta, oriente:

> No painel de onde você registrou seu domínio, procure a seção de **DNS** — é a agenda que diz para a internet qual servidor responde pelo seu nome. Crie ali um registro **do tipo A** apontando para `<ip>`. Se for o domínio sem nada na frente (`seusite.com`), o nome do registro é `@`.
>
> Depois de salvar, essa mudança leva de alguns minutos a algumas horas para chegar no mundo todo. Me avise e eu verifico de novo.

Com Caddy, o certificado sai sozinho no primeiro acesso pelo domínio. Com Nginx, o script usa certbot.

## Sem domínio

Funciona: a aplicação responde pelo endereco de rede, em HTTP, sem cadeado. Registre no perfil que o certificado ficou pendente e ofereça retomar quando houver domínio.

## Servidor web já instalado

Se a máquina veio com nginx, apache ou um painel de controle, o script **não sobrescreve** a configuração existente: cria arquivo próprio e identificavel. Avise a pessoa sobre o conflito e explique que pode haver disputa pela porta 80.

## Memória pequena

Servidor de 1 GB costuma morrer de falta de memória na hora de montar o projeto. Quando a memória é menor que 2 GB, o script cria um **arquivo de troca**: uma memória de emergência no disco, mais lenta, para o servidor não desistir no meio. Se ainda assim falhar, o caminho é montar o projeto na sua máquina e enviar pronto.

## Verificação final

Ao terminar, quatro coisas precisam ser verdade:

1. a aplicação fica ligada sozinha, e volta se o servidor reiniciar
2. ela responde por dentro da máquina
3. ela responde pela internet
4. o cadeado do navegador está válido, quando há domínio

Grave o resultado no perfil, em `provision`, e siga para `/sshp-deploy`.
