Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
188 changes: 188 additions & 0 deletions developer-guide/adapt/hardening-policy.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
---
title: "Steer how Darwin hardens your agent"
description: "Attach a hardening policy to a Darwin evolution job to control engine routing, mutation budgets, and how candidates are scored."

Check warning on line 3 in developer-guide/adapt/hardening-policy.mdx

View check run for this annotation

Mintlify / Mintlify Validation (vijil) - vale-spellcheck

developer-guide/adapt/hardening-policy.mdx#L3

'are scored' may be passive voice. Use active voice if you can.
---

When Darwin hardens an agent, it makes three decisions on every cycle: which engines to try against the weakest trust dimension, which engines to run and at what budget, and how to score each candidate before accepting it. Out of the box, Darwin makes those decisions with built-in defaults.

A **hardening policy** lets you set them yourself. You attach a data-only policy object to an evolution job, and Darwin honors it for that run. Nothing about the policy is code — it is plain JSON, so you can read it, diff it, version it, and let an automated optimizer propose one.

Check warning on line 8 in developer-guide/adapt/hardening-policy.mdx

View check run for this annotation

Mintlify / Mintlify Validation (vijil) - vale-spellcheck

developer-guide/adapt/hardening-policy.mdx#L8

'it is' is too wordy.

This guide shows you how to attach a policy to a job when you run Darwin as a module inside your Kubernetes cluster.

<Note>
Omit the policy and Darwin behaves exactly as it does today. A job with no `hardening_policy` (or `null`) runs the built-in pipeline unchanged — bit for bit. You opt into control one field at a time.
</Note>

## Prerequisites

- Darwin running in your cluster and reachable on its service address. In a Helm install the service is `service-darwin`, so the in-cluster URL is `http://service-darwin.<namespace>.svc.cluster.local:80` (or `http://service-darwin:80` from the same namespace).

Check warning on line 18 in developer-guide/adapt/hardening-policy.mdx

View check run for this annotation

Mintlify / Mintlify Validation (vijil) - vale-spellcheck

developer-guide/adapt/hardening-policy.mdx#L18

Did you really mean 'namespace'?
- A genome seeded for the agent. Darwin rejects an evolution job for an agent that has no genome — seed one first with `POST /v1/genomes`.
- For the SDK path: `pip install vijil-sdk`.

## Attach a Policy Over REST

The policy rides on Darwin's evolution endpoint. Add a `hardening_policy` object to the `POST /v1/evolutions` body:

```bash
curl -X POST http://service-darwin.<namespace>.svc.cluster.local:80/v1/evolutions \
-H "content-type: application/json" \
-d '{
"agent_id": "8dec5567-2ddd-4f7b-876e-6445575c31e5",
"team_id": "f8c08b5e-1c2d-4e3f-9a0b-1c2d3e4f5a6b",
"agent_config": {"agent_url": "http://my-agent:9000/v1"},
"harness_names": ["security"],
"hardening_policy": {
"tradeoff_weights": {"dimension_weights": {"security": 2.0}}
}
}'
```

Darwin returns `202 Accepted` with the job identity:

```json
{
"evolution_id": "…",
"status": "accepted",
"mode": "config",
"team_id": "…",
"agent_id": "…"
}
```

The policy above tells the scorer to weight every security gain twice as heavily as the default. Everything else stays on its built-in setting, because you set only `tradeoff_weights`.

<Note>
If your Darwin deployment fronts the API with authentication, add `-H "authorization: Bearer <token>"`. An in-cluster module deployment often runs without it.
</Note>

## Attach a Policy Through the SDK

The hardening policy lives on Darwin's own evolution endpoint, so point the SDK's HTTP transport at your Darwin service and call the evolutions resource:

Check warning on line 60 in developer-guide/adapt/hardening-policy.mdx

View check run for this annotation

Mintlify / Mintlify Validation (vijil) - vale-spellcheck

developer-guide/adapt/hardening-policy.mdx#L60

Did you really mean 'SDK's'?

```python
from vijil._generated.resources.evolutions import EvolutionsGeneratedResource
from vijil.config import VijilConfig
from vijil.http import VijilHTTP

http = VijilHTTP(
gateway_url="http://service-darwin.<namespace>.svc.cluster.local:80",
token="<token>", # whatever your Darwin deployment expects
)
evolutions = EvolutionsGeneratedResource(http, VijilConfig())

job = evolutions.create(
agent_id="8dec5567-2ddd-4f7b-876e-6445575c31e5",
team_id="f8c08b5e-1c2d-4e3f-9a0b-1c2d3e4f5a6b",
agent_config={"agent_url": "http://my-agent:9000/v1"},
harness_names=["security"],
hardening_policy={"tradeoff_weights": {"dimension_weights": {"security": 2.0}}},
)
print(job.evolution_id, job.status)
```

