Skip to main content

ADR-0007 — No accounts in v1

Date2026-08-17
Supersedes
Superseded by

Context

Ascents, wishlists, and partner tagging all belong to a person, and the sync protocol already scopes them by userId. The question was how that person is established.

Every real answer — Sign in with Apple, an email magic link, a managed provider — is a chunk of work that sits between the user and the app on first launch, and none of it is needed to build or test anything else. Meanwhile the offline-first design means identity has to work with the radio off anyway: a tick logged in a Peak valley cannot wait for a network round-trip to find out who made it.

Decision

No login. The app mints a local device identity on first launch and uses it for everything. Real authentication comes later, most likely through an external provider such as Clerk or WorkOS, connected to the main database.

Concretely:

  • On first launch the app generates a usr_<ULID> and persists it in sync_state. There is no signup screen, no email, no password, no account.
  • The client sends that id on sync requests. The server takes it at face value. There is no verification, because there is nothing to verify against.
  • The Worker still refuses unauthenticated sync in production unless ALLOW_UNAUTHENTICATED is explicitly set. One config flag, so shipping open writes to real users is a deliberate act rather than an oversight.
  • Everything else in the model is already account-shaped: Ascent.userId, the per-user sync scope, the rejection of mutations for another user's record. When auth lands it replaces one middleware and nothing else.

Consequences

What this costs, plainly:

  • A logbook lives on one device. No sync between a phone and an iPad, and reinstalling the app loses everything. That has to be said in the UI before someone logs a season's climbing into it.
  • The sync endpoints are trust-the-client. Anyone who knows or guesses a userId can read and write that user's ticks. Acceptable with zero users and disposable data; not acceptable at launch, which is what the ALLOW_UNAUTHENTICATED flag exists to enforce.
  • Partner tagging cannot ship. Tagging someone requires stable, findable identities. It stays on the roadmap behind this.
  • A claiming migration is deferred, not avoided. When accounts arrive, existing local ids have to be adopted into real accounts, and anything queued in the outbox under an old id rewritten. Doing it later is more work than doing it now — that is the trade being accepted.

What we are committed to:

  • Client-generated user ids. Whatever provider comes later has to tolerate an existing local id being claimed rather than insisting on minting its own.
  • Not shipping to real users until this ADR is superseded.

Alternatives considered

Sign in with Apple now

One tap, no password, Apple handles recovery, and it satisfies the App Store rule by construction. The strongest option on friction, and the likely shape of the eventual answer for iOS.

Not chosen now because it is work that blocks nothing: every feature in phases 1 and 2 can be built and tested against a local identity, and doing it later costs a claiming migration we are accepting anyway.

Platform-independent, survives an Android decision unchanged. Rejected as the wrong thing to build ourselves — deliverability, rate limiting, and link security all become ours to operate, for higher signup friction than one tap.

A managed provider now (Clerk, WorkOS, Auth0, Supabase Auth)

The likely eventual answer, and the reason it is named in the decision above. Not chosen now purely on sequencing: it adds a vendor, a bill, and a Workers-runtime integration to verify, none of which is needed to make the app work at a crag.

Open questions

Answering these is what supersedes this ADR:

  • Which provider. Clerk and WorkOS both fit; neither has been evaluated against the Workers runtime, or against the offline requirement that a token stay usable with no network.
  • The claiming migration. How a local usr_<ULID> becomes a real account without losing the outbox.
  • Account deletion and data export. Not optional under UK GDPR, and much easier to design in than to retrofit.
  • Multi-device. Does signing in on a second phone merge logbooks or replace one?
  • Whether a logbook can be public, and what that means for partner tagging.