Skip to content

Tutorial 21 โ€” Rust SDK (agent-governance-rust/ workspace)

Full agent governance in Rust lives in the top-level agent-governance-rust/ workspace. The main agentmesh crate now covers policy evaluation, trust scoring, hash-chain audit logging, Ed25519 agent identity, governance helpers, reward primitives, execution control, and lifecycle management. The companion agentmesh-mcp crate keeps MCP-focused security functionality available as a smaller standalone dependency.

Target runtime: Rust 1.75+ (2021 edition) Workspace: agent-governance-rust/ Crates: agentmesh and agentmesh-mcp Dependencies: serde, serde_yaml, sha2, ed25519-dalek, thiserror


What you'll learn

Section Topic
Quick Start Evaluate a policy in 5 lines of Rust
AgentMeshClient Unified governance pipeline โ€” identity + trust + policy + audit
Extended Crate Surface Governance, reward, and control-plane helpers beyond the core client
PolicyEngine YAML rules, capability/approval/rate-limit types, conflict resolution
TrustManager 0โ€“1000 trust scoring, tiers, decay, persistence
AuditLogger Hash-chain audit logging and verification
AgentIdentity Ed25519 key pairs, DIDs, signing, JSON export
Loading Policies from YAML File-based policy configuration
Full Governance Pipeline End-to-end example
Cross-Reference Equivalent Python and TypeScript tutorials
Next Steps Where to go from here

Prerequisites


Installation

From the repository root, the Rust workspace lives in:

agent-governance-rust/
  agentmesh/
  agentmesh-mcp/

Add the main governance crate to your project:

cargo add agentmesh

Or add it to your Cargo.toml directly:

[dependencies]
agentmesh = "3"

Quick Start

Five lines to evaluate your first policy:

use agentmesh::AgentMeshClient;

fn main() {
    let client = AgentMeshClient::new("my-agent")
        .expect("failed to create client");

    let result = client.execute_with_governance("data.read", None);
    println!("Allowed: {}", result.allowed);     // true
    println!("Decision: {:?}", result.decision);  // Allow
}

When no policy is loaded, the engine defaults to allow โ€” load a YAML policy to enforce governance rules.


AgentMeshClient

AgentMeshClient is the recommended entry point. It wires together identity, trust, policy, and audit into a single governance-aware pipeline.

Creating a Client

use agentmesh::{AgentMeshClient, ClientOptions};

// Default client โ€” generates identity, empty policy (allow-all)
let client = AgentMeshClient::new("analyst-001")?;

// Client with options
let opts = ClientOptions {
    capabilities: vec!["data.read".into(), "data.write".into()],
    trust_config: None,    // use defaults
    policy_yaml: None,     // load later
};
let client = AgentMeshClient::with_options("analyst-001", opts)?;

Accessing Components

Each subsystem is accessible as a public field:

// Identity (Ed25519 DID)
println!("DID: {}", client.identity.did);
println!("Capabilities: {:?}", client.identity.capabilities);

// Trust scores
let score = client.trust.get_trust_score(&client.identity.did);
println!("Trust: {} (tier: {:?})", score.score, score.tier);

// Audit trail
println!("Audit chain valid: {}", client.audit.verify());
println!("Entries: {}", client.audit.entries().len());

The Governance Pipeline

execute_with_governance() runs the full pipeline: evaluate โ†’ log โ†’ trust update.

use std::collections::HashMap;

let result = client.execute_with_governance("data.read", None);

// The result contains everything
println!("Allowed: {}", result.allowed);
println!("Decision: {:?}", result.decision);
println!("Trust: {} ({:?})", result.trust_score.score, result.trust_score.tier);
println!("Audit hash: {}", result.audit_entry.hash);

Extended Crate Surface

The Rust workspace now exposes a broader set of reusable governance helpers alongside the core client:

use agentmesh::{
    ComplianceEngine, ComplianceFramework, ExecutionRequest, ExecutionResponse,
    FrameworkGovernanceAdapter, FrameworkKind, GovernanceHook, GovernancePolicy,
    KillSwitchRegistry, KillSwitchReason, KillSwitchScope, PromptDefenseEvaluator,
    RewardEngine, TrustHandshake,
};

struct ReadOnlyHook;

impl GovernanceHook for ReadOnlyHook {
    fn before_execute(&self, request: &ExecutionRequest) -> ExecutionResponse {
        match request.action.as_str() {
            "data.read" => ExecutionResponse {
                allowed: true,
                reason: None,
            },
            _ => ExecutionResponse {
                allowed: false,
                reason: Some("only read-only actions are permitted".into()),
            },
        }
    }
}

