Infra Console
Local sandbox · Simulated agent reviews · No paid calls or GitHub pushes · Choose flowtest to try it
IC
Infra Console

Describe the infrastructure. The agent writes the Terraform.

Automated checks and independent reviewers examine the plan and your requirements. Your architects review the evidence before handing changes to CI.

Sign in with Microsoft
{{ loginNote }}
Sign in with the email and password an admin set for you (single sign-on is not wired yet). Developers raise requests and answer questions; architects and security review the evidence and approve changes.
What happens here
1 · ask
Pick the project and environment, say what you need in a sentence. The agent composes from the reviewed catalogue and asks only what it cannot infer.
2 · plan
A real terraform plan, against the account's state where the console can read it, priced from the public price list.
3 · review
Automated reviewers, then a named architect; security signs off on sensitive data and on any removal. The approved change lands as a pull request.
Nothing is applied from this console. Every request keeps its plan, the reviewers' verdicts and the names of the people who decided, and the History tab is the audit trail.
IC
Notifications
{{ notifSummary }}
{{ n.icon }}
{{ n.text }}
{{ n.ago }} {{ n.tag }}
{{ notifEmpty }}
Export
Take this request out of the Infra Console
{{ e.label }}
{{ e.note }}
{{ userName }}
{{ userRole }}
Viewing as
Cloud
Theme
Background
Background choices apply in light mode. Switch back with the header toggle to pick one.
{{ m.label }}
{{ m.note }}
‹ Home / {{ crumbTitle }} {{ crumbContext }} {{ crumbId }} {{ crumbState }}
{{ runningText }}
{{ toastText }}
‹ Thread {{ crumbTitle }} {{ crumbId }} {{ crumbState }}
{{ tabCountLabel }}

{{ blueprintTitle }}

{{ blueprintSub }}

Est. monthly
${{ monthly }}
{{ ingressLabel }}
{{ boundaryLabel }} {{ zoneLabel }}
{{ tier.name }}
{{ tier.spread }}
{{ n.name }}
{{ n.resource }}
{{ n.note }}
Traffic flows top to bottom; every tier below the edge is private and reachable only from the tier above it.
Threat score
{{ threatAvg }} / 10 · {{ threatBand }} · {{ threatAbove }} items at 6 or above
Per-item risk, scored the way the OWASP threat modelling process asks for it: decompose the system, classify each item's threats with STRIDE, then rank by damage and exploitability. Lower is safer. Enabling a control moves the number, so the score argues back.
layeritemstride classeswhat drives the scorescore
{{ t.layer }} {{ t.name }} {{ t.stride }} {{ t.driver }} {{ t.score }}
Module dependency view — what each module consumes and what consumes it, derived from the composed HCL itself, so the graph and the code cannot drift apart.
modulesourcedepends onfeedssize
{{ m.name }} {{ m.source }} {{ m.depends }} {{ m.feeds }} {{ m.count }}
layers this turn {{ l.name }} · {{ layerNote }}
{{ layer.name }}
{{ layer.tag }}
{{ layer.module }}
{{ tile.name }}
{{ tile.resource }}
{{ tile.generatedName }}
{{ tile.api }}
next The agent adds a layer here only when a decision creates one — e.g. picking multi-region emits a Failover layer. Layers are data, not layout.
main.tf {{ hclFilesNote }}
{{ providerPin }}
{{ hclText }}
# {{ hclNote }}
# composed from your registry: {{ registryGlob }}
# every value below traces to a decision or a live API lookup

locals {
  name_prefix = "{{ namePrefix }}"   # {{ patternLabel }}
  tags = {
    HelixProjectId = "{{ helixId }}"   # maps this stack back to the Helix project
    Application = "{{ appName }}"
    Environment = "prod"
    CostCenter  = "CC-4471"
    Owner       = "platform-oncall"
    ManagedBy   = "terraform/helm"
    DataClass   = "{{ dataClass }}"
  }
}

module "{{ netName }}" {
  source  = "{{ netSource }}"
  version = "{{ netVersion }}"
  name    = "${local.name_prefix}-net"
  {{ netArg1Key }} = {{ netArg1 }}  # {{ netArg1Note }}
  {{ netArg2Key }} = {{ netArg2 }}
  tags = local.tags
}

module "{{ primaryModuleName }}" {
  source  = "{{ primaryModuleSource }}"
  version = "{{ primaryModuleVersion }}"
  name    = "${local.name_prefix}-{{ primaryAbbr }}"
  {{ primaryKey }} = {{ primaryValue }}
  tags = local.tags
}

