cloudwire 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,186 @@
1
+ Metadata-Version: 2.4
2
+ Name: cloudwire
3
+ Version: 0.1.0
4
+ Summary: Scan and visualize your AWS infrastructure as an interactive graph
5
+ License-Expression: MIT
6
+ Project-URL: Homepage, https://github.com/hisingh_gwre/cloudwire
7
+ Project-URL: Issues, https://github.com/hisingh_gwre/cloudwire/issues
8
+ Keywords: aws,cloud,visualization,graph,infrastructure
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Environment :: Console
11
+ Classifier: Environment :: Web Environment
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Intended Audience :: System Administrators
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.9
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: Internet :: WWW/HTTP
20
+ Classifier: Topic :: System :: Systems Administration
21
+ Requires-Python: >=3.9
22
+ Description-Content-Type: text/markdown
23
+ Requires-Dist: fastapi>=0.115
24
+ Requires-Dist: uvicorn[standard]>=0.34
25
+ Requires-Dist: boto3>=1.37
26
+ Requires-Dist: botocore>=1.37
27
+ Requires-Dist: networkx>=3.4
28
+ Requires-Dist: pydantic>=2.11
29
+ Requires-Dist: click>=8.1
30
+
31
+ # cloudwire
32
+
33
+ Scan your AWS account and visualize resource dependencies as an interactive graph — directly in your browser, running entirely on your local machine.
34
+
35
+ No data leaves your system. AWS credentials never leave your terminal. The graph is built locally using your existing credential chain (`~/.aws/credentials`, `aws sso login`, `saml2aws`, `aws-vault` — all work out of the box).
36
+
37
+ ---
38
+
39
+ ## Install
40
+
41
+ ```bash
42
+ pip install cloudwire
43
+ cloudwire
44
+ ```
45
+
46
+ That's it. The browser opens automatically at `http://localhost:8080`.
47
+
48
+ > **Requirements:** Python 3.9+ and valid AWS credentials configured locally.
49
+
50
+ ---
51
+
52
+ ## What it looks like
53
+
54
+ - Dark hacker-aesthetic graph canvas
55
+ - Nodes represent AWS resources — Lambda functions, SQS queues, API Gateways, RDS instances, S3 buckets, and more
56
+ - Edges represent relationships and data flow between resources
57
+ - Click any node to inspect its attributes and connected resources
58
+ - Search, filter by service, highlight upstream/downstream blast radius
59
+
60
+ ---
61
+
62
+ ## Supported services
63
+
64
+ | Service | Scanner |
65
+ |---------|---------|
66
+ | API Gateway | Dedicated |
67
+ | Lambda | Dedicated (with state) |
68
+ | SQS | Dedicated |
69
+ | SNS | Dedicated |
70
+ | EventBridge | Dedicated |
71
+ | DynamoDB | Dedicated (with state) |
72
+ | EC2 | Dedicated (with state) |
73
+ | ECS | Dedicated |
74
+ | S3 | Dedicated |
75
+ | RDS | Dedicated (with state) |
76
+ | Step Functions | Dedicated |
77
+ | Kinesis | Dedicated |
78
+ | IAM | Dedicated |
79
+ | Cognito | Dedicated |
80
+ | CloudFront | Dedicated (with state) |
81
+ | ElastiCache | Dedicated (with state) |
82
+ | Glue | Dedicated |
83
+ | AppSync | Dedicated |
84
+ | Everything else | Generic (tagged resources only) |
85
+
86
+ ---
87
+
88
+ ## Project structure
89
+
90
+ ```
91
+ cloudwire/ # Python package (the distributable unit)
92
+ ├── __init__.py # Package version
93
+ ├── cli.py # `cloudwire` CLI entry point (click)
94
+ ├── static/ # Built React app (populated by `make build`)
95
+ │ ├── index.html
96
+ │ └── assets/
97
+ └── app/ # FastAPI backend
98
+ ├── main.py # App factory, API routes (/api/*), static serving
99
+ ├── models.py # Pydantic request/response models
100
+ ├── scanner.py # boto3 AWS scanner — one function per service
101
+ ├── scan_jobs.py # Async job store with progress tracking
102
+ └── graph_store.py # networkx graph with thread-safe mutations
103
+
104
+ frontend/ # React + Vite source (compiled into cloudwire/static/)
105
+ ├── src/
106
+ │ ├── pages/CloudWirePage.jsx # Main page — orchestrates all state
107
+ │ ├── components/
108
+ │ │ ├── graph/ # GraphCanvas, GraphNode, GraphEdge, Minimap, Legend
109
+ │ │ └── layout/ # TopBar, ServiceSidebar, InspectorPanel
110
+ │ ├── hooks/
111
+ │ │ ├── useScanPolling.js # Scan lifecycle, polling, graph data state
112
+ │ │ └── useGraphViewport.js # Pan/zoom viewport state
113
+ │ ├── lib/
114
+ │ │ ├── graphTransforms.js # Layout algorithms (circular, flow, swimlane)
115
+ │ │ ├── serviceVisuals.jsx # Service icon + color map
116
+ │ │ └── awsRegions.js # AWS region list
117
+ │ └── styles/graph.css # All UI styles
118
+ ├── vite.config.js # base: "./", outDir: ../cloudwire/static, dev proxy
119
+ └── package.json
120
+
121
+ .github/workflows/publish.yml # CI: build + publish to PyPI on version tag push
122
+ pyproject.toml # Package metadata, dependencies, entry point
123
+ Makefile # make build / make dev / make clean
124
+ .python-version # Pins Python 3.11 for consistent builds
125
+ ```
126
+
127
+ ---
128
+
129
+ ## Contributing
130
+
131
+ ### Prerequisites
132
+
133
+ - Python 3.9+ (3.11 recommended)
134
+ - Node.js 18+
135
+ - AWS credentials configured (any method)
136
+
137
+ ### Set up the dev environment
138
+
139
+ ```bash
140
+ git clone https://github.com/hisingh_gwre/cloudwire
141
+ cd cloudwire
142
+
143
+ # Python
144
+ python3 -m venv .venv
145
+ source .venv/bin/activate
146
+ pip install -e .
147
+
148
+ # Frontend
149
+ cd frontend && npm install
150
+ ```
151
+
152
+ ### Run in development mode
153
+
154
+ ```bash
155
+ make dev
156
+ ```
157
+
158
+ This starts the FastAPI backend on `:8000` (with `--reload`) and the Vite dev server on `:5173` concurrently. The Vite dev server proxies all `/api/*` requests to the backend — no CORS config needed.
159
+
160
+ ### Making changes
161
+
162
+ | Area | Where to edit |
163
+ |------|--------------|
164
+ | Add a new AWS service scanner | `cloudwire/app/scanner.py` → add a `_scan_<service>` method and register it in `self.service_scanners` |
165
+ | Change graph layout | `frontend/src/lib/graphTransforms.js` |
166
+ | Add a new UI component | `frontend/src/components/` |
167
+ | Change API routes | `cloudwire/app/main.py` — all routes are under the `/api` prefix |
168
+ | Change CLI options | `cloudwire/cli.py` |
169
+
170
+ ### Before opening a PR
171
+
172
+ - Run a scan against a real (or mocked) AWS account and confirm the graph renders
173
+ - Make sure `make build` completes without errors
174
+ - Keep PRs focused — one feature or fix per PR
175
+
176
+ ### Code style
177
+
178
+ - Python: standard library imports first, then third-party, then local. No formatter enforced yet.
179
+ - JavaScript: no linter enforced yet. Match the style of the surrounding file.
180
+
181
+ ---
182
+
183
+ ## Links
184
+
185
+ - [Usage & setup guide](docs/USAGE.md)
186
+ - [Release guide for maintainers](docs/RELEASING.md)
@@ -0,0 +1,156 @@
1
+ # cloudwire
2
+
3
+ Scan your AWS account and visualize resource dependencies as an interactive graph — directly in your browser, running entirely on your local machine.
4
+
5
+ No data leaves your system. AWS credentials never leave your terminal. The graph is built locally using your existing credential chain (`~/.aws/credentials`, `aws sso login`, `saml2aws`, `aws-vault` — all work out of the box).
6
+
7
+ ---
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ pip install cloudwire
13
+ cloudwire
14
+ ```
15
+
16
+ That's it. The browser opens automatically at `http://localhost:8080`.
17
+
18
+ > **Requirements:** Python 3.9+ and valid AWS credentials configured locally.
19
+
20
+ ---
21
+
22
+ ## What it looks like
23
+
24
+ - Dark hacker-aesthetic graph canvas
25
+ - Nodes represent AWS resources — Lambda functions, SQS queues, API Gateways, RDS instances, S3 buckets, and more
26
+ - Edges represent relationships and data flow between resources
27
+ - Click any node to inspect its attributes and connected resources
28
+ - Search, filter by service, highlight upstream/downstream blast radius
29
+
30
+ ---
31
+
32
+ ## Supported services
33
+
34
+ | Service | Scanner |
35
+ |---------|---------|
36
+ | API Gateway | Dedicated |
37
+ | Lambda | Dedicated (with state) |
38
+ | SQS | Dedicated |
39
+ | SNS | Dedicated |
40
+ | EventBridge | Dedicated |
41
+ | DynamoDB | Dedicated (with state) |
42
+ | EC2 | Dedicated (with state) |
43
+ | ECS | Dedicated |
44
+ | S3 | Dedicated |
45
+ | RDS | Dedicated (with state) |
46
+ | Step Functions | Dedicated |
47
+ | Kinesis | Dedicated |
48
+ | IAM | Dedicated |
49
+ | Cognito | Dedicated |
50
+ | CloudFront | Dedicated (with state) |
51
+ | ElastiCache | Dedicated (with state) |
52
+ | Glue | Dedicated |
53
+ | AppSync | Dedicated |
54
+ | Everything else | Generic (tagged resources only) |
55
+
56
+ ---
57
+
58
+ ## Project structure
59
+
60
+ ```
61
+ cloudwire/ # Python package (the distributable unit)
62
+ ├── __init__.py # Package version
63
+ ├── cli.py # `cloudwire` CLI entry point (click)
64
+ ├── static/ # Built React app (populated by `make build`)
65
+ │ ├── index.html
66
+ │ └── assets/
67
+ └── app/ # FastAPI backend
68
+ ├── main.py # App factory, API routes (/api/*), static serving
69
+ ├── models.py # Pydantic request/response models
70
+ ├── scanner.py # boto3 AWS scanner — one function per service
71
+ ├── scan_jobs.py # Async job store with progress tracking
72
+ └── graph_store.py # networkx graph with thread-safe mutations
73
+
74
+ frontend/ # React + Vite source (compiled into cloudwire/static/)
75
+ ├── src/
76
+ │ ├── pages/CloudWirePage.jsx # Main page — orchestrates all state
77
+ │ ├── components/
78
+ │ │ ├── graph/ # GraphCanvas, GraphNode, GraphEdge, Minimap, Legend
79
+ │ │ └── layout/ # TopBar, ServiceSidebar, InspectorPanel
80
+ │ ├── hooks/
81
+ │ │ ├── useScanPolling.js # Scan lifecycle, polling, graph data state
82
+ │ │ └── useGraphViewport.js # Pan/zoom viewport state
83
+ │ ├── lib/
84
+ │ │ ├── graphTransforms.js # Layout algorithms (circular, flow, swimlane)
85
+ │ │ ├── serviceVisuals.jsx # Service icon + color map
86
+ │ │ └── awsRegions.js # AWS region list
87
+ │ └── styles/graph.css # All UI styles
88
+ ├── vite.config.js # base: "./", outDir: ../cloudwire/static, dev proxy
89
+ └── package.json
90
+
91
+ .github/workflows/publish.yml # CI: build + publish to PyPI on version tag push
92
+ pyproject.toml # Package metadata, dependencies, entry point
93
+ Makefile # make build / make dev / make clean
94
+ .python-version # Pins Python 3.11 for consistent builds
95
+ ```
96
+
97
+ ---
98
+
99
+ ## Contributing
100
+
101
+ ### Prerequisites
102
+
103
+ - Python 3.9+ (3.11 recommended)
104
+ - Node.js 18+
105
+ - AWS credentials configured (any method)
106
+
107
+ ### Set up the dev environment
108
+
109
+ ```bash
110
+ git clone https://github.com/hisingh_gwre/cloudwire
111
+ cd cloudwire
112
+
113
+ # Python
114
+ python3 -m venv .venv
115
+ source .venv/bin/activate
116
+ pip install -e .
117
+
118
+ # Frontend
119
+ cd frontend && npm install
120
+ ```
121
+
122
+ ### Run in development mode
123
+
124
+ ```bash
125
+ make dev
126
+ ```
127
+
128
+ This starts the FastAPI backend on `:8000` (with `--reload`) and the Vite dev server on `:5173` concurrently. The Vite dev server proxies all `/api/*` requests to the backend — no CORS config needed.
129
+
130
+ ### Making changes
131
+
132
+ | Area | Where to edit |
133
+ |------|--------------|
134
+ | Add a new AWS service scanner | `cloudwire/app/scanner.py` → add a `_scan_<service>` method and register it in `self.service_scanners` |
135
+ | Change graph layout | `frontend/src/lib/graphTransforms.js` |
136
+ | Add a new UI component | `frontend/src/components/` |
137
+ | Change API routes | `cloudwire/app/main.py` — all routes are under the `/api` prefix |
138
+ | Change CLI options | `cloudwire/cli.py` |
139
+
140
+ ### Before opening a PR
141
+
142
+ - Run a scan against a real (or mocked) AWS account and confirm the graph renders
143
+ - Make sure `make build` completes without errors
144
+ - Keep PRs focused — one feature or fix per PR
145
+
146
+ ### Code style
147
+
148
+ - Python: standard library imports first, then third-party, then local. No formatter enforced yet.
149
+ - JavaScript: no linter enforced yet. Match the style of the surrounding file.
150
+
151
+ ---
152
+
153
+ ## Links
154
+
155
+ - [Usage & setup guide](docs/USAGE.md)
156
+ - [Release guide for maintainers](docs/RELEASING.md)
@@ -0,0 +1,3 @@
1
+ """CloudWire — scan and visualize your AWS infrastructure."""
2
+
3
+ __version__ = "0.1.0"
@@ -0,0 +1 @@
1
+ """CloudWire backend package."""
@@ -0,0 +1,88 @@
1
+ from __future__ import annotations
2
+
3
+ from datetime import datetime, timezone
4
+ from threading import Lock
5
+ from typing import Any, Dict, List
6
+
7
+ import networkx as nx
8
+
9
+
10
+ class GraphStore:
11
+ def __init__(self) -> None:
12
+ self.graph = nx.DiGraph()
13
+ self.metadata: Dict[str, Any] = {
14
+ "last_scan_at": None,
15
+ "region": None,
16
+ "scanned_services": [],
17
+ "warnings": [],
18
+ }
19
+ self._lock = Lock()
20
+
21
+ def reset(self, *, region: str, services: List[str]) -> None:
22
+ with self._lock:
23
+ self.graph = nx.DiGraph()
24
+ self.metadata = {
25
+ "last_scan_at": datetime.now(timezone.utc).isoformat(),
26
+ "region": region,
27
+ "scanned_services": services,
28
+ "warnings": [],
29
+ }
30
+
31
+ def add_warning(self, warning: str) -> None:
32
+ with self._lock:
33
+ self.metadata.setdefault("warnings", []).append(warning)
34
+
35
+ def update_metadata(self, **kwargs: Any) -> None:
36
+ with self._lock:
37
+ self.metadata.update(kwargs)
38
+
39
+ def add_node(self, node_id: str, **attrs: Any) -> None:
40
+ with self._lock:
41
+ current = self.graph.nodes[node_id] if self.graph.has_node(node_id) else {}
42
+ merged = {**current, **attrs}
43
+ merged["id"] = node_id
44
+ self.graph.add_node(node_id, **merged)
45
+
46
+ def add_edge(self, source: str, target: str, **attrs: Any) -> None:
47
+ with self._lock:
48
+ current = self.graph.get_edge_data(source, target, default={})
49
+ merged = {**current, **attrs}
50
+ self.graph.add_edge(source, target, **merged)
51
+
52
+ def _serialize_node(self, node_id: str, attrs: Dict[str, Any]) -> Dict[str, Any]:
53
+ payload = {"id": node_id}
54
+ payload.update(attrs)
55
+ return payload
56
+
57
+ def _serialize_edge(self, source: str, target: str, attrs: Dict[str, Any]) -> Dict[str, Any]:
58
+ payload = {"id": f"{source}__{target}", "source": source, "target": target}
59
+ payload.update(attrs)
60
+ return payload
61
+
62
+ def get_graph_payload(self) -> Dict[str, Any]:
63
+ with self._lock:
64
+ nodes = [self._serialize_node(node_id, attrs) for node_id, attrs in self.graph.nodes(data=True)]
65
+ edges = [
66
+ self._serialize_edge(source, target, attrs)
67
+ for source, target, attrs in self.graph.edges(data=True)
68
+ ]
69
+ metadata = dict(self.metadata)
70
+ metadata["node_count"] = len(nodes)
71
+ metadata["edge_count"] = len(edges)
72
+ return {"nodes": nodes, "edges": edges, "metadata": metadata}
73
+
74
+ def get_resource_payload(self, resource_id: str) -> Dict[str, Any]:
75
+ with self._lock:
76
+ if not self.graph.has_node(resource_id):
77
+ raise KeyError(resource_id)
78
+
79
+ node = self._serialize_node(resource_id, dict(self.graph.nodes[resource_id]))
80
+ incoming = [
81
+ self._serialize_edge(source, resource_id, dict(attrs))
82
+ for source, _, attrs in self.graph.in_edges(resource_id, data=True)
83
+ ]
84
+ outgoing = [
85
+ self._serialize_edge(resource_id, target, dict(attrs))
86
+ for _, target, attrs in self.graph.out_edges(resource_id, data=True)
87
+ ]
88
+ return {"node": node, "incoming": incoming, "outgoing": outgoing}