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
8 changes: 7 additions & 1 deletion .cspell-repo-terms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,23 @@ healthz
IATP
idweb
kanish
Landlock
LangChain
LangGraph
LlamaIndex
manylinux
markdown
Microsoft
Molty
Moltbook
Molty
msinternal
NemoClaw
Nemotron
networkx
npmjs
OpenAI
OpenClaw
openshell
ospo
plotly
pmcrepo
Expand All @@ -40,12 +44,14 @@ pypdf
pyproject
rrdatas
SCAK
seccomp
SemanticKernel
serde
spacy
spellcheck
spellchecking
streamlit
syscall
vnet
workflow
workflows
3 changes: 2 additions & 1 deletion docs/integrations/openshell.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,8 @@ OpenShell can *host* the sidecar. The governance sidecar runs inside or alongsid

## Related

- [OpenClaw Skill](../../packages/agentmesh-integrations/openclaw-skill/) — Lightweight skill for OpenClaw agents
- [OpenShell Governance Skill](../../packages/agentmesh-integrations/openshell-skill/) — Python skill package for OpenShell agents
- [Runnable Example](../../examples/openshell-governed/) — Self-contained demo with policy enforcement
- [OpenClaw Sidecar Deployment](../deployment/openclaw-sidecar.md) — AKS and Docker Compose guide
- [NVIDIA OpenShell](https://github.com/NVIDIA/OpenShell) — Runtime sandbox for AI agents
- [Architecture](../ARCHITECTURE.md) — Full toolkit architecture
4 changes: 2 additions & 2 deletions examples/openshell-governed/policies/sandbox-policy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ rules:
priority: 95
message: File writes restricted to /workspace
- name: allow-file-read
condition: {field: action, operator: starts_with, value: "file:read"}
condition: {field: action, operator: matches, value: "^file:read"}
action: allow
priority: 90
- name: allow-workspace-write
condition: {field: action, operator: starts_with, value: "file:write:/workspace"}
condition: {field: action, operator: matches, value: "^file:write:/workspace"}
action: allow
priority: 85
- name: allow-safe-shell
Expand Down
66 changes: 65 additions & 1 deletion packages/agent-mesh/sdks/go/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# AgentMesh Go SDK

Go SDK for the AgentMesh governance framework — identity, trust scoring, policy evaluation, and tamper-evident audit logging.
Go SDK for the AgentMesh governance framework — identity, trust scoring, policy evaluation, tamper-evident audit logging, MCP security scanning, execution privilege rings, and agent lifecycle management.

## Install

Expand Down Expand Up @@ -97,6 +97,70 @@ Unified governance client combining all modules.
| `NewClient(agentID, ...Option)` | Create a full client |
| `(*AgentMeshClient).ExecuteWithGovernance(action, params)` | Run action through governance pipeline |

### MCP Security (`mcp.go`)

Detects tool poisoning, typosquatting, hidden instructions, and rug-pull patterns in MCP tool definitions.

| Function / Method | Description |
|---|---|
| `NewMcpSecurityScanner()` | Create a new MCP security scanner |
| `(*McpSecurityScanner).Scan(tool)` | Scan a single tool definition |
| `(*McpSecurityScanner).ScanAll(tools)` | Scan multiple tool definitions |

```go
scanner := agentmesh.NewMcpSecurityScanner()
result := scanner.Scan(agentmesh.McpToolDefinition{
Name: "search",
Description: "Search the web.",
})
fmt.Printf("Safe: %v, Risk: %d\n", result.Safe, result.RiskScore)
```

### Execution Rings (`rings.go`)

Privilege ring model for agent access control (Ring 0 = Admin … Ring 3 = Sandboxed).

| Function / Method | Description |
|---|---|
| `NewRingEnforcer()` | Create a ring enforcer |
| `(*RingEnforcer).Assign(agentID, ring)` | Place an agent in a ring |
| `(*RingEnforcer).GetRing(agentID)` | Get an agent's ring |
| `(*RingEnforcer).CheckAccess(agentID, action)` | Check if action is allowed |
| `(*RingEnforcer).SetRingPermissions(ring, actions)` | Configure ring permissions |

```go
enforcer := agentmesh.NewRingEnforcer()
enforcer.SetRingPermissions(agentmesh.RingStandard, []string{"data.read", "data.write"})
enforcer.Assign("agent-1", agentmesh.RingStandard)
fmt.Println(enforcer.CheckAccess("agent-1", "data.read")) // true
```

### Lifecycle (`lifecycle.go`)

Eight-state lifecycle model with validated transitions.

States: `provisioning` → `active` → `suspended` / `rotating` / `degraded` / `quarantined` → `decommissioning` → `decommissioned`

| Function / Method | Description |
|---|---|
| `NewLifecycleManager(agentID)` | Create a lifecycle manager (starts provisioning) |
| `(*LifecycleManager).State()` | Get current state |
| `(*LifecycleManager).Events()` | Get transition history |
| `(*LifecycleManager).Transition(to, reason, by)` | Perform a validated transition |
| `(*LifecycleManager).CanTransition(to)` | Check if transition is valid |
| `(*LifecycleManager).Activate(reason)` | Convenience: move to active |
| `(*LifecycleManager).Suspend(reason)` | Convenience: move to suspended |
| `(*LifecycleManager).Quarantine(reason)` | Convenience: move to quarantined |
| `(*LifecycleManager).Decommission(reason)` | Convenience: start decommissioning |

```go
lm := agentmesh.NewLifecycleManager("agent-1")
lm.Activate("provisioned")
lm.Suspend("maintenance window")
lm.Activate("maintenance complete")
fmt.Println(lm.State()) // active
```

## License

See repository root [LICENSE](../../LICENSE).
138 changes: 138 additions & 0 deletions packages/agent-mesh/sdks/go/lifecycle.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

package agentmesh

import (
"fmt"
"sync"
"time"
)

// LifecycleState represents an agent's current lifecycle phase.
type LifecycleState string

const (
StateProvisioning LifecycleState = "provisioning"
StateActive LifecycleState = "active"
StateSuspended LifecycleState = "suspended"
StateRotating LifecycleState = "rotating"
StateDegraded LifecycleState = "degraded"
StateQuarantined LifecycleState = "quarantined"
StateDecommissioning LifecycleState = "decommissioning"
StateDecommissioned LifecycleState = "decommissioned"
)

// validTransitions defines the state machine for agent lifecycle.
var validTransitions = map[LifecycleState][]LifecycleState{
StateProvisioning: {StateActive, StateQuarantined, StateDecommissioning},
StateActive: {StateSuspended, StateRotating, StateDegraded, StateQuarantined, StateDecommissioning},
StateSuspended: {StateActive, StateQuarantined, StateDecommissioning},
StateRotating: {StateActive, StateDegraded, StateQuarantined},
StateDegraded: {StateActive, StateQuarantined, StateDecommissioning},
StateQuarantined: {StateActive, StateDecommissioning},
StateDecommissioning: {StateDecommissioned},
StateDecommissioned: {},
}

// LifecycleEvent records a single state transition.
type LifecycleEvent struct {
From LifecycleState `json:"from"`
To LifecycleState `json:"to"`
Reason string `json:"reason"`
InitiatedBy string `json:"initiated_by"`
Timestamp time.Time `json:"timestamp"`
}

// LifecycleManager manages state transitions for a single agent.
type LifecycleManager struct {
mu sync.RWMutex
agentID string
state LifecycleState
events []LifecycleEvent
}

// NewLifecycleManager creates a manager starting in the provisioning state.
func NewLifecycleManager(agentID string) *LifecycleManager {
return &LifecycleManager{
agentID: agentID,
state: StateProvisioning,
}
}

// State returns the current lifecycle state.
func (m *LifecycleManager) State() LifecycleState {
m.mu.RLock()
defer m.mu.RUnlock()
return m.state
}

// Events returns a copy of the transition history.
func (m *LifecycleManager) Events() []LifecycleEvent {
m.mu.RLock()
defer m.mu.RUnlock()
out := make([]LifecycleEvent, len(m.events))
copy(out, m.events)
return out
}

// Transition moves the agent to a new state if the transition is valid.
func (m *LifecycleManager) Transition(to LifecycleState, reason, initiatedBy string) (*LifecycleEvent, error) {
m.mu.Lock()
defer m.mu.Unlock()

if !m.canTransitionLocked(to) {
return nil, fmt.Errorf("invalid transition from %s to %s", m.state, to)
}

event := LifecycleEvent{
From: m.state,
To: to,
Reason: reason,
InitiatedBy: initiatedBy,
Timestamp: time.Now().UTC(),
}
m.state = to
m.events = append(m.events, event)
return &event, nil
}

// CanTransition reports whether a transition to the given state is allowed.
func (m *LifecycleManager) CanTransition(to LifecycleState) bool {
m.mu.RLock()
defer m.mu.RUnlock()
return m.canTransitionLocked(to)
}

func (m *LifecycleManager) canTransitionLocked(to LifecycleState) bool {
allowed, ok := validTransitions[m.state]
if !ok {
return false
}
for _, s := range allowed {
if s == to {
return true
}
}
return false
}

// Activate is a convenience method to move to the active state.
func (m *LifecycleManager) Activate(reason string) (*LifecycleEvent, error) {
return m.Transition(StateActive, reason, "system")
}

// Suspend is a convenience method to move to the suspended state.
func (m *LifecycleManager) Suspend(reason string) (*LifecycleEvent, error) {
return m.Transition(StateSuspended, reason, "system")
}

// Quarantine is a convenience method to move to the quarantined state.
func (m *LifecycleManager) Quarantine(reason string) (*LifecycleEvent, error) {
return m.Transition(StateQuarantined, reason, "system")
}

// Decommission is a convenience method to start decommissioning.
func (m *LifecycleManager) Decommission(reason string) (*LifecycleEvent, error) {
return m.Transition(StateDecommissioning, reason, "system")
}
Loading
Loading