Most "AI-powered" side projects follow the same arc: a slick demo, a hardcoded API key, one shared database, zero auth boundaries — and it falls apart the moment a second user shows up. I wanted to build something different: a real, production-shaped application, designed the way a security engineer would design it before a single line of code got written.
That's how Personal Gemini Journal came together — a private, AI-powered diary and brainstorming partner where every architectural decision starts with "what happens if someone tries to abuse this?" instead of "does this work in the demo?"
Personal Gemini Journal isn't just another chatbot wrapper. It's a secure, fully isolated journaling space where you sign in, have real multi-turn conversations with Gemini to brainstorm or reflect, and have those conversations automatically summarized and saved — privately, to you.
The feature I'm most excited about is semantic search over your own past entries. Instead of scrolling through weeks of journal history, you can ask something like "When did I talk about feeling stuck on my job search?" and get back the actual entries that match — not by keyword, but by meaning.
@google/genai
SDK — Gemini 2.5 Flash for conversation and summarization, text-embedding-004
for vector embeddingsSecure Authentication. The frontend never hands the backend a user ID and asks it to be trusted — it sends a Firebase ID token (JWT), and a custom Express middleware verifies that token server-side via the Firebase Admin SDK before deriving identity. The user is always who the token says they are, never who the request body claims they are. Multi-Turn AI Journaling. Conversation history lives in Firestore, maintained by the backend — not the client — so context can't be tampered with mid-conversation. Every call to Gemini is proxied through the server; the API key never touches the browser.
Semantic Search via RAG. When an entry is saved, the backend asks Gemini 2.5 Flash for a concise summary, then passes that summary to text-embedding-004
to generate a vector embedding. A search query gets embedded the same way and matched against those vectors using Firestore's native findNearest
with cosine distance — real retrieval-augmented search, not string matching.
Strict Data Isolation. This is the part I care about most. Data lives under users/{userId}/conversations/{conversationId}
, and isolation is enforced in two independent layers: the Express backend hardcodes every query — including vector search — to the authenticated user's ID, and Firestore Security Rules separately enforce request.auth.uid == userId
. Even if the backend were somehow bypassed, cross-user data leakage (IDOR) isn't just unlikely — it's structurally prevented.
The part of this build that made it feel genuinely different from a typical hackathon project was the process, not just the output:
roles/secretmanager.secretAccessor
scoped to exactly the Gemini API key it needs — not broad project access "to be safe."None of this shows up in a demo GIF. All of it is the difference between a toy and something you could actually ship.
AI-assisted coding tools can generate a working app in minutes — but "working" and "production-ready" are different bars. The interesting engineering problem isn't getting Gemini to respond to a prompt; it's making sure the system around that response holds up under real users, real attackers, and real scale. Deploying this kind of security-conscious, isolated architecture on Cloud Run is what makes the difference between a weekend demo and something you'd actually trust with your own data.
Built with Gemini 2.5 Flash, Firebase, Firestore, and Cloud Run.
#AccelerateAIwithCloudRun