Skip to main content

Policy as code

What you will build​

You will extend OpenJCockpit's default OPA policy with one rule: the QA Engineer agent may read the repository but never change it. You test the rule, then see it block a tool call.

Prerequisites
  • Docker
  • A checkout of OpenJCockpit, and the running stack for the last step
  • The files in downloads/policy
Tested version
This tutorial was tested on OpenJCockpit develop (draft). The rule and tests were verified with OPA 0.70.0; the live-stack step is not yet verified.. OpenJCockpit is in active development, so details may differ on other versions.
Video coming soon: Policy as code: a rule that blocks an agent. The written steps below cover the same content.
Policy as code: a rule that blocks an agent

Background: what is evaluated​

OPA is asked for a decision on workflow starts, agents, skills and every tool call. Each decision returns allowed, reason, requiredApproval, riskLevel and auditTags.

The default policy, infrastructure/opa/policies/workflow_decision.rego, evaluates rules top to bottom and the first match wins:

It is a starting point, not final governance logic.

Steps​

1. Read the example rule​

The downloadable workflow_decision.rego is the default policy plus one branch, before the restricted-project rule:

} else = result {
qa_write_attempt
result := {
"allowed": false,
"reason": sprintf("Agent '%v' is read-only; tool '%v' changes the repository.", [input.agentId, tool_label]),
"requiredApproval": false,
"riskLevel": "high",
"auditTags": ["guardrail", "qa-read-only"],
}
}

and its helper:

write_operations := {"write_file", "commit", "push", "create_branch"}

qa_write_attempt {
tool_call
is_string(input.agentId)
endswith(lower(input.agentId), "qa-engineer")
write_operations[tool_operation]
}

2. Run the tests​

cd downloads/policy
docker run --rm -v "$PWD:/p:ro" openpolicyagent/opa:0.70.0 test /p -v
Checkpoint

OPA reports PASS: 15/15: the 12 default tests and 3 for the new rule.

3. Ask for a decision​

docker run --rm -v "$PWD:/p:ro" openpolicyagent/opa:0.70.0 \
eval -d /p/workflow_decision.rego -i /p/blocked-input.json \
'data.openjcockpit.workflow.decision' --format pretty
Checkpoint

The result has "allowed": false and the audit tags guardrail and qa-read-only.

4. Try it in the stack​

Copy the file over infrastructure/opa/policies/workflow_decision.rego, restart OPA with docker compose restart opa, run a workflow and read docker compose logs opa. Check the real agentId in the decision log first, because the rule matches ids that end in qa-engineer.

What just happened​

You changed behaviour by editing a file, not Java code. Policies are mounted as plain files, so a restart is enough. Every tool call goes through a policy-guarded path, and with OPA enabled a denial blocks the call. Decisions are written to the decision log for audit.

Limits to know:

  • With OPA_ENABLED=false every decision is allowed. Production runs with fail-closed enforcement and a locked config.
  • The default policy is permissive. Replace it with your organization's rules.
  • A tool call's decision input has agentId, projectName, the tool name and operation. It does not carry a project classification, so rules on projectClassification do not fire for tool calls.
  • Audit records are files in a Docker volume, not a tamper-evident store.

See policy enforcement.

Troubleshooting​

  • Agent tool calls are denied for every agent: the policy input lacks workflowId or customerId, for example an agent run outside a workflow. Start agents through a workflow.
  • Your rule never fires: compare your agentId pattern with the real value in docker compose logs opa.

Next steps​

Credential isolation explains why agents never see your Git credentials.