Skip to main content
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:
Then import what you need:

Passthrough mode

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

#[tool] — declare a governed tool

Mark a function as a tool that Nanny should track and charge against the budget:
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:

Matching the tool allowlist

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

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.
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)
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.
Only input and output are required. You can optionally attach model and provider labels — identifiers only, never prompt or response content — and, if your provider’s response reports it, cache_read/cache_write (a finer split of input, never additional tokens beyond it):
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.
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.

#[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:
Rules are evaluated on every tool call. When a rule returns false, Nanny stops execution with:

PolicyContext fields

The ctx parameter gives you a snapshot of the current execution state: 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:
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.
webdingo running under nanny run — planner, researcher, and synthesizer agent scopes entering and exiting with structured NDJSON events

nanny::fresh_run — starting a fresh run mid-process

#[agent(...)] changes which ceiling the run’s one running total is checked against; it does not give a scope its own budget (see Named sets share one counter). If your process runs multiple, independent phases back to back and you want each one to start from zero rather than inherit whatever an earlier phase already spent, that’s a new run, Nanny’s real unit of governance: one cumulative counter, one stop state, “a stop is final.”
Only meaningful when governed through a network server (nanny run --serve / --join): the server keys independent state per run, so a stop in the run you just left has zero effect on the one you’re starting. Under local nanny run (no --serve), one process is already always exactly one run, so this is a safe no-op there. Mirrors nanny_sdk.fresh_run() on the Python side.

Complete example

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

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.