I spent a good part of today trying to get an admin overlay working on Hugo-generated landing pages. The idea sounds simple - let admin users edit theme colors, hero text, and CTAs directly from the production page without going back to the admin panel. The implementation turned into a lesson about cross-domain authentication and why the browser security model exists.
Things We Learned Today
The landing pages for Intensity Magic - the company admin and product platform - are static Hugo sites served from their own domains. The admin panel lives on a different domain entirely. I wanted to inject a floating edit panel on those Hugo pages so an admin could tweak colors and text without context-switching. The first version used an inline email/password form, which hit a wall immediately: cross-domain sessions don’t survive a redirect flow. Supabase’s signInWithPassword worked technically, but storing credentials in a form on a static site felt wrong.
The fix was a popup-based Google OAuth flow. The admin clicks “Login” on the overlay, which opens a popup to the IntensityMagic domain’s /auth/overlay-login route. That route kicks off Google OAuth with PKCE. When the callback comes back, the popup uses postMessage to send the session tokens back to the landing page opener, which calls setSession on its local Supabase client. The targetOrigin validation on the postMessage is critical - without it, any page could intercept tokens. I also added CSP headers, cache-control to prevent token caching, and an X-Content-Type-Options header on the callback HTML. The security surface of cross-domain auth is large and each layer matters.
A separate discovery came from working on AuthorMagic’s book enrichment route. The route had a hand-rolled FORMAT_TO_BINDING_TYPE mapping that translated format strings to binding types - kindle became "ebook", audible became "audiobook". The problem is those aren’t the canonical values. The database stores "kindle" and "audible" directly, and the canonical normalizeBindingTypeWithContext() function in binding-types.ts already handles all the edge cases. The mapping had drifted from its source of truth silently. The data was being written, queries were returning results, but the values didn’t match what other parts of the system expected. This is a classic case of two sources of truth that slowly diverge - the fix is to always use the canonical abstraction and never duplicate it locally.
Things We Did Today
Today’s production release was a big one - nine features and fixes across four apps.
CureCancerMagic - the cancer care coordination app - learned how to read its own email. I wired up contact extraction so the system automatically pulls names, phone numbers, and email addresses out of incoming messages and links them to the right people. It also figured out who sent what, who was CC’d, and who got mentioned in the body. I also tracked down a bug where inbound emails were arriving with blank bodies - a missing API key meant the system was silently failing to fetch the actual message content. I added a recovery tool so coordinators can backfill those messages.
The IntensityMagic landing pages got the admin overlay I described above, plus automatic brand color support. Landing pages now pull their colors from a central config instead of requiring someone to enter them manually for each page. I also swept out a bunch of dead UI code that had been sitting around from a previous iteration.
On the platform infrastructure side, I connected error monitoring to the app registry so Sentry issues automatically route to the right team. A few housekeeping items too - removing a legacy database column that stopped being used months ago and fixing a worktree cleanup filter that was tripping on a file it should have been ignoring.
AuthorMagic’s book rankings sync got smarter about distinguishing between “this book has no rankings data” and “something actually broke.” The admin notification email now separates those two cases instead of lumping them together as failures.
Fun Things to Try
The admin overlay currently saves config changes to the database but can’t trigger a Hugo rebuild from the landing page domain - the build-deploy endpoint lacks CORS headers. Adding CORS to that one endpoint would close the loop and let admins see their changes go live without touching the admin panel at all. That feels like a small change with outsized impact on the editing workflow.
The contact extraction in CureCancerMagic opens up an interesting direction. Right now the AI extracts contacts from individual emails. I could aggregate across all communications for a case and build a relationship graph - who talks to whom, how often, and in what context. That would give care coordinators a visual map of the care team’s communication patterns.