# RiLiGar Hoster — Master Implementation Guide (LLM-Ready) > Hospedagem de sites estáticos e SPAs sobre Cloudflare R2 + Workers. > Publique um diretório de build por CLI ou HTTP; o subdomínio, o HTTPS, > o fallback de SPA, as variáveis de ambiente e o cache já vêm resolvidos. > > Este arquivo é a documentação inteira do produto. Cole a URL no seu agente > (Claude Code, Cursor, Windsurf, Codex, Copilot) e peça a integração. > A seção 9 lista o que NÃO existe — leia antes de inventar API. Base da API: https://hoster.worker.myinfrastructure.click Domínio publicado: https://.hoster.myinfrastructure.click Servidor MCP: https://hoster.worker.myinfrastructure.click/mcp (seção 4) ## 0. Credenciais — leia antes de escrever código O Hoster tem uma credencial só: a chave `hk_…`. Mas há duas formas de chegar nela, e uma delas não pede chave nenhuma. ### Pelo MCP (preferencial para agentes) — nenhuma chave Se o seu host suporta Model Context Protocol, conecte **sem credencial no arquivo de config** e deixe o OAuth 2.1 resolver: o servidor responde 401, o cliente descobre o authorization server sozinho, o usuário loga no navegador e aprova os escopos. ```bash claude mcp add --transport http hoster https://hoster.worker.myinfrastructure.click/mcp ``` Não há id de projeto no endereço — diferente do Auth e do Storage, aqui o endpoint é único para a conta inteira. Ou seja: **para conectar o MCP você não precisa perguntar nada ao usuário**, só pedir que ele complete o login quando o navegador abrir. ⚠️ **Publicar pelo MCP não exige chave.** `hoster_deploy_ticket` devolve um comando pronto (`bunx @riligar/hoster-cli push ht_… --dir ./dist`); você o executa e os arquivos vão direto. Os bytes não passam pelo agente — nunca leia nem cole o conteúdo deles. A chave `hk_` só é necessária para usar a CLI por conta própria, fora do MCP. ### Pela CLI ou CI — aí sim, a chave `hk_` **Como pedir a chave ao usuário** — use este texto: > Preciso de uma chave de API do Hoster para publicar pela linha de comando. Ela está no seu > **dashboard**, em **Chaves de API**: crie uma, dê um nome (ex.: `deploy-local`) e copie o > valor — ele começa com `hk_`. > > **Copie na hora:** o valor completo aparece uma única vez. Depois disso, o painel só mostra > o prefixo. **Antes de pedir, avise do plano.** Gerenciar chaves de API é **recurso do plano pago**. Numa conta free o usuário vai encontrar, no lugar do gerenciador, um cartão com cadeado dizendo *"Gerenciamento de API é um recurso Pro"* e um botão **Ver planos**. Se isso acontecer: - **não insista** e não sugira contornar; - diga que gerenciar chaves para CLI/CI exige o plano Pro, e ofereça as alternativas reais: publicar pelo **MCP com OAuth** (que também envia arquivos, via `hoster_deploy_ticket`, e não pede chave) ou pelo **painel** (upload direto). ### Se o usuário ainda não tem conta, você se provisiona sozinho A `hk_` nasce de uma sessão autenticada — mas **você consegue essa sessão sem navegador**, pelo device flow do RiLiGar Auth. A pessoa lê um código de 8 caracteres no e-mail, informa a você, e daí em diante o percurso é seu: ```bash # 1. sessão (uma interação humana: o código do e-mail) POST https://auth.worker.myinfrastructure.click/auth/code/start {"email":"..."} POST https://auth.worker.myinfrastructure.click/auth/code/verify {"email":"...","code":"ABCD-EFGH"} → JWT # 2. o projeto — atenção: o campo é `projectId`, NÃO `name` POST https://hoster.worker.myinfrastructure.click/projects -H "authorization: Bearer " {"projectId":"meu-site"} → 200 {"success":true,"project":{...}} # 3. a chave de máquina (da CONTA, não do projeto) — aparece UMA vez POST https://hoster.worker.myinfrastructure.click/api-keys -H "authorization: Bearer " {"name":"agente"} → {"apiKey":{"key":"hk_..."}} ``` O percurso completo dos cinco produtos, e o que a RiLiGar é: https://myinfrastructure.click/llms.txt Se o usuário **já tem** a chave, peça — é mais rápido. Se ele não tem conta, use o caminho acima em vez de parar. O que você não pode é gerar código com `hk_xxx` de placeholder e dizer que terminou: sem credencial de verdade, **pare e peça**. Em CI, a chave vai por `--token ${{ secrets.HOSTER_TOKEN }}`, nunca por `hoster login` e nunca literal no YAML. ## 1. Modelo mental Um PROJETO é um subdomínio. `meu-site` responde em `https://meu-site.hoster.myinfrastructure.click`. Um DEPLOY substitui os arquivos daquele projeto. Não há histórico de versões, não há rollback, não há preview por branch: o deploy corrente é o que está no ar. Atualizar um projeto é reenviar os arquivos — o endereço não muda. Os arquivos vivem em R2 sob o prefixo `/`. Os metadados (dono, domínio próprio, variáveis, estratégia de cache, status de SSL) vivem em D1. Resolução de um request a `.hoster.myinfrastructure.click/`: 1. busca `/` no bucket; 2. se não achar, tenta `//index.html` (resolução de diretório); 3. se não achar e o path não tiver extensão (ou terminar em `.html`/`.htm`), serve o `mainFile` do projeto — este é o fallback de SPA, e é o que faz `/qualquer/rota` do React Router responder 200 em vez de 404; 4. se ainda não achar, 404. Requisições com extensão (`.js`, `.css`, `.png`) NÃO caem no fallback: um asset ausente devolve 404 de verdade, como deve ser. ## 2. CLI Pacote: `@riligar/hoster-cli` (npm, público). Roda em bun ou node. ```bash # sem instalar bunx @riligar/hoster-cli --help # ou global bun install -g @riligar/hoster-cli npm install -g @riligar/hoster-cli ``` Dois comandos. São dois mesmo. ### login ```bash hoster login hk_... ``` Grava a chave em `~/.hoster-config.json`. A chave sai do painel, em **"Chaves de API"** — **recurso de plano pago**: numa conta free o item abre um cadeado com "Ver planos" em vez do gerenciador. Ver §0 para o texto do pedido e o que fazer nesse caso. ### deploy ```bash hoster deploy --project meu-site --dir ./dist ``` | Opção | Default | O que faz | |---|---|---| | `-p, --project ` | nome da pasta atual | o subdomínio | | `-d, --dir ` | `.` | diretório publicado | | `-m, --main ` | `index.html` | arquivo de entrada | | `-t, --token ` | `~/.hoster-config.json` | chave da API, para CI | ### CI No pipeline não há `login`: a chave vai por `--token`, lida de um secret. ```yaml - name: Deploy to Hoster run: bunx @riligar/hoster-cli deploy --project site-producao --dir ./dist --token ${{ secrets.HOSTER_TOKEN }} ``` ## 3. API HTTP Toda rota exige `Authorization: Bearer `, onde o token é o JWT da sessão (painel) OU uma chave de API `hk_...` (CLI/CI). A única exceção é `GET /check-project/:id`, que é pública. Um 401 significa sessão inválida ou expirada. O JWT da Auth expira em 7 dias e NÃO há endpoint de refresh: a resposta correta é reautenticar. ### Projetos ``` POST /projects { projectId, mainFile } → { success, project } GET /projects → { projects: [...] } GET /check-project/:id → { exists: boolean } (público) DELETE /projects/:id → remove projeto, arquivos, DNS e registros DELETE /projects/:id/files → limpa os arquivos antes de um reenvio ``` `POST /projects` é idempotente para o dono: chamar de novo com o mesmo `projectId` atualiza o `mainFile` e a data, e devolve o projeto existente. Se o projeto pertence a outro usuário, responde 403. Forma de um projeto: ```json { "id": "uuid", "projectId": "meu-site", "userId": "user_...", "mainFile": "index.html", "customDomain": "meusite.com", "customDomainStatus": "pending", "cacheStrategy": "balanced", "sslStatus": "active", "createdAt": 1700000000000, "updatedAt": 1700000000000 } ``` ### Upload ``` POST /upload multipart: file, deployId, path POST /deploy-zip multipart: file (.zip), deployId ``` `path` é a chave completa dentro do bucket, incluindo o prefixo do projeto: `meu-site/assets/app.js`. O painel e a CLI enviam arquivo a arquivo com concorrência 10; um `.zip` é descompactado no cliente antes de subir. A ordem de um reenvio é: `POST /projects` → `DELETE /projects/:id/files` → `POST /upload` para cada arquivo. Sem o DELETE, arquivos de builds antigos sobrevivem no bucket e continuam sendo servidos. ### Variáveis de ambiente ``` GET /projects/:id/variables → { variables: { CHAVE: "valor" } } POST /projects/:id/variables { variables: { CHAVE: "valor" } } ``` O POST substitui o conjunto inteiro — não faz merge. Para remover uma variável, reenvie o objeto sem ela. Toda resposta HTML recebe, injetado antes de ``: ```html ``` Leia com `window.ENV.API_URL`. A injeção acontece na borda, a cada request: mudar uma variável NÃO exige rebuild nem novo deploy — vale no próximo carregamento (respeitado o cache; ver seção 7). São valores públicos, visíveis no HTML servido. Nunca coloque segredo aqui. ### Domínio próprio ``` POST /projects/:id/domain { domain: "meusite.com" } DELETE /projects/:id/domain GET /projects/:id/domain/verify → { status, message } GET /projects/:id/ssl-status → { status } ``` Depois do POST, aponte no seu provedor de DNS: ``` tipo: CNAME nome: @ (ou o subdomínio, ex.: www) valor: proxy.hoster.myinfrastructure.click ``` `customDomainStatus` vai de `pending` a `active` quando o certificado é emitido. Propagação de DNS costuma levar minutos, e pode levar até 24h. Um domínio já usado por outro projeto responde 409. ### Cache ``` GET /projects/:id/cache → { cacheStrategy } POST /projects/:id/cache { cacheStrategy } POST /projects/:id/purge-cache ``` Valores aceitos e o header que cada um produz: | cacheStrategy | Cache-Control servido | |---|---| | `development` | `no-cache, no-store, must-revalidate` | | `balanced` | `public, max-age=3600` | | `extreme` | `public, max-age=2592000` | | string livre | usada como o header, literalmente | A string livre aceita as diretivas padrão (`public`, `private`, `no-cache`, `no-store`, `must-revalidate`, `immutable`, `no-transform`, `proxy-revalidate`) e as com valor numérico (`max-age`, `s-maxage`, `stale-while-revalidate`, `stale-if-error`). `purge-cache` invalida a borda para o subdomínio e para o domínio próprio. Trocar a estratégia pelo painel já dispara um purge junto. ### Chaves de API ``` GET /api-keys → { apiKeys: [...] } POST /api-keys { name } → a chave em texto puro, UMA vez DELETE /api-keys/:id ``` O valor completo `hk_...` só aparece na resposta da criação. Depois disso, apenas o prefixo é legível. ## 4. Servidor MCP Endpoint: `https://hoster.worker.myinfrastructure.click/mcp` Revisão do protocolo: `2026-07-28` (stateless). Somente esta. O servidor expõe a gestão do Hoster como ferramentas de agente, publicação inclusa. Como o servidor é remoto e não alcança o seu disco, o deploy tem duas partes: `hoster_deploy_ticket` autoriza e devolve o comando; o comando envia os arquivos direto para o Hoster. Os bytes nunca entram na conversa — o que também significa que não são cobrados como contexto do usuário. 1. hoster_deploy_ticket { projectId: "meu-site", dir: "./dist" } 2. bunx @riligar/hoster-cli push ht_… --dir ./dist 3. hoster_deploy_status { projectId: "meu-site" } O ticket vale para um projeto, um uso, 15 minutos. Uma publicação que falhou no meio não o consome: repita o mesmo comando. ### Conectar ```bash claude mcp add --transport http hoster \ https://hoster.worker.myinfrastructure.click/mcp \ --header "Authorization: Bearer hk_..." ``` A chave é a mesma da CLI, gerada no painel em "Chaves de API". O servidor aceita DUAS credenciais: 1. **Chave de API** — `Authorization: Bearer hk_...` (ou `X-Hoster-Key`). É o caminho de quem cola a chave num arquivo de config ou roda em CI, sem navegador. Dá acesso total à conta. 2. **OAuth 2.1** — `Authorization: Bearer `, emitido pelo RiLiGar Auth. O cliente descobre o authorization server sozinho a partir do 401, faz login no navegador e recebe o token. Escopos: `auth:read` e `auth:write`. O 401 traz o desafio que inicia o fluxo OAuth: ``` WWW-Authenticate: Bearer resource_metadata="https://hoster.worker.myinfrastructure.click/.well-known/oauth-protected-resource", scope="auth:read" ``` Um token só-leitura (`auth:read`) recebe apenas as ferramentas de leitura — as de escrita não aparecem em `tools/list`. Tokens são validados por audiência: um token emitido para outro MCP da RiLiGar (Auth, Storage) é recusado aqui, ainda que carregue os mesmos escopos. É o `aud`, e não o nome do escopo, que separa um serviço do outro. ### Compatibilidade — leia antes de tentar Este servidor implementa APENAS a revisão `2026-07-28`, que removeu o handshake `initialize` e as sessões: cada request carrega versão, identidade e capabilities em `_meta`. Clientes que ainda falam a era do `initialize` (`2025-11-25` e anteriores — o que o SDK oficial declara hoje) NÃO conectam. Eles recebem um erro `-32022` nomeando a revisão falada aqui, em vez de falhar em silêncio. Não há fallback: a compatibilidade chega quando o cliente adotar a revisão nova. ### Ferramentas | Tool | O que faz | |---|---| | `hoster_list_projects` | lista os projetos da conta | | `hoster_get_project` | estado completo de um projeto, com variáveis | | `hoster_check_name` | verifica se um subdomínio está livre | | `hoster_deploy_ticket` | autoriza publicar um diretório e devolve o comando pronto | | `hoster_deploy_status` | diz se a publicação entrou no ar | | `hoster_set_variables` | substitui as variáveis (não faz merge) | | `hoster_set_cache` | troca a estratégia de cache | | `hoster_purge_cache` | invalida a borda | | `hoster_set_domain` | aponta domínio próprio e devolve o CNAME | | `hoster_check_domain` | status de DNS e SSL | | `hoster_list_deployments` | histórico de versões publicadas, e qual está no ar | | `hoster_rollback` | volta o site para uma versão anterior, sem republicar | | `hoster_delete_project` | apaga projeto, arquivos e DNS | `hoster_delete_project` exige `confirm: true` — uma chamada sem isso é recusada. As ferramentas destrutivas são anotadas como tal no protocolo, então o cliente pede confirmação ao usuário antes de executar. ### Resources e prompts ``` hoster://docs/llms.txt este documento hoster://projects/{projectId} o estado de um projeto, em JSON ``` Um prompt: `deploy_static_site`, que recebe `dir` e opcionalmente `projectId` e produz o roteiro verificar → publicar → confirmar. ### Chamada direta (sem cliente MCP) Todo request é um POST com `_meta` obrigatório e os headers espelhando o corpo: ```bash curl -X POST https://hoster.worker.myinfrastructure.click/mcp \ -H "Authorization: Bearer hk_..." \ -H "Content-Type: application/json" \ -H "MCP-Protocol-Version: 2026-07-28" \ -H "Mcp-Method: tools/call" \ -H "Mcp-Name: hoster_list_projects" \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "hoster_list_projects", "arguments": {}, "_meta": { "io.modelcontextprotocol/protocolVersion": "2026-07-28", "io.modelcontextprotocol/clientInfo": { "name": "meu-agente", "version": "1.0.0" }, "io.modelcontextprotocol/clientCapabilities": {} } } }' ``` Os headers `Mcp-Method` e `Mcp-Name` PRECISAM bater com o corpo: divergência é recusada com `-32020`. GET e DELETE no endpoint respondem 405 — esta revisão não tem stream por GET nem sessão para encerrar. Segurança: uma chave `hk_` dá acesso total à conta, incluindo apagar projetos. Para acesso restrito, use OAuth com escopo `auth:read` — chave de API com escopo não existe hoje. ## 5. Integração em minutos ```bash # 1. build do seu projeto bun run build # 2. publique bunx @riligar/hoster-cli login hk_... bunx @riligar/hoster-cli deploy --project meu-site --dir ./dist # 3. está no ar open https://meu-site.hoster.myinfrastructure.click ``` Para uma SPA (React Router, Vue Router), nada além disso: o fallback da seção 1 já responde as rotas do cliente. ## 6. Receita: SPA com variáveis por ambiente Não use `import.meta.env` para o que muda entre ambientes — isso congela o valor no build. Leia de `window.ENV`, e troque pelo painel ou pela API sem rebuildar: ```js const API_URL = window.ENV?.API_URL ?? 'http://localhost:3000' ``` Com fallback, o mesmo código roda em dev (sem injeção) e em produção. ## 7. Armadilhas conhecidas - **Publiquei e vejo o site antigo.** Estratégia `extreme` trava 30 dias na borda. Rode um purge, ou use `balanced` enquanto itera. - **Mudei uma variável e nada aconteceu.** O HTML veio do cache. Purgue, ou use `development` durante o desenvolvimento. - **Rota da SPA dá 404.** O path tem extensão (`/sobre.json`) ou o `mainFile` do projeto não é o `index.html` real do build. - **Assets 404 depois do deploy.** O `--dir` apontou para a raiz do repositório em vez do diretório de build. - **Arquivo velho continua no ar.** O reenvio pulou `DELETE /projects/:id/files`. - **Domínio próprio em `pending` há horas.** O CNAME não está resolvendo para `proxy.hoster.myinfrastructure.click`. Confira em whatsmydns.net. ## 8. Checklist para IA Antes de gerar código para o Hoster, confirme: - [ ] Você tem a credencial certa para a via escolhida (§0): MCP conecta por OAuth, sem chave; CLI e CI exigem `hk_`, que é de plano pago. Se falta, pare e peça — não use placeholder. - [ ] O deploy envia um DIRETÓRIO DE BUILD, não o código-fonte. - [ ] `--project` é o subdomínio e define a URL pública. - [ ] Variáveis de ambiente são lidas de `window.ENV`, nunca de `process.env` no cliente. - [ ] Nenhum segredo vai em variável de ambiente: elas são públicas. - [ ] Em CI, autentique por `--token`, nunca por `hoster login`. - [ ] Reenvio limpa os arquivos antes de subir os novos. - [ ] Não existe rollback: para voltar, republique o build anterior. - [ ] Pelo MCP, publicar é obter o comando e rodá-lo: o servidor não sobe arquivos. ## 9. O que NÃO existe Não invente estas APIs — elas não estão implementadas: - Build no servidor. O Hoster não roda `npm run build`; envie o build pronto. - Funções serverless, SSR, edge functions, rotas de API. É estático puro. - Histórico de deploys, rollback, deploy preview por branch ou por PR. - Integração automática com Git (push-to-deploy). O gatilho é a CLI, no seu CI. - Redirects, rewrites ou headers customizados por arquivo de configuração (não há `_redirects`, `_headers`, `vercel.json` ou equivalente). O único header configurável é o `Cache-Control`, e ele vale para o projeto inteiro. - Analytics, logs de acesso ou métricas de tráfego por projeto. - Webhooks, notificações ou eventos de deploy. - Upload de arquivos pelo servidor MCP: ele é remoto e não lê o seu disco. - Chave de API com escopo restrito. Toda chave `hk_` é total; para leitura apenas, use um token OAuth com escopo `auth:read`. - Suporte às revisões do MCP anteriores a 2026-07-28 (era do `initialize`). - Múltiplos ambientes (staging/produção) dentro de um mesmo projeto: cada ambiente é um projeto, com seu próprio subdomínio. - Wildcard de domínio (`*.meusite.com`). - Segredos criptografados, separados das variáveis públicas. ## 10. Design system O painel segue o RiLiGar Zen: raio 0, sem sombra em superfície (só em overlay), borda de 1px `gray.2` como único filete, hierarquia por tamanho e peso — nunca por cor. A rampa é cinza; `red.6` é a única exceção cromática, reservada a erro. Referência: https://myinfrastructure.click/design-system