Skip to main content

Installation

Install the Garnet API SDK using Go modules:
go get github.com/garnet-org/api
The SDK requires Go 1.25.0 or later. Update your go.mod file accordingly.

Authentication

Before using the SDK, you’ll need an authentication token from the Garnet platform. There are three types of tokens available:
User tokens are used for general API access. Get your token from the Garnet dashboard.
import "github.com/garnet-org/api/client"

// Initialize with user token
c := client.New("", "your-user-token")
The client automatically uses the default API endpoint (https://api.garnet.ai) when the base URL is empty.
For self-hosted Garnet instances, specify your custom base URL:
c := client.New("https://garnet.yourcompany.com", "your-token")

Your First API Call

Let’s create a new security agent and retrieve it:
1

Initialize the Client

Create a new client instance with your project token:
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/garnet-org/api/client"
    "github.com/garnet-org/api/types"
)

func main() {
    // Initialize client with project token
    c := client.New("", "").WithProjectToken("your-project-token")
    ctx := context.Background()
}
2

Create an Agent

Register a new agent with the Garnet platform:
// Define agent configuration
newAgent := types.CreateAgent{
    Kind:      types.AgentKindVanilla,
    OS:        "linux",
    Arch:      "amd64",
    Hostname:  "web-server-1",
    Version:   "1.0.0",
    IP:        "10.0.1.42",
    MachineID: "srv-a1b2c3d4",
    Labels: types.AgentLabels{
        "environment": "production",
        "region":      "us-east-1",
        "team":        "platform",
    },
    VanillaContext: &types.AgentVanillaContext{
        Environment: "production",
    },
}

// Create the agent
created, err := c.CreateAgent(ctx, newAgent)
if err != nil {
    log.Fatalf("Failed to create agent: %v", err)
}

fmt.Printf("Agent created with ID: %s\n", created.ID)
fmt.Printf("Agent token: %s\n", created.AgentToken)
The AgentToken returned should be stored securely and used by your agent for subsequent API calls.
3

Retrieve the Agent

Fetch the agent details using its ID:
// Retrieve agent by ID
agent, err := c.Agent(ctx, created.ID)
if err != nil {
    log.Fatalf("Failed to retrieve agent: %v", err)
}

fmt.Printf("Agent: %s\n", agent.Hostname)
fmt.Printf("Status: Active=%v\n", agent.Active)
fmt.Printf("Last Seen: %s\n", agent.LastSeen)
fmt.Printf("Labels: %+v\n", agent.Labels)

Complete Example

Here’s a complete working example that creates an agent, lists all agents in a project, and sends a heartbeat:
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/garnet-org/api/client"
    "github.com/garnet-org/api/types"
)

func main() {
    // Initialize client with project token for agent creation
    projectClient := client.New("", "").WithProjectToken("your-project-token")
    ctx := context.Background()

    // Create a new agent
    newAgent := types.CreateAgent{
        Kind:      types.AgentKindVanilla,
        OS:        "linux",
        Arch:      "amd64",
        Hostname:  "api-server-1",
        Version:   "1.0.0",
        IP:        "10.0.1.50",
        MachineID: "srv-xyz789",
        Labels: types.AgentLabels{
            "environment": "staging",
            "service":     "api",
        },
        VanillaContext: &types.AgentVanillaContext{
            Environment: "staging",
        },
    }

    created, err := projectClient.CreateAgent(ctx, newAgent)
    if err != nil {
        log.Fatalf("Failed to create agent: %v", err)
    }

    fmt.Printf("✓ Agent created: %s\n", created.ID)

    // Switch to agent token for agent operations
    agentClient := client.New("", "").WithAgentToken(created.AgentToken)

    // Send heartbeat to mark agent as active
    err = agentClient.AgentHeartbeat(ctx)
    if err != nil {
        log.Fatalf("Failed to send heartbeat: %v", err)
    }

    fmt.Println("✓ Heartbeat sent successfully")

    // List all agents in the project (using project token)
    projectID := "your-project-id"
    first := uint(10)
    
    agents, err := projectClient.Agents(ctx, types.ListAgents{
        ProjectID: projectID,
        PageArgs: types.CursorPageArgs{
            First: &first,
        },
    })
    if err != nil {
        log.Fatalf("Failed to list agents: %v", err)
    }

    fmt.Printf("\n✓ Found %d agents:\n", agents.PageInfo.TotalCount)
    for _, agent := range agents.Items {
        fmt.Printf("  - %s (%s) - Active: %v\n", 
            agent.Hostname, agent.ID, agent.Active)
    }
}
Never commit authentication tokens to version control. Use environment variables or a secure secret management system.

Filtering and Pagination

List agents with filters and pagination:
// Helper function to create pointer to uint
func ptr(i uint) *uint { return &i }
func ptrString(s string) *string { return &s }