module "{{ kmsName }}" {
  source  = "{{ kmsSource }}"
  version = "{{ kmsVersion }}"
  count   = {{ kmsCount }}
  description = "Customer key for {{ appName }} at rest"
  {{ keyArg1Key }} = {{ keyArg1 }}
  {{ keyArg2Key }} = {{ keyArg2 }}
  tags = local.tags
}
{{ hclStatus }}
{{ codeAuthorNote }}
{{ m.k }} {{ m.v }}
{{ p.value }}
{{ p.label }}
Resources
{{ row.sign }} {{ row.addr }} {{ row.why }}
Attribute-level changes
{{ planAttrsNote }}
{{ a.addr }}
{{ a.attr }}
{{ a.before }} {{ a.after }} {{ a.risk }}
{{ n.title }}
{{ n.body }}

What this request will cost

{{ costIntro }}

{{ costUnpricedTitle }}
{{ costUnpricedBody }}
tools/cost.py · reviewer "cost" · unavailable is not a pass
{{ costHeadline }}
{{ costMonthlyNote }}
{{ guardrailNote }} {{ costCap }}
{{ c.label }} ${{ c.amount }}
line itemquantityratemonthly
{{ l.item }} {{ l.qty }} {{ l.unit }} {{ l.amount }}
What drives this bill
{{ dr.label }}
{{ dr.note }}
{{ dr.amount }}
{{ costSavesTitle }} {{ costSavesHead }}
{{ sv.label }}
{{ sv.note }}
{{ sv.amount }}
Who this is billed to
{{ cb.k }} {{ cb.v }}
{{ costFootnote }}
Evaluated against acme/policy@v14 — OPA bundle run with conftest test against the plan JSON, re-run on every emit. Enforcement level decides whether an exception is even possible.
{{ p.title }} {{ p.level }}
{{ p.detail }}
{{ p.rule }}
{{ p.status }}
{{ p.remedy }}
{{ relaxTitle }}
{{ relaxTotal }} available without touching a hard rule
{{ relaxSub }}
{{ r.rule }} {{ r.level }}
{{ r.detail }}
{{ r.saves }} {{ r.verdict }}
How the workaround works
Advisory findings are informational. Soft-mandatory findings accept a time-boxed exception: justification, approver group, expiry, and a linked ticket, all written into the run record and re-checked on expiry. Hard-mandatory findings have no exception path at all — the agent instead offers the smallest plan change that clears them.

Questions people actually ask

If your question is not here, ask it in the thread — the agent answers questions about its own reasoning as readily as it answers questions about infrastructure.

{{ f.q }}
{{ f.a }}

What the agent enforces, and who enforces it

Best practice only means something if something checks it. Every line below names the reviewer, policy rule or tool that would catch it — none of this is advice you have to remember.

{{ g.name }}
{{ i.rule }}
{{ i.by }}
{{ reqLoadingTitle }}

Onboard a project

A Helix project the console does not know yet: say which account it lands in, its environments and its data class, and its service.yaml goes up as a draft pull request under your name. Requests for it can be raised once that is merged.

Project
{{ helixSearchNote }}
{{ h.name }} {{ h.detail }}
{{ onboardProjectLine }}
{{ onboardIntro }}
{{ onboardNote }}
{{ onboardDoneLine }}

Drafts

Requests you started and have not raised. Newest first; open one to carry on, discard what you will not raise. Tick several to discard them together.

{{ d.title }}{{ d.state }}
{{ d.meta }}
{{ d.when }}
{{ draftsNote }}

Architects' queue

Priority, then what is on you, then the last change. Unassigned requests are on every architect until someone takes one.

{{ q.n }}
{{ q.label }}
{{ r.pos }}
{{ r.prio }}{{ r.title }}
{{ r.meta }} {{ r.id }}
{{ r.why }}
{{ r.who }}
{{ queueNote }}
Background work{{ jobsWorkers }}
{{ j.what }} {{ j.state }}
A queued job knows its place in line. Reviewer panels and estate refreshes run here, two at a time.
‹ Home

Describe what you need. The agent writes the Terraform.

Compare AWS architecture optionsAlready have Terraform? Review an existing plan

One or two sentences is enough: what it is, what it is for, anything it must survive. The agent drafts every resource and asks only what it cannot infer.

