The Answering Diary
A journal that writes back, built to be the diary Tom Riddle's wasn't
Built for the Ideathon Challenge at the Gen AI Academy, APAC edition, run by Google Labs. An AI journal where the threat model came from a horror story: the aesthetic borrows from the enchanted diary, the behaviour deliberately doesn't.
Problem
An AI journal is four features: it authenticates you, holds a conversation, remembers what you told it, and answers in its own voice. That is also, almost exactly, the spec for Tom Riddle's diary.
The feature set was never the problem with that object. Anyone who picked it up could write in it, one girl's private memories sat somewhere a hostile party could read at will, and the secret that animated it was embedded in the artifact itself.
Insight
The interesting constraint wasn't building the features. It was building them so the obvious failure mode couldn't happen.
Every requirement in the brief (auth, isolated storage, secrets outside the code) maps to one of the ways the fictional diary betrayed its user. So I let the horror story write the threat model, and made the aesthetic borrow from the enchanted diary while the behaviour deliberately didn't.
What I built
A single Cloud Run service that serves both the page and its API, so the deployed URL is the application and the browser never makes a cross-origin call.
Google sign-in, multi-turn journaling against Gemini, and four things past the spec: a mood chart, semantic search over past entries, a weekly reflection delivered by scheduler, and a streak with a prompt drawn from your own recent themes.
How it fits together
Every request carries a Firebase ID token. The server derives the user id from the verified token and never from the client.
Entries live under a path scoped to that user id, and Firestore's rules deny cross-user reads on their own authority, which is a second, independent layer. The Gemini key is fetched from Secret Manager at runtime and never enters the browser.
Calls I made
Auth proves who is writing; App Check proves what is. Adding it means a stolen token replayed from the command line fails even when it's valid, which is the demo, not the claim.
I also gave Gemini an ordered fallback chain rather than one model name. That looked like over-engineering until, mid-build, two models were retired under me and the third ran out of credits on the same afternoon.
What happened
The chain caught all three: two 'no longer available' errors, one 'credits depleted', and a written message to the user instead of a stack trace.
Fixing it meant rotating the API key with no code change and no rebuild, the Secret Manager design paying for itself under fire. Thirteen unit tests cover the fallback logic, and eighteen post-deploy checks confirm each gate refuses what it should.
What I learned
My worst bug was one I wrote and my own tests couldn't see. I assumed same-origin requests send no Origin header, so the service refused its own frontend. The command-line tests sent no Origin either, so every test passed while the app was unusable in a browser. Test with the client you actually ship.
The second lesson: graceful degradation isn't a nice-to-have you add later. It's the difference between a demo and a product.