Thoth SDK
sdk v0.1.6 / proxy v0.2.7
SDKs

Go SDK

Full reference for the Thoth Go SDK — Config, NewClient(), WrapTool(), WrapToolFunc(), StartSession(), Session, PolicyViolationError, StepUpRequiredError.

Installation

go get github.com/atensecurity/thoth-go

Go 1.25+ required. The SDK has no required external dependencies beyond the Go standard library.

Config

Config holds configuration for the Thoth client. APIURL is required (directly or via THOTH_API_URL). Other string fields support environment-variable fallbacks.

type Config struct {
    // APIKey is the Thoth API key for hosted authentication.
    // Env fallback: THOTH_API_KEY.
    APIKey string
 
    // TenantID is your organization's Thoth tenant identifier.
    // Env fallback: THOTH_TENANT_ID.
    TenantID string
 
    // AgentID is the unique name for this agent instance.
    // Env fallback: THOTH_AGENT_ID.
    AgentID string
 
    // APIURL overrides the Thoth API base URL.
    // Required (or set THOTH_API_URL).
    // Used for both /v1/enforce and /v1/events/batch.
    // Env fallback: THOTH_API_URL.
    APIURL string
 
    // Environment scopes policy lookup (for example, dev vs prod).
    // Default: "prod".
    // Env fallback: THOTH_ENV.
    Environment string
 
    // UserID identifies the user on whose behalf the agent is acting.
    // Env fallback: THOTH_USER_ID.
    UserID string
 
    // ApprovedScope lists tool names authorized for this agent.
    // Env fallback: THOTH_APPROVED_SCOPE (comma-delimited).
    ApprovedScope []string
 
    // SessionIntent declares workflow purpose for HIPAA minimum-necessary checks.
    // Env fallback: THOTH_SESSION_INTENT.
    SessionIntent string
 
    // EnforcementTraceID sets an explicit cross-service correlation ID.
    // Env fallback: THOTH_ENFORCEMENT_TRACE_ID.
    EnforcementTraceID string
 
    // Timeout is the HTTP timeout for enforcer calls. Default: 5s.
    Timeout time.Duration
 
    // Enforcement controls how policy violations are handled.
    // Values: "observe" | "progressive" | "step_up" | "block"
    // Default: "progressive".
    Enforcement string
}

Enforcement values

ValueBehavior
"observe"Log only; never block or step-up
"progressive"Escalating enforcement (default)
"step_up"Require human approval for out-of-scope calls
"block"Immediately reject with PolicyViolationError

NewClient()

Initialize a Thoth client. Returns an error only if required configuration is missing after env var fallbacks are applied.

client, err := thoth.NewClient(thoth.Config{
    APIKey:      os.Getenv("THOTH_API_KEY"),
    APIURL:      os.Getenv("THOTH_API_URL"),
    TenantID:    "acme-corp",
    AgentID:     "invoice-processor-v2",
    Enforcement: "progressive",
})
if err != nil {
    log.Fatal(err)
}
defer client.Close()

Close()

Close() flushes buffered behavioral events and releases HTTP resources. Always defer it.

defer client.Close()

WrapTool()

Wrap a string-in / string-out tool function with Thoth governance. This is the most common variant for LLM tool calling, where both input and output are plain strings.

func (c *Client) WrapTool(
    name string,
    fn func(ctx context.Context, input string) (string, error),
) func(ctx context.Context, input string) (string, error)

Enforcement semantics

  • ALLOW — the tool executes normally; a behavioral event is emitted.
  • BLOCK — execution is prevented; *PolicyViolationError is returned.
  • STEP_UP — execution pauses; the enforcer waits for human approval. On timeout or denial, *PolicyViolationError is returned.
  • Enforcer unreachable — execution is blocked (fail-closed) and a policy violation error is returned.

Example

searchDocs := client.WrapTool("search_docs", func(ctx context.Context, query string) (string, error) {
    // your business logic
    return mySearch(ctx, query)
})
 
result, err := searchDocs(ctx, "quarterly earnings")
if err != nil {
    var pve *thoth.PolicyViolationError
    if errors.As(err, &pve) {
        log.Printf("blocked: %s (id=%s)", pve.Reason, pve.ViolationID)
        return
    }
    log.Fatal(err)
}
fmt.Println(result)

WrapToolFunc()

Wrap a map-based tool function with Thoth governance. Use this variant when your LLM framework passes tool arguments as map[string]any (e.g. OpenAI function calling, Anthropic tool use).

type ToolFunc func(ctx context.Context, args map[string]any) (any, error)
 
func (c *Client) WrapToolFunc(name string, fn ToolFunc) ToolFunc

Example

readFile := client.WrapToolFunc("read_file", func(ctx context.Context, args map[string]any) (any, error) {
    path, _ := args["path"].(string)
    content, err := os.ReadFile(path)
    if err != nil {
        return nil, err
    }
    return string(content), nil
})
 
result, err := readFile(ctx, map[string]any{"path": "/tmp/invoice.pdf"})
if err != nil {
    var pve *thoth.PolicyViolationError
    if errors.As(err, &pve) {
        log.Printf("blocked: %s", pve.Reason)
    }
}

StartSession()