Helix project
×
{{ p.name }} {{ p.detail }}
{{ projectChosenLine }}
{{ envPickLine }}
{{ ev.label }}
{{ onboardLinkLabel }}
{{ currentTitle }}
{{ cc.id }} {{ cc.kind }} {{ cc.name }} Remove
{{ currentNote }}
Asked first: it decides tagging, backend and who approves. Type a name; every Helix project autocompletes.
Priority
{{ composePriorityNote }}
⌘K
{{ c.label }}
{{ composeNote }}
{{ diagramNote }}
{{ diagramDropTitle }}
{{ diagramDropNote }}
{{ m.shape }} {{ m.module }} {{ m.confidence }}
{{ diagramUnresolved }}
{{ diagramComposeNote }}
2 shapes unresolved — the agent asks about them below instead of guessing.
try
Start from a pattern
{{ blueprintsCommon }}
{{ grp.label }}
{{ b.title }}
{{ b.summary }}
{{ pt.label }}
{{ f.text }}
1 · drafts
Every resource from the reviewed catalogue, named to the standard, tagged, priced. Usually under a minute.
2 · asks
Only what changes the shape of the plan. Two or three questions is normal.
3 · reviews
Automated reviewers, then an architect. Nothing applies without a named human.

{{ greeting }}

{{ glanceHeadlineLive }}

{{ laneTitle }} {{ approvalsSub }}
{{ a.headline }} high
{{ a.metaLine }}
{{ a.stage }} {{ a.ageShort }}
{{ approvalsAll }}
{{ allLink }}
Background work
{{ j.what }} {{ j.state }}
Nothing running. Reviewer panels and estate refreshes queue here.
This week
{{ g.value }}
{{ g.label }}
{{ spendNote }}

Requests

Every request, newest change first within its priority. A chip carries the count it will show; press it again to clear it.

{{ bulkSelectLabel }} {{ bulkCount }} clear
{{ g.label }}
{{ q.n }}
{{ q.label }}
{{ r.title }} {{ r.priority }} {{ r.marker }}
{{ r.metaLine }} {{ r.helix }}
{{ r.stage }} {{ r.when }} Withdraw Delete
{{ reqPageLabel }}{{ reqNote }} {{ reqPageOf }}
{{ deliveryTitle }}

{{ deliveryDetail }}

{{ deliveryChecked }}
{{ railRequest }}
{{ devHeadline }}
{{ devSub }}
Access · how each thing is reached
{{ accessPlacedLine }}
{{ ac.component }} {{ ac.kind }} {{ ac.how }}
Dry run · in the local emulator, never an account
{{ dryRunLine }}
{{ dr.text }}
{{ p.label }}
{{ p.badge }}
{{ p.at }}
{{ p.label }} {{ p.badge }} {{ p.actor }}
{{ p.detail }} {{ p.linkLabel }}
{{ g.name }}

Estate

Every taggable resource each account holds, from a read-only scout. Governed means it carries all the mandatory tags; eligible for preflight means the catalogue has a matching module. Tags do not establish Terraform ownership, so adoption stays held until account, region, repository and state ownership are verified.

{{ estateRefreshLine }}
{{ estateError }}
{{ a.label }} {{ a.id }}
{{ a.headline }}
{{ a.line }}
{{ a.managed }}
spend, last 30 days
{{ a.spend }}
{{ a.spendLine }}
Accounts and access {{ acctAccessLine }} {{ acctAccessHint }}
{{ acctAccessIdentity }}
{{ ar.label }} {{ ar.id }} {{ ar.state }} {{ ar.facts }}
{{ ar.next }}
alias (lower-case, hyphens; it names state paths)
{{ ar.handoutNote }}
{{ ar.handoutText }}
{{ estateLayoutLine }}
Layout · drawn from the scan · {{ estateLayoutRegion }}
{{ estateDiagramNote }}
Networks
{{ v.name }} {{ v.cidr }} · {{ v.id }}
{{ v.line }}
{{ v.holds }}
Load balancers and DNS
{{ lb.name }} {{ lb.scheme }}
{{ lb.line }}
{{ z.name }} · {{ z.records }} records{{ z.priv }}
{{ z.line }}
Compute · ECS clusters
{{ c.name }} {{ c.line }}
{{ c.services }}
Registries: {{ estateRegistries }}
Data stores and certificates
{{ d.id }} {{ d.line }}
Certificates: {{ estateCerts }}
{{ estateGaps }}
{{ estateGroupsLine }}
{{ sk.label }} {{ sk.line }} {{ sk.hint }}
drawing…
Members
{{ kg.kind }} · {{ kg.names }}
How they are linked
{{ lk.line }}
Cross-environment links {{ estateCrossLine }} stacks above keep environments apart; these are the links that cross
{{ cx.line }}
Linked to nothing the scan can see: {{ estateAlone }}
{{ gk.label }} {{ gk.count }} {{ gk.kinds }}
{{ gr.name }} {{ gr.kind }} {{ gr.env }} {{ gr.managed }}
{{ gk.more }}
{{ estateCount }}
resourceservicemanaged bytags
{{ r.name }}
{{ r.type }} · {{ r.region }}
{{ r.service }} {{ r.managed }} {{ r.tagsLine }}

