# share.heyadhi.com > Personal HTML publishing for the owner's registered machines. One HTML file > (plus assets) becomes a live page at https://{site_id}.share.heyadhi.com/, > each page its own browser origin, optionally behind a viewer password. > Publishing requires a machine key the owner has registered; there is no signup. ## Quick start - Agent Skill: https://share.heyadhi.com/SKILL.md - CLI (Node 24+, zero dependencies): https://share.heyadhi.com/share.mjs — updates itself before every command (GET /share.mjs with its own SHA-256 as If-None-Match; 304 = current) - `node share.mjs keygen` → prints this machine's public key for the owner to register - `node share.mjs publish page.html` → https://.share.heyadhi.com/ ; prints {site_id, url, ...}; uploads the web assets found beside the file. A name is 3–60 lowercase letters, digits and single hyphens; a taken name fails with 409. - `node share.mjs update page.html`, `asset [--path p]`, `inspect `, `delete ` - Viewer password: `--generate-password` (printed once) or `--password` (hidden prompt); `--remove-password` on update ## Limits - HTML: 10,000,000 bytes of UTF-8. Asset: 50,000,000 bytes. ## API Base: https://share.heyadhi.com/v1 — machine-readable: GET /v1/help - POST /v1/sites — create. JSON {"site_id": "", "html_content": "...", "password": "optional"}; site_id is the page name and address (3–60 lowercase letters, digits and single hyphens); header Idempotency-Key (128 random bits, base64url). 201 {site_id, url, status:"created"}; the same key and request again → 200 status:"existing"; a name already in use → 409 name_taken. - GET /v1/sites/{site_id} — status, html_sha256, and each locally referenced asset as present/missing. Anonymous for public pages; protected pages need authentication (else 404). - PUT /v1/sites/{site_id} — replace HTML. password omitted = unchanged, nonempty = set/rotate, "" = remove. - POST /v1/sites/{site_id}/assets?relative_path= — multipart field "file". - DELETE /v1/sites/{site_id} — unpublish; the name is free again afterwards. ## Authentication (every write; share.mjs does this for you) 1. GET /v1/auth/challenge?method=&target= → {challenge_id, message, expires_at} 2. Sign the UTF-8 bytes of `message` with the machine's registered Ed25519 private key. 3. POST /v1/auth/session {"challenge_id", "public_key": "ed25519:", "signature": ""} → {token} 4. Within 60 s, send the request with `Authorization: Bearer `. The token works once, for that method and target only. ## Errors JSON {"error": code, "message": sentence}. 401 machine auth, 403 unsafe asset path, 404 unknown page, 409 name taken or conflicting update, 413 too large, 422 invalid input, 429 rate limited, 503 storage unavailable (retry) — except 503 budget_exhausted: the monthly R2 budget is used up; do not retry. ## Differences from ht-ml.app Same core routes. No update_key: writes are signed by registered machine keys. Create requires Idempotency-Key. Pages are named by the client and can be deleted. Safe asset paths need not appear in the HTML. No public signup, directory, or browser dashboard.