Server SDK
Connect TypeScript agents to Edictum Console for centralized contract management, hot-reload via SSE, and server-backed audit.
Right page if: you want to connect a TypeScript agent to Edictum Console for centralized contract management, live hot-reload, or server-backed audit. Wrong page if: you want local-only enforcement without a server -- see https://docs.edictum.ai/docs/typescript. For Python server connection, see https://docs.edictum.ai/docs/console/connecting-agents. Gotcha: `createServerGuard()` is async and returns `{ guard, client, close }`. You must call `close()` on shutdown to flush audit events and stop the SSE watcher. HTTPS is required for non-loopback hosts unless `allowInsecure: true`.
Local YAML files work for development, but production agents need centralized contract management. The @edictum/server package connects TypeScript agents to Edictum Console -- contracts update via SSE without restarting the agent, audit events stream to the server, and approval workflows are handled server-side.
Getting Started
Install
pnpm add @edictum/core @edictum/server js-yamlCreate a server-connected guard
import { createServerGuard } from '@edictum/server'
const { guard, client, close } = await createServerGuard({
url: 'https://console.example.com',
apiKey: 'ed_live_...',
agentId: 'my-agent',
bundleName: 'production-contracts',
environment: 'production',
})Use with any adapter
import { VercelAIAdapter } from '@edictum/vercel-ai'
const adapter = new VercelAIAdapter(guard)
const result = await generateText({
model: openai('gpt-4o'),
tools: { readFile, runCommand },
prompt: 'Summarize the Q3 report',
...adapter.asCallbacks(),
})Clean up on shutdown
// Flush pending audit events, stop SSE watcher, close connections
await close()createServerGuard Options
| Option | Type | Default | Description |
|---|---|---|---|
url | string | required | Base URL of the Edictum Console server |
apiKey | string | required | API key for authentication |
agentId | string | required | Agent identifier registered with the server |
bundleName | string | null | null | Named bundle to fetch. If null, waits for server assignment |
environment | string | "production" | Environment name for contract filtering |
tags | Record<string, string> | null | Tags for server-side filtering |
mode | "enforce" | "observe" | bundle default | Override the bundle's defaults.mode |
autoWatch | boolean | true | Start SSE watcher for hot-reload |
verifySignatures | boolean | false | Verify Ed25519 signatures on bundles |
signingPublicKey | string | null | null | Ed25519 public key (hex) for verification |
allowInsecure | boolean | false | Allow plaintext HTTP to non-loopback hosts |
timeout | number | 30_000 | HTTP client timeout in ms |
maxRetries | number | 3 | HTTP max retries |
assignmentTimeout | number | 30_000 | Timeout for waiting for server assignment in ms |
onDeny | Function | null | Callback on tool denial |
onAllow | Function | null | Callback on tool approval |
onWatchError | Function | null | Callback for SSE watcher errors |
Hot-Reload via SSE
When autoWatch is enabled (the default), the guard subscribes to server-sent events. When a contract bundle is updated in the console, the new contracts are applied atomically -- no restart needed.
const { guard, close } = await createServerGuard({
url: 'https://console.example.com',
apiKey: 'ed_live_...',
agentId: 'my-agent',
bundleName: 'production-contracts',
onWatchError: (error) => {
console.error(`SSE error [${error.type}]: ${error.message}`)
},
})The onWatchError callback receives errors of four types:
| Type | Meaning |
|---|---|
signature_rejected | Bundle signature verification failed |
parse_error | Bundle YAML or base64 decoding failed |
fetch_error | HTTP request to fetch a bundle failed |
reload_error | Contract compilation or state replacement failed |
On any error, the guard retains its current contracts. The SSE watcher continues listening for the next update.
Server Assignment
If you omit bundleName, the guard starts empty and waits for the server to assign a bundle:
const { guard, close } = await createServerGuard({
url: 'https://console.example.com',
apiKey: 'ed_live_...',
agentId: 'my-agent',
// No bundleName -- server decides which contracts to assign
assignmentTimeout: 60_000,
})The guard polls for policyVersion until the SSE watcher delivers an assignment or assignmentTimeout expires. If the timeout is reached, createServerGuard throws EdictumConfigError.
bundleName is required when autoWatch is false. Without a named bundle and no SSE watcher, the guard has no contracts and createServerGuard throws immediately.
Ed25519 Bundle Verification
For tamper-proof contract delivery, enable signature verification:
const { guard, close } = await createServerGuard({
url: 'https://console.example.com',
apiKey: 'ed_live_...',
agentId: 'my-agent',
bundleName: 'production-contracts',
verifySignatures: true,
signingPublicKey: 'a1b2c3d4e5f6...', // hex-encoded Ed25519 public key
})Every bundle received -- both the initial fetch and subsequent SSE updates -- is verified against the public key. If verification fails, the bundle is rejected and the guard retains its current contracts. The onWatchError callback fires with type: 'signature_rejected'.
See bundle signing for key generation and server configuration.
Server Audit Sink
By default, createServerGuard configures a ServerAuditSink that streams audit events to the console. This is automatic -- you do not need to configure it separately.
To use a custom audit sink instead:
import { FileAuditSink } from '@edictum/core'
const { guard, close } = await createServerGuard({
url: 'https://console.example.com',
apiKey: 'ed_live_...',
agentId: 'my-agent',
bundleName: 'production-contracts',
auditSink: new FileAuditSink('audit.jsonl'),
})Low-Level Client
For advanced use cases, use EdictumServerClient directly:
import { EdictumServerClient } from '@edictum/server'
const client = new EdictumServerClient({
baseUrl: 'https://console.example.com',
apiKey: 'ed_live_...',
agentId: 'my-agent',
env: 'production',
})
// Fetch a bundle manually
const bundle = await client.get('/api/v1/bundles/my-bundle/current', {
env: 'production',
})TLS Enforcement
HTTPS is required when connecting to non-loopback hosts. This prevents API keys from being transmitted in plaintext. To connect to a local development server over HTTP:
// Only for local development -- never use in production
const { guard, close } = await createServerGuard({
url: 'http://localhost:8000',
apiKey: 'ed_dev_...',
agentId: 'my-agent',
bundleName: 'dev-contracts',
allowInsecure: true,
})Loopback addresses (localhost, 127.0.0.1, ::1) are allowed over HTTP without allowInsecure.
Next Steps
- Console setup -- deploy and configure Edictum Console
- Connecting agents -- server-side agent registration
- Hot-reload -- how SSE contract delivery works
- Bundle signing -- Ed25519 key management
- TypeScript adapters -- framework integrations
Last updated on