Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
eea587a
NAIS-0: resolve a platform agent into a runnable Iron Swarm victim
koralchapnik Aug 27, 2026
fb0f59d
NAIS-0: accept only registered agents as war-game targets
koralchapnik Aug 27, 2026
b02c261
NAIS-0: regenerate the iron-swarm OpenAPI spec
koralchapnik Aug 27, 2026
f782acb
NAIS-0: adopt guardrails as Relay plugin config, not workflow YAML
koralchapnik Aug 27, 2026
97a8147
NAIS-0: war-game the Dockerfile an agent shipped, when it has one
koralchapnik Aug 29, 2026
ad951ce
NAIS-0: read guardrails from the components array
koralchapnik Aug 29, 2026
1f09213
NAIS-0: refuse harnesses whose tool calls cannot be guarded
koralchapnik Aug 30, 2026
46389f8
NAIS-0: stage the whole registered bundle into the build context
koralchapnik Aug 30, 2026
c1d5000
NAIS-0: carry the agent's declared environment into the manifest
koralchapnik Aug 31, 2026
86b30a8
NAIS-0: allow-list the hosts the agent's own config names
koralchapnik Aug 31, 2026
999bf70
NAIS-0: keep sandbox and host paths apart, and normalize MCP transports
koralchapnik Aug 31, 2026
ee126e4
NAIS-0: accept a project bundle as a war-game target again
koralchapnik Sep 1, 2026
1b48b77
NAIS-0: give init a bring-your-own path
koralchapnik Sep 1, 2026
3cb540e
NAIS-0: offer both war-game sources in the create form
koralchapnik Sep 1, 2026
224f701
NAIS-0: format a test the formatter had not reached
koralchapnik Sep 1, 2026
10c2a3c
NAIS-0: cover every Iron Swarm screen in the browser
koralchapnik Sep 1, 2026
c4d4fe9
NAIS-0: rewrite the iron-swarm skill to spec as nemo-iron-swarm
koralchapnik Sep 3, 2026
8ee0f32
NAIS-0: pre-fill discovered egress when inspecting a registered agent
koralchapnik Sep 3, 2026
8ca74c1
NAIS-0: give every swarm-graph lane assertion the same timeout
koralchapnik Sep 3, 2026
6d2db8d
NAIS-0: add example victims for every guardable harness
koralchapnik Sep 3, 2026
2af2512
NAIS-0: make the war-game target an explicit question, never a default
koralchapnik Sep 3, 2026
bdcddcf
NAIS-0: fix the victim examples against a real war-game run
koralchapnik Sep 3, 2026
6e403ca
NAIS-0: record the Studio base-url footgun that fails every plugin sc…
koralchapnik Sep 3, 2026
15ea700
NAIS-0: say why the Harden tab is missing instead of skipping silently
koralchapnik Sep 3, 2026
b446a20
NAIS-0: enable the Hermes Relay plugin as the user that runs the victim
koralchapnik Sep 4, 2026
979e21b
NAIS-0: stop selecting defenders in Studio from weakening the run
koralchapnik Sep 4, 2026
a891052
NAIS-0: stop the sanity-check report clipping the Harden tab
koralchapnik Sep 4, 2026
d4393cc
NAIS-0: refuse mitigation adoption for bring-your-own manifests
koralchapnik Sep 4, 2026
85a289f
rename Iron Swarm to Agent Hardener
koralchapnik Sep 6, 2026
7582b99
docs: split Secure Agents and add gated Agent Governance section
yamini Aug 27, 2026
1ba9506
docs: pin Agent Governance slugs and regroup the Agents sidebar
yamini Aug 27, 2026
569dd4d
docs: correct the Agent Governance pages against the code
koralchapnik Sep 6, 2026
db3faeb
fix stale text in the agent-hardener CLI and project inspector
koralchapnik Sep 6, 2026
d8f2f54
chore: add the missing SPDX headers to the example victims
koralchapnik Sep 6, 2026
1bc87e1
style: format the renamed Studio e2e test
koralchapnik Sep 6, 2026
460e63d
test: assert the whole egress list instead of host membership
koralchapnik Sep 6, 2026
2f740c4
point the default spec at nvidia-agent-hardener 0.0.10
koralchapnik Sep 6, 2026
fad6a60
raise the agent-hardener floor to 0.0.11
koralchapnik Sep 6, 2026
fd06a03
docs: separate credential rotation from incident investigation
koralchapnik Sep 7, 2026
86ebee2
fix: fail closed when a project run's manifest is missing
koralchapnik Sep 7, 2026
5ba891e
test: cover init --project-dir manifest creation
koralchapnik Sep 7, 2026
5ae228a
fix: populate derived env vars in the new-manifest form
koralchapnik Sep 7, 2026
c781295
fix: ignore obsolete agent/project selections in manifest form
koralchapnik Sep 7, 2026
608845c
fix: require https:// for the agent-hardener package index
koralchapnik Sep 8, 2026
a933283
fix: emit host[:port] egress entries instead of full URLs
koralchapnik Sep 8, 2026
966fb22
fix: reject a dockerfile path that escapes the uploaded bundle
koralchapnik Sep 9, 2026
9ec15cc
fix: bound python_executor and cap tool output in the other-victim ex…
koralchapnik Sep 9, 2026
03223fe
fix: keep Apply to Agent disabled until the manifest query succeeds
koralchapnik Sep 9, 2026
837f24f
fix: invalidate the selection guard when a project file is removed
koralchapnik Sep 9, 2026
6ef4aea
fix: never send the model credential to a plain-HTTP endpoint
koralchapnik Sep 9, 2026
0584122
fix: keep the dockerfile path unresolved outside the containment check
koralchapnik Sep 9, 2026
2731a8c
fix: allow Apply for a run with no manifest behind it
koralchapnik Sep 9, 2026
c9c3bb4
fix: say why the credential was withheld instead of reporting bad auth
koralchapnik Sep 9, 2026
112d88c
fix: catch the bash timeout so it is not scored as a guardrail refusal
koralchapnik Sep 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,10 @@ docs/helm/index.mdx
.act-variables
.act-secrets

