Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

Dogwood Policy Python SDK

Python SDK and PyO3 binding for the Dogwood policy language. Dogwood is a policy language for fine-grained authorization decisions that depend on history or patterns of events over time - not just a single request. It adds temporal conditions (since, formerly, once, aggregations) and information providers (computed guardrail facts) on top of Cedar policy syntax, then lowers everything back to Cedar for evaluation. Existing Cedar policies stay valid as-is. For information, read the Dogwood documentation.

⚠️⚠️⚠️ Current Dogwood reference interpreter is not intended for production use; therefore, this Python SDK and PyO3 binding is experimental in nature.

GitHub Release GitHub Actions Test Workflow Status PyPI - Version Python Wheels Python Versions GitHub last commit PyPI - Status Conda Version License GitHub Downloads (all assets, all releases) PyPI Downloads

The public API is modeled after the Rust dogwood-language lifecycle:

  1. Build a ServiceSchema and PolicySchema.
  2. Parse and lower policy source into a LoweredPolicySet.
  3. Validate it.
  4. Feed Event values to a stateful Authorizer.

This package uses PyO3/maturin to bind the Rust dogwood-language reference for schema-backed lowering, validation, and trace replay. The Python SDK also keeps a temporary pure-Python fallback only for source-tree examples that omit a full Cedar action schema. Schema-backed workflows require the native extension.

dogwood-py also provides optional Strands Agents support. Dogwood policies can be attached as Strands interventions so tool calls are checked before execution, with typed outcomes such as proceed, deny, guide, confirm, and transform.

Install

Install the latest released package from PyPI:

pip install dogwood-py

To install the optional example dependencies:

pip install "dogwood-py[examples]"

To install the optional Strands Agents integration:

pip install "dogwood-py[strands]"

The package installs as dogwood:

from dogwood import native

assert native.available()

Native vs. Non-Native Execution

This package has two execution paths.

Native path The native extension imports as dogwood._dogwood_native; convenience wrappers live in dogwood.native. The native path is the PyO3 extension built by maturin. It calls the Rust dogwood-language reference implementation.

Used for:

  • schema-backed policy lowering
  • schema-backed validation
  • schema-backed trace replay
  • augmented Cedar schema export

This is the path to use for compatibility with the Rust reference. It requires a real Cedar action schema.

You can check whether it is available:

from dogwood import native

assert native.available()

For repeated decisions, use a persistent native authorizer so policy lowering happens once:

from dogwood import native

authorizer = native.NativeAuthorizer(policy_source, cedar_schema_source)

decision = authorizer.authorize_request(
    "Drupe::Action::SellShares",
    'Drupe::OAuthUser::"alice"',
    'Drupe::Gateway::"trading"',
    {"shares": 25, "stock": "AMZN"},
)

assert decision == "Allow"

Non-native fallback

The fallback path is pure Python. It exists only so SDK examples can run without a full Cedar schema while the native API surface is still being built out.

It supports only a small subset:

  • basic permit / forbid
  • when / unless checks over context.*
  • simple trace parsing
  • simple formerly within temporal checks

It is not a replacement for the Rust reference implementation.

The SDK requires native behavior when a non-empty PolicySchema is supplied. It will raise a clear error if the native extension is missing. If the schema is empty, examples may still use the Python fallback.

Event Schema

Dogwood has two schema layers:

  • The policy/action schema is the Cedar .cedarschema file. It defines entities, actions, and request context types such as context.input.amount.
  • The event schema tells Dogwood how actions become historical events: which event kinds exist (request, response, error), which fields are recorded in the temporal history, and which event kinds produce authorization decisions.

If no event schema is supplied, Dogwood uses its default event schema. Under that default, request events are decision points, and the event history records request input fields plus reserved fields like callerPrincipal, callerResource, and requestId. That is why a temporal policy can ask about past events such as:

Drupe::Action::"Transfer"::request{ input.user: context.input.user }

In other words, the Cedar schema says what a Transfer request looks like; the event schema says that Transfer::request is both authorizable and stored in history for later temporal checks.

To supply an explicit .dwschema:

from pathlib import Path
from dogwood import LoweredPolicySet, ServiceSchema

service = ServiceSchema(event_schema=Path("event.dwschema").read_text())
policies = LoweredPolicySet.from_str(policy, service, policy_schema)

Python API

from dogwood import Authorizer, Event, LoweredPolicySet, PolicySchema, ServiceSchema

policy = '''
@id("sell_small_only")
permit (
    principal,
    action == Drupe::Action::"SellShares",
    resource
)
when { context.input.shares <= 50 };
'''

policies = LoweredPolicySet.from_str(policy, ServiceSchema.defaults(), PolicySchema(""))
authorizer = Authorizer(policies)

event = (
    Event.builder('Drupe::Action::"SellShares"', "request")
    .principal('Drupe::OAuthUser::"alice"')
    .resource('Drupe::Gateway::"gw1"')
    .field("input", "shares", 50)
    .request_context("input", "shares", 50)
    .build()
)

