Secrets and Variables
A secret is any sensitive value your Worker or Function needs at runtime — an API key, a database password, a signing key, an OAuth client secret. You don’t want it in source code, you don’t want it in plain-text env vars on disk, and you don’t want it echoed in logs.
Alchemy.Secret binds a secret value directly to the active
deploy target (a Cloudflare Worker, a Lambda function, …) through
that platform’s secure binding — Cloudflare’s secret_text,
Lambda’s encrypted environment variable, etc. — and hands you back
an accessor that reads it at runtime as a Redacted<string>.
Alchemy.Variable is the same shape for non-sensitive config
(ports, log levels, feature flags). The value rides as plain text
(or JSON for non-strings) and the accessor preserves the original
type.
Bind a secret to your Worker
Section titled “Bind a secret to your Worker”yield*-ing Alchemy.Secret in the Worker’s Init phase
registers the binding and returns an accessor. yield*-ing the
accessor inside fetch (the Exec phase) resolves the value:
export default Cloudflare.Worker( "Worker", { main: import.meta.path }, Effect.gen(function* () { const apiKey = yield* Alchemy.Secret("API_KEY");
return { fetch: Effect.gen(function* () { const value = yield* apiKey; // Redacted<string> — Redacted.value(value) to unwrap }), }; }),);See Phases for why Init and Exec run separately.
Resolve from .env
Section titled “Resolve from .env”Alchemy.Secret("API_KEY") with no second argument is sugar for
Alchemy.Secret("API_KEY", Config.redacted("API_KEY")). The
active ConfigProvider
resolves the value at planning time — by default that reads
process.env.API_KEY, which Alchemy populates from your .env
file.
So this:
const apiKey = yield* Alchemy.Secret("API_KEY");is all you need if API_KEY=… is in .env.
To pull from a differently-named env var, pass an explicit
Config:
const apiKey = yield* Alchemy.Secret("API_KEY", Config.redacted("OPENAI_KEY"));Provide the value explicitly
Section titled “Provide the value explicitly”The second argument accepts a literal, a Redacted<string>, an
Effect, a Config, or an Output from another resource.
A common pattern: mint a stable random secret with
Alchemy.Random and bind it as a secret on the Worker — no
.env entry, no manual rotation, the value is generated once and
persisted in state:
export default Cloudflare.Worker( "Worker", { main: import.meta.path }, Effect.gen(function* () { const random = yield* Alchemy.Random("SESSION_SECRET"); const sessionSecret = yield* Alchemy.Secret("SESSION_SECRET", random.text);
return { fetch: Effect.gen(function* () { const value = yield* sessionSecret; // Redacted<string> }), }; }),);Any Output<Redacted<string>> works the same way — e.g. a
database password emitted by another resource can be piped
straight into the consuming Worker.
Variables
Section titled “Variables”Alchemy.Variable is the non-redacted variant. Strings ride as
plain_text, anything else as JSON, and the accessor returns the
original type:
export default Cloudflare.Worker( "Worker", { main: import.meta.path }, Effect.gen(function* () { const port = yield* Alchemy.Variable("PORT", 3000); const flags = yield* Alchemy.Variable("FLAGS", { beta: true });
return { fetch: Effect.gen(function* () { const p = yield* port; // number — 3000 const f = yield* flags; // { beta: true } }), }; }),);Alchemy.Variable(name) is sugar for
Alchemy.Variable(name, Config.string(name)).
Input shapes
Section titled “Input shapes”Both helpers accept the same shapes; Secret coerces every
resolved value to Redacted<string>, Variable keeps the value’s
original type:
Alchemy.Secret("API_KEY"); // Config.redacted("API_KEY")Alchemy.Secret("API_KEY", "sk-123"); // string literalAlchemy.Secret("API_KEY", Redacted.make("sk-123")); // already redactedAlchemy.Secret("API_KEY", Effect.succeed("sk-123")); // Effect<string | Redacted>Alchemy.Secret("API_KEY", Config.string("OPENAI_KEY")); // Config<string | Redacted>
Alchemy.Variable("HOST"); // Config.string("HOST")Alchemy.Variable("HOST", "localhost"); // string literalAlchemy.Variable("PORT", 3000); // any JSON valueAlchemy.Variable("FLAGS", { beta: true }); // nested objectAlchemy.Variable("HOST", Effect.succeed("localhost")); // Effect<T>Alchemy.Variable("LOG_LEVEL", Config.string("LEVEL")); // Config<T>If you already have a raw string or Redacted<string> and don’t
need an accessor, drop it into env directly — providers route
by value shape (Redacted<string> → secure binding, string →
plain, anything else → JSON). Alchemy.Secret /
Alchemy.Variable earn their keep when you want .env
resolution or a typed runtime accessor.
Account-wide secrets with Cloudflare.Secret
Section titled “Account-wide secrets with Cloudflare.Secret”Cloudflare has an account-level Secrets Store. A
Cloudflare.Secret lives there and can be referenced by any
Worker in the account:
const store = yield* Cloudflare.SecretsStore("Store");const apiKey = yield* Cloudflare.Secret("ApiKey", { store, value: Redacted.make("sk-123"),});For the step-by-step “wire up OPENAI_API_KEY from .env” walk,
see Guides › Secrets and env vars.