# OpenAPI 3.1 specification for StellaOps TaskRunner WebService openapi: 3.1.0 info: title: StellaOps TaskRunner API version: 0.1.0-draft description: | Contract for TaskRunner service covering pack runs, simulations, logs, artifacts, and approvals. Uses the platform error envelope and tenant header `X-StellaOps-Tenant`. ## Streaming Endpoints The `/runs/{runId}/logs` endpoint returns logs in NDJSON (Newline Delimited JSON) format for efficient streaming. Each line is a complete JSON object. ## Control Flow Steps TaskPacks support the following step kinds: - **run**: Execute an action using a builtin or custom executor - **parallel**: Execute child steps concurrently with optional maxParallel limit - **map**: Iterate over items and execute a template step for each - **loop**: Iterate with items expression, range, or static list - **conditional**: Branch based on condition expressions - **gate.approval**: Require manual approval before proceeding - **gate.policy**: Evaluate policy and optionally require override approval servers: - url: https://taskrunner.stellaops.example.com description: Production - url: https://taskrunner.dev.stellaops.example.com description: Development security: - oauth2: [taskrunner.viewer] - oauth2: [taskrunner.operator] - oauth2: [taskrunner.admin] paths: /v1/task-runner/simulations: post: summary: Simulate a task pack description: | Validates a task pack manifest, creates an execution plan, and simulates the run without actually executing any steps. Returns the simulation result showing which steps would execute, which are skipped, and which require approvals. operationId: simulateTaskPack tags: [Simulations] parameters: - $ref: '#/components/parameters/Tenant' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/SimulationRequest' examples: basic-simulation: summary: Basic simulation request value: manifest: | apiVersion: stellaops.io/pack.v1 kind: TaskPack metadata: name: scan-deploy version: 1.0.0 spec: inputs: - name: target type: string required: true sandbox: mode: sealed egressAllowlist: [] cpuLimitMillicores: 100 memoryLimitMiB: 128 quotaSeconds: 60 slo: runP95Seconds: 300 approvalP95Seconds: 900 maxQueueDepth: 100 steps: - id: scan run: uses: builtin:scanner with: target: "{{ inputs.target }}" inputs: target: "registry.example.com/app:v1.2.3" responses: '200': description: Simulation completed content: application/json: schema: $ref: '#/components/schemas/SimulationResponse' examples: simulation-result: value: planHash: "sha256:a1b2c3d4e5f6..." failurePolicy: maxAttempts: 1 backoffSeconds: 0 continueOnError: false steps: - id: scan templateId: scan kind: Run enabled: true status: Pending uses: "builtin:scanner" children: [] outputs: [] hasPendingApprovals: false '400': description: Invalid manifest or inputs content: application/json: schema: $ref: '#/components/schemas/PlanErrorResponse' default: $ref: '#/components/responses/Error' /v1/task-runner/runs: post: summary: Create a pack run description: | Creates a new pack run from a task pack manifest. The run is scheduled for execution and will proceed through its steps. If approval gates are present, the run will pause at those gates until approvals are granted. operationId: createPackRun tags: [Runs] parameters: - $ref: '#/components/parameters/Tenant' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateRunRequest' examples: create-run: summary: Create a new run value: runId: "run-20251206-001" manifest: | apiVersion: stellaops.io/pack.v1 kind: TaskPack metadata: name: deploy-app version: 2.0.0 spec: sandbox: mode: sealed egressAllowlist: [] cpuLimitMillicores: 200 memoryLimitMiB: 256 quotaSeconds: 120 slo: runP95Seconds: 600 approvalP95Seconds: 1800 maxQueueDepth: 50 approvals: - id: security-review grants: [packs.approve] steps: - id: build run: uses: builtin:build - id: approval gate: approval: id: security-review message: "Security review required before deploy" - id: deploy run: uses: builtin:deploy tenantId: "tenant-prod" responses: '201': description: Run created headers: Location: description: URL of the created run schema: type: string content: application/json: schema: $ref: '#/components/schemas/RunStateResponse' '400': description: Invalid manifest or inputs content: application/json: schema: $ref: '#/components/schemas/PlanErrorResponse' '409': description: Run ID already exists content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' default: $ref: '#/components/responses/Error' /v1/task-runner/runs/{runId}: get: summary: Get run state description: | Returns the current state of a pack run, including status of all steps, failure policy, and timing information. operationId: getRunState tags: [Runs] parameters: - $ref: '#/components/parameters/Tenant' - $ref: '#/components/parameters/RunId' responses: '200': description: Run state content: application/json: schema: $ref: '#/components/schemas/RunStateResponse' examples: running: summary: Run in progress value: runId: "run-20251206-001" planHash: "sha256:a1b2c3d4..." failurePolicy: maxAttempts: 2 backoffSeconds: 30 continueOnError: false createdAt: "2025-12-06T10:00:00Z" updatedAt: "2025-12-06T10:05:00Z" steps: - stepId: build kind: Run enabled: true continueOnError: false status: Succeeded attempts: 1 lastTransitionAt: "2025-12-06T10:02:00Z" - stepId: approval kind: GateApproval enabled: true continueOnError: false approvalId: security-review gateMessage: "Security review required before deploy" status: Pending attempts: 0 statusReason: "awaiting-approval" - stepId: deploy kind: Run enabled: true continueOnError: false status: Pending attempts: 0 '404': description: Run not found default: $ref: '#/components/responses/Error' /v1/task-runner/runs/{runId}/logs: get: summary: Stream run logs description: | Returns run logs as a stream of NDJSON (Newline Delimited JSON) entries. Each line is a complete JSON object representing a log entry with timestamp, level, event type, message, and optional metadata. **Content-Type**: `application/x-ndjson` operationId: streamRunLogs tags: [Logs] parameters: - $ref: '#/components/parameters/Tenant' - $ref: '#/components/parameters/RunId' responses: '200': description: Log stream content: application/x-ndjson: schema: $ref: '#/components/schemas/RunLogEntry' examples: log-stream: summary: Sample NDJSON log stream value: | {"timestamp":"2025-12-06T10:00:00Z","level":"info","eventType":"run.created","message":"Run created via API.","metadata":{"planHash":"sha256:a1b2c3d4...","requestedAt":"2025-12-06T10:00:00Z"}} {"timestamp":"2025-12-06T10:00:01Z","level":"info","eventType":"step.started","message":"Starting step: build","stepId":"build"} {"timestamp":"2025-12-06T10:02:00Z","level":"info","eventType":"step.completed","message":"Step completed: build","stepId":"build","metadata":{"duration":"119s"}} {"timestamp":"2025-12-06T10:02:01Z","level":"warn","eventType":"gate.awaiting","message":"Awaiting approval: security-review","stepId":"approval"} '404': description: Run not found default: $ref: '#/components/responses/Error' /v1/task-runner/runs/{runId}/artifacts: get: summary: List run artifacts description: | Returns a list of artifacts captured during the run, including file outputs, evidence bundles, and expression-evaluated results. operationId: listRunArtifacts tags: [Artifacts] parameters: - $ref: '#/components/parameters/Tenant' - $ref: '#/components/parameters/RunId' responses: '200': description: Artifact list content: application/json: schema: type: array items: $ref: '#/components/schemas/RunArtifact' examples: artifacts: value: - name: scan-report type: file sourcePath: "/output/scan-report.json" storedPath: "runs/run-20251206-001/artifacts/scan-report.json" status: captured capturedAt: "2025-12-06T10:02:00Z" - name: evidence-bundle type: object status: captured capturedAt: "2025-12-06T10:02:00Z" expressionJson: '{"sha256":"abc123...","attestations":[...]}' '404': description: Run not found default: $ref: '#/components/responses/Error' /v1/task-runner/runs/{runId}/approvals/{approvalId}: post: summary: Apply approval decision description: | Applies an approval decision (approved, rejected, or expired) to a pending approval gate. The planHash must match to prevent approving a stale plan. If approved, the run will resume execution. If rejected, the run will fail at the gate step. operationId: applyApprovalDecision tags: [Approvals] parameters: - $ref: '#/components/parameters/Tenant' - $ref: '#/components/parameters/RunId' - $ref: '#/components/parameters/ApprovalId' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ApprovalDecisionRequest' examples: approve: summary: Approve the gate value: decision: approved planHash: "sha256:a1b2c3d4e5f678901234567890abcdef1234567890abcdef1234567890abcdef" actorId: "user:alice@example.com" summary: "Reviewed and approved for production deployment" reject: summary: Reject the gate value: decision: rejected planHash: "sha256:a1b2c3d4e5f678901234567890abcdef1234567890abcdef1234567890abcdef" actorId: "user:bob@example.com" summary: "Security scan found critical vulnerabilities" responses: '200': description: Decision applied content: application/json: schema: $ref: '#/components/schemas/ApprovalDecisionResponse' examples: approved: value: status: approved resumed: true '400': description: Invalid decision or planHash format content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' '404': description: Run or approval not found '409': description: Plan hash mismatch content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' default: $ref: '#/components/responses/Error' /v1/task-runner/runs/{runId}/cancel: post: summary: Cancel a run description: | Requests cancellation of a run. Remaining pending steps will be marked as skipped. Steps that have already succeeded or been skipped are not affected. operationId: cancelRun tags: [Runs] parameters: - $ref: '#/components/parameters/Tenant' - $ref: '#/components/parameters/RunId' responses: '202': description: Cancellation accepted headers: Location: description: URL of the run schema: type: string content: application/json: schema: type: object properties: status: type: string enum: [cancelled] '404': description: Run not found default: $ref: '#/components/responses/Error' /.well-known/openapi: get: summary: Get OpenAPI metadata description: | Returns metadata about the OpenAPI specification including the spec URL, ETag for caching, and a signature for verification. operationId: getOpenApiMetadata tags: [Metadata] responses: '200': description: OpenAPI metadata headers: ETag: description: Spec version ETag schema: type: string X-Signature: description: Spec signature for verification schema: type: string content: application/json: schema: $ref: '#/components/schemas/OpenApiMetadata' examples: metadata: value: specUrl: "/openapi" version: "0.1.0-draft" buildVersion: "20251206.1" etag: '"abc123"' signature: "sha256:def456..." components: securitySchemes: oauth2: type: oauth2 flows: clientCredentials: tokenUrl: https://auth.stellaops.example.com/oauth/token scopes: taskrunner.viewer: Read-only access to runs and logs taskrunner.operator: Create runs and apply approvals taskrunner.admin: Full administrative access parameters: Tenant: name: X-StellaOps-Tenant in: header required: false description: Tenant slug (optional for single-tenant deployments) schema: type: string RunId: name: runId in: path required: true description: Unique run identifier schema: type: string pattern: '^[a-zA-Z0-9_-]+$' ApprovalId: name: approvalId in: path required: true description: Approval gate identifier (from task pack approvals section) schema: type: string responses: Error: description: Standard error envelope content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' examples: internal-error: value: error: code: internal_error message: "An unexpected error occurred" traceId: "f62f3c2b9c8e4c53" schemas: ErrorEnvelope: type: object required: [error] properties: error: type: object required: [code, message] properties: code: type: string description: Machine-readable error code message: type: string description: Human-readable error message traceId: type: string description: Trace ID for debugging SimulationRequest: type: object required: [manifest] properties: manifest: type: string description: Task pack manifest in YAML format inputs: type: object additionalProperties: true description: Input values to provide to the task pack SimulationResponse: type: object required: [planHash, failurePolicy, steps, outputs, hasPendingApprovals] properties: planHash: type: string description: SHA-256 hash of the execution plan pattern: '^sha256:[a-f0-9]{64}$' failurePolicy: $ref: '#/components/schemas/FailurePolicy' steps: type: array items: $ref: '#/components/schemas/SimulationStep' outputs: type: array items: $ref: '#/components/schemas/SimulationOutput' hasPendingApprovals: type: boolean description: Whether the plan contains approval gates SimulationStep: type: object required: [id, templateId, kind, enabled, status, children] properties: id: type: string templateId: type: string kind: type: string enum: [Run, GateApproval, GatePolicy, Parallel, Map, Loop, Conditional, Unknown] enabled: type: boolean status: type: string enum: [Pending, Skipped, RequiresApproval, RequiresPolicy, WillIterate, WillBranch] statusReason: type: string uses: type: string description: Executor reference for run steps approvalId: type: string gateMessage: type: string maxParallel: type: integer continueOnError: type: boolean children: type: array items: $ref: '#/components/schemas/SimulationStep' loopInfo: $ref: '#/components/schemas/LoopInfo' conditionalInfo: $ref: '#/components/schemas/ConditionalInfo' policyInfo: $ref: '#/components/schemas/PolicyInfo' LoopInfo: type: object description: Loop step simulation details properties: itemsExpression: type: string iterator: type: string index: type: string maxIterations: type: integer aggregationMode: type: string enum: [collect, merge, last, first, none] ConditionalInfo: type: object description: Conditional step simulation details properties: branches: type: array items: type: object properties: condition: type: string stepCount: type: integer elseStepCount: type: integer outputUnion: type: boolean PolicyInfo: type: object description: Policy gate simulation details properties: policyId: type: string policyVersion: type: string failureAction: type: string enum: [abort, warn, requestOverride, branch] retryCount: type: integer SimulationOutput: type: object required: [name, type, requiresRuntimeValue] properties: name: type: string type: type: string requiresRuntimeValue: type: boolean pathExpression: type: string valueExpression: type: string CreateRunRequest: type: object required: [manifest] properties: runId: type: string description: Optional custom run ID (auto-generated if not provided) manifest: type: string description: Task pack manifest in YAML format inputs: type: object additionalProperties: true description: Input values to provide to the task pack tenantId: type: string description: Tenant identifier RunStateResponse: type: object required: [runId, planHash, failurePolicy, createdAt, updatedAt, steps] properties: runId: type: string planHash: type: string pattern: '^sha256:[a-f0-9]{64}$' failurePolicy: $ref: '#/components/schemas/FailurePolicy' createdAt: type: string format: date-time updatedAt: type: string format: date-time steps: type: array items: $ref: '#/components/schemas/RunStateStep' RunStateStep: type: object required: [stepId, kind, enabled, continueOnError, status, attempts] properties: stepId: type: string kind: type: string enum: [Run, GateApproval, GatePolicy, Parallel, Map, Loop, Conditional, Unknown] enabled: type: boolean continueOnError: type: boolean maxParallel: type: integer approvalId: type: string gateMessage: type: string status: type: string enum: [Pending, Running, Succeeded, Failed, Skipped] attempts: type: integer lastTransitionAt: type: string format: date-time nextAttemptAt: type: string format: date-time statusReason: type: string FailurePolicy: type: object required: [maxAttempts, backoffSeconds, continueOnError] properties: maxAttempts: type: integer minimum: 1 backoffSeconds: type: integer minimum: 0 continueOnError: type: boolean RunLogEntry: type: object required: [timestamp, level, eventType, message] description: | Log entry returned in NDJSON stream. Each entry is a single JSON object followed by a newline character. properties: timestamp: type: string format: date-time level: type: string enum: [debug, info, warn, error] eventType: type: string description: | Event type identifier, e.g.: - run.created, run.started, run.completed, run.failed, run.cancelled - step.started, step.completed, step.failed, step.skipped - gate.awaiting, gate.approved, gate.rejected - run.schedule-failed, run.cancel-requested message: type: string stepId: type: string metadata: type: object additionalProperties: type: string RunArtifact: type: object required: [name, type, status] properties: name: type: string type: type: string enum: [file, object] sourcePath: type: string storedPath: type: string status: type: string enum: [pending, captured, failed] notes: type: string capturedAt: type: string format: date-time expressionJson: type: string description: JSON string of evaluated expression result for object outputs ApprovalDecisionRequest: type: object required: [decision, planHash] properties: decision: type: string enum: [approved, rejected, expired] planHash: type: string pattern: '^sha256:[a-f0-9]{64}$' description: Plan hash to verify against (must match current run plan) actorId: type: string description: Identifier of the approver (e.g., user:alice@example.com) summary: type: string description: Optional comment explaining the decision ApprovalDecisionResponse: type: object required: [status, resumed] properties: status: type: string enum: [approved, rejected, expired] resumed: type: boolean description: Whether the run was resumed (true for approved decisions) PlanErrorResponse: type: object required: [errors] properties: errors: type: array items: type: object required: [path, message] properties: path: type: string description: JSON path to the error location message: type: string OpenApiMetadata: type: object required: [specUrl, version, etag] properties: specUrl: type: string description: URL to fetch the full OpenAPI spec version: type: string description: API version buildVersion: type: string description: Build version identifier etag: type: string description: ETag for caching signature: type: string description: Signature for spec verification tags: - name: Simulations description: Task pack simulation without execution - name: Runs description: Pack run lifecycle management - name: Logs description: Run log streaming - name: Artifacts description: Run artifact management - name: Approvals description: Approval gate decisions - name: Metadata description: Service metadata and discovery