clay.js
  1. ClayJS
  2. How ClayJS works
  3. Hosts, assets and privacy

Hosts, assets and privacy

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

What each host gives#

HTML ClayHyperclay Localhyperclay.com
Who editsthe local userthe local user; teammates through device syncowner, team editors, edit-link holders
Live synctabs on this machinetabs, plus other devices through the sync engineevery editor and viewer
Outside disk edits reach open tabsyes (agents, text editors)yesno disk except through device sync
Presencenonenoneavatar stack, no API
by on frames, editing markernonoyes
Live stores (Live data: clay="live")tabs of one browser at full speedtabs of one browser at full speed; other devices at save speedevery editor at full speed
Page editor merge state (Live data: clay="live")none in the file: open tabs share it, a fresh open rebuilds it from the articlekept beside the filekept beside the file
Wire and ai-edityesyesai-edit only, for owners and team editors on a paid plan
Data API writeyesyesread only
Uploads25 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)locallocaldocument once the account’s uploaded files are protected; no field before that
Version historyyes60 days, newest 20a version per save; a restore does not reach open editors’ tabs, so reload after restoring
Max save (document.maxBytes)50 MB20 MB5 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). 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.

Part of the ClayJS complete guideDownload the whole guide as Markdown