{"_id":"@0x5278/blastradius","_rev":"2-7d4aa7024a084b2d9ffe834e321c3b17","name":"@0x5278/blastradius","dist-tags":{"latest":"0.1.1"},"versions":{"0.1.0":{"name":"@0x5278/blastradius","version":"0.1.0","keywords":["terraform","plan","risk","dependency-graph","iac"],"license":"MIT","_id":"@0x5278/blastradius@0.1.0","maintainers":[{"name":"0x5278","email":"joeykoch2010@gmail.com"}],"homepage":"https://github.com/JoeyKoch1/blastradius#readme","bugs":{"url":"https://github.com/JoeyKoch1/blastradius/issues"},"bin":{"blastradius":"bin/cli.js"},"dist":{"shasum":"1f9b52eb1984af401b353ceb0266db58d508f1f1","tarball":"https://registry.npmjs.org/@0x5278/blastradius/-/blastradius-0.1.0.tgz","fileCount":9,"integrity":"sha512-vhn30tA9gdlNOy02Rc9hpE+pL5vOrKSdLbjy7aWIHc3e2gN55lIUzCkUHo6G0nUCsl9v4oY10xdiglzpPxsvsg==","signatures":[{"sig":"MEUCIQDuZrk2I0DqwmptD/kFNViTzHObw6oZy4xCAhcydgsCuQIgSwiGSmGDn8AjTbR0yDQYjMXq31n3JtyNp+9JbcORrhg=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@0x5278%2fblastradius@0.1.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":20927},"main":"main.js","engines":{"node":">=18"},"gitHead":"d51deafb593bdd6fe1952bf90589263c73d89518","scripts":{"test":"node --test test/parser.test.js test/graph.test.js test/analyzer.test.js test/main.test.js test/cli.test.js","example":"node examples/run-example.js"},"_npmUser":{"name":"0x5278","email":"joeykoch2010@gmail.com"},"repository":{"url":"git+https://github.com/JoeyKoch1/blastradius.git","type":"git"},"_npmVersion":"10.8.2","description":"Find what a terraform plan actually breaks: BFS the plan's own dependency graph past the resources being changed.","directories":{},"_nodeVersion":"20.20.2","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/blastradius_0.1.0_1787077234302_0.6067396150109001","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@0x5278/blastradius","version":"0.1.1","description":"Find what a terraform plan actually breaks: BFS the plan's own dependency graph past the resources being changed.","main":"main.js","bin":{"blastradius":"bin/cli.js"},"repository":{"type":"git","url":"git+https://github.com/JoeyKoch1/blastradius.git"},"scripts":{"test":"node --test test/parser.test.js test/graph.test.js test/analyzer.test.js test/main.test.js test/cli.test.js","example":"node examples/run-example.js"},"engines":{"node":">=18"},"keywords":["terraform","plan","risk","dependency-graph","iac"],"license":"MIT","_id":"@0x5278/blastradius@0.1.1","gitHead":"2be18925a52a52f85668b5e3b0d6c4face3fac62","bugs":{"url":"https://github.com/JoeyKoch1/blastradius/issues"},"homepage":"https://github.com/JoeyKoch1/blastradius#readme","_nodeVersion":"20.20.2","_npmVersion":"10.8.2","dist":{"integrity":"sha512-wfM3dC3BDftguJ7lCPWG4xJfmUfhi3r75Csr9PeZ4UWL08NWIxC/8hAQo2hm3Z14yArdjL59c1hD+VBSVaRAZw==","shasum":"b7876524efafbdfb667488a2a808a864ee4de765","tarball":"https://registry.npmjs.org/@0x5278/blastradius/-/blastradius-0.1.1.tgz","fileCount":9,"unpackedSize":20907,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@0x5278%2fblastradius@0.1.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIAHvfWXh0iPWU9+JWKwCyLfbIZOS8iJ1Nerb0JpEVhpJAiB2n89qA29IGOBpn/bC/T7U9ul6tomeGpSElh1xDEt5Qw=="}]},"_npmUser":{"name":"0x5278","email":"joeykoch2010@gmail.com"},"directories":{},"maintainers":[{"name":"0x5278","email":"joeykoch2010@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/blastradius_0.1.1_1787159335371_0.8177725751583511"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-18T18:20:34.115Z","modified":"2026-08-19T17:08:55.906Z","0.1.0":"2026-08-18T18:20:34.487Z","0.1.1":"2026-08-19T17:08:55.538Z"},"bugs":{"url":"https://github.com/JoeyKoch1/blastradius/issues"},"license":"MIT","homepage":"https://github.com/JoeyKoch1/blastradius#readme","keywords":["terraform","plan","risk","dependency-graph","iac"],"repository":{"type":"git","url":"git+https://github.com/JoeyKoch1/blastradius.git"},"description":"Find what a terraform plan actually breaks: BFS the plan's own dependency graph past the resources being changed.","maintainers":[{"name":"0x5278","email":"joeykoch2010@gmail.com"}],"readme":"# blastradius\n\n[![CI](https://github.com/JoeyKoch1/blastradius/actions/workflows/ci.yml/badge.svg)](https://github.com/JoeyKoch1/blastradius/actions/workflows/ci.yml)\n[![npm](https://img.shields.io/npm/v/@0x5278/blastradius.svg)](https://www.npmjs.com/package/@0x5278/blastradius)\n[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)\n\n`terraform plan` tells you what it's changing. It doesn't tell you what\n**breaks as a result**. blastradius reads the plan's own dependency graph and\nBFS-traces every resource downstream of a delete/replace/update - including\nresources Terraform marks `no-op`, which can still fail at runtime when\nsomething they depend on vanishes.\n\n```\n-  DELETE  aws_security_group.db_sg  (blast risk: 18)\n     └─ aws_db_instance.main  [depth 1, risk 9]\n       └─ aws_db_instance.replica  [depth 2, risk 9]\n```\n\nThat depth-2 catch - the read replica nobody was thinking about - is the\nentire point. `terraform plan` is depth-0 by design; it only lists what\nchanges, not what breaks as a consequence.\n\n## Install\n\n```bash\nnpm install -g @0x5278/blastradius\n# or just: npx @0x5278/blastradius --plan plan.json\n```\n\n## Usage\n\n```bash\nterraform plan -out=tfplan\nterraform show -json tfplan > plan.json\n\nblastradius --plan plan.json                  # human-readable CLI report\nblastradius --plan plan.json --format markdown # for PR comments\nblastradius --plan plan.json --format json     # for scripting\n\n# or pipe directly, no intermediate file\nterraform show -json tfplan | blastradius\n```\n\nExit code is non-zero when any change has a `totalRisk >= 20` (default\nthreshold, override with `--threshold`), so it gates CI out of the box.\n\n## In CI\n\n```yaml\n- run: terraform plan -out=tfplan\n- run: terraform show -json tfplan > plan.json\n- run: npx @0x5278/blastradius --plan plan.json --format markdown >> $GITHUB_STEP_SUMMARY\n- run: npx @0x5278/blastradius --plan plan.json   # exits 1 if totalRisk >= 20\n```\n\nPR reviewers see the risk table before approving, not after the incident.\n\n## As a library\n\n```js\nconst { run, formatReport } = require('@0x5278/blastradius');\n\nconst results = run(planJsonString, { minSeverity: 'update' });\nconsole.log(formatReport(results, 'cli'));\n```\n\n`results` is an array, one entry per qualifying change:\n\n```js\n{\n  address: 'aws_security_group.db_sg',\n  type: 'aws_security_group',\n  severity: 'delete',\n  totalRisk: 18,\n  affected: [\n    { address: 'aws_db_instance.main', type: 'aws_db_instance', depth: 1, risk: 9 },\n    { address: 'aws_db_instance.replica', type: 'aws_db_instance', depth: 2, risk: 9 },\n  ],\n}\n```\n\nOptions:\n\n- `minSeverity`: skip changes below this severity (`create`, `update`,\n  `replace`, `delete`). Default: `create`.\n- `includeNoOps`: include changes whose only action is `no-op` (can still\n  fail at runtime when a dependency is deleted). Default: `false`.\n\n## How it works\n\n```\nlib/parser.js    validate plan JSON, extract resource_changes + configuration\nlib/graph.js     walk expressions[].references recursively -> reverse dependency graph\nlib/analyzer.js  BFS from each change over the reverse graph, score by resource type\nlib/report.js    cli / json / markdown formatters\nbin/cli.js       stdin or --plan, exits non-zero above the risk floor\nmain.js          library entry point\n```\n\nThe reference graph isn't reconstructed from HCL - Terraform's own plan JSON\nalready contains it under `configuration.root_module.resources[].expressions.*.references`.\nblastradius walks data that is already there.\n\nModule calls are recursed too: `configuration.root_module.module_calls[name].module`\nis walked with address flattening, so `module.ec2.aws_security_group.sg` and\noutputs (`module.ec2.main_arn`) resolve correctly across module boundaries.\n\nRisk scoring is a static weight table per resource type, summed over the\naffected dependents. It is deliberately a guess; the long-term plan is to\ntrain these weights off a team's own incident history.\n\n## Try it\n\n```bash\ngit clone https://github.com/JoeyKoch1/blastradius.git\ncd blastradius\nnpm install\nnpm run example\n```\n\nRuns against `examples/example-plan.json` - a security group deletion that\ncascades through an RDS instance to a read replica two hops away - and\n`examples/example-module-plan.json`, the same scenario with the replica\nreached through a module boundary.\n\n## Known limitations / roadmap\n\n- **`for_each`/`count` addresses** are normalized (index suffixes are\n  stripped when matching references), but the index itself is not derived\n  from expressions.\n- **Risk weights are hardcoded guesses**, not learned from anything. The\n  real long-term differentiator is training these off a team's own incident\n  history instead of a static table.\n- **No cross-state support.** Graphs that span `terraform_remote_state`\n  boundaries are not stitched together.\n\n## License\n\nMIT - see [LICENSE](LICENSE).","readmeFilename":"README.md"}