# PR7 Recon · Screen 13 — Auth (Clerk, themed) · DESIGN spec > RECON ONLY. No product code touched. Every claim cites `file:line`. > Scope: faithful component-level breakdown of the Auth screen design so a build > agent can implement it against the warm token system, PLUS the REAL-vs-BUILD > reality of auth in this monorepo (which is the load-bearing surprise here). Sources read in full: - `docs/design_handoff_waggle_app/design-files/screens/auth.html` (181 lines) - `docs/design_handoff_waggle_app/SCREENS.md` §13 (lines 269-278) - `docs/design_handoff_waggle_app/design-files/styles/waggle.css` (155 lines, token grounding) - `docs/redesign-warm-hive/BUILD-PLAN.md` §6 (PR7 row) + §7.5 (the BYO/metered gate) - Codebase auth reality: `packages/server/src/plugins/auth.ts`, `…/local/security-middleware.ts`, `…/services/user-service.ts`, `apps/web/src/components/os/AppShell.tsx`, `apps/web/src/lib/adapter.ts`, `apps/www/app/sign-in/[[...sign-in]]/page.tsx`, `apps/www/app/api/stripe/checkout/route.ts` --- ## 0. The headline (read this before building) **The desktop app (`apps/web`) — PR7's build target — has NO authenticated user identity and NO Clerk React SDK today.** Auth screen 13 is therefore overwhelmingly **EXTERNAL-DEP + decision-gated**, not a re-skin of something already wired. Two distinct server modes coexist; the design's "Clerk" assumption only matches ONE of them, and it's NOT the one the desktop talks to: | Mode | Auth mechanism | Has a real user account? | Where the design's screen would live | |---|---|---|---| | **Local sidecar** (what the Tauri desktop / `apps/web` talks to) | per-process **machine bearer token** via `GET /api/auth/session-token`, exchanged so loopback callers can't drive the API. NOT a login. (`packages/server/src/local/security-middleware.ts:235-377`, `:238`) | **No.** "Identity" is the local IdentityLayer name the user types in onboarding (`adapter.getIdentity()` → `/api/identity`, `IdentityResponse.name`), a memory record, not an account. (`apps/web/src/lib/adapter.ts:1090-1095`; `tauri-bindings.ts:133-145`) | n/a today — there is no `/auth` route in `apps/web` (grep for `'/auth'`/`appId.*auth` → **No matches**) | | **Cloud / Team server** (`packages/server/src/index.ts` + `plugins/auth.ts`) | **real Clerk** — `verifyToken()`, `clerkClient.users.getUser()`, Drizzle `users` table, auto-provision on first auth (`packages/server/src/plugins/auth.ts:3,19-52`; `services/user-service.ts:30-58`) | Yes (Clerk user → internal UUID) | n/a in `apps/web` either | | **`apps/www`** (Next.js landing) | **real Clerk UI**, hosted ``/`` catch-all pages, themed via `` (`apps/www/app/sign-in/[[...sign-in]]/page.tsx:1,17-23`); `@clerk/nextjs@^7.3.0` + `@clerk/themes@^2.4.57` (`apps/www/package.json:15-16`) | Yes | **This is the only place Clerk's themeable React UI already exists.** | So the build decision PR7 must surface: **does screen 13 ship as a real auth flow in the desktop at all, or is desktop auth always optional/local and "sign in for sync" links out to the `apps/www` Clerk flow?** The design copy itself ("account is optional — Waggle runs fully local without one") leans toward the latter. See §6 Decisions. --- ## 1. Exact layout (split: brand-panel left + form right) From `auth.html`: - **Top control bar** (`.controls`, `auth.html:78-87`) — concept-harness chrome: a label `Auth · Clerk · state` (`:79`), a 4-way segmented state switcher `Sign in / Sign up / Verify / SSO` (`:80-85`), and a theme toggle button (`:86`). **This bar is concept scaffolding for previewing states — NOT product UI.** In the real build the "state" is route/Clerk-flow-driven, not a manual segmented control. - **Split grid** (`.split`, `:89`; CSS `:20`) — `grid-template-columns: 1.05fr 1fr` (brand panel slightly wider than the form). - **Left brand panel** (`.brandside`, `:90-101`; CSS `:22-33`): - 56px padding, `linear-gradient(160deg, var(--bg-2), var(--bg))`, right border `--line-soft`, full-bleed honeycomb texture `.comb` masked by a radial gradient at 30%/30% (CSS `:23`). - Three vertical zones via `justify-content:space-between`: **brand lockup** (hex "W" mark + "Waggle" wordmark, `:92`), **pitch** (h2 + p, `:93-96`), **trust lines** (`:97-100`). - **Hidden below 820px** — `@media (max-width:820px){ .brandside{display:none} }` (CSS `:73`). Mobile = form only. - **Right form panel** (`.formside`, `:103-152`; CSS `:36-37`): centered, `max-width:380px` card, scrollable. Holds the four state views (`.view`, only one `.on` at a time, CSS `:72`). --- ## 2. Every state — verbatim copy + behavior ### 2a. Sign in (`data-view="signin"`, `auth.html:106-117`) — default - Heading **"Welcome back"**; sub **"New to Waggle? Create an account"** (link → signup) (`:107`). - **SSO block** (`.sso`, `:108-111`): two buttons — **"Continue with Google"** (mono "G" badge) and **"Continue with Apple"** (mono "⌥" badge). - Divider **"or"** (`.divider`, `:112`). - **Email** field, demo value `mara@egzakta.com` (`:113`). - **Password** field with a `lrow` header: label + **"Forgot?"** link (→ verify view in the demo; in product → Clerk reset) (`:114`). - Primary **"Sign in"** button (`data-go="home"` → routes to Home) (`:115`). - Fineprint: **"By continuing you agree to the Terms & Privacy Policy."** (`:116`). ### 2b. Sign up (`data-view="signup"`, `:120-132`) — carries the local-first note - Heading **"Create your hive"**; sub **"Already have an account? Sign in"** (`:121`). - **`.localnote` honey banner** (`:122`; CSS `:67-70`) — THE load-bearing trust copy: > **"You don't need this to start."** Waggle works locally right away — create an > account only when you want sync or a team. - **SSO block**: single **"Sign up with Google"** (`:124`). - Divider **"or"** (`:126`). - Fields: **Name** (demo `Mara Kovač`), **Email** (placeholder `you@company.com`), **Password** (placeholder **"At least 10 characters"**) (`:127-129`). - Primary **"Create account"** (→ verify) (`:130`). - Fineprint: **"We'll send a code to verify your email."** (`:131`). > Password rule "At least 10 characters" (`:129`) is design copy. Real minimum is > Clerk-policy-driven — do NOT hardcode "10" in validation; mirror whatever the Clerk > instance enforces, or omit the count. ### 2c. Verify — 6-box OTP (`data-view="verify"`, `:135-140`) - Heading **"Check your email"**; sub **"We sent a 6-digit code to mara@egzakta.com"** (the email is bolded in `--text-2`) (`:136`). - `.otp` row of **6 single-char inputs** (`:137`; CSS `:60-64`): 48×56px, mono 22px, `maxlength=1`, `inputmode="numeric"`; a `.filled` class flips border + text to honey on a non-empty box. - **Auto-advance / backspace nav** (the explicit design requirement), in the demo script (`:163-169`): `input` event focuses the next box when filled; `keydown` Backspace on an empty box focuses the previous box. The demo pre-fills boxes 0-2 with `[2,4,9]`. - Primary **"Verify & continue"** (→ Home) (`:138`). - **"Didn't get it? Resend code · Use a different email"** (→ back to sign in) (`:139`). ### 2d. SSO / enterprise (`data-view="sso"`, `:143-150`) - Heading **"Single sign-on"**; sub **"Use your organization's identity provider."** (`:144`). - Field **"Work email or organization"** (placeholder `you@company.com`) (`:145`). - Primary **"Continue with SSO"** (`:146`). - Divider **"enterprise"** (`:147`). - **Muted `.localnote`** (neutral, not honey — `background:var(--bg-2)`) (`:148`), verbatim: > SAML, SCIM provisioning, and audit logs are available on **Teams** and **KVARK**. > **Talk to sales →** - Back link **"← Back to sign in"** (`:149`). ### 2e. Brand-panel pitch + trust copy (verbatim, `:93-100`) - h2: **"Your work follows you, *everywhere.*"** ("everywhere." in honey via `em`, CSS `:29`). - p: **"Sign in to sync your hive across devices, collaborate with a team, and pick up any project exactly where you left off — on any machine."** - Trust line 1 (shield icon): **"An account is optional — Waggle runs fully local without one"** - Trust line 2 (arrow icon): **"Your memory stays yours; sign-in only adds sync"** --- ## 3. Warm tokens + primitives used (for faithful build) All from `waggle.css` (dark `:9-61`, light `:63-101`). The auth HTML uses these named tokens directly: | Primitive | Tokens (from `auth.html` `