Roadmap

Open questions and directions for onnne.link. Nothing here is a defect — the known defects are fixed, and their history is in git.

This is the decision everything else waits on. Today a link is a JSON file in data/links/, so publishing or editing one requires a commit and a redeploy. That is the single largest operational constraint on the product: non-technical staff cannot create a link, and nothing can change without an engineer.

Moving to a database (Deno KV is the natural fit) removes the redeploy step, makes per-link edits possible, and gives analytics somewhere to live. It costs the things the flat-file store gives for free: git history, reviewable diffs, and a local development setup that needs nothing running.

The groundwork is already in place, so this is a smaller change than it looks:

  • parseLink (lib/validate.ts) validates an unknown and touches no filesystem, so it guards a database row exactly as it guards a file today.
  • The link cache sits behind the loader in lib/data.ts, not in the route, so a database implementation can substitute its own strategy without any caller changing.

Open: which store, and whether files remain supported as a fallback or a seed format.

Note that /builder already produces valid records against parseLink, so whichever store wins, the authoring side of publishing is done.

Should there be an admin UI?

Half of one exists. /builder (BUILDER.md) authors a record of any type except mikro through a form, previews it with the real page components, keeps drafts in the URL and in IndexedDB, and takes image uploads that never leave the browser. What it cannot do is save: it has nowhere to save to. The export is a zip to unpack into the repository.

What is still missing all depends on the storage question — save, edit in place, a list of existing links, delete — plus authentication, which /builder needs none of because it owns no data. Two things are already shaped for it: the builder reaches the app only over POST /builder/preview and GET /builder/schema, so a write endpoint is an addition rather than a rewrite; and uploaded images already carry a stable /u/<id> reference, so an object store slots in where the zip is today.

What should we measure?

There is no instrumentation at all. For a QR product, scan counts, referrers and per-block click-through are the obvious first questions, and none of them can be answered today. Worth deciding what to collect before the storage change, since that is when there will somewhere to put it.

The current model cannot express expiry, soft deletion, or rotating a uuid without reprinting the QR code. Deleting the file makes the URL 404 and kills every printed card and programmed tag pointing at it. A real lifecycle would need at least a redirect or a "this link has moved" state.

Cheap to add — the Link union makes it compile-time guided. See LINK-TYPES.md. Candidates that have come up: event pages, menus, and app-download landing pages.

Right-to-left layout

lang and dir are plumbed through State and set from config.locale, so an Arabic link renders with dir="rtl" today. But no component has been audited for RTL: directional padding, icon placement, and the hardcoded sticky-header offsets in ShowcasePage would all need review before shipping one.

Better social previews

og:image currently points at the link's logo, which is a transparent PNG designed to sit on a white plate — it may read poorly against a chat client's own background. A purpose-built preview image per link, or a generated card, would look considerably better in WhatsApp and LinkedIn.