Client

:= {
    config : Config ?? Config.default,
    auth : Optional(Credentials) ?? Absent,
    select_db : Optional(Positive) ?? Absent,
}

A reusable client: validated Config plus optional session policy. Every field has a default — config falls back to Config.default — so construct with .{ } and set only what you need; there is no build step:

client = Client.{} custom = Client.{ config: Config.{ read_size: 32_768 }, auth: Present(creds), select_db: Present(3) }

Database 0 is the default, expressed by leaving select_db unset; a positive index excludes 0, so select_db: Present(0) is a compile-time error by design.

attach pure — bind a live transport into a Connection (no I/O). handshake! effect — run the session setup (AUTH, SELECT) a fresh socket needs; run once per socket. connect! effect — attach then handshake!, the batteries-included path.

A pool runs handshake! once when it dials a fresh socket and only attach on each subsequent reuse. The client owns Redis session identity; the platform owns the socket, its lifecycle, deadlines, and TLS.

attach : Client, ByteIo(read_err, write_err) -> Connection(read_err, write_err)

Bind a live transport into a Connection. Pure: no I/O and no handshake. Use for a reused socket whose session is already established, or when you will run handshake! yourself.

handshake! : Client, Connection(read_err, write_err) => Try({  }, HandshakeError(read_err, write_err))

Run the session handshake (AUTH then SELECT, as configured) over an already-attached connection. Run once per fresh socket. On any error the caller MUST discard the socket: Rejected means a clean frame but an unusable session (usually permanent, e.g. a bad password); ExchangeFailed means the transport failed mid-handshake and framing is ambiguous.

connect! : Client, ByteIo(read_err, write_err) => Try(Connection(read_err, write_err), HandshakeError(read_err, write_err))

attach then handshake!. The one obvious path for a single, fresh connection; pooling code drops to attach/handshake! directly.

disposition : HandshakeError(read_err, write_err) -> Disposition

A failed handshake always requires discarding the socket, whatever the cause: the session was never fully established.