- State & Storage
- Secrets Management
Secrets Management
Ironflow provides environment-scoped secrets management with AES-256-GCM encryption backed by NATS KV. Secret values are never exposed through the API, CLI, or Dashboard. Only metadata (name, revision, timestamps) is readable. Plaintext values are exclusively injected at runtime into function execution contexts.
Architecture
Section titled “Architecture”Secrets are designed around a “Management vs. Runtime” separation:
┌───────────────────────────────────────────────┐│ Management Plane ││ CLI / Dashboard / API ││ • Set, delete, list secrets ││ • Only metadata visible (name, revision) ││ • Plaintext values NEVER returned │└────────────────────┬──────────────────────────┘ │ AES-256-GCM encrypted ▼┌───────────────────────────────────────────────┐│ NATS JetStream KV ││ • Encrypted values at rest ││ • Revision tracking for atomic updates ││ • System events on every mutation │└────────────────────┬──────────────────────────┘ │ Decrypted at runtime ▼┌───────────────────────────────────────────────┐│ Runtime Plane ││ Function execution (push/pull mode) ││ • Secrets injected via `secrets.get()` ││ • Only declared secrets are resolved ││ • Values live only in function memory │└───────────────────────────────────────────────┘Key Design Decisions
Section titled “Key Design Decisions”Declarative Access
Section titled “Declarative Access”Functions declare which secrets they need in their configuration. The engine resolves and injects only those secrets at runtime:
createFunction( { id: "payout", triggers: [{ event: "payout.requested" }], secrets: ["STRIPE_KEY", "WEBHOOK_SECRET"], // Declare dependencies }, async ({ secrets, step }) => { const key = secrets.get("STRIPE_KEY"); // Injected at runtime });This prevents functions from accessing secrets they haven’t declared, following the principle of least privilege.
If a declared secret does not exist in the environment, the function runs without it and the server logs a warning. If a declared secret exists but cannot be read, the run fails with the error code SECRETS_UNAVAILABLE and the function does not run. This happens when the master key changed after the secret was stored, or when the stored value is damaged. If the secrets store does not answer, the function does not run either: a push run is retried within the function’s retry budget, and a pull run is retried when its lease expires.
Master Key Encryption
Section titled “Master Key Encryption”All secret values are encrypted with a master key before storage. The master key is provided via the IRONFLOW_MASTER_KEY environment variable as a hex-encoded 32-byte value (64 hex characters — e.g., the output of openssl rand -hex 32). When set, secrets are encrypted with AES-256-GCM. When unset, the system operates in dev mode without encryption (plaintext on disk).
Rotation Events
Section titled “Rotation Events”Every secret mutation emits a system event on the internal NATS bus — system.secret.{name}.{created|updated|deleted}. This enables:
- Audit logging: Track who rotated what and when.
- Automated workflows: Trigger cache invalidation or service restarts on rotation.
- Dashboard notifications: Real-time visibility into secret lifecycle.
When to Use Secrets vs. Config vs. KV
Section titled “When to Use Secrets vs. Config vs. KV”| Feature | Use For | Encrypted | Readable |
|---|---|---|---|
| Secrets | API keys, DB passwords, certificates | Yes (AES-256-GCM) | Runtime only |
| Config | App settings, feature flags, toggles | No | Yes |
| KV Store | Sessions, caching, counters | No | Yes |
Environment Scoping
Section titled “Environment Scoping”Like config, secrets are scoped to environments. A secret named STRIPE_KEY in production is entirely separate from STRIPE_KEY in staging, enabling safe per-environment credential management.
In This Section
Section titled “In This Section”- Secrets Guide — Setting, rotating, and using secrets in functions.
- Secrets Patterns — Rotation workflows, multi-environment strategies.