KnowledgeVault AI module-spec M-01
internal prototype · canonical JSON + Dreamborn Forge HTML
internal generated
module-spec · supabase_json

KnowledgeVault AI module-spec M-01

M-01 delivers end-to-end expert registration for the KnowledgeVault platform: a mobile signup form collecting name, email, phone, trade, specialties and experience; the POST /api/experts/signup endpoint that atomically creates the experts row, a Supabase Auth user with role=expert in app_metadata, and a Stripe Connect Express account for payouts; and a reusable JWT auth middleware consumed by M-02 and M-04. This module is the cold-start unlock — no sessions, captures or payments can occur until

Planning Surface

Use this to decide what happens next.

Status

approved

Phase

M-01

open questions

No items captured.

Agent Handoff
Start Here

M-01 delivers end-to-end expert registration for the KnowledgeVault platform: a mobile signup form collecting name, email, phone, trade, specialties and experience; the POST /api/experts/signup endpoint that atomically creates the experts row, a Supabase Auth user with role=expert in app_metadata, and a Stripe Connect Express account for payouts; and a reusable JWT auth middleware consumed by M-02 and M-04. This module is the cold-start unlock — no sessions, captures or payments can occur until

Completion Evidence

No explicit evidence field yet. Require tests, screenshots, linked PRs, or reviewed outputs before marking complete.

Open Questions

No section body captured.

Structured Payload

Machine-readable source fields

summary

M-01 delivers end-to-end expert registration for the KnowledgeVault platform: a mobile signup form collecting name, email, phone, trade, specialties and experience; the POST /api/experts/signup endpoint that atomically creates the experts row, a Supabase Auth user with role=expert in app_metadata, and a Stripe Connect Express account for payouts; and a reusable JWT auth middleware consumed by M-02 and M-04. This module is the cold-start unlock — no sessions, captures or payments can occur until an authenticated expert identity exists.

module id

M-01

api routes
authpatherrorsmethodrequestresponse
public — creates the account, no prior JWT required/api/experts/signup- Duplicate email — 409 { error: email already registered, code: duplicate_email, status: 409 } - Missing required field — 422 { error: field is required, code: missing_field, status: 422 } - Invalid email format — 422 { error: email is invalid, code: invalid_email, status: 422 } - Stripe API failure or timeout — 201 with stripe_onboarding_url: null and deferred: true (expert row committed, stripe_account_id = null) - Supabase Auth createUser failure — 500 { error: account creation failed, code: auth_error, status: 500 } with no experts row writtenPOSTJSON body: { name: string (required), email: string (required, valid email format), phone: string (optional), role_type: string (required, one of: Plumber | Electrician | HVAC Tech | Pipefitter | Millwright | Other), specialties: string[] (required, may be empty), years_experience: integer (optional, 0-60) }201: { expert_id: string (uuid), stripe_onboarding_url: string | null, deferred: boolean }
data model
notes

auth.expert_id() is a SQL function installed in D-01-01: (auth.jwt()->app_metadata->>expert_id)::uuid. All downstream modules (M-02 sessions, M-04 payments) reference this function in their RLS policies — must not be dropped or renamed. experts.stripe_account_id is nullable — null means Stripe onboarding is deferred and payout cannot be released until expert completes bank connection.

tables
rlsnameindexespurposekey columns
RLS enabled. Policy experts_self_rw: SELECT and UPDATE allowed WHERE id = auth.expert_id(). INSERT allowed via BYPASSRLS (service-key only). DELETE not permitted. No public SELECT.experts- UNIQUE INDEX on email (enforced by UNIQUE constraint)Stores registered expert profiles and Stripe Connect Express account references for payout routing.- id uuid NOT NULL DEFAULT gen_random_uuid() PRIMARY KEY - name text NOT NULL - email text UNIQUE NOT NULL - phone text NULL - role_type text NOT NULL - specialties text[] NOT NULL DEFAULT '{}' - years_experience integer NULL - bio text NULL - stripe_account_id text NULL - created_at timestamptz NOT NULL DEFAULT now()
module name

Auth & Expert Onboarding

