Skip to content

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

Choose a tag to compare

@kuyazee kuyazee released this 20 Jul 17:35
· 193 commits to main since this release
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