Create a new agent session with its own isolated tool-call history and tracer. Recommended for per-request isolation in servers.

func (c *Client) StartSession(ctx context.Context, agentID, sessionID string) (*Session, error)
ParameterDescription
agentIDOverride the agent ID for this session. Pass "" to use the client's agent ID.
sessionIDCustom session ID. Pass "" to auto-generate a UUID.

Example

sess, err := client.StartSession(ctx, "", requestID)
if err != nil {
    return err
}
defer sess.Close()
 
govSearch := sess.WrapTool("search_docs", searchFn)
govPayment := sess.WrapToolFunc("submit_payment", paymentFn)

Session

Session represents an active agent session.

type Session struct {
    ID string  // UUID — the session identifier included in all behavioral events
    // unexported fields
}

Session.WrapTool()

Identical to Client.WrapTool() but scoped to this session's tool-call history.

func (s *Session) WrapTool(
    name string,
    fn func(ctx context.Context, input string) (string, error),
) func(ctx context.Context, input string) (string, error)

Session.WrapToolFunc()

Identical to Client.WrapToolFunc() but scoped to this session.

func (s *Session) WrapToolFunc(name string, fn ToolFunc) ToolFunc

Session.Close()

Emits a session_end behavioral event and marks the session as closed. Idempotent.

func (s *Session) Close()

PolicyViolationError

Returned when the enforcer blocks a tool call.

type PolicyViolationError struct {
    ToolName    string  // the name of the blocked tool
    Reason      string  // human-readable explanation
    ViolationID string  // audit ID for Thoth evidence records
}
 
func (e *PolicyViolationError) Error() string

Handling

result, err := govTool(ctx, input)
if err != nil {
    var pve *thoth.PolicyViolationError
    if errors.As(err, &pve) {
        // Log to your observability system
        slog.Warn("thoth policy violation",
            "tool", pve.ToolName,
            "reason", pve.Reason,
            "violation_id", pve.ViolationID,
        )
        // Return safe default or surface to caller
        return ErrActionRequiresApproval
    }
    return fmt.Errorf("tool error: %w", err)
}

StepUpRequiredError

Exported for step-up approval workflows where callers choose to handle approval out-of-band. Current WrapTool / WrapToolFunc behavior waits inline and surfaces timeout/deny as PolicyViolationError, so StepUpRequiredError is not emitted by those wrappers today.

type StepUpRequiredError struct {
    ToolName  string  // the name of the tool pending approval
    HoldToken string  // opaque token for the Thoth step-up API
    Reason    string  // why step-up was triggered
}
 
func (e *StepUpRequiredError) Error() string

Complete example

package main
 
import (
    "context"
    "errors"
    "fmt"
    "log/slog"
    "net/http"
    "os"
    "time"
 
    "github.com/atensecurity/thoth-go"
)
 
func main() {
    client, err := thoth.NewClient(thoth.Config{
        // APIKey, TenantID, AgentID read from env vars if omitted
        APIURL:      os.Getenv("THOTH_API_URL"),
        Enforcement: "progressive",
        Timeout:     10 * time.Second,
    })
    if err != nil {
        slog.Error("failed to init thoth", "err", err)
        os.Exit(1)
    }
    defer client.Close()
 
    // Serve an HTTP endpoint that processes each request in its own session
    http.HandleFunc("/process", func(w http.ResponseWriter, r *http.Request) {
        sess, err := client.StartSession(r.Context(), "", r.Header.Get("X-Request-ID"))
        if err != nil {
            http.Error(w, "internal error", 500)
            return
        }
        defer sess.Close()
 
        govSearch := sess.WrapTool("search_docs", func(ctx context.Context, query string) (string, error) {
            return "search results for: " + query, nil
        })
        govSubmit := sess.WrapToolFunc("submit_payment", func(ctx context.Context, args map[string]any) (any, error) {
            return "payment submitted", nil
        })
 
        // Use tools in your agent logic
        result, err := govSearch(r.Context(), r.URL.Query().Get("q"))
        if err != nil {
            var pve *thoth.PolicyViolationError
            if errors.As(err, &pve) {
                http.Error(w, "action blocked by policy: "+pve.Reason, http.StatusForbidden)
                return
            }
            http.Error(w, err.Error(), 500)
            return
        }
 
        _ = govSubmit // use similarly
        fmt.Fprintln(w, result)
    })
 
    slog.Info("listening on :8080")
    if err := http.ListenAndServe(":8080", nil); err != nil {
        slog.Error("server error", "err", err)
        os.Exit(1)
    }
}

Environment Variables

VariableDescription
THOTH_API_KEYAPI key for hosted Thoth authentication
THOTH_TENANT_IDDefault tenant ID
THOTH_AGENT_IDDefault agent ID
THOTH_API_URLRequired tenant API base URL used for both enforcement and event ingestion
THOTH_ENVPolicy environment scope (default: prod)
THOTH_USER_IDDefault user ID for policy evaluation
THOTH_APPROVED_SCOPEComma-delimited default approved tool list
THOTH_SESSION_INTENTSession intent for HIPAA minimum-necessary checks
THOTH_ENFORCEMENT_TRACE_IDExplicit correlation ID for enforcement requests

On this page