Project-Specific Context: Simulation Engine
This document defines the architecture, data models, and canonical flow for the OCI Policy Simulation Engine, as consumed both via UI and Model Context Protocol (MCP) APIs.
1. Overview
The PolicySimulationEngine serves as the single source of truth for all permission simulation business logic. It enables the app to simulate allow/deny outcomes for OCI API operations, supporting conditional evaluation via “where” clauses. This engine powers both interactive user flows (Simulation UI Tab) and programmatic automation (via MCP server/tools).
Engine location:
src/oci_policy_analysis/logic/simulation_engine.pyDriven by:
UI:
src/oci_policy_analysis/ui/simulation_tab.pyMCP:
src/oci_policy_analysis/mcp_server.py
Data Schemas:
src/oci_policy_analysis/common/models.pyDemo usage:
docs/source/simulation_engine_usage_examples.py
2. Key Data Models for Simulation (MCP/JSON)
All simulation-related requests and responses use schema-typed dictionaries from common/models.py:
Preparation:
SimulationPrepareRequestSimulationPrepareResponse
Single Simulation Run:
SimulationScenarioSimulationResult
Batch Simulation:
SimulationBatchRequestSimulationBatchResponse
Policy/User/Group Filters:
PolicySearch,PolicyFilterResponseUserSearch,UserSearchResponseGroupSearch,GroupSearchResponseDynamicGroupSearch,DynamicGroupSearchResponse
Model fields are strictly enforced. Example:
To specify a principal for simulation, use a string for
"any-user"/"service"or a two-element list[domain, name]for users/groups/dyn-groups.“Where” variables are always a mapping from
strtostr.
3. Simulation Stages & Canonical Flow
Whether simulation is initiated from the UI or via MCP, the process is broken into stages. The engine flow is the same; the UI flow is now structured into three logical subtabs.
3.1 Canonical Engine Stages (UI + MCP)
Select Context:
Compartment path
Principal type & value (domain/name, or string for “any-user”/”service”)
Load Policy Statements (Real + Prospective):
Call:
UI: via simulation engine API
MCP: via
prepare_simulationtool
Output:
Principal Key (calculated example
"principal_key": "user:Default/anita")Merged list of applicable policy statements for the given
principal_keyandeffective_path, including:Real tenancy statements from the policy repository.
Any prospective (what‑if) statements whose
compartment_pathis equal to or a parent ofeffective_path.
Engine API:
Canonical call is
get_applicable_statements(principal_key, effective_path)(or equivalent wrapper).
The engine internally maps theprincipal_key(which encodes both the principal type and identity) to the properPolicySearchfilter (exact_users,exact_groups,exact_dynamic_groups, orsubject), ensuring correct filtering for all principal types and then merges in prospective statements (see section 10) before returning to UI/MCP.
Gather Where-Input Values:
Engine exposes a derived view of required where‑variables based on the selected statements’ condition strings (after real + prospective statements have been merged and filtered).
UI: Auto‑generates dynamic input fields for variables every time the set of included (checked) statements changes. The where‑context panel is always present, even if the user chooses not to fill any values (in which case the
where_contextis{}).MCP: Client must provide a mapping of variable inputs as JSON (the engine does not require a separate “load fields” call; it simply consumes the mapping).
Select/Specify API Operation:
Operation name, e.g.
oci:ListBuckets.UI: Single operation per run (though the engine supports batch scenarios).
MCP: May run batches where each scenario has its own API operation.
Simulate & Retrieve Result:
Call simulation engine with all context/inputs:
Effective Path (compartment)
Principal key (format above)
Where clause values (as JSON dict; may be empty
{})Optional list of statements to consider.
For UI, the internal IDs of the currently included statements are collected and passed (these IDs may reference real or prospective statements; the engine treats them uniformly).
For MCP, this is typically omitted, which implies “use all applicable statements from the filter (real + prospective)”.
Whether to provide trace output (if
True, include a per‑statement trace and final permission set).
Output:
Allow/Deny result
Set of granted permissions (reflecting the combined effect of real and prospective statements)
Optional: decision trace (per-statement allow/deny, conditions, including which statements were prospective).
Review/Present Results:
UI: Results & trace are displayed in a dedicated Simulation History view and can be exported as JSON. Each run is named (e.g.,
"oci:ListBuckets | user:Default/anita") and added to a history list.MCP: Results returned as structured JSON per canonical model.
For MCP, there is a limit on returned JSON, so responses are brief but contain enough detail for the AI consumer to build a rich explanation.
All simulation stages are orchestrated outside the engine. The engine itself is stateless and enforces all logic and normalization.
4. Implementation: Three-Subtab UI vs. MCP Tool
The UI’s Simulation experience is now organized into three subtabs that together orchestrate the same canonical engine stages described above.
Stage |
UI Tab (simulation_tab.py) – Subtab |
MCP Server (mcp_server.py) |
Simulation Engine |
|---|---|---|---|
1. Select context |
Simulation Environment: dropdowns for effective path and principal type/name; inline table for Prospective (what‑if) statements |
Receives context in |
Receives canonical input |
2. Load statements |
Statements and Context: auto-loads applicable statements whenever environment changes; merges real + prospective statements |
|
Same single entrypoint ( |
3. Gather where-inputs |
Statements and Context: dynamic where‑inputs panel automatically rebuilt whenever included (checked) statements change; values preserved when possible |
Provided as part of simulation payload |
Receives as mapping |
4. Select API operation |
Statements and Context: API operation combobox + validation + optional notes |
API parameter in |
Uniform behaviour |
5. Simulate |
Statements and Context → Simulation History: Run button calls |
|
Only business logic layer |
6. Present results |
Simulation History: trace history dropdown, “Show trace details” toggle, JSON export |
Returns JSON output in |
Dict output, schema-driven |
NO business logic is duplicated or implemented in the UI or MCP server.
UI and MCP are strictly callers, responsible for driving stages and displaying/returning output.
Any changes to simulation or scenario semantics must be made only in the engine.
5. Data Models for Each Simulation Call
Preparation Stage Example:
# SimulationPrepareRequest Example
{
"compartment_path": "ROOT/Finance",
"principal_type": "user", # Must be one of Literals "service", "user", "group", "dynamic-group", "any-user", "any-group"
"principal": ["Default", "anita"] # Optional - don't pass if type is any-user or any-group
}
# SimulationPrepareResponse Example
{
"required_where_fields": ["user.department", "request.time"],
"principal_key": "user:Default/anita" # Let the engien calculate this internally
}
Simulation Stage Example:
# SimulationScenario Example
{
"compartment_path": "ROOT/Finance",
"principal_key": "user:Default/anita"
"api_operation": "oci:ListBuckets",
"where_context": {"user.department": "Finance", "request.time": "2026-01-22T09:00:00Z"},
"checked_statements": ["12345","23456"] # Optional - if omitted, use all statements from filter. If included, get the statements and only use the ones passed in.
}
# SimulationResult Key Fields
{
"result": "YES" or "NO",
"api_call_allowed": true,
"final_permission_set": [...] // if trace enabled,
"missing_permissions": [...],
"failure_reason": "",
"trace_statements": [...] // if trace enabled
}
All model definitions in common/models.py must be strictly followed by both UI and MCP server.
6. Simulation Stage Flow Comparison (Flowchart)
flowchart LR
A(Select context – Environment subtab) --> B(Load applicable statements – Statements & Context)
B --> B2(Merge real + prospective statements in engine)
B2 --> G(Select statements for consideration – check/uncheck)
G --> C(Auto-build where-inputs panel from merged set)
C --> D(Select API operation)
D --> E(Run simulation on merged statement set)
E --> F(Review results – Simulation History)
UI path (three subtabs):
Simulation Environment: A
Statements and Context: B → G → C → D → E
Simulation History: F
MCP path: A-B-C-D-E driven entirely by JSON tool calls.
7. Deduplication Policy
Simulation, policy mapping, and condition evaluation logic MUST exist only in the simulation engine.
Code in
simulation_tab.pyormcp_server.pymay never reimplement the logic of any simulation stage—only drive input/output orchestration.Any shared workflow improvements (staging, normalization, batch mode, etc.) should begin in the engine.
All future contributions must comply with this policy.
8. Limitations, Known Issues, and Roadmap
Current Limitations:
UI and MCP paths must be kept manually in sync if engine APIs are refactored.
Existing engine focused on policy simulation for a single principal-context-operation set at a time; future multi-context or batch extensions are designed but not fully exercised in UI.
Some edge case error handling (e.g., improperly-formatted input in MCP) may be less user-friendly than in UI.
Ensure TypedDict models in
common/models.pystay in alignment with future simulation improvements.
Future Goals:
Full “batch” simulation flows in both UI and MCP
Unified scenario harness for end-to-end and regression tests across both drive paths
Improved visual/JSON diff for large sets of simulation traces
OpenAPI spec generation for MCP endpoints
10. Prospective (What-If) Policy Statements
The simulation engine also supports prospective policy statements: hypothetical or planned policies that do not exist in the live tenancy but should be evaluated as if they did. This enables “what-if” analysis for future changes.
Storage & Scope
Stored per tenancy in settings under
simulation_prospective_statements_by_tenancy.Keyed by tenancy OCID, value is a list of lightweight statement dicts with at least:
compartment_path(hierarchy path where the statement would live)description(user-friendly label, used by UI)statement_text(full OCI IAM policy statement text)
The Simulation Tab’s “Manage Prospective Statements” dialog lets users create/edit/delete these rows for any compartment in the tenancy.
Engine Representation
Internally, the engine normalizes these into full statement dicts and keeps them in:
PolicySimulationEngine._prospective_statements: list[dict]
Normalized fields include:
internal_id(unique, in aprospective-*namespace)compartment_pathpolicy_name(derived fromdescriptionwhen not provided)statement_textOptional
conditions,subject_type, etc. once parsed.is_prospective = Truemarker for UI/analysis.
Engine APIs
get_prospective_statements() -> list[dict]Returns the current in-memory list of normalized prospective statements.
set_prospective_statements(statements: list[dict]) -> NoneReplaces the engine’s prospective statement set.
Caller passes lightweight dicts with
compartment_path,statement_text, optionaldescription.Engine normalizes, sets
is_prospective=True, and assigns uniqueinternal_idvalues.
Applicability & Merging (Stage 2)
Prospective statements are merged into the normal flow as part of Stage 2 – Load Policy Statements via
get_applicable_statements:def get_applicable_statements(self, principal_key: str, effective_path: str) -> list[dict]: # 1) Use policy_repo.filter_policy_statements for base (tenancy) statements. # 2) For each prospective statement, if its compartment_path is equal to # or a parent of effective_path, include it. # 3) Return a merged list.
The same principal filtering semantics apply: callers pass a
principal_keyandeffective_path, and any prospective statements that would be in scope for that path are included alongside real tenancy statements.Downstream stages (where-field extraction, simulation, history) always operate on this merged set; they do not distinguish between real and prospective statements for business logic.
Simulation Semantics
After merging, prospective statements are indistinguishable from real statements for:
where-clause extraction (
get_required_where_fields)selection-by-internal-id from the UI
evaluation in
simulate_and_record(allow/deny logic, permissions, and trace output).
The Simulation Tab displays them with a
[Prospective]prefix in the “Policy Path/Name” column but otherwise treats them identically.
UI Integration Notes
The Simulation Tab includes a “Manage Prospective Statements” button in the Applicable Policy Statements table.
The dialog explains scope: users may define statements anywhere in the tenancy, but only those applicable to the currently selected compartment/principal will appear in the main table.
On save, the UI:
Calls
set_prospective_statementswith the new list.Updates the per-tenancy settings entry
simulation_prospective_statements_by_tenancy[tenancy_ocid]and persists viaconfig.save_settings.Reloads the Applicable Policy Statements table so new prospective entries appear.
As with all other simulation behavior, the engine remains the single source of truth for how prospective statements are normalized, merged, and evaluated. The UI is responsible only for collection and presentation of these inputs.