Skip to content

Phase 1 — Sign up & first contact

The first surface a brand-new user sees is /sign-in. From here to the dashboard takes about 30 seconds for OAuth users, ~90 seconds for email + password (verifying email).

┌────────────────────────────────────┐
│ AtomPRD Cloud │
│ Sign in to your account │
│ │
│ ⌘ Continue with Google │
│ ⌘ Continue with GitHub │
│ ⌘ Continue with Facebook │
│ ────────── or ────────── │
│ [ Email ] │
│ [ Password ] │
│ [ Sign in ] │
│ No account? Sign up │
└────────────────────────────────────┘

The sign-in screen offers four routes, in this priority order:

  1. Google — most users land here.
  2. GitHub — preferred by engineering audiences.
  3. Facebook — included for completeness.
  4. Email + password — fallback. Minimum 8 characters. Toggle to Sign up mode adds a Name field.

OAuth redirects through signIn.social({ provider, callbackURL }) from better-auth. The callback URL is always absolute${window.location.origin}/ — because better-auth resolves relatives against BETTER_AUTH_URL (the cloud-api at port 4000-4001) and relative URLs would 404 there. The cloud-web origin is registered in trustedOrigins.

Email sign-in posts directly to /auth/sign-in/email; sign-up to /auth/sign-up/email. On success the page hard-redirects to /. On failure a toast shows the API error message.

StepEffect
OAuth callback returnsSession cookie issued. User row created (or matched by provider id).
Email sign-upUser row + first organization auto-bootstrapped. The user is the org owner.
Email sign-inSession cookie issued. Redirected to last-used org or first owned org.

The first organization is named after the user (<Name>'s workspace or similar) and gets the FREE plan by default. Users can rename it later in /settings.

The AppLayout wraps every authenticated route and exposes:

  • OrgSwitcher in the top bar — shows current org, dropdown with all orgs the user is a member of, plus + New org at the bottom.
  • UserMenu — avatar, name, sign-out.
  • GuestBanner — visible if the org has not been activated (rare; only after a manual data wipe).

Switching orgs sets a cookie that scopes every subsequent API call. Project lists, MCP tokens, billing — all org-scoped.

Top-right of the dashboard:

BadgeMeaning
FREEDefault plan. Capped at 1 project, 50 AI calls/day, no MCP write tools.
SOLOPaid solo tier. Unlimited projects, 500 AI calls/day, full MCP.
TEAMMulti-seat tier. Unlimited everything + audit log + advanced collaboration.

The badge links to /org/billing. Plan upgrades flow through Lemon Squeezy as the merchant of record (the legal payment processor). See Concepts → Four patterns for the underlying spec — billing UI itself is coming soon in the early-access build.

Right of the plan badge, a small dot + label:

StateColorMeaning
live🟢 emerald (pulsing)Last fetch < 12 s ago. Data is fresh.
loading…greyFirst fetch in flight.
stalegreyLast fetch > 12 s ago. React Query will refetch on next 8 s tick or window-focus.

Hover for the exact Last refresh HH:MM:SS timestamp. This is the system pulse — if it stays stale, your network is throttled or the API is degraded.

Nothing. Every authenticated route checks currentOrg and renders an empty placeholder if absent:

Pick an organization first.

OAuth sign-up always creates one automatically; this state is only reachable if a manual data wipe ran while the session was active.