You pass `hardening_policy` as a plain dict — the same JSON shape as the REST body. The client accepts it as `dict[str, Any]`. `VijilConfig()` only satisfies the resource constructor here; the Darwin address comes from `VijilHTTP`, so you do not need a config file in the pod.

Check warning on line 83 in developer-guide/adapt/hardening-policy.mdx

View check run for this annotation

Mintlify / Mintlify Validation (vijil) - vale-spellcheck

developer-guide/adapt/hardening-policy.mdx#L83

Did you really mean 'config'?

<Warning>
The top-level `Vijil(...)` client and its `client.jobs` resource route through the Console-managed `/agents/{id}/evolutions` path, which is the hosted-platform flow — not a direct call to Darwin. When you run Darwin as a module in your own cluster, talk to the evolutions resource pointed at Darwin directly, as shown above.

Check warning on line 86 in developer-guide/adapt/hardening-policy.mdx

View check run for this annotation

Mintlify / Mintlify Validation (vijil) - vale-spellcheck

developer-guide/adapt/hardening-policy.mdx#L86

Consider using the '…' symbol instead of '...'.
</Warning>

## Choose Your Defaults — Leave Fields Out

Every level of the policy is opt-in. The top object has three sub-policies, and each one is independently optional:

```json
{
"attribution_routing": { "...": "how engines are ranked" },
"mutation_selection": { "...": "which engines run, at what budget" },
"tradeoff_weights": { "...": "how candidates are scored" }
}
```

Send only the sub-policies you want to change. A missing or `null` sub-policy falls back to Darwin's current behavior for that stage, and an all-empty policy reproduces the default pipeline exactly.

## Policy Reference

### `attribution_routing` — Rank the Engines

Controls how Darwin orders engines against the weakest trust dimension before it mutates.

| Field | Type | Effect |
| --- | --- | --- |
| `engine_relevance` | `{engine: {dimension: float}}` | Overrides the default relevance map Darwin uses to rank engines. |
| `failure_demotion` | `float`, 0.0–1.0 | Multiplier applied to an engine's relevance after it fails. `1.0` keeps it; `0.0` drops it. Darwin's default is `0.3`. |
| `tie_break_order` | `[engine, …]` | Breaks ties between equally-ranked engines in the order you give. |

### `mutation_selection` — Pick Engines and Budgets

Decides which gene tiers Darwin may mutate and how much work each engine does. Applied before generation.

| Field | Type | Effect |
| --- | --- | --- |
| `permitted_tiers` | `[tier, …]` | The only tiers Darwin may mutate. Tiers: `instruction`, `config`, `dome`, `source`. |
| `preferred_tiers` | `[tier, …]` | Reorders the permitted set. Must be a subset of `permitted_tiers`. |
| `per_tier_budget` | `{tier: {…}}` | Per-tier budget. Each entry takes `max_metric_calls` and/or `max_candidates` (both ≥ 1). |
| `trust_floors` | `{dimension: float}` | Stop criterion — Darwin stops trying more engines once every dimension sits at or above its floor. |

Each tier maps to one engine, and each engine reads one budget field:

| Tier | Engine | Budget field it reads |
| --- | --- | --- |
| `instruction` | GEPA (system-prompt optimization) | `max_metric_calls` |
| `config` | IC-QD (feature and config search) | `max_candidates` |

Check warning on line 131 in developer-guide/adapt/hardening-policy.mdx

View check run for this annotation

Mintlify / Mintlify Validation (vijil) - vale-spellcheck

developer-guide/adapt/hardening-policy.mdx#L131

Did you really mean 'config'?
| `dome` | Dome threshold calibration | reserved (no budget field yet) |
| `source` | Source/code evolution (future) | reserved |

### `tradeoff_weights` — Score the Candidates

Decides whether a candidate's net change is good enough to accept.

| Field | Type | Effect |
| --- | --- | --- |
| `dimension_weights` | `{dimension: float}` | Multiplies each dimension's fitness delta before summing. An absent dimension weighs `1.0`; an empty or `null` map is the default unweighted sum. Weights must be ≥ 0. |
| `min_improvement_threshold` | `float` | Overrides the configured acceptance threshold for this job. |
| `hard_constraints` | `[{dimension, min_delta}, …]` | Rejects any candidate whose delta on `dimension` falls below `min_delta`, whatever its net gain elsewhere. |

