DBL Policy
Deterministic, tenant-scoped policy evaluation for DBL. This package produces DECISION events only. It does not execute tasks. Status: Stable
What it is
dbl-policy is the contract layer for policy in the DBL stack:
- It evaluates a policy from authoritative inputs only
- It returns ALLOW or DENY with stable reason codes
- It can be bridged into a
dbl-coreDECISION event with a strict, contract-shapeddatamapping - It is pure: no IO, no time, no randomness, no env, no network, no trace-dependence
Non-goals
- No execution
- No orchestration
- No reading or depending on observational fields (trace, runtime, exceptions, etc.)
- No mutation of event streams
Contract
The authoritative specification is in:
docs/dbl_policy_contract.mddocs/ARCHITECTURE.md
Architecture direction:
dbl-policyis the contract and bridge layerdbl-policy-gatesis the gate algebra layer- domain policies are root-level assemblies built on top
Composition lives in dbl-policy-gates.
Key invariants enforced by this package:
- Inputs must be JSON-safe and deterministic
- No floats (including nested)
- Mapping keys must be exact
str(no subclasses)
- Policy input is whitelisted
- Unknown keys are rejected
PolicyContextis snapshotted- Caller mutation after construction cannot affect evaluation or digest
- Output can be converted into a
dbl-coreDECISION eventDECISION.datais a Mapping with a strict shape- Includes policy lineage:
policy_id,policy_version,tenant_id
Install
pip install dbl-policy
Requires Python 3.11+ and dbl-core>=0.3,<0.4.
Quickstart
- Use a built-in policy
from dbl_policy import PolicyContext
from dbl_policy.model import TenantId
from dbl_policy.allow_all import POLICY as ALLOW_ALL
ctx = PolicyContext(
tenant_id=TenantId("tenant-1"),
inputs={"intent_type": "chat.message"},
)
decision = ALLOW_ALL.evaluate(ctx)
- Convert a decision to a DBL DECISION event
from dbl_policy.bridge import decision_to_dbl_event
event = decision_to_dbl_event(decision, correlation_id="c1")
event.data will be shaped like:
{
"policy_id": "...",
"policy_version": "...",
"tenant_id": "...",
"gate": {
"decision": "ALLOW" | "DENY",
"reason_code": "...",
# "reason_message": "..." (optional)
}
}
Writing your own policy
Policies only implement evaluate(context) and must be deterministic.
from dataclasses import dataclass
from dbl_policy import PolicyContext, PolicyDecision
from dbl_policy.model import DecisionOutcome, PolicyId, PolicyVersion
import dbl_policy.reason_codes as reason_codes
@dataclass(frozen=True)
class ExamplePolicy:
policy_id: PolicyId = PolicyId("example")
policy_version: PolicyVersion = PolicyVersion("1.0.0")
def evaluate(self, context: PolicyContext) -> PolicyDecision:
intent_type = context.inputs.get("intent_type")
if intent_type == "blocked":
return PolicyDecision(
outcome=DecisionOutcome.DENY,
reason_code=reason_codes.TENANT_BLOCKED,
reason_message="blocked intent_type",
policy_id=self.policy_id,
policy_version=self.policy_version,
tenant_id=context.tenant_id,
)
return PolicyDecision(
outcome=DecisionOutcome.ALLOW,
reason_code=reason_codes.OK,
policy_id=self.policy_id,
policy_version=self.policy_version,
tenant_id=context.tenant_id,
)
Safe evaluation (recommended)
decide_safe wraps context validation and converts failures into a stable DENY.
- Valid inputs: evaluates policy, ensures
authoritative_digestis populated - Invalid inputs: DENY with stable reason_code
- Exceptions during evaluation: DENY with
evaluation_error
from dbl_policy import decide_safe
from dbl_policy.allow_all import POLICY
d1 = decide_safe(POLICY, "tenant-1", {"intent_type": "x"})
d2 = decide_safe(POLICY, "tenant-1", {"unknown_key": "x"}) # -> DENY
Allowed context keys
The contract enforces a strict whitelist:
- principal_id
- workspace_id
- intent_type
- capability
- model_id
- provider
- max_output_tokens
- input_bytes
- input_chars
- risk_tier
- request_tags
- extensions
Legacy removal
The pre-alignment starter policy pack from dbl_policy.policies has been
removed in 0.3.0.
- It is superseded by the
dbl-policy-gatesalgebra layer. dbl-policynow focuses on protocol, validation, and bridge concerns only.- See
docs/ARCHITECTURE.mdanddocs/MIGRATION_0_3.md.
Reason codes
Reason codes are stable semantic identifiers:
- ok
- allow_all
- deny_all
- invalid_input
- unknown_context_key
- tenant_blocked
- missing_required_input
- evaluation_error
- admission.missing_required
- admission.invalid_value
- capability.denied
- model.denied
- cost.output_tokens_cap
- cost.input_bytes_cap
- cost.input_chars_cap
- risk.high_requires_override
See src/dbl_policy/reason_codes.py.
Development
python -m venv .venv
.venv\Scripts\Activate.ps1
python -m pip install -e ".[dev]"
pytest
Metadata
Release files for dbl-policy 0.3.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 | |
|---|---|---|---|
| dbl_policy-0.3.1.tar.gz | 13.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| dbl_policy-0.3.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 23.9 kB
Release files / dbl_policy-0.3.1.tar.gz
| Download URL | dbl_policy-0.3.1.tar.gz |
|---|---|
| Size | 13.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ce940dffa98efe71956b9f9c28b8cc8a5de02384663e01c274f7f6a394044f98
|
|
BLAKE2b-256 checksum How to use checksums |
acc87ab4ec0e86fd0f192535cd15ecca91390acd057ed794cc8fc055a9e09e3d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.3
|
Release files / dbl_policy-0.3.1-py3-none-any.whl
| Download URL | dbl_policy-0.3.1-py3-none-any.whl |
|---|---|
| Size | 10.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
469d8a86317acdf9e09c196fea3c50cdb049ef2ef14d865cd94416015d536b1d
|
|
BLAKE2b-256 checksum How to use checksums |
07649019532d9b3ae0bf647b76f380a9c26fa52be5263faf218ffaa92ead7d2b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.3
|