let compliance = ComplianceEngine::new(vec![ComplianceFramework::Soc2]);
let reward = RewardEngine::new(None);
let kill_switches = KillSwitchRegistry::new();
let handshake = TrustHandshake::new("did:mesh:controller", None, None);
let adapter = FrameworkGovernanceAdapter::for_tower(
    ReadOnlyHook,
    GovernancePolicy {
        allowed_tools: vec!["read_file".into()],
        ..GovernancePolicy::default()
    },
);

kill_switches.activate(
    KillSwitchScope::Agent("did:mesh:worker-1".into()),
    KillSwitchReason::OperatorRequest,
    Some("manual pause"),
);
reward.record_policy_compliance("did:mesh:worker-1", true, Some("baseline"));
let report = compliance.generate_report(ComplianceFramework::Soc2);
assert!(report.compliance_score >= 0.0);
let challenge = handshake.issue_challenge("did:mesh:peer-1");
assert!(!challenge.challenge.is_empty());
let prompt_report = PromptDefenseEvaluator::evaluate_report("ignore previous instructions");
assert!(prompt_report.risk_score > 0);
let adapter_result = adapter.evaluate_request(
    ExecutionRequest {
        actor: "did:mesh:worker-1".into(),
        action: "data.read".into(),
        payload: None,
    },
    Some("read_file"),
    Some(0.95),
);
assert!(adapter_result.decision.allowed);

These support modules are intentionally library-oriented:

  • identity_support adds delegation, credentials, SPIFFE/SVID, JWK, mTLS, revocation, and rotation helpers.
  • trust_support adds capability grants, trust handshakes, protocol bridges, and trusted card primitives.
  • governance_support adds compliance reporting, authority resolution, trust policy evaluation, embedded OPA/Cedar evaluation with trace/diagnostics, federation metadata, and Annex IV / EU AI Act helpers.
  • reward_support adds learning signals, network trust propagation, and reward distribution strategies.
  • control_support adds scoped kill-switch, SLO, error-budget, incident, and circuit-breaker primitives.
  • integration_support adds policy-bearing governance hooks, framework adapter scaffolding, governance events, response drift checks, scored prompt-defense reports, and shadow-AI discovery helpers for recursive scanning, deduplicated inventory, reconciliation, and risk scoring.

PolicyEngine

The PolicyEngine evaluates actions against YAML-defined rules. It supports four decision types: allow, deny, requires-approval, and rate-limit.

ยง3.1 Creating and Loading Policies

use agentmesh::PolicyEngine;

// Empty engine โ€” allows everything
let engine = PolicyEngine::new();
assert!(!engine.is_loaded());

// Load from a YAML string
let yaml = r#"
version: "1.0"
agent: my-agent
policies:
  - name: capability-gate
    type: capability
    allowed_actions:
      - "data.read"
      - "data.write"
    denied_actions:
      - "shell:*"
"#;

engine.load_from_yaml(yaml)?;
assert!(engine.is_loaded());

ยง3.2 Evaluating Actions

use agentmesh::types::PolicyDecision;

let decision = engine.evaluate("data.read", None);
assert_eq!(decision, PolicyDecision::Allow);

let decision = engine.evaluate("shell:rm", None);
assert!(matches!(decision, PolicyDecision::Deny(_)));

// Actions outside the rule's scope fall through to Allow
let decision = engine.evaluate("admin.delete", None);
assert_eq!(decision, PolicyDecision::Allow);

ยง3.3 Four Decision Types

# policies/governance.yaml
version: "1.0"
agent: multi-decision-demo
policies:
  # 1. Capability โ€” allow/deny by action pattern
  - name: data-gate
    type: capability
    allowed_actions:
      - "data.*"
    denied_actions:
      - "shell:*"

  # 2. Approval โ€” require human sign-off
  - name: deploy-gate
    type: approval
    actions:
      - "deploy.*"
    min_approvals: 2

  # 3. Rate limit โ€” cap call frequency
  - name: api-throttle
    type: rate_limit
    actions:
      - "api.*"
    max_calls: 10
    window: "60s"
let engine = PolicyEngine::new();
engine.load_from_file("policies/governance.yaml")?;

// Capability โ€” allowed
assert_eq!(engine.evaluate("data.read", None), PolicyDecision::Allow);

// Capability โ€” denied
assert!(matches!(engine.evaluate("shell:rm", None), PolicyDecision::Deny(_)));

