Skip to main content

What is an Agent?

An agent in Garnet is a lightweight monitoring component that runs on your infrastructure to collect security events and enforce network policies. Agents are the eyes and ears of your security monitoring system, detecting suspicious behavior and network anomalies in real-time.

Agent Types

Garnet supports three types of agents, each designed for specific deployment contexts:

GitHub Agent

Monitors GitHub Actions workflows and repositories.
type AgentKindGithub AgentKind = "github"
Use cases:
  • Monitor CI/CD pipeline security
  • Detect suspicious network activity in build processes
  • Track workflow execution patterns

Kubernetes Agent

Monitors Kubernetes clusters, nodes, and workloads.
type AgentKindKubernetes AgentKind = "kubernetes"
Use cases:
  • Cluster-wide security monitoring
  • Node-level network policy enforcement
  • Container runtime security

Vanilla Agent

Generic agent for standard server environments.
type AgentKindVanilla AgentKind = "vanilla"
Use cases:
  • Traditional server monitoring
  • Virtual machine security
  • Custom deployment scenarios

Agent Structure

Every agent contains core system information and context-specific details:
type Agent struct {
    ID                string                  `json:"id"`
    ProjectID         string                  `json:"project_id"`
    OS                string                  `json:"os"`
    Arch              string                  `json:"arch"`
    Hostname          string                  `json:"hostname"`
    Version           string                  `json:"version"`
    IP                string                  `json:"ip"`
    MachineID         string                  `json:"machine_id"`
    Labels            AgentLabels             `json:"labels"`
    Kind              AgentKind               `json:"kind"`
    ContextID         string                  `json:"context_id"`
    GithubContext     *AgentGithubContext     `json:"github_context,omitempty"`
    KubernetesContext *AgentKubernetesContext `json:"kubernetes_context,omitempty"`
    VanillaContext    *AgentVanillaContext    `json:"vanilla_context,omitempty"`
    NetworkPolicy     *MergedNetworkPolicy    `json:"network_policy,omitempty"`
    Active            bool                    `json:"active"`
    LastSeen          time.Time               `json:"last_seen"`
    CreatedAt         time.Time               `json:"created_at"`
    UpdatedAt         time.Time               `json:"updated_at"`
}

Core Fields

  • OS: Operating system (e.g., “linux”, “darwin”)
  • Arch: CPU architecture (e.g., “amd64”, “arm64”)
  • Hostname: Machine hostname
  • Version: Agent software version
  • IP: Agent IP address (must be valid IPv4 or IPv6)
  • MachineID: Unique machine identifier
Each agent type has specific context requirements:Kubernetes Context:
type AgentKubernetesContext struct {
    ID        string    `json:"id"`
    Cluster   string    `json:"cluster"`
    Namespace string    `json:"namespace"`
    Node      string    `json:"node"`
    CreatedAt time.Time `json:"created_at"`
    UpdatedAt time.Time `json:"updated_at"`
}
The cluster and node names must follow Kubernetes naming conventions.
Labels are key-value pairs for organizing and filtering agents:
type AgentLabels map[string]string
Example usage:
labels := types.AgentLabels{
    "env":      "production",
    "team":     "backend",
    "region":   "us-east-1",
}
Labels can be used in queries:
?label.env=production&label.team=backend

Creating an Agent

To register a new agent with Garnet:
import (
    "context"
    "github.com/garnet-org/api/client"
    "github.com/garnet-org/api/types"
)

func createAgent() {
    c := client.New("your-api-key")
    
    agent := types.CreateAgent{
        OS:        "linux",
        Arch:      "amd64",
        Hostname:  "web-server-01",
        Version:   "1.0.0",
        IP:        "192.168.1.100",
        MachineID: "unique-machine-id",
        Kind:      types.AgentKindKubernetes,
        Labels: types.AgentLabels{
            "env":     "production",
            "cluster": "main",
        },
        KubernetesContext: &types.AgentKubernetesContext{
            Cluster:   "production-cluster",
            Namespace: "default",
            Node:      "node-1",
        },
    }
    
    result, err := c.CreateAgent(context.Background(), agent)
    if err != nil {
        // Handle error
    }
    
    // Store the agent token securely
    agentToken := result.AgentToken
    
    // The agent receives its network policy immediately
    networkPolicy := result.NetworkPolicy
}
The CreateAgent response includes an AgentToken that must be used for all subsequent API calls from that agent. Store this token securely.

Listing Agents