A worked example that uses all three sub-policies:

```json
{
"attribution_routing": {
"failure_demotion": 0.0,
"tie_break_order": ["gepa", "ic-qd"]
},
"mutation_selection": {
"permitted_tiers": ["instruction", "config"],
"per_tier_budget": {
"instruction": {"max_metric_calls": 30},
"config": {"max_candidates": 12}
},
"trust_floors": {"security": 0.8}
},
"tradeoff_weights": {
"dimension_weights": {"security": 2.0, "reliability": 1.0},
"hard_constraints": [{"dimension": "reliability", "min_delta": -0.05}]
}
}
```

This run drops a failed engine entirely, mutates only the instruction and config tiers within fixed budgets, stops once security reaches 0.8, scores security gains double, and rejects any candidate that costs more than 0.05 of reliability.

Check warning on line 168 in developer-guide/adapt/hardening-policy.mdx

View check run for this annotation

Mintlify / Mintlify Validation (vijil) - vale-spellcheck

developer-guide/adapt/hardening-policy.mdx#L168

Did you really mean 'config'?

## Typos Fail Loud

The policy schema is strict. An unknown field *inside* the policy is rejected at submit time with `422` and the message `Extra inputs are not permitted` — Darwin does not silently drop it. So `dimension_weight` (a typo for `dimension_weights`) returns an error instead of a policy that looks valid but does nothing. Check a rejected field against the reference above before you resubmit.

Check warning on line 172 in developer-guide/adapt/hardening-policy.mdx

View check run for this annotation

Mintlify / Mintlify Validation (vijil) - vale-spellcheck

developer-guide/adapt/hardening-policy.mdx#L172

'is rejected' may be passive voice. Use active voice if you can.

Check warning on line 172 in developer-guide/adapt/hardening-policy.mdx

View check run for this annotation

Mintlify / Mintlify Validation (vijil) - vale-spellcheck

developer-guide/adapt/hardening-policy.mdx#L172

Don't start a sentence with 'So '.

This strictness covers the fields inside `hardening_policy`. The surrounding job request is lenient: if you misspell the top-level `hardening_policy` key itself, the request still succeeds and the policy does not apply. If a job ignores your policy, check that the top-level key is spelled correctly.

Check warning on line 174 in developer-guide/adapt/hardening-policy.mdx

View check run for this annotation

Mintlify / Mintlify Validation (vijil) - vale-spellcheck

developer-guide/adapt/hardening-policy.mdx#L174

'is spelled' may be passive voice. Use active voice if you can.

## Troubleshooting

**`422 Extra inputs are not permitted`.** A field name is wrong. The usual suspects are `dimension_weight` vs `dimension_weights` and `permited_tiers` vs `permitted_tiers`. Fix the spelling.

**`422` on `preferred_tiers`, `dimension_weights`, or `failure_demotion`.** Three rules are enforced: `preferred_tiers` must be a subset of `permitted_tiers`, every weight in `dimension_weights` must be ≥ 0, and `failure_demotion` must sit between 0.0 and 1.0.

Check warning on line 180 in developer-guide/adapt/hardening-policy.mdx

View check run for this annotation

Mintlify / Mintlify Validation (vijil) - vale-spellcheck

developer-guide/adapt/hardening-policy.mdx#L180

'are enforced' may be passive voice. Use active voice if you can.

**A budget on the `dome` or `source` tier does nothing.** Those tiers accept a budget but read no budget field yet — they are reserved. Set budgets on `instruction` and `config`.

Check warning on line 182 in developer-guide/adapt/hardening-policy.mdx

View check run for this annotation

Mintlify / Mintlify Validation (vijil) - vale-spellcheck

developer-guide/adapt/hardening-policy.mdx#L182

'are reserved' may be passive voice. Use active voice if you can.

**The policy seems to vanish after a Darwin restart.** A job's policy is transient: it travels with the in-flight job and is not stored in the database. If a Darwin pod restarts mid-run, that job is marked failed rather than resumed, so its policy is not carried forward. Resubmit the job with the policy attached.

Check warning on line 184 in developer-guide/adapt/hardening-policy.mdx

View check run for this annotation

Mintlify / Mintlify Validation (vijil) - vale-spellcheck

developer-guide/adapt/hardening-policy.mdx#L184

'is marked' may be passive voice. Use active voice if you can.

## Next Steps

- [The Trust Score](/concepts/trust-score/introduction) — the reliability, security, and safety dimensions your policy weights and constrains.