← Dream Tracker
Field notes
How it's made
A private journal that turns what you remember of a dream into a watercolor painting. Here's the stack underneath it, and the six bugs that stood between working code and a working demo.
Claude CodeAuth0VercelSupabaseOpenAI gpt-image-1Next.js 16 · App RouterTypeScriptTailwind CSS v4
Components
Login
Email and password via Auth0, so a dream journal is only ever visible to the person who wrote it.
Profile
An Auth0 Progressive Profiling Action asks new users for their name once, after their first login.
Storage
Entries live in a Supabase Postgres table, scoped to the logged-in user on every read and write.
Illustration
Each entry's text is sent to OpenAI's gpt-image-1, which paints it as a watercolor and the result is saved to Supabase Storage.
Bugs & fixes
In the order they were found.
01
FixedImage upload rejected by Supabase
Invalid key: auth0|6a99f8492d4c5cfff4bc5ce5/…png- Cause
- Auth0 user IDs contain a “|”, which Supabase Storage won't allow in an object key.
- Fix
- Sanitize the user ID to auth0_6a99f849… before building the storage path.
02
FixedNew table invisible to the API
PGRST205 — Could not find the table 'public.dream_entries' in the schema cache- Cause
- PostgREST caches the schema and doesn't always notice a table created moments earlier via the SQL editor.
- Fix
- Reload the cache — the dashboard's “Reload schema cache” button, or notify pgrst, 'reload schema';
03
FixedRow Level Security was off
- Cause
- Supabase's own project-wide anon key — unused by this app, but issued by default — could read or write every dream in the table if it were ever exposed.
- Fix
- Enabled RLS with zero policies (default-deny for anon/authenticated). The app's own service_role key bypasses RLS by design, so nothing else changed.
04
FixedOne login shouldn't be able to break every page
- Cause
- Next.js 16 renamed middleware.ts to proxy.ts, and Auth0's request handler ran on every route by default — so a misconfigured Auth0 client could take the whole site down, not just this demo.
- Fix
- Migrated to proxy.ts and narrowed its route matcher to just this demo's own paths.
05
FixedThe name claim that wasn't there
- Cause
- The Progressive Profiling Action correctly wrote the user's name and set it as a custom ID token claim — confirmed deployed, confirmed running — yet the session never had it. Turned out @auth0/nextjs-auth0 silently strips any claim outside a fixed default allowlist before saving the session, unless told otherwise.
- Fix
- Added a beforeSessionSaved hook that keeps the SDK's default filtering and explicitly re-adds this one custom claim.
06
FixedEditing a dream left the old painting
- Cause
- Each entry's image lives at a fixed path — same user, same entry, same file — so a regenerated image reused the exact same URL. Browsers cache by URL, so the stale picture kept being served.
- Fix
- Append ?v=<timestamp> to the URL on every regeneration, so a new painting is never mistaken for the old one.