Publishing & versions
A publish takes an HTML file and stores a frozen artifact served at a URL. This page covers the model behind that: versions, slugs, visibility, and what actually gets uploaded.
Immutable versions
Every publish and every update creates one new immutable version. Prior versions are frozen and retained; nothing is overwritten in place. A publish mints a new page (version 1) at a fresh URL. An update adds a version to an existing page and keeps the same URL.
emit publish ./page.html # -> page, v1, new URL
emit update pg_abc123 ./page.html # -> same URL, v2
Use emit get <id> to see a page's full version
history.
Idempotency
Both publish and update accept --idempotency-key. A retry with the same key
returns the same result instead of creating a duplicate version, so an agent
can safely re-send a request after a network hiccup:
emit publish ./page.html --idempotency-key launch-2026-08-14
Slugs and URLs
Free pages serve at a per-page subdomain on emits.page:
https://<slug>.emits.page
Pass --domain <slug> to choose the slug; omit it and one is assigned. If the
slug you want is taken, publish returns a 409 and the CLI prints the id of
the page already holding it, plus the update command to reuse it.
On Pro, a bound custom domain also serves every page at your-domain/<slug>.
See custom domains.
Visibility
Every page is public, unlisted, or private. Set it at publish time with
--visibility, or change it later without republishing:
emit publish ./page.html --visibility private
emit visibility pg_abc123 public
- public: served to anyone and discoverable.
- unlisted: served to anyone with the URL, not discoverable.
- private: not served publicly.
Tags
Attach tags at publish time (--tag, repeatable) and filter on them in find.
Tags are how an agent groups and re-locates its own pages:
emit publish ./page.html --tag launch --tag q3
emit find --tag launch
Multi-page publishes
A single publish can ship more than one page. Pass --bundle and point it at
an entry file; the CLI publishes that file's whole directory as one linked unit
under a single slug:
emit publish ./site/index.html --bundle --domain handbook
- The entry file (
index.htmlabove) serves at the slug root:https://handbook.emits.page. - Every other
.htmlfile in the directory serves at its authored path:site/about.htmlbecomeshttps://handbook.emits.page/about.html,site/docs/guide.htmlbecomes.../docs/guide.html. - Links between pages are ordinary relative links (
<a href="about.html">,<a href="../index.html">). They resolve exactly as written, because each file is served at the path you authored it at. No rewriting, no absolute URLs.
The whole unit is one page as far as the rest of Emits is concerned: it has one slug, one visibility, one version, and a takedown or delete affects all of its pages together.
A few constraints for this first release:
.htmlfiles only. CSS, JS, and images in the directory are not bundled yet; style with inline CSS or a CDN for now.- Relative links only. A root-absolute link (
/about.html) is treated as a site-root link, not a bundle link. - Re-publishing updates in place. Publishing a bundle to a slug your account already owns updates it: the entry keeps its URL, a new immutable version is appended (prior versions retained), and added/removed sub-pages are reflected. A slug held by a non-bundle page, or by a deleted or quarantined one, still 409s.
- Caps: up to 2000 files and 50 MB per bundle.
How recipes travel with a publish
Most pages are plain HTML, uploaded and served untouched. This section is only
for pages authored with the Shortwind class
layer's @recipe shorthand. When you publish such a page from a project that
has a recipes/ directory, Emits expands the shorthand to Tailwind server-side
at publish time, and the publish carries what the expander needs: your html,
the lockfile pinning recipe versions, and only the recipe families the page
actually touches. The CLI assembles this for you by walking up from the current
directory to find the project. A recipe that is missing expands to nothing and
goes out as raw text, so compose only from recipes you actually have. Published
pages stay frozen against the versions they shipped with.
Finding and removing pages
emit find --q "launch" # search your pages
emit get pg_abc123 # metadata + version list
emit delete pg_abc123 # tombstone the page (prompts unless -y)
Deleting tombstones the page so it stops resolving. See trust & safety for how abuse takedowns differ from a user delete.