deliverables
idnametypeinputsoutputsdescription
D-01-01001_experts_table.sqlmigration- Supabase Postgres instance with M-00 migrations applied - supabase db push or psql execution context- experts table in public schema with all specified columns and constraints - auth.expert_id() function in Supabase DBCreates the experts table in the public schema with all required columns. RLS is enabled with a self-access policy using the auth.expert_id() helper, and service-key INSERT bypass. Also installs the auth.expert_id() SQL helper function used by downstream module RLS policies.
D-01-02POST /api/experts/signupapi_route- JSON body: { name: string (required), email: string (required), phone: string (optional), role_type: string (required), specialties: string[] (required, may be empty array), years_experience: integer (optional) }- 201 success: { expert_id: string (uuid), stripe_onboarding_url: string | null, deferred: boolean } - 409 on duplicate email - 422 on missing required field with field name in error body - 500 on Supabase Auth creation failure with no partial expert row writtenCreates an expert record, a Supabase Auth user with role=expert in app_metadata, and a Stripe Connect Express account. Returns expert_id and stripe_onboarding_url. If Stripe fails, the expert row is still committed with stripe_account_id=null and deferred=true is returned. If Supabase Auth creation fails, no expert row is written and 500 is returned. Signup form has no password field — Supabase user is created with a random UUID password; all future logins use magic link (passwordless).
D-01-03expertAuthMiddlewareservice- HTTP request with Authorization: Bearer JWT header (Supabase-issued JWT)- Mutated request with req.expert = { id: uuid } on valid expert JWT - 401 JSON error on missing or expired token - 403 JSON error on valid JWT with wrong roleReusable Next.js API middleware that validates the Supabase Auth JWT from the Authorization: Bearer header, reads role from app_metadata, and attaches expert_id to the request context. Exported as a single importable function. Used by all /api/experts/*, /api/sessions/*, and /api/products/queue routes in M-02 and M-04.
D-01-04ExpertSignupFlow — screens S-01-a, S-01-b, S-01-ccomponent- S-01-a: user-entered form values - S-01-b: stripe_onboarding_url from POST /api/experts/signup response - S-01-c: Stripe Connect return URL query params or deferred flag from API response- S-01-a submits POST /api/experts/signup then navigates to S-01-b on success or shows inline field error on failure - S-01-b fires external redirect to stripe_onboarding_url - S-01-c CTA navigates to product queue (S-02-a route)Three-screen mobile signup flow. S-01-a (Expert Signup Form): full-screen single-column scroll with sticky submit button, fields for name, email, phone (optional), trade selector (bottom-sheet picker: Plumber, Electrician, HVAC Tech, Pipefitter, Millwright, Other), specialties multi-select (bottom-sheet, chips), years experience (optional numeric 0-60), and submit CTA. Inline 409 error on email field shows correct copy. S-01-b (Stripe Redirect Interstitial): centered loading message, fires redirect to stripe_onboarding_url within 500ms of mount. S-01-c (Onboarding Confirmation): centered succe
D-01-05Supabase Auth JWT claim config — role=expert in app_metadataconfig- Supabase Auth admin API (SUPABASE_SERVICE_KEY) - experts table with at least one test row (created via D-01-02)- Expert JWTs with app_metadata.role = expert and app_metadata.expert_id = uuid - Verified auth.expert_id() SQL function returning correct UUID in RLS context - Test confirming JWT structure and middleware reads from app_metadata not user_metadataDocuments and validates the JWT claim structure established in D-01-02. Supabase Auth automatically includes app_metadata fields in the issued JWT — no custom hook required. Claim structure: app_metadata.role = expert, app_metadata.expert_id = experts.id uuid. The auth.expert_id() SQL helper (installed in D-01-01) reads expert_id from JWT for use in RLS policies across all modules. Deliverable includes a test confirming JWT structure and that middleware reads from app_metadata not user_metadata.
integrations
callspurposeservice
- POST /v1/accounts — { type: express, capabilities: { transfers: { requested: true } }, business_type: individual, email: expert_email } — creates Express account, response.id stored as experts.stripe_account_id - POST /v1/account_links — { account: stripe_account_id, refresh_url: NEXT_PUBLIC_BASE_URL/experts/signup/stripe-refresh, return_url: NEXT_PUBLIC_BASE_URL/experts/onboarding-complete, type: account_onboarding } — returns url returned as stripe_onboarding_url in API responseCreate Express payout accounts for experts so no bank details are stored on the platform. Stripe handles all KYC and bank verification.Stripe Connect
- supabase.auth.admin.createUser({ email: expert_email, password: crypto.randomUUID(), app_metadata: { role: expert, expert_id: uuid }, email_confirm: true }) — creates Auth user, sets app_metadata claims, marks email as pre-confirmedCreate authenticated user accounts for experts and issue JWTs with role=expert in app_metadata. Future logins use magic link (passwordless) — no password set by expert at signup.Supabase Auth
prerequisites
  • M-00 complete: Supabase project initialized, database connection accessible, and base schema migrations applied
  • Supabase Auth enabled with email/password provider and admin API access (SUPABASE_SERVICE_KEY in environment)
  • Stripe account with Connect enabled — STRIPE_SECRET_KEY present in environment
  • Environment variables set: SUPABASE_URL, SUPABASE_SERVICE_KEY, STRIPE_SECRET_KEY, NEXT_PUBLIC_BASE_URL
open questions

No items captured.

schema version

1.0