// Approval required
assert!(matches!(
    engine.evaluate("deploy.prod", None),
    PolicyDecision::RequiresApproval(_)
));

// Rate limiting
for _ in 0..10 {
    assert_eq!(engine.evaluate("api.call", None), PolicyDecision::Allow);
}
assert!(matches!(
    engine.evaluate("api.call", None),
    PolicyDecision::RateLimited { .. }
));

ยง3.4 Conditional Rules

Rules can include conditions that are matched against a context map:

version: "1.0"
agent: conditional-demo
policies:
  - name: prod-gate
    type: capability
    denied_actions:
      - "deploy.*"
    conditions:
      environment: "production"
use std::collections::HashMap;
use serde_yaml::Value;

let engine = PolicyEngine::new();
engine.load_from_yaml(yaml)?;

// Without context โ€” rule is skipped, action allowed
assert_eq!(engine.evaluate("deploy.app", None), PolicyDecision::Allow);

// With matching context โ€” denied
let mut ctx = HashMap::new();
ctx.insert("environment".into(), Value::String("production".into()));
assert!(matches!(
    engine.evaluate("deploy.app", Some(&ctx)),
    PolicyDecision::Deny(_)
));

ยง3.5 Conflict Resolution

When multiple policy candidates conflict, the engine resolves them using one of four strategies:

use agentmesh::types::{CandidateDecision, ConflictResolutionStrategy, PolicyScope};

let engine = PolicyEngine::with_strategy(ConflictResolutionStrategy::DenyOverrides);

let candidates = vec![
    CandidateDecision {
        decision: PolicyDecision::Allow,
        priority: 10,
        scope: PolicyScope::Global,
        rule_name: "allow-rule".into(),
    },
    CandidateDecision {
        decision: PolicyDecision::Deny("blocked".into()),
        priority: 5,
        scope: PolicyScope::Global,
        rule_name: "deny-rule".into(),
    },
];

let result = engine.resolve_conflicts(&candidates);
assert!(matches!(result.winning_decision, PolicyDecision::Deny(_)));
assert!(result.conflict_detected);
Strategy Behaviour
DenyOverrides Any deny wins, regardless of priority
AllowOverrides Any allow wins, regardless of priority
PriorityFirstMatch Highest priority wins (default)
MostSpecificWins Agent > Tenant > Global scope, then priority

TrustManager

The TrustManager tracks per-agent trust scores on a 0โ€“1000 scale across five tiers, with time-based decay and optional JSON persistence.

ยง4.1 Trust Tiers

Tier Score Range Description
Untrusted 0โ€“299 Agent has failed validation or behaved maliciously
Probationary 300โ€“499 New or recovering agent, limited access
Standard 500โ€“699 Default starting point
Trusted 700โ€“899 Established track record
VerifiedPartner 900โ€“1000 Highest confidence level

ยง4.2 Basic Usage

use agentmesh::{TrustManager, TrustTier};

let tm = TrustManager::with_defaults();

// New agent starts at 500 (Standard)
let score = tm.get_trust_score("agent-x");
assert_eq!(score.score, 500);
assert_eq!(score.tier, TrustTier::Standard);

// Record successes โ€” trust increases
tm.record_success("agent-x");
tm.record_success("agent-x");
let score = tm.get_trust_score("agent-x");
assert!(score.score > 500);

// Record failures โ€” trust decreases (asymmetric: penalty > reward)
tm.record_failure("agent-x");
let score = tm.get_trust_score("agent-x");
println!("After failure: {} ({:?})", score.score, score.tier);

ยง4.3 Custom Configuration

use agentmesh::TrustConfig;

let config = TrustConfig {
    initial_score: 800,
    threshold: 700,
    reward: 20,
    penalty: 100,
    persist_path: None,
    decay_rate: 0.95,
};
let tm = TrustManager::new(config);

let score = tm.get_trust_score("high-trust-agent");
assert_eq!(score.score, 800);
assert_eq!(score.tier, TrustTier::Trusted);

ยง4.4 Decay

Trust scores decay over time if no interactions occur. The decay_rate multiplier is applied per hour since the last update:

decayed_score = score ร— decay_rate ^ hours_elapsed

Set decay_rate closer to 1.0 for slower decay, or lower (e.g., 0.90) for aggressive decay requiring frequent re-validation.

ยง4.5 Persistence

Enable JSON persistence to survive process restarts:

let config = TrustConfig {
    persist_path: Some("trust-scores.json".into()),
    ..Default::default()
};
let tm = TrustManager::new(config);