// Filter by labels and environment
active := true
first := uint(50)

result, err := c.Agents(ctx, types.ListAgents{
    ProjectID: "proj_abc123",
    Active:    &active,                          // Only active agents
    OS:        ptrString("linux"),               // Linux only
    Labels: types.AgentLabels{                   // With specific labels
        "environment": "production",
    },
    PageArgs: types.CursorPageArgs{
        First: &first,                           // First 50 results
    },
})
if err != nil {
    log.Fatal(err)
}

fmt.Printf("Total agents: %d\n", result.PageInfo.TotalCount)
for _, agent := range result.Items {
    fmt.Printf("%s - %s/%s\n", agent.Hostname, agent.OS, agent.Arch)
}

// Fetch next page if available
if result.PageInfo.HasNextPage {
    nextResult, err := c.Agents(ctx, types.ListAgents{
        ProjectID: "proj_abc123",
        PageArgs: types.CursorPageArgs{
            First: &first,
            After: result.PageInfo.EndCursor,
        },
    })
    // Process next page...
    _ = nextResult
    _ = err
}

Working with Events

Ingest and query security events from your agents:
import "time"

// Ingest an event (using agent token)
agentClient := client.New("", "").WithAgentToken("agent-token")

event := types.CreateOrUpdateEventV2{
    // Event data in ashkaal format
    // See ashkaal documentation for event schema
}

result, err := agentClient.IngestEventV2(ctx, event)
if err != nil {
    log.Fatalf("Failed to ingest event: %v", err)
}

fmt.Printf("Event ingested: %s\n", result.ID)

// Query events with filters
page := 1
perPage := 20
timeStart := time.Now().Add(-24 * time.Hour)

events, err := c.Events(ctx, types.ListEvents{
    Filters: &types.EventFilters{
        AgentID:   ptrString("agent_123"),
        TimeStart: &timeStart,
    },
    PageArgs: types.PageArgs{
        Page:    &page,
        PerPage: &perPage,
    },
})
if err != nil {
    log.Fatal(err)
}

for _, evt := range events.Data {
    fmt.Printf("Event: %s at %s\n", evt.ID, evt.CreatedAt)
}

Updating Agents

Update agent metadata and configuration:
// Update agent hostname and labels
newHostname := "web-server-1-updated"

update := types.UpdateAgent{
    Hostname: &newHostname,
}

err := c.UpdateAgent(ctx, "agent_123", update)
if err != nil {
    log.Fatalf("Failed to update agent: %v", err)
}

fmt.Println("Agent updated successfully")

Error Handling Best Practices

Always handle errors appropriately:
import "errors"

// Check for specific error types
agent, err := c.Agent(ctx, agentID)
if err != nil {
    switch {
    case errors.Is(err, types.ErrAgentNotFound):
        fmt.Println("Agent not found")
        return
    case errors.Is(err, types.ErrUnauthorizedAgent):
        fmt.Println("Permission denied")
        return
    default:
        log.Fatalf("Unexpected error: %v", err)
    }
}

fmt.Printf("Found agent: %s\n", agent.Hostname)

Enabling Debug Mode

Enable debug mode to see raw HTTP requests and responses:
c := client.New("", "your-token")
c.Debug = true

// All subsequent requests will print debug information
agent, err := c.Agent(ctx, "agent_123")
Debug mode prints the full HTTP request including headers and body. Avoid using it in production with sensitive data.

Next Steps

Agent Management

Learn advanced agent management techniques

Event Monitoring

Deep dive into event ingestion and querying

Network Policies

Implement security policies across your infrastructure

API Reference

Explore the complete API reference

Common Patterns

Agent Heartbeat Loop

Keep your agent marked as active with periodic heartbeats:
import "time"

func runHeartbeatLoop(ctx context.Context, c *client.Client) {
    ticker := time.NewTicker(30 * time.Second)
    defer ticker.Stop()

    for {
        select {
        case <-ticker.C:
            if err := c.AgentHeartbeat(ctx); err != nil {
                log.Printf("Heartbeat failed: %v", err)
            }
        case <-ctx.Done():
            return
        }
    }
}

Paginating Through All Results

Iterate through all pages of results:
func getAllAgents(ctx context.Context, c *client.Client, projectID string) ([]types.Agent, error) {
    var allAgents []types.Agent
    first := uint(100)
    var after *string

    for {
        result, err := c.Agents(ctx, types.ListAgents{
            ProjectID: projectID,
            PageArgs: types.CursorPageArgs{
                First: &first,
                After: after,
            },
        })
        if err != nil {
            return nil, err
        }

        allAgents = append(allAgents, result.Items...)

        if !result.PageInfo.HasNextPage {
            break
        }
        after = result.PageInfo.EndCursor
    }

    return allAgents, nil
}
For large datasets, consider processing pages incrementally rather than loading everything into memory.

Build docs developers (and LLMs) love