Edictum
Edictum Console

Contract Management

The three-level contract model for Edictum Console -- Contract (authoring unit), Composition (assembly recipe), and Bundle (deployed artifact).

AI Assistance

Right page if: you need to understand the console's three-level contract model -- Contract (authoring unit), Composition (assembly recipe), Bundle (deployed artifact). Wrong page if: you want the YAML syntax for writing contracts (see https://docs.edictum.ai/docs/contracts/yaml-reference) or how bundles reach agents (see https://docs.edictum.ai/docs/console/concepts/hot-reload). Gotcha: compositions reference the latest version of each contract at assembly time -- pinning to a specific contract version is not supported.

Edictum Console uses a three-level model for contract management: Contract (authoring unit), Composition (assembly recipe), and Bundle (deployed artifact). This separation lets you write contracts once, reuse them across environments, and change enforcement modes without editing YAML.

The Three Levels

Contract Library
block-dotenv
type: pre
v3
pii-redact
type: post
v1
rate-limit
type: session
v2
Composition
defaults_mode: enforce
1block-dotenvmode: inherit (→ enforce)
2pii-redactmode: observe
3rate-limitmode: inherit (→ enforce)
"production-standard"
Assemble + Sign
Bundle
Assembled YAML
Ed25519 signature
SHA-256 revision hash
Pushed to agents via SSE on deploy

Contract (Authoring Unit)

A contract is an individual governance rule stored in the console's contract library. It is the smallest unit of governance -- one rule, one purpose.

FieldDescription
contract_idUser-provided string slug (format: [a-z0-9][a-z0-9_-]*)
typepre, post, session, or sandbox
nameHuman-readable identifier
descriptionWhat this contract does and why
definitionThe contract body as JSON (maps to YAML contract fields: tool, when, then, within, etc.)
tagsFree-form labels for organization (["security", "pii", "production"])
versionAuto-incremented on each update

Contracts are versioned. Every update creates a new version. Old versions are preserved -- you can always see what a contract looked like at any point in time.

YAML auto-fill. Pasting YAML into the contract editor automatically populates the id, name, type, description, and tags fields from the contract definition. Auto-fill only runs on the first paste — it will not overwrite fields you have already filled in.

Contract types map directly to the core library:

TypeWhen it runsCan deny?Example
preBefore tool executesYesBlock reads of .env files
postAfter tool executesRedact/suppress for READ toolsScan output for PII patterns
sessionBefore tool executes (stateful)YesLimit to 10 file writes per session
sandboxBefore tool executesYesRestrict file access to /workspace

Composition (Assembly Recipe)

A composition is a named, ordered recipe that assembles contracts into a deployable bundle. It defines which contracts to include, in what order, and with what enforcement modes.

FieldDescription
nameComposition name (becomes the bundle name on deploy)
descriptionPurpose of this composition
defaults_modeDefault enforcement mode: enforce or observe
itemsOrdered list of contracts with per-item settings

Each item in a composition references a contract from the library and can override its mode:

Item FieldDescription
contract_idReference to a library contract
positionOrder in the composition (contracts evaluate in order)
mode_overrideenforce, observe, or inherit

When adding contracts to a composition, the contract picker includes a Create New Contract inline action. Selecting it opens the contract editor without leaving the picker flow — the new contract is immediately available to add.

Mode resolution follows a clear precedence:

Item mode_override > Contract definition > Composition defaults_mode
  • enforce -- contract failures deny the tool call
  • observe -- contract failures produce audit events but do not deny
  • inherit -- falls through to the composition's defaults_mode

This lets you roll out a new contract in observe mode (test it in observe mode against production traffic), then flip it to enforce when you're confident -- without editing the contract itself.

Composition or direct upload? Use compositions when you want to mix and match contracts, override modes per-contract, and track which contracts are included. Use direct upload when you already have a tested YAML bundle from local development and want to deploy it as-is.

Bundle (Deployed Artifact)

A bundle is the assembled YAML artifact that agents actually enforce. It is the output of either:

  1. Composing from the library -- the composition recipe assembles contracts into a YAML bundle
  2. Direct upload -- raw YAML uploaded through the API or dashboard
FieldDescription
nameBundle identifier (agents subscribe by name)
versionAuto-incremented on each upload or composition deploy
yaml_contentThe assembled YAML text
revision_hashSHA-256 of the YAML content (drift detection)
signatureEd25519 signature (hex)
public_keySigning key public component (hex)
deployed_atTimestamp of most recent deployment
deployed_envEnvironment the bundle was deployed to

Every bundle is signed with Ed25519 on deploy. The signature and public key are included in the SSE event so agents can verify authenticity.

Import: YAML to Library

The Import dialog accepts three YAML formats. It automatically wraps single contracts and lists into a bundle before sending to the backend — you do not need to pre-format your YAML as a bundle.

FormatDescription
Single contractA YAML object with an id field — imported as one library contract
Contract listA YAML list of contract objects — each becomes a library contract
Full bundleA ContractBundle with a contracts: key — decomposed into individual contracts
POST /api/v1/contracts/import
Body: { "yaml_content": "..." }

Single contract, list, or full bundle
    |
    +-> Contract: block-dotenv (type: pre, v1)
    +-> Contract: pii-scan (type: post, v1)
    +-> Contract: rate-limit (type: session, v1)
    +-> Contract: workspace-sandbox (type: sandbox, v1)
    +-> Contract: block-sql-injection (type: pre, v1)

Each contract extracted from the YAML becomes a library contract at version 1. Existing contracts with the same id get a new version instead of a duplicate.

Version Management

Both contracts and bundles are versioned independently:

  • Contract versions: each update to a contract definition creates a new version. The old version is preserved. Compositions reference the latest version of each contract at assembly time.
  • Bundle versions: each deployment (from composition or direct upload) creates a new bundle version. The diff viewer lets you compare any two versions side by side.
Contract: block-dotenv
  v1: when args.path contains ".env" -> deny
  v2: when args.path contains ".env" or ".secret" -> deny
  v3: when args.path matches ".*\.(env|secret|key)$" -> deny

Bundle: production-standard
  v1: block-dotenv(v1) + pii-scan(v1)
  v2: block-dotenv(v2) + pii-scan(v1)  <- contract updated
  v3: block-dotenv(v2) + pii-scan(v1) + rate-limit(v1)  <- contract added
  v4: block-dotenv(v3) + pii-scan(v1) + rate-limit(v2)  <- two contracts updated

The Dashboard Experience

Contract management page in the console

The Contracts page has four tabs:

TabPurpose
LibraryBrowse, create, edit, and search contracts. Filter by type and tag. AI assistant helps write contract definitions.
BundlesUpload raw YAML or assemble from compositions. Deploy to environments. Diff viewer between any two versions.
DeploymentsHistory of all deployments: who deployed, when, which bundle version, which environment.
EvaluatePlayground: enter a tool name + JSON args, select a bundle, see the verdict and full contract evaluation trace. Replay mode re-evaluates past events against current contracts.

Contract preview and YAML diff viewer

Drift Detection

When an agent connects via SSE, it sends its policy_version (the SHA-256 revision hash of its current bundle). The console compares this against the latest deployed bundle for that agent's environment:

StatusMeaning
currentAgent's hash matches the deployed bundle
driftAgent's hash differs -- it is running a stale bundle
unknownAgent did not report a contract version

Drift is visible on the fleet monitoring page. A drifted agent will pick up the latest bundle on its next SSE reconnect.

Next Steps

Last updated on

On this page