# artifact.cafe docs

artifact.cafe publishes static HTML artifacts for review. The core loop is:

```sh
npx artifact-cafe@latest publish .
# share the review URL and collect guest comments
npx artifact-cafe@latest comments
# make changes, then publish v2 from the same folder
npx artifact-cafe@latest publish .
```

Each publish creates a new immutable version. Comments stay attached to the
version where they were made.

## For agents

Paste this into `CLAUDE.md`, `AGENTS.md`, or an agent instruction file:

```md
When you produce a reviewable HTML artifact, publish it with:
npx artifact-cafe@latest publish . --title "<descriptive title>" --json --no-open

Use the returned url as the review link. Keep working from the same folder so
.artifactcafe/config.json links future publishes to the same artifact.

Before revising, pull reviewer feedback with:
npx artifact-cafe@latest comments . --json --status open --version current

After changes, publish again from the same folder. Each publish creates a new
immutable version; do not edit old versions in place.
```

## CLI

### `publish`

```sh
npx artifact-cafe@latest publish [dir]
```

Options:

- `--title <title>` sets the artifact title.
- `--notes <text>` records a bullet-point changelog for this version (newline-separated), shown in the versions sidebar.
- `--entry <file>` selects the HTML entry file.
- `--expires-in <duration>` sets a temporary lifetime, such as `30m`, `1h`, or `7d`.
- `--artifact <id>` publishes to a specific existing artifact.
- `--password <password>` password-protects the artifact (min 6 characters).
- `--json` prints machine-readable output and does not open a browser.
- `--no-open` prints the review URL without opening it.

### `expiration`

```sh
npx artifact-cafe@latest expiration [dir] --in 7d --json
npx artifact-cafe@latest expiration [dir] --keep --json
```

Set expiry from server time, or clear the temporary deadline without creating
a version. `--artifact <id>` works without a linked folder. Authorize with the
publish token, a scoped agent token, or a managing account. `--keep` is the
**Keep permanently** action after **Set expiry** in the review toolbar's clock
panel and lifetime settings. Owned artifacts then have no automatic expiry;
anonymous retention still requires claiming. The link, versions, and comments
stay in place. The phone panel and actions fill the available width.

### `data`

```sh
npx artifact-cafe@latest data ls <collection> [dir] --json
npx artifact-cafe@latest data export [dir] --json
npx artifact-cafe@latest data clear <collection> [dir] --yes --json
```

List, export, or clear shared records on the linked, claimed artifact. All
accept `--json`; export always prints JSON. Clear requires `--yes`. Authorize
with an `ack_` account key (`artifact-cafe login`), an `ack_` key or scoped
`aat_` agent token in `ARTIFACT_CAFE_API_KEY`, or the linked publish token
(`ARTIFACT_CAFE_TOKEN` fallback).

### `comments`

```sh
npx artifact-cafe@latest comments [dir]
```

Options:

- `--status open | resolved | all` filters threads. Default: `open`.
- `--version current | all` filters versions. Default: `current`.
- `--json` prints the full export for agents.

### `protect`

```sh
npx artifact-cafe@latest protect [dir]
```

Sets or removes the password gate on the linked artifact.

Options:

- `--password <password>` sets or updates the gate password (min 6 characters).
- `--off` removes the password gate.
- `--json` prints machine-readable output.

### `open`

```sh
npx artifact-cafe@latest open [dir]
```

Prints and opens the linked artifact review URL.

Use `artifact-cafe login` for an account key, or the claim link from first publish to attach an anonymous artifact to an account.

## Publishing concepts

Artifacts are static files or folders. artifact.cafe does not execute uploaded SSR,
backend processes, cron jobs, or serverless functions. Shared records use the
bounded Artifact Data API below.

Entry file detection:

