Skip to content

Go SDK

The official MeshGuard Go SDK for governing AI agents from Go applications.

Go Reference

Installation

bash
go get github.com/meshguard/meshguard-go

Requires Go 1.21+. No external dependencies -- only the standard library.

Quick Start

go
package main

import (
    "context"
    "fmt"
    "log"

    meshguard "github.com/meshguard/meshguard-go"
)

func main() {
    client := meshguard.NewClient(
        "https://dashboard.meshguard.app",
        "your-agent-token",
    )

    ctx := context.Background()

    // Check if an action is allowed (never errors on deny)
    decision, err := client.Check(ctx, "agent-1", "read:contacts", nil)
    if err != nil {
        log.Fatal(err)
    }
    if decision.Allowed {
        fmt.Println("Access granted")
    } else {
        fmt.Printf("Denied: %s\n", decision.Reason)
    }
}

Client Initialization

With Explicit Configuration

go
client := meshguard.NewClient(
    "https://dashboard.meshguard.app",
    "your-agent-token",
    meshguard.WithAdminToken("admin-token"),
    meshguard.WithTimeout(10 * time.Second),
    meshguard.WithTraceID("custom-trace-id"),
    meshguard.WithHTTPClient(customHTTPClient),
    meshguard.WithUserAgent("my-app/1.0"),
)

With Environment Variables

bash
export MESHGUARD_GATEWAY_URL="https://dashboard.meshguard.app"
export MESHGUARD_AGENT_TOKEN="your-agent-token"
export MESHGUARD_ADMIN_TOKEN="your-admin-token"
go
// Uses env vars as fallbacks when constructor args are empty
client := meshguard.NewClient("", "")

Configuration Options

OptionEnv VariableDefaultDescription
gatewayURL (arg)MESHGUARD_GATEWAY_URLhttps://dashboard.meshguard.appGateway URL
apiKey (arg)MESHGUARD_AGENT_TOKEN--Agent JWT token (required)
WithAdminToken()MESHGUARD_ADMIN_TOKEN--Admin token for management APIs
WithTimeout()--30sRequest timeout
WithTraceID()--auto-generatedTrace ID for correlation
WithHTTPClient()--default http.ClientCustom HTTP client
WithUserAgent()--meshguard-go/0.1.0User-Agent header

Policy Evaluation

Check(ctx, agentID, action, meta)

Non-error check -- returns a *PolicyDecision without returning an error on denial. Errors are reserved for transport failures and authentication problems.

go
decision, err := client.Check(ctx, "agent-1", "write:email", map[string]any{
    "recipient": "user@example.com",
})
if err != nil {
    // Transport or auth error
    log.Fatal(err)
}

fmt.Println(decision.Allowed)   // true or false
fmt.Println(decision.Action)    // "write:email"
fmt.Println(decision.Decision)  // "allow" or "deny"
fmt.Println(decision.Policy)    // Policy name that matched
fmt.Println(decision.Reason)    // Reason for denial (if denied)
fmt.Println(decision.TraceID)   // Trace ID for correlation

Enforce(ctx, agentID, action, meta)

Returns an error if the action is denied. The error is a *PolicyDeniedError that wraps ErrPolicyDenied.

go
err := client.Enforce(ctx, "agent-1", "delete:records", nil)
if errors.Is(err, meshguard.ErrPolicyDenied) {
    var pde *meshguard.PolicyDeniedError
    errors.As(err, &pde)
    fmt.Printf("Denied by %s: %s\n", pde.Policy, pde.Reason)
    return
}
if err != nil {
    log.Fatal(err)
}

// Action is allowed, proceed
deleteRecords()

Govern(ctx, agentID, action, meta, fn)

Checks the policy and only calls your function if the action is allowed. If the policy denies the action, fn is never called and a *PolicyDeniedError is returned.

go
err := client.Govern(ctx, "agent-1", "read:contacts", nil, func() error {
    contacts, err := db.FetchContacts()
    if err != nil {
        return err
    }
    fmt.Printf("Found %d contacts\n", len(contacts))
    return nil
})
if err != nil {
    log.Fatal(err)
}

Proxy Requests

Route HTTP requests through the MeshGuard governance proxy:

go
// GET request
resp, err := client.Get(ctx, "/api/users")
if err != nil {
    log.Fatal(err)
}
defer resp.Body.Close()

// POST request
resp, err = client.Post(ctx, "/api/users", strings.NewReader(`{"name":"Alice"}`))
if err != nil {
    log.Fatal(err)
}
defer resp.Body.Close()

// PUT request
resp, err = client.Put(ctx, "/api/records/123", strings.NewReader(`{"status":"updated"}`))

// DELETE request
resp, err = client.Delete(ctx, "/api/records/123")

// Generic request
resp, err = client.Request(ctx, http.MethodPatch, "/api/records/123", body)

Admin Operations

Admin methods require an admin token, set via WithAdminToken or the MESHGUARD_ADMIN_TOKEN environment variable.

