O que host0 faz?
O host0 hospeda tudo que seu agente criou — HTML, sites, aplicativos com banco de dados compartilhado, Markdown, PDFs — em um link que você pode compartilhar, tão facilmente quanto compartilhar um Google Doc. Funciona com qualquer agente.
A primitiva central é um aplicativo: publique qualquer arquivo ou pasta — um diretório de arquivos estáticos, ou um único arquivo que abre em um navegador (um PDF, uma imagem, uma única página HTML, vídeo, áudio, Markdown, CSV, texto; um arquivo ou tipo apenas para download é rejeitado — extraia um zip e publique a pasta) — e receba uma URL ativa em https://{slug}.host0.app/ (desenvolvimento local: http://{slug}.localhost:3000/). Todo aplicativo é privado por padrão — apenas o proprietário (logado no host0) e pessoas convidadas por email podem abri-lo. Abri-lo para qualquer pessoa com a URL (sem login; não listado e nunca indexado) é opcional: --visibility link. --visibility public vai mais longe — público na web, mecanismos de busca podem indexá-lo — e é apenas para coisas que o usuário deseja encontrar em pesquisas. A URL de um novo aplicativo é aleatória (k3x9q2m7ta.host0.app); renomeie-a para algo legível (team-todo.host0.app) quando o usuário quiser um link mais bonito — veja "Renomear a URL" abaixo.
Um aplicativo também pode conter dados compartilhados: declare coleções de registros em um h0.json e seu frontend recebe um banco de dados em sua própria origem, sem código de servidor. Veja "Dados compartilhados" abaixo — é isso que transforma uma página em uma ferramenta que uma equipe usa junta.
Documentação atual
A documentação completa está em https://host0.ai/docs — leia-a quando o usuário perguntar como algo funciona, o que é possível ou antes de informar que um recurso não é suportado.
A API também se descreve. O índice legível por máquina:
curl -sS {base}/api/v1
Ele retorna a lista atual de endpoints, limites de arquivo e o contrato da plataforma (o que o host0 oferece hoje). Envie a chave da API (-H "authorization: bearer $HOST0_API_KEY") e limits e plan são do próprio usuário — os limites dependem do plano da conta; sem uma chave você obtém os padrões para uma nova conta. GET {base}/api/v1/account adiciona o que a conta usou este mês. Se este texto de habilidade e o comportamento da API ao vivo discordarem, confie na API ao vivo.
Para instalar ou atualizar esta habilidade: npx skills add host0-ai/skills (qualquer agente que a CLI de skills suporte; fonte: https://github.com/host0-ai/skills), ou busque dois arquivos: https://host0.ai/skill.md → salve como SKILL.md, e https://host0.ai/publish.sh → salve como scripts/publish.sh ao lado, depois chmod +x scripts/publish.sh. A URL do script é /publish.sh — não há /scripts/ nela.
Requisitos
- Binários necessários:
curl,jq(o script termina com uma mensagem clara se algum estiver faltando) - Uma chave de API, de
$HOST0_API_KEYou~/.host0/credentials. Sem nenhuma,publish.shinicia um login de dispositivo para você — o usuário aprova um link + código em seu próprio navegador (veja "Obtendo uma chave de API") - Auxiliar incluído:
./scripts/publish.sh - Rede: HTTPS de saída para
host0.ai(a API) ehost0.app(uploads e os aplicativos ativos). Em um sandbox com uma lista de permissões de saída, se uma solicitação falhar comSSL_ERROR_SYSCALL, um erro de proxy ou uma conexão recusada/redefinida toda vez, informe ao usuário qual domínio permitir — não entre em loop. - Falhas transitórias acontecem: alguns sandboxes em nuvem veem redefinições de conexão ocasionais /
SSL_ERROR_SYSCALL/ timeouts de conexão ao atingirhost0.ai.publish.shtenta novamente esses por conta própria (você veráhost0: network hiccup … retrying 2/4no stderr) — apenas deixe-o executar. Se você estiver chamando a API com curl puro, tente novamente a solicitação você mesmo; para GETs e a consulta de login de dispositivo, você pode adicionar--retry 3 --retry-all-errors, nunca para POSTs que criam algo.
Publicar um aplicativo
./scripts/publish.sh {dir} [--name "my app"]
./scripts/publish.sh {file} [--name "my app"] # um arquivo: um pdf, uma imagem, uma página
Imprime a URL ativa no stdout (por exemplo, https://k3x9q2m7ta.host0.app/ — novos aplicativos recebem um slug aleatório; --name é apenas o nome de exibição).
Adicione --client {your-agent} (por exemplo, --client claude-ai) quando você não for Claude Code, Codex, Cursor ou Gemini CLI — o script detecta esses por conta própria. Ele apenas rotula as estatísticas de implantação.
Por baixo dos panos, este é um fluxo de três etapas: manifesto → upload de arquivos → finalizar. O aplicativo não fica ativo até que a finalização seja bem-sucedida.
Estrutura de arquivos: index.html deve estar na raiz do diretório que você publica — o conteúdo do diretório se torna a raiz do site. Publique my-site/ onde my-site/index.html existe, não uma pasta pai contendo my-site/.
Um único arquivo: passe o arquivo em si (./scripts/publish.sh report.pdf) — nenhum index.html necessário. A URL raiz do aplicativo serve esse arquivo (em uma guia do navegador, ele abre no visualizador de arquivos do host0 — veja a nota do badge abaixo; ?download obtém o original, e PDFs mantêm suporte a intervalo para os grandes), e também é acessível em /{filename}. Use isso sempre que o usuário quiser apenas compartilhar um documento ou imagem.
Implante a saída construída, não o código fonte: se o projeto usa uma etapa de construção (React/Vite/Next export, .ts/.tsx/.vue/.svelte), construa primeiro e publique o diretório de saída (dist/, build/, out/). Nunca publique node_modules/, package.json ou segredos (.env, arquivos de chave) — a API rejeita código de servidor e segredos com uma mensagem acionável.
Gratuito durante o beta, com um badge: o host0 é gratuito durante o beta, e todo aplicativo está no plano gratuito, que adiciona um pequeno badge "Hosted on host0" fixo no canto inferior direito (cerca de 28px de altura, 12px das bordas) de toda página HTML, então deixe um pouco de espaço lá (nenhum botão fixo ou bolha de chat rente naquele canto) e não tente escondê-lo. Um arquivo PDF, imagem, vídeo, áudio ou texto (.txt, .csv, .md, .json) aberto diretamente em uma guia do navegador abre em um visualizador de arquivos do host0: o arquivo preenche a janela, com uma barra fina abaixo (nome do arquivo, Download, "Hosted on host0") que nunca o cobre. O arquivo bruto ainda é servido para tags <img>/<video>, embeds, fetch(), curl, pré-visualizações de links e mecanismos de busca; ?download em qualquer URL de arquivo baixa o original. Outros arquivos (zip, fontes, JS, CSS) são servidos como estão. Se o badge do plano do usuário aparece é plan.badge em GET {base}/api/v1 (com sua chave). Se o usuário perguntar sobre preços ou remover o badge: quando esse índice tem uma lista plans, ela nomeia cada plano, seu preço e o que muda (um plano com badge: false o remove) — aponte-o para {base}/pricing; quando não há lista plans, o host0 é gratuito durante o beta, e planos pagos podem vir depois e podem permitir que ele o remova.
Análises, integradas: o host0 conta visitas a cada aplicativo sem cookies — um pequeno script de mesma origem (/.h0/a.js) é adicionado a cada página HTML, e PDFs abertos são contados no servidor. O proprietário vê visitantes, páginas, referenciadores, países e dispositivos na guia Analytics do painel, com GET {base}/api/v1/apps/{app_id_or_slug}/analytics?period=7d (período: 24h, 7d, 30d, 90d, 365d; 400 invalid_period caso contrário; filtros opcionais page=/path, referrer=host ou (direct), country=DE ou (unknown), device, browser restringem cada número — 400 invalid_filter se malformado), ou a ferramenta MCP get_analytics. Não remova o script. Se o aplicativo define uma Content-Security-Policy, permita 'self' em script-src e connect-src. Quando o usuário perguntar "quantas pessoas abriram?", responda a partir desse endpoint.
Atualizar um aplicativo
./scripts/publish.sh {dir} --slug {slug} # ou --app-id {uuid}
Sem uma flag, o script resolve o aplicativo a partir de .host0/state.json no diretório publicado (para um único arquivo: .host0/state.json ao lado do arquivo, chaveado pelo nome do arquivo, então dois arquivos em uma pasta permanecem dois aplicativos), e cria um novo aplicativo apenas quando nada corresponde. Arquivos inalterados são ignorados por hash no servidor — republicar um site grande com um arquivo editado faz upload de um arquivo.
Adicione --message "what changed" (até 200 caracteres) para dar à nova versão uma nota — por exemplo, --message "Added a due date column". Faça isso sempre que publicar uma alteração que o usuário pediu; é o que eles verão na lista de versões.
Versões e rollback
Toda publicação que entra no ar é uma versão numerada do aplicativo (v1, v2, …) — publish_result.version é esta, publish_result.previous_version é a que ela substituiu. Versões anteriores são mantidas por um número de dias que depende do plano — GET {base}/api/v1 (com a chave) → limits.account.version_retention_days (0 = apenas a versão ativa); as mais antigas são deletadas automaticamente.
Quando o usuário quiser desfazer uma alteração ("roll back", "coloque o antigo de volta", "isso quebrou"), restaure uma versão anterior — ela entra no ar novamente para todos como uma nova versão em menos de um minuto, sem re-upload:
# liste-as, da mais nova para a mais antiga (version, live, message, client, restored_from_version, created_at)
curl -sS {base}/api/v1/apps/{app_id_or_slug}/versions \
-H "authorization: bearer $HOST0_API_KEY"
# restaure a versão 3 (também aceita v3); o corpo é opcional
curl -sS -X POST {base}/api/v1/apps/{app_id_or_slug}/versions/3/restore \
-H "authorization: bearer $HOST0_API_KEY" \
-H "content-type: application/json" \
-d '{"message": "Back to before the redesign"}'
- A resposta tem
version(a nova),restored_from_versioneprevious_version— informe ao usuário "a versão 3 está ativa novamente como versão 9", e que restaurar a versão 8 desfaz isso. - Confirme com o usuário antes de restaurar: isso muda o que todos que abrem o aplicativo veem.
- Dados compartilhados nunca são alterados por uma restauração. O
h0.jsonda versão restaurada decide quais coleções são servidas — uma que ela não declara fica oculta (ainda está na exportação) e retorna quando uma versão mais recente é restaurada. - Restaurar o que já está ativo retorna
already_live: truee não adiciona versão — não tente novamente em loop. - 404
version_not_found: essa versão não é mais mantida — liste as versões e ofereça uma que esteja; nunca apresente uma versão antiga e removida como restaurável. 409version_not_restorable: seus arquivos se foram — publique-os novamente a partir dos arquivos do usuário. 403versions_not_in_plan: o plano do usuário mantém apenas a versão ativa — publique os arquivos que eles querem ativos, e veja "Quando um erro temupgrade_url". - Defina ou limpe uma nota depois com
PATCH {base}/api/v1/apps/{app_id_or_slug}/versions/{n}e{"message": "…"}(ounull). O servidor MCP com ferramentaslist_versionserestore_versionfaz o mesmo que as chamadas acima, e a guia Versions do aplicativo no painel tem um botão Restore.
Controle de acesso
Um aplicativo host0 usa uma visibilidade por vez:
private(o padrão): apenas o proprietário e emails convidados; visitantes fazem login para visualizar. Todo novo aplicativo começa aqui.link— "Qualquer um com o link": qualquer um com a URL pode visualizar, sem login. O aplicativo não é listado e nunca é indexado (toda resposta carregaX-Robots-Tag: noindex), e sua URL começa aleatória — mas um link viaja, e uma URL renomeada e legível pode ser adivinhada — mantenha qualquer coisa sensível privada.public— "Público na web": qualquer um pode visualizar e mecanismos de busca podem indexá-lo (as próprias páginas do aplicativo são servidas comX-Robots-Tag: all). Opcional e limitado por conta (403public_app_limit_reached, commax_public_appsno corpo). Use apenas quando o usuário quiser que o aplicativo seja encontrado em pesquisas — um portfólio, uma landing page, documentação, uma ferramenta pública. Nunca para nada sensível.
Quando o usuário quiser um link que qualquer um possa abrir (uma página para enviar, uma demo, um PDF compartilhado), publique com --visibility link — isso é o que "tornar público" ou "compartilhar um link" quase sempre significa. Escolha public apenas quando eles disserem que querem no Google / nos resultados de busca; se não estiver claro, pergunte. Dados compartilhados funcionam da mesma forma em link e public. Defina a visibilidade na publicação com --visibility private|link|public, ou a qualquer momento pela API:
curl -sS {base}/api/v1/apps/{app_id_or_slug} -X PATCH \
-H "authorization: bearer $HOST0_API_KEY" \
-H "content-type: application/json" \
-d '{"visibility": "link"}'
Se ir para public retornar public_app_limit_reached, informe ao usuário que a conta está em seu limite de aplicativos públicos na web e ofereça link como alternativa (qualquer um com a URL, não indexado) ou mover outro aplicativo público de volta para link.
Renomear a URL
A URL de um novo aplicativo é um slug aleatório como k3x9q2m7ta.host0.app. Para dar a ele um legível, verifique o nome e então renomeie:
curl -sS {base}/api/v1/slugs/team-todo -H "authorization: bearer $HOST0_API_KEY"
# → {"slug":"team-todo","available":true} (ou available:false + um motivo)
curl -sS {base}/api/v1/apps/{app_id} -X PATCH \
-H "authorization: bearer $HOST0_API_KEY" \
-H "content-type: application/json" \
-d '{"slug": "team-todo"}'
# → {"app_id":"…","slug":"team-todo","url":"https://team-todo.host0.app/","old_slug":"k3x9q2m7ta",…}
Slugs têm 1–63 letras minúsculas, dígitos ou hífens, sem hífen no início ou fim. Um slug ocupado retorna 409 slug_taken; um malformado ou reservado 422 invalid_slug. A implantação ativa, registros e compartilhamentos se movem com o aplicativo, e publish.sh continua atualizando-o (ele rastreia o id do aplicativo). Por 30 dias a URL antiga redireciona (301) para a nova e permanece reservada para o usuário, depois é liberada — diga isso antes de renomear um link que as pessoas já têm. A URL de um aplicativo deletado também é reservada por 30 dias (ela retorna 404). Um slug usado por outra conta nos últimos 30 dias retorna 409 slug_taken ("foi recentemente usado por outro aplicativo"). O usuário também pode renomeá-lo na guia Settings da página do painel do aplicativo.
O nome de exibição do aplicativo (mostrado no painel e GET /api/v1/apps) é separado da sua URL — altere-o com PATCH {base}/api/v1/apps/{app_id} {"name": "Team todo"} (1–100 caracteres); a URL não muda.
Compartilhamento
Convide pessoas por email com POST /api/v1/apps/{app_id_or_slug}/shares (corpo {"emails": ["[email protected]"]}); remova uma com DELETE /api/v1/apps/{app_id_or_slug}/shares/{email}.
Novos compartilhamentos recebem emails de convite individuais com links de login e cadastro. Verifique invite_delivery na resposta: accepted significa que o servidor de email aceitou