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.
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.