Skip to content
SamK46Public

About

Batman had Alfred, Harvey had Donna, Ironman had Jarvis, and now you have Graham. (Local-first AI health assistant PWA (React, TypeScript, Vercel, Claude API)).

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Graham

A local-first, AI-powered personal assistant for health and daily routines, built as an installable iPhone PWA. Logging a meal, a glass of water, or a habit takes one or two taps, and Graham turns that history into briefings, reviews, and suggestions without keeping any personal data on a server.

What it does

  • Fast logging for meals, water, habits, supplements, weight, and caffeine, plus Siri Shortcuts URLs (/log?water=1) for hands-free logging.
  • Meal photo logging: snap a photo, get an editable calorie and macro estimate from a vision model, and save it as a reusable "usual."
  • AI features: a morning briefing written from your real data, an evening wind-down recap, a Sunday weekly review, a brain dump that sorts free text into tagged chips, and a chat with an adjustable personality.
  • Pulse surface: a breathing orb with three ranked suggestions for what to log or open next, learned from your own rhythm.
  • Integrations: read-only Google Calendar, weather, UV and sun times, and a tailorable news feed.
  • Your data stays yours: everything lives on the device, with one-tap JSON export and import.

Architecture

flowchart LR
  subgraph Device["iPhone (PWA)"]
    UI[React UI] --> IDB[(IndexedDB logs)]
    UI --> LS[(localStorage config)]
    SW[Service worker]
  end
  subgraph Vercel["Vercel serverless (stateless)"]
    AI["/api/ai"]
    CAL["/api/calendar"]
    PUSH["/api/push + push-cron"]
  end
  UI -- "task + data snapshot" --> AI --> Anthropic[Claude API]
  UI -- "OAuth code / refresh" --> CAL --> Google[Google OAuth]
  UI -- "subscription + times" --> PUSH --> Redis[(Upstash Redis)]
  Cron[External scheduler] --> PUSH -- "generic ping" --> SW
Loading

Design decisions

Local-first storage. Logs live in IndexedDB (via idb) in a single store with a common { id, type, refId, timestamp, data } shape, indexed by type and time, so a new tracker is just a new type value with no migration. Config and preferences live in localStorage.

Stateless AI proxy. The Anthropic API key exists only as a server environment variable. The client sends a task type and a snapshot of the relevant local data; the server picks a fixed, task-specific prompt and token cap, calls the model, and returns structured JSON. Nothing is stored between requests, and the endpoint is rate-limited per IP.

Privacy-preserving push notifications. Web Push with VAPID keys. The server stores only the push subscription and the user's chosen notification times. Every payload is a generic wake-up ping ("Your briefing is ready"), and the real content is composed on the device when the app opens. An external scheduler hits a secret-protected cron endpoint every ~10 minutes to send due pings.

Calendar OAuth without server storage. The serverless function handles only the two steps that need the client secret (code exchange and token refresh). Tokens are handed to the device and kept there. The tradeoff, documented in docs/DEVELOPMENT.md, is acceptable for a read-only scope.

On-device suggestion engine. Every loggable action is a candidate, collapsed to one per domain so the three suggestions are always distinct. Time-of-day curves handle cold start. Once a domain has 5+ logs, a recency-weighted model (30-day half-life) of when you actually log it is blended in by confidence, capped at 0.8 so defaults never fully disappear. All of it is computed locally from existing log history.

Network-first app shell. The service worker serves navigations network-first, so an installed PWA never boots a stale cached shell that references asset hashes a new deploy has already removed.

Stack

React 18, TypeScript, Vite, vite-plugin-pwa (Workbox, injectManifest), IndexedDB (idb), @dnd-kit for drag-to-reorder, Vercel serverless functions, Anthropic API (Claude), Google Calendar API, Web Push (web-push, VAPID), Upstash Redis.

Run it locally

npm install
cp .env.example .env.local   # fill in your own keys
npm run dev

The AI, calendar, and push features each need their own credentials (see .env.example). Full setup, including Google Cloud OAuth and installing the PWA on an iPhone over HTTPS, is in docs/DEVELOPMENT.md.


Built by Sameer Khan, with Claude as a pair programmer.

About

Batman had Alfred, Harvey had Donna, Ironman had Jarvis, and now you have Graham. (Local-first AI health assistant PWA (React, TypeScript, Vercel, Claude API)).

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages