# Open Artifacts > Open Artifacts is an open-source, self-hosted place to keep, version, and share what AI agents make: HTML pages, Markdown docs, code, and PDFs. The user deploys it to their own Cloudflare account and domain, and any MCP-capable agent publishes to it. You, the agent, can do the whole setup; the only step for the user is the Cloudflare login. ## Set up Open Artifacts for your user 1. **Check the prerequisites.** Run `node --version`, `pnpm --version`, and `git --version`. If anything is missing, or the user has no Cloudflare account or no domain on Cloudflare, explain what it is and how to get it, then wait. 2. **Clone the latest release.** Open Artifacts installs from the latest stable GitHub release, not `main` and not a pre-release. Release tags look like `vX.Y.Z`: ```sh REPO=https://github.com/leourbina/open-artifacts TAG=$(curl -fsSL https://api.github.com/repos/leourbina/open-artifacts/releases/latest | sed -n 's/.*"tag_name": *"\([^"]*\)".*/\1/p') git clone --depth 1 --branch "$TAG" "$REPO" open-artifacts cd open-artifacts ``` If `TAG` comes back empty or `curl` fails, stop and tell the user: "Open Artifacts has no stable release yet; check back later." 3. **Install dependencies:** `pnpm install`. 4. **Ask the user to log in to Cloudflare.** Ask them to run `npx wrangler login` and approve access in the browser. On a remote machine or in a container, use `npx wrangler login --device` instead. This is the one step a person has to do. 5. **Run setup.** Use the user's hostnames and email: ```sh pnpm -s oa:setup --json --app-host artifacts.example.com --content-host artifacts-content.example.com --owner-email you@example.com --name open-artifacts ``` `-s` keeps pnpm's own output off stdout, so every line is JSON. With `--json`, setup never prompts. Setup prints one JSON object per line, `{"step", "status", "message", "data"}`, where `status` is `ok`, `skipped`, `action_required`, or `error`. It stops at the first `action_required` or `error`. Then go by the exit code: - **0:** every step is done. Go on to step 6. - **2:** the last line is `action_required`. If its `message` asks you to pass a flag, such as `--account-id` when the login can reach several accounts, add that flag and run again; ask the user for the value if you don't know it. Otherwise relay the `message` to the user verbatim, with `data.url` if it has one, wait until they say it's done, and run the same command again. Setup saves its progress in `.openartifacts/state.json` and picks up where it stopped. - **1:** the last line is `error`. Show the user its `message` and stop. If the `access` step asks for an Access API token, the user creates one at its `data.url`. Ask them to run this in the terminal where setup runs and paste the token, which isn't shown or saved in shell history: ```sh read -rs OA_ACCESS_TOKEN; export OA_ACCESS_TOKEN ``` Then run the same command again. Never put the token on a command line, where shell history keeps it. Setup never saves it. Once Access is set up, runs without the token skip re-checking Access, as long as the app host and Cloudflare account stay the same. The token is needed again only to change the app host or account, or to re-check Access. 6. **Connect yourself.** The `owner-token` line's `data.token` is the owner's personal access token (`oa_pat_…`). It's shown only once, by the run that mints it, so keep it. The `agent-config` line's `data.url` is the MCP URL, and its `data.snippets` holds MCP configs for `claudeCode`, `codex`, `cursor`, `opencode`, and a generic `json`. They don't contain the token: most read it from the `OA_TOKEN` environment variable, and `json` has a `` placeholder to replace, in a file kept out of git. Add the one for *this* agent to your MCP config, and make the token available as `OA_TOKEN` to its client. If you've lost the token, run setup again with `--new-owner-token`, which mints a new one and revokes the old one. Connect agents shows each client's config format. 7. **Open the Welcome page.** Setup publishes a Welcome artifact to test the connection. Give the user its link, `data.welcomeUrl` on the `selftest` and `agent-config` lines, so they can sign in and see it. Only a run that mints the owner token publishes it; if there's no `welcomeUrl`, give the user `https:///` instead. ## Update an existing install 1. **Go to the existing install:** `cd` into the `open-artifacts` directory that setup ran from. If it's gone, for example on a new machine or in CI, clone the latest release as in [setup](https://open-artifacts.com/docs/setup.md) and continue from step 3. Without its saved state, add the install's `--name`, `--app-host`, `--content-host`, and `--owner-email` to the commands below; setup rebuilds the state from Cloudflare by `--name` and never guesses the name in a `--json` run. It also re-verifies Access, so set `OA_ACCESS_TOKEN` (as in setup) if the Cloudflare login can't read Access. 2. **Check out the latest release.** This is the latest stable GitHub release; pre-releases are skipped. Release tags look like `vX.Y.Z`: ```sh TAG=$(curl -fsSL https://api.github.com/repos/leourbina/open-artifacts/releases/latest | sed -n 's/.*"tag_name": *"\([^"]*\)".*/\1/p') git fetch --depth 1 origin tag "$TAG" git checkout "$TAG" ``` If `TAG` comes back empty or `curl` fails, stop and tell the user: "Open Artifacts has no stable release yet; check back later." 3. **Install dependencies:** `pnpm install`. 4. **Preview, if you like:** `pnpm -s oa:setup --upgrade --check --json` is read-only. It reports each step as `done` or `todo` without changing anything, so you can see what the upgrade would do. 5. **Run the upgrade:** `pnpm -s oa:setup --upgrade --json`. It reuses the hostnames and email saved by setup. Every step runs, but only `deploy` (new database migrations, then a redeploy) and `selftest` do work: `prereqs` and `resources` verify, and `access`, `secrets`, `owner-token`, and `agent-config` report `skipped` unless something is missing. It keeps the existing owner token and needs no `OA_ACCESS_TOKEN` when the state is saved. It never creates resources: if one is missing, it reports `action_required` and asks for a full setup. Go by the exit code, as in setup: - **0:** done. - **2:** relay the `action_required` line's `message` to the user verbatim, wait until they've done it, then run the same command again. - **1:** show the user the `error` line's `message`. If it says to rerun setup without `--upgrade`, run `pnpm -s oa:setup --json` (it may ask for `OA_ACCESS_TOKEN` again), then the upgrade again. 6. **Tell the user** which version is now running: the `deploy` line's `data.deployedVersion`. ## Connect another agent 1. **Pick the auth method.** Use OAuth if this agent's client supports MCP OAuth. Otherwise ask the user to create a personal access token in the dashboard under **Settings → Agents**. Don't run setup with `--new-owner-token` for this: it revokes the token the first agent uses. 2. **Add the server** to this agent's MCP config, using the snippet for its client from the Connect agents doc. Name the server after setup's `--name`, which defaults to `open-artifacts`. 3. **Check the connection** by calling `list_artifacts`. ## Tools - `publish_artifact`: Publish an artifact, or a new version when `id` is given. Pass `content` (one file, with `format`) or `files` (path → text, or { base64 } for binary, or { uploadId } from create_upload) plus `entry`. Inline payloads (content, text and base64 together) are limited to 256 KB; for larger files call create_upload first, then pass { uploadId } in `files`. Returns id, version and view URL. - `create_upload`: Get a signed URL to PUT one large file (over the 256 KB inline limit, up to 25 MB), then publish_artifact with `id` = the returned artifactId and `files: { "": { uploadId } }`. Omit artifactId to reserve a new artifact id; reuse it for the artifact's other uploads. - `get_artifact`: Read an artifact's metadata and one version (latest by default). Returns a file's text inline when it is text and at most 1 MB; otherwise a short-lived downloadUrl. - `list_artifacts`: List artifacts you can see, newest first. Filter by scope, format, visibility, owner, agent or updatedSince; page with cursor. - `search_artifacts`: Full-text search artifact titles, descriptions and content. Returns matches with snippets; page with cursor. - `list_versions`: List an artifact's versions, newest first. - `restore_version`: Restore an earlier version by publishing a copy of it as the new latest version. - `share_artifact`: Share an artifact with an email address as viewer or editor. Owner only. - `unshare_artifact`: Remove an email address's share. Owner only. - `set_visibility`: Set who can open an artifact: private (you and shares), team (all members) or public (anyone with the link). Owner only. - `delete_artifact`: Delete an artifact. It can be restored from the dashboard for 30 days. Owner only. - `artifact_data_get`: Read one document from an artifact's live-page data collection. - `artifact_data_list`: List documents in an artifact's live-page data collection; page with cursor. - `artifact_data_query`: Query an artifact's live-page data collection with equality `where`, `orderBy`, `order` and `limit`. - `artifact_data_set`: Create or replace a document in an artifact's live-page data collection. Anyone who can view the artifact may write (as on the page itself). - `artifact_data_update`: Merge top-level fields into a document in an artifact's live-page data collection. Anyone who can view the artifact may write (as on the page itself). - `artifact_data_delete`: Delete a document from an artifact's live-page data collection. Anyone who can view the artifact may write (as on the page itself). Inline `content` and `files` are capped at 256 KB in total. Send anything larger as an upload, one file at a time. A file can be up to 25 MB, and one publish up to 100 MB. 1. **Reserve the upload:** call `create_upload` with the file's `path` in the artifact, its exact `size` in bytes, and optionally its `contentType` (by default, it's taken from the extension). Pass `artifactId` to add to an existing artifact you can edit; leave it out to reserve a new artifact id. It returns `uploadId`, `artifactId`, a signed `url`, and `expiresAt`, 1 hour from now. 2. **Send the bytes:** a raw `PUT` of the file to `url`, with no auth header (the URL is signed) and a `Content-Length` equal to the declared `size`. Each upload takes one PUT; it answers `204` when it's done. ```sh curl -fsS -T report.pdf "$UPLOAD_URL" ``` 3. **Publish:** call `publish_artifact` with `id` set to the returned `artifactId`, and the file in `files` under the same path as `{ "uploadId": "…" }`. A new artifact also needs a `title`. Uploaded and inline files can be mixed in one publish. ```json { "id": "", "title": "Q3 report", "files": { "report.pdf": { "uploadId": "" } } } ``` An upload is used once. One that's never published is deleted by the daily cleanup once it's older than 24 hours. Search indexes each publish asynchronously, so a new version becomes searchable with `search_artifacts` within about 10 seconds of being published. Until then, search matches the previous version's text. Only the first 64 KB of an artifact's entry file is indexed; text past that isn't searchable. To find something you just published, use `get_artifact` or `list_artifacts`. ## Docs - [Set up Open Artifacts](https://open-artifacts.com/docs/setup.md): Deploy your own instance to Cloudflare. Your agent does every step except the Cloudflare login. - [Connect agents](https://open-artifacts.com/docs/connect-agents.md): Add your instance to Claude Code, Codex CLI, Cursor, OpenCode, or any MCP client. - [MCP tools](https://open-artifacts.com/docs/mcp-tools.md): The tools every instance's MCP server exposes, and what each one takes. - [Runtime API](https://open-artifacts.com/docs/runtime-api.md): Let HTML artifacts save and read data with window.openArtifacts. - [Security model](https://open-artifacts.com/docs/security.md): How Open Artifacts isolates untrusted artifacts and decides who sees what. - [Upgrade](https://open-artifacts.com/docs/upgrade.md): Move an existing instance to the latest release without losing data. - [Self-hosting costs](https://open-artifacts.com/docs/self-hosting-costs.md): What an instance costs on Cloudflare's free and paid plans. - [All docs in one file](https://open-artifacts.com/llms-full.txt)