go
client := meshguard.NewClient(
    "https://dashboard.meshguard.app",
    "agent-token",
    meshguard.WithAdminToken("admin-token"),
)

List Agents

go
agents, err := client.ListAgents(ctx)
if err != nil {
    log.Fatal(err)
}
for _, a := range agents {
    fmt.Printf("%s (%s)\n", a.Name, a.TrustTier)
}

Create Agent

go
agent, err := client.CreateAgent(ctx, meshguard.CreateAgentRequest{
    Name:      "my-agent",
    TrustTier: "verified",
    Tags:      []string{"production", "sales"},
    Capabilities: []string{"read:contacts", "write:email"},
})
if err != nil {
    log.Fatal(err)
}
fmt.Printf("Agent ID: %s\n", agent.ID)

Revoke Agent

go
err := client.RevokeAgent(ctx, "agent_abc123")

List Policies

go
policies, err := client.ListPolicies(ctx)
if err != nil {
    log.Fatal(err)
}
for _, p := range policies {
    fmt.Printf("%s: %d rules\n", p.Name, len(p.Rules))
}

Query Audit Log

go
// Recent entries
entries, err := client.AuditLog(ctx, meshguard.AuditQueryOptions{
    Limit: 50,
})

// Only denials
denials, err := client.AuditLog(ctx, meshguard.AuditQueryOptions{
    Limit:    100,
    Decision: "deny",
})

// By agent
agentLogs, err := client.AuditLog(ctx, meshguard.AuditQueryOptions{
    AgentID: "agent_abc123",
    Action:  "write:email",
})

for _, entry := range denials {
    fmt.Printf("%s: %s -> %s\n", entry.Timestamp, entry.Action, entry.Decision)
}

Error Handling

The SDK provides sentinel errors for use with errors.Is and a structured *PolicyDeniedError type:

go
import (
    "errors"
    meshguard "github.com/meshguard/meshguard-go"
)

err := client.Enforce(ctx, "agent-1", "dangerous:action", nil)

var pde *meshguard.PolicyDeniedError
if errors.As(err, &pde) {
    // Action blocked by policy
    fmt.Printf("Action: %s\n", pde.Action)
    fmt.Printf("Policy: %s\n", pde.Policy)
    fmt.Printf("Rule:   %s\n", pde.Rule)
    fmt.Printf("Reason: %s\n", pde.Reason)
} else if errors.Is(err, meshguard.ErrAuthentication) {
    // Invalid or expired token (HTTP 401)
    fmt.Println("Re-authenticate and retry")
} else if errors.Is(err, meshguard.ErrRateLimit) {
    // Rate limit exceeded (HTTP 429)
    fmt.Println("Too many requests")
} else if err != nil {
    // APIError or other transport error
    var apiErr *meshguard.APIError
    if errors.As(err, &apiErr) {
        fmt.Printf("HTTP %d: %s\n", apiErr.StatusCode, apiErr.Body)
    } else {
        fmt.Printf("Error: %v\n", err)
    }
}

Error Types

ErrorDescription
ErrAuthenticationInvalid or expired token (HTTP 401)
ErrPolicyDeniedAction denied by policy (HTTP 403)
ErrRateLimitRate limit exceeded (HTTP 429)
*PolicyDeniedErrorStructured denial details (Action, Policy, Rule, Reason); unwraps to ErrPolicyDenied
*APIErrorUnexpected HTTP error with StatusCode and Body

Health Check

go
if err := client.Health(ctx); err != nil {
    log.Printf("Gateway unhealthy: %v", err)
} else {
    fmt.Println("Gateway is healthy")
}

Types

PolicyDecision

go
type PolicyDecision struct {
    Allowed   bool      `json:"allowed"`
    Action    string    `json:"action"`
    Decision  string    `json:"decision"`   // "allow" or "deny"
    Policy    string    `json:"policy"`
    Rule      string    `json:"rule"`
    Reason    string    `json:"reason"`
    TraceID   string    `json:"traceId"`
    Timestamp time.Time `json:"timestamp"`
}

Agent

go
type Agent struct {
    ID           string    `json:"id"`
    Name         string    `json:"name"`
    TrustTier    string    `json:"trustTier"`
    Capabilities []string  `json:"capabilities"`
    Tags         []string  `json:"tags"`
    OrgID        string    `json:"orgId"`
    CreatedAt    time.Time `json:"createdAt"`
}

AuditEntry

go
type AuditEntry struct {
    ID        string         `json:"id"`
    Timestamp time.Time      `json:"timestamp"`
    Action    string         `json:"action"`
    Decision  string         `json:"decision"`
    AgentID   string         `json:"agentId"`
    Policy    string         `json:"policy"`
    Rule      string         `json:"rule"`
    Reason    string         `json:"reason"`
    Resource  string         `json:"resource"`
    Meta      map[string]any `json:"meta"`
}

Governance Control Plane for AI Agents