Skip to content
Merged
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
23 changes: 23 additions & 0 deletions deploy/kubernetes/governance-sidecar/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# OpenClaw.NET Governance Sidecar

These manifests show the intended deployment pattern for optional tool governance:

- OpenClaw.NET runs the gateway and native tool runtime.
- A governance sidecar runs in the same Pod.
- OpenClaw.NET calls the sidecar over `http://127.0.0.1:8088`.
- Policies are mounted from a ConfigMap.

Apply the example:

```bash
kubectl apply -f configmap-policy.yaml
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml
```

The example uses `/api/v1/execute` as the decision endpoint because that is OpenClaw.NET's default
adapter setting. Confirm the actual sidecar route and payload before production deployment, then
override `OpenClaw__Governance__DecisionEndpoint` if needed.

Governance is opt-in. If the sidecar is unavailable while governance is enabled, high-risk and
side-effecting tools fail closed by default.
35 changes: 35 additions & 0 deletions deploy/kubernetes/governance-sidecar/configmap-policy.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: openclaw-governance-policies
data:
policy.yaml: |
policies:
- id: require-approval-for-process-execution
match:
capabilities:
- process.execute
- code.execute
action: require_approval

- id: deny-external-export-from-low-trust-agent
match:
capabilities:
- external.http
- data.export
agentTrustBelow: 0.75
action: deny

- id: audit-filesystem-access
match:
capabilities:
- filesystem.read
- filesystem.write
action: audit_only

- id: require-approval-for-message-send
match:
capabilities:
- message.send
- email.send
action: require_approval
44 changes: 44 additions & 0 deletions deploy/kubernetes/governance-sidecar/deployment.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: openclaw-agent
spec:
replicas: 1
selector:
matchLabels:
app: openclaw-agent
template:
metadata:
labels:
app: openclaw-agent
spec:
containers:
- name: openclaw
image: ghcr.io/clawdotnet/openclaw-dotnet:latest
ports:
- containerPort: 8080
env:
- name: OpenClaw__Governance__Enabled
value: "true"
- name: OpenClaw__Governance__Provider
value: "http_sidecar"
- name: OpenClaw__Governance__SidecarBaseUrl
value: "http://127.0.0.1:8088"
- name: OpenClaw__Governance__DecisionEndpoint
value: "/api/v1/execute"
- name: OpenClaw__Governance__FailClosed
value: "true"
- name: OpenClaw__Governance__RequireGovernanceForHighRiskTools
value: "true"
- name: governance-sidecar
image: mcr.microsoft.com/agent-governance/agent-os-kernel:latest
ports:
- containerPort: 8088
volumeMounts:
- name: governance-policies
mountPath: /policies
readOnly: true
volumes:
- name: governance-policies
configMap:
name: openclaw-governance-policies
11 changes: 11 additions & 0 deletions deploy/kubernetes/governance-sidecar/service.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
apiVersion: v1
kind: Service
metadata:
name: openclaw-agent
spec:
selector:
app: openclaw-agent
ports:
- name: http
port: 80
targetPort: 8080
2 changes: 2 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ Use this page as the map. If you are unsure where to go next, the groups below a
| [MODEL_PROFILES.md](MODEL_PROFILES.md) | Provider-agnostic named model profiles, including Gemma-family setups. |
| [PROMPT_CACHING.md](PROMPT_CACHING.md) | Provider-aware prompt caching hints, dialects, diagnostics. |
| [PULSE.md](PULSE.md) | Runtime Pulse scheduled heartbeat turns, `HEARTBEAT.md`, alert suppression, and operator controls. |
| [governance/sidecar-pattern.md](governance/sidecar-pattern.md) | Optional central tool-governance middleware, sidecar flow, decisions, and audit fields. |
| [governance/microsoft-agent-governance.md](governance/microsoft-agent-governance.md) | Microsoft Agent Governance sidecar integration notes and deployment cautions. |

## Testing and Evaluation

Expand Down
4 changes: 4 additions & 0 deletions docs/SITE_MAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ Use this map when turning the Markdown docs into a documentation website. It kee
| Integrations | External Coding Backends | [external-coding-backends.md](external-coding-backends.md) |
| Operations | Security | [SECURITY.md](../SECURITY.md) |
| Operations | Payment Security | [security/payments.md](security/payments.md) |
| Operations | Tool Governance Sidecar | [governance/sidecar-pattern.md](governance/sidecar-pattern.md) |
| Operations | Microsoft Agent Governance | [governance/microsoft-agent-governance.md](governance/microsoft-agent-governance.md) |
| Operations | Releases | [RELEASES.md](RELEASES.md) |
| Operations | Docker Hub | [DOCKERHUB.md](DOCKERHUB.md) |
| Operations | Optional Sandboxing | [sandboxing.md](sandboxing.md) |
Expand Down Expand Up @@ -77,6 +79,8 @@ Integrations
Operations
Security
Payment Security
Tool Governance Sidecar
Microsoft Agent Governance
Releases
Docker Hub
Optional Sandboxing
Expand Down
72 changes: 72 additions & 0 deletions docs/governance/microsoft-agent-governance.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Microsoft Agent Governance Integration