- `index.html` is used when it exists.
- If the folder root has exactly one HTML file, that file is used.
- With multiple root HTML files, pass `--entry file.html`.
- With no HTML entry, finalize generates a viewer for one file (image, PDF,
  CSV table, rendered Markdown, or download link) or a file listing for multiple files. Each
  file retains its direct URL. `publish ./report.pdf` uploads only that file
  and stores local config in its parent directory.
- `publish ./report.md` renders CommonMark/GFM with selectable text for comments.
  `.markdown` and `text/markdown` API uploads work too. Raw HTML is escaped;
  previews show up to 1 MiB and the full original remains downloadable.

The CLI skips repo metadata, dependency folders, `.artifactcafe/`, env files,
private keys, symlinks, unsafe paths, and zero-byte files.

Limits:

- Maximum file size: 100 MB.
- Maximum total artifact size: 250 MB.
- Maximum files per publish: 1,000.
- Publish sessions expire after 1 hour.
- Anonymous publishing is limited to 20 publishes per IP per day.
- Agent-token publishes are limited to 120 per workspace per hour, not per IP.
- Anonymous artifacts expire 24 hours after their last publish by default unless claimed; an explicit temporary deadline can be earlier.

Temporary lifetimes start at successful finalize. Ordinary republishes,
claims, comments, and moves preserve the explicit deadline. JSON returns
`autoDeleteAt` and effective `expiresAt`; agents share the effective expiry
with a timezone alongside the review link. At expiry, new access stops,
including historical versions and Artifact Data, and lists/search omit the
artifact. Background cleanup deletes every version, comment, file, and data
record. There is no restore window.

## Artifact Data

Give polls, guestbooks, and forms typed records shared across visitors. Data
belongs to the artifact and survives new version publishes. **Claimed artifacts
only**: publish with an account/agent key or claim it first; an unclaimed
artifact returns `403 account_required`. Guests can submit without accounts.

Declare `.artifact/data.json` beside the entry file:

```json
{
  "collections": {
    "entries": {
      "fields": { "message": { "type": "string", "required": true, "maxLength": 1000 } },
      "access": { "read": "public", "insert": "public" }
    }
  }
}
```

```html
<script src="/__ac/data.js"></script>
<script type="module">
  const { records } = await cafe.data.list("entries");
  await cafe.data.insert("entries", { message: "Hello!" });
</script>
```

