Go Server SDK
Connect Go agents to Edictum Console for remote contract management, hot-reload via SSE, and Ed25519 bundle verification.
Right page if: you want to connect a Go agent to Edictum Console for remote contract management, SSE hot-reload, or bundle signing verification. Wrong page if: you want the Python server SDK -- see https://docs.edictum.ai/docs/console/connecting-agents. For self-hosting the console, see https://docs.edictum.ai/docs/console/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 Console for remote contract management. Contracts 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://console.edictum.ai",
os.Getenv("EDICTUM_API_KEY"),
"my-agent",
guard.WithBundleName("production-contracts"),
guard.WithAutoWatch(true),
)
if err != nil {
log.Fatal(err)
}
defer g.Close(ctx)
// g is ready -- contracts loaded from the server,
// SSE watcher running for live updates.FromServer fetches the named bundle, 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://console.edictum.ai",
APIKey: os.Getenv("EDICTUM_API_KEY"),
AgentID: "my-agent",
Env: "production",
BundleName: "production-contracts",
Timeout: 30 * time.Second,
MaxRetries: 3,
})
if err != nil {
log.Fatal(err)
}| Field | Type | Default | Description |
|---|---|---|---|
BaseURL | string | required | Console 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 bundle 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. Contract bundles 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 console:
approval := server.NewApprovalBackend(client)When a contract has effect: pending_approval, the approval backend sends the request to the console and polls for a decision.
Hot-Reload via SSE
The SSE watcher listens for contract_update events from the console. When the server pushes new contracts, 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 bundle name. The watcher fetches the full bundle 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 Bundle Verification
Verify that contract bundles 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("Bundle verification failed:", err)
}When configured on the SSE watcher via WithPublicKey, every incoming bundle is verified before reload. Bundles with invalid or missing signatures are rejected.
Using with FromServer
g, err := guard.FromServer(
"https://console.edictum.ai",
os.Getenv("EDICTUM_API_KEY"),
"my-agent",
guard.WithBundleName("production-contracts"),
guard.WithAutoWatch(true),
guard.WithVerifySignatures(true),
guard.WithSigningPublicKey("ab12cd34ef56..."),
)WithVerifySignatures(true) requires WithSigningPublicKey. The constructor returns an error if verification is enabled without a public key. This is intentional -- fail-closed, not fail-open.
Server-Assigned Mode
When you omit the bundle name, the server assigns a bundle based on agent tags:
g, err := guard.FromServer(
"https://console.edictum.ai",
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