# iron-swarm CLI run artifacts (builds, policies, run-logs) written into the working dir
# agent-hardener CLI run artifacts (builds, policies, run-logs) written into the working dir
.agent-hardener/
agent-hardener.yaml
# Pre-rename run artifacts still on developer machines
.iron-swarm/
iron-swarm.yaml
# Playwright MCP session output
Expand Down
2 changes: 1 addition & 1 deletion docker/base/Dockerfile.nmp-studio-ui
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ COPY plugins/nemo-agents/openapi /app/plugins/nemo-agents/openapi
COPY plugins/nemo-safe-synthesizer/openapi /app/plugins/nemo-safe-synthesizer/openapi
COPY plugins/nemo-evaluator/openapi /app/plugins/nemo-evaluator/openapi
COPY plugins/nemo-customizer/openapi /app/plugins/nemo-customizer/openapi
COPY plugins/nemo-iron-swarm/openapi /app/plugins/nemo-iron-swarm/openapi
COPY plugins/nemo-agent-hardener/openapi /app/plugins/nemo-agent-hardener/openapi
COPY plugins/nemo-insights/openapi /app/plugins/nemo-insights/openapi

# Install pnpm modules and generate SDK artifacts during postinstall.
Expand Down
161 changes: 161 additions & 0 deletions docs/agents/add-guardrails.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
---
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

title: "Add Guardrails to an Agent"
description: ""
---
<a id="agents-add-guardrails"></a>

Guardrails attach to an agent through a **guarded virtual model**: a VirtualModel
entity that uses a guardrail configuration to run input and output rails on every
call to the main model. Pointing the agent at the guarded VirtualModel secures the
agent's model path without changing its workflow logic.

There are two steps: create the guarded VirtualModel, then update the agent's
`llms` block to reference it.

<Note>

Routing model traffic through rails covers the agent's model path. It does not
cover tool misuse or instructions injected through tool output. To test an agent
against those and generate fixes, see Agent Governance.

</Note>

## Prerequisites

Before adding guardrails, make sure you have:

1. Local services running (`nemo services run`).
1. At least one deployed platform-managed agent.
1. A model provider and model entities registered in the workspace.
1. A guardrail configuration. See
[Guardrail Models](/documentation/guardrail-models).

Common catalog models to use as the guardrail backend (verify availability with
`nemo models list`):

- `nvidia-llama-3-1-nemoguard-8b-content-safety`
- `nvidia-llama-3-1-nemoguard-8b-topic-control`
- `nvidia-llama-3-1-nemotron-safety-guard-8b-v3`

