# 9. Hosts, assets and privacy

> What each host gives, assets and uploads, and what stays private.

Source: https://clayjs.com/docs/guide/hosts/

## What each host gives

| | HTML Clay | Hyperclay Local | hyperclay.com |
|---|---|---|---|
| Who edits | the local user | the local user; teammates through device sync | owner, team editors, edit-link holders |
| Live sync | tabs on this machine | tabs, plus other devices through the sync engine | every editor and viewer |
| Outside disk edits reach open tabs | yes (agents, text editors) | yes | no disk except through device sync |
| Presence | none | none | avatar stack, no API |
| `by` on frames, editing marker | no | no | yes |
| Live stores ([Live data: `clay="live"`](https://clayjs.com/docs/guide/live-sync/#live-data-clay-live)) | tabs of one browser at full speed | tabs of one browser at full speed; other devices at save speed | every editor at full speed |
| Page editor merge state ([Live data: `clay="live"`](https://clayjs.com/docs/guide/live-sync/#live-data-clay-live)) | none in the file: open tabs share it, a fresh open rebuilds it from the article | kept beside the file | kept beside the file |
| Wire and ai-edit | yes | yes | ai-edit only, for owners and team editors on a paid plan |
| Data API write | yes | yes | read only |
| Uploads | 25 MB, in `~/htmlclay/uploads/` | 25 MB, in the root's `uploads/` | 10, 20 or 100 MB by plan, in the account's `uploads/` |
| Upload access (`document.upload.access`) | `local` | `local` | `document` once the account's uploaded files are protected; no field before that |
| Version history | yes | 60 days, newest 20 | a version per save; a restore does not reach open editors' tabs, so reload after restoring |
| Max save (`document.maxBytes`) | 50 MB | 20 MB | 5 MB |

The `/_/meta` answer carries `document.maxBytes`: the maximum uncompressed UTF-8 HTML save body, inclusive. All three hosts send it, and ClayJS measures a save against it before sending, so a document that is over the limit is refused instead of lost ([Size limits](https://clayjs.com/docs/guide/saving/#size-limits)). A missing or unusable value means the limit is unknown, and the host's own refusal is the only answer.

On hyperclay.com only, the same block carries `document.maxBytesMeasures: "file"`. It means the limit is measured without the `<script merge="page">` tag that holds the document's live state. HTML Clay and Hyperclay Local send no such field.

A plain static server or a file opened from disk can show the file in view mode, but cannot save it. Use the `demo` plugin for a public demo that saves into each visitor's browser.

On hyperclay.com, every tab holds its own sync connection. Over HTTP/1.1, a browser allows six connections per origin, so a seventh tab of the same site can stall.

## Getting started on each

- **HTML Clay:** save the file as `name.htmlclay` and open it with HTML Clay 1.9.0 or newer. The extension changes nothing about the content. The file is ordinary HTML.
- **Hyperclay Local:** put the file in the synced folder and open it from the app.
- **hyperclay.com:** create a site and paste the file, or upload it.

## Privacy

The saved file is the data. Everyone who can download it can read everything in it: hidden elements, `data-*` attributes, JSON scripts, CSS-hidden sections and everything the owner typed. `show-when:editmode`, `hidden` and `display: none` are presentation only.

- To publish a version without private values, make a copy and remove them, or keep private data in a different file with different access.
- A file you hand out as a template should ship with sample data, not yours.
- On hyperclay.com, a file uploaded from inside a document is bound to that document. When `document.upload.access` is `document`, its bytes follow the document's read permission on every route. A reader without access gets the same 404 as a missing file, and the file is sent `Cache-Control: private, no-store`. Uploads made before that binding existed stay reachable by URL until the account's protection migration has run. A copy someone already downloaded cannot be taken back.
- HTML Clay and Hyperclay Local report `local`, meaning their own file-serving rules, not a sharing ACL. A file there is as private as the folder on that machine.
- Password inputs are never persisted. An authored `value` on them is still in the file.

## Offline in a local host

Hyperclay Local and HTML Clay each bundle one exact, published ClayJS release, chosen in their `offline-runtime.lock.json`. They ship the whole release listed in `dist/offline-manifest.json` in the npm package, not only what one document boots with. That index lists about 550 files: every module, plugin, satellite such as `sap.js` and `clay-router.js`, vendored library, theme sheet and font, plus the exact library builds older Journals load from other CDNs. A plugin, the merge engine, a drag library or a font that a document uses for the first time offline still loads.

- **The route.** The release is served at `/_/clayjs/<version>/<path>`, so `https://clayjs.com/v1/clay.js` is `/_/clayjs/<version>/clay.js`, with an immutable cache and an `ETag` of the file's sha256. A path the index does not list answers 404 and is never fetched from the network.
- **Projection on serve.** A served document is rewritten in two places only. First, `src` and `href` attributes for recognized ClayJS runtime URLs become the local address, with the same spelling the file used for the rest of the URL. Recognized URLs are `https://clayjs.com/v1/...`, exact-version URLs of the bundled release, and the few exact library and font URLs the release carries a copy of. Second, a `<script type="application/json" id="clay-runtime-map">` descriptor records the mapping. Text, code samples, Markdown and every other URL stay byte for byte.
- **Canonicalization on every write.** Save, data API writes, restore, export and live-sync frames turn those local addresses back into the public `https://clayjs.com/...` URLs and remove the descriptor before anything reaches disk. A saved or downloaded file never contains a `/_/clayjs/` address, so it stays portable to any host. A request with `Clay-Source: 1` receives the canonical bytes unprojected.
- **Versions.** A rolling `https://clayjs.com/v1/` URL is served from the host's bundled release. If a document pins a release the host does not bundle, or names a runtime file the bundled release lacks, every runtime URL is left public. It loads online, is not promised offline, and the response carries a `Clay-Runtime-Unavailable` header that says why. The host never substitutes another release's files into a page.
- **Older Journals.** Journals created before the release bundled its libraries load OverType and highlight.js from other CDNs, and their fonts from Google Fonts. The release maps three exact URLs to copies it ships: `https://cdn.jsdelivr.net/npm/overtype@2.6.1/dist/overtype.min.js` and `https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js` to the same classic builds (which define `window.OverType` and `window.hljs`, with their licenses and pinned hashes in `compat/journal-assets.lock.json`), and the Journal's Google Fonts stylesheet URL to a stylesheet over the bundled IBM Plex files. Each match is the whole URL. Another version, a query string, another file or another host stays on the network. A Journal saved with those URLs edits offline, and a document that names anything else from those CDNs does not.
- **The first switch to edit.** A document opened offline in view can switch to edit without the network. The switch reloads into an edit boot, and every file that boot loads (the mode controller, the plugins, the Document editor and their late imports) is in the bundled release. Both hosts serve that set from the release with its index digest, the first edit saves the canonical public URLs, and the reopened file is projected again. ClayJS checks it per release: a mode file missing from the release fails the closure check.
- **What stays online.** Pictures and media hosted elsewhere, AI features and collaboration with other people need the network. A picture uploaded into the local folder is a local file and works offline.
- **Checking a host.** `node scripts/verify-offline-closure.mjs` in the ClayJS repository checks a release's closure: every listed file is present with its recorded hash, and every module import resolves inside the release. It runs in three modes: `--package-root <dir>` for a built checkout, `--published-root <dir>` for an unpacked release tree and `--base-url <url>` for a running host's `/_/clayjs/<version>/` prefix. With `--base-url`, pass `--index <file>` naming the host's own bundled index. Without it, the check compares the host against the checkout's index.

Why the whole release and not a boot closure: a sap document loads much of its code late. A Kanban board loaded with `plugins=sortable,people,upload,sync,visitor-drafts,unsent,menu` boots with a few dozen modules. It then asks for `derive`, the merge engine, SortableJS on the first drag and the live-sync modules as they are needed, about 116 release paths in all. None of those appear in what the page requests at boot, so a host that served only the boot graph would break on the first drag with the network off. ClayJS's cross-host contract test opens that real board in both local hosts and requests every one of those paths through the host.
