Roadmap
Open questions and directions for onnne.link. Nothing here is a defect — the known defects are fixed, and their history is in git.
Where does link data live?
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 anunknownand 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.
How do links end?
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.
More link types
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.