Secrets Handling (Release Orchestrator)
Last updated: 2026-05-30
This document describes how the Stella Ops Release Orchestrator handles secret material — connector configuration, deployment-target credentials, and vault-resolved values — at rest, in transit, and in operation. It is the authoritative reference for engineers and operators wiring connectors and deployment targets.
Reconciled against
src/ReleaseOrchestratoron 2026-05-30. The original draft stated that “secrets are stored in Authority and referenced viasecretRef”; this is not how the Release Orchestrator works. Authority issues OAuth/OIDC tokens and scopes only — it does not hold connector or deployment-target secrets. The mechanisms actually implemented are described below.
Principles
- Secrets are never persisted in plaintext alongside release/run state. Two storage mechanisms exist in the orchestrator:
- IntegrationHub config-at-rest encryption — connector configurations are encrypted with AES-256-GCM using a per-tenant key derived via HKDF-SHA256 from a 256-bit master key (
IntegrationEncryption,DerivedTenantKeyProviderinStellaOps.ReleaseOrchestrator.IntegrationHub.Encryption). The persisted blob layout is[nonce(12)][tag(16)][ciphertext]. - External vault connectors — secrets can be fetched at use time from a registered vault connector (
IVaultConnectorCapability.GetSecretAsync, categoryConnectorCategory.Vault) rather than stored at all.
- IntegrationHub config-at-rest encryption — connector configurations are encrypted with AES-256-GCM using a per-tenant key derived via HKDF-SHA256 from a 256-bit master key (
- Deployment targets reference secret material by
*SecretRefstrings on the target connection config; the orchestrator resolves them at deploy time and keeps the resolved value in memory only for the operation. - No secrets in logs, traces, metrics, crash dumps, or health endpoints. Deployment-hook stdout/stderr is redacted to
[redacted]by default (see Redaction below). - Offline/air-gap:
file://andbase64:/plain:secret refs resolve from locally available material with no network egress; vault connectors are an online-only option.
Orchestrator-specific rules (DOCS-ORCH-34-002)
Secret references on deployment targets
TargetConnectionConfig subtypes carry typed *SecretRef fields rather than inline credentials (StellaOps.ReleaseOrchestrator.Environment.Models.TargetConnectionConfig):
| Target type | SecretRef fields |
|---|---|
docker_host / compose_host | CaCertSecretRef, ClientCertSecretRef, ClientKeySecretRef |
ssh_host | PrivateKeySecretRef, PasswordSecretRef |
winrm_host | PasswordSecretRef |
ecs_service | AccessKeyIdSecretRef, SecretAccessKeySecretRef |
nomad_job | TokenSecretRef |
ansible_host | PrivateKeySecretRef, VaultPasswordSecretRef, BecomePasswordSecretRef |
SecretRef values are resolved by SecretRefResolver (StellaOps.ReleaseOrchestrator.Deployment.Executor), which understands three schemes:
file://<path>— reads the file contents (returns null if missing/blank);base64:<value>— base64-decoded UTF-8;plain:<value>— the literal value after the prefix.
Any other prefix (or a bare string) resolves to null. Resolution is wired through per-transport interfaces (ISshSecretResolver, IWinRmSecretResolver, IEcsSecretResolver, INomadSecretResolver, IAnsibleSecretResolver).
NOT PRODUCTION-HARDENED. The default implementations (
DefaultSshSecretResolver,DefaultAnsibleSecretResolver, etc.) delegate toSecretRefResolverand are documented in source as “for non-prod and CI secret refs. Production deployments are expected to replace this with a platform-secret-store backed resolver.” A platform/vault-backed resolver is a roadmap item — these defaults do not provide vaulting or auditing on their own.
Secrets for connector plugins
Connector plugins receive an ISecretResolver on their ConnectorContext (StellaOps.ReleaseOrchestrator.Plugin.Models.ConnectorModels). The production implementation is TenantSecretResolver (ITenantSecretResolver):
ForTenant(tenantId)returns a tenant-scoped resolver; vault resolution throwsInvalidOperationExceptionif no tenant is set.ResolveAsync(key)currently resolves from environment variables only (returnsnullwhen the variable is absent); the source notes it “could extend to query tenant-specific secret stores.”ResolveFromVaultAsync(integrationId, path)locates the first registered vault connector and callsGetSecretAsync. (Current behaviour selects the first vault connector rather than matching byintegrationId— flagged in source as a known simplification.)ResolveConfigurationSecretsAsync(config)substitutes inline references matching the pattern${vault:<integration-guid>/<path>}in connector configuration with values fetched from the vault connector. Invalid GUIDs or resolution failures leave the placeholder unchanged and are logged at warning.
Note: workflow step plugins (
StepExecutionContext) do not receive anISecretResolver. Only connector plugins (ConnectorContext) do. A step that needs a secret obtains it indirectly (e.g. via a connector or a resolved target config), not via asecretReffield on the step context.
Redaction
- Target-side deployment hooks redact captured output.
DeploymentHookRunnerreplaces stdout/stderr with the literal[redacted]unless the hook’sRedactionPolicyis"none"; the default policy is"mask". Output is also truncated to 4096 chars when redaction is disabled. - Agent execution plugins project scoped values as
STELLA_VAR_*/STELLA_SECRET_*environment variables, reject operator payloads using the reservedSTELLA_prefix, and redact secret values from captured stdout/stderr before they reach pluginOutputs(seesrc/ReleaseOrchestrator/AGENTS.mdand__Agents/StellaOps.Agent.DeployNative/). - Cancellation, timeout, and error paths in
DeploymentHookRunnerroute through the same redaction helper, so timed-out and exception cases do not leak hook output.
Authorization scopes
Secret-adjacent orchestrator actions are gated by these canonical scopes (StellaOps.Auth.Abstractions.StellaOpsScopes):
orch:read,orch:operate,orch:quota,orch:backfill— orchestrator job state, control actions, quota, and backfill.release:read,release:write,release:publish,release:bypass— release metadata and gate operations.connector:credentials:read,connector:credentials:write— Console connector-credential list/detail (never returns plaintext or authref URIs) and create/rotate/revoke (RFC-0001 §6.4).crypto:kek:read,crypto:kek:rotate— KEK control-plane (distinct fromcrypto:admin) for rotating the keys protecting secret material.
Audit checklist
- [ ] Deployment-target configs use typed
*SecretReffields, not inline credentials. - [ ] Production deployments replace the default
*SecretResolvers with a vault-backed resolver (not the file/base64/plain defaults). - [ ] IntegrationHub connector configs are encrypted at rest (AES-256-GCM, per-tenant HKDF key).
- [ ] Deployment-hook output verified redacted (
RedactionPolicy!=none); error/timeout paths redacted too. - [ ] Agent secrets projected only as
STELLA_SECRET_*; reservedSTELLA_prefix rejected on operator payloads. - [ ] Inline
${vault:<guid>/<path>}references resolve through a registered vault connector; unresolved placeholders fail closed (left literal, logged). - [ ] Secret refresh/rotation tested (key rotation via
crypto:kek:rotate; connector/vault reload).
Related
- Scope taxonomy and the canonical scope catalog: scopes-and-roles.md
- Tenancy model and per-tenant isolation: tenancy-overview.md
- Trust roots and signing (offline/sealed mode): trust-and-signing.md