The client calls `/__ac/data/*` on the artifact's own origin. Render submitted
values with `textContent`. See [Artifact Data](/docs/data) for access rules and
[limits](/docs/data#limits).

## Tokens and auth

First publish stores `.artifactcafe/config.json` in the published folder. It
contains the artifact id, review URL, API URL, and publish token needed for
future versions.

- The publish token is returned once by the API and is not printed by the CLI.
- The CLI auto-adds `.artifactcafe/` to an existing `.gitignore`.
- If the file is lost (or you don't want to check it in for CI), set
  `ARTIFACT_CAFE_TOKEN` in the environment — the CLI falls back to it when
  `.artifactcafe/config.json` is missing or doesn't cover `--artifact <id>`.
  `ARTIFACT_CAFE_URL` overrides the API URL the same way. If you don't have
  the token, publish a new artifact and share the new URL.
- The claim link is shown once on first publish and turns an anonymous artifact
  into an owned artifact after sign-in.

## Reviewing

Reviewers open the artifact link, enter a guest name, and leave feedback where
it belongs. Point comments attach to an element position. Text comments attach
to selected text. Threads can be resolved or reopened. The version switcher
keeps old feedback visible on the version it described.

Some links are password-protected — enter the password once when prompted and
the artifact stays unlocked for later visits. If an artifact looks unsafe, see
Security and trust below for how to report it.

## Security and trust

Artifact HTML is untrusted content. It renders on its own
`{slug}.artifactcafe.dev` origin inside a sandboxed iframe, separate from
the review app. Legacy `{slug}.artifact.cafe` URLs redirect there.

- Do not enter passwords, API keys, or private data into an artifact.
- Anonymous retention is 24 hours after the last publish unless claimed. Explicit temporary deadlines also apply to owned artifacts.
- Anonymous publishing is rate-limited by IP.
- Owners can password-protect an artifact so only reviewers with the password
  can open it.
- Report an unsafe artifact with `POST /api/v1/artifacts/{artifactId}/report`,
  or contact the person who sent you the link.

## Errors

API errors return `code`, `message`, optional `retryAfter`, and `docsUrl`.
Stable anchors live at `/docs/errors#<code>`.

- `invalid_request`: request body, params, query, or manifest is not valid.
- `unauthorized`: a guest identity, publish token, or signed-in owner is required.
- `forbidden`: the credential is valid, but it cannot perform this action.
- `not_found`: the route, artifact, version, session, or thread does not exist.
- `rate_limited`: a publish, comment, or report rate limit was hit; retry after
  `retryAfter` seconds.
- `payload_too_large`: a file or artifact exceeds publish size limits.
- `session_expired`: the publish session expired before finalize.
- `session_finalized`: the publish session was already finalized.
- `uploads_incomplete`: finalize ran before every required upload reached storage.
- `claim_token_invalid`: the claim link is not recognized.
- `claim_token_expired`: the claim link expired with the anonymous artifact window.
- `claim_token_used`: the claim link was already used.
- `already_claimed`: the artifact already has an owner.
- `slug_taken`: another artifact already uses that slug.
- `slug_reserved`: that slug is reserved by artifact.cafe.
- `password_required`: the artifact is password-protected and no valid unlock
  cookie, publish token, or owner session was present.
- `invalid_password`: the password submitted to unlock a protected artifact
  was incorrect.
- `internal_error`: the server failed unexpectedly.

## HTTP API

The CLI uses the same public `/api/v1` API that agents can call directly. Exact
schemas are available at `/api/docs`.

Key endpoints:

- `POST /api/v1/publish`
- `POST /api/v1/publish/{publishSessionId}/finalize`
- `GET /api/v1/artifacts/{artifactId}`
- `PATCH /api/v1/artifacts/{artifactId}` (owner)
- `DELETE /api/v1/artifacts/{artifactId}` (owner)
- `GET /api/v1/artifacts/{artifactId}/versions`
- `GET /api/v1/artifacts/{artifactId}/data` (owner: collections and counts)
- `GET /api/v1/artifacts/{artifactId}/data/{collection}` (owner: list)
- `POST /api/v1/artifacts/{artifactId}/data/{collection}` (owner: insert one record, or an array of up to 100 all-or-nothing)
- `DELETE /api/v1/artifacts/{artifactId}/data/{collection}` (owner: clear)
- `GET /api/v1/artifacts/{artifactId}/data/{collection}/{recordId}` (owner: read)
- `PATCH /api/v1/artifacts/{artifactId}/data/{collection}/{recordId}` (owner: update)
- `DELETE /api/v1/artifacts/{artifactId}/data/{collection}/{recordId}` (owner: delete)
- `PUT /api/v1/artifacts/{artifactId}/protection`
- `POST /api/v1/artifacts/{artifactId}/unlock`
- `POST /api/v1/artifacts/{artifactId}/report`
- `GET /api/v1/artifacts/{artifactId}/comments`
- `GET /api/v1/artifacts/{artifactId}/comments/export`
- `POST /api/v1/artifacts/{artifactId}/comments`
- `POST /api/v1/comment-threads/{threadId}/messages`
- `POST /api/v1/comment-threads/{threadId}/resolve`
- `POST /api/v1/comment-threads/{threadId}/reopen`
- `DELETE /api/v1/comment-threads/{threadId}` (owner)
- `POST /api/v1/claim/{token}`

## Boundary

artifact.cafe is for reviewable static artifacts, not production app hosting.
Backend hosting, private apps, secrets, login-gated reviewer flows, analytics,
templates, and self-hosting are not part of the product.
