Skip to main content

Declarative policy-as-code evaluator. JSON/YAML rules + Python predicates over arbitrary context objects. Enforces AI Procurement Decision Card conditions at request time. Optional audit-stream-py integration via AUDIT_STREAM_URL.

Project description

policy-as-code-engine

CI Python License: MIT

Declarative policy-as-code evaluator for Python services. JSON/YAML rules → first-match-wins evaluation → structured allow/deny decision with the matching rule and the reason. Cheap to embed; ships with a FastAPI surface; pairs directly with procurement-decision-api so the same Decision Card that records a buyer's posture also becomes the runtime gate that enforces it.


Why

Most policy engines either ask you to learn a DSL (Rego, Cedar) or hand you a dictionary-of-lambdas and call it a library. Neither is the right shape when the source of truth is a JSON document a human signed off on. This engine:

  1. Reads JSON/YAML bundles. No DSL. The matcher tree is the policy.
  2. Returns why, not just what. Every decision carries the matched policy + rule + reason. Operators get a real audit log on each evaluation.
  3. Bridges to the Kinetic Gain Protocol Suite. A single endpoint turns an AI Procurement Decision Card into a runtime-enforceable PolicyBundle — approve, reject, or approve-with-conditions all map to concrete allow/deny logic.

Install

pip install policy-as-code-engine
# with the FastAPI surface:
pip install "policy-as-code-engine[api]"

Python 3.11+. Runtime deps: pydantic + PyYAML.


Library quickstart

from policy_as_code_engine import (
    EvaluationContext,
    PolicyBundle,
    PolicyEvaluator,
)

bundle = PolicyBundle.model_validate({
    "bundle_id": "edu-gate",
    "policies": [{
        "id": "writes-require-admin",
        "default_effect": "deny",
        "rules": [
            {
                "id": "admin-writes",
                "effect": "allow",
                "when": {
                    "kind": "all_of",
                    "matchers": [
                        {"kind": "in", "field": "action", "value": ["create", "update", "delete"]},
                        {"kind": "eq", "field": "subject.role", "value": "admin"},
                    ],
                },
            },
        ],
    }],
})

ctx = EvaluationContext(
    subject={"id": "u-42", "role": "admin"},
    action="update",
    resource={"id": "doc-7"},
)

result = PolicyEvaluator().evaluate(bundle, ctx)
print(result.decision.kind)             # "allow"
print(result.decision.matched_rule_id)  # "admin-writes"
print(result.decision.reason)           # "matched rule 'admin-writes'"

result.policy_decisions carries every per-policy outcome — drop it straight into your audit log.


Bundle DSL

A bundle is a small recursive structure. Matchers compose; rules ordered.

Field matchers

