English | 한국어
An AI-powered platform for end-to-end database operations. Run performance analysis, incident diagnosis, operational automation, and simulation across Amazon Aurora (PostgreSQL and MySQL), RDS for MySQL and SQL Server (standalone, non-Aurora instances), DocumentDB, DynamoDB, and ElastiCache (Redis/Valkey/Memcached), all through natural-language conversation.
A full-stack operations platform for DBAs: diagnose by talking to it, execute safely, monitor the whole fleet.
The sections below group the core capabilities by area, and the /path on each item is the Web UI page it lives on.
🤖 AI & Conversation
- AI Chat (
/chat): ask for performance analysis, incident diagnosis, and operational work in plain language. Answers cite the official AWS/Aurora documentation as their evidence via the AWS MCP Server (SigV4) - Ask the Fleet (
/ask): natural-language fleet queries such as "show me the clusters above 80% CPU". NL→filter compiler + saved views - AI Runbooks (
/runbooks): save, search, and reuse a chat diagnosis or prescription as a markdown playbook - Cross-Device Chat Sessions: conversations persist in DynamoDB, so you can pick one up on another device or browser (1.5s debounced sync + offline cache + 90-day TTL)
- Agent Memory Inspector (
/preferences): read and delete the preferences/facts held in AgentCore Memory. Namespaces are keyed on the Cognito sub, which blocks cross-user reads
📊 Performance & Analysis
- Performance Analysis: slow query analysis, EXPLAIN plan tree with automatic anti-pattern detection, index recommendations, anomaly detection
- Schema Lineage (
/schema): FK relationship graph visualized from livepg_constraintintrospection - Replication Topology (Dashboard): writer/readers plus per-instance AuroraReplicaLag, promotion tier, multi-AZ
- Redundant Indexes (Dashboard): automatic detection of prefix-covered, fully duplicate, and unused indexes
- Capacity Forecasting: 30/60/90-day linear-regression forecasts for storage, connections, and AAS, plus the point each one crosses its threshold
- PG Log Insights + Keyword Search (
/dashboard): CloudWatch Logs Insights grouped by category, with search terms AND-joined and regex sanitized - Saved Query Library (Query Lab): store, tag, and cross-device load the SQL you run often
- MySQL Dashboard Parity: Schema, Indexes, and Log Insights all support Aurora MySQL
📈 Monitoring & Alerting
- Monitoring Dashboard (
/dashboard): live cluster status, metric visualization, Health Score - Fleet Overview (
/fleet): every cluster at a glance, with ETL freshness badges (fresh/stale/no_data) - SLO Tracker (
/slo): measured availability and p-mean query-latency SLOs with error-budget burn-down - Compound Alert Rules (
/alerts): a single threshold or an AND/OR DSL (per-operand window/agg). Six DBA presets + two-way Slack Ack - Alert Impact: the slow queries, concurrent events, and concurrent alarms within ±5min of an alarm, in an inline panel (incident triage)
- Cost Anomaly Detection (
/cost): catches spikes in daily Bedrock spend through a triple gate of z-score, absolute delta, and relative ratio
🔧 Operations & Safety
- Operations Automation: parameter changes, DDL execution, scaling, snapshot and restore (all human-in-the-loop approved)
- Approval Guard: every write tool validates a DDB approval row server-side. The agent cannot flip
approved=trueby itself: it must also pass anapproval_id(issued when a DBA approves in/approvals), and the cluster, the action_type, a 30-minute window, and an atomic consume are all enforced - Simulation UI (
/simulator): estimate upgrades (compatibility + method matrix + ordered plan), parameter changes, ACU cost, and DDL impact directly, without going through chat
🚨 Incidents & Audit
- Incident Diagnosis: RCA, signal correlation, timeline reconstruction
- Incident Timeline (
/timeline): every signal for one cluster (alarms, RDS events, schema changes, proactive findings, Slack acks, executed writes) on a single time axis, with category chip filters - DBOps Activity Log (
/activity): a chronological record of who requested, approved, and executed what (compliance audit + post-incident review). Also queryable from chat through thequery_activity_auditMCP tool - Daily Operations Report (
/reports): thereport_generatorLambda aggregates 24h of metrics at midnight and summarizes them in Korean with Bedrock Claude (template fallback on failure)
🏢 Platform & Multi-Account
- Cross-Account: manage Aurora across several AWS accounts from one place with a hub-spoke IAM pattern
- Cluster Registration Wizard (
/clusters): same/cross-account mode toggle, plus a connection-test-only 3-step pre-flight (STS AssumeRole + DescribeDBClusters + master secret) - Schema Migration Auto-Trigger:
cdk deployinjects a SHA-256 hash of the SQL directory into schema_version, so a change migrates automatically
Web UI (Next.js, static) ──SSE──▶ AgentCore Runtime (Strands Agent)
│ │
AgentCore Gateway AWS MCP Server
(Cedar Policy) (SigV4, official AWS docs)
│
┌─────────────────────┼─────────────────────┐
▼ ▼ ▼ ▼
Performance Incident Operations Simulation
MCP MCP MCP MCP
(4 custom MCP servers, 64 tools)
│
▼
Aurora PG Cache ◀── Data Collection Pipeline
(hot cache) (ETL, Event Processor, Report, Monitor)
-
Custom tools go through the Gateway (write approval is enforced by the tool-level
approval_guard; the Cedar Policy Engine is bound at the Gateway in LOG_ONLY as defense in depth), official AWS docs go straight to the AWS MCP Server over SigV4, which exposes read-only documentation tools and nothing else -
Dashboard data comes from the pre-collected cache: no AWS API is called directly during rendering (the live panels are the only exception: topology and backup)
-
Single Agent + Gateway: one AgentCore Runtime plus one Gateway MCP, tuned for latency and token count
-
CDK-First: all infrastructure is managed through CDK only.
cdk deploy --alldeploys the whole thing -
Human-in-the-loop: reads are automatic, changes require DBA approval (enforced by the tool-level
approval_guard: fail-closed, payload-hash bound, single-use)
| Layer | Technology |
|---|---|
| Agent | Strands Agents SDK, AgentCore Runtime/Gateway |
| LLM | Amazon Bedrock Claude |
| Frontend | Next.js 16, React, shadcn/ui, Tailwind CSS |
| IaC | AWS CDK (Python) |
| Data | Aurora PostgreSQL (hot cache), DynamoDB, S3 (archive) |
- AWS Account with AdministratorAccess
- Node.js 20+, Python 3.10+
- AWS CDK CLI (
npm install -g aws-cdk) - Bedrock model access enabled for BOTH of:
- a Claude text model, via the cross-region inference profile for your region
(
apac./us./eu./global.prefix). This isAGENT_MODEL_ID. - the Anthropic "use case details" form, submitted once per account. This is a
SEPARATE step from enabling model access and it is easy to miss: the agent fails
every turn with
ResourceNotFoundException: Model use case details have not been submitted for this account, which reads like a missing model rather than a missing form. Submit it in the Bedrock console and allow ~15 minutes. Measured 2026-08-12: a fresh account returned that error from bothConverseandInvokeModelonapac.anthropic.claude-sonnet-4-20250514-v1:0while an account that had submitted the form answered normally with the identical call. - Amazon Titan Text Embeddings V2 (
amazon.titan-embed-text-v2:0). Semantic incident search embeds into pgvector with it (incident_embeddingscollector and thefind_similar_incidentstool), so without access those fall back to keyword matching while everything else looks healthy.
- a Claude text model, via the cross-region inference profile for your region
(
- A region where Bedrock AgentCore (Runtime, Gateway, Memory) is available. The agent stack cannot deploy without it, and it is not in every region.
This is not free tier. Two components bill whether or not anyone logs in:
| always-on | measured |
|---|---|
| Aurora Serverless v2 cache, min 0.5 ACU | about $70/month |
| one NAT Gateway (private Lambda egress) | about $37/month (hours only) |
So roughly $110/month before any traffic, plus usage: Lambda (scales with registered-cluster count times the 5-minute ETL), Bedrock tokens, CloudFront, DynamoDB and S3.
For scale: this project's own dev account, measured over the 30 days to 2026-08-11 with
Application=DBOps cost allocation, came to $485. Do not read that as your bill: most of
it is the demo TARGET databases being monitored, and one provisioned db.r6g.large
Aurora among them is about $180/month on its own. The platform's own share was closer to
$200/month with 11 registered clusters.
# 1. Clone + configure
git clone https://github.com/JinHyun-Park/dbops.git
cd dbops
cp cdk/config/settings.example.py cdk/config/settings.py
$EDITOR cdk/config/settings.py
# REQUIRED: ACCOUNT_ID, REGION
# CHECK: AGENT_MODEL_ID must carry the inference-profile prefix for YOUR
# region (apac. / us. / eu. / global.). Claude Sonnet 4 has no bare
# on-demand model ID, so a wrong prefix fails every chat turn.
# OPTIONAL: COGNITO_DOMAIN_PREFIX. Leave it EMPTY and the stack derives a
# prefix unique to your account (the Hosted UI prefix is globally
# unique per region, so a shared literal collides).
# 2. Bootstrap CDK once per account/region, FROM OUTSIDE THE REPO.
# `cdk bootstrap` synthesizes the app whenever it is run in a directory that has a
# cdk.json, and synth needs build artifacts that step 3 is what produces. Running it
# from a directory with no CDK app skips synthesis entirely. Verified 2026-08-12 in a
# clean account: from the repo it fails on the missing artifacts, from an empty
# directory it bootstraps in about 40 seconds.
# Passing the target explicitly is NOT enough on its own: `aws://ACCOUNT/REGION` still
# synthesizes when a cdk.json is present.
(cd /tmp && cdk bootstrap aws://<ACCOUNT_ID>/<REGION>)
# 3. Run the all-in-one deployer
./deploy.sh
# Builds frontend → bundles SQL schemas → builds ARM64 agent deps →
# deploys all CDK stacks (foundation, data, agent, frontend) →
# SchemaMigrator Custom Resource creates 14+ tables idempotently →
# Cognito Callback URLs auto-registered to your CloudFront domain →
# /config.json deployed with your apiUrl, region, cognitoClientId, agentRuntimeArn.# 4. Create the first user. Self sign-up is DISABLED by design, so without this
# step the app deploys successfully and nobody can log in.
ENV=$(cd cdk && python3 -c "from config.settings import Settings; print(Settings.ENV)")
POOL=$(aws cloudformation describe-stacks --stack-name "dbops-$ENV-foundation" \
--query "Stacks[0].Outputs[?OutputKey=='UserPoolId'].OutputValue" --output text)
aws cognito-idp admin-create-user --user-pool-id "$POOL" \
--username you@example.com \
--user-attributes Name=email,Value=you@example.com Name=email_verified,Value=true \
--message-action SUPPRESS
aws cognito-idp admin-set-user-password --user-pool-id "$POOL" \
--username you@example.com --password 'ChangeMe123!' --permanentEvery user is an admin unless you add them to the dbops-viewer group, so no
extra step is needed to get full access. Omit --permanent if you would rather
be forced to set a new password at first login: the login page handles that
flow. Later users can be invited the same way, and roles are managed in the app
under Settings.
The final output of ./deploy.sh shows your Web UI URL. Open it, log in with
the user you just created (the app has its own /login page, it does not use
the Cognito Hosted UI), then register a cluster from the Clusters page
(cross-account spoke roles are validated via STS AssumeRole at registration
time). Metrics appear after the first ETL cycle, which runs every 5 minutes.
CDK creates six Application Inference Profiles tagged with Application=DBOps,
Environment={env}, CostCategory=DBOps. To populate the /cost page with
real spend you must activate the tag in your billing preferences once:
- Open the Cost allocation tags console
- Find Application under "User-defined cost allocation tags"
- Select it → click Activate
- Wait ~24h. Cost Explorer is not retroactive, but new chat invocations are immediately attributed to the profile from then on.
The /cost page's Aurora / RDS tab works out of the box (RDS total +
usage-type breakdown). Per-cluster Aurora cost is opt-in: apply a
cost-allocation tag to your Aurora clusters (e.g. dbops:cluster=<id>) and
activate that tag key in the same console. Until then the per-cluster panel
shows a "not available" notice instead of fabricated numbers.
The "✓ Ack" button on outbound Slack alert messages needs one setup pass on the Slack app side. The Alerts page has a Slack two-way Ack setup section that hands you the 4-step guide plus your endpoint URL automatically (the setup-guide button under the PageHeader).
In short:
- Create a new Slack app at api.slack.com/apps
- Copy Basic Information → Signing Secret and paste it into
SLACK_SIGNING_SECRETincdk/config/settings.py - Enable Interactivity & Shortcuts and set the Request URL to
{API_GATEWAY_URL}/api/slack/interactive - Enable Incoming Webhooks, register the channel webhook URL under Subscribers on the Alerts page with the
slack-webhookprotocol, then runcdk deploy dbops-dev-agentonce more
Outbound Slack messages (the read direction) and every other feature work normally with an empty Signing Secret. Only two-way ack is disabled.
# Tighten CORS: by default Lambdas echo request Origin (dev-safe).
# Set ALLOWED_ORIGINS on dashboard/alerts Lambdas to your CloudFront domain
# in production. (Auto-injection would create a cyclic CFN dependency.)
# Configure AgentCore (post-deploy)
# npm install -g @aws/agentcore
# agentcore create --defaults
# agentcore deploy
# 8. Register your first cluster
# Open the Web UI → Clusters → + RegisterCognito Callback URLs are auto-registered: the frontend stack uses an AWS Custom Resource to add
<DistributionUrl>/callback(and localhost) to the User Pool client at deploy time. No manual Cognito setup needed.Runtime config: the frontend fetches
/config.jsonat boot, so the built static bundle is portable. CDK writesapiUrl,frontendUrl,region,cognitoDomain,cognitoClientId, andagentRuntimeArninto the config object, resolved from the live stack outputs at deploy time.
DBOps can file a ticket when an agent task completes. It ships as an inert
integration seam: off by default, toggled per-deployment in the web UI under
Configure → Settings (TICKETING_PROVIDER). Wiring your team's provider is
a few lines: see docs/ticketing-integration.md.
DBOps reads cluster monitoring data over the RDS Data API. By default the master secret discovered alongside each Aurora cluster is used, which works out-of-the-box but grants DBOps the same blast radius as the admin user.
For production, create a dedicated dbops_readonly role on each cluster and
register its credentials in Secrets Manager using the DBOps naming
convention: bulk Discover finds and attaches it automatically, no manual
ARN entry needed:
secret name: dbops/<cluster_id>/readonly
secret JSON: {"username":"dbops_readonly","password":"..."}
The Clusters page has a 📋 Setup guide button that walks through the SQL + AWS CLI snippets for both PostgreSQL and MySQL. In short:
PostgreSQL
CREATE ROLE dbops_readonly LOGIN PASSWORD '...';
GRANT pg_monitor, pg_read_all_settings, pg_read_all_stats TO dbops_readonly;MySQL
CREATE USER 'dbops_readonly'@'%' IDENTIFIED BY '...';
GRANT PROCESS, REPLICATION CLIENT ON *.* TO 'dbops_readonly'@'%';
GRANT SELECT ON performance_schema.* TO 'dbops_readonly'@'%';
GRANT SELECT ON information_schema.* TO 'dbops_readonly'@'%';
GRANT SELECT ON mysql.* TO 'dbops_readonly'@'%';Then register the credentials:
aws secretsmanager create-secret \
--region <region> \
--name "dbops/<cluster_id>/readonly" \
--secret-string '{"username":"dbops_readonly","password":"<password>"}'After this, the Discover table shows one of three badges per cluster:
✓ convention: dedicated user found and auto-attached (recommended)⚠ master fallback: using master secret; works but should be tightened✗ missing: no usable secret; needs setup before activation
To manage Aurora clusters in other AWS accounts:
-
Deploy the spoke role in each target account:
aws cloudformation deploy \ --template-file cdk/cross-account/spoke-role-template.yaml \ --stack-name dbops-spoke-role \ --parameter-overrides HubAccountId=<HUB_ACCOUNT_ID> \ --capabilities CAPABILITY_NAMED_IAM
Add
EnableProvisioningWrites=trueto the parameter overrides only if you want DBOps to CREATE resources in that account (restore a cluster from a snapshot or to a point in time, add a reader instance, create a custom cluster endpoint). Those actions cannot be restricted by theManagedBy=dbopstag, because IAM evaluates the tag against the resource being acted on and the new resource does not exist yet, so they are opt-in. With the flag off, the restore and reader-scale-out tools return AccessDenied in that account. -
Tag clusters for write access:
ManagedBy=dbopsEvery write that acts on an existing resource is gated on this tag, across RDS/Aurora, DocumentDB (authorized under the
rds:namespace), ElastiCache and DynamoDB. An untagged cluster is readable but not writable.Tag the cluster parameter group too.
rds:ModifyDBClusterParameterGroupauthorizes against the parameter group, not the cluster, so parameter tuning is denied if only the cluster carries the tag.Snapshot creation is the one exception: it is allowed without the tag. IAM evaluates
aws:ResourceTagagainst every resource in the request, and the snapshot being created does not exist yet, so a tag condition would deny every snapshot rather than scope it. -
Register in DBOps with the spoke role ARN
See cdk/cross-account/README.md for details.
dbops/
├── cdk/ # CDK infrastructure (4 stacks)
├── agent/ # Strands Agent + Dockerfile
├── mcp-servers/ # 4 Custom MCP servers (64 tools, incl. snapshot/restore + request_approval + query_activity_audit)
├── data-pipeline/ # ETL, Event Processor, Report Generator, Monitor
├── api/ # REST API Lambdas
├── frontend/ # Next.js Web UI
├── knowledge/ # Bedrock KB source documents
├── tests/ # Unit tests
├── .kiro/ # Kiro specs and steering
└── docs/ # Design specs and plans
# Run tests
pytest tests/ -v
# Frontend dev server
cd frontend && npm run dev
# CDK synth (validate templates)
cd cdk && cdk synth --quietInstall once per clone, and every commit / push is auto-checked from then on:
# Dev deps (pytest, ruff, pre-commit, CDK synth deps)
pip install -r requirements-dev.txt
# Install pre-commit hooks (ruff + prettier + tsc --noEmit on TS changes)
pre-commit installGitHub Actions (.github/workflows/ci.yml) re-runs the same checks on
every push/PR, in three parallel jobs:
- python:
ruff check .+pytest tests/unit - cdk:
cdk synthsmoke + 4-stack snapshot test - frontend:
tsc --noEmit+next build
Claude Code hooks (.claude/hooks/) add two structural gates while
working with the assistant:
pre-commit-review.sh: blocksgit commituntil the code-reviewer subagent runs. Bypass for trivial diffs by adding[skip-review]to the commit message.stop-session-memory.sh: at session end, surfaces commits since the last checkpoint and reminds the assistant to persist a project memory note so the next session resumes cleanly.
The PG cache (cluster_meta) still holds rows left behind by an earlier ETL run while the DDB registry has none. In the current build _multi_cluster_overview cross-checks against DDB automatically, so this stops happening after a new deploy. To clean up an existing cache, DELETE that cluster_id from cluster_meta and the per-cluster tables.
The Application cost allocation tag has not been activated in the billing console. Check the "post-deploy: activate Bedrock cost-allocation tags" step in Quick Start. Even after activation, past cost is not backfilled: only invocations from that point forward are aggregated.
SLACK_SIGNING_SECRET in cdk/config/settings.py is empty. Run step 4 of the "Slack two-way Ack" guide above and redeploy the agent stack.
Both are PostgreSQL-only features. On a MySQL cluster you get a friendly notice there, and every other panel works normally.
Cold start, or region capacity. Check the AgentCore Runtime logs in CloudWatch. To compare, temporarily point AGENT_MODEL_ID in settings.py at a lighter model such as Haiku.
Recharts injects series/grid/axis/tooltip colors as inline SVG attributes, so a CSS override never reaches them. Every chart component must take amber/sky/emerald/rose plus the grid/axis/tooltipBg/tooltipBorder/tooltipText tokens from the useChartColors() hook in frontend/src/lib/use-chart-colors.ts. The target is WCAG AA (4.5:1).
Since the server-side approval guard shipped, approved=true on its own is not enough: the same tool call must also carry the approval_id (the UUID request_approval returned). The agent's system prompt knows this flow, but a model that is too old, or one running at low reasoning depth, can drop it. When that happens, strengthen the guidelines in agent/prompts/system_prompt.py and redeploy the agent stack. An approval also has to be used within 30 minutes of being issued: once that window passes, call request_approval again.
The ReportGenerator Lambda failed its Bedrock invoke and the deterministic fallback took over. Look for [report_generator] Bedrock summary failed in CloudWatch Logs. The common causes are (1) a missing bedrock:InvokeModel IAM permission, which data_stack grants automatically as of this version, and (2) the inference profile that the REPORT_SUMMARY_MODEL_ID env var points at does not exist, so check the model ID in settings.py. The data itself (the JSONB data column) is stored correctly, so the cards and slow-query panels in the UI still render.
- Design Spec:
docs/superpowers/specs/2026-05-08-dbops-design.md - Implementation Plans:
docs/superpowers/plans/ - Kiro Specs:
.kiro/specs/ - Cedar Policies:
cdk/policies/ - Cross-Account:
cdk/cross-account/README.md
Private. All rights reserved