Skip to main content

Embedded ABAC policy engine for AI agents

Project description

initguard

Embedded ABAC policy engine for AI agents.

PyPI Python License

Write authorization policies in YAML with CEL conditions, evaluate them in-process. No sidecar, no HTTP, no external service. Policies use when/unless clauses for readable rules and return structured Decision objects with human-readable reasons and advice.

Install

pip install initguard

Quick start

Create a policy file:

# policies/tool_policy.yaml
apiVersion: initguard/v1
kind: ResourcePolicy
resource: tool
rules:
  - actions: ["execute"]
    effect: allow
    roles: ["agent"]
    when: request.resource.attr.tool_type in ["http", "search"]

  - actions: ["execute"]
    effect: deny
    roles: ["agent"]
    when: request.resource.attr.tool_type == "shell"
    unless: request.principal.attr.tags.exists(t, t == "trusted")
    advice: "Shell tools require the 'trusted' tag."

Load and evaluate:

from initguard import PolicyEngine, Principal, load_policies

engine = PolicyEngine(load_policies("./policies"))

agent = Principal(
    id="agent:helper",
    roles=["agent"],
    attrs={"tags": ["basic"]},
)

decision = engine.check(agent, "tool", "execute", resource_attrs={"tool_type": "shell"})

print(decision.allowed)  # False
print(decision.reason)   # "denied by tool_policy.yaml:rules[1]"
print(decision.advice)   # "Shell tools require the 'trusted' tag."

Policy format

Three document kinds, all in YAML.

Schema (optional)

Lint your policies against known attribute names. Catches typos at load time.

apiVersion: initguard/v1
kind: Schema
principals:
  agent:
    attrs:
      team: string
      tags: list
resources:
  tool:
    attrs:
      tool_type: string
    actions: [execute]

DerivedRoles

Conditional role elevation using CEL. A definition matches when when is true and unless is false (or absent). Names must be globally unique.

apiVersion: initguard/v1
kind: DerivedRoles
name: agent_roles
definitions:
  - name: trusted_agent
    parentRoles: ["agent"]
    when: request.principal.attr.tags.exists(t, t == "trusted")

  - name: same_team
    parentRoles: ["agent"]
    when: request.principal.attr.team != ""
    unless: request.principal.attr.team != request.resource.attr.team

ResourcePolicy

Allow/deny rules for a resource kind. Deny always wins over allow. Every rule must declare at least one of roles or derivedRoles.

apiVersion: initguard/v1
kind: ResourcePolicy
resource: tool
importDerivedRoles: [agent_roles]
rules:
  - actions: ["execute"]
    effect: allow
    derivedRoles: ["trusted_agent"]

  - actions: ["execute"]
    effect: deny
    roles: ["agent"]
    when: request.resource.attr.tool_type in ["shell", "python"]
    unless: request.principal.attr.tags.exists(t, t == "trusted")
    advice: "Requires the 'trusted' tag."

Derived roles are scoped to imports -- a ResourcePolicy only sees roles from the sets it explicitly imports, preventing privilege leakage across policy domains.

API

load_policies(policy_dir) -> PolicySet

Load all *.yaml / *.yml files from a directory. Files are loaded in sorted order for deterministic precedence. All CEL expressions are compiled at load time. Raises PolicyLoadError on invalid YAML, bad CEL syntax, duplicate role names, or broken references.

PolicyEngine(policy_set)

engine = PolicyEngine(policy_set)

engine.check(principal, resource_kind, action, resource_id="*", resource_attrs=None) -> Decision

Evaluate policies and return a Decision. Also available as check_async for async contexts.

engine.info() -> EngineInfo

Returns loaded policy summary: resource kinds, derived role sets, policy/rule counts, schema presence.

Decision

Field Type Description
allowed bool Whether the action is permitted
reason str Human-readable explanation, always populated
matched_rule str Stable rule identifier, e.g. "tool_policy.yaml:rules[1]"
advice str From the rule's advice field, or empty

Principal

Field Type Description
id str Identity string, e.g. "agent:code-reviewer"
roles list[str] Role list, e.g. ["agent", "team:platform"]
attrs dict[str, Any] Attributes for CEL conditions

CEL expressions

Conditions use Google Common Expression Language. Supported patterns:

# List membership
request.resource.attr.tool_type in ["shell", "python"]

# Existential quantifier
request.principal.attr.tags.exists(t, t == "trusted")

# Attribute equality
request.principal.attr.team == request.resource.attr.team

# Boolean operators
!request.principal.attr.tags.exists(t, t == "admin") && request.resource.attr.tool_type == "shell"

Missing attributes in CEL expressions evaluate to false (condition not met), not an error.

Development

uv sync --all-extras
uv run pytest tests/ -v
uv run ruff check . && uv run ruff format --check .

License

MIT OR Apache-2.0

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

initguard-2026.3.1.tar.gz (53.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

initguard-2026.3.1-py3-none-any.whl (16.5 kB view details)

Uploaded Python 3

File details

Details for the file initguard-2026.3.1.tar.gz.

File metadata

  • Download URL: initguard-2026.3.1.tar.gz
  • Upload date:
  • Size: 53.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.21 {"installer":{"name":"uv","version":"0.9.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for initguard-2026.3.1.tar.gz
Algorithm Hash digest
SHA256 eb22a579e8f7e06d06669e561a2820de5a9e0a30a1c8892b0acb101456726344
MD5 87e86df75c2955c1440854818c15b1cf
BLAKE2b-256 1427da06463b059992b8812ba2a11c8ac4677cf79ce634cacbf89769f2c640a2

See more details on using hashes here.

File details

Details for the file initguard-2026.3.1-py3-none-any.whl.

File metadata

  • Download URL: initguard-2026.3.1-py3-none-any.whl
  • Upload date:
  • Size: 16.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.21 {"installer":{"name":"uv","version":"0.9.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for initguard-2026.3.1-py3-none-any.whl
Algorithm Hash digest
SHA256 96c5a5e9c9fabb13627da04019a17bde19fc27f410aac3efe630b656f0a75cdb
MD5 1ec77389cc3498b98a9b19cfdb7027ac
BLAKE2b-256 22da616bc1704bf3abc792d6ea95d5c7c427f34491833a850d1c86484f427821

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page