openapi: 3.1.0 info: title: StellaOps Policy Engine REST API version: 1.0.0 description: | REST API for the StellaOps Policy Engine providing risk profile management, policy decisions, risk simulation, policy packs, and air-gap sealed mode operations. This API supports tenant-scoped operations with OAuth 2.0 authentication and scope-based authorization. contact: name: StellaOps Platform Team url: https://stellaops.org license: name: AGPL-3.0-or-later url: https://www.gnu.org/licenses/agpl-3.0.html servers: - url: https://api.stellaops.local description: Local development server - url: https://api.stellaops.io description: Production server security: - bearerAuth: [] - oauth2: [] tags: - name: Risk Profiles description: Risk profile CRUD, versioning, and lifecycle management - name: Policy Decisions description: Policy evaluation and decision endpoints - name: Risk Simulation description: Risk scoring simulation and analysis - name: Policy Packs description: Policy pack and revision management - name: AirGap description: Sealed mode and air-gap operations paths: # ============================================================================ # Risk Profiles # ============================================================================ /api/risk/profiles: get: operationId: ListRiskProfiles summary: List all available risk profiles tags: [Risk Profiles] security: - bearerAuth: [] - oauth2: [policy:read] responses: '200': description: List of risk profiles content: application/json: schema: $ref: '#/components/schemas/RiskProfileListResponse' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' post: operationId: CreateRiskProfile summary: Create a new risk profile version in draft status tags: [Risk Profiles] security: - bearerAuth: [] - oauth2: [policy:edit] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateRiskProfileRequest' responses: '201': description: Risk profile created content: application/json: schema: $ref: '#/components/schemas/RiskProfileResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' /api/risk/profiles/{profileId}: get: operationId: GetRiskProfile summary: Get a risk profile by ID tags: [Risk Profiles] security: - bearerAuth: [] - oauth2: [policy:read] parameters: - $ref: '#/components/parameters/ProfileId' responses: '200': description: Risk profile details content: application/json: schema: $ref: '#/components/schemas/RiskProfileResponse' '404': $ref: '#/components/responses/NotFound' /api/risk/profiles/{profileId}/versions: get: operationId: ListRiskProfileVersions summary: List all versions of a risk profile tags: [Risk Profiles] security: - bearerAuth: [] - oauth2: [policy:read] parameters: - $ref: '#/components/parameters/ProfileId' responses: '200': description: List of profile versions content: application/json: schema: $ref: '#/components/schemas/RiskProfileVersionListResponse' /api/risk/profiles/{profileId}/versions/{version}: get: operationId: GetRiskProfileVersion summary: Get a specific version of a risk profile tags: [Risk Profiles] security: - bearerAuth: [] - oauth2: [policy:read] parameters: - $ref: '#/components/parameters/ProfileId' - $ref: '#/components/parameters/Version' responses: '200': description: Risk profile version details content: application/json: schema: $ref: '#/components/schemas/RiskProfileResponse' '404': $ref: '#/components/responses/NotFound' /api/risk/profiles/{profileId}/versions/{version}:activate: post: operationId: ActivateRiskProfile summary: Activate a draft risk profile, making it available for use tags: [Risk Profiles] security: - bearerAuth: [] - oauth2: [policy:activate] parameters: - $ref: '#/components/parameters/ProfileId' - $ref: '#/components/parameters/Version' responses: '200': description: Profile activated content: application/json: schema: $ref: '#/components/schemas/RiskProfileVersionInfoResponse' '400': $ref: '#/components/responses/BadRequest' '404': $ref: '#/components/responses/NotFound' /api/risk/profiles/{profileId}/versions/{version}:deprecate: post: operationId: DeprecateRiskProfile summary: Deprecate an active risk profile tags: [Risk Profiles] security: - bearerAuth: [] - oauth2: [policy:edit] parameters: - $ref: '#/components/parameters/ProfileId' - $ref: '#/components/parameters/Version' requestBody: content: application/json: schema: $ref: '#/components/schemas/DeprecateRiskProfileRequest' responses: '200': description: Profile deprecated content: application/json: schema: $ref: '#/components/schemas/RiskProfileVersionInfoResponse' '400': $ref: '#/components/responses/BadRequest' '404': $ref: '#/components/responses/NotFound' /api/risk/profiles/{profileId}/versions/{version}:archive: post: operationId: ArchiveRiskProfile summary: Archive a risk profile, removing it from active use tags: [Risk Profiles] security: - bearerAuth: [] - oauth2: [policy:edit] parameters: - $ref: '#/components/parameters/ProfileId' - $ref: '#/components/parameters/Version' responses: '200': description: Profile archived content: application/json: schema: $ref: '#/components/schemas/RiskProfileVersionInfoResponse' '404': $ref: '#/components/responses/NotFound' /api/risk/profiles/{profileId}/events: get: operationId: GetRiskProfileEvents summary: Get lifecycle events for a risk profile tags: [Risk Profiles] security: - bearerAuth: [] - oauth2: [policy:read] parameters: - $ref: '#/components/parameters/ProfileId' - name: limit in: query schema: type: integer default: 100 minimum: 1 maximum: 1000 responses: '200': description: Profile lifecycle events content: application/json: schema: $ref: '#/components/schemas/RiskProfileEventListResponse' /api/risk/profiles/{profileId}/hash: get: operationId: GetRiskProfileHash summary: Get the deterministic hash of a risk profile tags: [Risk Profiles] security: - bearerAuth: [] - oauth2: [policy:read] parameters: - $ref: '#/components/parameters/ProfileId' - name: contentOnly in: query schema: type: boolean default: false description: If true, returns hash of content only (excludes metadata) responses: '200': description: Profile hash content: application/json: schema: $ref: '#/components/schemas/RiskProfileHashResponse' '404': $ref: '#/components/responses/NotFound' /api/risk/profiles/{profileId}/metadata: get: operationId: GetRiskProfileMetadata summary: Export risk profile metadata for notification enrichment description: Returns metadata suitable for notification context (POLICY-RISK-40-002) tags: [Risk Profiles] security: - bearerAuth: [] - oauth2: [policy:read] parameters: - $ref: '#/components/parameters/ProfileId' responses: '200': description: Profile metadata export content: application/json: schema: $ref: '#/components/schemas/RiskProfileMetadataExportResponse' '404': $ref: '#/components/responses/NotFound' /api/risk/profiles/compare: post: operationId: CompareRiskProfiles summary: Compare two risk profile versions and list differences tags: [Risk Profiles] security: - bearerAuth: [] - oauth2: [policy:read] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CompareRiskProfilesRequest' responses: '200': description: Comparison result content: application/json: schema: $ref: '#/components/schemas/RiskProfileComparisonResponse' '400': $ref: '#/components/responses/BadRequest' # ============================================================================ # Policy Decisions # ============================================================================ /policy/decisions: post: operationId: PolicyEngine.Decisions summary: Request policy decisions with source evidence summaries description: | Returns policy decisions with source evidence summaries, top severity sources, and conflict counts. tags: [Policy Decisions] security: - bearerAuth: [] - oauth2: [policy:read] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/PolicyDecisionRequest' responses: '200': description: Policy decisions content: application/json: schema: $ref: '#/components/schemas/PolicyDecisionResponse' '400': $ref: '#/components/responses/BadRequest' '404': $ref: '#/components/responses/NotFound' /policy/decisions/{snapshotId}: get: operationId: PolicyEngine.Decisions.BySnapshot summary: Get policy decisions for a specific snapshot tags: [Policy Decisions] security: - bearerAuth: [] - oauth2: [policy:read] parameters: - name: snapshotId in: path required: true schema: type: string - name: tenantId in: query schema: type: string - name: componentPurl in: query schema: type: string - name: advisoryId in: query schema: type: string - name: includeEvidence in: query schema: type: boolean default: true - name: maxSources in: query schema: type: integer default: 5 responses: '200': description: Policy decisions for snapshot content: application/json: schema: $ref: '#/components/schemas/PolicyDecisionResponse' '404': $ref: '#/components/responses/NotFound' # ============================================================================ # Risk Simulation # ============================================================================ /api/risk/simulation: post: operationId: RunRiskSimulation summary: Run a risk simulation with score distributions and contribution breakdowns tags: [Risk Simulation] security: - bearerAuth: [] - oauth2: [policy:read] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/RiskSimulationRequest' responses: '200': description: Simulation results content: application/json: schema: $ref: '#/components/schemas/RiskSimulationResponse' '400': $ref: '#/components/responses/BadRequest' '404': $ref: '#/components/responses/NotFound' /api/risk/simulation/quick: post: operationId: RunQuickRiskSimulation summary: Run a quick risk simulation without detailed breakdowns tags: [Risk Simulation] security: - bearerAuth: [] - oauth2: [policy:read] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/QuickSimulationRequest' responses: '200': description: Quick simulation results content: application/json: schema: $ref: '#/components/schemas/QuickSimulationResponse' '400': $ref: '#/components/responses/BadRequest' '404': $ref: '#/components/responses/NotFound' /api/risk/simulation/compare: post: operationId: CompareProfileSimulations summary: Compare risk scoring between two profile configurations tags: [Risk Simulation] security: - bearerAuth: [] - oauth2: [policy:read] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ProfileComparisonRequest' responses: '200': description: Comparison results content: application/json: schema: $ref: '#/components/schemas/ProfileComparisonResponse' '400': $ref: '#/components/responses/BadRequest' /api/risk/simulation/whatif: post: operationId: RunWhatIfSimulation summary: Run a what-if simulation with hypothetical signal changes tags: [Risk Simulation] security: - bearerAuth: [] - oauth2: [policy:read] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/WhatIfSimulationRequest' responses: '200': description: What-if simulation results content: application/json: schema: $ref: '#/components/schemas/WhatIfSimulationResponse' '400': $ref: '#/components/responses/BadRequest' /api/risk/simulation/studio/analyze: post: operationId: RunPolicyStudioAnalysis summary: Run a detailed analysis for Policy Studio with full breakdown analytics description: | Provides comprehensive breakdown including signal analysis, override tracking, score distributions, and component breakdowns for policy authoring. tags: [Risk Simulation] security: - bearerAuth: [] - oauth2: [policy:read] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/PolicyStudioAnalysisRequest' responses: '200': description: Studio analysis results content: application/json: schema: $ref: '#/components/schemas/PolicyStudioAnalysisResponse' '400': $ref: '#/components/responses/BadRequest' '404': $ref: '#/components/responses/NotFound' '503': description: Breakdown service unavailable /api/risk/simulation/studio/compare: post: operationId: CompareProfilesWithBreakdown summary: Compare profiles with full breakdown analytics and trend analysis tags: [Risk Simulation] security: - bearerAuth: [] - oauth2: [policy:read] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/PolicyStudioComparisonRequest' responses: '200': description: Comparison with breakdown content: application/json: schema: $ref: '#/components/schemas/PolicyStudioComparisonResponse' '400': $ref: '#/components/responses/BadRequest' /api/risk/simulation/studio/preview: post: operationId: PreviewProfileChanges summary: Preview impact of profile changes before committing description: Simulates findings against both current and proposed profile to show impact tags: [Risk Simulation] security: - bearerAuth: [] - oauth2: [policy:read] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ProfileChangePreviewRequest' responses: '200': description: Change preview results content: application/json: schema: $ref: '#/components/schemas/ProfileChangePreviewResponse' '400': $ref: '#/components/responses/BadRequest' '404': $ref: '#/components/responses/NotFound' # ============================================================================ # Policy Packs # ============================================================================ /api/policy/packs: get: operationId: ListPolicyPacks summary: List policy packs for the current tenant tags: [Policy Packs] security: - bearerAuth: [] - oauth2: [policy:read] responses: '200': description: List of policy packs content: application/json: schema: type: array items: $ref: '#/components/schemas/PolicyPackSummary' post: operationId: CreatePolicyPack summary: Create a new policy pack container tags: [Policy Packs] security: - bearerAuth: [] - oauth2: [policy:edit] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreatePolicyPackRequest' responses: '201': description: Policy pack created content: application/json: schema: $ref: '#/components/schemas/PolicyPack' /api/policy/packs/{packId}/revisions: post: operationId: CreatePolicyRevision summary: Create or update policy revision metadata tags: [Policy Packs] security: - bearerAuth: [] - oauth2: [policy:edit] parameters: - $ref: '#/components/parameters/PackId' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreatePolicyRevisionRequest' responses: '201': description: Revision created content: application/json: schema: $ref: '#/components/schemas/PolicyRevision' '400': $ref: '#/components/responses/BadRequest' /api/policy/packs/{packId}/revisions/{version}/bundle: post: operationId: CreatePolicyBundle summary: Compile and sign a policy revision bundle for distribution tags: [Policy Packs] security: - bearerAuth: [] - oauth2: [policy:edit] parameters: - $ref: '#/components/parameters/PackId' - $ref: '#/components/parameters/RevisionVersion' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/PolicyBundleRequest' responses: '201': description: Bundle created content: application/json: schema: $ref: '#/components/schemas/PolicyBundleResponse' '400': $ref: '#/components/responses/BadRequest' /api/policy/packs/{packId}/revisions/{version}/evaluate: post: operationId: EvaluatePolicyRevision summary: Evaluate a policy revision deterministically with in-memory caching tags: [Policy Packs] security: - bearerAuth: [] - oauth2: [policy:read] parameters: - $ref: '#/components/parameters/PackId' - $ref: '#/components/parameters/RevisionVersion' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/PolicyEvaluationRequest' responses: '200': description: Evaluation result content: application/json: schema: $ref: '#/components/schemas/PolicyEvaluationResponse' '400': $ref: '#/components/responses/BadRequest' '404': $ref: '#/components/responses/NotFound' /api/policy/packs/{packId}/revisions/{version}:activate: post: operationId: ActivatePolicyRevision summary: Activate an approved policy revision description: Enforces two-person approval when required by policy configuration tags: [Policy Packs] security: - bearerAuth: [] - oauth2: [policy:activate] parameters: - $ref: '#/components/parameters/PackId' - $ref: '#/components/parameters/RevisionVersion' requestBody: content: application/json: schema: $ref: '#/components/schemas/ActivatePolicyRevisionRequest' responses: '200': description: Revision activated content: application/json: schema: $ref: '#/components/schemas/PolicyRevisionActivationResponse' '202': description: Pending second approval content: application/json: schema: $ref: '#/components/schemas/PolicyRevisionActivationResponse' '400': $ref: '#/components/responses/BadRequest' '404': $ref: '#/components/responses/NotFound' # ============================================================================ # AirGap / Sealed Mode # ============================================================================ /system/airgap/seal: post: operationId: AirGap.Seal summary: Seal the environment description: Activates sealed mode for the specified tenant (CONTRACT-SEALED-MODE-004) tags: [AirGap] security: - bearerAuth: [] - oauth2: [airgap:seal] parameters: - $ref: '#/components/parameters/TenantIdHeader' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/SealRequest' responses: '200': description: Environment sealed content: application/json: schema: $ref: '#/components/schemas/SealResponse' '400': $ref: '#/components/responses/BadRequest' '500': description: Seal operation failed /system/airgap/unseal: post: operationId: AirGap.Unseal summary: Unseal the environment tags: [AirGap] security: - bearerAuth: [] - oauth2: [airgap:seal] parameters: - $ref: '#/components/parameters/TenantIdHeader' responses: '200': description: Environment unsealed content: application/json: schema: $ref: '#/components/schemas/UnsealResponse' '500': description: Unseal operation failed /system/airgap/status: get: operationId: AirGap.GetStatus summary: Get sealed-mode status tags: [AirGap] security: - bearerAuth: [] - oauth2: [airgap:status:read] parameters: - $ref: '#/components/parameters/TenantIdHeader' responses: '200': description: Sealed mode status content: application/json: schema: $ref: '#/components/schemas/SealedModeStatus' /system/airgap/verify: post: operationId: AirGap.VerifyBundle summary: Verify a bundle against trust roots tags: [AirGap] security: - bearerAuth: [] - oauth2: [airgap:verify] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/BundleVerifyRequest' responses: '200': description: Verification result content: application/json: schema: $ref: '#/components/schemas/BundleVerifyResponse' '400': $ref: '#/components/responses/BadRequest' '422': description: Verification failed components: securitySchemes: bearerAuth: type: http scheme: bearer bearerFormat: JWT oauth2: type: oauth2 flows: clientCredentials: tokenUrl: /oauth/token scopes: policy:read: Read policy and risk profiles policy:edit: Create and modify policies policy:activate: Activate policies airgap:seal: Seal/unseal environment airgap:status:read: Read sealed mode status airgap:verify: Verify bundles parameters: ProfileId: name: profileId in: path required: true schema: type: string description: Risk profile identifier Version: name: version in: path required: true schema: type: string description: Profile version string PackId: name: packId in: path required: true schema: type: string description: Policy pack identifier RevisionVersion: name: version in: path required: true schema: type: integer description: Policy revision version number TenantIdHeader: name: X-Tenant-Id in: header schema: type: string default: default description: Tenant identifier responses: BadRequest: description: Bad request content: application/problem+json: schema: $ref: '#/components/schemas/ProblemDetails' Unauthorized: description: Unauthorized content: application/problem+json: schema: $ref: '#/components/schemas/ProblemDetails' Forbidden: description: Forbidden content: application/problem+json: schema: $ref: '#/components/schemas/ProblemDetails' NotFound: description: Not found content: application/problem+json: schema: $ref: '#/components/schemas/ProblemDetails' schemas: ProblemDetails: type: object properties: type: type: string format: uri title: type: string status: type: integer detail: type: string instance: type: string # ========== Risk Profiles ========== RiskProfileListResponse: type: object required: [profiles] properties: profiles: type: array items: $ref: '#/components/schemas/RiskProfileSummary' RiskProfileSummary: type: object required: [profileId, version] properties: profileId: type: string version: type: string description: type: string nullable: true RiskProfileResponse: type: object required: [profile, hash] properties: profile: $ref: '#/components/schemas/RiskProfileModel' hash: type: string description: Deterministic SHA-256 hash of the profile versionInfo: $ref: '#/components/schemas/RiskProfileVersionInfo' RiskProfileModel: type: object required: [id, version, signals, overrides] properties: id: type: string version: type: string description: type: string nullable: true extends: type: string nullable: true description: Parent profile to inherit from signals: type: array items: $ref: '#/components/schemas/SignalDefinition' overrides: $ref: '#/components/schemas/ProfileOverrides' metadata: type: object additionalProperties: true nullable: true SignalDefinition: type: object required: [name, weight] properties: name: type: string weight: type: number format: double description: type: string nullable: true ProfileOverrides: type: object properties: severity: type: array items: $ref: '#/components/schemas/SeverityOverride' action: type: array items: $ref: '#/components/schemas/ActionOverride' SeverityOverride: type: object required: [set, when] properties: set: type: string enum: [critical, high, medium, low, info] when: type: object additionalProperties: true ActionOverride: type: object required: [set, when] properties: set: type: string enum: [block, warn, monitor, ignore] when: type: object additionalProperties: true RiskProfileVersionInfo: type: object required: [version, status, createdAt] properties: version: type: string status: type: string enum: [draft, active, deprecated, archived] createdAt: type: string format: date-time activatedAt: type: string format: date-time nullable: true deprecatedAt: type: string format: date-time nullable: true archivedAt: type: string format: date-time nullable: true successorVersion: type: string nullable: true deprecationReason: type: string nullable: true RiskProfileVersionListResponse: type: object required: [profileId, versions] properties: profileId: type: string versions: type: array items: $ref: '#/components/schemas/RiskProfileVersionInfo' RiskProfileVersionInfoResponse: type: object required: [versionInfo] properties: versionInfo: $ref: '#/components/schemas/RiskProfileVersionInfo' RiskProfileEventListResponse: type: object required: [profileId, events] properties: profileId: type: string events: type: array items: $ref: '#/components/schemas/RiskProfileLifecycleEvent' RiskProfileLifecycleEvent: type: object required: [eventType, timestamp] properties: eventType: type: string timestamp: type: string format: date-time actorId: type: string nullable: true details: type: object additionalProperties: true RiskProfileHashResponse: type: object required: [profileId, version, hash, contentOnly] properties: profileId: type: string version: type: string hash: type: string contentOnly: type: boolean RiskProfileMetadataExportResponse: type: object required: [profileId, version, hash, status, signalNames, severityThresholds, exportedAt] properties: profileId: type: string version: type: string description: type: string nullable: true hash: type: string status: type: string signalNames: type: array items: type: string severityThresholds: type: array items: $ref: '#/components/schemas/SeverityThresholdInfo' customMetadata: type: object additionalProperties: true nullable: true extendsProfile: type: string nullable: true exportedAt: type: string format: date-time SeverityThresholdInfo: type: object required: [targetSeverity, whenConditions] properties: targetSeverity: type: string whenConditions: type: object additionalProperties: true RiskProfileComparisonResponse: type: object required: [comparison] properties: comparison: $ref: '#/components/schemas/RiskProfileVersionComparison' RiskProfileVersionComparison: type: object properties: fromProfileId: type: string fromVersion: type: string toProfileId: type: string toVersion: type: string differences: type: array items: $ref: '#/components/schemas/ProfileDifference' ProfileDifference: type: object properties: path: type: string changeType: type: string enum: [added, removed, modified] oldValue: nullable: true newValue: nullable: true CreateRiskProfileRequest: type: object required: [profile] properties: profile: $ref: '#/components/schemas/RiskProfileModel' DeprecateRiskProfileRequest: type: object properties: successorVersion: type: string nullable: true reason: type: string nullable: true CompareRiskProfilesRequest: type: object required: [fromProfileId, fromVersion, toProfileId, toVersion] properties: fromProfileId: type: string fromVersion: type: string toProfileId: type: string toVersion: type: string # ========== Policy Decisions ========== PolicyDecisionRequest: type: object required: [snapshotId] properties: snapshotId: type: string tenantId: type: string nullable: true componentPurl: type: string nullable: true advisoryId: type: string nullable: true includeEvidence: type: boolean default: true maxSources: type: integer default: 5 PolicyDecisionResponse: type: object properties: snapshotId: type: string decisions: type: array items: $ref: '#/components/schemas/PolicyDecision' timestamp: type: string format: date-time PolicyDecision: type: object properties: componentPurl: type: string advisoryId: type: string decision: type: string enum: [allow, deny, warn, pending] severity: type: string evidenceSummary: $ref: '#/components/schemas/EvidenceSummary' EvidenceSummary: type: object properties: sourceCount: type: integer topSources: type: array items: $ref: '#/components/schemas/EvidenceSource' conflictCount: type: integer EvidenceSource: type: object properties: source: type: string severity: type: string confidence: type: number format: double # ========== Risk Simulation ========== RiskSimulationRequest: type: object required: [profileId, findings] properties: profileId: type: string profileVersion: type: string nullable: true findings: type: array items: $ref: '#/components/schemas/SimulationFinding' includeContributions: type: boolean default: true includeDistribution: type: boolean default: true mode: type: string enum: [quick, full, whatIf] default: full SimulationFinding: type: object required: [findingId, signals] properties: findingId: type: string componentPurl: type: string nullable: true advisoryId: type: string nullable: true signals: type: object additionalProperties: true RiskSimulationResponse: type: object required: [result] properties: result: $ref: '#/components/schemas/RiskSimulationResult' RiskSimulationResult: type: object required: [simulationId, profileId, profileVersion, timestamp, aggregateMetrics, findingScores, executionTimeMs] properties: simulationId: type: string profileId: type: string profileVersion: type: string timestamp: type: string format: date-time aggregateMetrics: $ref: '#/components/schemas/AggregateRiskMetrics' findingScores: type: array items: $ref: '#/components/schemas/FindingScore' distribution: $ref: '#/components/schemas/RiskDistribution' contributions: type: array items: $ref: '#/components/schemas/SignalContribution' executionTimeMs: type: number format: double AggregateRiskMetrics: type: object required: [meanScore, medianScore, criticalCount, highCount, mediumCount, lowCount, totalCount] properties: meanScore: type: number format: double medianScore: type: number format: double maxScore: type: number format: double minScore: type: number format: double criticalCount: type: integer highCount: type: integer mediumCount: type: integer lowCount: type: integer infoCount: type: integer totalCount: type: integer FindingScore: type: object required: [findingId, normalizedScore, severity, recommendedAction] properties: findingId: type: string rawScore: type: number format: double normalizedScore: type: number format: double severity: type: string enum: [critical, high, medium, low, info] recommendedAction: type: string enum: [block, warn, monitor, ignore] signalBreakdown: type: object additionalProperties: type: number format: double RiskDistribution: type: object properties: buckets: type: array items: $ref: '#/components/schemas/DistributionBucket' DistributionBucket: type: object properties: min: type: number format: double max: type: number format: double count: type: integer SignalContribution: type: object properties: signalName: type: string totalContribution: type: number format: double averageContribution: type: number format: double QuickSimulationRequest: type: object required: [profileId, findings] properties: profileId: type: string profileVersion: type: string nullable: true findings: type: array items: $ref: '#/components/schemas/SimulationFinding' QuickSimulationResponse: type: object required: [simulationId, profileId, profileVersion, timestamp, aggregateMetrics, executionTimeMs] properties: simulationId: type: string profileId: type: string profileVersion: type: string timestamp: type: string format: date-time aggregateMetrics: $ref: '#/components/schemas/AggregateRiskMetrics' distribution: $ref: '#/components/schemas/RiskDistribution' executionTimeMs: type: number format: double ProfileComparisonRequest: type: object required: [baseProfileId, compareProfileId, findings] properties: baseProfileId: type: string baseProfileVersion: type: string nullable: true compareProfileId: type: string compareProfileVersion: type: string nullable: true findings: type: array items: $ref: '#/components/schemas/SimulationFinding' ProfileComparisonResponse: type: object required: [baseProfile, compareProfile, deltas] properties: baseProfile: $ref: '#/components/schemas/ProfileSimulationSummary' compareProfile: $ref: '#/components/schemas/ProfileSimulationSummary' deltas: $ref: '#/components/schemas/ComparisonDeltas' ProfileSimulationSummary: type: object required: [profileId, profileVersion, metrics] properties: profileId: type: string profileVersion: type: string metrics: $ref: '#/components/schemas/AggregateRiskMetrics' ComparisonDeltas: type: object properties: meanScoreDelta: type: number format: double medianScoreDelta: type: number format: double criticalCountDelta: type: integer highCountDelta: type: integer mediumCountDelta: type: integer lowCountDelta: type: integer WhatIfSimulationRequest: type: object required: [profileId, findings, hypotheticalChanges] properties: profileId: type: string profileVersion: type: string nullable: true findings: type: array items: $ref: '#/components/schemas/SimulationFinding' hypotheticalChanges: type: array items: $ref: '#/components/schemas/HypotheticalChange' HypotheticalChange: type: object required: [signalName] properties: signalName: type: string newValue: nullable: true applyToAll: type: boolean default: true findingIds: type: array items: type: string WhatIfSimulationResponse: type: object required: [baselineResult, modifiedResult, impactSummary] properties: baselineResult: $ref: '#/components/schemas/RiskSimulationResult' modifiedResult: $ref: '#/components/schemas/RiskSimulationResult' impactSummary: $ref: '#/components/schemas/WhatIfImpactSummary' WhatIfImpactSummary: type: object properties: findingsImproved: type: integer findingsWorsened: type: integer findingsUnchanged: type: integer averageScoreDelta: type: number format: double severityShifts: $ref: '#/components/schemas/SeverityShifts' SeverityShifts: type: object properties: toLower: type: integer toHigher: type: integer unchanged: type: integer PolicyStudioAnalysisRequest: type: object required: [profileId, findings] properties: profileId: type: string profileVersion: type: string nullable: true findings: type: array items: $ref: '#/components/schemas/SimulationFinding' breakdownOptions: $ref: '#/components/schemas/RiskSimulationBreakdownOptions' RiskSimulationBreakdownOptions: type: object properties: includeSignalAnalysis: type: boolean default: true includeOverrideTracking: type: boolean default: true includeScoreDistributions: type: boolean default: true includeComponentBreakdowns: type: boolean default: true PolicyStudioAnalysisResponse: type: object required: [result, breakdown, totalExecutionTimeMs] properties: result: $ref: '#/components/schemas/RiskSimulationResult' breakdown: $ref: '#/components/schemas/RiskSimulationBreakdown' totalExecutionTimeMs: type: number format: double RiskSimulationBreakdown: type: object properties: signalAnalysis: type: object additionalProperties: true overrideTracking: type: object additionalProperties: true scoreDistributions: type: object additionalProperties: true componentBreakdowns: type: object additionalProperties: true PolicyStudioComparisonRequest: type: object required: [baseProfileId, compareProfileId, findings] properties: baseProfileId: type: string compareProfileId: type: string findings: type: array items: $ref: '#/components/schemas/SimulationFinding' breakdownOptions: $ref: '#/components/schemas/RiskSimulationBreakdownOptions' PolicyStudioComparisonResponse: type: object required: [baselineResult, compareResult, breakdown, executionTimeMs] properties: baselineResult: $ref: '#/components/schemas/RiskSimulationResult' compareResult: $ref: '#/components/schemas/RiskSimulationResult' breakdown: $ref: '#/components/schemas/RiskSimulationBreakdown' executionTimeMs: type: number format: double ProfileChangePreviewRequest: type: object required: [currentProfileId, findings] properties: currentProfileId: type: string currentProfileVersion: type: string nullable: true proposedProfileId: type: string nullable: true proposedProfileVersion: type: string nullable: true findings: type: array items: $ref: '#/components/schemas/SimulationFinding' proposedWeightChanges: type: object additionalProperties: type: number format: double proposedOverrideChanges: type: array items: $ref: '#/components/schemas/ProposedOverrideChange' ProposedOverrideChange: type: object required: [overrideType, when, value] properties: overrideType: type: string when: type: object additionalProperties: true value: nullable: true reason: type: string nullable: true ProfileChangePreviewResponse: type: object required: [currentResult, proposedResult, impact, highImpactFindings] properties: currentResult: $ref: '#/components/schemas/ProfileSimulationSummary' proposedResult: $ref: '#/components/schemas/ProfileSimulationSummary' impact: $ref: '#/components/schemas/ProfileChangeImpact' highImpactFindings: type: array items: $ref: '#/components/schemas/HighImpactFindingPreview' ProfileChangeImpact: type: object properties: findingsImproved: type: integer findingsWorsened: type: integer findingsUnchanged: type: integer severityEscalations: type: integer severityDeescalations: type: integer actionChanges: type: integer meanScoreDelta: type: number format: double criticalCountDelta: type: integer highCountDelta: type: integer HighImpactFindingPreview: type: object required: [findingId, currentScore, proposedScore, scoreDelta] properties: findingId: type: string currentScore: type: number format: double proposedScore: type: number format: double scoreDelta: type: number format: double currentSeverity: type: string proposedSeverity: type: string currentAction: type: string proposedAction: type: string impactReason: type: string # ========== Policy Packs ========== CreatePolicyPackRequest: type: object properties: packId: type: string nullable: true displayName: type: string nullable: true PolicyPack: type: object required: [packId, createdAt, revisions] properties: packId: type: string displayName: type: string nullable: true createdAt: type: string format: date-time revisions: type: array items: $ref: '#/components/schemas/PolicyRevision' PolicyPackSummary: type: object required: [packId, createdAt, versions] properties: packId: type: string displayName: type: string nullable: true createdAt: type: string format: date-time versions: type: array items: type: integer CreatePolicyRevisionRequest: type: object properties: version: type: integer nullable: true requiresTwoPersonApproval: type: boolean nullable: true initialStatus: type: string enum: [draft, approved] default: approved PolicyRevision: type: object required: [packId, version, status, requiresTwoPersonApproval, createdAt, approvals] properties: packId: type: string version: type: integer status: type: string enum: [draft, approved, active, superseded] requiresTwoPersonApproval: type: boolean createdAt: type: string format: date-time activatedAt: type: string format: date-time nullable: true approvals: type: array items: $ref: '#/components/schemas/PolicyActivationApproval' PolicyActivationApproval: type: object required: [actorId, approvedAt] properties: actorId: type: string approvedAt: type: string format: date-time comment: type: string nullable: true ActivatePolicyRevisionRequest: type: object properties: comment: type: string nullable: true PolicyRevisionActivationResponse: type: object required: [status, revision] properties: status: type: string enum: [pending_second_approval, activated, already_active] revision: $ref: '#/components/schemas/PolicyRevision' PolicyBundleRequest: type: object properties: signBundle: type: boolean default: true targetEnvironment: type: string nullable: true PolicyBundleResponse: type: object required: [success] properties: success: type: boolean bundleId: type: string bundlePath: type: string hash: type: string signatureId: type: string nullable: true errors: type: array items: type: string PolicyEvaluationRequest: type: object required: [packId, version, input] properties: packId: type: string version: type: integer input: type: object additionalProperties: true PolicyEvaluationResponse: type: object required: [result] properties: result: type: object additionalProperties: true deterministic: type: boolean cacheHit: type: boolean executionTimeMs: type: number format: double # ========== AirGap ========== SealRequest: type: object properties: reason: type: string nullable: true trustRoots: type: array items: type: string allowedSources: type: array items: type: string SealResponse: type: object required: [sealed, sealedAt] properties: sealed: type: boolean sealedAt: type: string format: date-time reason: type: string nullable: true UnsealResponse: type: object required: [sealed] properties: sealed: type: boolean unsealedAt: type: string format: date-time SealedModeStatus: type: object required: [isSealed] properties: isSealed: type: boolean sealedAt: type: string format: date-time nullable: true unsealedAt: type: string format: date-time nullable: true trustRoots: type: array items: type: string lastVerifiedAt: type: string format: date-time nullable: true BundleVerifyRequest: type: object required: [bundlePath] properties: bundlePath: type: string expectedHash: type: string nullable: true trustRootId: type: string nullable: true BundleVerifyResponse: type: object required: [valid, verificationResult] properties: valid: type: boolean verificationResult: $ref: '#/components/schemas/VerificationResult' bundleInfo: $ref: '#/components/schemas/BundleInfo' VerificationResult: type: object properties: signatureValid: type: boolean hashValid: type: boolean trustRootMatched: type: boolean error: type: string nullable: true BundleInfo: type: object properties: bundleId: type: string version: type: string createdAt: type: string format: date-time hash: type: string