# artifact.cafe
The home for AI-generated work. Agents publish interactive **static** artifacts
(HTML apps, dashboards, mockups, reports, slide decks) to a review URL; humans
open the link with **no login** and comment directly — pinning feedback to
elements and text; the author publishes new **immutable** versions. One loop:
```
publish a folder → review URL → guest comments (no login) → publish v2
```
## When to use
- You built something visual/interactive and want human **feedback** on it.
- You need a **shareable link** a non-technical reviewer can open and mark up.
- You're iterating: publish, pull comments, publish the next version.
## When not to use
- General file hosting with no review loop, or a backend/app host.
- Anything needing SSR, API routes, a database, or server runtime — artifacts
are static only.
## Install (for agents)
```bash
npx skills add artifact-cafe/skill --skill artifact-cafe -g # with npm
curl -fsSL https://artifact.cafe/install.sh | bash # without npm
```
The skill wraps the `artifact-cafe` npm CLI (preferred) and bundles a zero-Node
bash fallback. Nothing to install ahead of time if you have `npx`.
## Publish
```bash
npx artifact-cafe@latest publish ./folder --title "Onboarding redesign"
npx artifact-cafe@latest publish ./folder --workspace acme --folder specs
```
- The folder needs an entry `index.html` at its root (or pass `--entry`).
Assets (CSS, JS, images, fonts) upload alongside it.
- **Always set `--title`.** Generate a concise, descriptive title from the
artifact's content — it's the review-page heading **and** the readable
review-URL slug (`/a/{slug}`). Without one you get "Untitled artifact".
- **Set `--notes` on every publish** — a newline-separated bullet-point
changelog of what changed in this version, shown in the versions sidebar.
- **The entry HTML owns its share card.** `` (or
`og:description`), ``, and ``
(or `article:author`) are read at publish and become the preview a pasted
link unfurls with on X, Slack, Telegram, and Discord. Worth setting for
anything written to be read rather than reviewed. `og:image` must be a file
in the folder you publish (`og.png`, `/assets/og.png`); remote URLs and
`data:` are refused, and a path that isn't in the manifest, isn't
PNG/JPEG/WebP/GIF, or is over 5 MB falls back to the generated card. Every
version gets its own preview URL. `--description` overrides the document's
own tag. Gated artifacts (password, workspace, private) always unfurl as
the locked card and leak none of it.
- `--json` gives machine-readable output. `--no-open` skips the browser.
- `--review off` publishes a plain artifact — no review tools for anyone.
`--review readonly` keeps existing comments readable and refuses new ones.
Leave it open by default; getting comments back is the point. Changeable
later with `artifact-cafe review `, publish token or
account. Existing comments are kept either way.
- Workspace publishing requires `artifact-cafe login` (or `--api-key`). The
workspace/folder placement is persisted in `.artifactcafe/config.json`, so a
later bare publish creates the next version in the same place. Use
`artifact-cafe workspaces --json` to list memberships and
`artifact-cafe folders --workspace --json` to discover folders.
- Keep the library organized: file into a matching folder at publish time
(`--folder` takes an exact name or id), create one with
`artifact-cafe folders create --workspace ` when a real group
exists, re-file later with `artifact-cafe move --folder ` (no new
version), and see everything with `artifact-cafe list --workspace
--json` (artifacts with `folderId`, plus the folder list). Placing an
artifact into a folder applies the folder's visibility as its default — a
public folder makes it public, so confirm with the user first.
- Never guess a destination from the artifact's content. For a new artifact,
explicit `--workspace`/`--folder` wins, then the nearest
`artifact-cafe.json` project default. With neither, agent and `--json` runs
publish to Personal when signed in and stay anonymous otherwise. Existing
linked artifacts always republish in place. Read `destination`,
`destinationSource`, and `visibility` from JSON output and report them. An
artifact inherits its folder's visibility, so a `--folder` publish can make
the review link members-only — say so instead of calling it a public link.
A commit-safe project target contains no secret and is excluded from uploads:
```json
{
"workspace": "acme",
"folder": "specs"
}
```
Project workspace defaults require the preferred npm CLI. The no-Node bash
fallback refuses a new publish when it finds this file rather than silently
publishing to another destination.
Pipeline (same for the CLI and the bash fallback): scan folder + ignore rules →
build manifest with `sha256_` content hashes → `POST /api/v1/publish` (returns
presigned PUT URLs for new blobs only; unchanged files are deduped) → upload →
`POST /api/v1/publish/{id}/finalize` → review URL.
## Versions, auth, and claiming
- A first publish with no account creates an **anonymous** artifact that
**expires in 24h** and returns a one-time **claim URL** (surface it to the
user immediately — it's shown once). Claiming keeps it permanently.
- The first finalize also returns a one-time **publish token**, stored in
`./.artifactcafe/config.json` (chmod 600, auto-gitignored). It authorizes
future versions of that artifact. Never print, log, or commit it.
- Publish a new **immutable** version by re-running publish from the same
folder. Comments stay attached to the version they were made on.
- Anonymous first publish needs no auth. Signed-in publishes land in the
account's personal workspace (claiming moves an anonymous artifact there
too). New versions of a personal-workspace artifact accept the publish
token or owner auth; team-workspace versions require a current member
session or `ack_` key so every version records its publisher.
## Password protection
An artifact can be gated with a shared password so only reviewers who know it
can open the link:
```bash
npx artifact-cafe@latest publish ./folder --password "" # set on publish
npx artifact-cafe@latest protect --password "" # set/update later
npx artifact-cafe@latest protect --off # remove the gate
```
Backed by `PUT /api/v1/artifacts/{artifactId}/protection` (publish token or
owner) and `POST /api/v1/artifacts/{artifactId}/unlock` (public — a guest
proving they know the password). A gated request without a valid unlock
cookie, publish token, or owner session gets `401 password_required`.
## Pull review comments (agent read side)
```bash
npx artifact-cafe@latest comments --status open --version current # or --json
```
Backed by `GET /api/v1/artifacts/{artifactId}/comments/export`. Each thread
carries author, version, anchor (quoted text span or element path), body,
replies, and emoji reaction counts. Reviewers can also drop emoji directly on
the artifact; those arrive as point comments whose body is the emoji.
Address feedback, then publish the next version.
## Reply to and resolve comments (agent write side)
```bash
npx artifact-cafe@latest ack # "seen, working on it" — thread stays open
npx artifact-cafe@latest reply --body "Fixed in v2 — …" # --resolve to close too
npx artifact-cafe@latest resolve # reopen to undo; --json
```
The thread id is each thread's `id` from `comments --json`. When you pick up a
thread, `ack` marks it acknowledged so the reviewer sees it's being handled
(it keeps appearing in `--status open` until resolved). After you publish a
version that addresses it, `reply` posts what changed back into it (a
publish-token reply renders as the agent, named from the version's source
agent), and `resolve` closes it so the reviewer sees it done — or pass
`--resolve` to `reply` to do both. `--body` carries the text, or pipe it on
stdin. Backed by `POST /api/v1/comment-threads/{threadId}/acknowledge`,
`.../messages`, and `.../resolve`, authorized by the artifact's publish token
or the owner's account key (the same credential that runs `publish`).
To **open** a new thread (not just reply), use `comment` — it authors as your
account, so it needs a signed-in account key, not a publish token:
```bash
npx artifact-cafe@latest comment --quote "Where AI work lives" --body "Too vague."
npx artifact-cafe@latest comment --body "Reads well overall." # page-level, no --quote
```
`--as "Claude Code"` labels the comment after your account name (detected from
the environment when omitted; `--via` is the older name for the flag). The same
flag on `reply` names the agent itself — a publish-token reply reads
"Yulong's Claude", joining the label you send to the artifact's owner.
`edit ` / `delete ` revise or soft-delete a message you
wrote (ids come from `comments --json`: a thread's `messageId`, a reply's
`id`). Backed by `POST /api/v1/artifacts/{artifactId}/comments` and
`PATCH`/`DELETE /api/v1/comment-messages/{messageId}`.
## Optional live editing mode
Publishing does not start a live session. After sharing the review URL, ask the
user whether they want to enter live editing mode. Only after an explicit yes,
run this command from the linked local artifact folder:
```bash
npx artifact-cafe@latest listen --json --timeout 540
```
`listen` is `comments --wait` named for what it does; it does not run a local
server or auto-republish. While it runs, the review page shows **Agent is
listening** and new comments
return with their anchors. Revise locally — run `artifact-cafe status editing`
during long revisions so the page shows **Agent is editing** — and publish a
new immutable version; publishing shows **Agent is publishing** and open
review pages follow the new version live. If the command times out, ask
whether the user wants another listening window; do not renew it
automatically. Guests comment asynchronously without an active local session,
unless the author has closed review on the artifact (`--review readonly|off`).
## Reading an artifact as Markdown (agent read side)
Review URLs (`/a/{slug}`) render as a client-side shell — opaque if you just
fetch the HTML. To read an artifact's content as Markdown instead:
- **Append `.md`:** `https://artifact.cafe/a/{slug}.md` returns the entry
document as Markdown with a small YAML frontmatter block (title, slug,
version, the HTML URL, the review URL).
- **Or negotiate:** send `Accept: text/markdown` on `/a/{slug}` (or on the
render origin `https://{slug}.artifactcafe.dev/`) and you get Markdown back.
Both forms redirect to the render origin, where the bytes live.
- Deep pages work too: `{slug}.artifactcafe.dev/v{n}/{path}.md` renders that
page of that immutable version.
If the artifact is **password-protected**, an unauthenticated Markdown/JSON
request returns `401 password_required` with instructions: ask your user for
the password, then retry the same URL with `?ac_pw=` appended.
Never guess passwords. If the artifact has **workspace** or **private**
visibility, the same request returns `401 workspace_required`. For
`workspace`, any member can read it; for `private`, only the workspace's
owners/admins, whoever published it, and specifically-invited people can. Retry
with the API key of an account that has access (`Authorization: Bearer
ack_...`); if you don't have one, ask your user to share the artifact with your
account. Markdown renditions are `noindex` and do not count as human views.
## API (all under /api/v1)
```
POST /publish create a publish session (presigned upload URLs)
POST /publish/{publishSessionId}/finalize activate the version; first publish returns one-time publishToken + claimUrl
GET /artifacts/{artifactId} artifact metadata (slug, current version, claimed)
GET /artifacts/{artifactId}/versions list versions
PATCH /artifacts/{artifactId} update (owner)
DELETE /artifacts/{artifactId} delete (owner)
PUT /artifacts/{artifactId}/protection set or clear the password gate (publish token or owner)
POST /artifacts/{artifactId}/unlock unlock a protected artifact (public, verifies password)
PUT /artifacts/{artifactId}/review-mode set review mode: open | readonly | off (publish token or a manager)
GET /artifacts/{artifactId}/grants list per-person grants (publish token or a manager)
POST /artifacts/{artifactId}/grants share with a person by {email|userId}; invitation URL returned once
DELETE /artifacts/{artifactId}/grants/{grantId} revoke a grant
POST /shared/accept redeem an invitation link while signed in
POST /artifacts/{artifactId}/report report an artifact for abuse (public, rate-limited)
POST /claim/{token} claim an anonymous artifact
GET /artifacts/{artifactId}/comments list comment threads
POST /artifacts/{artifactId}/comments open a thread (account or guest; a bare publish token is rejected)
POST /comment-threads/{threadId}/messages reply to a thread (guest, owner, or agent via publish token)
PATCH /comment-messages/{messageId} edit a message you authored
DELETE /comment-messages/{messageId} soft-delete a message you authored (tombstone)
POST /comment-threads/{threadId}/resolve resolve a thread (publish token or owner)
POST /comment-threads/{threadId}/reopen reopen a thread (publish token or owner)
DELETE /comment-threads/{threadId} delete a thread (owner)
GET /artifacts/{artifactId}/comments/export pull comments for an agent (status, version filters)
```
Comment threads are opened by human reviewers through the web review UI, or by a
signed-in agent via `comment` (account credential only — publish tokens stay
reply-only). Agents mainly use `publish`, `finalize`, `comments/export`, and —
to answer feedback — `comment-threads/{id}/messages` (reply) and `.../resolve`.
## Limits
- 100 MB per file, 250 MB per artifact total.
- 15 publishes per IP per hour and 20 per day (anonymous); sign in to raise this.
- Anonymous artifacts expire 24h after their last publish unless claimed.
- Ignored automatically: `.git*`, `node_modules`, `.env*`, `*.pem`, `*.key`,
`.DS_Store`, `.artifactcafe/`, symlinks, and zero-byte files.
## Constraints and safety
- Static hosting only — no SSR, no API routes for artifacts, no backend runtime.
- Artifact HTML is untrusted: it renders only in a sandboxed iframe on its own
isolated origin (`{slug}.artifactcafe.dev`), never on the main site.
- Don't publish real secrets or credentials.
## Acceptable use — do not publish violating content
The publishing agent must refuse to publish, and human publishers must not
upload, artifacts containing: phishing / credential-harvesting pages; malware or
malicious/obfuscated code; hate or harassment; sexual content (content
sexualizing minors is an absolute hard stop); graphic violence, threats, or
incitement; spam, scams, or fraud; or other illegal content. Publishing such
content is a policy violation and will be rejected. Enforcement is agent-side
today; automated moderation at publish time (rejecting the version before it
goes live) is planned.
## Errors
Structured JSON: `{ "code", "message", "retryAfter", "docsUrl" }`. On `429`
(`rate_limited`) honor `retryAfter` (seconds). `401 unauthorized` on a version
publish means a missing/invalid publish token. `401 password_required` means
the artifact is gated — unlock it first.
## Reference
- OpenAPI spec: https://artifact.cafe/api/v1/openapi.json
- Interactive API reference: https://artifact.cafe/api/docs
- Error code reference: https://artifact.cafe/docs/errors
- Installer: https://artifact.cafe/install.sh
- Skill source: https://github.com/artifact-cafe/skill
- This file: https://artifact.cafe/llms.txt