// Scores are saved on every update and loaded on construction
tm.record_success("agent-x");
// trust-scores.json now contains the score

AuditLogger

The AuditLogger provides an append-only, hash-chain-linked audit trail. Each entry's SHA-256 hash incorporates the previous entry's hash, creating a tamper-evident chain.

ยง5.1 Logging Events

use agentmesh::AuditLogger;

let logger = AuditLogger::new();

let entry = logger.log("agent-001", "data.read", "allow");
println!("Hash: {}", entry.hash);
println!("Prev: {}", entry.previous_hash);  // empty for genesis entry
println!("Seq:  {}", entry.seq);             // 0

ยง5.2 Hash-Chain Integrity

let logger = AuditLogger::new();

logger.log("agent-1", "data.read",   "allow");
logger.log("agent-1", "data.write",  "deny");
logger.log("agent-2", "report.send", "allow");

// Verify the entire chain
assert!(logger.verify());

// Each entry links to the previous
let entries = logger.entries();
assert_eq!(entries.len(), 3);
assert_eq!(entries[1].previous_hash, entries[0].hash);
assert_eq!(entries[2].previous_hash, entries[1].hash);

How the chain works:

  Entry 0            Entry 1            Entry 2
  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”       โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”       โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
  โ”‚ hash: A  โ”‚โ”€โ”€โ”€โ”€โ”€โ”€โ–ถโ”‚ prev: A  โ”‚โ”€โ”€โ”€โ”€โ”€โ”€โ–ถโ”‚ prev: B  โ”‚
  โ”‚ prev: "" โ”‚       โ”‚ hash: B  โ”‚       โ”‚ hash: C  โ”‚
  โ”‚ seq: 0   โ”‚       โ”‚ seq: 1   โ”‚       โ”‚ seq: 2   โ”‚
  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜       โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜       โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

If any entry is tampered with, verify() returns false because the hash chain breaks.

ยง5.3 Filtering Entries

use agentmesh::types::AuditFilter;

let filter = AuditFilter {
    agent_id: Some("agent-1".into()),
    action: None,
    decision: None,
    start_time: None,
    end_time: None,
};

let filtered = logger.get_entries(&filter);
println!("Agent-1 entries: {}", filtered.len());

AgentIdentity

The AgentIdentity provides Ed25519-based cryptographic identity with DID identifiers, signing, and JSON serialisation.

ยง6.1 Generating an Identity

use agentmesh::AgentIdentity;

let identity = AgentIdentity::generate(
    "researcher-agent",
    vec!["data.read".into(), "search".into()],
)?;

println!("DID: {}", identity.did);               // did:agentmesh:researcher-agent
println!("Capabilities: {:?}", identity.capabilities);
println!("Public key: {} bytes", identity.public_key.len());

ยง6.2 Signing and Verifying

let data = b"important message";

// Sign
let signature = identity.sign(data)?;
println!("Signature: {} bytes", signature.len());  // 64 bytes

// Verify with the same identity
assert!(identity.verify(data, &signature));

// Tampered data fails verification
assert!(!identity.verify(b"wrong message", &signature));

ยง6.3 JSON Serialisation

Export the public portion of an identity for sharing:

let json = identity.to_json()?;
println!("{}", json);
// {"did":"did:agentmesh:researcher-agent","public_key":"...","capabilities":["data.read","search"]}

// Reconstruct from JSON
let imported = AgentIdentity::from_json(&json)?;
assert_eq!(imported.did, identity.did);

ยง6.4 Public Identity (Verification Only)

Share a PublicIdentity when you need verification without the private key:

use agentmesh::PublicIdentity;

let pub_id = identity.public_identity();
assert!(pub_id.verify(b"important message", &signature));

Loading Policies from YAML

The recommended pattern is to keep policies in versioned YAML files:

# policies/security.yaml
version: "1.0"
agent: production-agent
policies:
  - name: data-access
    type: capability
    allowed_actions:
      - "data.read"
      - "data.write"
    denied_actions:
      - "shell:*"
      - "admin.*"

  - name: deploy-approval
    type: approval
    actions:
      - "deploy.*"
    min_approvals: 2

  - name: api-rate-limit
    type: rate_limit
    actions:
      - "api.*"
    max_calls: 100
    window: "1m"
use agentmesh::{AgentMeshClient, ClientOptions};

// Load policy at client creation
let yaml = std::fs::read_to_string("policies/security.yaml")?;
let client = AgentMeshClient::with_options("prod-agent", ClientOptions {
    policy_yaml: Some(yaml),
    ..Default::default()
})?;