Kind Notes
eq / ne Strict equality.
gt / gte / lt / lte Comparison; returns false on incompatible types (won't raise).
in / not_in value must be a list.
contains Works against strings, lists, sets, dicts.
exists / missing No value. Operates against the dotted-path resolver.
regex Compiled patterns are cached per-evaluator.
starts_with / ends_with String-only.

Composite matchers

Kind Children Truth
all_of matchers: [...] All children true.
any_of matchers: [...] At least one child true.
not matcher: {...} Inverts the child.
always Always true. Useful as a final catch-all.

Dotted paths

The resolver looks at the merged context (data + subject + action + resource):

subject.role
resource.tags.0          # list index
data.conditions_satisfied.dpa-signed

Missing segments produce a _MISSING sentinel — exists / missing matchers see it; every other matcher returns false.


FastAPI surface

pip install "policy-as-code-engine[api]"
python -m policy_as_code_engine     # binds 0.0.0.0:8089 by default
Method Path What it does
GET /healthz Liveness probe.
GET / Service info.
POST /bundles Register a PolicyBundle in memory.
GET /bundles List registered bundle IDs.
GET /bundles/{bundle_id} Inspect a registered bundle.
POST /bundles/{bundle_id}/evaluate Evaluate a stored bundle against an EvaluationContext.
POST /evaluate One-shot. Bundle + context in, decision out.
POST /bundles/from-decision-card The cross-ecosystem hook. Turn a Kinetic Gain Procurement Decision Card into a PolicyBundle and register it.

The cross-ecosystem hook

The headline feature. An AI Procurement Decision Card is the buyer-side record that says "we evaluated this vendor and our position is X." This engine turns that human-authored artifact into a runtime gate, mechanically.

curl -X POST http://localhost:8089/bundles/from-decision-card \
  -H 'Content-Type: application/json' \
  -d @decision-card.json

Mapping:

Decision Card status Resulting bundle
approved Single allow-all policy.
rejected · rejected-with-remediation · withdrawn · expired · pending Single deny-all policy (fail safe).
approved-with-conditions One policy per condition. Each policy allows only when conditions_satisfied.{condition_id} is true in the evaluation context; deny otherwise. The bundle combiner does deny-trumps-allow, so every condition must be satisfied to allow.

Wire your own satisfaction signal — DPA verifier, bias-audit freshness check, attestation timestamp — into the context, and the bundle does the rest.

from policy_as_code_engine import (
    EvaluationContext,
    PolicyEvaluator,
    policy_bundle_from_decision_card,
)

card = {...}  # POST /decisions/draft output from procurement-decision-api
bundle = policy_bundle_from_decision_card(card)

ctx = EvaluationContext(
    subject={"id": "u-1"},
    action="enroll",
    data={
        "conditions_satisfied": {
            "dpa-signed":         True,
            "bias-audit-fresh":   True,
        }
    },
)

decision = PolicyEvaluator().evaluate(bundle, ctx).decision

CLI

python -m policy_as_code_engine eval examples/example-bundle.yaml examples/example-context.json

Prints the full EvaluationResult as JSON. Exits non-zero on deny.


How decisions combine

Inside a single policy: first matching rule wins, otherwise default_effect.

Across the bundle:

deny     -> deny      (any policy denies => bundle denies)
allow    -> allow     (otherwise, any allow => bundle allows)
neither  -> not_applicable

Per-policy decisions are always returned — useful for "we denied because of policy B, but A would have allowed" audit narratives.


Tests

pip install -e ".[dev]"
ruff check src tests && ruff format --check src tests
mypy src
pytest -v

CI matrix runs Python 3.11 / 3.12 / 3.13.


Related in this ecosystem


License

MIT. See LICENSE.

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

policy_as_code_engine-0.1.1.tar.gz (22.5 kB view details)

Uploaded Source

Built Distribution

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

policy_as_code_engine-0.1.1-py3-none-any.whl (18.7 kB view details)

Uploaded Python 3

File details

Details for the file policy_as_code_engine-0.1.1.tar.gz.

File metadata

  • Download URL: policy_as_code_engine-0.1.1.tar.gz
  • Upload date:
  • Size: 22.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for policy_as_code_engine-0.1.1.tar.gz
Algorithm Hash digest
SHA256 1c1a84f7f954371dd1c12f2864a12873ec70ca4459916f91e519239c7c805dc2
MD5 f0006a4ce558ba30c3847e0408a7ffa2
BLAKE2b-256 0889d20a05177c14916195c28c61bf2f7922bd1ace259433064dcb1810c29ede

See more details on using hashes here.

Provenance

The following attestation bundles were made for policy_as_code_engine-0.1.1.tar.gz:

Publisher: publish.yml on mizcausevic-dev/policy-as-code-engine

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file policy_as_code_engine-0.1.1-py3-none-any.whl.

File metadata

File hashes

Hashes for policy_as_code_engine-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 cb3355fc7e2feb87a934a713f1d02af0616c4f87e5f81eaaebbfdbb7e0740b3a
MD5 6990515e80dbd8835789bb8d49fbdaf2
BLAKE2b-256 9e280c77334e4e08fc5b7b9341d5f7836ac927806cfdaa88693da80201165fc1

See more details on using hashes here.

Provenance

The following attestation bundles were made for policy_as_code_engine-0.1.1-py3-none-any.whl:

Publisher: publish.yml on mizcausevic-dev/policy-as-code-engine

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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