Series: Introducing Blast Radius — See What Breaks Before You Deploy
What We Are Building
This article shows you how to use Blast Radius in your own CI/CD pipeline. By the end, you'll have a deployment gate that automatically analyzes infrastructure changes, scores downstream risk, and blocks merges that exceed your threshold.
Blast Radius comments on your PR with an analysis table (Highest Score, Affected Resources, AI Recommendation, Confidence) plus an AI-generated summary.
There are currently two paths to use Blast Radius:
- GitHub Action: fastest and auto-comments on PRs
- Raw CLI: works in any CI system such as GitLab, Jenkins, CircleCI, or whatever you're running.
Blast Radius is meant for your infrastructure. It works on any stage: dev, testing, production, or any stages you may use. It calls live AWS APIs (Config and Resource Explorer) against your actual account. The results reflect real dependency relationships, not static analysis.
Prerequisites — Deploy the Backend
The following must be enabled in your AWS Account before deploying the backend:
- AWS Config must be enabled and recording in the region you deploy to. This is where Blast Radius reads dependency relationships. Here's how to enable AWS Config.
- Resource Explorer must be enabled. It's the fallback for resources Config doesn't have relationships for. Here's how to enable Resource Explorer.
-
CDK Bootstrap must be run via
cdk bootstrapin your account and region you are deploying to. - Optional: Bedrock model access if you want AI summaries. You may choose any model via cross-region inference profile. Without enabling Bedrock model access, threshold gates will still work but the AI gate will not.
Deploy Steps:
git clone https://github.com/sburgholzer/BlastRadius.git
cd BlastRadius
npm install
npm run build
cd packages/infra
cdk deploy
This creates an API Gateway, Step Functions state machine, Lambda functions, DynamoDB tables, S3 bucket, and a CloudFront distribution for the frontend.
When the deployment finishes, grab the API URL from the CDK output. That's the only value you need for the rest of this article.
To enable AI summaries, set enableBedrockSummary in the CDK props. You can also configure resultsRetentionDays for S3 lifecycle management. More detail on these options is in the README.
GitHub Action — The Five-Minute Path
Terraform Workflow
Terraform is the simplest path. Generate the plan, convert it to JSON, and pass it to the action:
- run: terraform plan -out=plan.out
- run: terraform show -json plan.out > plan.json
- uses: sburgholzer/BlastRadius@v0.1.0
with:
format: terraform-plan
input: plan.json
threshold: 75
ai-gate: true
api-url: ${{ secrets.BLAST_RADIUS_URL }}
The action reads the JSON plan, runs the analysis, and comments the results on your PR.
CDK Workflow
CDK requires a few more steps. Blast Radius needs to know what CloudFormation will actually do — the changeset actions, not just what the template looks like. A cdk synth alone doesn't give you that; you have to ask CloudFormation directly.
- name: Generate CloudFormation changeset
run: |
npx cdk synth 2>/dev/null
cp cdk.out/MyStack.template.json template.json
CHANGESET_NAME="blast-radius-$(date +%s)"
aws cloudformation create-change-set \
--stack-name MyStack \
--change-set-name $CHANGESET_NAME \
--template-body file://template.json \
--capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAM CAPABILITY_AUTO_EXPAND \
--change-set-type UPDATE \
--output json
aws cloudformation wait change-set-create-complete \
--stack-name MyStack \
--change-set-name $CHANGESET_NAME
aws cloudformation describe-change-set \
--stack-name MyStack \
--change-set-name $CHANGESET_NAME \
--output json > changeset.json
aws cloudformation delete-change-set \
--stack-name MyStack \
--change-set-name $CHANGESET_NAME
- name: Blast Radius Analysis
uses: sburgholzer/BlastRadius@v0.1.0
id: blast-radius
with:
format: cloudformation
input: changeset.json
threshold: 75
ai-gate: true
api-url: ${{ secrets.BLAST_RADIUS_API_URL }}
Each step has a specific job: synth produces the template, create-change-set asks CloudFormation what it would change, wait holds until it's ready, describe-change-set captures the answer as JSON, and delete-change-set cleans up so it doesn't linger between runs.
CloudFormation Workflow
Same as CDK but without the synth step — you already have a template file:
- name: Generate CloudFormation changeset
run: |
CHANGESET_NAME="blast-radius-$(date +%s)"
aws cloudformation create-change-set \
--stack-name MyStack \
--change-set-name $CHANGESET_NAME \
--template-body file://template.json \
--capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAM \
--change-set-type UPDATE \
--output json
aws cloudformation wait change-set-create-complete \
--stack-name MyStack \
--change-set-name $CHANGESET_NAME
aws cloudformation describe-change-set \
--stack-name MyStack \
--change-set-name $CHANGESET_NAME \
--output json > changeset.json
aws cloudformation delete-change-set \
--stack-name MyStack \
--change-set-name $CHANGESET_NAME
- name: Blast Radius Analysis
uses: sburgholzer/BlastRadius@v0.1.0
with:
format: cloudformation
input: changeset.json
threshold: 75
ai-gate: true
api-url: ${{ secrets.BLAST_RADIUS_API_URL }}
This is essentially the same as the CDK Workflow, except we don't need to run a synth.
What the GitHub Action Does Under the Hood
- Downloads the bundled CLI from the GitHub release
- Runs
analyzewith--ciflag for JSON output - Parses the JSON into GitHub Action outputs
- Comments on the PR with a formatted table + AI summary (disable with
comment-pr: false)
Action inputs:
| Input | Required | Default | Description |
|---|---|---|---|
| format | ✅ | Input format: cloudformation / terraform-plan / canonical | |
| input | ✅ | Path to the input file (changeset JSON / terraform plan JSON / canonical manifest) | |
| api-url | ✅ | Blast Radius API URL | |
| threshold | Risk score threshold (0-100). Fails if any resource exceeds this. | ||
| ai-gate | false | Fail if AI recommends against deployment | |
| no-summary | false | Skip AI summary generation | |
| comment-pr | true | Automatically comment results on the PR | |
| version | latest | CLI version to use |
Action outputs:
| Output | Description |
|---|---|
| analysis-id | The analysis ID |
| verdict | pass or fail |
| highest-score | Highest impact score (0-100) |
| total-affected | Total affected resources |
| recommend-deploy | AI deployment recommendation (true/false) |
| confidence | AI recommendation confidence (high/medium/low) |
| summary | AI-generated risk summary (markdown) |
| result-json | Full JSON result (for custom downstream steps) |
CLI in Any CI System
The GitHub Action is a convenience wrapper. Underneath, it's the CLI. If you're on GitLab, Jenkins, CircleCI, Bitbucket Pipelines, any other tool, or want greater control than the GitHub Action, you can use the CLI directly.
It's a single bundled JavaScript file. No npm install, no dependencies beyond a Node.js runtime. Download it, run it, read the exit code.
How to get the CLI:
curl -sL https://github.com/sburgholzer/BlastRadius/releases/latest/download/blast-radius.js -o blast-radius.js
sed -i '1{/^#!/d}' blast-radius.js
The sed command strips the shebang line. Node.js can't parse #!/usr/bin/env node when you invoke it with node blast-radius.js directly (needed in CI environments that don't support executing .js files as scripts).
Environment setup:
Set BLAST_RADIUS_API_URL as an environment variable pointing to your deployed API Gateway URL. That's the only required config. Everything else goes in command flags.
GitLab CI example
blast-radius:
image: node:20
stage: validate
script:
- curl -sL https://github.com/sburgholzer/BlastRadius/releases/latest/download/blast-radius.js -o blast-radius.js
- sed -i '1{/^#!/d}' blast-radius.js
- node blast-radius.js analyze --format terraform-plan --input plan.json --threshold 75 --ai-gate --ci
variables:
BLAST_RADIUS_API_URL: $BLAST_RADIUS_URL
artifacts:
paths:
- blast-radius-result.json
when: always
Note: --ci outputs JSON to stdout. Capture it with > blast-radius-result.json and save as an artifact for downstream jobs. Exit code 1 naturally fails the GitLab job, no extra configuration needed.
Jenkins example
stage('Blast Radius') {
environment {
BLAST_RADIUS_API_URL = credentials('blast-radius-url')
}
steps {
sh '''
curl -sL https://github.com/sburgholzer/BlastRadius/releases/latest/download/blast-radius.js -o blast-radius.js
sed -i '1{/^#!/d}' blast-radius.js
node blast-radius.js analyze --format cloudformation --input changeset.json --threshold 75 --ai-gate --ci
'''
}
}
Store your API URL in Jenkins credentials and reference it as an environment variable. Non-zero exit fails the stage, standard Jenkins behavior.
CircleCI example
- run:
name: Blast Radius Analysis
command: |
curl -sL https://github.com/sburgholzer/BlastRadius/releases/latest/download/blast-radius.js -o blast-radius.js
sed -i '1{/^#!/d}' blast-radius.js
node blast-radius.js analyze --format terraform-plan --input plan.json --ai-gate --ci
environment:
BLAST_RADIUS_API_URL: ${BLAST_RADIUS_URL}
Parsing the JSON output
With --ci, the CLI outputs a JSON object to stdout:
{
"analysisId": "abc-123",
"verdict": "fail",
"exitCode": 1,
"reason": "ai-gate",
"recommendDeploy": false,
"confidence": "high",
"riskSummary": {
"highestScore": 82,
"totalAffected": 14,
"critical": 2,
"high": 5,
"medium": 4,
"low": 3
},
"naturalLanguageSummary": "## Executive Overview\n\nThis deployment presents..."
}
What you can do with this:
- Pipe to jq for specific fields:
node blast-radius.js analyze ... --ci | jq '.riskSummary.highestScore' - Send to Slack: extract
verdict,highestScore, andnaturalLanguageSummaryinto a webhook payload. - Build a custom PR comment in GitLab/Bitbucket using the JSON fields.
- Feed into a dashboard or metrics system; track risk scores over time per team or per service.
Exit Codes
Any CI system's native "fail on non-zero" behavior works out of the box. No special error handling needed.
| Code | Meaning |
|---|---|
| 0 | Pass - threshold OK and AI approves (or gates not enabled) |
| 1 | Fail - threshold exceeded OR AI recommends against deployment |
| 2 | Error - invalid input / pipeline failure / timeout / misconfiguration |
Choosing Your Gate Strategy
Threshold only (--threshold N)
This is deterministic. Same input, same result, every time. It's based on the scoring formula from the previous article: depth (how far the resource is from the changed resource), criticality (how critical the resource is to your infrastructure), and change severity (how much the change affects the resource). Each factor is weighted to produce a score between 0 and 100.
Because it's a formula, there's no AI dependency. If you run Blast Radius without Bedrock, you still get a usable result for your CI/CD pipeline.
This gate is good for compliance-driven teams, environments where you need reproducible gates, or if you want to understand how the scoring system works before enabling AI.
N is your cutoff: if the highest score of any resource exceeds that value, Blast Radius fails your pipeline. Here's a general guideline:
- 90: very permissive. Only blocks Critical-category changes. Good for dev/staging environments.
- 75: moderate. Blocks Critical and most High-category hits. Good default for production.
- 50: conservative. Blocks anything above Medium. Good for sensitive workloads.
Start at 75 to see how it works in your environment, then fine-tune from there.
AI gate only (--ai-gate)
This is where judgment-based thinking comes into play. The AI gate looks at the full graph, not just the individual scores. It catches patterns like shared dependencies, fan-out risk, and cascading failure paths. It will return recommendDeploy: true/false with confidence: high/medium/low.
This is good for teams that trust AI tooling or want to experiment with it to catch systemic risks. If you use this gate, I recommend reading through the summary and doing manual investigation to verify the reasoning, and to understand what to fix when it blocks.
One caveat: --ai-gate requires Bedrock enabled on the backend. If Bedrock is disabled, the CLI exits with error code 2.
Using Both Gates Together
This is my recommended configuration. When you use both gates, Blast Radius fails your pipeline if EITHER triggers. The threshold catches obvious high-scoring resources. The AI catches non-obvious patterns below your threshold value.
Running Without AI Summary
If you run Blast Radius with --no-summary, it skips the AI-generated natural language summary but will still run the analysis. This saves a few seconds if you only care about the score.
Note: --ai-gate overrides --no-summary because the gate needs the AI to run.
Reading the Output
You've set up the gate. It ran. Now you're looking at a PR comment (or JSON output) with scores, dependency chains, and an AI recommendation. Here's how to read it all.
The PR Comment Anatomy
The GitHub Action comments on your PR with two parts:
-
Summary table: four key metrics at a glance:
- Highest Score: the single scariest number in the analysis (0-100)
- Affected Resources: total number of downstream resources discovered
- AI Recommendation: Deploy ✅ or Do Not Deploy ❌
- Confidence: how sure the AI is (high/medium/low)
-
AI-generated summary (if Bedrock is enabled): structured markdown with:
- Executive overview: one-paragraph assessment
- Key findings: the specific resources and patterns that drove the recommendation
- Cascading risks: what could go wrong if you deploy anyway
- Recommendation: the final call with reasoning
If you're using the CLI with --ci, you get the same information as JSON fields: riskSummary, recommendDeploy, confidence, and naturalLanguageSummary.
Risk Scores — What the Numbers Mean
| Score | Category | Meaning |
|---|---|---|
| 75-100 | Critical (red) | Direct or near-direct impact on high-criticality resources (databases, auth systems, core networking). Stop and think. |
| 50-74 | High (orange) | Significant downstream impact. Review the dependency chain before deploying. |
| 25-49 | Medium (yellow) | Worth noting. Probably fine, but look at what's at the end of the chain. |
| 0-24 | Low (green) | Far away, low criticality, or a non-destructive change type. Safe to proceed. |
How the Score is Calculated (review)
impactScore = round((depthScore × 0.30) + (criticalityScore × 0.40) + (changeTypeSeverity × 0.30))
- Depth (30%) — how many hops away. Depth 1 = 100, Depth 5 = 60, Depth 10 = 10.
- Criticality (40%) — how important the resource type is. Database = 100, Lambda = 75, S3 = 50, Log group = 25.
- Change severity (30%) — how dangerous the action is. Remove = 100, Replace = 80, Modify = 50, Add = 30.
Criticality is weighted highest because what's affected matters more than how far away it is. Deleting a test log group one hop away is less dangerous than modifying something three hops from a production database.
Dependency Chains — the Breadcrumb Trail
Every scored resource includes a dependencyChain: the ordered path showing exactly how risk flows from your change to that resource.
Example: sg-abc123 → ec2-instance-1 → rds-prod
Read left to right: your changed resource → intermediate resources → the thing at risk.
- Longer chains mean lower depth scores (the resource is further away).
- But a long chain ending at a Critical-class resource (like a database) can still score high because criticality carries 40% of the weight.
- Short chains ending at low-criticality resources score low even though they're close.
In the frontend graph view, the dependency chain is the highlighted path when you click a node. In the PR comment, it appears in the detail expansion for each resource.
AI Confidence Levels
When using the AI gate, the model returns a confidence alongside its recommendation:
- High: clear signal. The graph strongly supports the recommendation. Trust it.
- Medium: mixed signals. Some risk factors present, some mitigating. Worth human review before overriding.
- Low: AI isn't sure. Limited data, ambiguous patterns, or a very small graph. Don't gate on this alone — treat it as advisory.
When the AI says "don't deploy" but scores are below threshold:
This is the AI gate earning its keep. It's seeing a pattern that individual scores don't capture:
- Fan-out risk: many resources all depending on one thing you're changing.
- Single points of failure: a resource that's the only path between your change and critical infrastructure.
- Cascading depth: the graph is deep and converges on something important at the far end.
Check the AI summary for the specific reasoning. It will explain what pattern it detected and why it recommends against deployment.
When the AI says "deploy" but you're nervous:
The AI can be wrong. If your gut says something's off:
- Look at the dependency chain for the highest-scored resource. Does the path make sense?
- Check whether the graph is missing resources (AWS Config relationships aren't always complete).
- Use the interactive frontend to explore the full graph visually.
- When in doubt, deploy to a non-production stage first.
Local Development and Testing
You don't have to go straight to CI/CD pipeline. The CLI has commands for generating, inspecting, and exploring results locally. The interactive frontend also gives you a visual way to understand your infrastructure's dependency landscape.
blast-radius generate — Create Input Files without Submitting
The generate command produces the same input file that analyze would create and submit, but stops there. No analysis is run, no API is called.
# CDK: synthesizes and creates a changeset, saves to disk
blast-radius generate --format cdk --stack MyStack --output changeset.json
# Terraform: runs terraform plan + show, saves the JSON
blast-radius generate --format terraform-plan --output plan.json
# CloudFormation: creates changeset from template, saves it
blast-radius generate --format cloudformation --stack MyStack --template cfn.json --output changeset.json
Why this is useful:
- Verify your CI workflow produces valid input before enabling the gate
- Inspect exactly what Blast Radius will see — review the changeset JSON to understand the resources and change types
- Build a library of test inputs for different scenarios
- Share with teammates: "here's what the analysis would run against"
--save flag — save AND submit
If you want to both inspect the input and run the analysis in one shot:
blast-radius analyze --format cdk --stack MyStack --save changeset.json --ai-gate
This saves the generated input to disk, then submits it. When a result comes back surprising ("why did my security group change score 82?"), you can open changeset.json and see exactly what was sent: which resources, which change types, which properties.
The Interactive Frontend
After deploying the backend, the CloudFront URL serves a React SPA where you can explore results visually.
-
Analysis list (
/analyses): shows all past analyses sorted by date. Click any row to drill in. - Dependency graph: Cytoscape.js interactive graph. Your changed resources appear as solid blue nodes. Downstream dependencies fan out, colored by risk category (red/orange/yellow/green). Click any node for a detail panel: resource ID, type, score, risk category, and full dependency chain.
- Filters: filter by risk category, resource type, or toggle direct changes on/off. Filters apply as intersection (resource must match all active filters).
- Table view: sortable alternative to the graph. Sorted by impact score (highest first) by default. Paginated at 50 rows.
- Export: download the full analysis as JSON for archiving or feeding into other tools.
- AI Summary: rendered as formatted markdown below the graph (if Bedrock was enabled for the analysis).
The frontend is useful for exploring results visually after CI runs, understanding dependency patterns in your account before they cause problems, demoing Blast Radius to stakeholders, and investigating why the AI gate blocked a deployment.
Running against the demo examples
The repo includes examples/cdk-demo/ with a two-step demo that doesn't require your real production infrastructure:
-
01-baseline/: deploys a realistic infrastructure: VPC, ECS Fargate service, Aurora PostgreSQL cluster, ALB, Lambda function, S3 bucket. -
02-risky-change/: modifies the security group (restricts from public to internal-only) and resizes the database instance.
# Deploy baseline
cd examples/cdk-demo/01-baseline
npm install && npx cdk deploy
# Run Blast Radius against the risky change
cd ../02-risky-change
npm install
BLAST_RADIUS_API_URL=<your-url> blast-radius analyze --format cdk --stack BlastRadiusDemoBaseline --ai-gate
This produces a real analysis showing 7 affected resources cascading from the security group and database changes, ECS service, Lambda, ingress/egress rules, scored and visualized.
Cost note: The demo creates real AWS resources (~$0.45/hr). Run cdk destroy when you're done. Don't do what I did and forget to run the destroy!
Other useful commands
# Check status of a running analysis
blast-radius status --analysis-id abc-123
# Fetch completed results
blast-radius export --analysis-id abc-123 --format json
What's Next?
You now have a working deployment gate. Every infrastructure PR gets analyzed for downstream risk before it can merge. Whether you're using the GitHub Action, the CLI in GitLab, or exploring results in the frontend, the workflow is the same: generate, analyze, gate, decide.
Next in this series, we go under the hood:
- Article 3: Serverless IaC Risk Analysis — The Architecture Behind Blast Radius: How the pipeline is designed. Why Step Functions. How the canonical format abstraction makes multi-IaC support possible without touching the analysis engine.
- Article 4: 349 Tests, Zero Mocks — Building Blast Radius in TypeScript: Engineering decisions, property-based testing with fast-check, dependency injection over module mocking, and the Lambda runtime gotchas that wasted a day.
These articles are coming in the next few weeks. I'll update this post with links as each one publishes.

Top comments (0)