Projekt-Dashboard — Runbook
Setup-, Deploy- und Betriebs-Anleitung. Geschwister: _index.md (Projekt-Stub), plan.md (Implementations-Plan).
Pre-Deploy-Steps (einmalig, vor cdk deploy DashboardStack)
Step 1 — Google OAuth Client anlegen
Cognito braucht einen Google-OAuth-Client für „Sign in with Google”.
- Browser: Google Cloud Console — OAuth Client erstellen
- Projekt anlegen (oder existierendes nutzen — Vorschlag „Agentic Ventures Dashboard”)
OAuth 2.0 Client IDs→Create Credentials- Application type: Web application
- Name:
av-dashboard - Authorized JavaScript origins:
https://dXXXXXX.cloudfront.net(kennst du erst nach erstem Deploy — fürs erste leer lassen oder Platzhalter, später updaten)https://dashboard.agenticventures.dehttp://localhost:4321
- Authorized redirect URIs:
https://agentic-ventures-dashboard.auth.eu-central-1.amazoncognito.com/oauth2/idpresponse- Wichtig: das ist die Cognito-Hosted-UI-Callback, NICHT die Frontend-Callback
- Speichern → Client-ID + Client-Secret notieren
Step 2 — Secret in AWS Secrets Manager (av-production)
# 1Password Memo "Google OAuth dashboard" → Client-ID + Secret bereithalten
aws secretsmanager create-secret \
--name dashboard/google-oauth \
--description "Google OAuth Client fuer Cognito Sign-in" \
--secret-string '{"client_id":"123-abc.apps.googleusercontent.com","client_secret":"GOCSPX-..."}' \
--profile av-prod --region eu-central-1Verifikation:
aws secretsmanager get-secret-value \
--secret-id dashboard/google-oauth \
--query SecretString --output text \
--profile av-prod --region eu-central-1 | jq .Step 3 — Cost-Reader-Role im mgmt-Account
Lambda im av-production assumed Role im mgmt-Account um org-weite Cost-Explorer-Daten zu lesen.
Erst Stack einmal deployen (Step 4), dann den Output RefreshLambdaRoleArn notieren, dann diese Role im mgmt-Account anlegen:
# In mgmt-Account (Profile mgmt)
aws iam create-role \
--role-name dashboard-cost-reader \
--assume-role-policy-document '{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {"AWS": "<RefreshLambdaRoleArn aus DashboardStack-Output>"},
"Action": "sts:AssumeRole"
}]
}' \
--profile mgmt
aws iam attach-role-policy \
--role-name dashboard-cost-reader \
--policy-arn arn:aws:iam::aws:policy/AWSBillingReadOnlyAccess \
--profile mgmt
# Plus inline policy für Cost Explorer
aws iam put-role-policy \
--role-name dashboard-cost-reader \
--policy-name CostExplorerRead \
--policy-document '{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": ["ce:GetCostAndUsage","ce:GetCostForecast","ce:GetDimensionValues"],
"Resource": "*"
}]
}' \
--profile mgmtAnmerkung Cost Explorer: Cost Explorer muss org-weit aktiviert sein, sonst sieht der mgmt-Account nur eigene Kosten. Prüfen:
aws ce get-cost-and-usage \
--time-period Start=2026-05-01,End=2026-05-14 \
--granularity DAILY --metrics UnblendedCost \
--group-by Type=DIMENSION,Key=LINKED_ACCOUNT \
--profile mgmt --region us-east-1Wenn die Groups alle Sub-Account-IDs zeigen → org-weit aktiv. Sonst: Console → AWS Cost Management → Settings → enable „Include linked accounts”.
Step 4 — Deploy
cd ~/source/agents-platform
AWS_PROFILE=av-prod npm run synth -- DashboardStack # synth zur Verifikation
AWS_PROFILE=av-prod npx --prefix infra cdk diff DashboardStack
AWS_PROFILE=av-prod npx --prefix infra cdk deploy DashboardStackErster Deploy bricht ggf bei der Google-IdP-Resource ab wenn das Secret noch nicht da ist → Step 2 wiederholen, dann nochmal.
Outputs notieren (kommen am Ende des Deploys auf stdout):
CloudFrontDomain→ fuer Cloudflare-CNAME (Sprint 1.5)CloudFrontDistributionId→ fuer Frontend-Deploy-InvalidationFrontendBucketName,DataBucketNameUserPoolId,UserPoolClientId,CognitoFullDomain→ fuer Frontend.envRefreshLambdaRoleArn→ in Step 3 fuer mgmt-Trust-Policy verwenden
Step 5 — Lambda manuell testen (Smoke)
AWS_PROFILE=av-prod aws lambda invoke \
--function-name agent-dashboard-refresh \
--region eu-central-1 \
/tmp/dashboard-out.json
cat /tmp/dashboard-out.json | jq .
AWS_PROFILE=av-prod aws s3 ls s3://av-dashboard-data-425924867359/api/data/ --region eu-central-1
AWS_PROFILE=av-prod aws s3 cp s3://av-dashboard-data-425924867359/api/data/aws-costs.json - --region eu-central-1 | jq .Wenn aws-costs.json echte Zahlen zeigt → Backend live.
Step 6 — Frontend bauen + deployen
av-cockpit hat ein eigenes Deploy-Script das Build + S3-Sync + CloudFront-Invalidation macht:
cd ~/source/av-cockpit
npm install
npm run deploy # = npm run build && aws s3 sync out/ s3://... + cloudfront invalidationAlternativ manuell:
cd ~/source/av-cockpit
NEXT_STATIC=1 npm run build
AWS_PROFILE=av-production aws s3 sync out/ s3://av-dashboard-frontend-425924867359/ --delete --region eu-central-1
AWS_PROFILE=av-production aws cloudfront create-invalidation \
--distribution-id <CloudFrontDistributionId> --paths "/*"Step 7 — DNS-Cutover (Sprint 1.5)
Cloudflare-DNS:
- CNAME
dashboard→<CloudFrontDomain>.cloudfront.net - Proxied: OFF (DNS only — CloudFront macht eigenes TLS, Cloudflare-Proxy würde es brechen)
ACM-Cert in us-east-1 fuer dashboard.agenticventures.de:
- Wird via Stack-Update angefuegt (TODO Sprint 1.5)
- Validation via Cloudflare-CNAME (manueller Schritt)
Debug
Lambda-Logs
AWS_PROFILE=av-prod aws logs tail /aws/lambda/agent-dashboard-refresh --follow --region eu-central-1Cognito-Sign-Up fehlgeschlagen
Pre-Sign-Up-Lambda blockiert? Log lesen:
AWS_PROFILE=av-prod aws logs tail /aws/lambda/av-dashboard-presignup --follow --region eu-central-1Häufige Ursachen:
- ALLOWED_EMAIL passt nicht (in Lambda env
ALLOWED_EMAILchecken) - Google-Account vs
hello@-Mail-Mismatch (case-sensitivity? Lambda lowert beides)
Frontend zeigt White-Screen
- Browser-Devtools öffnen → Network-Tab → 404? 403?
- CloudFront-Invalidation laufen lassen
- localStorage purgen → erneut einloggen
Rollback
Daten-Bucket wiederherstellen
Data-Bucket ist versioned, 30 Tage Lifecycle:
AWS_PROFILE=av-prod aws s3api list-object-versions \
--bucket av-dashboard-data-425924867359 \
--prefix api/data/aws-costs.json --region eu-central-1
# vorherige Version restoren mit aws s3api copy-object --copy-source <bucket>/<key>?versionId=<id>Stack komplett zurueckrollen
AWS_PROFILE=av-prod npx --prefix ~/source/agents-platform/infra cdk destroy DashboardStackHinweis: Buckets haben RemovalPolicy.RETAIN — bleiben übrig und müssen manuell gelöscht werden falls gewollt.
Cost-Watching
Erwartet Sprint 1 Kosten/Monat:
- CloudFront PriceClass 100: ~$0 fuer Marvin-only-Traffic
- S3 Storage (Frontend + Data): <100 MB → ~$0.003
- Lambda 3x/Tag × 30 Tage × <30s × 1024 MB: ~$0.05
- Cognito User Pool (1 MAU): kostenlos im Free-Tier
- Total: <$2/Monat
Bei > $10/Monat: prüfen ob Cron öfter läuft als erwartet oder Bucket-Sync ungewollt scant.
Agent-Cockpit Live-State (seit 2026-05-21)
Live-Sync der state.md-Files pro Projekt nach S3 + CloudFront.
Pfade
| Komponente | Pfad |
|---|---|
| Vault SoT | intern/projekte/<slug>/state.md |
| Push-Script | ~/source/av-cockpit/scripts/push-state.mjs |
| Local Mirror | ~/source/av-cockpit/public/api/states/<slug>.json |
| S3 | s3://av-dashboard-data-425924867359/api/states/<slug>.json |
| Live URL | https://dashboard.agenticventures.de/api/states/<slug>.json |
CloudFront-Config
- Distribution:
E3KFMPWSKO68UU - Cache-Policy:
av-states-livesync(IDfd4aa170-ccda-4eb3-bf54-5ed2676762d8) —MinTTL=0,DefaultTTL=10,MaxTTL=30 - Behavior:
/api/states/*→DashboardStackCDNOrigin2E8B97E77(data-Bucket) - Auth: Cloudflare Access (gleiche Schutz-Schicht wie Rest der Site)
Push-Commands
cd ~/source/av-cockpit
npm run push-state -- <slug> # ein Projekt → S3 + local
npm run push-all-states # alle Projekte mit state.md
npm run push-state -- <slug> --local # nur local (kein S3, kein AWS-Login nötig)
npm run push-state -- <slug> --force # ignoriert Diff-CheckDiff-Check: Push skipped wenn S3-Object-Hash mit lokalem Body übereinstimmt (Metadata.content-sha256).
Troubleshooting
Push fehlt SSO-Token:
✗ <slug> ExpiredToken: The provided token has expired
→ aws sso login --profile av-production
Frontend zeigt „verbinde…” statt „live”:
- Browser DevTools → Network: GET
/api/states/<slug>.jsonStatus? - 200: SWR-Bug oder JSON-Parse-Fehler — Console checken
- 404: state.md nicht gepusht →
npm run push-state -- <slug>laufen lassen - 302 zu cloudflareaccess.com: Marvin nicht eingeloggt → CF Access Login
CloudFront cached zu lang (>30s):
aws cloudfront create-invalidation --distribution-id E3KFMPWSKO68UU \
--paths "/api/states/<slug>.json" --profile av-productionKostet $0.005 pro Invalidation, in Praxis nie nötig.
state.md gepflegt, Dashboard zeigt veralteten Stand:
- Push gelaufen? → S3 prüfen:
aws s3 ls s3://av-dashboard-data-425924867359/api/states/ - CloudFront-TTL? → max 10s warten oder F5 mit Cache-Bust
- Browser-Tab nicht sichtbar? → SyncIndicator zeigt „paused”, Page-Visibility-API pausiert SWR
Kosten-Monitoring
Bestehende AWS Cost Explorer Konfiguration deckt api/states/* automatisch ab (gleicher Bucket wie api/data/*).
Ziel: <0.10. Worst-Case (Polling 24/7 ohne Visibility-Pause): ~$4/Monat. Siehe plan-agent-cockpit.md §Kosten.
Related
- plan.md — Implementations-Plan v2
- plan-agent-cockpit.md — Agent-Cockpit Erweiterungs-Plan (2026-05-21)
- v1-obsidian-bases-2026-05-14.md — verworfener v1-Plan
- _index.md — AWS-Org-Setup
- accounts.md — Account-Mapping
- chat-output-disziplin.md — CLAUDE.md Rule 24 Detail