Docs
Connect a site to Tusk.
Your site keeps its code, host and build. You add one attribute per editable thing, Tusk builds the editor from it, and your build pulls the published words and photos. Nothing of Tusk runs between a visitor and the site.
Connect a site in five minutes
1. Mark the elements. Put data-tusk="page.field" on the element whose content the client edits. Tusk infers the kind from the tag; set data-tusk-kind when it guesses wrong.
<body data-tusk-page="Home">
<h1 data-tusk="home.headline">A small studio.</h1> text
<p data-tusk="home.intro">Thoughtful spaces, made for everyday
life. We work with families and small businesses.</p> textarea
<img data-tusk="home.hero" data-tusk-label="Hero photo"
data-tusk-aspect="16:9" src="/img/hero.jpg" alt="A bright room"> photo
<a data-tusk="home.brochure" href="/downloads/capability.pdf">
Capability brochure</a> file
<div data-tusk-list="home.team" data-tusk-label="Team" data-tusk-max="8">
<article data-tusk-item>
<img data-tusk-field="photo" data-tusk-aspect="1:1" src="/img/mia.jpg" alt="">
<h3 data-tusk-field="name">Mia Chen</h3>
<p data-tusk-field="bio" data-tusk-kind="textarea">Mia set up the studio in 2019.</p>
</article>
<article data-tusk-item>…</article>
</div> listOne field, one element. Never nest a mark inside another mark, and never mark the same field twice on a page. Text that appears on every page, like a footer line, goes under the page key globals and is marked once. Leave navigation, form controls and legal text alone.
2. Deploy the site as you normally would. Tusk reads the rendered HTML, so the marks must be in what the server sends.
3. Scan. In Tusk, add the site, open its manage page and paste the page addresses into Scan for editable fields. Tusk fetches each page, reads the marked elements, builds the editor and takes the words already on the page as starting content. A rescan refreshes pages nobody has published yet; anything a client has published is kept. The same panel lets you add pages and fields by hand.
4. Invite the client. Give a client access on the manage page sends an invitation link. They choose a password, land in the editor on their site, and see only the fields you marked.
To check the markup before you scan, run npx tusk-connect check against the page addresses. It lists every mark, then errors (duplicate keys, nested marks, unknown kinds) and warnings (missing labels, images without alt, elements that look editable but are unmarked).
Let your AI tool do the marking
If the site was built in Claude Code, Cursor, Lovable or any other agent, let the agent add the attributes. Open the site in Tusk, then Manage > Connect with your AI tool and press Copy. The instruction is generated for that site, so the keys the agent writes into the HTML are the keys Tusk already holds.
It tells the agent to add attributes only, never change layout, styles, markup order or wording; how to mark each kind, lists and site-wide text; the pages and fields Tusk already has for the site, with their exact keys; to check the work with npx tusk-connect check and write .tusk/schema.json with npx tusk-connect init; and how the build pulls content once the pages are scanned. It never contains the build token, only where the token lives. The same schema is available as JSON at GET /api/studio/sites/:slug/schema while signed in.
Get content into your build
Every publish writes a snapshot. Your build takes a copy before it compiles, and the finished site serves its own words and photos. Three ways to take that copy.
The SDK: tusk pull
tusk pull downloads the snapshot to .tusk/published.json and every photo to public/tusk/, rewriting the photo URLs to /tusk/<filename>. Run it as prebuild with three variables set on the host, never in the repo. The build token is under Publishing on the manage page.
TUSK_SITE=north-studio
TUSK_TOKEN=<build token, from Manage > Publishing>
TUSK_URL=https://tuskcms.com
{ "scripts": { "prebuild": "tusk pull", "build": "next build" } }Then read the file in your templates. No network at render time.
import { load, field, paragraphs, photo, photos, file } from '@tuskcms/sdk'
const site = await load() // .tusk/published.json
field(site, 'home.headline', 'Welcome') // a string, with a fallback
paragraphs(site, 'home.intro') // a textarea split on blank lines
photo(site, 'home.hero')?.hero // '/tusk/hero-1600x900.webp'
photos(site, 'home.team') // [{ url, thumb, card, hero, alt }, …]
file(site, 'home.brochure')?.url // '/tusk/capability.pdf'The packages are not on npm yet. Clone the Tusk repository, run npx tsc -p . in packages/tusk-sdk and packages/tusk-connect, and run them from dist/ or link them into your project.
The raw build feed
If you would rather fetch it yourself, the feed is one request with the site’s build token as a Bearer token. /snapshot returns the stored copy from the last publish; add ?assets=1 for the list of files to download and ?history=1 for the kept versions. /asset/:id serves one photo by its token, with ?size=thumb|card|hero.
curl -H "Authorization: Bearer $TUSK_TOKEN" \
https://tuskcms.com/api/published/north-studio/snapshot?assets=1
# the live feed, and one photo at hero size
curl -H "Authorization: Bearer $TUSK_TOKEN" https://tuskcms.com/api/published/north-studio
curl -H "Authorization: Bearer $TUSK_TOKEN" -o hero.webp \
"https://tuskcms.com/api/published/north-studio/asset/12?size=hero"The JSON has site, publishedAt, pages keyed by page key (each with content) and media keyed by id. Photo fields hold media ids; list fields hold arrays of entries.
GitHub push mode
The other way round: Tusk commits to your repository on every publish and your host’s ordinary git deploy does the rest. The commit holds .tusk/published.json and any new files under public/tusk/, the same layout tusk pull writes, so the SDK reads it unchanged and the build needs no token and no request to Tusk.
Install the Tusk CMS GitHub App on the site’s repository, only that repository. The installation’s settings address ends in a number; that is the installation id. On the manage page under Publish to GitHub enter the owner, repository, branch (default main), path (default .tusk/published.json) and the installation id, press Test, then publish. A push failure never fails a publish; it shows on the site’s activity with the reason.
What clients can and cannot do
- Change any text you marked, one line or paragraphs.
- Replace photos, with a crop at the aspect you set.
- Swap a download for a new PDF or document.
- Add, remove and reorder entries in a list, up to your maximum.
- Open History and restore an earlier publish as a draft.
- Save drafts as they go and press Publish when ready.
- Move, add or remove sections, or change the layout.
- Touch styles, classes, scripts or any code.
- Edit navigation, forms or anything you did not mark.
- See your repository, host, deploy hook or build token.
- See any site they were not invited to.
Plans, trial, what happens if you stop paying
Developer is US$49 a month for two live sites and Agency is US$149 for ten, with every extra live site US$15; client seats are unlimited on both. Every studio starts with a fourteen-day trial and no card. If you stop paying, editing pauses for you and your clients, but the site keeps serving the last published content from your own hosting, because nothing of Tusk runs in front of it. You can export every site as a zip at any time, and the export includes the snapshot, the photos and a note on how to read them.
Attribute reference
| Attribute | Where | What it does |
|---|---|---|
data-tusk="page.field" | The editable element | page.field, or a bare field for the page the URL implies (/ is home, /about/ is about). Letters, digits, underscore. Site-wide text uses the page key globals. |
data-tusk-label | Any marked element | The name the client sees in the editor. Without it Tusk humanises the field id. |
data-tusk-kind | Any marked element | text, textarea, photos or file. Overrides the guess: photos for img, file for a link to .pdf/.docx/.xlsx/.zip, textarea for p tags or over 140 characters, text otherwise. Inside a list: text, textarea or photo. |
data-tusk-page | body | Names the page and sets its key when the URL is not enough. |
data-tusk-aspect | A photo | The crop the editor suggests, as w:h, for example 16:9 or 1:1. |
data-tusk-list="page.field" | The wrapper of a repeated group | One list field. Anything marked inside it belongs to the list. |
data-tusk-item | Each repeated element | One entry. The client can add, remove and reorder entries. |
data-tusk-field="name" | A part inside an item | A sub-field. The spec comes from the first item, the content from every item. |
data-tusk-max | The list wrapper | The most entries a client may have. |
Reserved field ids: id, key, kind, title, url, order, state, portal, updatedAt, tenant. A complete marked page is at /examples/marked-site.html.