Skip to main content

NetQA — read-only AWS network discovery, knowledge graph, and Q&A

Project description

NetQA

Ask questions about your AWS network in plain English.

NetQA is an open-source, read-only CLI that discovers your AWS network (VPCs, subnets, route tables, security groups, NACLs, Transit Gateways), builds a local knowledge graph from it, and lets you interrogate it — with plain commands or with an LLM (Claude):

netqa ask "can the app subnet reach the database subnet?"
netqa ask "what breaks if I delete subnet-0a1b2c3d?"
netqa ask "what is attached to my transit gateway?"
netqa plan "isolate the staging VPC from the rest of the network"

Everything runs locally: your AWS credentials stay in your boto3 chain, state is a single JSON file on disk, and the LLM layer is optional — every check also works deterministically without an API key.

Why

AWS networking questions — "why can't these two subnets talk?", "who still references this security group?", "what's the blast radius of deleting this route?" — usually mean clicking through five consoles or writing one-off boto3 scripts. NetQA answers them from a typed graph of your real network, and the LLM answers are grounded in that graph: Claude can only call deterministic graph-query tools and narrate their results, so it can't hallucinate resources or paths that don't exist.

Features

  • Read-only discovery — pulls network state with ec2:Describe* only; never modifies anything in your account.
  • Knowledge graph — VPCs, subnets, route tables, SGs, NACLs, and Transit Gateways as a typed graph, persisted as one JSON file.
  • Deterministic checks — reachability (same-VPC, and cross-VPC via Transit Gateway or VPC peering), dependency lookups, delete blast-radius reports. No LLM required.
  • Natural-language Q&Aask (one-shot) and chat (multi-turn) answered by Claude calling the graph primitives as tools; --trace shows every query it ran.
  • Change-impact simulation — "what happens if I detach this TGW / remove this route / delete this subnet?" applied to an in-memory copy and diffed. The stored graph is never touched, and nothing is ever executed in AWS.
  • Change planning — turn an intent into an ordered, approval-ready plan where every step's impact report comes from the simulator, not the model.

Install

Requires Python 3.11+.

uv tool install netqa      # or: pipx install netqa
# from a source checkout:
uv tool install /path/to/netqa

Quick start

NetQA runs against your AWS account, read-only: discovery needs nothing beyond ec2:Describe* and uses your standard boto3 credential chain — env vars, ~/.aws/credentials, SSO, assumed roles. It never creates, modifies, or deletes anything.

# 1. Discover your network and build the graph (read-only)
netqa discover --region us-east-1 --region us-west-2 \
    --profile my-readonly-profile

# 2. Explore deterministically
netqa summary                             # what was discovered
netqa show vpc-0abc...                    # a resource and its relationships
netqa tgw tgw-0abc...                     # what's attached to this TGW?
netqa reach subnet-0123... subnet-0456... # cross-VPC reachability
netqa impact subnet-0123...               # delete blast-radius
netqa consumers sg-0123...                # who references this SG?

# 3. Ask in plain English (works offline via a keyword router; see below for LLM mode)
netqa ask "what is attached to my transit gateway?"
netqa ask "can the app subnet reach the db subnet?"

A minimal IAM policy for a dedicated read-only role:

{
  "Version": "2012-10-17",
  "Statement": [{ "Effect": "Allow", "Action": "ec2:Describe*", "Resource": "*" }]
}

LLM-powered Q&A with Claude

Set an Anthropic API key — in the environment or in a .env file (see Configuration) — and ask / chat are answered by Claude using the graph-query primitives as tools:

