Infrastructure targets
This page explains what an Infrastructure Target is in Proofarc, how it differs from the older Environment Target concept, and how the platform uses each.
If you've been running security scans (nmap, nuclei, trivy, ZAP) and want to understand the model behind the target you pick at scan time, this is the page for you.
The short version
Three things you need to know:
- An Infrastructure Target is a host, network, cluster, or registry — a real-world asset you scan. Top-level. Independent of any Project or Environment.
- An Environment Target is the way that asset is used inside an environment. Same host in dev/staging/prod = three environment targets, but they can all point at one Infrastructure Target.
- Findings are deduplicated at the Infrastructure level. When you scan a host across multiple environments, you don't end up with three disconnected sets of findings — they roll up to the canonical asset.
The model
┌──────────────────────────────────────────────────────────────┐
│ InfrastructureTarget (top-level, canonical) │
│ name "auth-host-prod" │
│ kind vm_host │
│ metadata {"ip":"10.0.1.5","hostname":"auth", │
│ "port":443} │
│ is_active true │
└──────────────────────────────────────────────────────────────┘
▲
│ infrastructure_target_id (FK, nullable)
│
┌───────────────┴────────────────┐
│ EnvironmentTarget │
│ name "auth dev" │
│ target_type HOSTNAME │
│ host / ip auth.dev │
│ environment_id 1 (dev) │
└────────────────────────────────┘
One Infrastructure Target, N Environment Targets referencing it. The
Environment Target can also stand alone (when it's a REST_SERVICE,
WEB_APP, MOBILE_APP, or DATABASE — those aren't infra).
The four kinds
| Kind | What it represents | Required metadata | Used by |
|---|---|---|---|
vm_host | A single server, VM, or any host you can ssh / scan / curl | ip and/or hostname, optional port | nmap, nikto, nuclei, ZAP |
ip_range | A subnet — 192.168.1.0/24 — for nmap-style sweeps | cidr | nmap discovery, network mappers |
k8s_cluster | A Kubernetes API endpoint | api_url, optional credential_tag | trivy-k8s, kube-bench |
container_registry | A Docker Hub / ECR / GCR / Harbor instance | registry_url, optional credential_tag, optional registry_type | trivy image scans |
The kind controls which metadata fields are required at save time and
which scanners can target this row.
What you see in the UI
Sidebar → Infrastructure → Targets (/infrastructure/targets)
The Infrastructure Target registry. Each row is one canonical asset, showing kind, status, and a count of Environment Targets that link to it. The New Target button opens the wizard.
Environment Target detail (/environments/{id}/targets/{tid})
If the Environment Target points at infrastructure (target_type
IP_ADDRESS, HOSTNAME, IP_RANGE, or NETWORK), there's a
Linked Infrastructure Target chip linking back to its canonical
row.
Scan templates that need infra
Scan templates with target_kind set (nmap, trivy-k8s, etc.) only
accept Infrastructure Targets of matching kinds. You'll see a filtered
dropdown when you set the target at scan time.
How existing data got here
When the Infrastructure Target registry shipped, the platform walked
every existing Environment Target of type IP_ADDRESS, HOSTNAME,
IP_RANGE, or NETWORK and either:
- Created a new Infrastructure Target with the same name, populating
kindandmetadatafrom the Environment Target's columns, or - Reused an existing one with the same name (idempotent — re-running doesn't create duplicates).
Then set environment_targets.infrastructure_target_id to point at it.
The original Environment Target row stays untouched; both rows coexist.
Application-shaped Environment Targets (REST_SERVICE, WEB_APP,
MOBILE_APP) and DATABASE rows were not migrated — they aren't
infrastructure, they're applications and data services.
Working with the API
List infrastructure targets
curl -s -H "Authorization: Bearer $TOKEN" \
"$URL/api/infrastructure/targets" | jq
Get the schema for each kind
curl -s -H "Authorization: Bearer $TOKEN" \
"$URL/api/infrastructure/kinds" | jq
{
"vm_host": {
"code": "vm_host",
"description": "Single server / VM",
"requiredFields": ["ip"],
"optionalFields": ["hostname", "port"]
},
"ip_range": {
"code": "ip_range",
"description": "CIDR range for network sweeps",
"requiredFields": ["cidr"],
"optionalFields": []
},
"k8s_cluster": {
"code": "k8s_cluster",
"description": "Kubernetes API endpoint",
"requiredFields": ["api_url"],
"optionalFields": ["credential_tag"]
},
"container_registry": {
"code": "container_registry",
"description": "Docker registry endpoint",
"requiredFields": ["registry_url"],
"optionalFields": ["credential_tag", "registry_type"]
}
}
Create a host
curl -s -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "auth-host-prod",
"kind": "vm_host",
"metadata": {"ip":"10.0.1.5","hostname":"auth","port":443}
}' "$URL/api/infrastructure/targets"
Create a subnet
curl -s -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "dmz-subnet",
"kind": "ip_range",
"metadata": {"cidr": "10.0.0.0/24"}
}' "$URL/api/infrastructure/targets"
Validation
The platform enforces the per-kind metadata schema. Trying to create an
ip_range without cidr, or a vm_host with neither ip nor
hostname, returns 400 with an inline error.
Why this matters for scanners
Today scanners reference Environment Targets. Once Phase E lands, they will follow the FK and dispatch against Infrastructure Targets instead. The user-visible effect:
- One canonical set of findings per asset, instead of one per environment.
- A K8s cluster scanned with trivy-k8s is named by its cluster row, not by which environment happened to be selected.
- Container image scans target a Container Registry row directly.
Existing scans continue to work unchanged in the meantime — the Environment Target → Infrastructure Target link is purely additive.
Common questions
Q: Can the same Infrastructure Target be linked from multiple
Environment Targets?
Yes. That's the point. One auth-host-prod row, with dev/staging/prod
Environment Targets all pointing at it.
Q: What if I rename a host? Rename the Infrastructure Target. Every Environment Target that links to it keeps working, because they reference it by FK id, not by name.
Q: Can I delete an Infrastructure Target?
Yes, soft-delete (sets is_active = false). Environment Targets that
reference it stay alive — their FK becomes a soft reference. To clean
up properly, unlink the Environment Targets first or reassign them.
Q: What kind do I pick for a load balancer?
vm_host — a load balancer IP plus port is just a host from the
scanner's perspective. If you want to scan all backends behind the LB,
that's an ip_range (CIDR) or multiple vm_host rows.
Related reading
- Scan templates guide — pairing a scanner with the right kind of target
- How tests map to applications — the same separation-of-concerns pattern, applied to test artifacts
- Projects & Environments — what an Environment Target is in the broader model