Project · Bobs-lifebot

Bob's Lifebot — High-Level Design

Aggregate personal health/fitness data (Hevy, Apple Health via Health Auto Export, study + nutrition) into one normalized store and layer AI coaching on top — fully serverless, cheap (≤ £5/mo), single-user, secrets out of state. Two consumers: **machines** pushing data in, and a **browser app** Bob reads/chats through. Each gets its own auth tier.

type hldstatus activeaws · serverless · bedrock · cognito

Architecture

flowchart TB
  subgraph Sources
    HEVY[Hevy API] ; HAE[Health Auto Export iOS] ; STUDY[Udemy userscript]
  end
  subgraph Browser
    SPA[Web SPA<br/>CloudFront + S3]
  end
  GW["API Gateway (HTTP API)<br/>throttled 20/40"]
  subgraph Lambdas["Lambdas · python3.12 · arm64 · perms-boundary"]
    ING[ingest] ; ADV[advice] ; CHAT[chat] ; STATS[stats] ; HSYNC[hevy_sync]
  end
  DDB[("DynamoDB<br/>lifebot-records")]
  S3[("S3 raw archive<br/>versioned · AES256")]
  BED["Bedrock<br/>Claude Haiku 4.5"]
  SSM["SSM SecureString<br/>/lifebot/*"]
  COG[Cognito user pool<br/>PKCE · admin-create-only]
  CRON[EventBridge daily]

  HEVY & HAE & STUDY -- "x-lifebot-key" --> GW
  SPA -- "Cognito JWT" --> GW
  SPA -. login .-> COG
  GW --> ING & ADV & CHAT & STATS
  CRON --> ADV
  HSYNC --> DDB
  ING --> S3 & DDB
  ADV & CHAT --> BED
  ADV & CHAT & STATS --> DDB
  SSM -.-> ING & ADV & HSYNC

Auth model (two tiers)

  1. Machine ingestPOST /ingest/{health,hevy,study} and POST /advice are gated by a shared x-lifebot-key header (an SSM SecureString), because the callers (Hevy, Health Auto Export, a Udemy userscript) are machines with changing IPs — a shared secret is the right M2M auth, not IP-locking. Ingest is idempotent (de-dupes re-sends).
  2. Browser appGET /api/stats, POST /api/goals, POST /api/chat require a Cognito JWT (API Gateway JWT authorizer). The user pool is admin-create-only (no public signup), single user, 12-char password policy, PKCE public SPA client, short tokens (id/access 1 h, refresh 30 d). Login via the Cognito hosted UI.

Components

  • API Gateway HTTP API (cheaper than REST) — CORS restricted to the app domain, the CloudFront domain, and www.udemy.com (the study userscript); throttled (rate 20 / burst 40) to cap Bedrock + DynamoDB cost/DoS.
  • ingest — archives raw JSON to versioned S3, normalizes to daily records in DynamoDB.
  • advice — daily digest (EventBridge) + on-demand POST /advice; Bedrock Haiku; stores advice.
  • chatPOST /api/chat, Q&A over your recent records via Bedrock (lookback window).
  • statsGET /api/stats (dashboard) + POST /api/goals (branches on HTTP method).
  • hevy_sync — pulls Hevy workouts into the store.
  • Storage — DynamoDB lifebot-records (on-demand, 25 GB free tier); S3 raw archive (versioned, AES256, private).
  • Frontend — CloudFront + S3 SPA; runtime config.js templated by Terraform.

Security

  • Permissions boundary on every Lambda role — a ceiling the deploy user itself cannot exceed. Each role also gets a least-privilege inline policy scoped to the one DynamoDB table ARN and only the actions it needs (ingest write-only; chat/stats read; etc.).
  • Bedrock scoped to the Haiku model/inference-profile ARNs only; KMS Decrypt gated by a kms:ViaService = ssm condition (can only decrypt SSM params).
  • Secrets in SSM SecureString, never in Terraform state; Cognito password set out-of-band.
  • S3 private + versioned + AES256; raw health payloads never logged anywhere world-readable.
  • AI privacy — Bedrock/Anthropic do not train on API data (confidential by default), which is why Bedrock Haiku was chosen over an unpaid Gemini tier for health data.
  • Full findings + minor items in Bobs-lifebot/security-review.

Cost

Target ≤ £5/mo, typically pennies: HTTP API + Lambda (free tier covers personal volume), DynamoDB on-demand (25 GB free), S3 (pennies), SSM standard (free), Bedrock Haiku (~pennies at ~1 call/day on aggregated data). No always-on DB (RDS/Aurora was explicitly rejected as budget-blowing). Deliberately aggregate to daily/hourly before any AI call — never dump raw HealthKit samples into a prompt.

Compiled from wiki/projects/Bobs-lifebot/HLD.md · git is the source of truth