ADR-0007 — No accounts in v1
| Date | 2026-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 insync_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_UNAUTHENTICATEDis 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
userIdcan read and write that user's ticks. Acceptable with zero users and disposable data; not acceptable at launch, which is what theALLOW_UNAUTHENTICATEDflag 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.
Email magic link, built here
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.