Configuration Reference
Environment variables and OPA policy examples for the Release Orchestrator.
Status: Mixed reference; implemented sections identify their source of truth. Source: Architecture Advisory Section 15.2 (archived) Related Modules: Security Overview, Promotion Manager Sprint: 101_001 Foundation (archived)
Overview
This document provides the configuration reference for the Release Orchestrator, including environment variables and OPA policy examples.
Environment Variables
Verified 2026-09-26 against commit
942a18727c. The earlierSTELLA_*variable list (STELLA_DATABASE_URL,STELLA_OAUTH_*,STELLA_AGENT_*,STELLA_PLUGIN_*,STELLA_VAULT_*) was not read by any code and is removed. Configuration uses standard .NET keys (Section:Key, orSection__Keyas environment variables). The main keys:
| Key / variable | Purpose | Source |
|---|---|---|
STELLAOPS_POSTGRES_RELEASE_ORCHESTRATOR_CONNECTION | Connection to RO’s own database stellaops_release_orchestrator; required, fails closed | devops/compose/docker-compose.stella-services.yml, devops/release/bundle/docker-compose.yml |
ReleaseOrchestrator:AgentTaskTransport:Enabled, Agent:Transport, Agent:AllowInProcessInProduction | Agent task transport selection | src/ReleaseOrchestrator/__Apps/StellaOps.ReleaseOrchestrator.WebApi/Program.cs |
ReleaseOrchestrator:DeploymentDecision:Signing:KeyId, Es256PrivateKeyPemFile, Es256PrivateKeyPkcs8Base64 | WF5 deployment-decision signing key | …/WebApi/Services/DeploymentDecisionSigningConfiguration.cs |
Crypto:SecretProviders:<n>:* (compose: RELEASE_ORCHESTRATOR_VAULT_ADDR, RELEASE_ORCHESTRATOR_VAULT_TOKEN, RELEASE_ORCHESTRATOR_VAULT_KV_MOUNT) | Secret-reference resolution (Vault / OpenBao) | src/__Libraries/StellaOps.Cryptography.CredentialStore/SecretProvidersOptions.cs |
ReleaseOrchestrator:DeviationNotify:Enabled (compose: RO_DEVIATION_NOTIFY_ENABLED) | Deviation events to Notify | …/WebApi/Services/HttpDeviationEventPublisher.cs |
STELLAOPS_FEATURE_FEDERATION, ReleaseOrchestrator:Federation:* | Regional federation surface | …/WebApi/FederationHostExtensions.cs |
ReleaseOrchestrator:Policy:* | CVE-aware Policy.Engine gate | …/WebApi/Services/PolicyEngineGateOptions.cs |
ReleaseOrchestrator:Postgres:* | Connection-pool budget | …/WebApi/Services/ReleaseOrchestratorPostgresPoolBudget.cs |
Plugins:PluginPaths, Agent:EnabledCapabilities (agent host) | External execution plugins on the agent | src/ReleaseOrchestrator/AGENTS.md, src/ReleaseOrchestrator/__Agents/StellaOps.Agent.Host/ |
See architecture.md for the full set and its Evidence section.
Release Environment Limits
Administrators can restrict release creation and artifact-derived successor versions to a known set of environments. This protects self-service developer updates and operator release creation from accidentally targeting environments outside the tenant’s approved release path.
The normal administrator control is persisted per tenant through GET/PUT /api/v1/release-orchestrator/release-environment-limit and the Platform Setup Defaults & Guardrails page. The configuration keys below are a bootstrap/fallback path: they apply only until a tenant-stored setting exists.
# Require every release create/update/clone/successor request to name a target environment.
ReleaseOrchestrator__ReleaseEnvironmentLimit__RequireTargetEnvironment=true
# Array form, preferred in appsettings.json and Kubernetes/compose env expansion.
ReleaseOrchestrator__ReleaseEnvironmentLimit__AllowedEnvironments__0=env-dev
ReleaseOrchestrator__ReleaseEnvironmentLimit__AllowedEnvironments__1=env-stage
# CSV fallback for simpler deployment systems.
ReleaseOrchestrator__ReleaseEnvironmentLimit__AllowedEnvironmentsCsv=env-dev,env-stage
When RequireTargetEnvironment=true and no target environment is supplied, the API returns 400 with release_target_environment_required. When an allow-list is present and the target is outside the list, the API returns 403 with release_environment_not_allowed.
Auto-deploy on approval (durable approval → deployment transition)
Status: Implemented (Sprint 20260703_002 / DTC-8). Source of truth:
src/ReleaseOrchestrator/__Apps/StellaOps.ReleaseOrchestrator.WebApi/Services/DeploymentDispatchProcessor.cs,DispatchOnApprovalReleaseTruthRepository.cs, and migrationStellaOps.ReleaseOrchestrator.Persistence/Migrations/008_deployment_dispatches.sql.
When a promotion approval reaches approved state (manual quorum met or auto-approved by policy), a deployment-dispatch intent is persisted into release_orchestrator.deployment_dispatches in the same transaction as the approval write. A hosted dispatcher drains due intents with bounded retry/backoff and starts the deployment through the same code path POST /releases/{id}/deploy uses (identical ReleaseTruthGuard fail-closed checks and deploy-engine entry). Terminal dispatch failure surfaces as release status dispatch_failed plus a deployment_dispatch_failed release event.
# Kill-switch (default: true). false = approvals no longer enqueue dispatch
# intents and the dispatcher stays idle; operators deploy via POST /deploy.
ReleaseOrchestrator__AutoDeployOnApproval=true
# Tuning knobs (defaults shown).
ReleaseOrchestrator__AutoDeployDispatch__PollIntervalSeconds=5
ReleaseOrchestrator__AutoDeployDispatch__MaxAttempts=5
ReleaseOrchestrator__AutoDeployDispatch__RetryBaseDelaySeconds=10 # doubles per attempt (deterministic)
ReleaseOrchestrator__AutoDeployDispatch__RetryMaxDelaySeconds=600
ReleaseOrchestrator__AutoDeployDispatch__ClaimBatchSize=10
ReleaseOrchestrator__AutoDeployDispatch__ClaimLeaseSeconds=600 # crash self-heal lease
Decision evidence seal (durable, revision-bound)
Status: Implemented in source (SPRINT_20261007_027 / CCP-050); disabled by the option default. Source of truth:
src/ReleaseOrchestrator/__Apps/StellaOps.ReleaseOrchestrator.WebApi/Services/ReleaseDecisionEvidenceSealWriter.cs,ReleaseDecisionSealIntentStore.cs, and migrationStellaOps.ReleaseOrchestrator.Persistence/Migrations/012_release_orchestrator_decision_seal_intents.sql.
When enabled, the promotion path writes the final gate decision revision (operator-decision signature applied) and a seal intent into release_orchestrator.decision_seal_intents in the same transaction. A hosted reconciler delivers due intents to Evidence’s snapshot route under a tenant-bound Idempotency-Key, retries failures with bounded backoff, and records Evidence’s receipt (bundle id, root hash) on the intent. A pass claims up to BatchSize intents and attempts them one after the other, so the claim lease is never shorter than BatchSize x (TimeoutSeconds + 5 s): an intent of the batch is not due again before its attempt has had its time. A larger LeaseSeconds is kept; it is also how long a crashed claim waits before it is due again. When disabled, no intent is captured, no reconciler runs and no request is sent. The shipped Compose files set Enabled through RO_EVIDENCE_SEAL_ENABLED (default true there); the service identity needs Authority’s evidence:hold grant.
ReleaseOrchestrator__EvidenceSeal__Enabled=false # option default
ReleaseOrchestrator__EvidenceSeal__BaseAddress=http://evidence.stella-ops.local:8080
ReleaseOrchestrator__EvidenceSeal__TimeoutSeconds=15
# Reconciler tuning (defaults shown).
ReleaseOrchestrator__EvidenceSeal__PollIntervalSeconds=5
ReleaseOrchestrator__EvidenceSeal__BatchSize=16
ReleaseOrchestrator__EvidenceSeal__LeaseSeconds=120 # crash self-heal lease; effective lease is at least BatchSize x (TimeoutSeconds + 5), 320 s with these defaults
ReleaseOrchestrator__EvidenceSeal__RetryBaseSeconds=10 # doubles per attempt (deterministic)
ReleaseOrchestrator__EvidenceSeal__RetryMaxSeconds=900
Scheduled topology readiness
Status: Implemented, enabled by default (
Enableddefaults totrue). Source of truth:src/ReleaseOrchestrator/__Apps/StellaOps.ReleaseOrchestrator.WebApi/Services/TopologyReadinessScheduler.cs(sectionReleaseOrchestrator:TopologyReadiness).
The WebApi background pass refreshes target connection health and persists readiness reports for the Environments Command view. While enabled it causes periodic outbound probes to operator-configured target or binding destinations and persistent health/readiness writes. The service validates all durations at startup; Interval and PerTargetTimeout must be positive and InitialDelay must not be negative. Positive intervals below 60 seconds are clamped to 60 seconds.
# Default true; set false to stop the periodic pass.
ReleaseOrchestrator__TopologyReadiness__Enabled=true
# Cadence and per-target bounds (defaults shown).
ReleaseOrchestrator__TopologyReadiness__InitialDelay=00:00:45
ReleaseOrchestrator__TopologyReadiness__Interval=00:05:00
ReleaseOrchestrator__TopologyReadiness__PerTargetTimeout=00:00:12
# Independent steps (both default true when the scheduler is enabled).
ReleaseOrchestrator__TopologyReadiness__RefreshHealth=true
ReleaseOrchestrator__TopologyReadiness__EvaluateReadiness=true
There is no distributed lease for this hosted service. It relies on the Release Orchestrator WebApi running as exactly one instance per database, which the deploy engine requires: several WebApi replicas are not a supported shape (see Rollback interrupted by a restart). For sealed/air-gap operation, the scheduler does not select any public endpoint itself, but configured targets and infrastructure bindings can cross a network boundary. Set Enabled=false unless all destinations are local or explicitly allow-listed; operators can instead run the authenticated POST /api/v1/release-orchestrator/environments/readiness/validate-all action when a manual, bounded evaluation is appropriate. Periodic cadence is the only automatic trigger; deployment and agent-heartbeat events do not immediately run the pass.
Approval transition safety
Status: Implemented (RDT-3). Source of truth:
Services/ApprovalSafetyGuard.cs,ApprovalEndpoints.cs,ReleaseControlV2Endpoints.cs, andReleaseDashboardEndpoints.cs.
Every approval route rejects incomplete gate/evidence truth before mutating quorum. Environment protection class is persisted configuration; the optional list below adds decision-signing enforcement to selected target environments. The global OperatorDecisionEnforcement:Approve=true switch remains the all-environments equivalent.
# Per-environment signing rollout. Repeat the numeric suffix for more targets.
ReleaseOrchestrator__ApprovalSafety__SigningRequiredEnvironments__0=production
# Global all-environment approval signing (default false).
ReleaseOrchestrator__OperatorDecisionEnforcement__Approve=false
# Required before the always-signed gate-exception endpoint can be used.
# If either value is absent, an authorized request fails closed with
# 503 operator_decision_signing_not_configured and names these settings.
# Folded into Authority by SPRINT_20260722_016 AUTH-9 (2026-09-08); the
# issuerdirectory.stella-ops.local alias was removed with the host at AUTH-10.
IssuerDirectory__Client__BaseAddress=http://authority.stella-ops.local
ReleaseOrchestrator__OperatorDecisionEnforcement__IssuerId=operator-signing
Protected or signing-required environments also require a signed, basis-bound gate decision and a genuinely verified policy-decision evidence packet. Configure trust roots under ReleaseOrchestrator__EvidenceVerification__TrustedPublicKeys__N; without a trust anchor signed evidence remains honestly recorded and the protected transition fails closed.
Operator-key directory service identity
Verified against source 7e3898fbce (CCP-081, 2026-10-13). Re-verify the actual host/client path with tools/scripts/test-targeted-xunit.ps1, Integration.Tests class filter *IssuerDirectory*, and the shipped target configuration with python devops/release/bundle/tools/test_bundle_direct_caller_target.py -v.
The shipped Compose and generated bundle expose IssuerDirectory__Client__BaseAddress through AUTHORITY_ISSUER_DIRECTORY_DIRECT_BASE_ADDRESS (default http://authority.stella-ops.local) and set ReleaseOrchestrator__OperatorDecisionEnforcement__IssuerId=operator-signing. These settings do not enable the default-off decision flags or grant a scope.
Key reads reuse the one IStellaOpsTokenClient already configured by the enabled Release Orchestrator Scanner, Policy, EvidenceSeal or DeviationNotify service identity. In the shipped profile these use stellaops-release-dispatch and the existing STELLAOPS_RELEASE_DISPATCH_CLIENT_SECRET secret pointer. Configure the existing identity’s issuer, metadata/token endpoint and credentials; no separate IssuerDirectory password, bearer or token cache is required. Each read requests issuer-directory:read with the same tenant as its tenant header. A missing identity, failed token acquisition, non-Bearer/expired token or missing read scope refuses the read before any directory request. A directory refusal or outage cannot reuse an earlier key result.
R9 operator package (proposal; not performed by CCP-081):
- Record the current Authority and Release Orchestrator image digests, source bindings, ordered Compose chains, effective non-secret settings, client scope set and health. Preserve the current runtime data-protection and token-cache mounts. Retain the existing secret pointer; never copy the secret into a receipt.
- Add only
issuer-directory:readto the existingstellaops-release-dispatchallowed scopes through the supported Authority client administration/provisioning process. Preserve its tenants, audiences, grants and credential source. The source task leavesdevops/etc/authority/plugins/standard.yamlunchanged; the approved durable grant must survive Authority’s provisioning reconciliation. Announce the separately authorized Authority restart and verify the resulting tenant-bound client-credentials grant. Never add key enrollment or admin scopes. - Select the tested Release Orchestrator source in a new digest pin, use the recorded chain and
operator-signingnamespace, then perform the separately authorized recreate. The durable local profile and replay tool agree on this namespace. Verify image/source/chain/settings/health and an authenticated read of the sanctioned actor’s public key, including tenant, issuer, key and lifecycle. - Run the signed approval/exception only after the separate R2 actor/key, R6 human-approval rule and fresh-authentication prerequisites are satisfied. The existing admin-owned key and a successful directory GET do not establish this acceptance. Trust-anchor changes remain separately authorized R9/R13 inputs.
- For restoration, restore the recorded client scope set and reconcile/restart Authority as authorized; restore the exact prior Release Orchestrator digest, ordered chain and effective namespace/settings, then recheck health and expected refusals. Preserve all public-key lifecycle history, trust records, decision evidence and runtime stores. Do not delete keys or acceptance records as a rollback shortcut.
SBOM-readiness gate
Status: Implemented. Source of truth:
src/ReleaseOrchestrator/__Apps/StellaOps.ReleaseOrchestrator.WebApi/Services/SbomReadinessGateServices.cs(SbomReadinessGateOptions, sectionReleaseOrchestrator:SbomReadinessGate).
The promotion gate that blocks dispatch until the Scanner reports SBOM results for every digest-pinned release component. The gate fails closed: a probe timeout or transport error maps to failed and denies promotion, so TimeoutSeconds must comfortably exceed the Scanner’s real /api/v1/sbom-readiness latency (observed ~8s on live deployments — the historical 5s default false-blocked promotions with 409 even though the SBOMs existed).
# Master switch (default: true).
ReleaseOrchestrator__SbomReadinessGate__Enabled=true
# Scanner base address the gate probes.
ReleaseOrchestrator__SbomReadinessGate__ScannerBaseAddress=http://scanner.stella-ops.local
# HttpClient timeout (seconds) for the readiness/scan calls. Default 20.
# Fail-closed: a timeout DENIES promotion, so keep this above real scanner latency.
ReleaseOrchestrator__SbomReadinessGate__TimeoutSeconds=20
# Require the SBOM to be attached to the EXTERNAL registry (default: false).
ReleaseOrchestrator__SbomReadinessGate__RequireExternalAttachment=false
# Internal-first fallback: read the deployment registry's OCI referrers when the
# internal store reports `absent` (default: true; fail-safe, never relaxes a DENY).
ReleaseOrchestrator__SbomReadinessGate__ExternalRegistryFallback=true
ReleaseOrchestrator__SbomReadinessGate__ExternalRegistryFallbackTimeoutSeconds=5
# Orchestrator's OWN service identity for the scanner calls (see architecture.md);
# HttpTimeoutSeconds bounds only the Authority token mint, not the readiness probe.
ReleaseOrchestrator__SbomReadinessGate__ServiceIdentity__Enabled=false
ReleaseOrchestrator__SbomReadinessGate__ServiceIdentity__HttpTimeoutSeconds=10
Full Configuration File
# stella-config.yaml
database:
url: postgresql://user:pass@host:5432/stella
pool_size: 20
ssl_mode: require
redis:
url: redis://host:6379 # Valkey (Redis-compatible)
prefix: stella
auth:
issuer: https://auth.example.com
client_id: stella-app
client_secret_ref: vault://secrets/oauth-client-secret
agents:
listen_port: 8443
tls:
cert_path: /etc/stella/agent.crt
key_path: /etc/stella/agent.key
ca_path: /etc/stella/ca.crt
heartbeat_interval: 30
task_timeout: 600
plugins:
directory: /var/stella/plugins
sandbox:
memory: 512m
cpu: 1
network: restricted
evidence:
storage_path: /var/stella/evidence
signing_key_ref: vault://secrets/evidence-signing-key
retention_days: 2555 # 7 years
logging:
level: info
format: json
output: stdout
telemetry:
enabled: true
otlp_endpoint: otel-collector:4317
service_name: stella-release-orchestrator
OPA Policy Examples
Security Gate Policy
# security_gate.rego
package stella.gates.security
default allow = false
allow {
input.release.components[_].security.reachable_critical == 0
input.release.components[_].security.reachable_high == 0
}
deny[msg] {
component := input.release.components[_]
component.security.reachable_critical > 0
msg := sprintf("Component %s has %d reachable critical vulnerabilities",
[component.name, component.security.reachable_critical])
}
Approval Gate Policy
# approval_gate.rego
package stella.gates.approval
default allow = false
allow {
count(input.approvals) >= input.environment.required_approvals
separation_of_duties_met
}
separation_of_duties_met {
not input.environment.require_sod
}
separation_of_duties_met {
input.environment.require_sod
approver_ids := {a.approver_id | a := input.approvals[_]; a.action == "approved"}
not input.promotion.requested_by in approver_ids
}
Freeze Window Gate Policy
# freeze_window_gate.rego
package stella.gates.freeze
default allow = true
allow = false {
window := input.environment.freeze_windows[_]
time.now_ns() >= time.parse_rfc3339_ns(window.start)
time.now_ns() <= time.parse_rfc3339_ns(window.end)
not input.promotion.requested_by in window.exceptions
}
API Error Codes
| Code | HTTP Status | Description |
|---|---|---|
RELEASE_NOT_FOUND | 404 | Release with specified ID does not exist |
ENVIRONMENT_NOT_FOUND | 404 | Environment with specified ID does not exist |
PROMOTION_BLOCKED | 403 | Promotion blocked by policy gates |
APPROVAL_REQUIRED | 403 | Additional approvals required |
FREEZE_WINDOW_ACTIVE | 403 | Environment is in freeze window |
DIGEST_MISMATCH | 400 | Image digest does not match expected |
AGENT_OFFLINE | 503 | Required agent is offline |
WORKFLOW_FAILED | 500 | Workflow execution failed |
PLUGIN_ERROR | 500 | Plugin returned an error |
QUOTA_EXCEEDED | 429 | Digest analysis quota exceeded |
VALIDATION_ERROR | 400 | Request validation failed |
UNAUTHORIZED | 401 | Authentication required |
FORBIDDEN | 403 | Insufficient permissions |
Default Values
| Setting | Default | Description |
|---|---|---|
| Agent heartbeat interval | 30s | Frequency of agent heartbeats |
| Task timeout | 600s | Maximum time for agent task |
| Deployment batch size | 25% | Percentage of targets per batch |
| Health check timeout | 60s | Timeout for health checks |
| Evidence retention | 7 years | Audit compliance requirement |
| Max workflow steps | 50 | Maximum steps per workflow |
| Max parallel tasks | 10 | Per-agent concurrent tasks |
