Edictum
Rulesets Reference

YAML Rule Reference

Current `edictum/v1` Ruleset reference.

AI Assistance

Right page if: you need the current `kind: Ruleset` shape, rule types, and action semantics. Wrong page if: you want workflow syntax -- see https://docs.edictum.ai/docs/reference/workflows. Gotcha: the current schema is `kind: Ruleset`, top-level `rules:`, and `then.action`. Do not use retired bundle-era fields.

This page covers the current edictum/v1 ruleset shape shared by the Python, TypeScript, and Go SDKs.

Minimal Ruleset

apiVersion: edictum/v1
kind: Ruleset

metadata:
  name: file-safety

defaults:
  mode: enforce

rules:
  - id: block-dotenv
    type: pre
    tool: read_file
    when:
      args.path:
        contains: '.env'
    then:
      action: block
      message: 'Sensitive file blocked.'

Top-Level Fields

FieldRequiredNotes
apiVersionYesMust be edictum/v1
kindYesMust be Ruleset
metadata.nameYesRuleset identifier
metadata.descriptionNoHuman-readable description
defaults.modeYesenforce or observe
toolsNoTool side-effect classifications for postconditions
observe_alongsideNoObserve-test a layered ruleset without changing real decisions
rulesYesArray of pre, post, session, or sandbox rules

Loading Rulesets

from edictum import Edictum

guard = Edictum.from_yaml("rules.yaml")

Multiple files compose left-to-right:

guard = Edictum.from_yaml("rules/base.yaml", "rules/overrides.yaml")

String/bytes content uses from_yaml_string().

Every loaded ruleset stamps a policy_version hash into audit events and telemetry.

Tool Classifications

The optional tools: block controls how postconditions behave after execution.

tools:
  read_file:
    side_effect: read
  write_file:
    side_effect: write
  deploy:
    side_effect: irreversible
FieldRequiredNotes
side_effectYespure, read, write, or irreversible
idempotentNoDefaults to false

Unclassified tools default to irreversible.

Common Rule Fields

FieldRequiredNotes
idYesUnique within one ruleset
typeYespre, post, session, or sandbox
enabledNoDefaults to true
modeNoPer-rule override of defaults.mode
thenConditionalUsed by pre, post, and session rules

Pre Rules

Pre rules run before the tool executes.

- id: block-sensitive-reads
  type: pre
  tool: read_file
  when:
    args.path:
      contains_any: ['.env', '.pem', 'id_rsa']
  then:
    action: block
    message: "Sensitive file '{args.path}' blocked."

Current pre-rule actions:

  • block
  • ask

Notes:

  • tool accepts an exact tool name, a glob such as "mcp__*", or "*"
  • output.text is invalid in type: pre
  • action: ask uses the configured approval backend
  • timeout and timeout_action are only valid for action: ask

Post Rules

Post rules run after the tool executes and inspect output.text.

- id: pii-in-output
  type: post
  tool: "*"
  when:
    output.text:
      matches_any:
        - '\b\d{3}-\d{2}-\d{4}\b'
        - 'AKIA[0-9A-Z]{16}'
  then:
    action: redact
    message: 'Sensitive data redacted from output.'

Current post-rule actions:

  • warn
  • redact
  • block

Notes:

  • redact and block only stay enforced for pure / read tools
  • for write / irreversible tools, those actions degrade to warnings because the side effect already happened
  • observe mode always downgrades postcondition enforcement to warning-only behavior

Session Rules

Session rules enforce cumulative limits across a session.

- id: session-limits
  type: session
  limits:
    max_tool_calls: 50
    max_attempts: 120
    max_calls_per_tool:
      deploy: 2
  then:
    action: block
    message: 'Session limit reached.'

Session rules:

  • do not use tool
  • do not use when
  • only support then.action: block

Sandbox Rules

Sandbox rules define allowlists instead of deny-lists.

- id: file-sandbox
  type: sandbox
  tools: [read_file, write_file, edit_file]
  within:
    - /workspace
    - /tmp
  not_within:
    - /workspace/.env
  outside: block
  message: "File access outside workspace: {args.path}"

Supported sandbox boundaries:

  • within / not_within for file paths
  • allows.commands for exec tools
  • allows.domains / not_allows.domains for network tools

outside currently supports:

  • block
  • ask

Selectors

Common selectors:

  • environment
  • tool.name
  • args.*
  • principal.user_id
  • principal.service_id
  • principal.org_id
  • principal.role
  • principal.ticket_ref
  • principal.claims.*
  • env.*
  • metadata.*
  • output.text (post rules only)

Missing fields evaluate to false, not to an exception.

Operators

Common operators:

  • exists
  • equals
  • not_equals
  • in
  • not_in
  • contains
  • contains_any
  • starts_with
  • ends_with
  • matches
  • matches_any
  • gt
  • gte
  • lt
  • lte

See Operators for full examples.

Action Block

The then block is used by pre, post, and session rules:

then:
  action: block
  message: 'Explain what happened.'
  tags: [security, secrets]
FieldRequiredNotes
actionYesConstrained by rule type
messageNoHuman-readable message with {placeholder} expansion
tagsNoString tags copied into audit metadata
timeoutNoApproval timeout in seconds for action: ask
timeout_actionNoblock or allow for approval timeout

Allowed actions by rule type:

Rule typeActions
preblock, ask
postwarn, redact, block
sessionblock
sandboxuse outside, not then

Observe Mode

Observe mode logs what would have happened without enforcing it.

defaults:
  mode: observe

or per rule:

mode: observe

In observe mode:

  • matching pre rules emit would-block audit events instead of blocking
  • matching post rules emit warnings instead of changing the real tool result
  • matching sandbox or session findings are recorded but do not change the real decision

Composition And Inheritance

Multi-file composition

Edictum.from_yaml("base.yaml", "overrides.yaml") composes multiple files left-to-right.

  • later files replace earlier rules by id
  • later files win for defaults
  • observe_alongside: true keeps a layer as observe-only instead of replacing the enforced rule

extends inheritance

Ruleset inheritance exists as a low-level helper, not as magical auto-resolution in every loader path.

Python:

from edictum.yaml_engine import resolve_ruleset_extends

merged = resolve_ruleset_extends(rulesets, "child")

TypeScript:

import { resolveRulesetExtends } from '@edictum/core'

const merged = resolveRulesetExtends(rulesets, 'child')

Go:

merged, err := yaml.ResolveExtendsFromRegistry(rulesets, "child")

Inheritance semantics:

  • parent rules come before child rules
  • child metadata/defaults win on conflict
  • circular references are rejected
  • missing parents are rejected

Validation Notes

The current loaders reject:

  • legacy pre-ruleset YAML syntax
  • invalid selectors for the rule type
  • invalid action names
  • duplicate rule IDs
  • malformed regex
  • malformed sandbox boundaries

Last updated on

On this page