Skip to content

Secrets Management

Ironflow provides environment-scoped secrets management with AES-256-GCM encryption backed by NATS KV. Secrets are write-only—values can be set but never retrieved through the API, CLI, or Dashboard.

Event-Driven by Design

Every secret operation (create, update, delete) is a JetStream message. This ensures that secret state inherits the same durability and replication guarantees as your workflows.

  • Atomic: Secret updates use native JetStream revision tracking.
  • Observable: Every rotation emits a system event (e.g., system.secret.STRIPE_KEY.updated).
  • Declarative: Functions declare exactly which secrets they need; the engine resolves and injects them only at runtime.

Secure by Default

The architecture enforces a strict “Management vs. Runtime” separation:

  • Write-Only API: The CLI and Dashboard only expose metadata (revision, name). Plaintext values are never returned.
  • Runtime Injection: Values are only decrypted when a worker receives a job.
  • Master Key: Encryption is controlled by the IRONFLOW_MASTER_KEY environment variable, or by spec.auth.masterKey in the config file. The value must be a hex-encoded 32 bytes (generate with openssl rand -hex 32).

When you start with ironflow serve -f ironflow.yaml, the file is the only source of truth and IRONFLOW_MASTER_KEY is not read — set spec.auth.masterKey, or reference the variable from the file as masterKey: ${IRONFLOW_MASTER_KEY}. serve and validate print a warning for each env var set but ignored.

Dev mode — no encryption. If IRONFLOW_MASTER_KEY is unset or empty, Ironflow runs in development mode and stores secrets in plaintext on disk. The engine logs a warning at startup. Always set a master key in production.

Turning encryption on later does not encrypt what is already stored. There is no rekey command: a value written before the master key was set stays plaintext, and reading it after the key is set fails with “secret was stored unencrypted … re-run ironflow secret set. Re-run ironflow secret set for every secret after setting IRONFLOW_MASTER_KEY for the first time. Rotating to a different master key has the same requirement, and values sealed with the old key are unreadable without it.


Quick Start (Node.js)

1. Set a secret via CLI:

Terminal window
ironflow secret set STRIPE_KEY sk_live_123 --env default --description "Stripe live key"

The --env / -e flag targets a specific environment (defaults to default). All ironflow secret subcommands (set, get, list, delete) accept --env. set also accepts --description / -d.

2. Declare and use in a function:

import { ironflow } from "@ironflow/node";
export const payout = ironflow.createFunction(
{
id: "payout",
triggers: [{ event: "payout.requested" }],
secrets: ["STRIPE_KEY"], // Declare dependency
},
async ({ event, secrets, step }) => {
// Access the decrypted value
const apiKey = secrets.get("STRIPE_KEY");
await step.run("charge", () => stripe(apiKey).charge(...));
}
);

When to Use Secrets

FeatureUse For
SecretsAPI Keys, DB Passwords, Certificates.
ConfigRollout percentages, Theme settings, Timeouts.
KV StoreSessions, Caching, Counters.

Subscribing to Rotations

You can subscribe to secret lifecycle events to audit rotations or trigger security workflows.

import { ironflow } from "@ironflow/browser";
await ironflow.subscribe("system.secret.*.updated", {
onEvent: (e) => console.log(`Secret ${e.data.name} was rotated!`),
});