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 earlier STELLA_* 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, or Section__Key as environment variables). The main keys:

Key / variablePurposeSource
STELLAOPS_POSTGRES_RELEASE_ORCHESTRATOR_CONNECTIONConnection to RO’s own database stellaops_release_orchestrator; required, fails closeddevops/compose/docker-compose.stella-services.yml, devops/release/bundle/docker-compose.yml
ReleaseOrchestrator:AgentTaskTransport:Enabled, Agent:Transport, Agent:AllowInProcessInProductionAgent task transport selectionsrc/ReleaseOrchestrator/__Apps/StellaOps.ReleaseOrchestrator.WebApi/Program.cs
ReleaseOrchestrator:DeploymentDecision:Signing:KeyId, Es256PrivateKeyPemFile, Es256PrivateKeyPkcs8Base64WF5 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 agentsrc/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 migration StellaOps.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 migration StellaOps.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 (Enabled defaults to true). Source of truth: src/ReleaseOrchestrator/__Apps/StellaOps.ReleaseOrchestrator.WebApi/Services/TopologyReadinessScheduler.cs (section ReleaseOrchestrator: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, and ReleaseDashboardEndpoints.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):

  1. 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.
  2. Add only issuer-directory:read to the existing stellaops-release-dispatch allowed scopes through the supported Authority client administration/provisioning process. Preserve its tenants, audiences, grants and credential source. The source task leaves devops/etc/authority/plugins/standard.yaml unchanged; 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.
  3. Select the tested Release Orchestrator source in a new digest pin, use the recorded chain and operator-signing namespace, 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.
  4. 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.
  5. 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, section ReleaseOrchestrator: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

CodeHTTP StatusDescription
RELEASE_NOT_FOUND404Release with specified ID does not exist
ENVIRONMENT_NOT_FOUND404Environment with specified ID does not exist
PROMOTION_BLOCKED403Promotion blocked by policy gates
APPROVAL_REQUIRED403Additional approvals required
FREEZE_WINDOW_ACTIVE403Environment is in freeze window
DIGEST_MISMATCH400Image digest does not match expected
AGENT_OFFLINE503Required agent is offline
WORKFLOW_FAILED500Workflow execution failed
PLUGIN_ERROR500Plugin returned an error
QUOTA_EXCEEDED429Digest analysis quota exceeded
VALIDATION_ERROR400Request validation failed
UNAUTHORIZED401Authentication required
FORBIDDEN403Insufficient permissions

Default Values

SettingDefaultDescription
Agent heartbeat interval30sFrequency of agent heartbeats
Task timeout600sMaximum time for agent task
Deployment batch size25%Percentage of targets per batch
Health check timeout60sTimeout for health checks
Evidence retention7 yearsAudit compliance requirement
Max workflow steps50Maximum steps per workflow
Max parallel tasks10Per-agent concurrent tasks

See Also