Skip to main content

Policy engine

The Policy Engine gives Jentrix one enforceable home for safety rules. Every risky action — a risky MCP tool, an outbound webhook, a repo push, a bulk mutation, a credential grant — is evaluated by a deterministic evaluatePolicy(action, context) before it runs. A denial returns the same structured error envelope agents already understand, with the policy reason and the next step.

How resolution works

Policies are layered and the most-restrictive decision wins:

system → workspace → project → agent profile → work order → one-time grant

For a given action the engine collects every policy whose layer applies to the context and whose template matches the action, then takes the most restrictive outcome. The ordering is DENY > REQUIRE_ > ALLOW*; same-restrictiveness ties break by priority (higher wins), then by layer specificity. When no policy matches, the action is allowed (low-risk paths default to ALLOW).

Outcomes

OutcomeMeaning
ALLOWThe action may proceed.
DENYThe action cannot proceed.
REQUIRE_APPROVALThe action pauses and an admin must approve it (Control Tower).
REQUIRE_STRONGER_CONTEXTThe Work Order must include missing fields/sources.
REQUIRE_SAFER_MODEConvert the action to draft / dry-run / read-only.
REQUIRE_CREDENTIAL_BROKERThe action must use a brokered grant, not raw credentials.

A denied MCP/admin action returns { error: { code: "FORBIDDEN", message, hint } } where message is the policy reason and hint is the next step.

Templates (the v1 policy "language")

There is no DSL. A policy is a typed template plus a small, validated JSON condition object. The built-in templates:

TemplateDomainGatesDefault outcome
read_only_workspace(all)every mutating action — a read-only client workspaceDENY
draft_vs_sendcommunicationcomm:send (drafting is allowed)REQUIRE_SAFER_MODE
repo_commit_denyreporepo:commit (optional branch) — forbid the harness local-commit boundary (runner-performed, not brokered; no approval path)DENY
repo_push_approvalreporepo:push (optional branch)REQUIRE_APPROVAL
repo_branch_create_approvalreporepo:branch_createREQUIRE_APPROVAL
repo_pr_create_approvalreporepo:pr_create (optional baseBranch)REQUIRE_APPROVAL
merge_approvalreporepo:merge (optional baseBranch) — the production boundary under gate_before_mergeREQUIRE_APPROVAL
deploy_environment_gaterepodeploy:preview / deploy:production (optional environment)REQUIRE_APPROVAL
high_risk_changerepoa push/PR/merge over a diff-size cap or touching a sensitivePaths entryREQUIRE_APPROVAL
webhook_admin_approvalwebhookcreating an outbound webhookREQUIRE_APPROVAL
admin_tool_approvalmcp toollisted admin tools (tools: [...])REQUIRE_APPROVAL
max_spend_per_runjoba job whose estimatedCostUsd exceeds maxUsdREQUIRE_APPROVAL
harness_advance_approvalautomationharness:advance — govern the harness orchestrator advancing a run (put a human in the outer loop)REQUIRE_APPROVAL
harness_freeze_denyautomationharness:freeze — forbid an automated/policy-initiated run freeze (freeze stays a manual operator control; no approval path)DENY

The delivery action classes are open strings (no schema change), evaluated under the repo domain: repo:merge, deploy:preview, deploy:production, ci:rerun (an explicit Actions re-run — off by default; the CI-triage path pushes a commit instead), release:approve, and release:rollback. The merge, deploy, and rollback boundaries are also brokered and leased (M8.3 Stage 3.4) — the policy gate authorizes the action; a short-lived scoped credential carries it out. An agent never holds a standing merge/deploy token.

Two SYSTEM-scope defaults (webhook_admin_approval, repo_push_approval) are seeded off; admins enable or add policies at Settings → Policies.

Approvals and overrides

A REQUIRE_APPROVAL outcome surfaces in the Control Tower policy queue. An admin resolves it, which mints an ApprovalBinding bound to the exact action-payload hash + actor + an expiry. The binding:

  • authorizes only that exact payload — it cannot be reused for a different payload (the hash differs);
  • is single-use and time-bounded;
  • relaxes a REQUIRE_APPROVAL to ALLOW only when the matched policy sets allowOverride. DENY is never relaxed.

A partial unique index guarantees at most one active binding per (workspace, actionHash, actor), so the same payload can be re-approved later once a binding is consumed/expired/revoked.

What gets evaluated

Enforcement is wired into the ops core (so both the UI and MCP surfaces are covered) and surfaced through safe() as a FORBIDDEN envelope for MCP tools:

  • Webhook creation (create_webhook / settings).
  • Bulk mutations (bulk_create_tasks / bulk_update_tasks / bulk_move_tasks).
  • Board-kind conversion (convert_board_kind).
  • GitHub outbound sync (held back, not denied loudly — the sync is skipped).
  • Credential grant issuance (the CREDENTIAL domain, before any grant).

Every evaluation writes an append-only PolicyEvaluation with redacted inputs.

MCP tools

ToolReturns
list_policiesWorkspace policies with template, domain, outcome, priority, enabled. Filter by domain/scope.
get_policy_evaluationOne evaluation: action class, outcome, reason, matched policy, and redacted inputs.
list_policy_evaluationsThe append-only evaluation feed (newest first). Filter by runId/outcome/domain/since; paginated.

Reads never surface credential backend references or any secret material. The related admin-scope write tools are live: resolve_approval resolves a require_approval evaluation by minting a scoped, time-bounded approval binding, and request_credential_grant issues a short-lived credential grant (see Credential broker).