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.

Install

Install the latest released package from PyPI:

pip install dogwood-py

Pre-release builds are published for pull requests. To try the latest pre-release:

pip install --pre dogwood-py

To install the optional example dependencies:

pip install "dogwood-py[examples]"

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.

The examples use Dogwood's 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.

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, 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

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 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 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.1.dev10.tar.gz (33.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.1.dev10-cp310-abi3-win_amd64.whl (5.4 MB view details)

Uploaded CPython 3.10+Windows x86-64

dogwood_py-0.0.1.dev10-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.1.dev10-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.1.dev10-cp310-abi3-macosx_11_0_arm64.whl (5.3 MB view details)

Uploaded CPython 3.10+macOS 11.0+ ARM64

dogwood_py-0.0.1.dev10-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.1.dev10.tar.gz.

File metadata

  • Download URL: dogwood_py-0.0.1.dev10.tar.gz
  • Upload date:
  • Size: 33.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.1.dev10.tar.gz
Algorithm Hash digest
SHA256 c4aa82fb36322c70576202aa452b08278f1cf2a6d4e2335cc6b1efe266f939f3
MD5 54b45789109aa406eaba2c6331f239b7
BLAKE2b-256 169052a1ef7b2ed9282878e827c26f23df007fd9d6ddf9e01bf4340b07e3b386

See more details on using hashes here.

Provenance

The following attestation bundles were made for dogwood_py-0.0.1.dev10.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.1.dev10-cp310-abi3-win_amd64.whl.

File metadata

File hashes

Hashes for dogwood_py-0.0.1.dev10-cp310-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 54e00d5bdbc447edd1ca50a7704757e7bd557275d8e11703b796949e97363f89
MD5 9faf24653b04be718e2a5c210f728895
BLAKE2b-256 9e89ca3d64eaaf22df4e65b2fe969c01c97a0f49ac955a3e57443c1200ab459d

See more details on using hashes here.

Provenance

The following attestation bundles were made for dogwood_py-0.0.1.dev10-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.1.dev10-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for dogwood_py-0.0.1.dev10-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 e585caf987e720a53dc27a230e450de14c73b1b95ecf658958db0ec7b26eb116
MD5 2245db311f884eb57cd08b03a80e9a2c
BLAKE2b-256 72f0bc420b113ba59a7d0bc19d54985c0700bde4fded2100d8a494eed7525261

See more details on using hashes here.

Provenance

The following attestation bundles were made for dogwood_py-0.0.1.dev10-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.1.dev10-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for dogwood_py-0.0.1.dev10-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 ace184aabfaf06c8c03d944990583db088355133f96479b38d4335c07f0b4249
MD5 4e7bcc515f60a7a6e24ed6bb9f3a598e
BLAKE2b-256 1583456f98a98070d4dfaafe948459c46bd76c972192892e9671fe3ccee82ce7

See more details on using hashes here.

Provenance

The following attestation bundles were made for dogwood_py-0.0.1.dev10-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.1.dev10-cp310-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for dogwood_py-0.0.1.dev10-cp310-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 79bfa3f3671e58901527477d4403e44652f89d21d8730c92ea138901957c7b75
MD5 bb38538c72ae9f4fb36ff5ca3c0f778e
BLAKE2b-256 17c1e4dbaf416e9645262960965c00e9635c0f30a2aaa5449bf960f452293d22

See more details on using hashes here.

Provenance

The following attestation bundles were made for dogwood_py-0.0.1.dev10-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.1.dev10-cp310-abi3-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for dogwood_py-0.0.1.dev10-cp310-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 675f434962fab6b7526751eb4a3a471373c49cbd8d8bd7c92c8db5d63edd8f35
MD5 1eef0c83ec815cc3093b40b2d67a671f
BLAKE2b-256 f350089f746b699d9cda831367a58ceb4ef5f94a8a72f46b5d77f3c803deeae8

See more details on using hashes here.

Provenance

The following attestation bundles were made for dogwood_py-0.0.1.dev10-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