Expand description
Policy-based tool approval for Ferrin.
A PolicyClient evaluates a policy path with a JSON input and returns
the raw decision document. policy_approval turns a client into an
ApprovalPolicy: the tool
name, its input, the messages and the runtime context become the policy
input, and the normalized PolicyDecision becomes the approval status.
capability_middleware filters the tools offered to the model through
the same client. shadow observes decisions without enforcing them and
with_default gives every call a decision.
Clients: HttpPolicyClient speaks the OPA REST Data API
(POST /v1/data/<path>); RegoPolicyClient (feature rego) evaluates
Rego policies in-process with regorus. policy_client adapts a
closure, for tests and static rules.
Design: docs/01-architecture/18-policy-approval.md, ADR 0020.
§Attribution
The decision document format, its normalization rules and the shadow and
capability patterns are derived from the Vercel AI SDK (Apache-2.0,
Copyright 2023 Vercel, Inc.) and reimplemented in Rust. See the NOTICE
file in the crate root.
§Examples
use ferrin_core::generate_text::ApprovalStatus;
use ferrin_policy::PolicyDecision;
use ferrin_policy::policy_approval;
use ferrin_policy::policy_client;
use serde_json::json;
// A decision document as returned by a policy server or a Rego rule.
let decision = PolicyDecision::normalize(&json!({
"decision": "requires-approval",
"reason": "writes outside the workspace"
}));
assert_eq!(
decision.into_approval(),
Some(ApprovalStatus::user_approval().with_reason("writes outside the workspace"))
);
// A static in-process client; pass the policy to
// `generate_text(..).tool_approval(policy)`.
let client = policy_client(|_path, input| {
Ok(json!({ "decision": if input["tool"]["name"] == "delete_file" { "deny" } else { "allow" } }))
});
let _policy = policy_approval(client, "ferrin/tools/decision");Structs§
- Capability
Middleware - Middleware created by
capability_middleware. - Http
Policy Client - Evaluates policies through a policy server implementing the OPA REST
Data API:
POST <base>/v1/data/<path>with{"input": ..}, answering{"result": ..}. - Http
Policy Client Builder - Builder of an
HttpPolicyClient. - Policy
Approval - Approval policy created by
policy_approval. - Policy
Client Fn - Adapter turning a synchronous closure into a
PolicyClient. - Policy
Decision Event - The observed and effective approval decisions for one tool call.
- Policy
Decision Tool Call - Identifying information for the tool call a policy evaluated.
- Rego
Policy Client - Evaluates Rego policies in-process.
- Rego
Policy Client Builder - Builder of a
RegoPolicyClient. - Shadow
- Approval policy created by
shadow. - With
Default - Approval policy created by
with_default.
Enums§
- Enforcement
- Whether a
Shadowpolicy acts on the decisions it observes. - Failure
Mode - What an approval policy or capability middleware does when the policy cannot be evaluated (transport failure, engine error, invalid response).
- Policy
Decision - A normalized policy decision.
- Policy
Error - Why a policy could not be evaluated.
Constants§
- DEFAULT_
MAX_ RESPONSE_ BYTES - Default limit on the size of a decision response body.
- UNRECOGNIZED_
DECISION - Reason attached to denials of documents that are not recognized.
Traits§
- Policy
Client - Evaluates policies: a path and a JSON input in, a raw decision document out.
Functions§
- capability_
middleware - Restricts
CallOptions::toolsto the allowlist returned by the policy atpath. - default_
capability_ input - The default capability input:
- default_
input - The default policy input matches the reference SDK’s OPA rule input.
- parse_
allowlist - Parses the allowlist of a capability decision: an array of tool names or
an object with a
toolsarray. ReturnsNonefor anything else. - policy_
approval - Resolves tool approvals by evaluating the policy at
pathwithclient. - policy_
client - Wraps a synchronous closure
(path, input) -> decisionas a policy client, for tests and static rules. - shadow
- Evaluates
policyfor every call and reports its decision throughShadow::on_decision, but only acts on it underEnforcement::Enforce. Roll a policy out by observing first and flipping the enforcement later without changing the wiring. - with_
default - Gives calls with
NoneorNotApplicablefrom the inner policy the statusdefault, so that every call has a decision.
Type Aliases§
- Capability
Input Fn - Builds the policy input for a model call.
- OnDecision
Fn - Asynchronous observer receiving each evaluated and effective decision.
- OnDecision
Sync Fn - Synchronous low-level observer of the original policy return value.
- Shared
Policy Client - Shared reference to a policy client.
- ToInput
Fn - Builds the policy input for a tool call.