Repository navigation
feat(auth): Hazel sign-in on @yielded/auth (backend + Clerk user migration) - #329
Confidence 6/10 · 1 issue to address
🟡 Confidence 6/10 · needs attention
Auth/tenancy and migration code carries high risk, and the new auth routes' span coverage was not verified; earlier-review fixes all hold at this head.
quality 88/100 · 1 warning · 1 note · tests covered · risk high · 11 files not read
Adds Hazel's own sign-in on @yielded/auth (routes, session resolution, Clerk-identity import, Postgres schema) alongside Clerk, gated on AUTH_GITHUB_CLIENT_ID. Every earlier-review defect holds fixed at this head; two small new defects remain. The listed open error issues (Warehouse*, McpQuery, Vcs*, IntegrationsUpstream, GithubAppError) come from other services and only share bundler-mapped paths with packages/auth/src/yielded/tables.ts and web-foldkit/src/page/registry.ts — this change does not touch them. The new test files and the deploy workflow were not read.
HazelSessionresolves a session cookie (trusted Origin) or bearer, then falls back to Clerk- New
/auth/*,POST /auth/tokenand/.well-known/jwks.jsonbehind AUTH_GITHUB_CLIENT_ID - Auth storage runs on the request's own lazily-opened Postgres connection
import-clerk-identities.tslinks existing users by verified email
Before merge
- Secret · Add secret
AUTH_ACCESS_TOKEN_JWKSto every environment ·apps/actors/alchemy.run.ts:33 - Env var · Set
S3_PUBLIC_URLin every environment, or confirm its default ·apps/backend/src/rpc/handlers/users.ts:14 - Env var · Set
API_BASE_URLin every environment, or confirm its default ·apps/backend/src/services/hazel-auth.ts:41 - Env var · Set
FRONTEND_URLin every environment, or confirm its default ·apps/backend/src/services/hazel-auth.ts:42 - Env var · Set
INVITE_EMAIL_FROMin every environment, or confirm its default ·apps/backend/src/services/invite-mailer.ts:68 - Env var · Set
IS_DEVin every environment, or confirm its default ·apps/backend/src/services/invite-mailer.ts:94 - Env var · Set
VITE_APP_URLin every environment, or confirm its default ·apps/web-foldkit/src/platform/desktop-auth.ts:131 - Env var · Set
TAURI_DEV_HOSTin every environment, or confirm its default ·apps/web-foldkit/vite.config.ts:13 - Env var · Set
TAURI_ENV_PLATFORMin every environment, or confirm its default ·apps/web-foldkit/vite.config.ts:27 - Env var · Set
TAURI_ENV_DEBUGin every environment, or confirm its default ·apps/web-foldkit/vite.config.ts:28 - Migration · Check it is safe on production data and with the code still deployed ·
packages/db/migrations/20261013222300_hazel_auth/migration.sql - Infra · Check every environment has what this config now expects ·
apps/actors/alchemy.run.ts
Findings
🟠 Warning · F10 · slugify can end a long workspace slug in -, which the form then rejects
correctness · apps/web-foldkit/src/page/create-workspace.ts:49-56
.replace(/^-+|-+$/g, "") runs before .slice(0, 48), so a name whose hyphenated form is longer than 48 characters and has a hyphen at index 48 (a…a b → a…a-) auto-fills a slug that SLUG_PATTERN rejects, and SubmittedWorkspace returns without dispatching CreateWorkspace, showing "Use at least 3 lowercase letters, numbers or hyphens" for a slug the user never typed.
Strip trailing hyphens after truncating: `.replace(/[^a-z0-9]+/g, "-").slice(0, 48).replace(/-+$/g, "")`, and add a test for a name at the slice boundary.
🔵 Note · F11 · Hand-written DDL names the PK yielded_browser_login_namespace_attempt_id_pk, the migration says yielded_browser_login_pkey
maintainability · packages/db/sql/2026-10-13-desktop-sign-in.sql:17
The runbook applies this file to prd before db:migrate, so CREATE TABLE IF NOT EXISTS "yielded_browser_login" in packages/db/migrations/20261013222300_hazel_auth/migration.sql is skipped and the live table keeps this PK name, which the snapshot records as yielded_browser_login_pkey — the tracked schema then never matches the database db:generate/db:verify diff against.
Rename the constraint here to `"yielded_browser_login_pkey"`, or regenerate the migration from `packages/db/src/tables/auth.ts` so both artifacts name it the same.
Fixed since the last review
- ✅
F1 ·safeReturnToaccepts/\\t/evil.example, an off-origin post-sign-in navigation - ✅
F2 ·readSessionretries permanent failures forever, so the app never leaves the loader - ✅
F4 · New 503 auth-unavailable branch does not incrementproxyAuthFailures - ✅
F5 · One malformed address fails every invite in its role batch - ✅
F6 · TheDownloadingtoast pushed afterInstall & Restartlands on a toast already marked removed - ✅
F7 ·exchangeposts the session cookie without the requiredx-effect-auth-csrfheader - ✅
F8 ·openconsumes a stale queued callback from an earlier attempt - ✅
F9 ·2026-10-13-desktop-sign-in.sqlcannot be re-applied
Production impact
Open errors in the changed files
| Issue | Service | Occurrences | File |
|---|---|---|---|
| @maple/api/vcs/VcsSourceRepositoryNotFoundError | maple-ai | 68 | apps/web-foldkit/src/page/registry.ts |
| @maple/http/errors/WarehouseMalformedQueryError | maple-vcs-sync | 60 | packages/auth/src/yielded/tables.ts |
| @maple/mcp/errors/McpQueryError | maple-chat | 45 | apps/web-foldkit/src/page/registry.ts |
| @maple/http/errors/WarehouseMalformedQueryError | maple-vcs-sync | 42 | packages/auth/src/yielded/tables.ts |
| @maple/mcp/errors/McpQueryError | maple-chat | 36 | apps/web-foldkit/src/page/registry.ts |
| @maple/http/errors/WarehouseConfigError | maple-api | 18 | packages/auth/src/yielded/tables.ts |
| @maple/http/errors/WarehouseConfigError | maple-api | 18 | packages/auth/src/yielded/tables.ts |
| @maple/mcp/errors/McpQueryError | maple-ai | 18 | apps/web-foldkit/src/page/registry.ts |
| @maple/http/errors/IntegrationsUpstreamError | maple-chat | 15 | apps/web-foldkit/src/page/registry.ts |
| @maple/api/vcs/GithubAppError | maple-chat | 14 | apps/web-foldkit/src/page/registry.ts |
After this merges, Maple checks whether they stop.
Production traffic of the changed files (last 7 days)
| File | Calls/day | Busiest operations |
|---|---|---|
apps/backend/src/services/hazel-auth.ts |
1.4k | POST 573/day, 0.1% errGET 446/day, 1.4% errGET 168/day, 0.0% err |
apps/backend/src/rpc/middleware/auth.ts |
737 | POST 573/day, 0.1% errPOST 164/day, 0.4% err |
apps/web/src/lib/access-token.ts |
737 | POST 573/day, 0.1% errPOST 164/day, 0.4% err |
packages/auth/src/yielded/desktop-login.ts |
737 | POST 573/day, 0.1% errPOST 164/day, 0.4% err |
apps/backend/src/services/hazel-session.ts |
614 | GET 446/day, 1.4% errGET 168/day, 0.0% err |
Telemetry this change adds and removes (4)
- ➕ span name
InviteMailer.send·apps/backend/src/services/invite-mailer.ts:72 - ➕ span name
AccessTokenVerifier.verify·packages/auth/src/yielded/access-token.ts:132 - ➕ span name
HazelAuth.authorizePasskeyAction·packages/auth/src/yielded/policy.ts:42 - ➕ span name
HazelAuth.authorizeOAuthAction·packages/auth/src/yielded/policy.ts:86
What was checked
safeReturnToparses against a fixed origin, so/ /evil.examplereturns null (return-to.ts:15-16)readSessionretries withtimes: 3then falls back to SignedOut (app/auth.ts:36-37)exchangesendsx-effect-auth-csrf: 1with the cookie (access-token.ts:39)
Files not reviewed (11)
The review ended before it read these diffs, so nothing above vouches for them.
.github/workflows/deploy-prd.ymlapps/backend/package.jsonapps/backend/src/rpc/handlers/users-avatar.test.tsapps/backend/src/rpc/middleware/auth.test.tsapps/backend/src/scripts/migrate-to-hazel-auth/import-clerk-identities.test.tsapps/backend/src/services/hazel-auth-config.test.tsapps/backend/src/services/hazel-auth.test.tsapps/backend/src/services/invitations.test.tsapps/backend/src/services/invite-mailer.test.tsapps/backend/src/test/chat-sync-db-harness.tsapps/backend/src/worker.ts
7b899c9 · Updated on every push. Reply "won't fix" to dismiss a finding, or mention @maple-review-bot to ask about one.
Annotations
Check warning on line 56 in apps/web-foldkit/src/page/create-workspace.ts
maple-review-bot / Maple / review
correctness: `slugify` can end a long workspace slug in `-`, which the form then rejects
`.replace(/^-+|-+$/g, "")` runs before `.slice(0, 48)`, so a name whose hyphenated form is longer than 48 characters and has a hyphen at index 48 (`a…a b` → `a…a-`) auto-fills a slug that `SLUG_PATTERN` rejects, and `SubmittedWorkspace` returns without dispatching `CreateWorkspace`, showing "Use at least 3 lowercase letters, numbers or hyphens" for a slug the user never typed.
Check notice on line 17 in packages/db/sql/2026-10-13-desktop-sign-in.sql
maple-review-bot / Maple / review
maintainability: Hand-written DDL names the PK `yielded_browser_login_namespace_attempt_id_pk`, the migration says `yielded_browser_login_pkey`
The runbook applies this file to prd before `db:migrate`, so `CREATE TABLE IF NOT EXISTS "yielded_browser_login"` in `packages/db/migrations/20261013222300_hazel_auth/migration.sql` is skipped and the live table keeps this PK name, which the snapshot records as `yielded_browser_login_pkey` — the tracked schema then never matches the database `db:generate`/`db:verify` diff against.