## 1. Create a Guarded VirtualModel

<Tabs>

<Tab title="CLI">

```bash
nemo inference virtual-models create guarded-agent-model \
--workspace default \
--models '[{"model":"default/<main-model-entity>","backend_format":"OPENAI_CHAT"}]' \
--request-middleware '[{
"name":"nemo-guardrails",
"config_type":"guardrail_config",
"config_id":"default/<guardrail-config>"
}]' \
--response-middleware '[{
"name":"nemo-guardrails",
"config_type":"guardrail_config",
"config_id":"default/<guardrail-config>"
}]'
```

Wire the same `<guardrail-config>` on both `--request-middleware` (for input
rails) and `--response-middleware` (for output rails). Omit a side if the
config defines no flows for it. For the full middleware schema, entity-backed
versus inline configs, and caching behavior, refer to
[Guardrails Architecture](/documentation/guardrail-models/core-concepts/architecture).

</Tab>
<Tab title="Skill">

Ask your coding agent:

> Check guardrail coverage on my deployed agent.

The `agents-secure` skill lists deployed agents, inspects each LLM's
`model_name`, and suggests creating a guarded virtual model where one is
missing. Verify the skill is installed:

```bash
nemo skills show agents-secure
```

What it does under the hood:

- Lists deployed agents and prompts you to choose one.
- Inspects each LLM's `model_name`. If it does not reference a guarded
virtual model (one with a content-safety, topic-control, or safety-guard
backend), suggests creating one.
- Names the recommended guardrails catalog model and walks you through
creating the guarded virtual model.
- Persists suggestions to the `nemo-agent-security` fileset.

The skill reports whether a guardrail is present. It does not test whether
that guardrail stops a given attack.

</Tab>
<Tab title="Python SDK">

```python
import os
from nemo_platform import NeMoPlatform

client = NeMoPlatform(
base_url=os.environ.get("NMP_BASE_URL", "http://localhost:8080"),
workspace="default",
)

guardrail_mw = {
"name": "nemo-guardrails",
"config_type": "guardrail_config",
"config_id": "default/<guardrail-config>",
}

client.inference.virtual_models.create(
name="guarded-agent-model",
workspace="default",
models=[{"model": "default/<main-model-entity>", "backend_format": "OPENAI_CHAT"}],
request_middleware=[guardrail_mw],
response_middleware=[guardrail_mw],
)
```

</Tab>

</Tabs>

## 2. Point the Agent at the Guarded VirtualModel

