{"id":51439720,"url":"https://github.com/redscaresu/infrafactory","last_synced_at":"2026-07-05T10:01:54.189Z","repository":{"id":361723523,"uuid":"1162435836","full_name":"redscaresu/infrafactory","owner":"redscaresu","description":"Scenario-driven infrastructure-as-code generation with LLM agents. Multi-cloud (Scaleway, GCP, AWS) with deterministic mock-backed validation.","archived":false,"fork":false,"pushed_at":"2026-06-10T13:58:37.000Z","size":99263,"stargazers_count":13,"open_issues_count":6,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-10T15:13:28.186Z","etag":null,"topics":["ai-agents","aws","claude","gcp","golang","infrastructure-as-code","llm","openrouter","opentofu","scaleway","sveltekit","terraform"],"latest_commit_sha":null,"homepage":"https://github.com/redscaresu/infrafactory","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/redscaresu.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":"ROADMAP.md","authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-02-20T09:03:58.000Z","updated_at":"2026-06-10T13:59:07.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/redscaresu/infrafactory","commit_stats":null,"previous_names":["redscaresu/infrafactory"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/redscaresu/infrafactory","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/redscaresu%2Finfrafactory","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/redscaresu%2Finfrafactory/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/redscaresu%2Finfrafactory/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/redscaresu%2Finfrafactory/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/redscaresu","download_url":"https://codeload.github.com/redscaresu/infrafactory/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/redscaresu%2Finfrafactory/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35150063,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-05T02:00:06.290Z","response_time":100,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai-agents","aws","claude","gcp","golang","infrastructure-as-code","llm","openrouter","opentofu","scaleway","sveltekit","terraform"],"created_at":"2026-07-05T10:01:53.617Z","updated_at":"2026-07-05T10:01:54.177Z","avatar_url":"https://github.com/redscaresu.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# InfraFactory\n\n[![CI](https://github.com/redscaresu/infrafactory/actions/workflows/ci.yml/badge.svg)](https://github.com/redscaresu/infrafactory/actions/workflows/ci.yml)\n[![License: Apache 2.0](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](LICENSE)\n[![Go 1.25+](https://img.shields.io/badge/Go-1.25%2B-00ADD8?logo=go)](go.mod)\n\nScenario-driven OpenTofu generation and validation across **AWS**, **GCP**, and **Scaleway** — generated by an LLM, validated against deterministic mock servers in seconds, optionally deployed against real cloud APIs.\n\n## Why this exists\n\nHand-iterating IaC against real cloud APIs is slow, expensive, and flaky. LLMs are good at writing terraform but bad at debugging \"why didn't this apply\" — the error messages are layers deep and the feedback loop is 90 seconds per attempt against a real cloud.\n\nInfraFactory closes that loop. You write a scenario YAML declaring intent (resources + acceptance criteria). The pipeline generates HCL with an LLM, validates it through four layers (static → mock-deploy → real-deploy → destruction), and feeds structured failures back into the next iteration's prompt. Subsecond mock validation, no cloud credentials required.\n\n## Demo\n\n### CLI\n\n![CLI demo](docs/demo/infrafactory.gif)\n\n`infrafactory run scenarios/training/gcp-pubsub.yaml` against `fakegcp`: scenario YAML → 3-phase LLM generation → 3-layer validation → AI's first iteration fails (fakegcp rejects `google_project_service`) → feedback fed into the next iteration's prompt → second iteration converges to `Status: success`. Demonstrates the feedback loop that makes the pipeline robust against partial mock coverage. Re-record with `./docs/demo/record.sh` (requires `make mocks-up` + an LLM credential in env).\n\n### Web UI — live run\n\n![UI live-run demo](docs/demo/ui-walkthrough-run.gif)\n\nActually runs `gcp-pubsub` through the UI: scenario page → click Run → Live page populates with iteration stages live as the AI tries to build the topic + subscription against fakegcp → iteration 1 fails (fakegcp doesn't model `google_project_service` yet) → AI sees the feedback in iteration 2's prompt and converges → success banner → per-run IaC viewer shows the converged HCL with auto-injected `*_custom_endpoint` overrides pointing at fakegcp. ~2min end-to-end, 2 LLM iterations. Re-record with `make demo-ui-run` (needs `make mocks-up` + Claude CLI authenticated).\n\n### Web UI — tour\n\n![UI walkthrough](docs/demo/ui-walkthrough.gif)\n\nBrowser walkthrough of `full-stack-paris` (the most resource-dense scenario) — no `infrafactory run`, just a tour of the Scenario / Runs / Compare / Pitfalls / Diagnostics pages so viewers see the UI surface (24s, no LLM credit needed). Re-record with `make demo-ui`.\n\n## Quickstart — 60-second demo\n\nThree commands gets you a working LLM-driven infra pipeline against\nlocal mock servers, validates a real terraform scenario end-to-end,\nand tears everything down cleanly. **No cloud credentials. No real\ncloud calls. ~60 seconds.**\n\n```bash\n# 1. Clone the five repos side-by-side (sibling layout).\nmkdir -p ~/dev \u0026\u0026 cd ~/dev\nfor repo in infrafactory fakeaws fakegcp mockway fakegenesys; do\n  git clone https://github.com/redscaresu/$repo.git\ndone\ncd infrafactory\n\n# 2. Bring up the full stack — mockway + fakegcp + fakeaws + fakegenesys +\n#    SeaweedFS (S3) + the SvelteKit UI — in one command, backgrounded.\nmake up\n\n# 3. Run the fastest scenario end-to-end (~30s, 1 iteration).\n./bin/infrafactory run scenarios/training/block-paris.yaml --config infrafactory.yaml\n\n# 4. (Optional) point a browser at http://127.0.0.1:4173 to see the\n#    same scenario in the UI with per-iteration stage breakdown.\n\n# 5. Tear it all down.\nmake down\n```\n\nYou should see `Status: success` and `run/terminal_reason: pass (target_reached)`\nafter step 3. The LLM generated a Scaleway Block Storage volume in HCL,\nthe static validator + mockway apply + topology test + destroy/orphan-check\nall passed. The default `run` tears the resources down at the end of the\ntest cycle (the scenario's `destruction: no_orphans` acceptance criterion),\nso `http://127.0.0.1:8080/mock/state` reports empty collections. To inspect\nthe post-apply state, add `--no-destroy` to the run command.\n\nUse `make status` at any time to see which of the six ports\n(`8080`, `8081`, `8082`, `9090`, `9091`, `4173`) are listening.\n\n### Prerequisites\n\n- Go 1.25+\n- OpenTofu (https://opentofu.org) on PATH\n- Docker (for the SeaweedFS S3 backend used by AWS scenarios) — only\n  needed when running AWS-cloud scenarios; Scaleway-only and GCP-only\n  demos don't require it\n- An LLM credential, see below\n\n### LLM provider\n\nInfraFactory drives generation through the Claude CLI by default —\nsign in with `claude login` once and it works out of the box. To use\na different model via OpenRouter instead, export `OPENROUTER_API_KEY`\nand set `agent.type: openrouter` in `infrafactory.yaml`. Both paths\nhit the same 3-phase generation pipeline (`plan → write HCL →\nself-review`); pick whichever fits your budget/latency profile.\n\n### What's running\n\n| Port | Service | Why |\n|---|---|---|\n| 8080 | mockway | Scaleway HTTP API mock |\n| 8081 | fakegcp | GCP API mock |\n| 8082 | fakeaws | AWS API mock |\n| 8083 | fakegenesys | Genesys Cloud CCaaS mock |\n| 9090 | SeaweedFS | S3-compatible backend (Docker; AWS-only scenarios) |\n| 9091 | s3router (S80) | HTTP shim that fans S3 traffic across SeaweedFS (data plane) and fakeaws (`?publicAccessBlock` subresource SeaweedFS doesn't model). `infrafactory.yaml` `s3.url` points here, not directly at SeaweedFS. See `cmd/s3router/`. |\n| 4173 | infrafactory UI | SvelteKit dashboard + scenario runner |\n\n### Other scenarios\n\nAfter `make up`, any of these run against the same stack:\n\n```bash\n./bin/infrafactory run scenarios/training/gcp-full-stack.yaml          # cloud: gcp      → fakegcp\n./bin/infrafactory run scenarios/training/aws-full-stack.yaml          # cloud: aws      → fakeaws\n./bin/infrafactory run scenarios/training/full-stack-paris.yaml        # cloud: scaleway → mockway\n./bin/infrafactory run scenarios/training/genesys-full-stack.yaml      # cloud: genesys  → fakegenesys\n```\n\nThere are 44 scenarios under `scenarios/training/` (39 existing + 5 genesys from the S108-S115 arc). Inspect generated\nHCL at `output/\u003cscenario\u003e/` (overwritten each run) and immutable\nper-run artifacts at `.infrafactory/runs/\u003cscenario\u003e/\u003crun-id\u003e/`.\n\nA successful run ends with `Status: success` and `run/terminal_reason: pass (target_reached)`. If a validation layer fails, the failure JSON feeds into the next iteration's LLM prompt and the loop retries (default budget: 5 iterations).\n\n## Web UI\n\n`make up` already started the UI on `http://127.0.0.1:4173`. If you'd\nrather start just the UI (without the mocks), use `make run`.\n\nThe UI provides a scenario browser (edit YAML, see real-time validation), run controls (`--clean` / `--no-destroy` / Layer-3 toggles), a live page with per-iteration timer and stage indicators, per-run IaC viewer with diffs, and a pitfalls editor. See the UI demo above for the `full-stack-paris` walkthrough.\n\n## How it works\n\n```\nscenario YAML  ──▶  3-phase LLM generation  ──▶  3-layer validation  ──▶  retry on failure\n   (intent)         plan → write HCL → review     static / mock / real     (5x budget)\n```\n\n**Three-phase generation** (`prompts/{aws,gcp,scaleway}/phase{1,2,3}*.md`):\n1. **Plan architecture** — scenario YAML + T-shirt size mappings → JSON architecture plan\n2. **Generate HCL** — architecture + cloud-specific pitfalls + provider schema → OpenTofu `.tf` files\n3. **Self-review** — generated HCL → 10-point checklist → corrections or `NO ISSUES FOUND`\n\n**Three-layer validation** (each gates the next):\n1. **Static** — `tofu init/validate/plan` + OPA `deny` policies on the plan JSON\n2. **Mock deploy** — `tofu apply` against the matching mock; topology checks against `/mock/state`; OPA `deny_state` policies; mock-enforced FK integrity\n3. **Real deploy** (optional, gated by `validation.layers.sandbox_deploy.enabled`) — `tofu apply` against the real cloud with auto-destroy on failure\n\nOn failure, the structured failure (`layer`, `stage`, `check`, `detail`, `failure_class`) is appended to the next iteration's prompt as a `\u003cfeedback\u003e` block so the LLM sees what specifically broke.\n\n**Auto-learning loop**: when an iteration self-corrects (iter N+1 succeeds after iter N failed) OR a run terminates with `stuck`/`repair_budget_exhausted`, the failure detail is extracted into `pitfalls/\u003ccloud\u003e.yaml` so future runs of any scenario in that cloud see the lesson up front. The descriptive fallback emits `source: descriptive` — the system seeds itself from real runs and a CI ratchet (`TestPitfallsNoHumanSeeding`) rejects hand-authored entries.\n\nWhen a run reaches `target_reached` AFTER ≥1 failing iteration, two diff-based extractors run: one emits `source: fix` (the HCL snippet the LLM added to clear the failure), the other emits `source: avoid` (an attribute or block the LLM removed). Both are prescriptive guidance derived from real runs, not hand-written. This unblocked the \"prompt-collapse\" effort: prescriptive rules in the phase-2 prompts retired as the system's learned pitfalls replaced them. See [`docs/auto-learning-loop.md`](docs/auto-learning-loop.md) for the full architecture; `ADR-0012` and `ADR-0018` carry the original contracts.\n\n## Cloud coverage\n\nEach cloud has the same set of extension points; the scenario's `cloud:` field drives every dispatch.\n\n| Extension point | AWS | GCP | Scaleway |\n|---|---|---|---|\n| Mock server | [`fakeaws`](https://github.com/redscaresu/fakeaws) (`:8082`) + [SeaweedFS](https://github.com/seaweedfs/seaweedfs) for S3 (`:9090`) | [`fakegcp`](https://github.com/redscaresu/fakegcp) (`:8081`) | [`mockway`](https://github.com/redscaresu/mockway) (`:8080`) |\n| Provider pin | `hashicorp/aws ~\u003e 5.70` | `hashicorp/google \u003e= 5.0` (v5 for IAM SA) | `scaleway/scaleway \u003e= 2.50` |\n| Prompts | `prompts/aws/` | `prompts/gcp/` | `prompts/scaleway/` |\n| Pitfalls | `pitfalls/aws.yaml` | `pitfalls/gcp.yaml` | `pitfalls/scaleway.yaml` |\n| OPA policies | `policies/aws/` | `policies/gcp/` | `policies/scaleway/` |\n| Training scenarios | `scenarios/training/aws-*.yaml` | `scenarios/training/gcp-*.yaml` | `scenarios/training/*-paris.yaml` |\n| Full-stack example | `aws-full-stack.yaml` | `gcp-full-stack.yaml` | `full-stack-paris.yaml` |\n\nEach first-party mock is wire-shape compatible with the matching real provider, enforced by an `examples/working/\u003csvc\u003e` smoke harness in the mock's own repo (`apply → plan -detailed-exitcode 0 → destroy`). See each mock's README for the API-compatibility contract.\n\nAWS S3 is the exception: bucket sub-resource reads (GetBucketPolicy / GetBucketTagging / etc.) are served by SeaweedFS instead of fakeaws's stripped-down S3 handler — `terraform-provider-aws`'s bucket Read flow needs the full management surface. SeaweedFS doesn't model `?publicAccessBlock`, so a small reverse-proxy shim (`cmd/s3router/`, S80) fronts both backends: it routes `?publicAccessBlock` to fakeaws and fans `PUT/DELETE /\u003cbucket\u003e` to both so the bucket exists in both stores. Rationale + the SeaweedFS-vs-Adobe-S3Mock-vs-Garage-vs-LocalStack evaluation is documented in [`CONCEPT.md`](CONCEPT.md) under \"Third-Party Mock Integration\".\n\nAdding a new cloud requires: prompt templates, pitfalls file, topology derivation rules, mock server, OPA policies, and training scenarios. Dispatch is driven by `cloudMockStateRouter`, `cloudConstraintPolicies`, `filterPolicyPathsByCloud`, `ExtractProviderSchemaForCloud`, and `detectCloud`.\n\n## Commands\n\n| Command | Purpose |\n|---|---|\n| `infrafactory init --path \u003cfile\u003e` | Scaffold a new scenario YAML |\n| `infrafactory validate \u003cscenario\u003e` | Layer 1 static checks only |\n| `infrafactory generate \u003cscenario\u003e` | 3-phase LLM generation only |\n| `infrafactory test \u003cscenario\u003e` | Layers 1-4 (no retry loop) |\n| `infrafactory run \u003cscenario\u003e` | Full pipeline with retry loop + holdouts |\n| `infrafactory mock start/stop/status/logs` | Manage the Mockway (Scaleway) mock only. Use `make mocks-up`/`-down`/`-status`/`-logs` to manage all three (mockway/fakegcp/fakeaws). |\n| `infrafactory mock reset` | Reset state across every configured mock backend (mockway + fakegcp + fakeaws + s3 cascade in one call). Use this between scenarios in sweep harnesses instead of bare `curl` to `/mock/reset` — only this path cascades to the SeaweedFS s3 backend. |\n| `infrafactory ui` | Serve the web dashboard |\n\nAuxiliary binary (`bin/extract-pitfall`, built by `make build`) drives\nthe fix + avoid pitfall extractors against a recorded run directory\nand emits a candidate `pitfalls/\u003ccloud\u003e.yaml` snippet on stdout —\nused by the prompt-rule retirement protocol's step 2 when the organic\nlearning loop hasn't fired for the target pattern. See\n[`docs/auto-learning-loop.md`](docs/auto-learning-loop.md),\n`docs/decisions/0012-dynamic-pitfalls.md`, and\n`docs/decisions/0018-n11-retirement-criteria.md` for the\nauto-learning architecture.\n\nKey flags for `run`: `--clean` (fresh start), `--no-destroy` (keep resources for incremental follow-up), `--repair-iterations-max N` (retry budget, default 5).\n\n## Acceptance criteria\n\nScenario YAML declares criteria that gate run success:\n\n| Type | Layer 2 (mock) | Layer 3 (real) |\n|---|---|---|\n| `connectivity` | Topology graph query | TCP connect with retry |\n| `http_probe` | Topology graph query | HTTP GET, expect 2xx/3xx |\n| `dns_resolution` | Auto-pass (informational) | DNS A/AAAA lookup with retry |\n| `policy` | OPA rules on plan + state | Same |\n| `destruction` | Orphan check after destroy | Same + real destroy |\n\n## Repository layout\n\n```\ninfrafactory/\n├── cmd/infrafactory/      CLI + embedded UI build\n├── internal/\n│   ├── cli/               command tree, runtime wiring\n│   ├── config/            infrafactory.yaml loader\n│   ├── scenario/          YAML loader + JSON schema validation\n│   ├── generator/         3-phase LLM pipeline (Claude / OpenRouter)\n│   ├── harness/           static/mock/destroy primitives + provider schema extraction\n│   ├── feedback/          failure-signature modelling, stuck detection\n│   ├── runstore/          .infrafactory/runs persistence\n│   └── e2e/               cross-repo end-to-end tests\n├── ui/                    SvelteKit dashboard\n├── scenarios/training/    per-cloud training scenarios\n├── prompts/{aws,gcp,scaleway}/  phase 1-3 prompt templates\n├── pitfalls/{aws,gcp,scaleway}.yaml  static + learned pitfalls\n├── policies/{aws,gcp,scaleway}/  OPA rego files\n├── scenario.schema.json   scenario contract\n└── infrafactory.yaml      runtime config contract\n```\n\n## Development\n\nPre-commit hook runs gitleaks + `make test` (Go unit + UI unit + Playwright e2e). Wire it once:\n```bash\nmake install-hooks\n```\n\nCommon targets:\n```bash\nmake test                  # full suite\nmake test-unit             # Go only\nmake ui-test-e2e           # Playwright only\n\n# go-run mock path (foreground subprocesses; quick iteration on mock code):\nmake mocks-up              # start mockway + fakegcp + fakeaws (+ SeaweedFS via Docker)\nmake mocks-down            # stop them all\nmake mocks-status          # show port + PID for each (probes lsof, not just pidfiles)\nmake mocks-restart         # mocks-down + mocks-up; picks up sibling-repo source changes\nmake mockway-restart       # restart just one mock (also: fakegcp-restart, fakeaws-restart)\n\n# container mock path (alternative — needs Docker, no Go install required;\n# wires identical ports 8080/8081/8082 so scenarios + infrafactory.yaml\n# don't care which path is active):\nmake mocks-up-containers   # build + start fakeaws + fakegcp + mockway\nmake mocks-down-containers\nmake mocks-pull            # refresh published GHCR images\n\n# canonical sustain-ratchet sweep. Drives `infrafactory run` across\n# every scenario under scenarios/training/ with `infrafactory mock reset`\n# between scenarios. Output lands in /tmp/sweep-N/summary.tsv. Discards\n# pitfall additions (sweep noise); preserves `avoid` entries via\n# bin/pitfall-merge. Renames `make sweep-39` → `make sweep-N` when a new\n# cloud lands; current N = 39.\nmake sweep-39\n\n# session-close hygiene — sweeps lingering sweep scripts, log tails,\n# and stray mock test binaries (compiled to /tmp/ by `go test`) on\n# non-canonical ports. Safe to run any time; idempotent.\nmake clean-bg\n```\n\nWhen working on a sibling mock repo (`../fakegcp`, `../fakeaws`,\n`../mockway`), `make mocks-up` spins up those mocks via `go run`\nwhich compiles ONCE at boot. After committing a change in the\nsibling repo, run `make \u003cmock\u003e-restart` (e.g. `make fakegcp-restart`)\nto pick up the new source — otherwise the running mock keeps\nserving the stale binary, a footgun that's wasted several debugging\nsessions worth of time.\n\nGated e2e tests (cross-repo, require `tofu` + the sibling mock repos checked out):\n```bash\nINFRAFACTORY_ENABLE_E2E=1 go test ./internal/e2e/...\n```\n\n## Documentation\n\n- [`docs/architecture.md`](docs/architecture.md) — component overview and validation-layer details\n- [`docs/auto-learning-loop.md`](docs/auto-learning-loop.md) — deep-dive on the mock-server-bug classifier + fix/avoid extractors + diff-pattern templates, the ratchets, the sweep-time protocol, and a worked example\n- [`docs/decisions/`](docs/decisions/) — ADRs (dynamic pitfalls, topology derivation, etc.)\n- [`docs/scenario-failure-matrix.md`](docs/scenario-failure-matrix.md) — per-scenario pass/fail snapshot + failure classification\n- [`AGENTS.md`](AGENTS.md) — entry point for AI agents working on this repo\n- [`CONTRIBUTING.md`](CONTRIBUTING.md) — code conventions, PR contract, quality gates\n- [`SECURITY.md`](SECURITY.md) — disclosure policy\n- [`CHANGELOG.md`](CHANGELOG.md)\n\n## License\n\nApache 2.0 — see [LICENSE](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fredscaresu%2Finfrafactory","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fredscaresu%2Finfrafactory","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fredscaresu%2Finfrafactory/lists"}