OpenClaw.NET integrates with Microsoft Agent Governance through the generic
`IToolGovernanceService` boundary. The default implementation is an AOT-safe HTTP sidecar adapter,
so the gateway does not need to reference Microsoft preview packages or make governance a required
runtime dependency.

Microsoft public references currently show action interception and the .NET
`GovernanceKernel.EvaluateToolCall` API. The OpenClaw.NET sidecar adapter therefore keeps the
endpoint configurable instead of treating `/api/v1/execute` as a permanent Microsoft contract.

Useful upstream references:

- [microsoft/agent-governance-toolkit](https://github.com/microsoft/agent-governance-toolkit)
- [Agent OS README](https://github.com/microsoft/agent-governance-toolkit/blob/main/packages/agent-os/README.md)
- [Quickstart](https://github.com/microsoft/agent-governance-toolkit/blob/main/QUICKSTART.md)
- [FAQ](https://github.com/microsoft/agent-governance-toolkit/blob/main/FAQ.md)

## Local Configuration

```json
{
"OpenClaw": {
"Governance": {
"Enabled": true,
"Provider": "http_sidecar",
"SidecarBaseUrl": "http://127.0.0.1:8088",
"DecisionEndpoint": "/api/v1/execute",
"TimeoutMs": 300,
"AuditResults": true,
"FailClosed": true,
"RequireGovernanceForHighRiskTools": true
}
}
}
```

Keep governance disabled for normal local development unless you are running a compatible sidecar.
When governance is enabled, startup requires `SidecarBaseUrl` to be an absolute URL.

## Kubernetes Pattern

Run OpenClaw.NET and the governance sidecar in the same Pod and communicate over localhost. Mount
policies as a ConfigMap so the sidecar can enforce policy without an external policy service.

Example manifests live under:

```text
deploy/kubernetes/governance-sidecar/
```

## OWASP Agentic Top 10 Mapping

Governance decisions can help mitigate tool misuse, excessive agency, unsafe code execution,
data exfiltration, and weak auditability. Do not claim full OWASP Agentic Top 10 coverage unless
the policy set, tests, and deployment controls are mapped and verified for the target environment.

Recommended policy coverage for the first deployment:

| Capability | Suggested default |
| --- | --- |
| `process.execute` / `code.execute` | Require approval or deny. |
| `filesystem.write` | Require approval, scoped paths, and audit. |
| `external.http` / `data.export` | Deny for low-trust agents; audit all access. |
| `message.send` / `email.send` | Require approval unless the channel is tightly scoped. |
| Payment and home automation writes | Require approval and fail closed. |

## Future Direct Adapter

A future optional package such as `OpenClaw.Governance.Microsoft` can wrap the Microsoft .NET
`GovernanceKernel` directly. That package should remain optional and must not be referenced by the
default NativeAOT gateway target.
88 changes: 88 additions & 0 deletions docs/governance/sidecar-pattern.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# Tool Governance Sidecar Pattern

OpenClaw.NET enforces optional tool governance at one central point: `OpenClawToolExecutor`.
Registered, preset-allowed tool calls are evaluated before approval, sentinel substitution,
sandbox routing, or execution. Unknown tools and preset-blocked tools continue to fail locally
without calling the governance sidecar.

## Runtime Flow

```text
client
-> OpenClaw.NET gateway / agent runtime
-> OpenClawToolExecutor
-> IToolGovernanceService
-> optional HTTP sidecar decision endpoint
-> existing approval / hooks / sandbox / executor path
-> optional sidecar result audit endpoint
```

Governance is disabled by default. When enabled, the runtime sends only redacted tool arguments
and tool metadata to the sidecar. The sidecar decision is attached to the stored `ToolInvocation`,
tool audit entries, activity tags, and logs.

## Configuration

```json
{
"OpenClaw": {
"Governance": {
"Enabled": true,
"Provider": "http_sidecar",
"SidecarBaseUrl": "http://127.0.0.1:8088",
"DecisionEndpoint": "/api/v1/execute",
"ResultEndpoint": "/api/v1/result",
"TimeoutMs": 300,
"AuditResults": true,
"FailClosed": true,
"FailOpenReadOnlyLowRisk": false,
"RequireGovernanceForHighRiskTools": true
}
}
}
```

`DecisionEndpoint` defaults to `/api/v1/execute`, but this is an adapter setting, not a hard
OpenClaw.NET contract. Change it if the deployed sidecar uses another route or payload shape.

## Decision Behavior

Supported actions:

| Action | Runtime behavior |
| --- | --- |
| `allow` | Continue through the normal OpenClaw.NET tool path. |
| `deny` | Return a blocked tool result; the tool is not executed. |
| `require_approval` | Use the existing OpenClaw.NET approval callback. If no approval-capable surface is attached, execution is denied. |
| `redact` | Record redacted governance metadata. Execution arguments change only if the sidecar returns explicit replacement arguments. |
| `audit_only` | Continue and attach the governance decision to audit/trace data. |

Sidecar failures and timeouts fail closed by default. High-risk, write-capable, data-export,
network-write, shell, process, and code-execution tools fail closed when
`RequireGovernanceForHighRiskTools` is enabled. Read-only low-risk tools can fail open only when
`FailOpenReadOnlyLowRisk` is explicitly enabled.

## Tool Metadata

OpenClaw.NET keeps a central descriptor catalog for built-in and native tool names. Descriptors
include category, capabilities, risk level, approval hints, filesystem/network/code-execution
flags, and data-export flags. This metadata is sent to the sidecar so policies can reason about
capabilities instead of hardcoding every tool name.

Plugin and dynamic tools that are not in the catalog receive a conservative fallback descriptor:
medium risk, not read-only, and capability `plugin.invoke`.

## Audit Fields

Tool invocations and audit entries include:

- `GovernanceAllowed`
- `GovernanceAction`
- `GovernanceReason`
- `GovernancePolicyId`
- `GovernanceRuleId`
- `GovernanceTrustScore`
- `GovernanceEvaluationMs`

OpenClaw.NET does not send raw tool results to the sidecar result endpoint. Result audit payloads
include status, failure code/message, duration, timeout/failure flags, and result byte count.
4 changes: 3 additions & 1 deletion src/OpenClaw.Agent/AgentRuntime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,8 @@ public AgentRuntime(
Action<Session, string>? appendContractSnapshot = null,
ToolAuditLog? toolAuditLog = null,
IRedactionPipeline? redaction = null,
ISentinelSubstitutionService? sentinelSubstitution = null)
ISentinelSubstitutionService? sentinelSubstitution = null,
IToolGovernanceService? toolGovernance = null)
{
_chatClient = chatClient;
_tools = tools;
Expand Down Expand Up @@ -160,6 +161,7 @@ public AgentRuntime(
toolPresetResolver: toolPresetResolver,
redaction: _redaction,
sentinelSubstitution: _sentinelSubstitution,
toolGovernance: toolGovernance,
auditLog: toolAuditLog);
_sessionTokenBudget = sessionTokenBudget;
_estimateTokenBudgetAdmission = gatewayConfig?.EnableEstimatedTokenAdmissionControl ?? false;
Expand Down
1 change: 1 addition & 0 deletions src/OpenClaw.Agent/IAgentRuntimeFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public sealed class AgentRuntimeFactoryContext
public required bool RequireToolApproval { get; init; }
public required IReadOnlyList<string> ApprovalRequiredTools { get; init; }
public IToolSandbox? ToolSandbox { get; init; }
public IToolGovernanceService? ToolGovernance { get; init; }
public ToolUsageTracker? ToolUsageTracker { get; init; }
public ToolAuditLog? ToolAuditLog { get; init; }
public Func<Session, bool>? IsContractTokenBudgetExceeded { get; init; }
Expand Down
1 change: 1 addition & 0 deletions src/OpenClaw.Agent/NativeAgentRuntimeFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ private AgentRuntime CreateRuntime(
toolPresetResolver: context.Services.GetService(typeof(OpenClaw.Core.Abstractions.IToolPresetResolver)) as OpenClaw.Core.Abstractions.IToolPresetResolver,
redaction: context.Services.GetService(typeof(OpenClaw.Core.Security.IRedactionPipeline)) as OpenClaw.Core.Security.IRedactionPipeline,
sentinelSubstitution: context.Services.GetService(typeof(OpenClaw.Core.Security.ISentinelSubstitutionService)) as OpenClaw.Core.Security.ISentinelSubstitutionService,
toolGovernance: context.ToolGovernance,
isContractTokenBudgetExceeded: context.IsContractTokenBudgetExceeded,
isContractRuntimeBudgetExceeded: context.IsContractRuntimeBudgetExceeded,
recordContractTurnUsage: context.RecordContractTurnUsage,
Expand Down
Loading
Loading