Settings

Three scopes. Org-locked settings are the platform's contract and no project can override them. Project settings are yours to set. Inherited values come from the Helix project record and change there, not here.

architect & admin only
{{ g.name }}
{{ r.label }} {{ r.scopeLabel }}
{{ r.value }}
{{ r.note }}
Every change here is versioned and shows up in History as a settings event. Changing a project setting re-emits the surface and re-runs the reviewers, so you see the consequence before you sign anything off.

Where the numbers come from

The model does not invent CIDRs, RTOs, node counts or control mappings. Each is a deterministic tool call, and every figure in this surface is traceable to one — which is what makes a reviewer able to argue with it.

toolreturnsappears incalled
{{ t.name }} {{ t.returns }} {{ t.feeds }} {{ t.called }}

{{ destroyTitle }}

{{ destroySub }}

{{ s.value }}
{{ s.label }}
{{ destroyListLabel }}
{{ r.sign }}
{{ r.addr }}
{{ r.effect }}
{{ r.guard }}
{{ r.risk }}
What forces a replacement
Changing {{ forceRepl }} forces destroy-then-create. The agent flags these before you answer a decision, not after the plan.
Standing protections
{{ standingProtect }}
The agent has no destroy permission
Its execution role carries an explicit deny on every Delete* action, and the runner rejects any plan containing a destroy. A teardown is a human's job, run from the CLI with this analysis as the runbook.

Reviews

{{ reviewHeadline }}

{{ saWhy }}
Exceptions
{{ x.title }} {{ x.status }}
{{ x.line }}
Needs a look {{ reviewLookCount }}
{{ r.verdict }}
{{ r.name }} · {{ r.summary }}
{{ r.detail }}
{{ r.nextStep }}
{{ r.where }}
Cleared {{ reviewClearedCount }}{{ reviewPlanLine }}
{{ c.name }} {{ c.note }} {{ c.by }}
{{ a.step }} · {{ a.short }}
{{ a.headline }}
{{ a.detail }}
{{ a.comment }}

History

Every event on this request, newest first, read from the record and the live repo.

{{ h.when }} {{ h.title }} {{ h.kind }}
{{ h.detail }}
{{ h.actor }} {{ h.diff }}

What the front end has to fetch

Every dynamic value in this surface, the call behind it, and how long it may be cached. Nothing on this page is hard-coded except copy.

fieldbackend endpointupstreamcache
{{ a.field }} {{ a.endpoint }} {{ a.upstream }} {{ a.cache }}
Streaming channel
POST /v1/sessions/:id/messages → SSE of surface patches. The client applies JSON-Patch ops to the current surface, so a decision answer never reloads the page.
Auth & blast radius
Read calls use a scoped read-only role per account (sts:AssumeRole, 15 min). Apply runs server-side in a job runner — the browser never holds AWS credentials.

The agent emits this, not HTML

An A2UI-style component tree over the agent-to-UI channel. The client owns a renderer registry — the model may only pick from registered component types and bind them to data paths, so it can restructure the page (add a layer, add a decision) without shipping markup.

surface.v{{ surfaceVersion }}.json a2ui/0.3 · streaming
{
  "surface": "terraform.build",
  "version": {{ surfaceVersion }},
  "layers": {{ specLayers }},
  "nodes": [
    { "type": "DecisionCard",
      "id": "{{ specDecisionId }}",
      "bind": "$.decisions.{{ specDecisionId }}",
      "emits": "answer" },
    { "type": "LayerStack",
      "bind": "$.blueprint.layers[*]",
      "child": "ResourceTile",
      "source": "aws.live" },
    { "type": "PolicyList",
      "bind": "$.policy.findings[*]",
      "actions": ["fix", "request_exception"] },
    { "type": "ApplyGate",
      "bind": "$.readiness",
      "blocked_by": {{ specBlockers }} }
  ]
}
Registry, not free-form
11 component types: DecisionCard, ChipSet, LayerStack, ResourceTile, DiffList, CostBars, PolicyList, ApplyGate, CodePane, NamingPicker, AssumptionList. Unknown type → the client drops the node and logs it.
Why it survives
Layout stays with the design system; the model only chooses which registered pieces appear and what they bind to. New infra pattern → new layer data, no front-end release.
Guardrail
The apply gate is client-enforced and re-checked server-side. A model-emitted spec can never mark itself ready.
Walkthrough · {{ wtIndex }} of {{ wtTotal }}
{{ wtStepData.title }}
{{ wtStepData.body }}
{{ dlgTitle }}
{{ dlgBody }}
Value
{{ p.name }} {{ p.email }}
Reason
{{ dlgError }}