elsa-core/specs/007-secrets-module/contracts/runtime-contract.md
Sipke Schoorstra e2e00ff235
Add secrets module (#7468)
* Add secrets module

* Address Greptile feedback for secrets module

* Handle unavailable secrets in provider adapter

* Address path combine review comments

* Address additional Greptile secrets review

* Address final Greptile secrets feedback

* Handle secrets test payload failures

* address greptile feedback on secrets rotation

* fix secret recreation concurrency

* address greptile secrets followups

* address greptile secrets reliability feedback

* align secret store capabilities
2026-05-20 11:48:01 +02:00

3.4 KiB

Runtime Contract: Secrets Module

Secret Resolver

Runtime consumers resolve by immutable technical name. Implementations return latest active versions only and never expose cleartext through metadata APIs.

public interface ISecretResolver
{
    ValueTask<ResolvedSecret> ResolveAsync(SecretReference reference, CancellationToken cancellationToken = default);
}

Resolution failures use safe error codes such as NotFound, Inactive, Expired, Revoked, TypeMismatch, ScopeMismatch, StoreUnavailable, or Unauthorized.

Secret Manager

Management operations create logical secrets, rotate values, revoke/delete secrets, and return metadata-only models.

public interface ISecretManager
{
    ValueTask<SecretMetadata> CreateAsync(CreateSecretRequest request, CancellationToken cancellationToken = default);
    ValueTask<SecretMetadata> RotateAsync(string technicalName, RotateSecretRequest request, CancellationToken cancellationToken = default);
    ValueTask<SecretMetadata?> FindAsync(string technicalName, CancellationToken cancellationToken = default);
    ValueTask<Page<SecretMetadata>> ListAsync(SecretQuery query, CancellationToken cancellationToken = default);
    ValueTask RevokeAsync(string technicalName, CancellationToken cancellationToken = default);
    ValueTask DeleteAsync(string technicalName, CancellationToken cancellationToken = default);
}

Rules:

  • technicalName is immutable after creation.
  • RotateAsync retires the current latest active version and creates a new latest active version.
  • No method returns current cleartext values.

Secret Store

Stores own payload persistence and declare their capabilities.

public interface ISecretStore
{
    SecretStoreDescriptor Descriptor { get; }
    ValueTask<SecretPayload> WriteAsync(SecretWriteContext context, CancellationToken cancellationToken = default);
    ValueTask<SecretPayload?> ReadAsync(SecretReadContext context, CancellationToken cancellationToken = default);
    ValueTask DeleteAsync(SecretDeleteContext context, CancellationToken cancellationToken = default);
    ValueTask<SecretTestResult> TestAsync(SecretTestContext context, CancellationToken cancellationToken = default);
}

Capability rules:

  • Configuration store supports read/test where possible, not write/delete/rotate.
  • Elsa encrypted store supports read/write/delete/rotate/encrypted export.
  • Store-private payloads are not exposed through general metadata APIs.

Secret Type Provider

Types provide validation, generation, payload shaping, and Studio editor metadata.

public interface ISecretTypeProvider
{
    SecretTypeDescriptor Descriptor { get; }
    ValueTask ValidateAsync(SecretTypeValidationContext context, CancellationToken cancellationToken = default);
    ValueTask<SecretPayloadInput> GenerateAsync(SecretGenerationContext context, CancellationToken cancellationToken = default);
}

V1 built-in types are Text, RsaKey, and X509CertificateReference.

Compatibility Adapter

Existing ISecretProvider.GetSecretAsync(name) consumers continue through an adapter.

public interface ISecretProvider
{
    Task<string?> GetSecretAsync(string name, CancellationToken cancellationToken = default);
}

Adapter behavior:

  • Map name to SecretReference.TechnicalName.
  • Resolve latest active version.
  • Return null only for legacy compatibility where the current provider did so; new APIs should return structured failures.