Skip to content

Releases: anvilnine/artifacts

v1.3.0 — duplicate artifacts, markdown render controls, Anvil Nine look

Choose a tag to compare

@kuyazee kuyazee released this 23 Jul 17:13
245eeef

Highlights

Duplicate an artifact

Copy any artifact into a new one that keeps all of its setup. A new Duplicate... item in
each artifact's overflow menu opens the compose form pre-filled from the source: the title is
set to {name} (n) (auto-incrementing, e.g. anvilnine (1)), the slug to a derived
anvilnine-1, and the frame, visibility, tags, and project are carried over. Every field stays
editable before you create the copy.

  • Works for inline artifacts (html/jsx/tsx/md) and zip sites on one server path,
    POST /api/artifacts/:slug/duplicate. Any field the request omits inherits from the source.
  • The content is copied as-is; to change a copy's content, edit it after.
  • The view password is never copied (it is stored hashed) — a copy set to password visibility
    asks for a new one.

Markdown render controls

Global render config for markdown artifacts: reading font (system/serif/mono), content width,
font size, and default theme (auto/light/dark). Markdown renders at serve time, so changing the
global config applies to every markdown artifact live with no re-publish. The viewer frame gets
a light/dark/auto theme toggle. Inline code no longer clips at the edges.

Anvil Nine look

The dashboard and the viewer shells (frame, jsx, md, password) now carry the Anvil Nine brand.

Dashboard UX

A tag chip editor and a project combobox on the compose and edit forms, list and grid view
modes, and inline menus/dialogs with optimistic updates.

Docs

New setup guides: preview deploys from CI (GitHub Actions), and three storage backends
(Coolify + MinIO, Forgejo, GitHub). README screenshots refreshed.

Upgrade notes

Backward compatible, no migration. Duplicate is available to any key with the publish scope.
Markdown render config has sensible defaults, so existing markdown artifacts render unchanged
until you set a global preference.

Full changelog: v1.2.0...v1.3.0

v1.2.0 — visibility, two-tier auth, DoS hardening

Choose a tag to compare

@kuyazee kuyazee released this 20 Jul 17:35
a437bd8

Highlights

Per-artifact visibility

Each artifact is now public (default, unchanged), private (operator-only), or
password-protected (a shared password you hand out). Gated on every serve path
(/a/:slug, ?raw=1, /source, zip sub-assets) — sub-resources return 404 with no
existence leak. A correct password sets an HttpOnly, slug-scoped, 7-day signed cookie so
shared links aren't re-prompted. Set it from the dashboard, the CLI (artifacts visibility),
or MCP (set_artifact_visibility). Existing artifacts stay public with no migration.

Two-tier auth

The dashboard now has a proper admin login (username + password, scrypt-hashed, signed
HttpOnly session cookie) separate from scoped, revocable API keys (read / publish /
full, optional expiry) for CLI and MCP. The bootstrap ARTIFACTS_API_KEY still works as an
all-scope break-glass bearer. Seed the admin from ARTIFACTS_ADMIN_USERNAME /
ARTIFACTS_ADMIN_PASSWORD or via the first-run setup screen.

Abuse resistance (DoS + rate limiting)

  • Password hashing moved off the event loop. scrypt ran synchronously on two
    unauthenticated routes (login, unlock); a handful of concurrent requests stalled the whole
    process. It now runs on the libuv threadpool behind a concurrency semaphore, so a flood
    degrades those two routes instead of the server.
  • Rate limiting on login (10 failures / 15 min per client IP) and unlock (10 / hour per
    IP + slug), 429 with Retry-After. Failures only — a correct password never counts.
  • TRUST_PROXY (none | cloudflare | xff) resolves the real client IP behind a
    tunnel or proxy, with IPv6 /64 bucketing. Pair with a CDN/edge limiter for volumetric
    protection — see the deploy guide.
  • Structured auth-failure logging — one JSON line per failed or rate-limited attempt.

Fixes

  • Postgres move/deleteSlug range under locale-aware collation.
  • Key/session expiry boundary now matches artifact expiry (<=).

Upgrade notes

Backward compatible. No migration: artifacts with no visibility field remain public, and
the bootstrap key keeps working. If you deploy behind Cloudflare or a reverse proxy, set
TRUST_PROXY so rate limits key on the real client IP (see
deploying).

Full changelog: v1.1.0...v1.2.0

v1.1.0

Choose a tag to compare

@kuyazee kuyazee released this 19 Jul 16:59
e1d9e76

What's new in v1.1.0

Storage backends

  • Pluggable storage layer with a local backend foundation
  • S3-compatible backend for durable, restart-proof hosting
  • Git backend (commit + push on every change)
  • Postgres and SQLite backends

Hosting

  • Host Flutter web (SPA) builds as zip sites
  • Astro static-site example + subpath deploy guidance

Artifact UI

  • Redesigned admin UI with a warm boutique identity
  • Group artifacts by project (dedicated field + search)
  • Artifact tags with click-to-filter
  • Optional hidable top frame for served artifacts
  • Responsive row actions with an overflow menu

CLI & registry

  • Artifacts CLI: publish/deploy/update/list/rename/disable/expire
  • MCP Registry metadata (server.json + OCI image label)

Docs

  • README restructured as a landing page + docs/ reference split
  • Added .env.example
  • flutter-demo frame + ?raw=1 full-screen notes

Full changelog: https://github.com/kuyazee/artifacts-host/compare/v1.0.0...v1.1.0

v1.0.0

Choose a tag to compare

@kuyazee kuyazee released this 08 Jul 14:26

Initial open-source release.

artifacts-host — self-hosted, Claude-style artifact publishing. POST HTML, JSX/TSX (React), Markdown, or a zipped static site; get back an unguessable, non-crawlable public URL on your own domain.

Highlights

  • Four content types: HTML, JSX/TSX (client-rendered via esm.sh, no build step), Markdown, zip sites (multi-file static projects)
  • Built-in MCP server for coding agents (publish, update, rename, disable/enable, expiry, list, delete)
  • Drag-and-drop web UI with API-key lock screen
  • Lifecycle controls: rename slugs, disable without deleting (404), auto-expiry (410), delete
  • Zip uploads validated before storage: index.html required, static-only extension whitelist, traversal/symlink rejection, size/count limits
  • No database — plain files under /data
  • Docker image: ghcr.io/kuyazee/artifacts:latest

Deploy

git clone https://github.com/kuyazee/artifacts && cd artifacts
ARTIFACTS_API_KEY=$(openssl rand -hex 32) BASE_URL=https://artifacts.example.com docker compose up -d