Embedding and Integrating OCI Policy Analysis
Deploy web or MCP directly on OCI
OCI Policy Analysis supports a ready-to-run web application, MCP server, or combined web/MCP deployment on OCI Container Instances using Resource Principal. Choose this first when you want a hosted interface or agent endpoint over tenancy data: no custom Python wrapper or user API key in the running container is needed. Grant the workload the required read/inspect permissions and configure networking for the clients and OCI APIs it must reach.
Follow Setup for workload permissions and MCP configuration. The canonical run and deployment guide covers web, MCP, and combined deployments, source versus published-release images, health checks, and explicit replacement/rollback. Use the operations guide from the same Git revision as your deployment. Deployment scripts manage the named Container Instance; IAM, registry, network, Load Balancer, and backends are configured separately. Image or runtime changes use delete/create replacement with downtime.
Choose deployment or embedding
Pattern |
What you run |
Authentication |
What you supply |
|---|---|---|---|
Hosted web or MCP |
Packaged application on OCI Container Instances |
Resource Principal |
Workload IAM, runtime settings, networking and durable retention if required |
Custom OCI Function |
Python core and focused services inside your own function image |
Function Resource Principal |
Function handler, schedule, capacity/timeout validation, output storage and delivery |
Scheduled job on an OCI VM |
CLI or Python core, optionally alongside a standalone MCP process |
Instance Principal |
Scheduler, job locking, snapshot retention and report delivery |
Offline integration |
Python core over saved combined-cache JSON |
No OCI authentication for offline loads |
Input snapshots, analysis options and output handling |
The Function and scheduled-job patterns below are integration recipes, not packaged function deployments or a built-in scheduling service. Oracle documents Function resource-principal access and recurring Function invocation through Resource Scheduler. A Function starts a bounded analysis job; a web/MCP container keeps a service available between requests. Size the job for actual tenancy collection and analysis time; use a VM or other worker when that exceeds the function’s limits.
The current analysis snapshot
The in-memory repository and its cache/export representation are analysis snapshots. They are intentionally allowed to contain incomplete, unresolved, or invalid data. For example, a policy can be retained with validation findings so a caller can find and correct it rather than silently losing it during load.
An integration must therefore preserve the distinction between:
a successful load and a complete load;
no matching policy and an indeterminate result caused by incomplete data;
a normalized statement and an OCI-authoritative authorization decision.
See reference catalog coverage for resources without published mappings, conditional API requirements and reviewed corrections.
Treat validation findings, load warnings, collection timestamp, source mode, and any scope limits as output that belongs beside the requested result.
Saved snapshot compatibility in 7.0
The combined-cache format is unchanged from 6.5.7: version: 2, with current
writers using schema_version: 3. The existing policies and policy_statements
sections remain required. Optional inventory and metadata fields retain their
existing defaults, so older snapshots using the supported structure can load
without a fresh OCI collection. This does not add support for formats that 6.5.7
already rejected.
Regular and cross-tenancy corpora, intelligence overlays, and the permissions report are rebuilt at runtime; they are not added to the combined-cache payload. Statement records can contain updated derived fields, including canonical principal keys and structured association endpoints. Original statement text remains available. For old cross-tenancy association records without complete endpoint structure, corpus construction reparses the source text on a detached copy; it does not rewrite the cache file.
The breaking change is the removal of the former common and logic Python
import packages (and the legacy top-level web aliases), not a requirement to discard saved tenancy data. Callers should
migrate imports and run the appropriate post-load profile after loading. Changes
in validation, permission mappings, and analysis can legitimately change derived
results when the same snapshot is processed by a newer version.
Python embedding
The application core API reference documents the context, load, query, report and historical-analysis services used here.
Constructing AppContext loads bundled reference data but does not authenticate,
collect tenancy data, or compile a corpus. LoadService replaces the active
snapshot and derived engines/corpora. Read those from the current context after
each load; serialize loads, queries, and mutable analysis operations rather than
sharing a context across concurrent jobs. A fresh context per scheduled run is
the simplest boundary.
Post-load profile |
Use when |
Result |
|---|---|---|
|
Focused queries, inventory reports, MCP |
Query foundations and regular/cross-tenancy corpora; no full recommendation or permissions report |
|
Recommendations, permissions reports, simulation workflows |
Full post-load intelligence, corpora, permissions report and simulation preparation |
|
Collection-only workflows |
Skips enrichment; derived analysis must be prepared before use |
AppContext is the reusable application wiring used by non-desktop consumers.
Construct it from settings, load data through LoadService, then use a focused
service for the task. The service boundary is preferable to reaching directly
into UI code.
from oci_policy_analysis.application.context import AppContext
from oci_policy_analysis.application.core.support import config
from oci_policy_analysis.application.services.load_service import LoadService
from oci_policy_analysis.application.services.mcp_query_service import MCPQueryService
from oci_policy_analysis.application.core.models.models_policy import PolicySearch
context = AppContext.from_settings(config.load_settings())
loaded = LoadService(context).load_from_cache('my-tenancy-cache', post_load_profile='minimal')
if not loaded.success:
raise RuntimeError(loaded.message)
statements = MCPQueryService(context).filter_policy_statements(PolicySearch(resource=['buckets'], verb=['read']))
Use the current context and services rather than presentation modules or
private helpers. The former top-level oci_policy_analysis.common and
oci_policy_analysis.logic compatibility packages are removed in 7.0; import
models, repositories, and support helpers from application.core instead.
The top-level oci_policy_analysis.web aliases are also removed; ASGI integrations
should import oci_policy_analysis.presentation.web.main:app. The published
oci-policy-analysis-web command name is unchanged.
Keep integration tests close to the package version you run;
there is not yet a separately versioned plugin SDK.
Choosing live policy discovery
Live loads support two policy discovery methods through the same detail fetch and normalization pipeline:
result = LoadService(context).load_from_tenancy(
use_instance_principal=False,
profile='DEFAULT',
recursive=True,
policy_load_method='compartment',
post_load_profile='minimal',
)
resource_search is the default. It follows all Resource Search pages, then fetches
full policy details. compartment lists active policies in each loaded compartment
and fetches the same details, bypassing the policy search index. With recursive=False,
only the tenancy root is listed; otherwise the root and all discovered accessible
compartments are included. Identity-domain depth does not limit policy recursion.
An explicit argument overrides settings['policy_load_method']. When the argument
and setting are absent, older callers continue to use Resource Search. Unsupported
values raise ValueError before the current context is reset. The chosen method is
retained for subsequent policy-only refreshes of that live repository and cleared
when the snapshot is reset. Compartment listings and detail fetches use bounded
workers; parsing updates the repository serially. Individual failures remain in
collection diagnostics; total discovery/fetch failure is rejected rather than
accepted as an empty inventory. Success still does not establish complete coverage.
The independent defined-tag namespace catalog continues to use Resource Search.
Qt exposes Policy loading in Overview & Settings → Live tenancy only in Advanced mode. The choice is saved locally, restored at startup, and continues to apply if Advanced mode is hidden. The loaded snapshot coverage panel identifies the method used. Cache/export payloads remain version 2/schema 3: this option is a local load preference, not a new stored snapshot field, so no cache migration is needed and offline queries never rewrite source files.
Desktop app
The new desktop app is a modern interface around the same services used by the
original desktop app, CLI, web app, and MCP server. See the
7.0 guide for what’s new, the
new desktop usage guide for loading and evidence inspection, and
MCP Assistant guide for the managed snapshot endpoint.
The original Tkinter app remains available through
oci-policy-analysis-legacy-ui. Neither desktop app is required for embedding.
Scheduled reporting and retained history
Weekly dynamic-group review
A custom weekly job can collect IAM/policy data, find dynamic groups with no known policy references, and write a review artifact. After the cache-load example above, the same context supports:
from oci_policy_analysis.application.core.models.models_iam import DynamicGroupSearch
from oci_policy_analysis.application.services.reports_service import ReportsService
unreferenced = MCPQueryService(context).search_dynamic_groups(DynamicGroupSearch(in_use=False))
reports = ReportsService(context)
inventory = reports.get_report('policy-inventory')
artifacts = reports.export_formats(inventory) # json, markdown, html strings
unreferenced is input to your own report; it is not a built-in report ID.
The loaded in_use field means known policy-reference status, not recent use by
resources or OCI Audit activity. Missing inventory, unresolved references, or
collection failures can make absence inconclusive. Include collection scope,
load warnings, source timestamp and matching rules in the review; do not turn
this list into automatic deletion instructions. See Reports for
built-in reports and their enrichment prerequisites.
For a live Function-based job, replace the offline load with:
loaded = LoadService(context).load_from_tenancy(
use_instance_principal=False,
use_resource_principal=True,
recursive=True,
compartment_domain_search_depth=2,
post_load_profile='minimal',
save_cache_after_load=False,
)
if not loaded.success:
raise RuntimeError(loaded.message)
This is the core call inside a handler you provide, not a complete Function
handler or deployment. Create the context with explicit job settings, configure
writable application storage/logging, and store outputs in your chosen durable
location. Use full if the requested reports require full enrichment. Domain
depth 2 covers root plus immediate children; increase deliberately for deeper
identity-domain coverage. It is independent of recursive policy loading.
On an OCI VM use use_instance_principal=True and
use_resource_principal=False instead.
Weekly refresh with historical comparisons
Keep a dated baseline combined-cache export in durable storage, with the collection scope, timestamp, diagnostics and application version.
Run a scheduled collection with the same intended scope. A core/CLI job can save/export the new snapshot. Alternatively, an authenticated MCP client can request
data_operations(operation='reload')from a live standalone MCP server. When OAuth update scopes are configured, the caller needs that scope.Retain the resulting combined-cache snapshot under a new dated identity and check that the expected sections were actually collected. A successful reload and the server’s current snapshot are not, by themselves, a retention policy.
Compare baseline and target through
HistoricalAnalysisService.compare_snapshotsor Qt Historical Comparison. Store the comparison alongside both inputs, then deliver the report through your own workflow.
The standalone reload path saves a cache after loading; arranging durable storage, export/backup, unique retention names, retries and scheduling remains your job. Container replacement or Function termination must not remove the only copy of history. For example, an integration can explicitly upload snapshots and reports to Object Storage, with separate write permissions for that output location. MCP status/reload responses are summaries, not downloadable snapshot artifacts. Serialize refreshes and readers at your orchestration boundary; the shared context is not an automatic concurrency boundary.
Qt’s embedded MCP endpoint is query-only and pins the loaded snapshot. It exposes
neither data_operations nor reload/history tools; stop it before loading another
snapshot. Use the standalone service or a separate collection job for unattended
refresh. See MCP and historical analysis.
Once your job has read the two combined-cache JSON objects, compare them without loading either into the active context:
from oci_policy_analysis.application.services.historical_analysis_service import HistoricalAnalysisService
comparison = HistoricalAnalysisService(context.cache).compare_snapshots(
baseline_snapshot,
target_snapshot,
left_name='Previous week',
right_name='This week',
meaningful_only=True,
)
Historical comparisons show differences between collected evidence, not a complete OCI change log. Known different tenancy OCIDs are rejected; incomplete sections remain qualified or unavailable. Meaningful mode ignores ephemeral identifiers while retaining source changes: comment-only statement edits are modifications; grant or condition changes appear as removal/addition. No source snapshot is rewritten by comparison.
Extension checklist
Create a context and load one input source.
Run the post-load profile appropriate to the caller (
minimalfor focused programmatic analysis; full profiles when their derived overlays are needed).Query through a service or engine, retaining source statements and findings.
Return a task-level result plus data-quality and provenance information.
Test against valid, invalid, and partial snapshots.
For scripts and scheduled jobs, use the CLI guide. For agent-facing queries, use the documented MCP tools instead of constructing or mutating internal repositories. Both return analysis evidence, not an OCI-authoritative allow/deny decision.
Explain a run and draft scoped what-if candidates
After loading a context, use the shared workbench service for the same request evidence shown by Qt. An explicit empty checked-ID list means no statements. Execution appends runtime history; candidate generation does not save prospects or change OCI. Serialize these calls with loads and prospective mutation.
from oci_policy_analysis.application.services.simulation_service import SimulationService
from oci_policy_analysis.application.services.simulation_workbench_service import SimulationWorkbenchService
selection = SimulationService(context).statement_candidates(
compartment_path='ROOT/Applications',
principal_type='group',
principal=('Default', 'AppDevelopers'),
)
workbench = SimulationWorkbenchService(context)
report = workbench.execute(
{
'principal_type': 'group',
'principal': ('Default', 'AppDevelopers'),
'principal_key': selection['principal_key'],
'compartment_path': 'ROOT/Applications',
'api_operation': 'objectstorage:CreateBucket',
'checked_statement_ids': [row['internal_id'] for row in selection['statements'] if row['checkable']],
'where_context': {},
}
)
drafts = workbench.policy_candidates(
report,
principal_key=selection['principal_key'],
granularity='permission', # also resource or family
policy_path='ROOT',
effective_path='ROOT/Applications',
where_clause="request.operation = 'CreateBucket'",
)
# Review drafts["candidates"], additional permissions, unmapped permissions
# and scoped corpus examples before explicitly saving through prospects.
original_runs = workbench.history()
related_choices = report['related_scenarios']
Use actual inventory paths/principals and catalog operation labels. History and reports are detached copies with original inputs; runtime snapshot IDs reject candidate generation after replacing the engine. A related operation requires a new compartment selection, preparation and explicit run, not reuse of primary checked IDs. Matching denies, conditions and inventory/reference coverage remain constraints after any suggested allow. The current reverse mapping uses greedy catalog coverage rather than proving globally minimal privilege.
7.0 integration changes and tag inventory
AppContext.ai and the desktop OCI Generative AI backend were removed in 7.0; callers
should remove AI repository/model setup. Existing saved AI settings are inert. This does
not change combined-cache version 2/schema 3 or require snapshot migration.
TagBasedPolicyService.inventory(context.policy_repo.regular_statements) returns detached
statements, conditions and namespace_count data. Predicate rows retain the full condition
and provenance; a matching atom does not establish that the complete condition passes.
No cache, source record or OCI resource is modified. Rebuild the projection after reload.
Qt desktop live loading supports config profiles (including session tokens) and Instance Principal. A saved Resource Principal selection falls back to profile authentication; explicit Resource Principal worker requests are rejected. Resource Principal remains supported for container-based web/MCP and shared service callers.