export ANTHROPIC_API_KEY=sk-ant-...   # or put it in ~/.netqa/.env
netqa ask "why might the app tier not reach the data tier?"
netqa ask --trace "can the app subnet reach the data subnet?"
netqa chat            # interactive: context carries across turns
  • ask is one-shot; chat is a conversation — when the model needs to disambiguate ("there are four app subnets — which environment?"), just answer and the follow-up resolves against the full dialogue.
  • --trace prints the exact graph queries the model ran to reach its answer, so every response is auditable.
  • Human names like "the app subnet" are resolved to resource IDs via a search_resources tool.
  • Without a key, ask falls back to a deterministic keyword router; force a mode with --llm / --no-llm.
  • Default model is claude-haiku-4-5 (cheap and fast). Use --model claude-opus-4-8 for the highest-quality answers and change plans, or set model in the config file.

Reasoning lives in code (qa/engine.py), not in a prompt. Claude only chooses which primitive to call and narrates the structured result — keeping answers reproducible, auditable, and grounded in your actual network.

Change-impact analysis ("what happens if I…")

Predict the consequences of a change before making it. The engine applies the proposed change to an in-memory copy of the graph, re-runs reachability and dependency analysis, and diffs — reporting broken/new paths and dangling dependencies:

netqa simulate tgw-1   --op detach_tgw --vpc vpc-bbb
netqa simulate rtb-aaa --op remove_route --destination 10.1.0.0/16
netqa simulate subnet-a1 --op delete_resource
Change Impact: detach tgw-1 from vpc-bbb
Not recommended   risk=high
Not recommended — 4 reachability path(s) would break.
  ✗ subnet-a1 → subnet-b1
  ✗ subnet-b1 → subnet-a1
  ...

Supported ops: delete_resource, remove_route, add_route, detach_tgw. The LLM agent exposes this as a tool too, so ask "what happens if I detach tgw-1 from vpc-bbb?" answers in natural language. (Reachability is route-level; SG/NACL enforcement is on the roadmap.)

Change planning from natural language

Give NetQA an intent and it drafts an approval-ready plan: an ordered list of structured change ops, each with a deterministic impact report. Claude must run the simulator on every candidate step — submissions with unsimulated steps are rejected in code, and accepted plans are re-simulated cumulatively (step N on top of steps 1..N-1). Nothing is executed; the plan is a draft for human review.

export ANTHROPIC_API_KEY=sk-ant-...
netqa plan "isolate vpc-b from the rest of the network"
netqa plan --trace "stop the app subnets reaching the data VPC"
Change Plan: isolate vpc-b from the rest of the network
Needs review   risk=high   steps=2   status=draft

1. remove route 10.1.0.0/16 from rtb-a  medium
   Drop vpc-a's route toward vpc-b before detaching.
   ...
2. detach tgw-core from vpc-b  high
   ✗ subnet-b1 → subnet-a1
   ...

Use as a Python library

Everything the CLI does is available programmatically — pip install netqa and import it. The same pipeline: discover → build graph → query.

from netqa import AwsDiscoverer, MockDiscoverer, build_graph, QueryEngine

# Discover from AWS (read-only) — or MockDiscoverer("fixture.json") for tests
inventories = AwsDiscoverer(regions=["us-east-1"]).discover()
graph = build_graph(inventories)
graph.save("graph.json")            # reload later with NetworkGraph.load()

# Deterministic queries — no LLM involved
engine = QueryEngine(graph)
r = engine.can_subnet_reach("subnet-0123", "subnet-0456")
print(r.reachable, r.path, r.reasons)

rep = engine.delete_impact("subnet-0123")   # blast radius
print(rep.recommended, rep.findings)

Simulate a change before making it:

from netqa import ProposedChange, analyze_change

impact = analyze_change(graph, ProposedChange(
    op="detach_tgw", target="tgw-1", vpc_id="vpc-bbb"))
print(impact.risk, impact.summary, impact.broken_reachability)

And the LLM layer (needs ANTHROPIC_API_KEY in the environment):

from netqa import NetworkAgent, ChangePlanner

agent = NetworkAgent(engine)                 # optional: model="claude-opus-4-8"
result = agent.ask("what breaks if I delete subnet-0123?")
print(result.text)      # grounded answer
print(result.trace)     # every graph query the model ran

