Policies
Technical reference. This page is for integrations, advanced configuration, or platform operations. For portal tasks, start with the user guide.
Policies are governed assets of kind policy_bundle. They define ordered rules that produce one of four outcomes at runtime: allow, deny, require_approval, or allow_with_obligations.
What a policy bundle contains
Policy bundle content is versioned JSON with a required rules array.
{
"rules": [
{
"id": "KNOWLEDGE-BASE-NOT-ALLOWED",
"condition": "match",
"match_fields": { "resource_kind": "knowledge_base" },
"outcome": "deny",
"reason_code": "KNOWLEDGE_BASE_NOT_ALLOWED"
},
{
"id": "RESTRICTED-LANE",
"condition": "expression",
"expression": "service_lane == 'restricted'",
"outcome": "require_approval",
"reason_code": "RESTRICTED_LANE"
},
{
"id": "FINANCE-KNOWLEDGE-ACCESS",
"condition": "expression",
"expression": "resource_kind matches 'kb-finance-*'",
"outcome": "allow_with_obligations",
"obligations": [{ "type": "capture_evidence" }, { "type": "require_human_review" }],
"reason_code": "FINANCE_KNOWLEDGE_ACCESS"
},
{
"id": "DEFAULT-ALLOW",
"condition": "always",
"outcome": "allow",
"reason_code": "DEFAULT_ALLOW"
}
]
}Rule fields
For redact_or_filter_output, a rule may contain at most one redaction
obligation. Its optional payload.redaction_rules list is limited to 16 strict
objects. Each object requires a non-empty stdlib-compatible pattern of at
most 512 characters and may include a replacement of at most 256 characters.
Patterns are checked again at activation and execution; invalid, timed-out, or
expanded substitutions replace the affected response sink with [REDACTED].
The primary response and execution-log copy share one bounded execution budget.
An absent or empty list keeps the platform-owned default PII redaction path.
| Field | Required | Description |
|---|---|---|
id | Yes | A stable id (letters, digits, ., _, -; up to 64 characters), unique within the bundle. Decisions and links cite a rule by its id, so reordering the rules does not break them. |
condition | Yes | Exactly one of always, never, match, or expression — no other value is evaluable, and an unsupported value now fails schema validation before the bundle can be saved. |
match_fields | With match | Key/value pairs that must all equal the runtime context (service_lane, resource_kind, interface_kind) for the rule to apply. |
expression | With expression | A non-empty policy expression evaluated against the same runtime context — comparisons (==, !=, in [...], not_in [...]), the matches glob operator, and and/or combinations. |
outcome | Yes | allow, deny, require_approval, or allow_with_obligations. |
reason_code | Yes | Machine-readable reason recorded for auditing. |
obligations | No | Additional obligations emitted with allow_with_obligations. |
priority | No | Integer weight used to choose among matched rules; defaults to 0. A boolean, null, string or fractional value is a structural error. |
Evaluation model
Every rule is evaluated against the runtime context. Among the rules that match,
the highest integer priority wins (default 0); when priorities tie, the
earliest-listed rule wins. decision_basis.selection reports first_match,
highest_priority, or default_deny.
If no rule matches, the effective behavior is deny-by-default with the reason
no_matching_policy_rule.
A structurally malformed bundle is not a no-match. A present-but-null
allowed_knowledge_base_ids, a non-integer priority, an unsupported or empty
outcome or reason_code on any matched rule, or an expression with
unbalanced quotes, brackets or empty operands fails closed: runtime execution
returns 422 malformed_policy_bundle before admission, retrieval or evidence,
and simulation returns deny with the reason malformed_policy_bundle,
decision_basis.selection = default_deny and an empty matched_rules list.
Time-bounded exceptions
Policy exceptions are workspace-scoped records for temporary, auditable policy relief. Each exception names a scope (workspace, workload, release, policy_bundle, or tenant_secret), an optional policy bundle, an expiry time, a reason, evidence references, requester attribution, approver attribution, and lifecycle state.
Create a pending exception:
curl -X POST "http://localhost:8000/v1/policies/exceptions?workspace_id=<workspace-id>" \
-H "Content-Type: application/json" \
-d '{
"scope_kind": "release",
"scope_ref": "<release-id>",
"policy_bundle_id": "<policy-asset-id>",
"reason": "Temporary exception for a CAB-approved validation window.",
"expires_at": "2026-06-01T20:00:00Z",
"evidence_refs": [{"kind": "change_request", "ref": "CAB-1234"}]
}'Approve, revoke, and record use through the same workspace scope. Recording use is a mutating governance action and requires policy management permission:
curl -X POST "http://localhost:8000/v1/policies/exceptions/<exception-id>/approve?workspace_id=<workspace-id>"
curl -X POST "http://localhost:8000/v1/policies/exceptions/<exception-id>/use?workspace_id=<workspace-id>" \
-H "Content-Type: application/json" \
-d '{
"decision_id": "<policy-decision-id>",
"scope_kind": "release",
"scope_ref": "<release-id>",
"policy_bundle_id": "<policy-asset-id>",
"reason": "Selected exception basis"
}'
curl -X POST "http://localhost:8000/v1/policies/exceptions/<exception-id>/revoke?workspace_id=<workspace-id>" \
-H "Content-Type: application/json" \
-d '{"reason": "Exception window closed"}'A tenant_secret exception authorizes one tenant credential, named by scope_ref as {environment|*}/{name}. It requires an accountable_owner (a user: reference), must be requested and approved by two different users, and cannot be approved for more than 90 days. It never acts as a runtime permission grant.
Expired exceptions, scope mismatches, pending/revoked exceptions, and exceptions from another workspace return denial context on use. They do not become silent allows. Every create, approve, use, revoke, and denial path is written to the governance audit chain.
Runtime permission grants use the same exception lifecycle for bounded runtime
capabilities. Orqestra ships a closed profile catalog: workspace_asset_write
authorizes named plugin/tool slugs, and mediated_network_tool authorizes named
MCP server URLs. Profiles contain only metadata; concrete targets live on each
operator-approved grant. The runtime path fails closed unless the static
certification posture and an active approved grant both cover the requested
target.
orqestractl runtime-permission-profile list --workspace-id <workspace-id>
orqestractl policy exceptions create <workspace-id> \
--scope-kind workload --scope-ref <workload-id> \
--reason "Temporary CRM write access for release validation" \
--expires-at 2026-08-01T00:00:00Z \
--profile-ref workspace_asset_write \
--capability-kind tool_side_effect \
--authorized-target connector:crm.update-contactThe same lifecycle is available without raw HTTP. The CLI exposes an
orqestractl policy exceptions group (create, list, get, approve,
revoke, use), and the Python SDK exposes client.policy_exceptions with
the matching methods. The SDK also exposes client.runtime_permission_profiles
for read-only profile discovery:
orqestractl policy exceptions create <workspace-id> \
--scope-kind release --scope-ref <release-id> \
--reason "CAB-approved validation window" \
--expires-at 2026-08-01T00:00:00Z
orqestractl policy exceptions list <workspace-id> --active-only
orqestractl policy exceptions approve <workspace-id> <exception-id>Exceptions can also be listed read-only through the AI Dev Kit MCP
list_policy_exceptions tool, and the profile catalog is exposed through
list_runtime_permission_profiles and get_runtime_permission_profile.
Creating, approving, revoking, and using exceptions remain human operator
actions and are intentionally not exposed as agent tools, preserving
separation-of-duties and break-glass auditability.
API reference
Create a policy bundle asset
curl -X POST http://localhost:8000/v1/assets \
-H "Content-Type: application/json" \
-d '{
"slug": "content-safety-policy",
"asset_kind": "policy_bundle",
"owner_ref": "team:compliance"
}'{
"asset_id": "p1a2b3c4-0000-0000-0000-000000000001",
"slug": "content-safety-policy",
"asset_kind": "policy_bundle",
"owner_ref": "team:compliance",
"lifecycle_state": "active",
"created_at": "2026-04-06T12:00:00Z",
"created_by": "user:alice"
}Create a policy bundle version
curl -X POST http://localhost:8000/v1/assets/{asset_id}/versions \
-H "Content-Type: application/json" \
-d '{
"version_label": "v1",
"content": {
"rules": [
{
"id": "KNOWLEDGE-BASE-NOT-ALLOWED",
"condition": "match",
"match_fields": { "resource_kind": "knowledge_base" },
"outcome": "deny",
"reason_code": "KNOWLEDGE_BASE_NOT_ALLOWED"
},
{
"id": "DEFAULT-ALLOW",
"condition": "always",
"outcome": "allow",
"reason_code": "DEFAULT_ALLOW"
}
]
}
}'List policies
curl http://localhost:8000/v1/policiesThe convenience endpoint returns the same asset metadata you would get from /v1/assets?asset_kind=policy_bundle.
Get one policy with latest version content
curl http://localhost:8000/v1/policies/{bundle_id}Response shape:
{
"asset_id": "p1a2b3c4-0000-0000-0000-000000000001",
"slug": "content-safety-policy",
"asset_kind": "policy_bundle",
"owner_ref": "team:compliance",
"lifecycle_state": "active",
"created_at": "2026-04-06T12:00:00Z",
"created_by": "user:alice",
"latest_version": {
"asset_version_id": "pv1a2b3c-0000-0000-0000-000000000001",
"asset_id": "p1a2b3c4-0000-0000-0000-000000000001",
"version_label": "v1",
"content": {
"rules": [
{
"id": "KNOWLEDGE-BASE-NOT-ALLOWED",
"condition": "match",
"match_fields": { "resource_kind": "knowledge_base" },
"outcome": "deny",
"reason_code": "KNOWLEDGE_BASE_NOT_ALLOWED"
}
]
},
"artifact_ref": "assets/...",
"checksum": "sha256...",
"created_at": "2026-04-06T12:01:00Z",
"created_by": "user:alice"
}
}Search decision precedents
Precedent search ranks prior governed decisions (policy_decision rows)
against a scenario context by blended field similarity, structural
reason-code/obligation overlap, and recency. It is read-only, bounded to a
top_k of at most 50, and draws from a bounded recent-decision candidate pool
rather than scanning the whole table.
orqestractl precedent search --resource-kind asset --action read --reason-code evidence_missing| Surface | Example |
|---|---|
| API | POST /v1/precedents/search |
| Python SDK | client.precedents.search(resource_kind=..., action=...) |
| CLI | orqestractl precedent search --resource-kind ... --action ... |
Binding a policy to a release
There are two policy-related fields in the current release model:
policy_bundle_idon the release record- a release binding with role
policy_bundle
In practice today, a production-ready release should carry both:
- Set
policy_bundle_idwhen creating the release so the runtime can resolve the governing bundle. - Add a
policy_bundleasset-version binding so release activation validation and asset lineage stay intact.
Create the release with policy_bundle_id
curl -X POST http://localhost:8000/v1/releases \
-H "Content-Type: application/json" \
-d '{
"workload_id": "a1b2c3d4-0000-0000-0000-000000000001",
"environment": "production",
"target_lane": "all",
"policy_bundle_id": "p1a2b3c4-0000-0000-0000-000000000001"
}'Bind the policy bundle version to the release
curl -X POST http://localhost:8000/v1/releases/{release_id}/bindings \
-H "Content-Type: application/json" \
-d '{
"asset_version_id": "pv1a2b3c-0000-0000-0000-000000000001",
"role": "policy_bundle"
}'If you only set
policy_bundle_idand do not add thepolicy_bundlebinding,approved -> activecan fail because release activation validates required binding roles.
Common obligations
| Obligation | Purpose |
|---|---|
capture_evidence | Require handler-produced evidence artifacts |
require_human_review | Require human review before execution proceeds |
restrict_knowledge_base_scope | Limit retrieval to a governed subset |
apply_token_budget | Enforce a maximum total token budget |
use_release | Route execution through a specific release |
use_model_config | Steer execution toward a specific model configuration |
redact_or_filter_output | Apply output filtering or redaction rules |