# Decision: Redis configuration shape PG_PLAN.md §7. Captures the standing rules adopted by Auth/Session Service when it joined the project-wide Redis topology defined in `ARCHITECTURE.md §Persistence Backends`. ## Context Auth/Session Service intentionally stays Redis-only. All authsession state is TTL-bounded and recoverable from a fresh login flow: - challenge records expire with the login window; - device-session records expire with their session TTL; - gateway projection cache keys are write-through reflections of the source-of-truth session record; - the gateway-session-events stream is consumed lazily by the gateway and trimmed by `MAXLEN ~`; - the resend-throttle protector is purely TTL-driven. Stage 7 brought authsession in line with the steady-state rules established in Stage 0: every Galaxy service uses one master plus zero-or-more replicas with a mandatory password, no TLS, and no Redis ACL username; the connection is configured by the shared `pkg/redisconn` helper. ## Decisions ### One shared `*redis.Client` owned by the runtime `internal/app/runtime.go` constructs a single `*redis.Client` via `internal/adapters/redis.NewClient`, attaches OpenTelemetry tracing and metrics via `internal/adapters/redis.InstrumentClient`, performs one bounded `PING` via `internal/adapters/redis.Ping`, and registers `client.Close` for shutdown. The challenge store, session store, config provider, projection publisher and resend-throttle protector all receive this same client. Adapters no longer build or own a Redis client. Their `Config` structs hold only namespace and per-adapter timeout settings (no Addr/Username/Password/ DB/TLSEnabled). Adapter constructors take `(*redis.Client, Config)`. ### One env-var prefix per service Connection topology is loaded from a single `AUTHSESSION_REDIS_*` group via `redisconn.LoadFromEnv("AUTHSESSION")`: - `AUTHSESSION_REDIS_MASTER_ADDR` (required) - `AUTHSESSION_REDIS_REPLICA_ADDRS` (optional, comma-separated; currently unused, reserved for future read-routing) - `AUTHSESSION_REDIS_PASSWORD` (required) - `AUTHSESSION_REDIS_DB` (default `0`) - `AUTHSESSION_REDIS_OPERATION_TIMEOUT` (default `250ms`) The per-adapter namespace and stream env vars (`*_KEY_PREFIX`, `*_STREAM`, `*_STREAM_MAX_LEN`) keep their existing names and semantics — they describe key shape, not connection topology. ### Retired env vars (hard removal) - `AUTHSESSION_REDIS_ADDR` — replaced by `AUTHSESSION_REDIS_MASTER_ADDR`. - `AUTHSESSION_REDIS_USERNAME` — Redis ACL not used. - `AUTHSESSION_REDIS_TLS_ENABLED` — TLS disabled by policy. - `AUTHSESSION_REDIS_OPERATION_TIMEOUT` keeps its name (it now lives in `redisconn.Config`). `pkg/redisconn.LoadFromEnv` rejects `AUTHSESSION_REDIS_TLS_ENABLED` and `AUTHSESSION_REDIS_USERNAME` at startup with a clear error pointing to `ARCHITECTURE.md §Persistence Backends`. There is no backward-compatibility shim; this is consistent with the project-wide rule that the migration window has no production deploys to preserve. ### Telemetry `redisconn.Instrument` wires `redisotel.InstrumentTracing` (with `WithDBStatement(false)`) and `redisotel.InstrumentMetrics`. This is the first authsession release that emits Redis tracing and connection-pool metrics; downstream dashboards will start populating without further changes. ## Consequences - Test code that previously constructed a Redis client per adapter must now construct one client and pass it to every adapter under test (see the pattern in `internal/adapters/redis//store_test.go`). - Operators must set `AUTHSESSION_REDIS_PASSWORD`. A passwordless local Redis is still acceptable as long as a placeholder password is supplied to the binary; Redis without `requirepass` accepts AUTH unconditionally. - The integration test harness passes `AUTHSESSION_REDIS_PASSWORD = "integration"` alongside `AUTHSESSION_REDIS_MASTER_ADDR` (see `integration/internal/harness/authsessionservice.go`).