In the agent's workflow YAML, set `model_name` on the relevant `llms` entry to
the guarded VirtualModel's entity reference, with slashes converted to hyphens
(per the [agent configuration conventions](/documentation/agents#agent-definition)):

```yaml
llms:
llm:
_type: openai
model_name: default-guarded-agent-model
```

Leave `base_url` and `api_key` unset. Once redeployed, every model call from the
agent flows through the guarded VirtualModel. The agent itself is unchanged and
unaware of the rails.

For the end-to-end request flow, streaming behavior, header forwarding, and the
`guardrails` request options, refer to
[Running Inference with Guardrails](/documentation/guardrail-models/core-concepts/running-inference).

Redeploy the agent, re-run evaluation, and compare quality, cost, latency and
safety signals against the baseline before promoting.

## Troubleshooting

**Virtual model creation fails with an unknown model.** Confirm the backend model entity exists with `nemo models list`. The `<main-model-entity>` and `<guardrail-config>` placeholders must reference entities the workspace can resolve.

**The agent still calls the unguarded model.** Entity references in `model_name` use hyphens, not slashes. Confirm the agent was redeployed after the config change.

**The `agents-secure` skill is not available.** Run `nemo skills list` to confirm the skill is installed. If it is missing, install it with `nemo skills install --agent <claude|codex|cursor|opencode>`.

155 changes: 155 additions & 0 deletions docs/agents/governance/apply-mitigations.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
---
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

title: "Review and Apply Mitigations"
description: ""
---
<a id="agents-governance-apply-mitigations"></a>

<Warning>

Agent Governance is released with _early access_ availability and is subject to limited support and potential API changes in future releases.

</Warning>

A finished war-game produces proposed defenses. This page covers choosing which
to keep, scoring that selection, and writing it back to the agent.

Nothing reaches the agent until you apply it.

## What a Run Proposes

Each proposed defense is tagged by the surface it changes:

| Tag | Written by | Effect |
|-----|-----------|--------|
| Guardrail | Guardrails defender | Pre-tool verifier middleware in the agent's workflow |
| Policy | OpenShell policy defender | A sandbox policy covering network egress, filesystem access, process identity, seccomp and Landlock |

Defenses are grouped by the tool they guard. Each one shows the attack that
motivated it and the exact configuration diff.

## Score a Selection Before Applying

A sanity check freezes a chosen subset, replays the recorded attacks against it,
and runs the benign suite. Use it to compare selections without touching the
agent.

```bash
nemo agent-hardener sanity-check --manifest-id react-agent \
--mitigations mitigations.json \
--replay-hitlog <fileset-ref> \
--keep custom_guardrail_1
```

| Option | Effect |
|--------|--------|
| `--mitigations <path>` | The run's proposed defenses |
| `--replay-hitlog <fileset-ref>` | The recorded attacks to replay |
| `--keep <id>` | Keep this defense. Repeatable |
| `--exclude <id>` | Drop this defense. Repeatable |
| `--env-file <path>` | Dotenv supplying the agent's secrets |

The result reports how many attacks the selection blocks and how many benign
requests still pass. Re-run it with a different `--keep` set to compare.

## Apply to the Agent

<Note>

Applying is available in Studio and through the REST API. The CLI stops at
`sanity-check`.

</Note>

<Tabs>

<Tab title="Studio">

Open the finished run and select the **Harden** panel.

1. Toggle the defenses you want. Each row expands to show the attack it
counters and the configuration diff.
1. Select **Sanity check** to replay the attacks against just that selection
and see what it blocks and which ordinary requests it breaks.
1. Select **Apply to Agent** to record the hardened guardrails onto the
agent config.

</Tab>
<Tab title="REST API">

Applying takes two calls. First compose the subset you chose, which returns the
workflow and policy for exactly those defenses:

```bash
curl -X POST \
$NMP_BASE_URL/apis/agent-hardener/v2/workspaces/default/runs/<run-name>/compose-defense \
-H "Content-Type: application/json" \
-d '{
"mitigations": { ... },
"selected_defense_ids": ["custom_guardrail_1", "openshell_policy"]
}'
```

`mitigations` is the run's mitigations artifact. `selected_defense_ids` takes
guardrail ids and `openshell_policy`. The response carries `guardrails_toml` and
`policy_yaml`, and an unselected surface comes back at its baseline.

Then write the composed guardrails onto the agent:

```bash
curl -X POST \
$NMP_BASE_URL/apis/agent-hardener/v2/workspaces/default/runs/<run-name>/apply-mitigation \
-H "Content-Type: application/json" \
-d '{"guardrails_toml": "<the guardrails_toml from compose-defense>"}'
```

`guardrails_toml` is required; the call returns `422` without it. Only the
guardrails are written — `policy_yaml` is composed for review and sanity-check
but is not applied to the agent. Runs from a bring-your-own manifest
(`--project-dir`) are refused, because there is no registered agent to write to.

The response reports `applied`, the `agent` whose config changed, and a
`detail` note.

</Tab>

</Tabs>

Applying records the hardened guardrails on the agent config and refreshes the
manifest, so the next run measures the change you just made.

<Warning>

Applying does not yet activate the guardrails on a running agent. The guardrails
are stored on the agent entity, but the deployment path does not read them back,
so redeploying does not put them in force. Use `sanity-check` to confirm a
defense works, and apply the guardrails to your agent's own configuration until
this is wired up.

</Warning>

<Note>

Applying writes another service's entity: it updates the target agent's config.
The `agent-hardener.runs.apply` permission is therefore an agent-write grant in
practice. Assign it accordingly.

</Note>

## Compare Two Runs

Because runs target a frozen manifest, two reports for the same manifest are
directly comparable. Run the cycle again after applying and compare the attack
block rate and the benign pass rate against the earlier run:

```bash
nemo agent-hardener run --manifest-id react-agent
nemo agent-hardener status --limit 5
```

## Related Topics

- [Agent Hardener CLI Reference](/documentation/agents/governance/cli-reference): every command and flag.
- [Guardrail Models](/documentation/guardrail-models): manage the guardrail configurations the defender writes against.
Loading
Loading