> ## Documentation Index
> Fetch the complete documentation index at: https://docs.nanny.run/llms.txt
> Use this file to discover all available pages before exploring further.

# Rust

> Per-function governance for Rust agents using Nanny macros.

The Rust SDK brings Nanny's enforcement boundary into your code. Instead of relying only on process-level limits, you mark individual functions as tools and rules — Nanny governs each call before it executes.

***

## Installation

The SDK ships inside the same crate as the CLI binary. Add it to your project:

```bash theme={null}
cargo add nannyd
```

Then import what you need:

```rust theme={null}
use nanny::{tool, rule, agent, PolicyContext};
```

***

## Passthrough mode

If your agent runs without `nanny run`, every macro is a no-op. The function executes normally with no enforcement overhead:

```bash theme={null}
# Governed — enforcement active (reads [start].cmd from nanny.toml)
nanny run

# Not governed — macros silent, agent runs normally
cargo run
```

***

## `#[tool]` — declare a governed tool

Mark a function as a tool that Nanny should track and charge against the budget:

```rust theme={null}
use nanny::tool;

#[tool(tokens = 10)]
fn fetch_page(url: &str) -> String {
    // HTTP call, file read, LLM call, or any side-effecting operation
    reqwest::blocking::get(url).unwrap().text().unwrap()
}
```

When the agent calls `fetch_page`:

1. Nanny checks: is `fetch_page` in the `[tools] allowed` list?
2. Nanny checks: has `fetch_page` exceeded `[tools.fetch_page] max_calls`?
3. Nanny charges 10 tokens against the budget.
4. If any check fails, execution stops immediately — the function body never runs.

### Tokens

The `tokens` argument is required. It is the number of tokens charged per call.
Set it to `0` for tools you want tracked but not charged:

```rust theme={null}
#[tool(tokens = 0)]
fn log_step(msg: &str) { ... }
```

### Matching the tool allowlist

The tool name used for allowlist checks is the **function name** as declared in Rust:

```toml theme={null}
# nanny.toml
[tools]
allowed = ["fetch_page", "read_file"]

[tools.fetch_page]
max_calls       = 20
tokens_per_call = 10   # nanny.toml value overrides the macro default
```

***

## `nanny::http_get` — built-in HTTP tool

`nanny::http_get` is a built-in governed HTTP GET function. It requires no `#[tool]` annotation — Nanny applies allowlist, call-count, and token enforcement automatically.

```rust theme={null}
use nanny::http_get;

let html = http_get("https://example.com")?;
```

Governance applied automatically:

* Checked against the `[tools] allowed` list (tool name: `"http_get"`)
* Subject to `[tools.http_get] max_calls` limit
* Costs 10 tokens per successful call (configurable via `[tools.http_get] tokens_per_call`)

```toml theme={null}
# nanny.toml
[tools]
allowed = ["http_get"]

[tools.http_get]
max_calls       = 15
tokens_per_call = 10
```

In passthrough mode (no `nanny run`), `nanny::http_get` makes the request directly with no enforcement overhead.

***

## `nanny::report_usage` — report LLM token usage

`#[tool(tokens = N)]` charges a *declared* token cost per call. To charge the *actual* tokens an LLM used, report them after the call with `nanny::report_usage`. Hand Nanny the token counts already present on the response — Nanny debits `input + output` from the budget.

```rust theme={null}
use nanny::{report_usage, Usage};

let resp = client.chat().create(request).await?;

report_usage(Usage {
    input:  resp.usage.prompt_tokens,
    output: resp.usage.completion_tokens,
    ..Default::default()
});
```

Only `input` and `output` are required. You can optionally attach `model` and `provider` labels — identifiers only, never prompt or response content:

```rust theme={null}
report_usage(Usage {
    input:  resp.usage.prompt_tokens,
    output: resp.usage.completion_tokens,
    model:    Some("gpt-4o".into()),
    provider: Some("openai".into()),
});
```

Each call emits an `LlmUsageRecorded` event to the log. `report_usage` is fire-and-forget: it never blocks your agent and never panics. In passthrough mode (no `nanny run`) it is a no-op.

<Note>
  Rust reports usage explicitly, one call per LLM response. In Python, `nanny_sdk.instrument(client)` does the same thing automatically by wrapping the client — Rust cannot patch a client at runtime, so the reporting is an explicit call instead.
