Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions contributing/samples/agent_governance/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Agent Governance Toolkit — GovernancePlugin for Google ADK

A governance plugin for [Google ADK](https://github.com/google/adk-python) that enforces
policy-as-code rules before tool execution, verifies agent identity, and produces
tamper-evident audit trails.

Built on the [Agent Governance Toolkit](https://github.com/microsoft/agent-governance-toolkit)
(v3.2.0, 9,500+ tests, MIT licensed).

## Features

- **Policy enforcement** — Evaluate YAML/OPA/Cedar policies before every tool call (<5ms)
- **Agent identity** — Zero-trust verification via Ed25519 + SPIFFE
- **Audit logging** — Merkle-chained tamper-evident action logs
- **Configurable** — Allow/deny/warn/require-approval actions per policy rules

## Install

```bash
pip install agentmesh-platform[server]
```

## Usage

```python
from google.adk.agents import Agent
from governance_plugin import GovernancePlugin

governance = GovernancePlugin(
policy_dir="./policies",
agent_did="did:mesh:my-agent",
)

agent = Agent(
name="governed-agent",
model="gemini-2.0-flash",
tools=[my_tool],
plugins=[governance],
)
```

## Policy Example (policies/default.yaml)

```yaml
apiVersion: governance.toolkit/v1
name: adk-agent-policy
rules:
- name: block-dangerous-tools
condition: "action in ['shell_exec', 'file_delete']"
action: deny
- name: rate-limit-api-calls
condition: "action == 'api_call'"
action: allow
limit: "100/hour"
```

## How It Works

The plugin hooks into ADK's `before_tool_call` lifecycle:

1. **Before tool execution** — GovernancePlugin evaluates the tool name + arguments against loaded policies
2. **Identity check** — Optionally verifies the calling agent's DID
3. **Decision** — Allow, deny, warn, or require approval
4. **Audit** — Logs the decision with Merkle hash chaining

## Links

- [Agent Governance Toolkit](https://github.com/microsoft/agent-governance-toolkit)
- [Policy-as-Code Tutorial](https://github.com/microsoft/agent-governance-toolkit/tree/main/docs/tutorials/policy-as-code)
- [OWASP Agentic Compliance](https://github.com/microsoft/agent-governance-toolkit/blob/main/docs/OWASP-COMPLIANCE.md)
125 changes: 125 additions & 0 deletions contributing/samples/agent_governance/governance_plugin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
# Copyright 2026 Microsoft Corporation
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can have a plugins/ folder under src/google/adk_community to host the plugin

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why Microsoft?

#
# Licensed under the MIT License.
"""
GovernancePlugin for Google ADK.

Enforces policy-as-code rules before tool execution, verifies agent
identity, and produces tamper-evident audit trails using the Agent
Governance Toolkit (https://github.com/microsoft/agent-governance-toolkit).
"""

from __future__ import annotations

import logging
from pathlib import Path
from typing import Any

logger = logging.getLogger(__name__)


class GovernancePlugin:
"""ADK plugin that enforces governance policies before tool execution.

Usage::

from governance_plugin import GovernancePlugin

governance = GovernancePlugin(policy_dir="./policies")
agent = Agent(name="my-agent", plugins=[governance])
"""

def __init__(
self,
policy_dir: str | Path = "./policies",
agent_did: str = "did:mesh:adk-agent",
sponsor_email: str = "adk-operator@example.com",
default_action: str = "allow",
) -> None:
self._policy_dir = Path(policy_dir)
self._agent_did = agent_did
self._sponsor_email = sponsor_email
self._default_action = default_action
self._engine = None
self._audit = None
self._setup()

def _setup(self) -> None:
"""Initialize AGT policy engine and audit service."""
try:
from agentmesh.governance.policy import PolicyEngine
from agentmesh.services.audit import AuditService

self._engine = PolicyEngine()
self._audit = AuditService()

if self._policy_dir.exists():
for f in sorted(self._policy_dir.glob("*.yaml")):
try:
self._engine.load_yaml(f.read_text())
logger.info("Loaded policy: %s", f.name)
except Exception as exc:
logger.warning("Skipped %s: %s", f.name, exc)

logger.info(
"GovernancePlugin initialized (agent=%s, policies=%s)",
self._agent_did,
self._policy_dir,
)
except ImportError:
logger.warning(
"agentmesh-platform not installed. "
"Install with: pip install agentmesh-platform"
)

def before_tool_call(
self,
tool_name: str,
args: dict[str, Any] | None = None,
**kwargs: Any,
) -> dict[str, Any]:
"""Evaluate governance policy before a tool call.

Returns:
Dict with 'allowed' (bool), 'decision', 'reason', and
'audit_entry_id'.
"""
if self._engine is None:
return {"allowed": True, "decision": "allow", "reason": "AGT not installed"}

context = {
"action": tool_name,
"tool_args": args or {},
**kwargs,
}

result = self._engine.evaluate(
agent_did=self._agent_did,
context=context,
)

if self._audit:
entry = self._audit.log_policy_decision(
agent_did=self._agent_did,
action=tool_name,
decision=result.action,
policy_name=result.policy_name or "",
data={"tool_args": args or {}, "reason": result.reason},
)
audit_id = entry.entry_id
else:
audit_id = None

return {
"allowed": result.allowed,
"decision": result.action,
"reason": result.reason,
"matched_rule": result.matched_rule,
"audit_entry_id": audit_id,
}

def get_audit_summary(self) -> dict[str, Any]:
"""Return audit service summary."""
if self._audit:
return self._audit.summary()
return {"error": "Audit service not initialized"}
46 changes: 46 additions & 0 deletions contributing/samples/agent_governance/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Copyright 2026 Microsoft Corporation
#
# Licensed under the MIT License.
"""
Example: Google ADK agent with Agent Governance Toolkit policy enforcement.

Demonstrates:
1. Loading YAML governance policies
2. Evaluating policies before tool calls
3. Producing tamper-evident audit trails
"""

from pathlib import Path

from google.adk.agents import Agent
from governance_plugin import GovernancePlugin


def create_governed_agent() -> Agent:
"""Create an ADK agent with governance controls."""
governance = GovernancePlugin(
policy_dir=Path(__file__).parent / "policies",
agent_did="did:mesh:adk-demo-agent",
)

# Example: check policy before a tool call
result = governance.before_tool_call(
tool_name="web_search",
args={"query": "latest AI safety research"},
)
print(f"Policy decision: {result['decision']} — {result['reason']}")

# Check audit trail
summary = governance.get_audit_summary()
print(f"Audit: {summary['total_entries']} entries, chain valid: {summary['chain_valid']}")

return Agent(
name="governed-research-agent",
model="gemini-2.0-flash",
instruction="You are a research assistant with governance controls.",
)


if __name__ == "__main__":
agent = create_governed_agent()
print(f"Agent '{agent.name}' created with governance enabled.")
25 changes: 25 additions & 0 deletions contributing/samples/agent_governance/policies/default.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
apiVersion: governance.toolkit/v1
name: adk-demo-policy
description: Example governance policy for Google ADK agents
rules:
- name: block-shell-execution
condition: "action in ['shell_exec', 'code_exec', 'file_delete']"
action: deny
description: Block dangerous system-level tool calls
priority: 100

- name: rate-limit-api-calls
condition: "action == 'api_call'"
action: allow
limit: "100/hour"
description: Rate limit external API calls
priority: 50

- name: require-approval-for-payments
condition: "action == 'process_payment'"
action: require_approval
approvers: ["admin@example.com"]
description: Payment actions require human approval
priority: 90

default_action: allow
Loading