plan = ChangePlanner(engine).plan("isolate vpc-b from the network")

The supported surface is exactly what netqa.__all__ exports (discoverers, graph, QueryEngine, impact analysis, agent, planner) — anything deeper is internal and may change without notice. Pre-1.0, breaking changes to these names bump the minor version.

Configuration

Everything is configurable via files, so day-to-day use needs zero flags. Settings live in a TOML config; secrets live in a .env file.

Settings~/.netqa/config.toml (CLI flags always win; override the location with NETQA_CONFIG):

model   = "claude-haiku-4-5"          # Claude model for ask/chat/plan
graph   = "~/.netqa/graph.json"
regions = ["us-east-1", "us-west-2"]  # AWS regions to discover
profile = "my-readonly-profile"       # AWS profile (else boto3 default chain)

Every key is optional — anything missing falls back to the built-in default.

Secrets — a .env file, deliberately separate from config.toml so your settings file is safe to share or commit. Loaded from ./.env or ~/.netqa/.env (override the location with NETQA_ENV):

# ~/.netqa/.env  — then: chmod 600 ~/.netqa/.env
ANTHROPIC_API_KEY=sk-ant-...

Standard dotenv format: one KEY=VALUE per line; blank lines and # comments are ignored; values may be quoted; an export prefix is allowed (so the same file also works with source .env). Any variable can go here — e.g. AWS_PROFILE or AWS_DEFAULT_REGION — but a variable already set in your real environment is never overridden.

Precedence (highest first):

CLI flag  >  environment variable  >  ./.env  >  ~/.netqa/.env  >  config.toml  >  built-in default

The tool warns if the .env file is readable by other users. Check your effective setup any time with netqa config — it shows every value and where it came from (it never prints the key itself).

Privacy & security

  • Read-only by design. The tool only calls ec2:Describe*. It never creates, modifies, or deletes anything in your account — including in simulate and plan, which operate on an in-memory copy of the local graph.
  • Your credentials never leave your machine. AWS access goes through your local boto3 chain; nothing is proxied through any service.
  • What the LLM sees. Only when you use ask/chat/plan with an API key, your question and the results of the graph queries Claude requests (resource IDs, CIDRs, route/SG relationships) are sent to the Anthropic API. No AWS credentials are ever sent. Skip the API key and everything deterministic still works entirely offline.
  • Local state. The graph is a single JSON file (default ~/.netqa/graph.json). Delete it and the tool forgets everything.

How it works

 Your AWS account (ec2:Describe* only)                       ┌─►  QueryEngine (deterministic)
         │                                                   │    reach · deps · impact · simulate
         └──►  RawInventory ─► NetworkGraph ─────────────────┤
               (boto3 describe_*)   (NetworkX)               └─►  NetworkAgent (Claude tool-use)
                                                                  ask · chat · plan
Layer Module Responsibility
Discovery discovery/ Pull raw network state from AWS via read-only describe_* calls. (A JSON-fixture discoverer with the identical RawInventory shape exists for tests.)
Graph graph/ Turn raw describe_* responses into a typed NetworkX graph, serialized to JSON.
Q&A engine qa/engine.py Deterministic graph-traversal primitives — reachability, dependencies, impact. The auditable source of truth.
LLM layer qa/agent.py, qa/planner.py Claude calls the engine primitives as tools and narrates the results; the planner drafts simulated change plans. Optional — offline keyword fallback without a key.

Nodes are AWS resources (keyed by resource ID); edges are typed relationships:

VPC        --contains-->        Subnet / RouteTable / SecurityGroup / NACL
RouteTable --associated_with--> Subnet
RouteTable --routes_to-->       igw / nat / tgw / pcx / local-vpc / ...
SecurityGroup --allows-->       SecurityGroup
NACL       --protects-->        Subnet
TransitGateway --attached_to--> VPC
VpcPeering     --attached_to--> VPC (requester and accepter)

