Skip to Content
Task guidesTechnical: Policies

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.

FieldRequiredDescription
idYesA 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.
conditionYesExactly 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_fieldsWith matchKey/value pairs that must all equal the runtime context (service_lane, resource_kind, interface_kind) for the rule to apply.
expressionWith expressionA non-empty policy expression evaluated against the same runtime context — comparisons (==, !=, in [...], not_in [...]), the matches glob operator, and and/or combinations.
outcomeYesallow, deny, require_approval, or allow_with_obligations.
reason_codeYesMachine-readable reason recorded for auditing.
obligationsNoAdditional obligations emitted with allow_with_obligations.
priorityNoInteger 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-contact

The 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/policies

The 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
SurfaceExample
APIPOST /v1/precedents/search
Python SDKclient.precedents.search(resource_kind=..., action=...)
CLIorqestractl precedent search --resource-kind ... --action ...

Binding a policy to a release

There are two policy-related fields in the current release model:

  • policy_bundle_id on the release record
  • a release binding with role policy_bundle

In practice today, a production-ready release should carry both:

  1. Set policy_bundle_id when creating the release so the runtime can resolve the governing bundle.
  2. Add a policy_bundle asset-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_id and do not add the policy_bundle binding, approved -> active can fail because release activation validates required binding roles.

Common obligations

ObligationPurpose
capture_evidenceRequire handler-produced evidence artifacts
require_human_reviewRequire human review before execution proceeds
restrict_knowledge_base_scopeLimit retrieval to a governed subset
apply_token_budgetEnforce a maximum total token budget
use_releaseRoute execution through a specific release
use_model_configSteer execution toward a specific model configuration
redact_or_filter_outputApply output filtering or redaction rules