Skip to content

ADR-0003: Anonymous Visitor build persists in cookie + claims to account on sign-in

Context

Use case UC-C (Save a build) was originally drafted with two parallel paths:

  1. Account-bound save — Builder signs in, build persists in their account, syncs across devices
  2. Anonymous save with URL only — Visitor saves, gets a public URL, can come back to it without an account

The Use Case Diagram (UML #1) flagged this as an open decision. The question is whether anonymous Visitors should be able to "save" at all, and if so, where the state lives.

The pull on the anonymous side: PC building is a lot of work to throw away. A Visitor who spent 20 minutes assembling a Build shouldn't lose it because they closed the tab or didn't sign in. Forcing sign-in to save is a friction wall right at the conversion point.

The pull on the account side: cross-device sync, naming, a library of builds, and the ability to attach price-drop alerts (UC-G) all require an identity.

Decision

Both, with claim-on-sign-in.

  • Anonymous Visitor's build persists in a signed build_session HTTP cookie, 1-year expiry, signed with a server secret to prevent client tampering. The cookie holds either the build state directly (if small enough) or a server-side session ID that points to the build row.
  • On sign-in, the cookied build is "claimed" into the new Builder's account in the same request that completes auth. The cookie is then cleared. Build now belongs to the user and survives across devices.
  • URL-share (UC-D) is independent of save scope. Every saved build — anonymous or account-bound — gets a public unlisted slug. Shareability is a property of the build, not of who owns it.

Consequences

Positive

  • No friction wall at save time. Visitor can build, walk away, come back tomorrow on the same browser, build is still there
  • Sign-in is a value-add ("save across devices, get alerts"), not a gate
  • Single mental model: every build can be shared via URL, regardless of ownership
  • Account claim is a one-time delight moment — "your build came with you"

Negative

  • Two storage paths to maintain in code: cookie path and account path. Mitigated by making both serialize/deserialize through the same Build shape
  • Cookie size limits — if a Build's serialized form ever exceeds ~4KB, must switch to server-side session ID (already planning for this from day one)
  • Need to rotate the cookie signing key periodically; that becomes a runbook (rotate-build-session-cookie-key.md)
  • Race conditions during claim — if a user has builds on two devices and signs into the same account on both, we need a merge policy (later concern; for v1, last-write-wins is fine)

Neutral

  • Auth.js (issue #11) becomes a hard dependency for UC-C polish but not for the core save flow — the cookie path works without auth at all

Alternatives considered

Sign-in required to save

Rejected because: forcing identity at save time is the strongest possible friction wall, placed right at the moment a user has invested most in the product. The metric this would optimise (signups) trades against the metric we actually care about (conversions to deep-link click).

Anonymous save only, no account claim

Rejected because: cross-device sync is a real user need (started build at work, want to finish at home), and price-drop alerts (UC-G) require an email anyway. Half-stepping with anonymous-only forces users into a worse account-creation experience later.

Rejected because: localStorage is per-origin per-browser, doesn't survive a private window, and can't be claimed server-side without a separate sync step. Cookies are the right tool — they auto-attach to requests and we can claim atomically with the auth response.

Server-side anonymous account with auto-generated ID

Rejected because: indistinguishable from a real account except no email, but creates a maintenance burden of "ghost accounts" cluttering the DB. The cookie + 1y expiry is simpler.

References

  • Architecture → Use cases — pending decision section, now resolved
  • Auth.js docs on credential cookies (will be referenced from RFC for issue #11)
  • Cookie signing: iron-session or built into the Auth.js session strategy (decision pending in issue #11)