Credentials
Authenticate with fixed secrets or short-lived tokens, and rotate them without restarting.
Credentials are Redacted values, never plain strings. Pass a fixed value for a static password, or an Effect that fetches one when a connection opens.
Using a fixed password
Put the secret in a Redacted and read it from Config, so it never appears in logs.
Example (Reading a Password from the Environment)
import { } from "effect";
import { } from "@effect-kit/redis/node";
export const = .(
.({
: .("REDIS_URL"),
: .({
: .("REDIS_USERNAME"),
: .("REDIS_PASSWORD"),
}),
}),
);username is optional. A password in the URL (rediss://user:pass@host) also works, but giving both a URL password and credentials fails with RedisConfigError. A plain string for credentials is a type error.
Fetching credentials on demand
Pass an Effect that returns the values. It runs for every new connection, and may require services, which become requirements of the layer.
Example (A Token Source That the Layer Requires)
import { , } from "effect";
import type { } from "effect";
import { } from "@effect-kit/redis";
import type { } from "@effect-kit/redis";
import { } from "@effect-kit/redis/node";
class extends .<
,
{ readonly : .<> }
>()("app/TokenSource") {}
const = .(function* () {
const = yield* ;
return yield* .;
});
export const = .({
: "rediss://cache.example.com:6380",
: .(, { : 120_000 }),
});
type Needs = .<typeof >;RedisCredentials.provider(effect, policy) wraps the effect with a cache. The values it returns have password, optional username, and optional expiresAtMillis: an absolute UTC time in epoch milliseconds, as a number.
| Option | Default | Meaning |
|---|---|---|
refreshBeforeMillis | 60000 | How long before expiresAtMillis a new token is fetched |
acquisitionTimeoutMillis | 5000 | How long one fetch can take (1 to 60000) |
maxWaiters | 128 | How many callers can wait for a fetch (1 to 10000) |
An Effect passed directly as credentials is the same as RedisCredentials.provider(effect) with these defaults. If the effect fails or times out, opening the connection fails with HandshakeError where stage is "credentials".
Retiring connections before a token expires
A connection authenticates once, when it opens. credentialLifetime decides when an open connection is closed so the next command reopens it with fresh credentials.
mode | Behavior |
|---|---|
"reconnect" | The default. Connections stay open until something else closes them. |
"token-expiry" | Closes the connection retireBeforeMillis (default 30000) before expiresAtMillis. The credentials must carry expiresAtMillis. |
"max-age" | Closes the connection maxAgeMillis after it opened. |
Example (Closing Connections Before the Token Expires)
import { Effect, Redacted } from "effect";
import { RedisCredentials } from "@effect-kit/redis";
import { RedisNode } from "@effect-kit/redis/node";
const fetchToken = Effect.sync(() => ({
username: Redacted.make("app"),
password: Redacted.make("short-lived-token"),
expiresAtMillis: Date.now() + 15 * 60 * 1000,
}));
export const RedisLive = RedisNode.layer({
url: "rediss://cache.example.com:6380",
credentials: RedisCredentials.provider(fetchToken),
credentialLifetime: { mode: "token-expiry", retireBeforeMillis: 30_000 },
});The package does not insert AUTH into an open connection. It closes the connection and lets the next one authenticate. A cached token is also refreshed at least retireBeforeMillis early in this mode.
Rotating on demand
RedisCredentials is a service the runtime layer provides. Its rotate effect discards the cached credentials and closes the open connections. New connections fetch fresh credentials.
Example (Rotating After a Secret Changes)
import { } from "effect";
import { } from "@effect-kit/redis";
export const = .(function* () {
const = yield* ;
yield* .;
});On Upstash, rotate aborts in-flight HTTP requests instead of closing connections. rotate fails with CredentialError, whose reason is one of configuration, acquisition, expired, timeout, capacity, closed, rotated or retirement.
Credential errors never include the secret or the provider response.