assert authorizer.is_authorized(event).allowed()

There is also a runnable example:

make example

FastAPI Native Example

The FastAPI example uses the native binding and a real Cedar schema. It loads its own examples/fastapi_simple/policy.dw and examples/fastapi_simple/schema.cedarschema plus examples/fastapi_simple/event.dwschema, creates a persistent native.NativeAuthorizer, and exposes an authorization endpoint.

The policy enforces a $50 daily transfer limit per user. Three $20 transfers by the same user produce:

Allow, Allow, Deny

Run it:

make develop
make examples-deps
make fastapi-example

First transfer:

curl -s http://127.0.0.1:8000/authorize \
  -H 'content-type: application/json' \
  -d '{"user":"alice","amount":20}'

Expected response:

{"decision":"Allow","allowed":true,"daily_limit":50}

Second transfer:

curl -s http://127.0.0.1:8000/authorize \
  -H 'content-type: application/json' \
  -d '{"user":"alice","amount":20}'

Expected response:

{"decision":"Allow","allowed":true,"daily_limit":50}

Third transfer:

curl -s http://127.0.0.1:8000/authorize \
  -H 'content-type: application/json' \
  -d '{"user":"alice","amount":20}'

Expected response:

{"decision":"Deny","allowed":false,"daily_limit":50}

Dogwood authorizers are stateful. The example keeps one shared native authorizer and protects it with a lock. For high-throughput services, use a pool or request-partitioned authorizers based on your temporal semantics.

The same FastAPI app also includes a quota-based rate limit endpoint:

curl -s http://127.0.0.1:8000/authorize/quota \
  -H 'content-type: application/json' \
  -d '{"user":"carol","amount":1}'

That endpoint uses examples/fastapi_simple/quota_policy.dw, which permits fewer than three transfers by the same user within one hour. For one user, the first two requests are allowed and the third is denied.

CLI

dogwood-py validate policy.dw --policy-schema schema.cedarschema
dogwood-py replay policy.dw --policy-schema schema.cedarschema --trace trace.log
dogwood-py lower policy.dw --policy-schema schema.cedarschema
dogwood-py replay policy.dw --policy-schema schema.cedarschema --event-schema event.dwschema --trace trace.log

When using the local virtualenv directly:

.venv/bin/dogwood-py validate policy.dw --policy-schema schema.cedarschema

Run the checked-in CLI example:

make cli-example

Equivalent command:

.venv/bin/dogwood-py replay examples/cli/policy.dw \
  --policy-schema examples/cli/schema.cedarschema \
  --trace examples/cli/trace.log

Expected output:

@0 (time point 0): true
@1 (time point 1): false

Make Targets

  • make setup creates .venv and installs development tools.
  • make activate prints the command to activate .venv.
  • make deactivate prints the command to deactivate .venv.
  • make develop builds and installs the PyO3 extension in editable mode.
  • make examples-deps installs optional dependencies used by examples.
  • make test runs the Python test suite.
  • make perf-test runs the opt-in native-vs-Python replay performance check.
  • make example runs examples/api_usage.py.
  • make cli-example runs the dogwood-py replay example.
  • make fastapi-example starts the native-backed FastAPI server.
  • make strands-shopping-agent runs the Strands shopping agent example.
  • make docs builds Sphinx HTML documentation in docs/build/html.
  • make docs-watch rebuilds and serves docs at http://127.0.0.1:8001.
  • make build builds a wheel with maturin.
  • make clean removes generated caches and Rust build output.

The performance test is a coarse regression guard, not a precise benchmark. It uses DOGWOOD_PERF_TESTS=1, prints native and pure-Python replay timings, and asserts the Rust-backed end-to-end path is not catastrophically slower than the fallback on the same generated trace. Tune the ceiling with DOGWOOD_NATIVE_MAX_RATIO when needed.

Latest local performance check:

native replay: 0.4793s
python fallback replay: 0.0708s
ratio native/python: 6.77

persistent native authorizer: 0.4424s
python fallback authorizer: 0.0147s
ratio native/python: 30.14

This result does not mean the reference Rust implementation is slower in general. The test compares the full native Dogwood path, including real schema-backed Rust lowering/replay semantics, against the intentionally minimal Python fallback. The value of the test is detecting large accidental regressions in the binding path, not benchmarking the Rust engine in isolation.

The persistent native authorizer avoids repeated policy lowering, but each request still crosses the Python/Rust boundary, converts Python input into Dogwood values, and runs the full Cedar-backed decision path. The fallback remains much faster for this tiny policy because it evaluates only a narrow regex-parsed subset with no real Cedar schema semantics.

Current Scope

Rust-backed operations cover schema-backed lowering, validation, and trace replay. The Python fallback is temporary and schema-less only. The intended end state is to remove it once the Rust-backed SDK objects cover the same ergonomic surface.

Download files

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

Source Distribution

dogwood_py-0.0.3.dev12.tar.gz (40.1 kB view details)

Uploaded Source

