Skip to content

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.

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 │
└───────────────────────────────────────────────┘

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.

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

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.
FeatureUse ForEncryptedReadable
SecretsAPI keys, DB passwords, certificatesYes (AES-256-GCM)Runtime only
ConfigApp settings, feature flags, togglesNoYes
KV StoreSessions, caching, countersNoYes

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.