-
Notifications
You must be signed in to change notification settings - Fork 0
HLD
Status: v1.0 · Last updated: 2026-07-23
Diagram: diagrams/nwaf-agent-hld.png (v2.3) · Decisions: adr/0001…0009
Governance: governance.md · Value: business-case.md
This is the narrative companion to the HLD diagram. It walks the components, the nightly execution sequence, the data flows and their classification, and the trust boundaries. It records what the architecture is; the ADRs record why each choice was made, and this document links to them rather than re-arguing.
The NWAF Agent is an autonomous nightly pipeline that reviews a single workload's Terraform (declared state) and live AWS account (actual state) against the AWS Well-Architected Framework, classifies drift, updates a Well-Architected Tool posture, and reports risk to the team.
Scope: single workload / account per run; the cross-account trust model (§6) makes fleet operation a matter of running the pipeline per target.
Guiding principle: deterministic where determinism exists; the LLM only for the reasoning nothing else can do. Detection is done by mature scanners; an LLM reconciles their output. Orchestration is code, not a model (ADR-0003).
A code-orchestrated pipeline (buildspec) runs five phases inside one ephemeral
compute boundary:
trigger → ① scan (live + code) → ② reconcile (LLM) → ③ score / write-back → ④ deliver
Everything between the phases is deterministic code; only phase ② uses the LLM, and it consumes structured findings, not raw configuration.
| Component | Responsibility | Tech | ADR |
|---|---|---|---|
| Trigger | Nightly kick-off, no orchestrator | EventBridge Scheduler → CodeBuild StartBuild (direct target) |
0001 |
| Runtime | Ephemeral pipeline host | AWS CodeBuild project; image pins scanners + terraform CLI + Strands SDK | 0001, 0004 |
| ① Live scanners | Read-only detection of actual state | Prowler · Steampipe/Powerpipe (WA/Thrifty) · Compute Optimizer | 0004 |
| ① Code scanners | Detection of declared state | Checkov (SARIF) · Infracost · (+ tfstate) | 0004 |
| Finding normaliser | Uniform record across tools | canonical {pillar, resource, check_id, source, origin, severity, evidence}
|
0004 |
| ② Reconcile | Correlate live vs code, classify drift, map to pillars, prioritise, draft remediation, answer judgment items | Claude via provider layer (Bedrock default), bounded structured output | 0002, 0003 |
| WA reference | Supply WA criteria to Reconcile | bundled, versioned local index — no vector DB | 0005 |
| ③ Scorer / write-back | Update WA posture, extract risk counts, generate report |
UpdateAnswer + CreateMilestone to a dedicated automation workload
|
0006 |
| Delivery | Fan-out notification + tracked work | S3 (full report) + SNS → Slack / Jira / Email adapters | 0009 |
Inputs: Git repo (Terraform HCL, the CodeBuild source), S3 (tfstate), the live AWS environment (read-only). Outputs: the dedicated WA-Tool workload, S3 logs/report, and the delivery channels.
-
Trigger. At 23:00 the EventBridge Scheduler invokes
StartBuilddirectly — no orchestrator Lambda (ADR-0001). - Hydrate. CodeBuild natively clones the Git source (HCL); the code-scan stage pulls tfstate from S3. Live scanning assumes the cross-account read-only role (ADR-0007).
- ① Scan (deterministic). Live scanners assess the running account; code scanners assess the HCL/tfstate. Each emits structured findings, normalised to the canonical schema.
-
② Reconcile (bounded LLM). Claude consumes the findings (not raw config),
queries the bundled WA reference for criteria, and:
- classifies drift — code + matching live → code-origin HRI; live-only → out-of-band drift; code-only → not-yet-applied;
- maps findings to WA pillars/questions, dedupes, prioritises, drafts remediation, and reasons over the judgment items no scanner covers (labelled as judgment, per ADR-0004).
-
③ Score / write-back. The Scorer writes scanner-backed answers to the
dedicated automation workload, records a milestone, and reads back
AWS's authoritative HRI/MRI counts. Judgment items are proposed, not written
(ADR-0006). If
advisory_prmode is on, a draft PR is opened for scanner-backed fixes only — never merged (ADR-0008). - ④ Deliver. The full report goes to S3; a thin summary + link is published to SNS; adapters route to Slack (digest), Jira (idempotent tickets), and Email (ADR-0009). The container terminates.
| Data | Classification | Handling |
|---|---|---|
| Terraform HCL (Git) | internal | cloned as build source; read-only |
| tfstate (S3) | sensitive (may contain secrets) | read via a scoped role + one KMS key; encrypted at rest (0007) |
| Live account config | sensitive (topology, security posture) | control-plane reads only — explicit data-plane deny; values never read (0007) |
| Scanner findings | internal | normalised; the only thing the LLM sees |
| LLM prompts/responses | contains sensitive context | in-region Bedrock, not used for training, PrivateLink (0002) |
| Report / logs | internal | S3 (encrypted); thin link over SNS |
The LLM never receives raw secrets or object/secret values — it reasons over config-level findings. Sensitive data stays in-region and inside the AWS trust boundary end-to-end.
- Two accounts (general case): a tooling account runs the pipeline; each target workload lives in its own account. Single-account is the degenerate case.
-
Cross-account access is via
sts:AssumeRoleinto a read-only role deployed in each target account (trust = tooling role + ExternalId), so the agent is structurally incapable of writing to the reviewed account. - Per-stage roles: the CodeBuild base role is near-empty; each phase assumes only its scope (scan = read-only; reconcile = Bedrock invoke only; scorer = WA-write to the automation workload + SNS + S3). A prompt-injection in Reconcile cannot reach scan or write credentials — they never exist in that stage.
- No long-lived credentials anywhere; all STS-temporary, capped to the build.
See ADR-0007 for the full identity model; governance.md §7 for the AI-security
controls this enables.
Detailed in governance.md; summarised here:
- Reproducibility — deterministic pipeline + versioned reference; same inputs → same review (0003, 0005).
- Least-privilege — read-anything / write-nothing in the target; single write scope, single stage (0006, 0007).
- Responsible AI — deterministic detection; judgment labelled; no invented findings; no fabricated "fixed" status; human disposition (0004, 0008).
- Portability — provider layer keeps the LLM swappable on a per-run flag (0002).
- Cost — pay-per-run compute, no always-on tier (0001, 0005).
- The target account owner deploys the read-only role (via StackSet/IaC).
- The dedicated WA-Tool automation workload is provisioned out-of-band (IaC), so the agent's runtime write scope stays minimal (ADR-0006).
- Live scanning is control-plane only — no VPC / data-plane access required (ADR-0001/0007).
- Scanners cover Security/Cost and a config subset of Reliability/Ops; Performance Efficiency, process-side Operational Excellence, and Sustainability are judgment-assessed and labelled as such (ADR-0004).
- Runtime ≤ CodeBuild's ceiling; the nightly cadence is non-urgent by design.
-
Run-failure alerting is not yet designed — a failed nightly run is
currently silent until the next success (tracked in
governance.md§4; candidate follow-on ADR). - Runtime provider fallback (surviving a mid-run provider/region outage) is deferred (ADR-0002 revisit).
- The agent does not remediate the environment (structural, ADR-0007);
advisory_pronly proposes code changes for humans to merge.
- Decision records:
adr/0001…adr/0009 - Governance:
governance.md· Business case:business-case.md - Diagram:
diagrams/nwaf-agent-hld.png/.drawio(v2.3)
NWAF Agent repository · generated from docs/ — do not edit wiki pages directly.
Design
Decision Records