Edictum
Go SDK

Go Server SDK

Connect Go agents to Edictum Control Plane for remote rule management, hot-reload via SSE, and Ed25519 ruleset verification.

AI Assistance

Right page if: you want to connect a Go agent to Edictum Control Plane for remote rule management, SSE hot-reload, or ruleset signing verification. Wrong page if: you want the Python server SDK -- see https://docs.edictum.ai/docs/control-plane/connecting-agents. For self-hosting the control plane, see https://docs.edictum.ai/docs/control-plane/self-hosting. Gotcha: HTTPS is required by default for non-loopback hosts. Use AllowInsecure only for local development -- never in production.

The Go Server SDK connects agents to Edictum Control Plane for remote rule management. Rulesets are fetched over HTTP, updated in real time via SSE, and optionally verified with Ed25519 signatures. The agent never needs a local YAML file.

Quick Start

import (
	"log"
	"os"

	"github.com/edictum-ai/edictum-go/guard"
)

g, err := guard.FromServer(
	"https://control-plane.example.com",
	os.Getenv("EDICTUM_API_KEY"),
	"my-agent",
	guard.WithBundleName("production-rulesets"),
	guard.WithAutoWatch(true),
)
if err != nil {
	log.Fatal(err)
}
defer g.Close(ctx)

// g is ready -- rulesets loaded from the server,
// SSE watcher running for live updates.

FromServer fetches the named ruleset, compiles it, starts the SSE watcher, and returns a fully configured guard. The guard automatically provisions server-backed audit, session storage, and approval backends.

Client Configuration

For fine-grained control, create the server client directly:

import "github.com/edictum-ai/edictum-go/server"

client, err := server.NewClient(server.ClientConfig{
	BaseURL:    "https://control-plane.example.com",
	APIKey:     os.Getenv("EDICTUM_API_KEY"),
	AgentID:    "my-agent",
	Env:        "production",
	BundleName: "production-rulesets",
	Timeout:    30 * time.Second,
	MaxRetries: 3,
})
if err != nil {
	log.Fatal(err)
}
FieldTypeDefaultDescription
BaseURLstringrequiredControl Plane server URL
APIKeystringrequiredAPI key for authentication
AgentIDstring"default"Agent identifier for audit and assignment
Envstring"production"Environment name
BundleNamestring""Named ruleset to fetch (empty = server-assigned)
Tagsmap[string]stringnilAgent tags for server-assigned mode
Timeouttime.Duration30sHTTP request timeout
MaxRetriesint3Retry count for GET requests (exponential backoff)
AllowInsecureboolfalseAllow HTTP for non-loopback hosts

HTTPS is enforced by default. The client rejects http:// URLs for non-loopback hosts. Set AllowInsecure: true only for local development -- never in production. Rulesets contain your security policy; transmitting them over plaintext is a vulnerability.

Server-Backed Components

When using FromServer, three server-backed components are automatically provisioned:

Audit Sink

Batched delivery of audit events to the server:

sink := server.NewAuditSink(client,
	server.WithBatchSize(50),
	server.WithFlushInterval(5*time.Second),
	server.WithMaxBufferSize(10_000),
)
defer sink.Close(ctx)

Events are buffered and flushed when the batch is full or on a timer. Buffer overflow drops the oldest events. Failed flushes restore events to the buffer front (fail-closed), except for permanent 4xx errors which are dropped to prevent infinite retry loops.

Session Backend

HTTP-backed session storage for cross-turn state:

backend := server.NewBackend(client)

Session counters (tool call counts, per-tool caps) are stored on the server, enabling session limits across distributed agent instances.

Approval Backend

HITL approval requests routed to the control plane:

approval := server.NewApprovalBackend(client)

When a rule has action: ask, the approval backend sends the request to the control plane and polls for a decision.

Hot-Reload via SSE

The SSE watcher listens for ruleset_updated events from the control plane. When the server pushes new rulesets, the guard reloads without downtime.

watcher := server.NewSSEWatcher(client, myGuard,
	server.WithPublicKey("ab12cd34..."),  // optional Ed25519 verification
	server.WithReconnectDelay(1*time.Second),
	server.WithMaxReconnectDelay(60*time.Second),
)

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

go watcher.Watch(ctx) // blocks until ctx is cancelled or watcher.Close()

The watcher handles two event types:

  • Regular update: YAML is in the event data (raw or base64-encoded). Reloads directly.
  • Assignment change: server pushes a new ruleset name. The watcher fetches the full ruleset from the server, then reloads.

Reconnection uses exponential backoff. If a connection was stable (>30 seconds), the backoff resets.

Using FromServer with WithAutoWatch(true)? The SSE watcher is started automatically. You do not need to create one manually.

Ed25519 Ruleset Verification

Verify that rulesets have not been tampered with in transit:

import "github.com/edictum-ai/edictum-go/server"

err := server.VerifyBundleSignature(
	yamlBytes,                    // raw YAML content
	signatureB64,                 // base64-encoded Ed25519 signature
	"ab12cd34ef56...",            // hex-encoded Ed25519 public key
)
if err != nil {
	log.Fatal("Ruleset verification failed:", err)
}

When configured on the SSE watcher via WithPublicKey, every incoming ruleset is verified before reload. Rulesets with invalid or missing signatures are rejected.

Using with FromServer

g, err := guard.FromServer(
	"https://control-plane.example.com",
	os.Getenv("EDICTUM_API_KEY"),
	"my-agent",
	guard.WithBundleName("production-rulesets"),
	guard.WithAutoWatch(true),
	guard.WithVerifySignatures("ab12cd34ef56..."),
)

WithVerifySignatures(pubKeyHex) enables Ed25519 verification. Pass the hex-encoded public key directly -- there is no separate WithSigningPublicKey option. This is intentional -- verification cannot be enabled without a key (fail-closed, not fail-open).

Server-Assigned Mode

When you omit the bundle name, the server assigns a ruleset based on agent tags:

g, err := guard.FromServer(
	"https://control-plane.example.com",
	os.Getenv("EDICTUM_API_KEY"),
	"my-agent",
	guard.WithAutoWatch(true),          // required for server-assigned mode
	guard.WithTags(map[string]string{
		"team": "platform",
		"tier": "production",
	}),
)

FromServer starts the SSE watcher and waits up to 30 seconds for the server to push an assignment. If no assignment arrives, it returns an error. Once assigned, the guard reloads on assignment changes automatically.

Cleanup

Always close the guard when done to stop the SSE watcher and flush buffered audit events:

defer g.Close(ctx)

Next Steps

Last updated on

On this page