# TotalRecall - AI-Powered Daily Journal > Your day, analyzed. A journal that writes itself. --- ## Concept TotalRecall is a privacy-first, self-hostable daily journal application that captures life through multiple input methods (text, photos, voice, data) and uses AI to generate thoughtful, reflective journal entries at the end of each day. The core philosophy: **your data is yours**. Whether you host it yourself or use a hosted service, you own everything. The same codebase runs everywhere. --- ## The Problem - Most journaling apps are siloed, paid, or data-mining platforms - Manual journaling is time-consuming and inconsistent - People capture moments (photos, voice memos, scattered notes) but never reflect on them - AI tools exist but require manual input of context ## The Solution TotalRecall aggregates all your daily inputs throughout the day, then at your command (or scheduled), sends everything to an AI to synthesize your day into a coherent, reflective journal entry. --- ## Features ### Input Capture | Type | Description | Storage | |------|-------------|---------| | **Text Notes** | Quick thoughts, feelings, observations | Markdown | | **Photos** | Camera or gallery uploads | JPEG/PNG in media folder | | **Voice Memos** | Audio recordings | WebM/Opus | | **Location** | Optional GPS tagging of entries | Lat/long metadata | | **Health Data** | Manual entry (steps, mood, sleep) | JSON metadata | ### AI Journal Generation - Aggregates all entries for a day - Sends to configurable AI provider - Generates reflective, narrative journal entry - Stores generated journal alongside raw entries ### AI Provider Support (Pluggable) - **OpenAI** - GPT-4, GPT-4-Turbo, GPT-3.5 - **Anthropic** - Claude 3 Opus, Sonnet, Haiku - **Ollama** - Local LLM (Llama, Mistral, etc.) - **LM Studio** - Local LLM with OpenAI-compatible API --- ## Architecture ``` ┌─────────────────────────────────────────────────────────────────┐ │ End User │ │ │ │ ┌────────────────────┐ ┌────────────────────┐ │ │ │ Web App │ │ Mobile App │ │ │ │ (React/PWA) │ │ (Future: iOS) │ │ │ └─────────┬──────────┘ └─────────┬──────────┘ │ │ │ │ │ │ └──────────┬─────────────────┘ │ │ │ │ │ ▼ │ │ ┌──────────────────────┐ │ │ │ REST API │ │ │ │ /api/v1/* │ │ │ │ │ │ │ │ - Authentication │ │ │ │ - Entries CRUD │ │ │ │ - Journal Gen │ │ │ │ - Settings │ │ │ └──────────┬───────────┘ │ │ │ │ │ ▼ │ │ ┌──────────────────────┐ │ │ │ SQLite Database │ │ │ │ + Media Storage │ │ │ └──────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────┘ ``` ### Tech Stack | Layer | Technology | Reason | |-------|------------|--------| | Backend Runtime | **Bun** | Fast startup, native TypeScript, small Docker image | | API Framework | **Hono** | Lightweight, fast, works everywhere | | Database | **SQLite / MySQL / PostgreSQL** (via Prisma ORM) | Switch databases with one line, zero-config for SQLite | | Frontend | **React + Vite** | Fast dev, PWA capable | | Styling | **Tailwind CSS** | Rapid UI development | | Container | **Docker + Docker Compose** | One-command deployment | --- ## Deployment Modes TotalRecall runs the same codebase whether self-hosted or on a SaaS platform. ### Single-User (Self-Hosted) ```yaml services: totalrecall: image: ghcr.io/totalrecall/totalrecall:latest ports: - "3000:3000" volumes: - ./data:/data ``` - Users create accounts locally - No external dependencies - One Docker command to run ### Multi-Tenant (SaaS) ```yaml services: totalrecall: image: ghcr.io/totalrecall/totalrecall:latest ports: - "3000:3000" environment: - DEPLOYMENT_MODE=multi-tenant - JWT_SECRET=${JWT_SECRET} - DATABASE_URL=file:/data/totalrecall.db volumes: - ./data:/data ``` - Multiple users with accounts - API key authentication - Same features as self-hosted --- ## API Design ### Authentication ``` Authorization: Bearer {api_key} ``` All requests (except auth endpoints) require a valid API key. ### Endpoints ``` POST /api/v1/auth/register # Create account POST /api/v1/auth/login # Get API key POST /api/v1/auth/logout # Invalidate session GET /api/v1/days # List days with entries GET /api/v1/days/:date # Get day's data (entries + journal) DELETE /api/v1/days/:date # Delete day and all entries POST /api/v1/entries # Create entry GET /api/v1/entries/:id # Get entry PUT /api/v1/entries/:id # Update entry DELETE /api/v1/entries/:id # Delete entry POST /api/v1/entries/:id/photo # Upload photo to entry POST /api/v1/entries/:id/voice # Upload voice to entry POST /api/v1/journal/generate/:date # Generate AI journal for date GET /api/v1/journal/:date # Get generated journal GET /api/v1/settings # Get user settings PUT /api/v1/settings # Update settings (AI provider, etc.) GET /api/v1/health # Health check (no auth) ``` ### Response Format ```typescript // Success { "data": { ... }, "error": null } // Error { "data": null, "error": { "code": "NOT_FOUND", "message": "Entry not found" } } ``` --- ## Data Models ### User ```typescript interface User { id: string; // UUID email: string; // Unique passwordHash: string; // bcrypt createdAt: string; // ISO timestamp } ``` ### API Key ```typescript interface ApiKey { id: string; userId: string; keyHash: string; // SHA-256 hash, not stored plaintext name: string; // "iPhone", "Web", etc. lastUsedAt: string; createdAt: string; } ``` ### Entry ```typescript type EntryType = 'text' | 'voice' | 'photo' | 'health' | 'location'; interface Entry { id: string; userId: string; date: string; // YYYY-MM-DD type: EntryType; content: string; // Text or metadata JSON mediaPath?: string; // Path to uploaded file metadata?: { source?: 'manual' | 'health' | 'calendar'; location?: { lat: number; lng: number }; duration?: number; // For voice entries [key: string]: unknown; }; createdAt: string; } ``` ### Journal ```typescript interface Journal { id: string; userId: string; date: string; // YYYY-MM-DD content: string; // Markdown entryCount: number; // How many entries were used generatedAt: string; } ``` ### Settings ```typescript interface Settings { userId: string; aiProvider: 'openai' | 'anthropic' | 'ollama' | 'lmstudio'; aiConfig: { // Provider-specific config (API keys stored encrypted) model?: string; baseUrl?: string; // For Ollama/LM Studio }; journalPrompt?: string; language: string; } ``` --- ## File Storage ``` /data/ ├── totalrecall.db # SQLite database └── media/ └── {user_id}/ └── {date}/ ├── entry-{uuid}.webm # Voice recordings └── entry-{uuid}.jpg # Photos ``` --- ## Project Structure ``` totalrecall/ ├── backend/ │ ├── src/ │ │ ├── index.ts # Entry point │ │ ├── app.ts # Hono app setup │ │ ├── routes/ │ │ │ ├── auth.ts # Registration, login │ │ │ ├── days.ts # Day operations │ │ │ ├── entries.ts # Entry CRUD │ │ │ ├── journal.ts # Journal generation │ │ │ └── settings.ts # User settings │ │ ├── services/ │ │ │ ├── db.ts # Database connection │ │ │ ├── storage.ts # File storage operations │ │ │ └── ai/ │ │ │ ├── mod.ts # Provider interface │ │ │ ├── openai.ts │ │ │ ├── anthropic.ts │ │ │ └── ollama.ts │ │ ├── middleware/ │ │ │ └── auth.ts # API key validation │ │ └── types.ts │ ├── prisma/ │ │ └── schema.prisma # Database schema │ ├── Dockerfile │ └── package.json │ ├── frontend/ │ ├── src/ │ │ ├── main.tsx │ │ ├── App.tsx │ │ ├── pages/ │ │ │ ├── Home.tsx # Today's view │ │ │ ├── History.tsx # Browse past days │ │ │ ├── Journal.tsx # View generated journals │ │ │ └── Settings.tsx # Configuration │ │ ├── components/ │ │ │ ├── EntryInput.tsx │ │ │ ├── PhotoCapture.tsx │ │ │ ├── VoiceRecorder.tsx │ │ │ ├── EntryList.tsx │ │ │ └── JournalView.tsx │ │ ├── lib/ │ │ │ └── api.ts # API client │ │ └── hooks/ │ ├── Dockerfile │ └── package.json │ ├── docker-compose.yml # Full stack ├── docker-compose.prod.yml # Production overrides ├── Dockerfile # Multi-stage build └── README.md ``` --- ## Implementation Phases ### Phase 1: Backend Foundation ✅ - [x] Bun + Hono setup - [x] Prisma ORM with SQLite (dev) / PostgreSQL (prod) - [x] User registration & login (JWT) - [x] API key authentication - [x] Basic CRUD routes ### Phase 2: Data Management ✅ - [x] Entry CRUD with media support - [x] File upload handling (photos, voice) - [x] Day aggregation queries - [x] Media file storage ### Phase 3: AI Integration ✅ - [x] AI provider interface - [x] OpenAI implementation - [x] Anthropic implementation - [x] Ollama implementation - [x] Journal generation endpoint ### Phase 4: Frontend Core ✅ - [x] React + Vite setup - [x] API client library - [x] Authentication flow - [x] Home page (today's entries) - [x] Entry creation (text input) ### Phase 5: Media Inputs ✅ - [x] Photo capture/upload - [x] Voice recording - [x] Entry list with media preview ### Phase 6: Journal & Settings ✅ - [x] Journal viewing - [x] Settings page - [x] AI provider configuration - [x] Prompt customization ### Phase 7: Deployment ✅ - [x] Docker multi-stage build - [x] Docker Compose setup - [x] Health checks - [ ] PWA manifest ### Phase 8: Future - [ ] iOS app (SwiftUI) - [ ] Android app (Kotlin) - [ ] Calendar integration - [ ] Health data sync --- ## Security Considerations - API keys hashed with SHA-256 (never stored plaintext) - Passwords hashed with bcrypt - CORS configurable for domain restriction - Rate limiting on auth endpoints - File upload validation (type, size) - SQL injection prevention via ORM --- ## Environment Variables | Variable | Default | Description | |----------|---------|-------------| | `DATABASE_URL` | `file:./data/totalrecall.db` | SQLite/PostgreSQL/MySQL connection | | `MEDIA_DIR` | `./data/media` | Directory for uploaded files | | `JWT_SECRET` | (required) | Secret for JWT signing | | `PORT` | `3000` | Server port | | `CORS_ORIGIN` | `*` | Allowed origins | | `RATE_LIMIT` | `100/hour` | Auth endpoint rate limit | ### Database Connection Examples ```bash # SQLite (development, default) DATABASE_URL="file:./data/totalrecall.db" # PostgreSQL (production) DATABASE_URL="postgresql://user:password@localhost:5432/totalrecall" # MySQL DATABASE_URL="mysql://user:password@localhost:3306/totalrecall" ``` --- ## Licensing This project is open source under [MIT License](LICENSE). You are free to: - Use it for yourself - Host it for others - Modify and redistribute No attribution required (but appreciated).