Skip to content

Fly.Secret reference

Source: src/Fly/GetSecret.ts

Fetch one Fly.io App secret. The App and name are fixed by GetSecret(secret). Calls take no app_name.

Bind the client in init. Call it from fetch or an Action body. Provide GetSecretHttp. Alchemy transports the bound App name, secret name, and deployment’s org token automatically. There is no need to configure FLY_SECRET_* environment variables.

Fly only returns plaintext from a Machine in the same App. From a deploy-time Action you get metadata (name, digest, timestamps).

export default class Api extends Fly.Service<Api>()(
"Api",
{ app: Site, main: import.meta.url, port: 3000 },
Effect.gen(function* () {
const get = yield* Fly.GetSecret(ApiToken);
return {
fetch: Effect.gen(function* () {
const got = yield* get().pipe(Effect.orDie);
return HttpServerResponse.json({ name: got.name });
}),
};
}).pipe(Effect.provide(Fly.GetSecretHttp)),
) {}

Source: src/Fly/GetSecretHttp.ts Kind: Layer · Provides: Fly.GetSecret

HTTP implementation of GetSecret. Provide it on the Service or Action Effect.

Effect.gen(function* () {
const get = yield* Fly.GetSecret(ApiToken);
// ...
}).pipe(Effect.provide(Fly.GetSecretHttp))

Source: src/Fly/ListSecrets.ts

List Fly.io App secrets. Scoped to an App. Fly’s list API is GET /apps/{app}/secrets, not a single Secret.

The App is fixed by ListSecrets(app). Calls take no app_name. Provide ListSecretsHttp.

Plaintext is only returned from inside a Machine in the same App. From a deploy-time Action you get metadata (name, digest, timestamps).

const list = yield* Fly.ListSecrets(Site);
const { secrets } = yield* list();

From an Action, the org FLY_API_TOKEN can list any App in the org. ListSecrets(other) is how you reach across Apps.

From a Machine, deploy tokens are per-App. Mixing Apps on one host shares one FLY_API_TOKEN and is not supported.

const Seed = Alchemy.Action(
"Seed",
Effect.gen(function* () {
const list = yield* Fly.ListSecrets(Other);
return Effect.fn(function* () {
const { secrets } = yield* list();
return secrets;
});
}).pipe(Effect.provide(Fly.ListSecretsHttp)),
);

Source: src/Fly/ListSecretsHttp.ts Kind: Layer · Provides: Fly.ListSecrets

HTTP implementation of ListSecrets. Provide it on the Service or Action Effect.

Effect.gen(function* () {
const list = yield* Fly.ListSecrets(Site);
// ...
}).pipe(Effect.provide(Fly.ListSecretsHttp))

Source: src/Fly/Secret.ts

A Fly.Secret is an App vault entry. Fly injects it as an environment variable on every Machine. Use it when the value is shared and managed in one place by Fly.

For a secret only this Service reads from .env at deploy time, yield Config.Redacted instead. Do not pass env: { ... } on a Service.

Most secrets in a Service come from your .env. Yield Config.Redacted in init. Alchemy binds the value onto the Machine.

import * as Config from "effect/Config";
import * as Redacted from "effect/Redacted";
export default class Api extends Fly.Service<Api>()(
"Api",
{ app: Site, main: import.meta.url, port: 3000 },
Effect.gen(function* () {
const apiKey = yield* Config.Redacted("API_KEY");
return {
fetch: Effect.gen(function* () {
const token = Redacted.value(apiKey);
return HttpServerResponse.text("ok");
}),
};
}),
) {}

Wrap the value with Redacted.make so it is never logged. The plaintext is never stored in attributes. Omit name and Alchemy generates an ownership-stamped name.

const dbUrl = yield* Fly.Secret("DatabaseUrl", {
app: Site,
value: Redacted.make("postgres://…"),
});

name is the env-var Machines see. It is stored as-is (case-sensitive).

export const ApiToken = Fly.Secret("ApiToken", {
app: Site,
name: "API_TOKEN",
value: Redacted.make("sk_live_…"),
});

Updating value is in place via updateSecrets.

export const ApiToken = Fly.Secret("ApiToken", {
app: Site,
name: "API_TOKEN",
value: Redacted.make("sk_live_rotated"),
});

GetSecret is bound to one Secret. Provide GetSecretHttp. Fly only returns plaintext from a Machine in the same App. From a deploy-time Action you get metadata (name, digest, timestamps).

const get = yield* Fly.GetSecret(ApiToken);
const got = yield* get();

ListSecrets is bound to an App. From an Action, the org token can list any App in the org. From a Machine, deploy tokens are per-App. Mixing Apps on one Machine shares one FLY_API_TOKEN and is not supported.

const list = yield* Fly.ListSecrets(Site);
const { secrets } = yield* list();

WriteSecret creates, updates, and deletes by name. Provide WriteSecretHttp on the Action or Service Effect.

const Seed = Alchemy.Action(
"Seed",
Effect.gen(function* () {
const secrets = yield* Fly.WriteSecret(ApiToken);
return Effect.fn(function* () {
yield* secrets.update("API_TOKEN", Redacted.make("sk_live_rotated"));
});
}).pipe(Effect.provide(Fly.WriteSecretHttp)),
);

Source: src/Fly/WriteSecret.ts

Create, update, and delete Fly.io App secrets at runtime.

The App is fixed by WriteSecret(secret). Calls take no app_name. Inside a Machine or Service, Alchemy mints an App deploy token. Inside an Action, the ambient FLY_API_TOKEN is used.

Bind the client in init. Provide WriteSecretHttp. Wrap values with Redacted.make.

const Seed = Alchemy.Action(
"Seed",
Effect.gen(function* () {
const secrets = yield* Fly.WriteSecret(ApiToken);
return Effect.fn(function* () {
yield* secrets.create("API_KEY", Redacted.make("sk_live"));
});
}).pipe(Effect.provide(Fly.WriteSecretHttp)),
);

update rotates by name (batch of one).

yield* secrets.update("API_KEY", Redacted.make("sk_live_rotated"));

delete removes a secret by name.

yield* secrets.delete("API_KEY");

Source: src/Fly/WriteSecretHttp.ts Kind: Layer · Provides: Fly.WriteSecret

HTTP implementation of WriteSecret. Provide it on the Service or Action Effect.

Effect.gen(function* () {
const secrets = yield* Fly.WriteSecret(ApiToken);
// ...
}).pipe(Effect.provide(Fly.WriteSecretHttp))