An AI agent on a self-hosted model investigates security findings on a target service it cannot reach directly, and proposes fixes that are tested before review. No change reaches the target service without human approval of that exact change.
Tools establish facts. The agent proposes changes. Neither approves anything.
Detailed breakdown in Architecture.
The names in the diagram
| Term | What it is |
|---|---|
| Orchestrator | The trusted VM. It runs K3s and the workflow, and holds the secrets |
| AI worker | The VM that runs the model and the agent. The design assumes it can be compromised |
| Edge VM | The egress proxy: a NAT gateway and a Squid forward proxy, the platform's only outbound path to the internet |
| Router | The mesh VPN's subnet router, and the only mesh node in Azure. The operator reaches every other VM through it |
| Fetch window | A time-limited allowlist: an operator command on the edge VM that opens code and package hosts for at most 60 minutes |
| Milestone | 4, NetBox target and exercise 1 investigation, started on 2026-10-24 (roadmap) |
| Works since 2026-10-21 | A workflow hands a task to the agent on the worker. The agent calls the tool service with a token only that job holds, and the checked result comes back |
| Not yet | No target, so no finding has been investigated |
| Concern | Design answer | Decided in |
|---|---|---|
| Evidence reaching a model vendor | Open-weight model on CPU, inside the platform. No external model API | Architecture |
| The agent compromised by what it reads | Own VM, no cluster credentials. Reaches only the tool service and two package hosts | 0005, 0011 |
| Root on the host with the secrets | No direct internet. The only way out is the edge VM's proxy | 0020 |
| The mesh VPN vendor | Its only node is a separate router VM. SSH certificates expire within 12 hours | 0025, 0022 |
| A compromised target | Reaches nothing outside its own network. Own repository, state, and identity | 0002, 0024 |
| Exposure to the internet | No VM has a public address. An Azure policy refuses one | 0009 |
Three regions, because each allows only 4 vCPUs (0003, 0020).
- The model is wrong. The fix clears the finding and breaks the service.
- The evidence lies. A compromised target or an advisory carries false data or instructions.
- A job dies halfway. The change is half applied, or applied twice.
So the agent gets narrow tools and no cloud, repository, or delivery credentials, every proposal is rehearsed against the service's real consumer before review, and each result is compared with the same run without the agent.
Three exercises test this on a NetBox target (ADR 0004): excess inventory access, excess network access, and a vulnerable image or dependency.
| Topic | Page |
|---|---|
| Status and milestones | Roadmap |
| How it is built | Architecture, components |
| Why each choice | Decision records |
| The check behind each claim | Validation |
| Commands and output per milestone | Worklogs |
| Threats and controls | Security |
| Rebuild steps | Runbooks |
| Failures and causes | Troubleshooting |
| Known defects | Open issues |
| The code | Repository layout |
Show the directories
| Path | What it holds | Described in |
|---|---|---|
infra/bootstrap/ |
Terraform for what the other roots depend on: resource groups, state storage, the two deployment identities, and the public address policy. Also the flow logs and the Activity Log alert rules, which neither deployment identity can reach. Applied by the operator | Identities and state |
infra/platform/ |
Terraform for the platform side: networks, security group rules, the NAT gateway, all four VMs, their first-boot templates, and the Log Analytics workspace with its private endpoint and alert rules | Egress and policy, orchestrator, AI worker |
ai-security-platform-test |
A separate repository. Terraform for the target network, and NetBox and its consumer | ADR 0002, ADR 0024 |
infra/modules/network/ |
The network module both sides use | Architecture |
platform/logs/ |
Host units and scripts on the orchestrator that copy the tool service's audit lines and pull the AI worker's journal into append-only files, and their tests | Audit log |
platform/monitoring/ |
The script that writes the monitoring private endpoint's addresses into the hosts file of each VM with a log agent | Monitoring |
platform/tool-service/ |
Kubernetes manifests and scripts that deploy the tool service to K3s: namespaces, network policies, the pod, the certificate, and job registration | Tool service, checks |
edge/ |
The fetch window command and the list of hosts Squid opens during a window, both installed on the edge VM | Package proxy |
worker/model-server/ |
Pins, download, build, and install scripts for the model server on the AI worker | Runbook |
worker/log-export/ |
The forced command on the AI worker that hands its journal to the orchestrator's pull, and its install script | Audit log |
worker/feed/ |
The forced command of the feed account on the AI worker, which only unpacks staged files, and its install script | ADR 0026 |
worker/agent-runtime/ |
Pins, install, and run scripts for the agent runtime, and the fixture tool | Runbook |
tool-service/ |
The tool service: its code, tests, and Dockerfile | Tool service |
policies/ |
The tool service policy: the tools and arguments each job may use | Tool service |
validation/ |
The script that runs every check against the live system | Validation |
.github/ |
The checks on every pull request, the image build, and the signature check with its allowed signers | Pipeline |
docs/ |
Everything in Find your way around |
- Not production: one node, one operator, no high availability.
- Not real data: the target's inventory is synthetic.
- Not protection from Azure: the provider and the platform administrator are trusted (trust boundaries).
MIT.