See docs/ARCHITECTURE.md for the full design.

Supported resources & roadmap

Today: VPC · Subnet · Route Table · Security Group · NACL · Transit Gateway (+ VPC attachments) · VPC Peering Connection. Other route targets (IGW/NAT/…) appear as lightweight nodes.

Capability Status
Read-only discovery (7 core resource types)
Typed knowledge graph + JSON persistence
Dependency / "who uses this" queries
Route-level reachability (same-VPC & cross-VPC via TGW or VPC peering)
Delete-impact (blast radius) report
LLM-backed natural-language Q&A + multi-turn chat
Change-impact simulation ("what happens if I change X")
NL change planning — intent → simulated, approval-ready plan
SG + NACL enforcement layered onto reachability 🔜
ENI / Load Balancer / VPN / multi-account (Organizations) discovery 🔜
Graph diffing between discoveries ("what changed") 🔜

Development

This project uses uv. One command creates the virtualenv and installs everything from uv.lock:

uv sync
uv run pytest -q

Tests run the real builder and Q&A engine against examples/sample_network.json, which is shaped exactly like live boto3 describe_* output — the offline path exercises the same code that runs against AWS. The same fixture powers a no-AWS demo of the whole CLI (testing/development only):

uv run netqa discover --fixture examples/sample_network.json
uv run netqa reach subnet-a1 subnet-b1     # cross-VPC via the sample TGW
uv run netqa ask "what is attached to tgw-1?"

To test at scale, scripts/generate_network.py emits a parameterized fixture in the same boto3 shape — N VPCs on a central TGW, with the last --isolated VPCs left unattached for guaranteed unreachable cases:

uv run python scripts/generate_network.py --vpcs 12 --subnets-per-vpc 4 \
    --isolated 2 --out /tmp/network.json
uv run netqa discover --fixture /tmp/network.json

scripts/test_workflow.sh runs the whole tool end-to-end — generate → discover → every CLI query → offline Q&A → change-impact simulation — asserting on each step's output. It needs no AWS account or Anthropic key.

Contributing

Issues and pull requests are welcome. Good first areas: new resource types in discovery, SG/NACL-aware reachability, and graph diffing between discoveries (see the roadmap above). Please make sure uv run pytest -q and scripts/test_workflow.sh pass before submitting.

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

netqa-0.1.0.tar.gz (50.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

netqa-0.1.0-py3-none-any.whl (43.6 kB view details)

Uploaded Python 3

File details

Details for the file netqa-0.1.0.tar.gz.

File metadata

  • Download URL: netqa-0.1.0.tar.gz
  • Upload date:
  • Size: 50.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for netqa-0.1.0.tar.gz
Algorithm Hash digest
SHA256 f4ec91412ea708efd0e265201963bfdc3901263e557eee3c9b9c6c0f6b0fd8e6
MD5 0c35cde6e148f16e7db8ac2b0b7c69c2
BLAKE2b-256 d5f761cdb141c97c9aa0e5be1874b740c44274c29db24dc9e22ed72d2a9ed756

See more details on using hashes here.

Provenance

The following attestation bundles were made for netqa-0.1.0.tar.gz:

Publisher: release.yml on RayVenn/network-steward-oss

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file netqa-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: netqa-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 43.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for netqa-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 8e56fc7df25b5aaf4bccd6579589a8a191dcb22a32c6fb2abf1ae93c1f292eb3
MD5 dc09993b00c9db16577746cbdc0fdffe
BLAKE2b-256 e8377e21f17bf2fea7c522a6e9d5cb0c09c6c294d417e17ba820c8433d0eb659

See more details on using hashes here.

Provenance

The following attestation bundles were made for netqa-0.1.0-py3-none-any.whl:

Publisher: release.yml on RayVenn/network-steward-oss

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page