policy-as-code-engine
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:
- Reads JSON/YAML bundles. No DSL. The matcher tree is the policy.
- Returns why, not just what. Every decision carries the matched policy + rule + reason. Operators get a real audit log on each evaluation.
- 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
- procurement-decision-api — drafts the Decision Cards that this engine enforces.
- ai-procurement-decision-spec — the v0.1 schema.
- slo-budget-tracker — error-budget tracker that you can wire into the same FastAPI app.
- reliability-toolkit-rs — Rust async reliability primitives.
- More at kineticgain.com.
License
MIT. See LICENSE.
Metadata
Release files for policy-as-code-engine 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| policy_as_code_engine-0.1.1.tar.gz | 22.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| policy_as_code_engine-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 41.2 kB
Release files / policy_as_code_engine-0.1.1.tar.gz
| Download URL | policy_as_code_engine-0.1.1.tar.gz |
|---|---|
| Size | 22.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
1c1a84f7f954371dd1c12f2864a12873ec70ca4459916f91e519239c7c805dc2
|
|
BLAKE2b-256 checksum How to use checksums |
0889d20a05177c14916195c28c61bf2f7922bd1ace259433064dcb1810c29ede
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on May 15, 2026.
Transparency logRelease files / policy_as_code_engine-0.1.1-py3-none-any.whl
| Download URL | policy_as_code_engine-0.1.1-py3-none-any.whl |
|---|---|
| Size | 18.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
cb3355fc7e2feb87a934a713f1d02af0616c4f87e5f81eaaebbfdbb7e0740b3a
|
|
BLAKE2b-256 checksum How to use checksums |
9e280c77334e4e08fc5b7b9341d5f7836ac927806cfdaa88693da80201165fc1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on May 15, 2026.
Transparency log