- State & Storage
- Secrets Management
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.
In the dashboard, existing secret names are read-only because functions reference them by name. You can edit a secret’s description. Creating and deleting secrets remain available.
Event-Driven by Design
Section titled “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: Create, update and delete emit the system events
system.secret.{name}.created,.updatedand.deleted(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
Section titled “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_KEYenvironment variable, or byspec.auth.masterKeyin the config file. The value must be a hex-encoded 32 bytes (generate withopenssl rand -hex 32).
Quick Start (Node.js)
Section titled “Quick Start (Node.js)”1. Set a secret via CLI:
ironflow secret set STRIPE_KEY sk_live_123 --env env_default --description "Stripe live key"The --env / -e flag targets a specific environment; when omitted it falls back to IRONFLOW_ENV, then to current, the environment of your API key. The value is an environment ID, such as env_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(...)); });In Python, ctx.secrets is a read-only mapping:
from ironflow.worker import function
@function(id="payout", triggers=[{"event": "payout.requested"}], secrets=["STRIPE_KEY"])async def payout(ctx): api_key = ctx.secrets["STRIPE_KEY"] # the decrypted value
await ctx.step.run("charge", lambda: stripe(api_key).charge(ctx.event.data))When to Use Secrets
Section titled “When to Use Secrets”| Feature | Use For |
|---|---|
| Secrets | API Keys, DB Passwords, Certificates. |
| Config | Rollout percentages, Theme settings, Timeouts. |
| KV Store | Sessions, Caching, Counters. |
Subscribing to Rotations
Section titled “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<{ name: string }>("system.secret.*.updated", { onEvent: (e) => console.log(`Secret ${e.data.name} was rotated!`),});from ironflow import IronflowRPCfrom ironflow.rpc.v1 import SubscribeRequest
with IronflowRPC(server_url="http://localhost:9123") as rpc: for e in rpc.pubsub.subscribe(SubscribeRequest(pattern="system.secret.*.updated")): print(f"Secret {e.data.to_python()['name']} was rotated!")