Skip to content

Go Client

The client package is a convenience wrapper around the HTTP API. It provides typed Go functions for every endpoint so you don’t have to manage HTTP requests, response parsing, or error handling yourself.

You still need to run the warden binary as a server — the client talks to it over HTTP.

import "github.com/thesimonho/warden/client"
c := client.New("http://localhost:8090")
projects, err := c.ListProjects(ctx)
if err != nil {
return err
}
for _, proj := range projects {
fmt.Printf("%s (%s): %s\n", proj.Name, proj.AgentType, proj.State)
}

Each project includes an AgentType field ("claude-code" or "codex") that identifies which CLI agent it runs.

resp, err := c.CreateContainer(ctx, projectID, "claude-code", api.CreateContainerRequest{
ProjectPath: "/home/user/projects/my-app",
ProjectName: "my-app",
NetworkMode: api.NetworkModeFull,
EnabledRuntimes: []string{"node", "python", "go"},
})

The EnabledRuntimes field specifies which language runtimes to install in the container. Available runtimes are auto-detected from the project and provide network domains and environment variables.

resp, err := c.CreateWorktree(ctx, projectID, "claude-code", "feature-branch")
if err != nil {
return err
}
fmt.Printf("Created worktree: %s\n", resp.WorktreeID)

The agentType parameter identifies which agent the project runs ("claude-code" or "codex"). When creating a worktree for an existing project, use the same agent type as the project.

The client exposes the same four management actions as the web and TUI dashboards:

// Reset all cost tracking data for a project.
err := c.ResetProjectCosts(ctx, projectID, agentType)
// Purge all audit events for a project.
err := c.PurgeProjectAudit(ctx, projectID, agentType)
// Delete a project's container (stop + remove).
_, err := c.DeleteContainer(ctx, projectID, agentType)
// Remove a project from Warden (untrack).
_, err := c.RemoveProject(ctx, projectID, agentType)

All management operations require both the projectID and agentType to uniquely identify the project.

// Read a .warden.json template from an arbitrary path (for import).
tmpl, err := c.ReadProjectTemplate(ctx, "/path/to/.warden.json")
if err != nil {
return err
}
fmt.Printf("Image: %s, Network: %s\n", tmpl.Image, tmpl.NetworkMode)
// Check if the server is running.
resp, err := http.Get("http://localhost:8090/api/v1/health")
// resp.Header.Get("X-Warden") == "1" confirms it's a Warden server.
// Gracefully shut down the server.
err := c.Shutdown(ctx)

HTTP errors are wrapped in client.APIError with machine-readable codes:

var apiErr *client.APIError
if errors.As(err, &apiErr) {
fmt.Printf("API error %d [%s]: %s\n", apiErr.StatusCode, apiErr.Code, apiErr.Message)
switch apiErr.Code {
case "NAME_TAKEN":
// Handle name collision
case "NOT_FOUND":
// Handle missing resource
}
}

See the HTTP API error codes for the full list.

ApproachSetupUse when
client.New() (HTTP wrapper)Run warden binary separatelyMulti-process, remote server, or when the binary is already running
warden.New() (Layer 1 import)No binary neededSingle-process deployment, embedded applications, full control

If you don’t want to run a separate server process, you can import the library directly — see the Go Library guide.

This is the same approach the TUI binary uses internally. The TUI wraps the service via a Client interface and delegates to it. See internal/tui/ for the reference implementation — the Client interface shows the abstraction boundary, and ServiceAdapter shows how to adapt the Layer 1 service to the interface for embedded use.