19 KiB
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 <SignIn/>/<SignUp/> catch-all pages, themed via <ClerkProvider> (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 labelAuth · Clerk · state(:79), a 4-way segmented state switcherSign 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.combmasked 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.
- 56px padding,
- Right form panel (
.formside,:103-152; CSS:36-37): centered,max-width:380pxcard, scrollable. Holds the four state views (.view, only one.onat 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
lrowheader: 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). .localnotehoney 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 (placeholderyou@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). .otprow of 6 single-char inputs (:137; CSS:60-64): 48×56px, mono 22px,maxlength=1,inputmode="numeric"; a.filledclass flips border + text to honey on a non-empty box.- Auto-advance / backspace nav (the explicit design requirement), in the demo script
(
:163-169):inputevent focuses the next box when filled;keydownBackspace 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 <style> + waggle.css) |
|---|---|
| Brand-panel bg | linear-gradient(160deg, --bg-2, --bg); border --line-soft (auth.html:22) |
| Honeycomb texture | .comb data-URI honey @ 5% stroke, radial mask (CSS :119-122) |
| Hex mark | .hex clip-path (waggle.css:117) + linear-gradient(150deg,--honey-bright,--honey-deep) + --honey-glow (auth.html:25) |
| SSO buttons | --surface bg, --line-strong border; hover → --honey-line + --surface-2 (auth.html:44-45) |
| Divider | flex rule, --line-soft lines, mono --text-dim label (auth.html:47-48) |
| Inputs | --surface bg, --line border, --r:11px; focus → --honey-line + --honey-glow (auth.html:52-53) |
| OTP boxes | mono, --r:12px; .filled → --honey-line + honey text (auth.html:62-64) |
| Primary submit | --honey bg, text #1a1407; hover → --honey-bright (auth.html:56-57) |
| Honey trust banner | .localnote → --honey-wash bg + --honey-line border (auth.html:68) |
| Neutral enterprise banner | .localnote overridden to --bg-2 + --line-soft (auth.html:148) |
| Links / accents | --honey; fineprint --text-dim (auth.html:41,55,58) |
| Focus ring (global) | :focus-visible{outline:2px solid --honey} (waggle.css:125) |
| Fonts | --sans Hanken Grotesk, --mono JetBrains Mono (waggle.css:51-53) |
In the real build these map to the PR1-landed warm tokens in apps/web
(BUILD-PLAN.md §3.1, already shipped per MEMORY.md — index.css carries the verbatim
waggle.css names + the shadcn HSL recolor). So no new tokens are needed — the build
re-skins Clerk/custom components against the already-present token set. Honey "#1a1407"
button-foreground is the same --primary-foreground PR1 set (BUILD-PLAN.md:67).
#1a1407 (honey-button text) and data-theme theming are app-global; the screen
inherits dark default + the warm-paper light variant for free.
4. Clerk's themeable components vs custom
Per SCREENS.md §13: "Build with Clerk components themed to the tokens." Mapping the
design's pieces to what Clerk's appearance API covers:
| Design piece | Clerk coverage | Notes |
|---|---|---|
| Sign in (Google/Apple SSO + email/pw) | <SignIn/> |
Social buttons, email/pw, "Forgot?" reset are first-class. Theme via appearance.variables (colorPrimary ← --honey, colorBackground ← --surface, etc.) + elements overrides. @clerk/themes already a www dep (apps/www/package.json:16). |
| Sign up + local-first note | <SignUp/> + custom |
The form is Clerk; the honey .localnote "you don't need this to start" banner is custom chrome placed above/around <SignUp/>. |
| Verify 6-box OTP (auto-advance/backspace) | Clerk built-in | Clerk's email-code step renders its own OTP input with auto-advance. Re-skinning to the exact 48×56 honey boxes needs elements.otpCodeField* overrides (or Clerk Elements / a fully custom flow if pixel-parity is required). |
| SSO / enterprise (SAML/SCIM → Teams/KVARK) | partial Clerk + custom | Clerk Enterprise SSO exists but is a paid Clerk feature + per-org config. The design's panel is mostly a custom "Talk to sales" CTA (KVARK funnel), not a live SAML form. Safe build: custom panel, link to sales. |
| Left brand panel + pitch + trust lines | fully custom | Pure layout chrome around the Clerk <SignIn/>/<SignUp/> card. |
To match the warm design, two integration styles are possible:
- Themed Clerk prebuilt (
<SignIn appearance={…}/>) wrapped in the custom split layout — fastest, matches SCREENS.md's instruction, but OTP/element pixel-parity is limited to whatappearance.elementsexposes. - Clerk Elements / headless (
useSignIn,useSignUp) feeding the design's exact custom inputs/OTP/buttons — full visual control, more code, the only way to get the exact 48×56 honey OTP boxes + custom SSO buttons.
The brand panel, dividers, local-first banner, and enterprise→sales CTA are custom in either case.
5. Honesty contract — where Auth could fabricate (MUST be gated off)
Auth is the single highest-risk screen for fabrication because the desktop has no real account. Each of these must be real or absent — never invented:
- A logged-in identity that isn't real. The demo hardcodes
mara@egzakta.com/Mara Kovač(auth.html:113,127,136). A build MUST NOT pre-fill or display a fake signed-in user. The desktop's only "identity" is the local IdentityLayer name (adapter.getIdentity(),apps/web/src/lib/adapter.ts:1090), which is not an authenticated account and must never be rendered as "signed in". - Fake SSO success. Google/Apple/SSO buttons that "succeed" without a real Clerk
(or any) provider configured are fabrication. If Clerk isn't wired in the desktop,
these buttons must be honestly disabled / "coming soon" / route to
apps/www, not fake a session. (No Clerk publishable key path exists inapps/webtoday.) - Fake OTP verification. The demo's "Verify & continue" advances on any input
(
auth.html:138,164). Real verify must check a real code via Clerk; otherwise the verify state must not claim to have verified anything. - "Continue → Home" as a real auth boundary. In the demo all submits just navigate
to Home (
auth.html:171-172). The desktop already has a real structural auth gate (the session-tokenensureReady()contract,adapter.authgate.test.ts) — but that gates the local sidecar, not a user login. The Auth screen must not imply a login happened when only the local app opened. - SAML/SCIM as live. The enterprise panel names SAML/SCIM/audit logs
(
auth.html:148). These are Teams/KVARK/Clerk-Enterprise features — the panel is a sales CTA, and must stay one unless those are genuinely provisioned. Do not render a SAML form that does nothing.
Gate-off rule: if Clerk is not configured for the desktop, the entire authenticated path (SSO, email/pw, OTP, SSO/org) should degrade to the honest local-first framing the design itself already provides ("an account is optional — Waggle runs fully local") and a single "Sign in for sync →" link to the real flow, rather than a non-functional look-alike.
6. Decisions a build agent must get answered first
-
Does desktop auth ship at all, or link out?
- Options: (a) full Clerk in
apps/web(add@clerk/clerk-react+ publishable key + a/authroute — none exist today); (b) desktop stays local-only, "Sign in for sync" deep-links to the existingapps/wwwClerk flow (apps/www/app/sign-in/...); (c) embed/redirect toapps/wwwin a webview. - Recommendation: (b) for first ship — matches "account is optional", reuses the
real, already-themed
apps/wwwClerk surface, and avoids standing up a second Clerk React integration + token bridge into the local sidecar (which currently authenticates with a machine token, not a user token). - Blast radius: large if (a) — new dep, new route, new token-exchange between Clerk user-JWT and the local bearer; small if (b)/(c).
- Options: (a) full Clerk in
-
BYO-key vs Waggle-metered (DESIGN_POV §4 / BUILD-PLAN §7.5 #5). Explicitly flagged as blocking Billing/PR7 (
DESIGN_POV.md:62-70,:88-90;BUILD-PLAN.md:165-166). It reshapes whether "sign in" is even required to use models (BYO = local key, no account needed; metered = account + payment up front). Settle before building 13/14.- Blast radius: shapes Auth (is sign-in required for inference?), Onboarding model gate, Billing, Usage. Founder decision, not a build choice.
-
OTP fidelity: themed Clerk prebuilt vs Clerk Elements/headless. Pixel-exact 48×56 honey OTP boxes need headless; "good enough" needs only
appearanceoverrides.- Recommendation: themed prebuilt first (ships SCREENS.md's instruction), upgrade to Elements only if review demands the exact boxes. Blast radius: small/local.
-
SSO/enterprise panel = sales CTA only (no live SAML). Recommendation: keep it a custom "Talk to sales → KVARK/Teams" panel; do not implement live SAML in PR7. Blast radius: small.
7. REAL vs DERIVABLE vs MUST-BUILD vs EXTERNAL-DEP (screen 13)
| Feature | Status | Evidence / note |
|---|---|---|
| Warm tokens + primitives the screen needs | REAL | PR1 landed verbatim waggle.css tokens in apps/web (BUILD-PLAN.md §3.1, MEMORY.md PR1). No new tokens. |
| Split brand panel, pitch, trust lines, dividers, local-first banner, enterprise CTA | MUST-BUILD (custom chrome, low risk) | Pure layout/copy; no backend. All copy verbatim in §2/§4 above. |
Clerk <SignIn/>/<SignUp/>/OTP UI in apps/web |
EXTERNAL-DEP | No @clerk/clerk-react in apps/web; no publishable key; no /auth route (grep: no matches). Clerk React UI exists ONLY in apps/www (apps/www/app/sign-in/[[...sign-in]]/page.tsx). |
| Real user account / login / session | EXTERNAL-DEP | Real Clerk auth lives in cloud/team server (packages/server/src/plugins/auth.ts:3,31-48) + users table (services/user-service.ts). Desktop sidecar auth is a machine bearer token, not a user (local/security-middleware.ts:235-377). |
| Local "identity" (name) for the user row | REAL but NOT an account | adapter.getIdentity() → /api/identity → IdentityLayer name (adapter.ts:1090; tauri-bindings.ts:133-145); used in AppShell.tsx:98-108, degrades to "Account". Must NOT be shown as "signed in". |
| SSO with Google/Apple | EXTERNAL-DEP | Clerk social providers; need Clerk + OAuth app config. Not wired in desktop. |
| SAML / SCIM (enterprise) | EXTERNAL-DEP (Teams/KVARK/Clerk-Enterprise) | Design panel is a sales CTA, not a live form (auth.html:148). |
| "Continue → Home" navigation | DERIVABLE | Routes to /home; desktop already has the structural sidecar auth gate (adapter.authgate.test.ts) but that is not a user login. |
| Theme toggle / segmented state switcher (top bar) | N/A — concept scaffolding | .controls is harness chrome for previewing states (auth.html:78-87), not product UI. |
8. One-paragraph build brief (for the implementer)
Build screen 13 as a custom warm split layout (left brand panel: hex "W" + honeycomb
.comb + verbatim pitch/trust copy from §2e; right: a centered max-width:380px card)
in apps/web, against the already-present PR1 warm tokens (no new tokens). The form
itself is EXTERNAL-DEP on Clerk, which is wired only in apps/www today — so the
first, honest ship is local-first: the desktop stays usable without an account
(reuse the design's own "you don't need this to start" .localnote), and a single
"Sign in for sync →" links to the real, already-themed apps/www Clerk flow rather
than a non-functional Clerk look-alike in the desktop. If founder confirms full in-app
Clerk (decision §6.1a), add @clerk/clerk-react + a publishable key + a /auth route +
a user-JWT→local-sidecar token bridge, and theme <SignIn/>/<SignUp/>/OTP via
appearance (Elements only if pixel-exact OTP boxes are required). Never render a
fabricated signed-in identity, fake SSO/OTP success, or a dead SAML form (§5). The
BYO-vs-metered decision (§6.2) blocks this screen and Billing and must be settled
first.