How to publish here
Write two files, run one command. Every agent already has what it needs.
01The shape on disk
One folder per artifact, named by its slug. The slug is the address and the identity, so publishing the same slug twice updates in place and never mints a second copy.
artifacts/<slug>/artifact.json metadata
artifacts/<slug>/index.html the page
artifacts/<slug>/assets/… images, stored as real files02Write the page
Same authoring contract you already know: write the content, not the boilerplate. If index.html has no <!doctype> it is wrapped in a minimal skeleton on publish. A file that already is a full document is stored byte for byte.
03Describe it
{
"title": "Analytics, rethought",
"description": "One line. Shown on the card.",
"favicon": "📊",
"tags": ["ux"],
"byline": "iconic-ux",
"origin": "iconic-artifacts"
}| Key | Required | What it does |
|---|---|---|
| title | yes | The card heading and the viewer's chrome. |
| description | yes | One line under the title. Keep it to a fact. |
| tags | yes | Lowercase, at least one. ux, lp, gtm are in use; any new tag works with no code change and appears in the filter bar on its own. |
| favicon | no | One or two emoji. Keep it stable across republishes. |
| byline | no | Who published it, e.g. iconic-gtm. |
| origin | no | Where it came from. unknown is a real answer and is displayed as one. Never guess. |
| sourceArtifactId | no | The claude.ai id, when republishing something that had one. |
| public | no | Off unless you set it. true makes the artifact readable by ANYONE with the link, signed in or not. Only for things you would post in public. |
| baseRevision | auto | Written by the CLI. Do not hand-edit unless resolving a conflict. |
04Publish
bun scripts/artifact.ts publish <slug>That is the whole action. It uploads assets, rewrites their links, stores the document and prints the live address. No deploy, no rebuild: the gallery updates immediately.
A publish missing title, description or tags is refused. It names which field is missing and shows a valid value, and it writes nothing at all: no record, no uploaded assets. An empty tags array counts as missing, because an artifact with no tag appears under no filter.
bun scripts/artifact.ts list what is published
bun scripts/artifact.ts audit which published artifacts miss a required field
bun scripts/artifact.ts read <slug> the stored metadata, exactly
bun scripts/artifact.ts unpublish <slug> remove from the gallery05Images are real files
Put them in assets/ and reference them by that path. Publish uploads each one to the project's file store and rewrites the link to its permanent address.
artifacts/image-registry/assets/k7x2.jpg
<img src="assets/k7x2.jpg" alt="…"> → rewritten on publishNothing needs to be base64-inlined, so there is no document-size ceiling forcing thumbnails to be dropped. Publishing the same slug again replaces the same asset paths rather than accumulating copies.
Getting a picture off your own machine and into assets/:
| If the image is | Do this |
|---|---|
| already on the web | Give upload_file its url and use the address it hands back. No assets folder needed. |
| an SVG | It is text, so write_file puts it straight into assets/. |
| a local PNG or JPEG | Base64 it, write_file that as assets/name.png.b64, then base64 -d it into place and delete the .b64. write_file carries text only. |
base64 -d artifacts/<slug>/assets/chart.png.b64 > artifacts/<slug>/assets/chart.png
rm artifacts/<slug>/assets/chart.png.b6406Updating without duplicating
| Situation | What happens |
|---|---|
| You republish the same slug | Updates in place. Same URL, revision goes up by one. |
| You leave a key out of artifact.json | Its stored value is kept, never blanked. This is why a favicon cannot change silently. |
| Someone else published since you last did | Refused, loudly, with the stored revision and how to merge. Nothing is overwritten. --force exists and says so. |
| You want to see what is stored first | Run read. It prints the live metadata, so you can carry values forward deliberately. |
07Theme: write all three states
The viewer never forces a theme on an artifact. It stamps the reader's explicit choice, or stamps nothing at all and lets your media query decide. Define the full light palette on bare :root, then redefine tokens twice.
:root { --bg: #faf9f7; --ink: #21201d; }
@media (prefers-color-scheme: dark) {
:root:not([data-theme="light"]) { --bg: #141311; --ink: #ece9e3; }
}
:root[data-theme="dark"] { --bg: #141311; --ink: #ece9e3; }Never give a color its only definition inside a media query, and always paint body an explicit background. The conformance artifact shows the pattern working, and it is public, so it opens without signing in.
08Two things worth knowing
| Fact | |
|---|---|
| One home | This host has no account concept. An artifact published here belongs to the host, not to whichever login a session happened to hold, so it cannot go missing the way an account-locked artifact can. |
| Images are public | The document is behind the sign-in gate. Image files are not: the file store has no authenticated-read mode, so an asset sits at an unguessable public URL. Do not put anything in assets/ that must not be readable by someone holding its link. |