LifeTrace Quickstart
Get LifeTrace running on your Mac in ten minutes. At the end you'll have the engine running, one or more data sources connected, an LLM provider configured, and a web dashboard showing your traces.
What you need
- macOS (the engine runs cross-platform, but iMessage / Apple Photos / Google Timeline ingestion are macOS-flavored — Linux + Windows still work for everything else)
- Node.js 18 or newer — check with
node --version - An LLM provider — either an OpenAI/Anthropic/Google API key, an OpenAI Codex subscription, or Ollama running locally
LifeTrace has no required environment variables and no database setup. Everything is stored locally in ~/Library/Application Support/LifeTrace/.
1. Clone and install
git clone https://github.com/mattshax/lifetrace.git
cd lifetrace/lifetrace-engine
npm install
2. Start the engine
You have two ways to run it:
Option A: Desktop app (recommended)
npm run electron
LifeTrace launches as a real macOS desktop window. The engine is embedded in the app so you don't need a separate terminal session, and Full Disk Access (needed for iMessage / Photos) gets granted to LifeTrace.app itself instead of your terminal.
The first launch auto-rebuilds native modules for Electron's runtime (~30s); subsequent launches are instant.
Option B: CLI mode (for development)
npm start
You'll see:
LifeTrace engine listening on port 9030
Web UI: http://localhost:9030
Sync manager started
Open http://localhost:9030 in your browser.
3. Connect your first data sources
LifeTrace currently ships with seven connector types. Pick whichever you want to start with — you can add more later.
iMessage (macOS only, recommended first)
- Go to Sources → click iMessage
- The preflight check tells you if the engine has permission. If it says permission denied:
- In the Electron app: System Settings → Privacy & Security → Full Disk Access → toggle on LifeTrace.app, then quit and relaunch.
- In CLI mode: toggle on your terminal app instead, then restart the engine.
- Click Re-check access.
- Once confirmed, click Connect iMessage.
- The engine starts syncing in the background. With 100k+ messages this may take several minutes on first run.
Apple Photos (macOS only)
Same Full Disk Access flow as iMessage. The connector reads the local Photos library, clusters geotagged photos by 30-min / 200-m proximity, and emits each cluster as a place visit. Photos themselves never leave your machine — only GPS coordinates and timestamps are read.
Google Timeline (location history)
Google retired Takeout for Location History in late 2024. The only current export path is from inside the Google Maps mobile app:
- On your phone: Google Maps → profile icon → Settings → Location services → Timeline → Export Timeline data.
- AirDrop (or email/cloud-share) the resulting
Timeline.jsonto your Mac. - In LifeTrace: Sources → Timeline → drop the file in.
The connector handles both the new on-device schema (semanticSegments array) and legacy Takeout archives (timelineObjects / timelineEdits).
Gmail / Google Calendar
Both require a one-time Google Cloud OAuth setup:
- Go to console.cloud.google.com, create a project.
- Enable the Gmail API and the Calendar API.
- Create OAuth 2.0 credentials (Web application type) with redirect URI
http://localhost:9030/api/auth/gmail/callback. - In LifeTrace: Sources → Gmail (or Google Calendar), paste Client ID + Secret, sign in.
Once connected, both sync incrementally and wake when your laptop does.
Local files
- Sources → Local files, enter a directory path (e.g.
~/Documents/notes). - The engine walks the directory and ingests
.mdand.txtfiles.
4. Configure an LLM provider
Chat, embeddings, and the daily journal all need an LLM provider. Pick one:
Ollama (free, local, recommended for privacy)
- Install Ollama from ollama.com
- Pull the embedding model:
ollama pull nomic-embed-text - Pull a chat model:
ollama pull llama3.2 - LifeTrace auto-detects Ollama on
localhost:11434— no configuration needed
OpenAI API key
- Get a key from platform.openai.com
- In LifeTrace, go to Providers → select type OpenAI
- Enter your API key, set model to
gpt-5.4(or your preference) - Click Save, then Set default for chat and Set default for embeddings
OpenAI Codex subscription (via ChatGPT Plus/Pro)
- Go to Providers → select type OpenAI Codex
- Click Connect Codex — a device code appears
- Copy the code, open the verification URL, sign in with your ChatGPT account
- When authorized, the provider is created automatically. Click Set default for chat
GLM or other OpenAI-compatible endpoints
- Go to Providers → select type OpenAI-compatible
- Enter Name, API key, Base URL (e.g.,
https://api.z.ai/api/coding/paas/v4for GLM), Model name - Save and set as default
5. Embed your corpus
After ingestion, items need to be embedded for the RAG chat to work.
Embedding runs automatically after each sync if an embedding provider is available. You can also trigger it manually:
- Dashboard → Embedding status section shows progress
- API:
POST http://localhost:9030/api/embedembeds the next batch (50 items)
With 50k items and Ollama locally, expect a couple of hours. Cloud embedding providers (text-embedding-3-small) are much faster but cost money.
6. Try the chat
- Click Chat in the sidebar
- Ask something grounded in your corpus: "What did I tell Mom about the trip?"
- Responses include citations back to the specific corpus items used
7. (Optional) Enable the daily journal
- Settings → Daily journal → Enable.
- Every morning the engine auto-generates a short narrative about yesterday's activity.
The Journal page has three tabs:
- Timeline — daily entries, with Day / Week / Month scope toggles, search across content, tag filtering, year-section dividers, and pagination.
- Threads — three insight cards (Constants of your life · Episodic chapters · Emerging recently) above a heatmap of tag frequencies over time at daily / weekly / monthly granularity.
- History — GitHub-contribution-style heatmap: one row per year, every day in your corpus shaded by trace volume, journal entries outlined in teal.
Backfill historical entries
The default journal is forward-only, but you can fill the past:
- Journal → Timeline → + Backfill opens a date-range picker.
- Fill missing days generates entries only for days that don't have one yet (additive, cheap).
- Regenerate all rewrites every entry in the range — use this after editing your About-you profile or when prompts change.
- The picker maxes out at yesterday (today's day is incomplete); the server rejects future dates.
About-you profile (recommended)
The journal correctly attributes activities to people in your life only if you tell it who's who. Settings → About you is a free-text profile injected into every prompt.
- Click Generate suggestion from corpus and the engine reads your existing entries + top contacts, asks the LLM to draft a starter profile, and shows you the result.
- Edit anything that's wrong (especially: whose activity is whose — interviews, illnesses, work).
- Save.
After saving, run Regenerate all on the date range you care about so existing entries pick up the profile.
Places
If you've connected Photos and/or Timeline, the Places page in the sidebar shows every visit on a map — Leaflet + dark CartoDB tiles, soft teal markers, faint chronological lines connecting same-day visits, marker clustering that scales to thousands of points. Click a marker to open that day's journal entry in a side panel.
Reverse geocoding
Coordinates aren't very useful to a language model on their own. The engine ships with a Nominatim-backed reverse geocoder (1 req/sec, persistent SQLite cache, ~110m granularity).
After your first Places sync, kick off the backfill once:
curl -sX POST http://localhost:9030/api/locations/geocode
Or from the Places page once we add a button (planned). The job:
- Resolves every unique coordinate to neighborhood, city, region (e.g.
41.91, -87.65 → Lincoln Park, Chicago, Illinois). - Auto-rewrites
content_texton every location item with the rich place name. - Marks them for re-embedding so RAG (chat) can match queries like "where do I usually get coffee".
A library of ~1000 unique places takes ~20 minutes the first time, then resolves instantly from cache.
8. (Optional) Enable remote status
Check your engine's corpus state from any browser even when your Mac is asleep:
- Settings → Remote status → Enable
- Copy the Status key shown below the toggle
- Open lifetrace.io/status in any browser, paste the key
What's next
- User guide — deeper walkthrough of each feature
- API reference — full endpoint docs for building on top of the engine
- Maintaining — how we keep docs in sync (for contributors)
Troubleshooting
"No access token. Reconnect via Providers." after Codex connection Your saved provider's OAuth tokens are missing. Remove and reconnect the Codex provider.
iMessage sync crashes midway through
Shouldn't happen anymore — but if it does, the engine resumes from the last checkpoint on restart. Each batch of 500 items persists a lastSyncAt timestamp.
Embedding not running
Check the dashboard's Embedding status section. If it says no provider available, either install Ollama (ollama pull nomic-embed-text) or add an OpenAI-compatible provider.
Port 9030 already in use
Set PORT=9031 in an .env file in lifetrace-engine/ or PORT=9031 npm start.
Journal doesn't generate The scheduler only runs if the feature is enabled, yesterday has no existing entry, and a chat provider is configured. It generates in the background within a minute of detection. Use Generate yesterday manually to force it.
Backfill says "Nothing to do — every day in range already has an entry" Every day in the range is already populated. To rewrite them with newer prompts or your edited About-you profile, use the Regenerate all button on the backfill panel.
Electron rebuild fails on Apple Silicon
If npm run electron errors with "incompatible architecture", it's because better-sqlite3 was built against system Node and not Electron's runtime. The script auto-handles this via electron/check-rebuild.js, but you can force it with arch -arm64 npx @electron/rebuild -f -w better-sqlite3 -a arm64.