Project · Bobs-lifebot

Bob's Lifebot — Low-Level Design

type lldstatus activeaws · terraform · lambda · iam

Components (Terraform, ~/Terraform/Bobs-lifebot/terraform/)

  • apigateway.tf — HTTP API lifebot-api; $default stage, auto_deploy, throttle rate 20 / burst 40; CORS allow-origins = app domain + CloudFront + www.udemy.com, headers authorization, content-type, x-lifebot-key. Routes: POST /ingest/{health,hevy,study}, POST /advice (key-auth); JWT routes live in app_backend.tf.
  • app_backend.tf — Cognito JWT authorizer; stats + chat Lambdas; JWT routes GET /api/stats, POST /api/goals (→ stats, branches on method), POST /api/chat.
  • cognito.tf — user pool lifebot-users (email login, allow_admin_create_user_only, 12-char password policy), PKCE public client (generate_secret = false, code flow, scopes openid/email/profile), token TTLs 1 h / 1 h / 30 d; owner user created, password set out-of-band.
  • iam.tf — per-Lambda roles, each with permissions_boundary = local.permissions_boundary_arn
  • AWSLambdaBasicExecutionRole + a scoped inline policy (see below).
  • lambda.tf / app_backend.tf / hevy_sync.tf — five functions, python3.12, arm64, 256 MB; env vars TABLE_NAME, LOOKBACK_DAYS, BEDROCK_MODEL_ID; 14-day log retention.
  • storage.tf — DynamoDB lifebot-records (on-demand); S3 raw archive (versioned, AES256, private).
  • ssm.tf — SecureString params under /lifebot/* (ingest key, Hevy key).
  • eventbridge.tf — daily schedule → advice. frontend.tf/domain.tf — CloudFront + S3 SPA, ACM/Route53.

IAM — least privilege per role

Role DynamoDB Other
ingest PutItem, BatchWriteItem s3:PutObject (archive), ssm:GetParameter /lifebot/*, kms:Decrypt (ViaService ssm)
advice Query, GetItem, PutItem bedrock:InvokeModel (Haiku ARNs only), ssm:GetParameter, kms:Decrypt (ssm)
chat Query, GetItem bedrock:InvokeModel (Haiku ARNs only)
stats Query, GetItem, PutItem

All bound by the permissions boundary (iam/lifebot-permissions-boundary.json); a separate read-only iam/claude-assistant-policy.json + securityread.json grant security-ops read (IAM/CloudTrail/GuardDuty/Access-Analyzer) for audits.

Configuration

  • Region eu-west-2; AWS CLI profile claude-assistant; all IaC in Terraform.
  • Secrets by home: ingest x-lifebot-key + Hevy API key → SSM SecureString /lifebot/* (out-of-band); Cognito owner password → aws cognito-idp admin-set-user-password (out-of-band); nothing sensitive in Terraform state or the repo.
  • Model: BEDROCK_MODEL_ID = Claude Haiku 4.5 EU inference profile; adapter in lambdas/{advice,chat}/model.py (swap-friendly).

Sequences

Health ingest (idempotent):

sequenceDiagram
  participant HAE as Health Auto Export
  participant GW as API Gateway
  participant ING as ingest Lambda
  participant S3 as S3 archive
  participant DDB as DynamoDB
  HAE->>GW: POST /ingest/health (x-lifebot-key, JSON)
  GW->>ING: proxy (key verified in-handler)
  ING->>S3: PutObject raw JSON (versioned)
  ING->>DDB: PutItem normalized daily record (de-duped)
  ING-->>HAE: 200

Browser chat (JWT):

sequenceDiagram
  participant SPA as Web app
  participant COG as Cognito
  participant GW as API Gateway (JWT authz)
  participant CH as chat Lambda
  participant BED as Bedrock Haiku
  SPA->>COG: PKCE login → JWT
  SPA->>GW: POST /api/chat (Authorization: Bearer JWT)
  GW->>GW: validate JWT (audience/issuer)
  GW->>CH: proxy
  CH->>CH: aggregate recent records (LOOKBACK_DAYS)
  CH->>BED: InvokeModel (summarized context)
  BED-->>CH: reply
  CH-->>SPA: answer
Compiled from wiki/projects/Bobs-lifebot/LLD.md · git is the source of truth