These guides are primarily for agent readers. They explain how agents use Sails. If you are a person, start at sails.app or Add Sails.

Publish a site

publish create binds one folder to one URL. You run it once. Autosave redeploys that same URL on every checkpoint. There is no build command.

A directory with sails.site.json at the mount root compiles as a static site (.page.html / .page.md). Any other folder is served as files. Tailwind CSS and TypeScript/TSX are transpiled on publish.

Done looks like

publish list shows a row whose public_url you can open. For a public site that URL returns 200 without sign-in. After you edit a page, the same URL shows the change. You did not create a second mount.

Worked example: public marketing site

Job: copy a template, put it on a public URL, confirm the URL, then ship a copy change without publishing again.

cp -r /templates/static-site-demo ./site
publish create site --policy 644

<name> is required and unique in the repo. When --path is omitted, the folder defaults to <name>, so this publishes ./site as mount site.

--policy 644 is public web. The default 600 is owner-only: everyone else is sent to /auth/login, then 403.

Expected output is JSON. Shape (not exact ids):

{
  "id": "…",
  "name": "site",
  "path": "site",
  "policy": "644",
  "public_url": "https://{id}.sails.app/"
}
publish list

Open public_url. That is the preview host. Then edit a page under site/pages/ and stop. Do not run publish create again. Do not run repo save as a ritual. Autosave already checkpointed; the mount tracks branch head.

See Static sites for the folder layout.

Who can publish

Actorcreate / update / delete--hostname / publish verify
Repository ownerAny path, including repo rootYes
write member / agentNon-root path they have Unix write on (and inside an agent jail)No
read memberNoNo

Agents can publish a preview URL for a folder they can write. Binding a custom hostname stays owner-only.

Flags

publish help
CommandWhat it does
publish create <name> [--path PATH] [--branch name] [--hostname host] [--policy MODE] [--mcp]Create the mount once. Path defaults to <name>.
publish listJSON of mounts, including public_url
publish update <id> [--name NAME] [--policy MODE]Rename or change policy. Cannot change hostname.
publish verify <id>DNS ownership check for a custom domain
publish analytics <id-or-name> [--since 7d] …Page views and custom events (any member can read)
publish delete <id>Permanently kills the URL. Do not use this to “refresh” a link.

--policy gates HTTP access to the whole mount (static files and *.api.sh). It does not rewrite per-file modes in the workspace.

PolicyWho can load the URL
644Anyone
640Owner or default-group members (others sign in, then 403)
600Owner only (default)

--mcp turns on Streamable HTTP MCP at /mcp for *.mcp.sh on that mount.

Custom hostname

Platform hosts (*.sails.app, *.localtest.me, *.localhost) always include the org slug. publish create crm --hostname crm binds {org}-crm.sails.app, not crm.sails.app. Those hosts are auto-verified.

Custom domains do not get the org prefix. The first host on a new domain needs a DNS TXT. After that, this org can publish further hosts on that domain without another TXT.

--hostname host/prefix (example: {org}.sails.app/admin) mounts under a path. The host root must already be published by this repository. You cannot hang a prefix on another repo’s host.

publish update cannot change --hostname. To move a custom domain, delete the old mount and publish create the new one. Keep at least one verified host on the domain if you want to skip a new TXT.

Path on a host you already publish

publish create guides --path sites/sails-guides --hostname sails.app/guides --policy 644

Publish the host root first, then the prefix. sails.app/guides is a path on a host this repo already owns.

Custom domain TXT

Do not invent a TXT value. Sails prints a one-time token.

publish create site --path ./site --hostname example.com --policy 644
publish list
publish verify <id>

Until verify succeeds, public_url is still {published-path-id}.sails.app. hostname already shows example.com. Unverified JSON includes hostname_verification_txt as _sails-challenge.example.com = <token>. Copy the token (the part after =).

DNS (zone example.com):

FieldValue
TypeTXT
Name / host_sails-challenge
Valuethe token only

Then point the hostname at Sails: CNAME example.comcname.sails.app. Apex domains often cannot CNAME; use ALIAS / ANAME / CNAME-flattening. publish verify checks ownership only — it does not wait for the CNAME.

After verify, public_url becomes https://example.com/ and hostname_verification_txt is omitted.

Analytics

HTML page views are recorded automatically. Unique visitors use a first-party sails_vid cookie. Known crawlers are excluded from headline counts.

publish analytics site --since 7d --granularity day

Custom events (name and value, both strings, max 128 characters):

navigator.sendBeacon('/sails/analytics/events', JSON.stringify({ name: 'signup', value: 'pro' }))

On a path-prefixed URL, POST /prefix/sails/analytics/events or POST /sails/analytics/events with a same-origin Referer. Do not count that POST as a page view.

--visitor / --exclude-visitor filter by sails_vid. --visitors-limit 0 omits the visitor list (use that on a public dashboard). In a published *.api.sh, security: definer queries as the publisher; security: invoker needs a signed-in member. publish create / update / delete / list / verify stay blocked inside published scripts.

Failure modes

What you seeWhat it meansWhat to do
Visitors hit /auth/login then 403Policy is 600 or 640Public sites need --policy 644. Members-only is 640.
Second publish create for the same folderYou tried to mint a “fresh” URLKeep the first mount. publish list is the URL.
publish delete then a new URLYou tore down the old linksOnly delete when the URL should die for good
Agent cannot --hostnameCustom hosts are owner-onlyOwner binds the host; agent can still publish a preview path
publish create denied for repo rootWrite members / agents cannot publish /Publish a subfolder they can write
Custom domain still shows *.sails.appTXT not verifiedCopy hostname_verification_txt, add the TXT, publish verify <id>
Prefix URL 404s on another repo’s hostPrefix only on a host this repo publishesPublish this repo’s host root first
You ran repo save “to deploy”UnnecessaryAutosave already updated the mount

See Publish for the command summary. Forms on the same mount: Put a form on a site.