For agents
host0 — publish a folder, get a live URL.
Base URL: https://host0.ai
Supported today: static sites — HTML, CSS, JS, images/fonts/assets; ≤1000 files, ≤25 MB/file, ≤100 MB total; index.html required (or deploy a single file on its own — it serves at /). Shared data: declare record collections in h0.json and read/write them from the page at /.h0/data/… (docs: /docs#shared-data). HTML pages on the free plan get a small "Hosted on host0" badge fixed bottom-right — leave that corner clear. Server code is on the roadmap.
Human here? The visual landing page has the pitch, the demo, and the waitlist. This page is the plain-text version, written for agents.
Install the skill
Two files: an instruction file and one Bash script. Requires curl, jq, and shasum/sha256sum. The paths below are where Claude Code keeps skills — other agents keep them elsewhere, so use whichever directory yours reads.
mkdir -p ~/.claude/skills/host0/scripts
curl -sS https://host0.ai/skill.md -o ~/.claude/skills/host0/SKILL.md
curl -sS https://host0.ai/publish.sh -o ~/.claude/skills/host0/scripts/publish.sh
chmod +x ~/.claude/skills/host0/scripts/publish.shPaste this into your agent
It installs the skill, signs in with a link you approve in your browser, and asks you what to publish. Nothing to type into the chat.
Set me up with host0 — the cloud for small software, where a folder becomes a live URL. 1. Install the host0 skill: save https://host0.ai/skill.md as SKILL.md, and https://host0.ai/publish.sh as scripts/publish.sh beside it, then chmod +x scripts/publish.sh. In Claude Code that folder is ~/.claude/skills/host0/ — other agents keep skills elsewhere, so use whichever directory mine reads. 2. Read SKILL.md, then curl https://host0.ai/api/v1 for the live contract. If the two disagree, trust the API. 3. Sign me in with a device login — don't ask for my email or password: run scripts/publish.sh --login, show me the link and code it prints, and once I've approved in my browser run scripts/publish.sh --login-wait — it saves the key to ~/.host0/credentials with chmod 600. Then ask me what I want to publish.
Get an API key
Device login: the agent sends nothing personal. It prints a link and a short code, the human approves it on host0 in their own browser, and the script saves the key. With no key, a publish starts this on its own — exit code 3 means “show the link and code, then run again”. The key starts h0_, is returned exactly once, and travels as Authorization: Bearer h0_… on every authenticated request.
# 1. start a sign-in — no email, nothing personal leaves this machine
# prints a link + code and exits 3: show both to the user
~/.claude/skills/host0/scripts/publish.sh --login
# 2. wait for the approval — saves the key to ~/.host0/credentials (chmod 600)
~/.claude/skills/host0/scripts/publish.sh --login-waitThe same over plain REST:
# 1. start — returns device_code, user_code and verification_uri_complete
curl -sS -X POST https://host0.ai/api/v1/auth/device
# 2. show the user verification_uri_complete + user_code, then poll every
# "interval" seconds until they approve (authorization_pending until then)
curl -sS --retry 3 --retry-all-errors https://host0.ai/api/v1/auth/device/token \
-H "content-type: application/json" \
-d '{"device_code": "…"}'
# 3. save the returned api_key yourself — don't make the human run this
mkdir -p ~/.host0 && printf '%s' "h0_your_key_here" > ~/.host0/credentials && chmod 600 ~/.host0/credentialsEmail code (fallback) — only for an agent on the human's own machine, never from a cloud sandbox:
# fallback — only for an agent on your own machine, never from a cloud sandbox
# 1. request a 6-digit code — host0 emails it
curl -sS https://host0.ai/api/v1/auth/request-code \
-H "content-type: application/json" \
-d '{"email": "you@example.com"}'
# 2. exchange the code for an api key (returned exactly once)
curl -sS https://host0.ai/api/v1/auth/verify-code \
-H "content-type: application/json" \
-d '{"email": "you@example.com", "code": "123456"}'
# 3. save it yourself — don't make the human run this
mkdir -p ~/.host0 && printf '%s' "h0_your_key_here" > ~/.host0/credentials && chmod 600 ~/.host0/credentialsPublish
Publish a built static site with index.html at the root of the directory — its contents become the site root. The script handles hashing, the manifest, uploads, and finalize.
# first publish — creates the app
~/.claude/skills/host0/scripts/publish.sh ./my-site --name "my site"
# every publish after that — same folder, same app, unchanged files skipped
~/.claude/skills/host0/scripts/publish.sh ./my-siteOr drive the REST API directly — four steps:
- 01POST https://host0.ai/api/v1/apps — body {"name": "my site"} → app_id, slug (random; rename later with PATCH {"slug"}), url
- 02POST https://host0.ai/api/v1/apps/{app}/deployments — body {"files": [{path, size, sha256}]} → upload.targets (unchanged files come back in upload.skipped)
- 03PUT each target url — raw bytes, size and sha256 must match the manifest
- 04POST https://host0.ai/api/v1/deployments/{id}/finalize — flips the deployment live, atomically
Nothing is live until finalize succeeds — a half-finished upload never serves, and the previous deployment keeps serving until the flip.
On the free plan, host0 adds a small “Hosted on host0” badge fixed to the bottom-right corner of every HTML page (PDFs, images and other files are untouched) — leave a little room there and don't try to hide it; paid plans will be able to remove it.
Shared data
An app is not limited to static content. Put an h0.json at the root of the deploy declaring record collections, and the page reads and writes them at /.h0/data/{collection} on its own origin — a team todo, a ticket board, an RSVP list, with no server code and no connection string.
{
"records": {
"tickets": {
"fields": {
"title": { "type": "string", "required": true, "maxLength": 200 },
"done": { "type": "boolean", "default": false },
"status": { "type": "string", "enum": ["todo", "doing", "done"], "default": "todo" },
"assignee": { "type": "string" },
"notes": { "type": "string", "maxLength": 5000 }
},
"access": {
"read": "viewers",
"create": "viewers",
"update": "members",
"delete": "members"
}
}
}
}- –
GET /.h0/data/{collection}— List records, newest first. `?limit=` and `?order=asc|desc`. → {records: [...], count} - –
POST /.h0/data/{collection}— Create one — body is the record's declared fields. → the record - –
GET /.h0/data/{collection}/{id}— Read one record. - –
PATCH /.h0/data/{collection}/{id}— Merge the fields in the body into the record; absent fields are left alone, so two people editing different fields don't clobber each other. - –
DELETE /.h0/data/{collection}/{id}— Delete one record. → 200 {id, deleted: true} — a JSON body like every other response, never an empty 204. - –
GET /.h0/me— Who is calling — {anonymous: true, signin_url} or {anonymous: false, user_id, email, owner}.
Audiences are viewers (anyone who can open the app, including anonymous visitors of a public one), members (the owner plus invited emails), owner (the app's owner only), creator (each signed-in person, their own records only), or an explicit list of emails. By default everyone the app admits can read and create, while update and delete need an invite (`members`). The owner always passes every rule.
Decide whose data it is before writing `h0.json` — and if the user hasn't said, ask them. Shared: everyone the app admits works on the same records (a team board, a poll, a guestbook) — leave `read` on `viewers` or `members`. Per-person: each visitor signs in and gets their own private records (a habit tracker, a journal, a reading list anyone can use from the same URL) — set `read`, `create`, `update` and `delete` all to `creator`, and when `/.h0/me` says `{anonymous: true}` show a sign-in screen with its `signin_url` instead of loading data. `owner` is neither: it means only you, the app's owner — every other visitor is shut out.
On a public app, call `/.h0/me` when the page loads and, if it answers `{anonymous: true}`, render its `signin_url` as a visible sign-in link — a link the visitor chooses to follow, never an automatic redirect on page load. Members are not recognised automatically — reading is open, so nothing sends them through the gate on their own. Handle 401 on writes too: the body carries `signin_url`.
The full shape, with a worked team-todo example, is in the docs.
Visibility
Every app goes live public at an unlisted URL unless you ask for something else. An app uses one visibility at a time:
- –
public— what you get unless you ask for something else. The app is unlisted and never indexed, but anyone who has the URL can view it. A new app's URL is random; rename it to something readable in the dashboard or with the API. - –
link— the same behaviour, stated explicitly: anyone with the link, listed nowhere. - –
private— only the owner and emails invited throughPOST /api/v1/apps/{app}/shares. Visitors sign in to view; removing an invite kills their existing sessions immediately.
Set it on publish with --visibility private|link|public, or any time with PATCH /api/v1/apps/{app}.
Limits
Upload (REST + skill) 1000 files · 25 MB (26214400)/file · 100 MB (104857600) total
Account 100 apps · 200 deploys/hour · 500 MB (524288000) stored
API rate 120 requests/minute per API keyParenthesised figures are exact byte counts. These are rendered from the same constants the server enforces — GET /api/v1 returns them as JSON under limits. Over a cap you get bad_request (a manifest over the upload caps), app_limit_reached, deploy_rate_limited, storage_quota_exceeded or rate_limited, each with a message saying what to change.
MCP
Deploying is the skill's job. To manage apps you've deployed from Claude and other MCP clients, add host0 as a remote server:
claude mcp add --transport http host0 https://host0.ai/mcpSign in when prompted; the tools list and inspect apps, change visibility, rename an app's URL, share and unshare by email, and delete an app (live or not). They don't deploy — to put new files live, always use the skill. Custom domains will land here later.
Machine surfaces
- –/skill.md — The skill instruction file, served verbatim
- –/publish.sh — The bundled publish script (chmod +x it)
- –/api/v1 — The live API index — endpoints, caps, contract
- –/llms.txt — The llmstxt.org index of everything on this list
- –/docs — The full human+agent documentation
- –/mcp — The remote MCP endpoint for managing apps (OAuth; a bare GET answers 401)