Built Distributions

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

dogwood_py-0.0.3.dev12-cp310-abi3-win_amd64.whl (5.4 MB view details)

Uploaded CPython 3.10+Windows x86-64

dogwood_py-0.0.3.dev12-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (6.1 MB view details)

Uploaded CPython 3.10+manylinux: glibc 2.17+ x86-64

dogwood_py-0.0.3.dev12-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (5.9 MB view details)

Uploaded CPython 3.10+manylinux: glibc 2.17+ ARM64

dogwood_py-0.0.3.dev12-cp310-abi3-macosx_11_0_arm64.whl (5.3 MB view details)

Uploaded CPython 3.10+macOS 11.0+ ARM64

dogwood_py-0.0.3.dev12-cp310-abi3-macosx_10_12_x86_64.whl (5.6 MB view details)

Uploaded CPython 3.10+macOS 10.12+ x86-64

File details

Details for the file dogwood_py-0.0.3.dev12.tar.gz.

File metadata

  • Download URL: dogwood_py-0.0.3.dev12.tar.gz
  • Upload date:
  • Size: 40.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for dogwood_py-0.0.3.dev12.tar.gz
Algorithm Hash digest
SHA256 b625709eed6d4d76ed21788fd29822789261583242b9bceea0ea33cdd3dd015f
MD5 774bb72694d6a2a3e7d94b8c2351759e
BLAKE2b-256 41c7727f9d65a6141858d11101e4d4d76c071fa4c2a6ce85ba983a63eb7c4468

See more details on using hashes here.

Provenance

The following attestation bundles were made for dogwood_py-0.0.3.dev12.tar.gz:

Publisher: release.yml on abhishektiwari/dogwood-py

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

File details

Details for the file dogwood_py-0.0.3.dev12-cp310-abi3-win_amd64.whl.

File metadata

File hashes

Hashes for dogwood_py-0.0.3.dev12-cp310-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 5a39d042568e1788a9d4623411ea1d031c63e18fb7fb52f6ca58d14a132f8810
MD5 20e340de256ad6b62541303901fc4a1d
BLAKE2b-256 53b0961e8b18c32aeb0dd7cc8321d287d1cbe6c237746b0466ef8aee3eded605

See more details on using hashes here.

Provenance

The following attestation bundles were made for dogwood_py-0.0.3.dev12-cp310-abi3-win_amd64.whl:

Publisher: release.yml on abhishektiwari/dogwood-py

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

File details

Details for the file dogwood_py-0.0.3.dev12-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for dogwood_py-0.0.3.dev12-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 282e8be707811af4f32e628151f6f204fcb22da566d21a1ac774d2b1dcd97a7d
MD5 e45249ca7fde01790f0bee81bb43a635
BLAKE2b-256 d942ce6396ad22ecfa45b62d35fb13bf44dbf871e5653715b3a69a97408d8c74

See more details on using hashes here.

Provenance

The following attestation bundles were made for dogwood_py-0.0.3.dev12-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: release.yml on abhishektiwari/dogwood-py

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

File details

Details for the file dogwood_py-0.0.3.dev12-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for dogwood_py-0.0.3.dev12-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 03fc8eaa6af0919e0d22ebd0beae0a4f2f96542155d7047c51ef1211ca29a53f
MD5 70d6a03982797850a5a417df55cf2409
BLAKE2b-256 8c8c9491e656170df09126e144397b82f577b49c37f9aed4413485be36787bdc

See more details on using hashes here.

Provenance

The following attestation bundles were made for dogwood_py-0.0.3.dev12-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:

Publisher: release.yml on abhishektiwari/dogwood-py

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

File details

Details for the file dogwood_py-0.0.3.dev12-cp310-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for dogwood_py-0.0.3.dev12-cp310-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 758f70867749307ee6cf4b154286beafabd2fc6ca611dd2e81ea28224f4d6a98
MD5 58c8ff2a75672cc3ffd43b7b637b085b
BLAKE2b-256 a6b06b99d9d9c4c393461912a512245f558a80cf97f9904f0923bb981e304c06

See more details on using hashes here.

Provenance

The following attestation bundles were made for dogwood_py-0.0.3.dev12-cp310-abi3-macosx_11_0_arm64.whl:

Publisher: release.yml on abhishektiwari/dogwood-py

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

File details

Details for the file dogwood_py-0.0.3.dev12-cp310-abi3-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for dogwood_py-0.0.3.dev12-cp310-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 00017fae36a178c699a626241f45ff4d0c943f9bf50c233af70a7b913053cac3
MD5 d951dabeaefb60f1c86d5f668e60bc59
BLAKE2b-256 ec9ef88ed87c97e71c6b00cda6b1cf2391f3e6a1ce5753d728a9c8e2089eb5cb

See more details on using hashes here.

Provenance

The following attestation bundles were made for dogwood_py-0.0.3.dev12-cp310-abi3-macosx_10_12_x86_64.whl:

Publisher: release.yml on abhishektiwari/dogwood-py

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