* 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
74 lines
4.7 KiB
Markdown
74 lines
4.7 KiB
Markdown
# Research: Secrets Module
|
|
|
|
## Decision: Redesign the existing extension module before promotion
|
|
|
|
**Rationale**: The `elsa-extensions` secrets module already has useful pieces: simple runtime resolution, management services, versioning, expiration, EF Core persistence, API endpoints, Studio pages, and JavaScript helpers. It also exposes clear gaps for the requested Orchard-style model: per-secret store choice, extensible types/editors, picker contracts, import/export, complete shell features, and safer API boundaries. Promoting a redesigned module preserves compatibility while avoiding a leaky first-party API.
|
|
|
|
**Alternatives considered**:
|
|
|
|
- Copy the extension module as-is: rejected because it keeps Data Protection portability limits, cleartext edit endpoints, and module-level store selection.
|
|
- Start from scratch: rejected because the extension already proves useful lifecycle and persistence concepts.
|
|
|
|
## Decision: Use immutable technical names as serialized references
|
|
|
|
**Rationale**: The user clarified that technical names cannot be changed; a user creates a new secret when a different technical name is needed. This makes workflow definitions and module settings readable, export-friendly, and stable without needing rename semantics.
|
|
|
|
**Alternatives considered**:
|
|
|
|
- Store database IDs: rejected because exports become less readable and cross-environment mapping becomes harder.
|
|
- Store mutable names: rejected because renames would break existing consumers.
|
|
|
|
## Decision: Resolve latest active version only
|
|
|
|
**Rationale**: Secret rotation should update consumers automatically. Always resolving the latest active version keeps workflow definitions stable and avoids stale credential pinning.
|
|
|
|
**Alternatives considered**:
|
|
|
|
- Version pinning: rejected for v1 because it increases UX and lifecycle complexity and can keep consumers on retired credentials.
|
|
- Explicit version policy per reference: rejected as unnecessary for current requirements.
|
|
|
|
## Decision: Do not support cleartext reveal after creation
|
|
|
|
**Rationale**: A no-reveal model reduces the highest-risk UI/API surface. Operators can replace, rotate, test, use, and encrypted-export secrets without reading the current cleartext value.
|
|
|
|
**Alternatives considered**:
|
|
|
|
- Reveal with permission and audit: rejected because it still creates a cleartext disclosure path.
|
|
- Reveal only for Elsa-managed stores: rejected because it complicates user expectations and provider capability rules.
|
|
|
|
## Decision: V1 includes Elsa-managed encrypted and configuration-backed read-only stores
|
|
|
|
**Rationale**: These two stores cover the most important first-party scenarios: Elsa-owned values and deployment-owned values. They also exercise both writable and read-only store capability paths without adding cloud/provider dependencies.
|
|
|
|
**Alternatives considered**:
|
|
|
|
- Include cloud vault and OS certificate stores in v1: rejected because provider-specific behavior should follow after the stable contract lands.
|
|
- Define abstractions only: rejected because the module needs a complete usable default.
|
|
|
|
## Decision: Keep Data Protection for local-at-rest protection, not portable exports
|
|
|
|
**Rationale**: Data Protection is suitable for values staying in one deployment environment. The transcript's import/export case requires portable encryption material, so exports use an explicit key/certificate/asymmetric target rather than shared application Data Protection keys.
|
|
|
|
**Alternatives considered**:
|
|
|
|
- Use Data Protection for export: rejected because isolated environments often do not share keys.
|
|
- Store raw values in export with transport security only: rejected because packages are often stored and transferred outside the original channel.
|
|
|
|
## Decision: Treat import name conflicts as explicit operator choices
|
|
|
|
**Rationale**: Same-technical-name conflicts can either overwrite an operational secret or leave workflows pointing to stale credentials. Failing by default until the import request chooses create-new, update/rotate, or skip prevents hidden behavior.
|
|
|
|
**Alternatives considered**:
|
|
|
|
- Skip by default: rejected because imports can appear successful while leaving missing values.
|
|
- Update by default: rejected because imports can unexpectedly rotate production secrets.
|
|
|
|
## Decision: Define Studio contracts here and implement UI in `elsa-studio`
|
|
|
|
**Rationale**: Server/Core contracts must shape picker, metadata, permission, and inline-create behavior. The concrete UX belongs in the Studio repository, so this feature adds Studio source code in the paired `elsa-studio` worktree rather than `elsa-core`.
|
|
|
|
**Alternatives considered**:
|
|
|
|
- Implement Studio UI in `elsa-core`: rejected because the repository boundary is wrong.
|
|
- Exclude Studio entirely: rejected because picker contracts affect server API shape.
|