// Or load into an existing engine
client.policy.load_from_file("policies/additional.yaml")?;

Full Governance Pipeline

End-to-end example combining all subsystems:

use agentmesh::{AgentMeshClient, ClientOptions, TrustConfig};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 1. Configure the client
    let policy_yaml = r#"
version: "1.0"
agent: research-agent
policies:
  - name: data-gate
    type: capability
    allowed_actions:
      - "data.read"
      - "search.*"
    denied_actions:
      - "data.delete"
      - "shell:*"
  - name: api-throttle
    type: rate_limit
    actions:
      - "search.*"
    max_calls: 5
    window: "60s"
"#;

    let client = AgentMeshClient::with_options("research-agent", ClientOptions {
        capabilities: vec!["data.read".into(), "search.web".into()],
        trust_config: Some(TrustConfig {
            initial_score: 500,
            reward: 15,
            penalty: 75,
            ..Default::default()
        }),
        policy_yaml: Some(policy_yaml.into()),
    })?;

    println!("Agent DID: {}", client.identity.did);

    // 2. Execute governed actions
    let actions = ["data.read", "search.web", "data.delete", "shell:ls"];
    for action in &actions {
        let result = client.execute_with_governance(action, None);
        println!(
            "  {} โ†’ {} (trust: {}, tier: {:?})",
            action,
            if result.allowed { "โœ… allowed" } else { "โŒ denied" },
            result.trust_score.score,
            result.trust_score.tier,
        );
    }

    // 3. Verify audit chain
    let entries = client.audit.entries();
    println!("\nAudit trail: {} entries", entries.len());
    println!("Chain valid: {}", client.audit.verify());

    for entry in &entries {
        println!(
            "  [{}] {} โ†’ {} (hash: {}...)",
            entry.seq,
            entry.action,
            entry.decision,
            &entry.hash[..12],
        );
    }

    Ok(())
}

Expected output:

Agent DID: did:agentmesh:research-agent
  data.read   โ†’ โœ… allowed (trust: 510, tier: Trusted)
  search.web  โ†’ โœ… allowed (trust: 520, tier: Trusted)
  data.delete โ†’ โŒ denied  (trust: 445, tier: Neutral)
  shell:ls    โ†’ โŒ denied  (trust: 370, tier: Provisional)

Audit trail: 4 entries
Chain valid: true
  [0] data.read   โ†’ allow (hash: 3a7f2b8c91e4...)
  [1] search.web  โ†’ allow (hash: f1d9a2c38b71...)
  [2] data.delete โ†’ deny  (hash: 8e4c1a7d02f3...)
  [3] shell:ls    โ†’ deny  (hash: b5e7d3f9c128...)

Cross-Reference

Rust Crate Feature Python Equivalent Tutorial
PolicyEngine agent_os.policy Tutorial 01 โ€” Policy Engine
TrustManager agent_os.trust Tutorial 02 โ€” Trust & Identity
AuditLogger agent_os.audit Tutorial 04 โ€” Audit & Compliance
AgentIdentity agent_os.identity Tutorial 02 โ€” Trust & Identity
AgentMeshClient AgentMeshClient Tutorial 20 โ€” TypeScript SDK

Note: The Rust agentmesh crate wraps all governance features into a single crate, while the Python implementation splits them across separate agent_os.* modules. Policy YAML files work identically across all SDKs.


Source Files

Component Location
Main exports agent-governance-rust/agentmesh/src/lib.rs
Type definitions agent-governance-rust/agentmesh/src/types.rs
PolicyEngine agent-governance-rust/agentmesh/src/policy.rs
TrustManager agent-governance-rust/agentmesh/src/trust.rs
AuditLogger agent-governance-rust/agentmesh/src/audit.rs
AgentIdentity agent-governance-rust/agentmesh/src/identity.rs
Unit tests agent-governance-rust/agentmesh/src/*.rs
Package config agent-governance-rust/agentmesh/Cargo.toml

Next Steps

  • Run the tests to see the crate in action:
    cd agent-governance-rust
    cargo test --workspace
    
  • Load a YAML policy from the repository's policies/ directory and evaluate it against your agent
  • Enable trust persistence with persist_path to retain trust scores across process restarts
  • Verify audit chains in your CI/CD pipeline โ€” call audit.verify() as a post-deployment check
  • Explore the TypeScript SDK tutorial (Tutorial 20) for equivalent patterns in TypeScript