Slug-Based Viewer URLs for Artidrop — Design Analysis

Date: April 15, 2026
Scope: Feasibility and design options for artidrop.ai/a/{slug} viewer URLs


Background

Today, every artifact on Artidrop gets a viewer URL of the form /a/{shortId}, where shortId is a 10-character base62 string auto-generated at publish time (e.g. /a/a1b2c3d4e5). The proposal is to let users set a human-readable slug so a published artifact can be reached at a memorable URL like /a/my-cool-project.


Current Architecture

Artifact Identifiers

Each artifact carries two IDs:

Field Format Purpose
id art_xxxxxxxx (8-char base62) Internal DB primary key
shortId xxxxxxxxxx (10-char base62) Current public viewer URL
slug [a-z0-9-]+ text, optional Idempotent publish key (upsert), not in viewer URL

The slug field already exists in the schema with a per-owner unique constraint: UNIQUE(ownerId, slug). It is validated against the pattern ^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$ and is already accepted by the CLI via -s / --slug. However, it is only used server-side as an upsert key — it never appears in the viewer URL returned to the user.

URL Flow

Publisher                API                   Viewer
artidrop publish foo.md  →  POST /v1/artifacts  →  /a/{shortId}
  --slug my-report           slug stored, but      (shortId in URL,
                             url = /a/{shortId}     not slug)

Content is served from a separate domain (content.artidrop.com) under the same /{shortId}/v/{version} structure. The app domain (artidrop.ai) hosts the viewer shell at /a/{shortId}.


Design Options

Option A — Global Slug (Simplest URL)

URL shape: /a/my-cool-project

Make slugs globally unique (add a global UNIQUE(slug) constraint, drop or retain the per-owner one). The first user to claim a slug owns it.

Pros: Clean, short, shareable URLs.
Cons: Squatting risk; slug conflicts across users require a resolution strategy; schema migration needed to enforce global uniqueness on a column that previously allowed per-owner collisions.


Option B — User/Slug Namespaced URL

URL shape: /a/alice/my-cool-project

Keep per-owner uniqueness. Requires surfacing a username in the schema (not currently present — only userId exists).

Pros: Unambiguous; no squatting; mirrors GitHub's model.
Cons: Longer URLs; requires adding a username field to users and a registration flow.


URL shape: /a/my-cool-project → 301 → /a/a1b2c3d4e5

The shortId URL remains canonical. A slug lookup hits the DB, finds the shortId, and redirects. Old shortId links never break. If a user renames their slug, the old slug 404s but the shortId link still works.

Pros: Safest for link longevity; no canonical URL ambiguity for crawlers; slug rename doesn't silently serve stale content.
Cons: Two hops for slug-based access; the "pretty" URL isn't what ends up in the browser bar after redirect.


Option D — Slug as Alias (Both URLs Serve Identically)

Both /a/{slug} and /a/{shortId} return 200 with identical content. Use a <link rel="canonical" href="/a/{shortId}"> tag so search engines index only one.

Pros: Slug URL stays in browser bar; no redirect hop.
Cons: Two live URLs for the same content; canonical tag must be consistently set or crawlers will split page rank.


Option C (redirect) with globally unique slugs is the lowest-risk path:

  1. Add a global unique index on slug (or enforce uniqueness at the application layer with a clear error message on conflict).
  2. In the pages route (/a/:id in pages.ts) and the API route (/v1/artifacts/:id in artifacts.ts), add a third lookup branch: try slug after shortId fails.
  3. In the pages route, if the lookup resolved via slug, issue a 301 to the shortId-based canonical URL.
  4. In the API response, emit "url": "/a/{slug}" when a slug is set, "/a/{shortId}" otherwise.
  5. Add a slug-edit input to the artifact detail panel on the dashboard with live validation.

Implementation Scope

Area Change Effort
DB schema Global unique index on slug Trivial (one migration)
artifacts.ts API route Lookup by slug; return slug-based URL when set ~20 lines
pages.ts server route Slug → 301 to shortId ~15 lines
ArtifactView.tsx + App.tsx No change needed if API resolves slugs None
Dashboard UI Slug edit field + validation + URL preview ~1 day
Share menu Emit slug URL when available ~10 lines

Total estimate: 1–2 days.


Open Questions

  1. Slug conflict UX: What error message when a slug is already taken? First-come-first-served or per-user namespacing?
  2. Slug on existing artifacts: Should users be able to add a slug to artifacts they published before this feature? (Yes, via the dashboard edit panel.)
  3. Slug in CLI output: After artidrop publish --slug foo, should the CLI print /a/foo or /a/{shortId}? Slug-based URL is friendlier but only works after the redirect is in place.
  4. Anonymous artifacts: Currently anonymous publishers cannot use slugs. Keep that restriction?

Conclusion

The slug field is already in the schema and the CLI already accepts it — the infrastructure is 80% there. The remaining work is wiring slug lookups into the viewer URL resolution path, deciding on global vs. per-owner uniqueness, and building the dashboard UI for slug editing. The redirect approach keeps existing links safe and avoids canonical URL complexity.