</Note>

***

## `#[rule]` — declare an enforcement rule

A rule is a function that returns a verdict on whether execution should continue.
Return `true` to allow, `false` to deny:

```rust theme={null}
use nanny::{rule, PolicyContext};

#[rule("no_spiral")]
fn check_spiral(ctx: &PolicyContext) -> bool {
    let h = &ctx.tool_call_history;
    // Deny if the last three tool calls were all the same URL
    !(h.len() >= 3 && h[h.len()-3..].iter().all(|u| u == &h[h.len()-1]))
}
```

Rules are evaluated on every tool call. When a rule returns `false`, Nanny stops execution with:

```
StopReason::RuleDenied { rule_name: "no_spiral" }
```

### PolicyContext fields

The `ctx` parameter gives you a snapshot of the current execution state:

| Field               | Type                      | Description                                                                                    |
| ------------------- | ------------------------- | ---------------------------------------------------------------------------------------------- |
| `step_count`        | `u32`                     | Number of steps completed                                                                      |
| `elapsed_ms`        | `u64`                     | Wall-clock time elapsed                                                                        |
| `tokens_spent`      | `u64`                     | Total tokens spent so far                                                                      |
| `next_tool_tokens`  | `u64`                     | Declared token cost of the tool currently being evaluated. `0` when no tool call is in flight. |
| `tool_call_counts`  | `HashMap<String, u32>`    | Per-tool call counts                                                                           |
| `tool_call_history` | `Vec<String>`             | Ordered log of tool names called                                                               |
| `requested_tool`    | `Option<String>`          | The tool being evaluated right now                                                             |
| `last_tool_args`    | `HashMap<String, String>` | Arguments of the tool call currently being evaluated                                           |

Rules are evaluated **before** the tool runs — `requested_tool` is set to the tool name being checked.

***

## `#[agent]` — activate named limits for a scope

Mark a function to run under a named limit set from `nanny.toml`.
When the function is entered, Nanny switches to those limits; when it exits, limits revert:

```rust theme={null}
use nanny::agent;

#[agent("researcher")]
fn run_research(topic: &str) {
    // This runs under [limits.researcher] from nanny.toml
    let page = fetch_page(&format!("https://en.wikipedia.org/wiki/{topic}"));
    // ...
}
```

```toml theme={null}
# nanny.toml
[limits.researcher]
steps   = 200
tokens  = 5000
timeout = 120000
```

The named set inherits from `[limits]` and overrides only the declared fields.
Nesting `#[agent]` functions is supported — limits revert to the caller's set on exit.

<Frame>
  <img src="https://raw.githubusercontent.com/nanny-run/nanny/main/assets/demo/webdingo-agent-scopes.gif" alt="webdingo running under nanny run — planner, researcher, and synthesizer agent scopes entering and exiting with structured NDJSON events" />
</Frame>

***

## Complete example

```rust theme={null}
use nanny::{tool, rule, agent, PolicyContext};
use std::collections::HashMap;

#[tool(tokens = 10)]
fn fetch_page(url: &str) -> String {
    reqwest::blocking::get(url).unwrap().text().unwrap()
}

#[rule("no_spiral")]
fn check_spiral(ctx: &PolicyContext) -> bool {
    let h = &ctx.tool_call_history;
    !(h.len() >= 3 && h[h.len()-3..].iter().all(|u| u == &h[h.len()-1]))
}

#[agent("researcher")]
fn research(topic: &str) -> Vec<String> {
    let mut pages = Vec::new();
    let mut url = format!("https://en.wikipedia.org/wiki/{topic}");

    loop {
        let content = fetch_page(&url);
        pages.push(content.clone());
        // extract next URL from content ...
        break;
    }

    pages
}

fn main() {
    let results = research("Alan Turing");
    println!("Collected {} pages", results.len());
}
```

Run it under Nanny:

```bash theme={null}
nanny run
```

Run it without Nanny (macros silent, agent runs normally):

```bash theme={null}
cargo run
```

***

## What happens on stop

When Nanny stops execution inside an instrumented function, the macro propagates the stop signal by panicking with a structured message. The panic is caught by the Nanny runtime — your agent process exits cleanly with a non-zero code and an `ExecutionStopped` event in the log.

You do not need to handle stop reasons in your agent code. Nanny handles the exit path.
