host0
host0 héberge tout ce que votre agent a créé — HTML, sites, applications avec base de données partagée, Markdown, PDF — à un lien que vous pouvez partager, aussi simplement qu'un Google Doc. Fonctionne avec n'importe quel agent.
La primitive de base est une application : publiez n'importe quel fichier ou dossier — un répertoire de fichiers statiques, ou un seul fichier qui s'ouvre dans un navigateur (un PDF, une image, une page HTML unique, vidéo, audio, Markdown, CSV, texte ; une archive ou un autre type téléchargeable uniquement est rejeté — extrayez un zip et publiez le dossier) — et obtenez une URL live à https://{slug}.host0.app/ (développement local : http://{slug}.localhost:3000/). Chaque application est privée par défaut — seul le propriétaire (connecté à host0) et les personnes invitées par email peuvent l'ouvrir. L'ouvrir à toute personne ayant l'URL (sans connexion ; non listé et jamais indexé) est facultatif : --visibility link. --visibility public va plus loin — public sur le web, les moteurs de recherche peuvent l'indexer — et n'est réservé qu'aux choses que l'utilisateur souhaite trouver dans les résultats de recherche. L'URL d'une nouvelle application est aléatoire (k3x9q2m7ta.host0.app) ; renommez-la en quelque chose de lisible (team-todo.host0.app) lorsque l'utilisateur souhaite un lien plus joli — voir "Renommer l'URL" ci-dessous.
Une application peut également contenir des données partagées : déclarez des collections d'enregistrements dans un h0.json et son frontend obtient une base de données sur son propre domaine, sans code serveur. Voir "Données partagées" ci-dessous — c'est ce qui transforme une page en un outil qu'une équipe utilise ensemble.
Documentation actuelle
La documentation complète se trouve à https://host0.ai/docs — lisez-la lorsque l'utilisateur demande comment quelque chose fonctionne, ce qui est possible, ou avant de lui dire qu'une fonctionnalité n'est pas prise en charge.
L'API se décrit également elle-même. L'index lisible par machine :
curl -sS {base}/api/v1
Il renvoie la liste actuelle des points de terminaison, les limites de fichiers et le contrat de la plateforme (ce que host0 propose aujourd'hui). Envoyez la clé API (-H "authorization: bearer $HOST0_API_KEY") et limits et plan correspondent à ceux de l'utilisateur — les limites dépendent du plan du compte ; sans clé, vous obtenez les valeurs par défaut d'un nouveau compte. GET {base}/api/v1/account ajoute ce que le compte a utilisé ce mois-ci. Si ce texte de compétence et le comportement de l'API live diffèrent, faites confiance à l'API live.
Pour installer ou mettre à jour cette compétence : npx skills add host0-ai/skills (tout agent pris en charge par le skills CLI ; source : https://github.com/host0-ai/skills), ou récupérez deux fichiers : https://host0.ai/skill.md → enregistrez-le sous SKILL.md, et https://host0.ai/publish.sh → enregistrez-le sous scripts/publish.sh à côté, puis chmod +x scripts/publish.sh. L'URL du script est /publish.sh — il n'y a pas de /scripts/.
Prérequis
- Binaires requis :
curl,jq(le script se termine par un message clair si l'un d'eux manque) - Une clé API, depuis
$HOST0_API_KEYou~/.host0/credentials. Sans clé,publish.shlance une connexion d'appareil pour vous — l'utilisateur approuve un lien + un code dans son propre navigateur (voir "Obtenir une clé API") - Assistant intégré :
./scripts/publish.sh - Réseau : HTTPS sortant vers
host0.ai(l'API) ethost0.app(téléchargements et applications live). Dans un sandbox avec une liste d'autorisation de trafic sortant, si une requête échoue avecSSL_ERROR_SYSCALL, une erreur de proxy, ou une connexion refusée/réinitialisée à chaque fois, dites à l'utilisateur quel domaine autoriser — ne bouclez pas dessus. - Les défaillances transitoires sont possibles : certains sandbox cloud voient des réinitialisations de connexion /
SSL_ERROR_SYSCALL/ des délais d'attente pour atteindrehost0.ai.publish.shréessaie automatiquement (vous verrezhost0: network hiccup … retrying 2/4sur stderr) — laissez-le s'exécuter. Si vous appelez l'API avec curl brut, réessayez la requête vous-même ; pour les GET et l'interrogation de connexion d'appareil, vous pouvez ajouter--retry 3 --retry-all-errors, jamais pour les POST qui créent quelque chose.
Publier une application
./scripts/publish.sh {dir} [--name "my app"]
./scripts/publish.sh {file} [--name "my app"] # un seul fichier : un pdf, une image, une page
Affiche l'URL live sur stdout (par exemple https://k3x9q2m7ta.host0.app/ — les nouvelles applications obtiennent un slug aléatoire ; --name est uniquement le nom d'affichage).
Ajoutez --client {votre-agent} (par ex. --client claude-ai) lorsque vous n'êtes pas Claude Code, Codex, Cursor ou Gemini CLI — le script les détecte lui-même. Il ne fait qu'étiqueter les statistiques de déploiement.
Sous le capot, il s'agit d'un flux en trois étapes : manifeste → téléchargement des fichiers → finalisation. L'application n'est pas live tant que la finalisation n'a pas réussi.
Structure des fichiers : index.html doit se trouver à la racine du répertoire que vous publiez — le contenu du répertoire devient la racine du site. Publiez mon-site/ où mon-site/index.html existe, pas un dossier parent contenant mon-site/.
Un seul fichier : passez le fichier lui-même (./scripts/publish.sh rapport.pdf) — pas besoin d'index.html. L'URL racine de l'application sert ce fichier (dans un onglet de navigateur, il s'ouvre dans la visionneuse de fichiers de host0 — voir la note sur le badge ci-dessous ; ?download récupère l'original, et les PDF conservent la prise en charge des grandes plages), et il est également accessible à /{filename}. Utilisez-le chaque fois que l'utilisateur souhaite simplement partager un document ou une image.
Déployez la sortie construite, pas le code source : si le projet utilise une étape de construction (React/Vite/Next export, .ts/.tsx/.vue/.svelte), construisez d'abord et publiez le répertoire de sortie (dist/, build/, out/). Ne publiez jamais node_modules/, package.json ou les secrets (.env, fichiers de clés) — l'API rejette le code serveur et les secrets avec un message exploitable.
Gratuit pendant la bêta, avec un badge : host0 est gratuit pendant la bêta, et chaque application est sur le plan gratuit, qui ajoute un petit badge "Hosted on host0" fixé en bas à droite (environ 28 px de haut, 12 px des bords) de chaque page HTML, alors laissez un peu d'espace (pas de bouton fixe ou de bulle de chat dans ce coin) et n'essayez pas de le masquer. Un fichier PDF, image, vidéo, audio ou texte (.txt, .csv, .md, .json) ouvert directement dans un onglet de navigateur s'ouvre dans une visionneuse de fichiers host0 : le fichier remplit la fenêtre, avec une barre fine en dessous (nom du fichier, Télécharger, "Hosted on host0") qui ne le recouvre jamais. Le fichier brut est toujours servi aux balises <img>/<video>, aux intégrations, fetch(), curl, aux aperçus de liens et aux moteurs de recherche ; ?download sur n'importe quelle URL de fichier télécharge l'original. Les autres fichiers (zip, polices, JS, CSS) sont servis tels quels. Le fait que le plan de l'utilisateur affiche le badge est plan.badge dans GET {base}/api/v1 (avec sa clé). Si l'utilisateur pose des questions sur la tarification ou la suppression du badge : lorsque cet index a une liste plans, elle nomme chaque plan, son prix et ce qu'il modifie (un plan avec badge: false le supprime) — orientez-le vers {base}/pricing ; lorsqu'il n'y a pas de liste plans, host0 est gratuit pendant la bêta, et des plans payants pourraient arriver plus tard et pourraient permettre de le supprimer.
Analytiques intégrées : host0 compte les visites de chaque application sans cookies — un petit script de même origine (/.h0/a.js) est ajouté à chaque page HTML, et les PDF ouverts sont comptés sur le serveur. Le propriétaire voit les visiteurs, les pages, les référents, les pays et les appareils dans l'onglet Analytics du tableau de bord, avec GET {base}/api/v1/apps/{app_id_or_slug}/analytics?period=7d (période : 24h, 7d, 30d, 90d, 365d ; 400 invalid_period sinon ; filtres optionnels page=/path, referrer=host ou (direct), country=DE ou (unknown), device, browser réduisent chaque nombre — 400 invalid_filter si mal formé), ou l'outil MCP get_analytics. Ne supprimez pas le script. Si l'application définit une Content-Security-Policy, autorisez 'self' dans script-src et connect-src. Lorsque l'utilisateur demande "combien de personnes l'ont ouvert ?", répondez à partir de ce point de terminaison.
Mettre à jour une application
./scripts/publish.sh {dir} --slug {slug} # ou --app-id {uuid}
Sans indicateur, le script résout l'application à partir de .host0/state.json dans le répertoire publié (pour un seul fichier : .host0/state.json à côté du fichier, indexé par le nom du fichier, donc deux fichiers dans un dossier restent deux applications), et crée une nouvelle application uniquement lorsque rien ne correspond. Les fichiers inchangés sont ignorés par hachage côté serveur — republier un grand site avec un fichier modifié télécharge un fichier.
Ajoutez --message "ce qui a changé" (jusqu'à 200 caractères) pour donner une note à la nouvelle version — par exemple --message "Ajout d'une colonne d'échéance". Faites-le chaque fois que vous publiez une modification demandée par l'utilisateur ; c'est ce qu'il verra dans la liste des versions.
Versions et rollback
Chaque publication live est une version numérotée de l'application (v1, v2, …) — publish_result.version est celle-ci, publish_result.previous_version celle qu'elle a remplacée. Les versions antérieures sont conservées pendant un nombre de jours qui dépend du plan — GET {base}/api/v1 (avec la clé) → limits.account.version_retention_days (0 = seule la version live) ; les plus anciennes sont supprimées automatiquement.
Lorsque l'utilisateur souhaite annuler une modification ("roll back", "remettre l'ancienne", "cela l'a cassé"), restaurez une version antérieure — elle devient live à nouveau pour tout le monde en tant que nouvelle version en moins d'une minute, sans nouveau téléchargement :
# les lister, du plus récent au plus ancien (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"
# restaurer la version 3 (accepte aussi v3) ; le corps est facultatif
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": "Retour avant la refonte"}'
- La réponse a
version(la nouvelle),restored_from_versionetprevious_version— dites à l'utilisateur "la version 3 est à nouveau live en tant que version 9", et que la restauration de la version 8 l'annule. - Confirmez avec l'utilisateur avant de restaurer : cela modifie ce que toute personne qui ouvre l'application voit.
- Les données partagées ne sont jamais modifiées par une restauration. Le fichier
h0.jsonde la version restaurée décide quelles collections sont servies — une collection non déclarée est masquée (toujours dans l'export), et réapparaît lorsqu'une version plus récente est restaurée. - La restauration de ce qui est déjà live renvoie
already_live: trueet n'ajoute pas de version — ne réessayez pas en boucle. - 404
version_not_found: cette version n'est plus conservée — listez les versions et proposez-en une qui l'est ; ne présentez jamais une version plus ancienne et purgée comme restaurable. 409version_not_restorable: ses fichiers ont disparu — republiez les fichiers que l'utilisateur souhaite live à la place. 403versions_not_in_plan: le plan de l'utilisateur ne conserve que la version live — republiez les fichiers qu'il souhaite live, et consultez "Lorsqu'une erreur aupgrade_url". - Définissez ou effacez une note plus tard avec
PATCH {base}/api/v1/apps/{app_id_or_slug}/versions/{n}et{"message": "…"}(ounull). Le serveur MCPlist_versionsetrestore_versionfont la même chose que les appels ci-dessus, et l'onglet Versions du tableau de bord de l'application a un bouton Restaurer.
Contrôle d'accès
Une application host0 utilise une seule visibilité à la fois :
private(par défaut) : seul le propriétaire et les emails invités ; les visiteurs se connectent pour voir. Chaque nouvelle application commence ici.link— "Toute personne avec le lien" : toute personne ayant l'URL peut voir, sans connexion. L'application n'est pas listée et jamais indexée (chaque réponse porteX-Robots-Tag: noindex), et son URL commence par un slug aléatoire — mais un lien voyage, et une URL renommée et lisible peut être devinée — gardez les choses sensibles privées.public— "Public sur le web" : toute personne peut voir et les moteurs de recherche peuvent l'indexer (les pages de l'application sont servies avecX-Robots-Tag: all). Facultatif, et limité par compte (403public_app_limit_reached, avecmax_public_appsdans le corps). Utilisez-le uniquement lorsque l'utilisateur souhaite que l'application soit trouvée dans les résultats de recherche — un portfolio, une page d'accueil, une documentation, un outil public. Jamais pour quoi que ce soit de sensible.
Lorsque l'utilisateur souhaite un lien que tout le monde peut ouvrir (une page à diffuser, une démo, un PDF partagé), publiez avec --visibility link — c'est ce que "rendre public" ou "partager un lien" signifie presque toujours. Choisissez public uniquement lorsqu'il dit qu'il le veut sur Google / dans les résultats de recherche ; si ce n'est pas clair, demandez. Les données partagées fonctionnent de la même manière sur link et public. Définissez la visibilité lors de la publication avec --visibility private|link|public, ou à tout moment via l'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"}'
Si le passage à public renvoie public_app_limit_reached, dites à l'utilisateur que le compte a atteint sa limite d'applications publiques sur le web et proposez link à la place (toute personne avec l'URL, non indexé) ou déplacez une autre application publique vers link.
Renommer l'URL
L'URL d'une nouvelle application est un slug aléatoire comme k3x9q2m7ta.host0.app. Pour lui donner un nom lisible, vérifiez le nom, puis renommez :
curl -sS {base}/api/v1/slugs/team-todo -H "authorization: bearer $HOST0_API_KEY"
# → {"slug":"team-todo","available":true} (ou available:false + une raison)
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",…}
Les slugs font 1 à 63 caractères (lettres minuscules, chiffres ou tirets), sans tiret au début ni à la fin. Un slug pris renvoie 409 slug_taken ; un slug mal formé ou réservé renvoie 422 invalid_slug. Le déploiement live, les enregistrements et les partages suivent l'application, et publish.sh continue de la mettre à jour (il suit l'ID de l'application). Pendant 30 jours, l'ancienne URL redirige (301) vers la nouvelle et reste réservée pour l'utilisateur, puis elle est libérée — dites-le avant de renommer un lien que les gens ont déjà. L'URL d'une application supprimée est réservée pendant 30 jours également (elle renvoie 404). Un slug utilisé par un autre compte au cours des 30 derniers jours renvoie 409 slug_taken ("a été récemment utilisé par une autre application"). L'utilisateur peut également le renommer dans l'onglet Paramètres de la page du tableau de bord de l'application.
Le nom d'affichage de l'application (affiché dans le tableau de bord et GET /api/v1/apps) est distinct de son URL — modifiez-le avec PATCH {base}/api/v1/apps/{app_id} {"name": "Team todo"} (1 à 100 caractères) ; l'URL ne change pas.
Partage
Invitez des personnes par email avec POST /api/v1/apps/{app_id_or_slug}/shares (corps {"emails": ["[email protected]"]}) ; supprimez-en un avec DELETE /api/v1/apps/{app_id_or_slug}/shares/{email}.
Les nouveaux partages reçoivent des emails d'invitation individuels avec des liens de connexion et d'inscription. Vérifiez invite_delivery dans la réponse : accepted signifie que le serveur de messagerie a accepté
…
[tronqué pour la traduction]