Go Server SDK
Connect Go agents to Edictum Control Plane for remote rule management, hot-reload via SSE, and Ed25519 ruleset verification.
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)
}| Field | Type | Default | Description |
|---|---|---|---|
BaseURL | string | required | Control Plane server URL |
APIKey | string | required | API key for authentication |
AgentID | string | "default" | Agent identifier for audit and assignment |
Env | string | "production" | Environment name |
BundleName | string | "" | Named ruleset to fetch (empty = server-assigned) |
Tags | map[string]string | nil | Agent tags for server-assigned mode |
Timeout | time.Duration | 30s | HTTP request timeout |
MaxRetries | int | 3 | Retry count for GET requests (exponential backoff) |
AllowInsecure | bool | false | Allow 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