Components (Terraform, ~/Terraform/Bobs-lifebot/terraform/)
apigateway.tf— HTTP APIlifebot-api;$defaultstage,auto_deploy, throttle rate 20 / burst 40; CORS allow-origins = app domain + CloudFront +www.udemy.com, headersauthorization, content-type, x-lifebot-key. Routes:POST /ingest/{health,hevy,study},POST /advice(key-auth); JWT routes live inapp_backend.tf.app_backend.tf— Cognito JWT authorizer;stats+chatLambdas; JWT routesGET /api/stats,POST /api/goals(→ stats, branches on method),POST /api/chat.cognito.tf— user poollifebot-users(email login,allow_admin_create_user_only, 12-char password policy), PKCE public client (generate_secret = false,codeflow, 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 withpermissions_boundary = local.permissions_boundary_arnAWSLambdaBasicExecutionRole+ a scoped inline policy (see below).lambda.tf/app_backend.tf/hevy_sync.tf— five functions,python3.12, arm64, 256 MB; env varsTABLE_NAME,LOOKBACK_DAYS,BEDROCK_MODEL_ID; 14-day log retention.storage.tf— DynamoDBlifebot-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 inlambdas/{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
Related
Compiled from
wiki/projects/Bobs-lifebot/LLD.md · git is the source of truth