Skip to main content

Policy simulator

The Policy simulator screen lets you compare the decisions of a candidate policy with those of the active policies, on inputs you write and on decisions recorded by the platform, without changing anything, and read the revisions of a policy with the difference between two of them. Portal path: /security/policy-simulator, menu Security and governance > Policy simulator. It is used by security officers, tenant administrators and developers before they publish a policy change, and by auditors who read the history.

What the screen shows​

The screen (PolicySimulator in portal/src/screens/security-kit.tsx) has two sections.

  • Simulate a candidate policy ("Nothing is changed: decisions are compared on inputs and recorded decisions."): a form with Candidate (JSON, prefilled with a builtin policy that denies model.invoke), Policy (identifier of the policy the candidate replaces; empty: the candidate is added), Inputs (JSON list of Policy Contract requests, default []) and Recorded decisions (number of the last recorded decisions to replay, default 50). The result shows four badges, Total, Changed, Allow to deny, Deny to allow (amber when above zero), and a table with Current allows, Candidate allows, Changed and Input, one row per compared input. Without any row: "No input compared".
  • Revisions ("Every create, update and delete appends an immutable revision."): a picker Policy with Apply, then a table of the revisions, columns Revision, Change, Name, Version, Status, and next to it "Diff from revision {from} to {to} (click two rows)" with the JSON of the difference. Without revision: "No revision".

An example of Candidate and Inputs:

{
"candidate": {"name": "candidate", "engine": "builtin", "priority": 100,
"rules": [{"effect": "deny", "actions": ["model.invoke"], "resource_types": ["model"]}]},
"inputs": [{"action": "model.invoke", "resource": {"type": "model", "id": "claude-sonnet-eu"}}]
}

Actions​

Button or actionWhat it doesAPI callRoles
Run (Simulate a candidate policy)Evaluates every input and recorded decision with the active policies, then with the candidate in place, and returns the differences; audited as policy.simulate. Nothing is stored.POST /v1/policies/simulatetenant_admin, security_officer, developer (form hidden for others)
Apply (Revisions, picker Policy)Loads the revisions of the policy and the difference of its last change.GET /v1/policies/{policy_id}/revisions, GET /v1/policies/{policy_id}/revisions/difftenant_admin, security_officer, auditor, developer
Click on a first row, then a second rowSets the range from then to and reads the difference between these two revisions; a third click starts a new range.GET /v1/policies/{policy_id}/revisions/diff (query from_revision, to_revision)Same
Retry (error state)Reads the revisions again.GET /v1/policies/{policy_id}/revisionsSame

Roles​

The portal shows the simulation form to ROLES.policySimulators (tenant_admin, security_officer, developer, portal/src/lib/kit.ts); other users see "Requires one of these roles". The Control Plane (services/control-plane/src/eai/domains/policies/__init__.py) requires the same roles for the simulation, and tenant_admin, security_officer, auditor or developer for the revisions and their difference. platform_admin passes every role check; its role is removed from the simulated subjects.

Typical tasks​

Measure the effect of a new rule before publishing it​

  1. Write the candidate. The candidate is a full policy, same format as on the Policies screen; only the builtin engine can be simulated. Edit Candidate. Expected result: valid JSON in the field. If it fails: "Invalid JSON" under the field.
  2. Choose what to compare. Recorded decisions show the real traffic, inputs show the cases you care about. Keep Recorded decisions 50 and add your Inputs. Expected result: at least one source of cases. If it fails: validation_error (HTTP 422) "inputs or recorded is required" when both are empty or zero.
  3. Run. Click Run. Expected result: the four badges and one row per case; Allow to deny counts what the candidate would block. If it fails: simulation_engine_unsupported (HTTP 422) for an opa candidate; forbidden for a missing role.
  4. Decide. No change was made. If the differences are the expected ones, create or edit the policy on the Policies screen. Expected result: the decisions of the platform follow the new rule.

Compare two revisions of a policy​

  1. Load the history. Enter the identifier (UUID) of the policy in Policy and click Apply. Expected result: the revisions with their Change (create, update, delete). If it fails: not_found means an unknown policy or an identifier that is not a UUID.
  2. Pick the range. Click the older revision, then the newer one. Expected result: "Diff from revision X to Y" and the JSON of the difference. If it fails: not_found with policy_revision means the range does not exist.

Troubleshooting​

SymptomCauseFix
validation_error (HTTP 422) "inputs or recorded is required"No input and Recorded decisions at 0.Add inputs or replay recorded decisions.
validation_error (HTTP 422) on CandidateThe candidate does not follow the policy format (name, engine, rules), or more than 200 inputs, or Recorded decisions above 500.Fix the JSON or the numbers.
simulation_engine_unsupported (HTTP 422)Only builtin rule candidates can be simulated.Simulate a builtin candidate.
not_found with policyPolicy is unknown or not a UUID.Copy the identifier from the Policies screen.
forbidden (HTTP 403)The simulation needs tenant_admin, security_officer or developer; the revisions also accept auditor.Ask for one of these roles.
"No input compared"The simulation returned no case.Add inputs, or make sure decisions were recorded.