openapi: 3.1.0 info: title: StellaOps Policy Registry API version: 1.0.0 description: | Policy Registry API for managing verification policies, policy packs, snapshots, violations, overrides, and air-gap operations. This specification unblocks: REGISTRY-API-27-001 through 27-010 contact: name: Policy Registry Guild email: policy-guild@stella-ops.org license: name: AGPL-3.0-or-later url: https://www.gnu.org/licenses/agpl-3.0.html servers: - url: /api/v1/policy description: Policy Engine API tags: - name: verification-policy description: Verification policy CRUD operations - name: policy-pack description: Policy pack workspace, compile, simulation - name: snapshot description: Policy snapshot management - name: violation description: Policy violation tracking - name: override description: Policy override management - name: sealed-mode description: Air-gap sealed mode operations - name: staleness description: Advisory staleness tracking paths: # ============================================================ # VERIFICATION POLICY ENDPOINTS # ============================================================ /verification-policies: get: operationId: listVerificationPolicies tags: [verification-policy] summary: List all verification policies parameters: - $ref: '#/components/parameters/TenantHeader' - $ref: '#/components/parameters/PageSize' - $ref: '#/components/parameters/PageToken' responses: '200': description: List of verification policies content: application/json: schema: $ref: '#/components/schemas/VerificationPolicyList' '401': $ref: '#/components/responses/Unauthorized' post: operationId: createVerificationPolicy tags: [verification-policy] summary: Create a new verification policy parameters: - $ref: '#/components/parameters/TenantHeader' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateVerificationPolicyRequest' responses: '201': description: Policy created content: application/json: schema: $ref: '#/components/schemas/VerificationPolicy' '400': $ref: '#/components/responses/BadRequest' '409': $ref: '#/components/responses/Conflict' /verification-policies/{policyId}: parameters: - $ref: '#/components/parameters/PolicyId' - $ref: '#/components/parameters/TenantHeader' get: operationId: getVerificationPolicy tags: [verification-policy] summary: Get a verification policy by ID responses: '200': description: Verification policy content: application/json: schema: $ref: '#/components/schemas/VerificationPolicy' '404': $ref: '#/components/responses/NotFound' put: operationId: updateVerificationPolicy tags: [verification-policy] summary: Update a verification policy requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/UpdateVerificationPolicyRequest' responses: '200': description: Policy updated content: application/json: schema: $ref: '#/components/schemas/VerificationPolicy' '404': $ref: '#/components/responses/NotFound' delete: operationId: deleteVerificationPolicy tags: [verification-policy] summary: Delete a verification policy responses: '204': description: Policy deleted '404': $ref: '#/components/responses/NotFound' # ============================================================ # POLICY PACK WORKSPACE ENDPOINTS # ============================================================ /packs: get: operationId: listPolicyPacks tags: [policy-pack] summary: List policy packs in workspace parameters: - $ref: '#/components/parameters/TenantHeader' - $ref: '#/components/parameters/PageSize' - $ref: '#/components/parameters/PageToken' - name: status in: query schema: type: string enum: [draft, published, archived] responses: '200': description: List of policy packs content: application/json: schema: $ref: '#/components/schemas/PolicyPackList' post: operationId: createPolicyPack tags: [policy-pack] summary: Create a new policy pack parameters: - $ref: '#/components/parameters/TenantHeader' 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' /packs/{packId}: parameters: - $ref: '#/components/parameters/PackId' - $ref: '#/components/parameters/TenantHeader' get: operationId: getPolicyPack tags: [policy-pack] summary: Get a policy pack by ID responses: '200': description: Policy pack content: application/json: schema: $ref: '#/components/schemas/PolicyPack' '404': $ref: '#/components/responses/NotFound' put: operationId: updatePolicyPack tags: [policy-pack] summary: Update a policy pack requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/UpdatePolicyPackRequest' responses: '200': description: Policy pack updated content: application/json: schema: $ref: '#/components/schemas/PolicyPack' delete: operationId: deletePolicyPack tags: [policy-pack] summary: Delete a policy pack (draft only) responses: '204': description: Policy pack deleted '409': description: Cannot delete published pack /packs/{packId}/compile: post: operationId: compilePolicyPack tags: [policy-pack] summary: Compile a policy pack parameters: - $ref: '#/components/parameters/PackId' - $ref: '#/components/parameters/TenantHeader' responses: '200': description: Compilation result content: application/json: schema: $ref: '#/components/schemas/CompilationResult' '422': description: Compilation errors content: application/json: schema: $ref: '#/components/schemas/CompilationResult' /packs/{packId}/simulate: post: operationId: simulatePolicyPack tags: [policy-pack] summary: Simulate a policy pack against sample data parameters: - $ref: '#/components/parameters/PackId' - $ref: '#/components/parameters/TenantHeader' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/SimulationRequest' responses: '200': description: Simulation result content: application/json: schema: $ref: '#/components/schemas/SimulationResult' /packs/{packId}/publish: post: operationId: publishPolicyPack tags: [policy-pack] summary: Publish a policy pack (requires review approval) parameters: - $ref: '#/components/parameters/PackId' - $ref: '#/components/parameters/TenantHeader' requestBody: content: application/json: schema: $ref: '#/components/schemas/PublishRequest' responses: '200': description: Pack published content: application/json: schema: $ref: '#/components/schemas/PolicyPack' '409': description: Pack not in reviewable state /packs/{packId}/promote: post: operationId: promotePolicyPack tags: [policy-pack] summary: Promote a policy pack to production parameters: - $ref: '#/components/parameters/PackId' - $ref: '#/components/parameters/TenantHeader' requestBody: content: application/json: schema: $ref: '#/components/schemas/PromoteRequest' responses: '200': description: Pack promoted content: application/json: schema: $ref: '#/components/schemas/PolicyPack' # ============================================================ # SNAPSHOT ENDPOINTS # ============================================================ /snapshots: get: operationId: listSnapshots tags: [snapshot] summary: List policy snapshots parameters: - $ref: '#/components/parameters/TenantHeader' - $ref: '#/components/parameters/PageSize' - $ref: '#/components/parameters/PageToken' responses: '200': description: List of snapshots content: application/json: schema: $ref: '#/components/schemas/SnapshotList' post: operationId: createSnapshot tags: [snapshot] summary: Create a policy snapshot parameters: - $ref: '#/components/parameters/TenantHeader' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateSnapshotRequest' responses: '201': description: Snapshot created content: application/json: schema: $ref: '#/components/schemas/Snapshot' /snapshots/{snapshotId}: parameters: - $ref: '#/components/parameters/SnapshotId' - $ref: '#/components/parameters/TenantHeader' get: operationId: getSnapshot tags: [snapshot] summary: Get a snapshot by ID responses: '200': description: Snapshot content: application/json: schema: $ref: '#/components/schemas/Snapshot' '404': $ref: '#/components/responses/NotFound' delete: operationId: deleteSnapshot tags: [snapshot] summary: Delete a snapshot responses: '204': description: Snapshot deleted /snapshots/by-digest/{digest}: get: operationId: getSnapshotByDigest tags: [snapshot] summary: Get a snapshot by content digest parameters: - name: digest in: path required: true schema: type: string pattern: '^sha256:[a-f0-9]{64}$' - $ref: '#/components/parameters/TenantHeader' responses: '200': description: Snapshot content: application/json: schema: $ref: '#/components/schemas/Snapshot' '404': $ref: '#/components/responses/NotFound' # ============================================================ # VIOLATION ENDPOINTS # ============================================================ /violations: get: operationId: listViolations tags: [violation] summary: List policy violations parameters: - $ref: '#/components/parameters/TenantHeader' - $ref: '#/components/parameters/PageSize' - $ref: '#/components/parameters/PageToken' - name: severity in: query schema: type: string enum: [critical, high, medium, low, info] responses: '200': description: List of violations content: application/json: schema: $ref: '#/components/schemas/ViolationList' post: operationId: appendViolation tags: [violation] summary: Append a new violation parameters: - $ref: '#/components/parameters/TenantHeader' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateViolationRequest' responses: '201': description: Violation created content: application/json: schema: $ref: '#/components/schemas/Violation' /violations/batch: post: operationId: appendViolationBatch tags: [violation] summary: Append violations in batch parameters: - $ref: '#/components/parameters/TenantHeader' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ViolationBatchRequest' responses: '201': description: Violations created content: application/json: schema: $ref: '#/components/schemas/ViolationBatchResult' /violations/{violationId}: get: operationId: getViolation tags: [violation] summary: Get a violation by ID parameters: - $ref: '#/components/parameters/ViolationId' - $ref: '#/components/parameters/TenantHeader' responses: '200': description: Violation content: application/json: schema: $ref: '#/components/schemas/Violation' '404': $ref: '#/components/responses/NotFound' # ============================================================ # OVERRIDE ENDPOINTS # ============================================================ /overrides: post: operationId: createOverride tags: [override] summary: Create a policy override parameters: - $ref: '#/components/parameters/TenantHeader' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateOverrideRequest' responses: '201': description: Override created content: application/json: schema: $ref: '#/components/schemas/Override' /overrides/{overrideId}: parameters: - $ref: '#/components/parameters/OverrideId' - $ref: '#/components/parameters/TenantHeader' get: operationId: getOverride tags: [override] summary: Get an override by ID responses: '200': description: Override content: application/json: schema: $ref: '#/components/schemas/Override' '404': $ref: '#/components/responses/NotFound' delete: operationId: deleteOverride tags: [override] summary: Delete an override responses: '204': description: Override deleted /overrides/{overrideId}:approve: post: operationId: approveOverride tags: [override] summary: Approve an override parameters: - $ref: '#/components/parameters/OverrideId' - $ref: '#/components/parameters/TenantHeader' requestBody: content: application/json: schema: $ref: '#/components/schemas/ApproveOverrideRequest' responses: '200': description: Override approved content: application/json: schema: $ref: '#/components/schemas/Override' /overrides/{overrideId}:disable: post: operationId: disableOverride tags: [override] summary: Disable an override parameters: - $ref: '#/components/parameters/OverrideId' - $ref: '#/components/parameters/TenantHeader' responses: '200': description: Override disabled content: application/json: schema: $ref: '#/components/schemas/Override' # ============================================================ # SEALED MODE ENDPOINTS # ============================================================ /sealed-mode/status: get: operationId: getSealedModeStatus tags: [sealed-mode] summary: Get sealed mode status parameters: - $ref: '#/components/parameters/TenantHeader' responses: '200': description: Sealed mode status content: application/json: schema: $ref: '#/components/schemas/SealedModeStatus' /sealed-mode/seal: post: operationId: seal tags: [sealed-mode] summary: Activate sealed mode (air-gap) parameters: - $ref: '#/components/parameters/TenantHeader' requestBody: content: application/json: schema: $ref: '#/components/schemas/SealRequest' responses: '200': description: Environment sealed content: application/json: schema: $ref: '#/components/schemas/SealedModeStatus' /sealed-mode/unseal: post: operationId: unseal tags: [sealed-mode] summary: Deactivate sealed mode parameters: - $ref: '#/components/parameters/TenantHeader' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/UnsealRequest' responses: '200': description: Environment unsealed content: application/json: schema: $ref: '#/components/schemas/SealedModeStatus' /sealed-mode/verify: post: operationId: verifyBundle tags: [sealed-mode] summary: Verify an air-gap bundle parameters: - $ref: '#/components/parameters/TenantHeader' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/VerifyBundleRequest' responses: '200': description: Bundle verification result content: application/json: schema: $ref: '#/components/schemas/BundleVerificationResult' # ============================================================ # STALENESS ENDPOINTS # ============================================================ /staleness/status: get: operationId: getStalenessStatus tags: [staleness] summary: Get advisory staleness status parameters: - $ref: '#/components/parameters/TenantHeader' responses: '200': description: Staleness status content: application/json: schema: $ref: '#/components/schemas/StalenessStatus' /staleness/evaluate: post: operationId: evaluateStaleness tags: [staleness] summary: Evaluate staleness for a specific advisory source parameters: - $ref: '#/components/parameters/TenantHeader' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/EvaluateStalenessRequest' responses: '200': description: Evaluation result content: application/json: schema: $ref: '#/components/schemas/StalenessEvaluation' components: parameters: TenantHeader: name: X-Tenant-Id in: header required: true schema: type: string format: uuid description: Tenant identifier for multi-tenant isolation PolicyId: name: policyId in: path required: true schema: type: string description: Verification policy identifier PackId: name: packId in: path required: true schema: type: string format: uuid description: Policy pack identifier SnapshotId: name: snapshotId in: path required: true schema: type: string format: uuid description: Snapshot identifier ViolationId: name: violationId in: path required: true schema: type: string format: uuid description: Violation identifier OverrideId: name: overrideId in: path required: true schema: type: string format: uuid description: Override identifier PageSize: name: page_size in: query schema: type: integer minimum: 1 maximum: 100 default: 20 PageToken: name: page_token in: query schema: type: string responses: BadRequest: description: Bad request content: application/json: schema: $ref: '#/components/schemas/ProblemDetails' Unauthorized: description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ProblemDetails' NotFound: description: Resource not found content: application/json: schema: $ref: '#/components/schemas/ProblemDetails' Conflict: description: Conflict (resource already exists) content: application/json: schema: $ref: '#/components/schemas/ProblemDetails' schemas: # ============================================================ # VERIFICATION POLICY SCHEMAS # ============================================================ VerificationPolicy: type: object required: [policy_id, version, tenant_scope, predicate_types, signer_requirements, created_at, updated_at] properties: policy_id: type: string version: type: string description: type: string tenant_scope: type: string predicate_types: type: array items: type: string signer_requirements: $ref: '#/components/schemas/SignerRequirements' validity_window: $ref: '#/components/schemas/ValidityWindow' metadata: type: object additionalProperties: true created_at: type: string format: date-time updated_at: type: string format: date-time SignerRequirements: type: object required: [minimum_signatures, trusted_key_fingerprints, require_rekor] properties: minimum_signatures: type: integer minimum: 1 default: 1 trusted_key_fingerprints: type: array items: type: string trusted_issuers: type: array items: type: string require_rekor: type: boolean default: false algorithms: type: array items: type: string enum: [ES256, RS256, EdDSA, ES384, RS384, PS256, PS384] ValidityWindow: type: object properties: not_before: type: string format: date-time not_after: type: string format: date-time max_attestation_age: type: integer description: Maximum age of attestation in seconds CreateVerificationPolicyRequest: type: object required: [policy_id, version, predicate_types] properties: policy_id: type: string version: type: string description: type: string tenant_scope: type: string predicate_types: type: array items: type: string signer_requirements: $ref: '#/components/schemas/SignerRequirements' validity_window: $ref: '#/components/schemas/ValidityWindow' metadata: type: object additionalProperties: true UpdateVerificationPolicyRequest: type: object properties: version: type: string description: type: string predicate_types: type: array items: type: string signer_requirements: $ref: '#/components/schemas/SignerRequirements' validity_window: $ref: '#/components/schemas/ValidityWindow' metadata: type: object additionalProperties: true VerificationPolicyList: type: object required: [items] properties: items: type: array items: $ref: '#/components/schemas/VerificationPolicy' next_page_token: type: string total_count: type: integer # ============================================================ # POLICY PACK SCHEMAS # ============================================================ PolicyPack: type: object required: [pack_id, name, version, status, created_at, updated_at] properties: pack_id: type: string format: uuid name: type: string version: type: string description: type: string status: type: string enum: [draft, pending_review, published, archived] rules: type: array items: $ref: '#/components/schemas/PolicyRule' metadata: type: object additionalProperties: true created_at: type: string format: date-time updated_at: type: string format: date-time published_at: type: string format: date-time digest: type: string description: Content-addressable hash of the pack PolicyRule: type: object required: [rule_id, name, severity] properties: rule_id: type: string name: type: string description: type: string severity: type: string enum: [critical, high, medium, low, info] rego: type: string description: OPA/Rego policy code enabled: type: boolean default: true CreatePolicyPackRequest: type: object required: [name, version] properties: name: type: string version: type: string description: type: string rules: type: array items: $ref: '#/components/schemas/PolicyRule' metadata: type: object additionalProperties: true UpdatePolicyPackRequest: type: object properties: name: type: string description: type: string rules: type: array items: $ref: '#/components/schemas/PolicyRule' metadata: type: object additionalProperties: true PolicyPackList: type: object required: [items] properties: items: type: array items: $ref: '#/components/schemas/PolicyPack' next_page_token: type: string CompilationResult: type: object required: [success] properties: success: type: boolean errors: type: array items: $ref: '#/components/schemas/CompilationError' warnings: type: array items: $ref: '#/components/schemas/CompilationWarning' digest: type: string CompilationError: type: object required: [message] properties: rule_id: type: string line: type: integer column: type: integer message: type: string CompilationWarning: type: object required: [message] properties: rule_id: type: string message: type: string SimulationRequest: type: object required: [input] properties: input: type: object additionalProperties: true description: Input data to simulate against options: type: object properties: trace: type: boolean default: false explain: type: boolean default: false SimulationResult: type: object required: [result] properties: result: type: object additionalProperties: true violations: type: array items: $ref: '#/components/schemas/SimulatedViolation' trace: type: array items: type: string explain: $ref: '#/components/schemas/PolicyExplainTrace' SimulatedViolation: type: object required: [rule_id, severity, message] properties: rule_id: type: string severity: type: string message: type: string context: type: object additionalProperties: true PolicyExplainTrace: type: object properties: steps: type: array items: type: object PublishRequest: type: object properties: approval_id: type: string description: Optional approval reference PromoteRequest: type: object properties: target_environment: type: string enum: [staging, production] approval_id: type: string # ============================================================ # SNAPSHOT SCHEMAS # ============================================================ Snapshot: type: object required: [snapshot_id, digest, created_at] properties: snapshot_id: type: string format: uuid digest: type: string pattern: '^sha256:[a-f0-9]{64}$' description: type: string pack_ids: type: array items: type: string format: uuid metadata: type: object additionalProperties: true created_at: type: string format: date-time created_by: type: string CreateSnapshotRequest: type: object required: [pack_ids] properties: description: type: string pack_ids: type: array items: type: string format: uuid metadata: type: object additionalProperties: true SnapshotList: type: object required: [items] properties: items: type: array items: $ref: '#/components/schemas/Snapshot' next_page_token: type: string # ============================================================ # VIOLATION SCHEMAS # ============================================================ Violation: type: object required: [violation_id, rule_id, severity, message, created_at] properties: violation_id: type: string format: uuid policy_id: type: string rule_id: type: string severity: type: string enum: [critical, high, medium, low, info] message: type: string purl: type: string cve_id: type: string context: type: object additionalProperties: true created_at: type: string format: date-time CreateViolationRequest: type: object required: [rule_id, severity, message] properties: policy_id: type: string rule_id: type: string severity: type: string enum: [critical, high, medium, low, info] message: type: string purl: type: string cve_id: type: string context: type: object additionalProperties: true ViolationBatchRequest: type: object required: [violations] properties: violations: type: array items: $ref: '#/components/schemas/CreateViolationRequest' maxItems: 1000 ViolationBatchResult: type: object required: [created, failed] properties: created: type: integer failed: type: integer errors: type: array items: type: object properties: index: type: integer error: type: string ViolationList: type: object required: [items] properties: items: type: array items: $ref: '#/components/schemas/Violation' next_page_token: type: string total_count: type: integer # ============================================================ # OVERRIDE SCHEMAS # ============================================================ Override: type: object required: [override_id, rule_id, status, created_at] properties: override_id: type: string format: uuid profile_id: type: string format: uuid rule_id: type: string status: type: string enum: [pending, approved, disabled, expired] reason: type: string scope: $ref: '#/components/schemas/OverrideScope' expires_at: type: string format: date-time approved_by: type: string approved_at: type: string format: date-time created_at: type: string format: date-time created_by: type: string OverrideScope: type: object properties: purl: type: string cve_id: type: string component: type: string environment: type: string CreateOverrideRequest: type: object required: [rule_id, reason] properties: profile_id: type: string format: uuid rule_id: type: string reason: type: string scope: $ref: '#/components/schemas/OverrideScope' expires_at: type: string format: date-time ApproveOverrideRequest: type: object properties: comment: type: string # ============================================================ # SEALED MODE SCHEMAS # ============================================================ SealedModeStatus: type: object required: [sealed, mode] properties: sealed: type: boolean mode: type: string enum: [online, sealed, transitioning] sealed_at: type: string format: date-time sealed_by: type: string bundle_version: type: string last_advisory_update: type: string format: date-time time_anchor: $ref: '#/components/schemas/TimeAnchor' TimeAnchor: type: object required: [timestamp, valid] properties: timestamp: type: string format: date-time signature: type: string valid: type: boolean expires_at: type: string format: date-time SealRequest: type: object properties: reason: type: string time_anchor: type: string format: date-time UnsealRequest: type: object required: [reason] properties: reason: type: string audit_note: type: string VerifyBundleRequest: type: object required: [bundle_digest] properties: bundle_digest: type: string public_key: type: string BundleVerificationResult: type: object required: [valid] properties: valid: type: boolean bundle_digest: type: string signed_at: type: string format: date-time signer_fingerprint: type: string errors: type: array items: type: string # ============================================================ # STALENESS SCHEMAS # ============================================================ StalenessStatus: type: object required: [overall_status, sources] properties: overall_status: type: string enum: [fresh, stale, critical, unknown] sources: type: array items: $ref: '#/components/schemas/SourceStaleness' last_check: type: string format: date-time SourceStaleness: type: object required: [source_id, status, last_update] properties: source_id: type: string source_name: type: string status: type: string enum: [fresh, stale, critical, unknown] last_update: type: string format: date-time max_age_hours: type: integer age_hours: type: number EvaluateStalenessRequest: type: object required: [source_id] properties: source_id: type: string threshold_hours: type: integer StalenessEvaluation: type: object required: [source_id, is_stale] properties: source_id: type: string is_stale: type: boolean age_hours: type: number threshold_hours: type: integer recommendation: type: string # ============================================================ # COMMON SCHEMAS # ============================================================ ProblemDetails: type: object required: [type, title, status] properties: type: type: string format: uri title: type: string status: type: integer detail: type: string instance: type: string errors: type: array items: type: object properties: field: type: string message: type: string