Tutorial 22 โ Go module (agentmesh)¶
Build governance-aware AI agents in Go. The agentmesh module provides Ed25519 cryptographic identity, trust scoring, declarative policy evaluation, and hash-chain audit logging โ all in a single go get.
Target runtime: Go 1.21+ Module:
github.com/microsoft/agent-governance-toolkit/agent-governance-golangPackage:agentmesh
What you'll learn¶
| Section | Topic |
|---|---|
| Quick Start | Evaluate a policy in 5 lines of Go |
| AgentMeshClient | Unified governance pipeline โ identity + trust + policy + audit |
| PolicyEngine | Declarative rules, YAML policies, rate limiting, approval |
| TrustManager | Trust scoring with decay, tiers, and file persistence |
| AuditLogger | Hash-chain audit logging and verification |
| AgentIdentity | Ed25519 key pairs, DIDs, signing, JSON serialisation |
| 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¶
- Go 1.21+
- Familiarity with Go modules (
go.mod) - Recommended: read Tutorial 01 โ Policy Engine for governance concepts
Installation¶
The module has a single external dependency โ gopkg.in/yaml.v3 for YAML policy parsing.
# Verify the install
go list -m github.com/microsoft/agent-governance-toolkit/agent-governance-golang
Quick Start¶
Five lines to create a governed agent:
package main
import (
"fmt"
agentmesh "github.com/microsoft/agent-governance-toolkit/agent-governance-golang"
)
func main() {
client, err := agentmesh.NewClient("my-agent")
if err != nil {
panic(err)
}
result, _ := client.ExecuteWithGovernance("data.read", nil)
fmt.Println("Allowed:", result.Allowed) // true
fmt.Println("Decision:", result.Decision) // allow
}
When no policy rules are provided, the default decision is deny โ secure by default.
Or configure at creation with functional options:
client, err := agentmesh.NewClient("my-agent",
agentmesh.WithCapabilities([]string{"data.read", "data.write"}),
agentmesh.WithPolicyRules([]agentmesh.PolicyRule{
{Action: "data.read", Effect: agentmesh.Allow},
{Action: "data.write", Effect: agentmesh.Allow},
{Action: "*", Effect: agentmesh.Deny},
}),
)
AgentMeshClient¶
AgentMeshClient is the recommended entry point. It wires together identity, trust, policy, and audit into a single governance-aware pipeline.
Creating a Client¶
// Default client โ generates identity, no policy rules (deny-all)
client, err := agentmesh.NewClient("analyst-001")
// Client with functional options
client, err := agentmesh.NewClient("analyst-001",
agentmesh.WithCapabilities([]string{"data.read", "search"}),
agentmesh.WithTrustConfig(agentmesh.TrustConfig{
InitialScore: 0.8,
DecayRate: 0.01,
RewardFactor: 1.0,
PenaltyFactor: 1.5,
TierThresholds: agentmesh.TierThresholds{High: 0.8, Medium: 0.5},
}),
agentmesh.WithPolicyRules([]agentmesh.PolicyRule{
{Action: "data.read", Effect: agentmesh.Allow},
{Action: "*", Effect: agentmesh.Deny},
}),
)
Functional Options¶
| Option | Description |
|---|---|
WithCapabilities([]string) | Set capabilities on the generated identity |
WithTrustConfig(TrustConfig) | Override default trust configuration |
WithPolicyRules([]PolicyRule) | Set initial policy rules |
Accessing Components¶
// Identity
fmt.Println("DID:", client.Identity.DID)
fmt.Println("Capabilities:", client.Identity.Capabilities)
// Trust
score := client.Trust.GetTrustScore(client.Identity.DID)
fmt.Println("Trust:", score.Overall, "Tier:", score.Tier)
// Audit
fmt.Println("Chain valid:", client.Audit.Verify())
The Governance Pipeline¶
ExecuteWithGovernance runs the full pipeline: evaluate โ log โ trust update.
result, err := client.ExecuteWithGovernance("data.read", nil)
fmt.Println("Allowed:", result.Allowed)
fmt.Println("Decision:", result.Decision)
fmt.Println("Trust:", result.TrustScore.Overall)
fmt.Println("Audit hash:", result.AuditEntry.Hash)
When the decision is Allow, trust increases. When it's Deny, trust decreases. The audit entry is always appended to the chain.
PolicyEngine¶
The PolicyEngine evaluates actions against a set of rules. Rules are evaluated in order; first match wins. The default decision when no rule matches is Deny.
ยง3.1 Policy Rules¶
rules := []agentmesh.PolicyRule{
{Action: "data.read", Effect: agentmesh.Allow},
{Action: "data.write", Effect: agentmesh.Allow},
{Action: "deploy.*", Effect: agentmesh.Review},
{Action: "shell.*", Effect: agentmesh.Deny},
{Action: "*", Effect: agentmesh.Deny}, // catch-all
}
engine := agentmesh.NewPolicyEngine(rules)
fmt.Println(engine.Evaluate("data.read", nil)) // allow
fmt.Println(engine.Evaluate("shell.exec", nil)) // deny
fmt.Println(engine.Evaluate("deploy.prod", nil)) // review
fmt.Println(engine.Evaluate("unknown", nil)) // deny (catch-all)
ยง3.2 Decision Types¶
| Decision | Constant | Description |
|---|---|---|
| Allow | agentmesh.Allow | Action is permitted |
| Deny | agentmesh.Deny | Action is blocked |
| Review | agentmesh.Review | Action requires human review |
| Rate Limit | agentmesh.RateLimit | Action is rate-limited |
| Requires Approval | agentmesh.RequiresApproval | Action needs explicit approval |
ยง3.3 Wildcard Patterns¶
The engine supports glob-style action matching:
| Pattern | Matches | Does Not Match |
|---|---|---|
* | Everything | โ |
data.* | data.read, data.write | shell.exec |
shell.* | shell.exec, shell.ls | data.read |
data.read | data.read (exact) | data.write |
ยง3.4 Conditional Rules¶
Rules can include conditions matched against a context map:
rules := []agentmesh.PolicyRule{
{
Action: "deploy.*",
Effect: agentmesh.Deny,
Conditions: map[string]interface{}{"environment": "production"},
},
{
Action: "deploy.*",
Effect: agentmesh.Allow,
},
}
engine := agentmesh.NewPolicyEngine(rules)
// Production deploys are denied
prodCtx := map[string]interface{}{"environment": "production"}
fmt.Println(engine.Evaluate("deploy.app", prodCtx)) // deny
// Staging deploys are allowed (conditions don't match first rule)
stagingCtx := map[string]interface{}{"environment": "staging"}
fmt.Println(engine.Evaluate("deploy.app", stagingCtx)) // allow
The engine supports $and, $or, $not, and comparison operators ($gt, $gte, $lt, $lte, $ne, $in) in conditions.
ยง3.5 Rate Limiting¶
Rules with MaxCalls and Window enable per-action rate limiting:
rules := []agentmesh.PolicyRule{
{
Action: "api.call",
Effect: agentmesh.Allow,
MaxCalls: 5,
Window: "1m", // 5 calls per minute
},
}
engine := agentmesh.NewPolicyEngine(rules)
for i := 0; i < 5; i++ {
fmt.Println(engine.Evaluate("api.call", nil)) // allow
}
fmt.Println(engine.Evaluate("api.call", nil)) // rate_limit
ยง3.6 Approval Requirements¶
rules := []agentmesh.PolicyRule{
{
Action: "deploy.production",
Effect: agentmesh.Allow,
MinApprovals: 2,
Approvers: []string{"lead", "sre"},
},
}
engine := agentmesh.NewPolicyEngine(rules)
fmt.Println(engine.Evaluate("deploy.production", nil)) // requires_approval
Loading Policies from YAML¶
Store policies in version-controlled YAML files:
# policies/governance.yaml
rules:
- action: "data.read"
effect: allow
- action: "data.write"
effect: allow
conditions:
role: admin
- action: "shell.*"
effect: deny
- action: "deploy.*"
effect: review
- action: "*"
effect: deny
engine := agentmesh.NewPolicyEngine(nil)
err := engine.LoadFromYAML("policies/governance.yaml")
if err != nil {
log.Fatalf("failed to load policy: %v", err)
}
fmt.Println(engine.Evaluate("data.read", nil)) // allow
Rules loaded from YAML are appended to any existing rules.
TrustManager¶
The TrustManager tracks per-agent trust scores on a 0.0โ1.0 scale with configurable tiers, decay, and file persistence.
ยง5.1 Trust Tiers¶
| Tier | Score Range | Description |
|---|---|---|
low | 0.0โ0.49 | Untrusted or new agent |
medium | 0.5โ0.79 | Provisional trust |
high | 0.8โ1.0 | Fully trusted |
ยง5.2 Basic Usage¶
tm := agentmesh.NewTrustManager(agentmesh.DefaultTrustConfig())
// New agent starts at 0.5 (medium tier)
score := tm.GetTrustScore("agent-x")
fmt.Println(score.Overall) // 0.5
fmt.Println(score.Tier) // medium
// Record successes โ trust increases
tm.RecordSuccess("agent-x", 0.05)
tm.RecordSuccess("agent-x", 0.05)
score = tm.GetTrustScore("agent-x")
fmt.Println(score.Overall) // ~0.59
// Record failure โ trust decreases (asymmetric: penalty factor = 1.5ร)
tm.RecordFailure("agent-x", 0.1)
score = tm.GetTrustScore("agent-x")
fmt.Println(score.Overall, score.Tier)
ยง5.3 Custom Configuration¶
cfg := agentmesh.TrustConfig{
InitialScore: 0.8,
DecayRate: 0.02,
RewardFactor: 1.0,
PenaltyFactor: 2.0,
TierThresholds: agentmesh.TierThresholds{
High: 0.8,
Medium: 0.5,
},
MinInteractions: 5,
}
tm := agentmesh.NewTrustManager(cfg)
score := tm.GetTrustScore("high-trust-agent")
fmt.Println(score.Overall) // 0.8
fmt.Println(score.Tier) // high
ยง5.4 Peer Verification¶
Verify a peer agent's identity and trust score in one call:
peer, _ := agentmesh.GenerateIdentity("peer-agent", nil)
result, err := tm.VerifyPeer("peer-agent", peer)
fmt.Println("Verified:", result.Verified)
fmt.Println("Score:", result.Score.Overall)
ยง5.5 File Persistence¶
Enable persistence to survive process restarts:
cfg := agentmesh.TrustConfig{
PersistPath: "trust-state.json",
// ... other config ...
}
tm := agentmesh.NewTrustManager(cfg)
// Scores are automatically saved after each update
tm.RecordSuccess("agent-x", 0.05)
// trust-state.json now contains the serialised score state
// On next startup, scores are loaded from disk automatically
tm2 := agentmesh.NewTrustManager(cfg)
score := tm2.GetTrustScore("agent-x")
fmt.Println(score.Overall) // restored 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.
ยง6.1 Logging Events¶
logger := agentmesh.NewAuditLogger()
entry := logger.Log("agent-001", "data.read", agentmesh.Allow)
fmt.Println("Hash:", entry.Hash)
fmt.Println("Prev:", entry.PreviousHash) // empty for genesis entry
ยง6.2 Hash-Chain Integrity¶
logger := agentmesh.NewAuditLogger()
logger.Log("agent-1", "data.read", agentmesh.Allow)
logger.Log("agent-1", "data.write", agentmesh.Deny)
logger.Log("agent-2", "report.send", agentmesh.Allow)
// Verify the entire chain
fmt.Println(logger.Verify()) // true
How the chain works:
Entry 0 Entry 1 Entry 2
โโโโโโโโโโโโ โโโโโโโโโโโโ โโโโโโโโโโโโ
โ hash: A โโโโโโโโถโ prev: A โโโโโโโโถโ prev: B โ
โ prev: "" โ โ hash: B โ โ hash: C โ
โโโโโโโโโโโโ โโโโโโโโโโโโ โโโโโโโโโโโโ
hash = SHA-256(timestamp | agentID | action | decision | previousHash)
ยง6.3 Retention Limits¶
Set MaxEntries to limit memory usage in long-running services:
logger := agentmesh.NewAuditLogger()
logger.MaxEntries = 1000 // keep last 1000 entries
// Old entries are evicted when the limit is exceeded
for i := 0; i < 1500; i++ {
logger.Log("agent", fmt.Sprintf("action-%d", i), agentmesh.Allow)
}
// Chain still verifies (eviction is chain-aware)
fmt.Println(logger.Verify()) // true
ยง6.4 Filtering and Querying¶
filter := agentmesh.AuditFilter{
AgentID: "agent-1",
}
entries := logger.GetEntries(filter)
fmt.Println("Agent-1 entries:", len(entries))
// Filter by decision
deny := agentmesh.Deny
filter = agentmesh.AuditFilter{
Decision: &deny,
}
denied := logger.GetEntries(filter)
fmt.Println("Denied entries:", len(denied))
ยง6.5 Exporting the Audit Trail¶
AgentIdentity¶
The AgentIdentity provides Ed25519-based cryptographic identity with DID identifiers and data signing.
ยง7.1 Generating an Identity¶
identity, err := agentmesh.GenerateIdentity(
"researcher-agent",
[]string{"data.read", "search"},
)
if err != nil {
log.Fatal(err)
}
fmt.Println("DID:", identity.DID) // did:agentmesh:researcher-agent
fmt.Println("Capabilities:", identity.Capabilities)
fmt.Println("Public key:", len(identity.PublicKey), "bytes") // 32 bytes
ยง7.2 Signing and Verifying¶
data := []byte("important message")
// Sign
signature, err := identity.Sign(data)
if err != nil {
log.Fatal(err)
}
fmt.Println("Signature:", len(signature), "bytes") // 64 bytes
// Verify
fmt.Println("Valid:", identity.Verify(data, signature)) // true
// Tampered data fails
fmt.Println("Tampered:", identity.Verify([]byte("wrong"), signature)) // false
ยง7.3 JSON Serialisation¶
Export the public portion of an identity for sharing:
jsonBytes, err := identity.ToJSON()
if err != nil {
log.Fatal(err)
}
fmt.Println(string(jsonBytes))
// {"did":"did:agentmesh:researcher-agent","public_key":"...","capabilities":["data.read","search"]}
// Reconstruct from JSON (public key only)
imported, err := agentmesh.FromJSON(jsonBytes)
fmt.Println("Imported DID:", imported.DID)
fmt.Println("Can verify:", imported.Verify(data, signature)) // true
Full Governance Pipeline¶
End-to-end example combining all subsystems:
package main
import (
"fmt"
"log"
agentmesh "github.com/microsoft/agent-governance-toolkit/agent-governance-golang"
)
func main() {
// 1. Create a governed client
client, err := agentmesh.NewClient("research-agent",
agentmesh.WithCapabilities([]string{"data.read", "search.web"}),
agentmesh.WithTrustConfig(agentmesh.TrustConfig{
InitialScore: 0.5,
DecayRate: 0.01,
RewardFactor: 1.0,
PenaltyFactor: 1.5,
TierThresholds: agentmesh.TierThresholds{High: 0.8, Medium: 0.5},
}),
agentmesh.WithPolicyRules([]agentmesh.PolicyRule{
{Action: "data.read", Effect: agentmesh.Allow},
{Action: "search.*", Effect: agentmesh.Allow},
{Action: "data.write", Effect: agentmesh.Review},
{Action: "*", Effect: agentmesh.Deny},
}),
)
if err != nil {
log.Fatal(err)
}
fmt.Println("Agent DID:", client.Identity.DID)
// 2. Execute governed actions
actions := []string{"data.read", "search.web", "data.write", "shell.exec"}
for _, action := range actions {
result, _ := client.ExecuteWithGovernance(action, nil)
status := "โ
allowed"
if !result.Allowed {
status = "โ denied"
}
fmt.Printf(" %s โ %s (trust: %.2f, tier: %s)\n",
action, status, result.TrustScore.Overall, result.TrustScore.Tier)
}
// 3. Verify audit chain
fmt.Println("\nAudit chain valid:", client.Audit.Verify())
// 4. Export audit trail
jsonStr, _ := client.Audit.ExportJSON()
fmt.Println("Audit JSON:", jsonStr[:80], "...")
}
Expected output:
Agent DID: did:agentmesh:research-agent
data.read โ โ
allowed (trust: 0.54, tier: medium)
search.web โ โ
allowed (trust: 0.59, tier: medium)
data.write โ โ denied (trust: 0.44, tier: low)
shell.exec โ โ denied (trust: 0.28, tier: low)
Audit chain valid: true
Audit JSON: [{"timestamp":"2025-07-15T10:30:00Z","agent_id":"did:agentmesh:resear ...
Cross-Reference¶
| Go module 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 package |
Note: The Go module uses a 0.0โ1.0 trust scale with three tiers, while the Rust crate uses 0โ1000 with five tiers. Both use the same governance concepts and YAML policy format.
Source Files¶
| Component | Location |
|---|---|
| Client + options | agent-governance-golang/client.go |
| Type definitions | agent-governance-golang/types.go |
PolicyEngine | agent-governance-golang/policy.go |
TrustManager | agent-governance-golang/trust.go |
AuditLogger | agent-governance-golang/audit.go |
AgentIdentity | agent-governance-golang/identity.go |
| Conflict resolution | agent-governance-golang/conflict.go |
| Metrics | agent-governance-golang/metrics.go |
| Tests | agent-governance-golang/*_test.go |
Next Steps¶
- Run the tests to see the module in action:
- Load YAML policies from the repository's
policies/directory - Enable trust persistence with
PersistPathto retain scores across restarts - Verify audit chains in your CI/CD pipeline โ call
Verify()as a post-deployment check - Explore the Rust crate tutorial (Tutorial 21) for the Rust equivalent
- Read the Python tutorials (01โ04) for detailed governance concepts