Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ ENV COREPACK_ENABLE_DOWNLOAD_PROMPT=0
RUN echo "node ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/node \
&& chmod 0440 /etc/sudoers.d/node

# Allow node user to access Docker socket (gid 999 is the typical docker group gid)
RUN groupadd -g 999 docker 2>/dev/null || true \
&& usermod -aG docker node

USER node

RUN mkdir -p /home/node/.local/share/pnpm/store
Expand Down
1 change: 1 addition & 0 deletions .devcontainer/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ services:
- ..:/workspace:cached
- openarchflow-pnpm-store:/home/node/.local/share/pnpm/store
- ${HOME}/.aws:/home/node/.aws:ro
- /var/run/docker.sock:/var/run/docker.sock
environment:
MINISTACK_ENDPOINT: http://ministack:4566
command: sleep infinity
Expand Down
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ OpenArchFlow can now deploy your AWS architecture diagrams to a local AWS emulat
**Browser-Direct Architecture**

- All AWS SDK v3 calls go directly from the browser to `localhost:4566` — no Next.js API route proxy required. This means the panel works even when the app is hosted on Vercel (the user's browser calls their local MiniStack directly).
- MiniStack CORS support enables this without any extra configuration.
- When the app's origin differs from `localhost` (e.g., `app.openarchflow.cloud`), use the bundled CORS reverse-proxy (`docker-compose.ministack.yml` on port 4567) to add the missing CORS headers that MiniStack itself does not provide.

### Technical

Expand Down
56 changes: 51 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,13 +90,34 @@ Scan a live AWS account and turn its running resources into a diagram in seconds

Deploy your architecture diagram to a local AWS emulator ([MiniStack](https://ministack.dev)) running on `localhost:4566` — turning OpenArchFlow into a full **design → deploy → operate** platform with no cloud costs and no AWS account required.

#### Why a CORS proxy?

When you access the **hosted app** at [app.openarchflow.cloud](https://app.openarchflow.cloud) from your browser and connect to MiniStack running on your local machine (`localhost:4566`), the browser enforces CORS (Cross-Origin Resource Sharing) policy. Your browser at `https://app.openarchflow.cloud` cannot directly fetch from `http://localhost:4566` without CORS headers. MiniStack doesn't provide global CORS configuration, so we use a lightweight nginx reverse-proxy to add these headers transparently.

**You don't need the proxy if:**
- You run OpenArchFlow locally too (`localhost:3000`) — same origin, no CORS issue
- You're willing to disable CORS in your browser (insecure, not recommended)

**Getting started:**

**Option 1: OpenArchFlow running locally** (`localhost:3000`):
```bash
docker run -p 4566:4566 ministackorg/ministack
```
Then open `localhost:3000`, configure MiniStack endpoint as `http://localhost:4566`, and deploy.

**Option 2: Using the hosted app** ([app.openarchflow.cloud](https://app.openarchflow.cloud)) with local MiniStack:
```bash
docker compose -f docker-compose.ministack.yml up -d
```
This starts MiniStack **and** a local nginx sidecar that adds CORS headers, listening on `http://localhost:4567`. Then:
1. Open [app.openarchflow.cloud](https://app.openarchflow.cloud)
2. Click the **Rocket** icon → **Configure MiniStack**
3. Set **Endpoint URL** to `http://localhost:4567` (note: port `4567`, not `4566`)
4. Click **Test Connection** → should see "Connected"
5. Click **Deploy All**

Then click the **Rocket** icon in the toolbar, configure the endpoint, and hit **Deploy All**.
The nginx proxy (port 4567) forwards all requests to MiniStack (port 4566) and adds `Access-Control-Allow-Origin: *` headers, enabling browser access from any origin.

**Supported AWS services:**

Expand All @@ -122,9 +143,27 @@ Then click the **Rocket** icon in the toolbar, configure the endpoint, and hit *
- **Per-node console** — click any deployed node to open its interactive console (S3 browser, Lambda invoker, SQS message reader, etc.).
- **Simulation hybrid mode** — when a node is deployed, simulation traffic is routed to the real MiniStack resource and uses wall-clock latency instead of synthetic values.
- **Traffic Source node** — generate configurable req/s traffic from the diagram canvas; live response icon shows last result.
- **Browser-direct** — all AWS SDK v3 calls go from your browser directly to `localhost:4566`. Works even when OpenArchFlow is hosted on Vercel.
- **Browser-direct** — all AWS SDK v3 calls go from your browser directly to `localhost:4566`. Works even when OpenArchFlow is hosted on Vercel (with the CORS proxy for remote access).
- **Teardown** — delete all deployed resources in one action.

#### Troubleshooting: CORS errors when using the hosted app

**Error:** `Access to fetch at 'http://localhost:4566/...' from origin 'https://app.openarchflow.cloud' has been blocked by CORS policy`

**Solution:** Use the CORS proxy. MiniStack doesn't provide global CORS configuration, so the bundled nginx reverse-proxy adds the required headers transparently:

```bash
# Start MiniStack with the CORS proxy sidecar
docker compose -f docker-compose.ministack.yml up -d

# In the app, configure MiniStack endpoint as: http://localhost:4567
# (note port 4567, not 4566)
```

The proxy (port 4567) forwards all requests to MiniStack (port 4566) and adds `Access-Control-Allow-Origin: *` headers. This only affects browser access; command-line tools (aws-cli, Terraform) can still talk directly to port 4566.

**Why is this needed?** When you access the hosted app (`app.openarchflow.cloud`) from your browser, the browser enforces CORS policy. Your browser can reach `localhost:4566` on your machine, but only if the response includes appropriate CORS headers. The proxy adds these headers, enabling secure cross-origin access.

### 🏗️ Terraform IaC Generation (NEW)

- **HCL Code Generation**: Export your AWS architecture diagram directly as production-ready HashiCorp Terraform — `main.tf`, `variables.tf`, and `outputs.tf` generated from your nodes and edges.
Expand Down Expand Up @@ -282,9 +321,16 @@ pnpm start

### 6. Deploy to Local AWS (MiniStack)

1. Start MiniStack: `docker run -p 4566:4566 -v /var/run/docker.sock:/var/run/docker.sock -e GLUE_DOCKER_IMAGE=ghcr.io/dmux/openarchflow/ministack_glue_libs_4.0.0_image_01:latest -e S3_PERSIST=1 ministackorg/ministack:full`
2. Click the **🚀 Rocket** icon in the toolbar
3. Click **Test Connection** — you should see "Connected"
1. Start MiniStack (see **MiniStack Local Deploy** section above for setup options)
- Basic: `docker run -p 4566:4566 ministackorg/ministack`
- With Glue support: `docker run -p 4566:4566 -v /var/run/docker.sock:/var/run/docker.sock -e GLUE_DOCKER_IMAGE=ghcr.io/dmux/openarchflow/ministack_glue_libs_4.0.0_image_01:latest -e S3_PERSIST=1 ministackorg/ministack:full`
- With CORS proxy (hosted app): `docker compose -f docker-compose.ministack.yml up -d`
2. In the app, click the **🚀 Rocket** icon in the toolbar
3. In the MiniStack Connection dialog:
- Set **Endpoint URL** to:
- `http://localhost:4566` if running OpenArchFlow locally
- `http://localhost:4567` if using the hosted app (with the CORS proxy)
- Click **Test Connection** to verify
4. Click **Deploy All** — nodes deploy in sequence with live status badges
5. Click any deployed node → **Open Console** to interact with the resource
6. Run a simulation — deployed nodes receive real traffic from the simulation engine
Expand Down
17 changes: 17 additions & 0 deletions docker-compose.ministack.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
version: "3.8"

services:
ministack:
image: ministackorg/ministack
ports:
- "4566:4566"
restart: unless-stopped

ministack-cors-proxy:
build:
context: ./docker/ministack-cors-proxy
ports:
- "4567:4566"
depends_on:
- ministack
restart: unless-stopped
5 changes: 5 additions & 0 deletions docker/ministack-cors-proxy/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
FROM nginx:1.27-alpine

COPY nginx.conf /etc/nginx/conf.d/default.conf

EXPOSE 4566
73 changes: 73 additions & 0 deletions docker/ministack-cors-proxy/nginx.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# CORS-adding reverse proxy in front of MiniStack.
# MiniStack (ministackorg/ministack) does not expose a way to configure global
# CORS headers, so this proxy adds them to every response and short-circuits
# CORS preflight (OPTIONS) requests before they ever reach MiniStack.

resolver 127.0.0.11 valid=10s; # Docker embedded DNS — re-resolve "ministack"
# at runtime instead of caching its IP forever,
# so a `docker compose restart ministack` doesn't
# require restarting this proxy too.

server {
listen 4566;
server_name _;

# --- Local-dev-friendly limits -----------------------------------------
client_max_body_size 0; # unlimited: S3 PUTs and Lambda .zip uploads
client_body_timeout 300s;
proxy_connect_timeout 10s;
proxy_send_timeout 300s;
proxy_read_timeout 300s; # generous for Lambda cold starts / CWL polling

# --- Stream bodies instead of buffering to disk -------------------------
# Important for large S3 objects / Lambda zip uploads: buffering the whole
# request or response would add latency and a disk-size limit.
proxy_http_version 1.1;
proxy_request_buffering off;
proxy_buffering off;

location / {
# --- CORS preflight short-circuit -----------------------------------
# AWS SigV4 requests set Authorization / X-Amz-Date / X-Amz-Content-Sha256
# / X-Amz-Security-Token / Content-Type headers. These are "non-simple"
# per the Fetch spec, so the browser sends an OPTIONS preflight first.
# MiniStack never needs to see these — answer them here directly.
if ($request_method = OPTIONS) {
add_header Access-Control-Allow-Origin "*" always;
add_header Access-Control-Allow-Methods "GET,POST,PUT,PATCH,DELETE,HEAD,OPTIONS" always;
# Reflect whatever headers the SDK says it's about to send, rather
# than hard-coding an exhaustive SigV4 header allowlist.
add_header Access-Control-Allow-Headers "$http_access_control_request_headers" always;
add_header Access-Control-Max-Age 600 always;
return 204;
}

# --- Proxy the real request to MiniStack ----------------------------
set $upstream http://ministack:4566;
proxy_pass $upstream;

# Preserve the Host header EXACTLY as the browser/SDK sent it.
# The AWS SigV4 signature includes "host" as a signed header. MiniStack
# (like LocalStack) recomputes the signature against whatever Host
# header it actually receives, so it must see the same host:port the
# SDK signed (e.g. "localhost:4567") — not the upstream's internal
# name. nginx's default proxy_pass behavior would otherwise send
# "ministack:4566" here and break signature verification.
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Connection "";
proxy_pass_request_headers on;

# --- CORS headers on the real response -------------------------------
# `always` also applies these on 4xx/5xx responses from MiniStack, so
# SDK error-handling code sees the real error instead of a generic
# CORS network failure masking it.
add_header Access-Control-Allow-Origin "*" always;
# Expose AWS-specific response headers (ETag, request IDs, checksums)
# that aren't on the browser's default CORS-safelisted header list —
# some AWS SDK v3 middleware reads these from the raw fetch Response.
add_header Access-Control-Expose-Headers "*" always;
}
}
5 changes: 4 additions & 1 deletion src/components/ministack/MiniStackConfigDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,10 @@ export function MiniStackConfigDialog({ open, onClose }: MiniStackConfigDialogPr
</DialogTitle>
<DialogDescription>
Configure the local AWS emulator endpoint. MiniStack must be running
at the specified address.
at the specified address. Seeing a CORS/network error on Test
Connection when using the hosted app? See the "MiniStack Local
Deploy" section in the README for the CORS proxy setup (use port
4567 instead of 4566).
</DialogDescription>
</DialogHeader>

Expand Down
3 changes: 2 additions & 1 deletion src/lib/ministack/browser-actions.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// All AWS SDK v3 operations — browser-compatible (no Node.js Buffer/fs/stream APIs).
// MiniStack supports CORS; import freely from any "use client" component.
// When the app's origin differs from localhost (e.g., app.openarchflow.cloud),
// use the bundled CORS proxy (docker-compose.ministack.yml) on port 4567.

import {
getS3Client, getSQSClient, getDynamoDBClient, getLambdaClient,
Expand Down
Loading