This is a submission for the Hacktoberfest Weekend Challenge: Build for a Friend
This is a team submission
Teammate - @piyushnextgen
What We Built
Daybook began with a simple problem: a friend of ours who was already journaling, but most of the time, the entries just stayed there.
He could write about a stressful day, a productive week, a bad habit, or something that kept coming up, but noticing those patterns across dozens of entries was almost impossible manually.
So we built Daybook for him.
Daybook is a private AI journal that reads your entries locally and helps surface patterns in your habits, mood, routines, and thoughts over time. Instead of just storing what you write, it helps you understand what keeps showing up.
The important part is where this happens.
Your journal never needs to leave your device. Daybook uses a local LLM, so the AI can work with your personal entries without sending them to a cloud AI service.
We wanted to build something that was genuinely useful to one person, while also answering a question we kept coming back to:
Can AI be helpful with something as personal as journaling without requiring you to give away your data?
Daybook is our attempt at an answer.
Demo
Note: Local AI features are not available in the deployed demo because DayBook runs AI locally using Ollama. The backend requires a running Ollama instance on port
11434.
Code
greenbugx
/
Daybook
A private AI journal that learns your habits, finds patterns, and helps you improve, powered by a local LLM with your data staying on-device.
A local-first AI journal that learns your habits, finds recurring patterns, and helps you reflect on what you could improve
Everything happens on your own machine. Your writing never leaves your computer, and the AI that reads it runs locally through Ollama, so there is no account to create, no key to paste, and no company holding a copy of your thoughts.
Table of Contents
- Screenshots
- What DayBook Does
- Requirements
- AI Model
- Installation
- Running DayBook
- How the Project Works
- Project Structure
- Database
- How AI Answers Questions
- Journal Images
- Session and Ownership
- State That Lives Only in Memory
- Frontend Details
- Backend Details
- API Reference
- Data on Disk
- Development
- Privacy
- Troubleshooting
- License
Screenshots
What DayBook Does
DayBook is built around a few simple habits that turn into something useful over time.
Write a page a day. Each date has one journal entry with a topic…
How We Built It
We wanted Daybook to be useful with genuinely personal data, which made the usual "send it to an AI API" approach feel wrong for this project.
So we built it local-first from the beginning.
At the core is Gemma 3 4B, an open-weight model running locally through Ollama. There is no OpenAI API, Gemini API, or hosted AI service sitting between the journal and the model.
The whole stack lives on the user's machine:
flowchart LR
U["You, in a browser"] -->|"localhost:5173"| V["Vite dev server"]
V -->|"React app"| U
V -->|"proxies /api to 3001"| B["Fastify backend"]
B -->|"SQL, WAL mode"| D[("SQLite<br/>data/daybook.db")]
B -->|"local HTTP"| O["Ollama"]
O --> G["Gemma 3 4B"]
The frontend is built with React 19 and TypeScript, while the local backend handles application logic and communication with Ollama. Journal data and personal memories are stored locally using SQLite.
That separation was intentional. The application can use an LLM for things like identifying recurring habits, finding patterns across entries, and pointing out areas for reflection, while the actual data stays on the same machine.
When a user asks Daybook to analyze their journal, the request stays within that local stack:
Journal → Backend → Ollama → Gemma → Analysis → Daybook
That's what allows Daybook to look for recurring habits, patterns, wins, struggles, and areas for improvement without requiring a cloud AI API.
To run Daybook, we only need three local processes:
- React + TypeScript for the interface
- Local backend for application logic
- Ollama + Gemma 3 4B for AI analysis
No API key required
What Daybook Can Actually Do
- Daily entries with topic, mood, weather, location, and a rich text editor
- Goals tied to a date, so you can see what you meant to do next to what you wrote about
- A reflection chat where you ask in plain language and get an answer grounded in your actual entries
- Per entry observations the AI generates after you save
- Memory suggestions you approve or dismiss, so nothing is remembered without your say so
- A streak that counts unbroken days honestly
- One image per entry, stored as a real file on your disk
- Saved quotes collected in a Library
- A profile built through onboarding that shapes how reflections are written
The AI is grounded, not creative
This was the part we spent the most time on. A journal AI that invents things is worse than useless, because you cannot trust what it tells you about your own life.
The system prompt carries 18 explicit rules. The model is told that its only source of truth is the context handed to it, that it must never invent entries, dates, goals, or statistics, that it must separate a one time observation from a recurring pattern, and that it must say plainly when there is not enough evidence. It is also told to avoid diagnosing anything about mental or physical health, and to respect a thingsToAvoidAssuming list the user fills in during onboarding.
Eight reflection intents decide how the model reads the context:
| Intent | Looks for |
|---|---|
RECURRING_PATTERNS |
The same thing showing up again |
MOOD_EMOTIONAL |
Mood and emotional tone |
STRUGGLES |
What keeps getting in the way |
WINS_PROGRESS |
Wins, progress, momentum |
GOALS |
Goal follow through |
HABITS_ROUTINES |
Routines and timing |
CHANGE_OVER_TIME |
How things shifted over time |
SELF_UNDERSTANDING |
What Daybook knows about you |
Questions that never reach the model
We route every question before it costs anything.
If a question can be answered from the database, Daybook answers it with SQL and skips the model entirely. Ask how many entries you have written, or what your goals are, and you get an exact answer instantly. Only questions that genuinely need interpretation go to Gemma.
flowchart TD
Q["Your question"] --> ROUTE["classifyAiIntent"]
ROUTE -->|"quote"| DQ["Return today's quote"]
ROUTE -->|"streak"| DS["Return the streak"]
ROUTE -->|"goals"| DG["Read goals from SQL"]
ROUTE -->|"journal"| DJ["Read entries from SQL"]
ROUTE -->|"reflection, or anything else"| LLM["Ask Gemma"]
LLM --> INTENT["classifyReflectionIntent"]
INTENT --> CTX["buildAiContext<br/>entries, goals, profile,<br/>stats, memories, observations"]
CTX --> PROMPT["buildAiSystemPrompt<br/>18 grounding rules plus context"]
PROMPT --> RUN["Ollama generates the answer"]
RUN --> OUT["Answer with its intent label"]
This keeps answers exact where exactness matters, and spends model time only where interpretation is actually needed.
The streak is not AI
The streak is plain arithmetic over your entry dates. It converts dates to day numbers, ignores the future, returns zero unless you wrote today, then counts backwards while days are unbroken. If you skip a day, it ends there. No model, no guessing, no being generous with dates.
High-Level System Architecture
flowchart TB
subgraph USER["User Machine - 100% Local"]
BROWSER["Browser<br/>localhost:5173"]
subgraph FRONTEND["Frontend - React 19 + TS + Vite + Tailwind 4"]
APP["App.tsx<br/>Routing and shared state"]
VIEWS["Views<br/>InteractiveBook / JournalBook<br/>Calendar / Goals / Library<br/>AnalyticsAI / Settings / Onboarding"]
APICLIENT["lib/api.ts<br/>Typed fetch wrapper for /api/*"]
end
subgraph BACKEND["Backend - Fastify :3001"]
ROUTES["REST API<br/>users / journals / goals<br/>quotes / memories / ai/*"]
ZOD["Zod Validation"]
CONTEXT["buildAiContext()<br/>entries + goals + memories + profile"]
AILOGIC["Intent routing and<br/>grounded prompt building"]
end
subgraph DATA["Data Layer"]
DRIZZLE["Drizzle ORM"]
SQLITE["SQLite<br/>better-sqlite3<br/>data/daybook.db<br/>WAL + foreign keys ON"]
FILES["data/media/<userId>/<date>/<id>.png"]
end
subgraph AI["AI Layer - Ollama :11434"]
OLLAMA_SDK["ollama npm SDK<br/>format json, temp 0.4"]
GEMMA["Gemma 3:4b"]
end
end
BROWSER --> APP --> VIEWS --> APICLIENT
APICLIENT -- "/api/* via Vite proxy" --> ROUTES
ROUTES --> ZOD --> DRIZZLE --> SQLITE
ROUTES --> CONTEXT --> AILOGIC --> OLLAMA_SDK --> GEMMA
ROUTES --> FILES
Using an open-weight model also meant we weren't locked into one AI provider. The model can be changed, experimented with, or eventually customized without redesigning the entire product around a proprietary API.
For a journal, where the data can be deeply personal, local inference isn't just a technical choice. It is part of the product itself.
Data model
Twelve tables, with foreign keys turned on so deleting an entry cleans up its goals, media rows, and observations in one step.
erDiagram
users ||--o| user_profiles : has
users ||--o{ local_sessions : opens
users ||--o{ journal_entries : writes
journal_entries ||--o{ journal_goals : contains
journal_entries ||--o| journal_media : illustrated_by
journal_entries ||--o{ journal_observations : analysed_by
journal_entries ||--o{ memories : may_seed
quotes ||--o{ saved_quotes : saved_as
Details we are proud of
Ownership is decided on the server, every time. No endpoint accepts a user id, an entry id, or a file path from the client. Each request resolves the active local session, finds that user's entry for the requested date, then works only with the media and goals hanging off it.
Images are safe by construction. Files are named with a server generated UUID, so your original filename never reaches the filesystem. Every path is built from your user id and the date you asked for, and any path resolving outside the media folder is refused. We tested a ../../../../etc/passwd payload and it landed on a UUID filename inside the media root like any other upload.
A photo failure never costs you your writing. The journal text is saved first. If the image fails afterwards, your text stays saved, your preview stays visible, and you get a clear error instead of a false success.
Date changes cannot leak content. Opening a date fires four requests in parallel, each carrying an AbortController. Switch to October 5 while October 4 is still loading and the stale reply is discarded, so October 4 can never appear on October 5.
Tech Stack
| Layer | Technology | Port / Location |
|---|---|---|
| Frontend | React 19, TypeScript, Vite 8, Tailwind CSS 4, shadcn/ui, Lucide |
:5173 · src/
|
| Backend | Fastify 5, Zod 4, Drizzle ORM, tsx |
:3001 · server/src/index.ts
|
| Database | better-sqlite3, Drizzle Kit | data/daybook.db |
| AI | Ollama, Gemma 3 4B, Ollama npm package | :11434 |
| Development | pnpm, Ollama | Local |
Running Daybook
Ollama → Backend (:3001) → Frontend (:5173)
↓
Gemma 3 4B
ollama serve
ollama pull gemma3:4b
cd server
pnpm dev
# In another terminal
pnpm dev
Requires Node.js 26 or newer, pnpm, and Ollama.
Why Does Open Innovation Matter?
For Daybook, open innovation wasn't just about using a different AI model. It changed what we were comfortable building in the first place.
A journal is full of things you probably wouldn't want sitting on someone else's servers. Thoughts, bad days, habits, goals, personal memories. We wanted Daybook to actually analyze that information without making the user choose between useful AI and privacy.
Using an open-weight model like Gemma 3 4B made that possible.
Using Gemma 3 4B through Ollama gave us a way to do that. The model runs locally, so in our setup, the journal and its AI analysis can stay on the user's machine without requiring a third-party AI API or API key.
And that's where open models became more interesting to us than just being a cheaper alternative.
We can run the model locally through Ollama, keep the journal and its analysis on the user's machine, and build around the model without depending on a third-party AI API or API key.
It also gives us something a closed API doesn't: control.
We have control over what runs, where it runs, and how we build around it. We can experiment with different models, swap them out, run the system offline, and eventually customize the model for the kind of reflection Daybook needs.
For us, that control matters because of the kind of data we're working with.
It's that we could build an AI product around data we don't want to send anywhere in the first place.
For us, that's the real promise of open innovation: it lets developers decide where AI runs, what it sees, and how the system is built around it.
That's what it meant for Daybook.
My Agent Session
Prize Categories
Gemma - Daybook uses Gemma 3 4B as its core AI model, running locally through Ollama to analyze journal entries while keeping personal data on-device.
Render - Used to deploy the parts of Daybook that need to be accessible beyond the local development environment.
GitHub - Used for open-source development, CI/CD, collaboration, version control, and sharing Daybook's code with the community.







Top comments (2)
This honestly makes me want to pick journaling back up again. I’ve always loved the idea of documenting my life, but having something that can quietly notice the thoughts, patterns, and things I keep coming back to makes it feel so much more meaningful. The AI aspect is such a beautiful finishing touch too.
I genuinely feel like this is the kind of thing I’ve been looking for without even realizing it. Somewhere to put my thoughts, look back on my life, understand myself a little better, and hopefully grow along the way.
Thank you for making something that feels this thoughtful and useful. I can already see myself coming back to it a lot, and I’m genuinely excited to start using it.
Bro, this genuinely made my day. 😭❤️
I really wanted DayBook to feel like something you’d actually keep coming back to, rather than just a project I built for a challenge. Hearing you say that it makes you want to journal again and that you can see yourself using it honestly means a lot.
And yeah, I finally submitted it! 😭 Thank you for taking the time to write this.
Seriously 🫶