# 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.