Query agents with powerful filtering options:
func listAgents(projectID string) {
    c := client.New("your-api-key")
    
    listParams := types.ListAgents{
        ProjectID: projectID,
        Active:    boolPtr(true),  // Only active agents
        Kinds:     []types.AgentKind{types.AgentKindKubernetes},
        Labels: types.AgentLabels{
            "env": "production",
        },
        PageArgs: types.CursorPageArgs{
            Limit: intPtr(50),
        },
    }
    
    page, err := c.Agents(context.Background(), listParams)
    if err != nil {
        // Handle error
    }
    
    for _, agent := range page.Data {
        fmt.Printf("Agent: %s (%s)\n", agent.Hostname, agent.ID)
    }
}

Available Filters

System Filters

  • Active: Filter by active status
  • OS: Operating system
  • Arch: CPU architecture
  • Hostname: Machine hostname
  • Version: Agent version
  • IP: IP address
  • MachineID: Machine identifier

Metadata Filters

  • Kinds: Agent types (can specify multiple)
  • Labels: Key-value label filters
  • TimeStart: Filter by creation time start
  • TimeEnd: Filter by creation time end

Agent Lifecycle

Registration

  1. Agent starts and collects system information
  2. Sends registration request with context details
  3. Receives agent ID and authentication token
  4. Receives initial network policy configuration

Heartbeat

Agents must send periodic heartbeats to maintain their Active status:
func sendHeartbeat() {
    c := client.New("agent-token")
    
    err := c.AgentHeartbeat(context.Background())
    if err != nil {
        // Handle error
    }
}
The heartbeat updates the agent’s LastSeen timestamp. Agents that haven’t sent a heartbeat recently are marked as inactive.

Updating Agent Information

Agent metadata can be updated as needed:
func updateAgent(agentID string) {
    c := client.New("your-api-key")
    
    newVersion := "1.1.0"
    update := types.UpdateAgent{
        Version: &newVersion,
    }
    
    err := c.UpdateAgent(context.Background(), agentID, update)
    if err != nil {
        // Handle error
    }
}

Deregistration

Remove an agent from monitoring:
func deleteAgent(agentID string) {
    c := client.New("your-api-key")
    
    err := c.DeleteAgent(context.Background(), agentID)
    if err != nil {
        // Handle error
    }
}

Agent Counts and Statistics

Retrieve aggregate statistics about your agents:
func getAgentCounts(projectID string) {
    c := client.New("your-api-key")
    
    params := types.RetrieveAgentsCounts{
        ProjectID: projectID,
        Kind:      &types.AgentKindKubernetes,
    }
    
    counts, err := c.AgentsCounts(context.Background(), params)
    if err != nil {
        // Handle error
    }
    
    fmt.Printf("Total: %d, Active: %d, Inactive: %d\n",
        counts.Total, counts.Active, counts.Inactive)
}
The AgentsCounts response includes:
  • Total: Total number of agents
  • Active: Currently active agents
  • Inactive: Inactive agents
  • CreatedSince: Agents created since specified time (optional)
  • At: Timestamp of the count

Network Policy Integration

Agents automatically receive and enforce network policies based on their context. The merged network policy combines:
  1. System-wide global policies
  2. Project-level global policies
  3. Context-specific policies (repo, workflow, cluster, or node)
When an agent is created or updated, it automatically receives the appropriate merged network policy for its context. See Network Policies for more details.

Best Practices

  • Store agent tokens securely (never in code or logs)
  • Rotate agent tokens periodically
  • Use separate agents for different environments
  • Apply appropriate labels for access control
  • Use consistent label naming conventions
  • Include environment (env), team, and region labels
  • Add deployment-specific labels (cluster, namespace, etc.)
  • Use labels for organizing and filtering in queries
  • Monitor agent heartbeat status
  • Track agent version distribution
  • Alert on inactive agents
  • Review agent counts regularly
  • Keep agent software up-to-date
  • Remove decommissioned agents promptly
  • Validate context information accuracy
  • Review and update labels as infrastructure changes

Error Handling

Common Errors

ErrAgentNotFound (types/agent.go:34)
ErrAgentNotFound = errs.NotFoundError("agent not found")
Returned when querying a non-existent agent. ErrUnauthorizedAgent (types/agent.go:31)
ErrUnauthorizedAgent = errs.UnauthorizedError("permission denied")
Returned when accessing an agent without proper permissions. ErrInvalidAgentType (types/agent.go:232)
ErrInvalidAgentType = errs.InvalidArgumentError("invalid agent kind")
Returned when the agent kind is not one of the supported types.

Build docs developers (and LLMs) love