Area
RESP2 (Redis-compatible)
Problem or motivation
Tellstone's RESP listener accepts connections from any client with no authentication. Anyone who can reach port 6379 can read, write, and delete data. This makes it impossible to expose Tellstone beyond a trusted network boundary. Redis solves this with the AUTH command — users expect the same from a Redis-compatible system
Proposed solution
Implement the AUTH command following Redis AUTH semantics:
AUTH <password> # single-password mode (Redis < 6.0 compatible)
AUTH <username> <password> # ACL mode (Redis 6.0+, future-proofs for ACL work)
Wire protocol: RESP2 simple string reply +OK\r\n on success, or RESP2 error -ERR invalid password\r\n on failure. This matches the Redis AUTH return specification.
Configuration:
--require-pass / TSD_REQUIRE_PASS — server password (plaintext flag, hashed at startup)
- When unset, AUTH is a no-op and all connections are immediately authenticated (zero overhead)
Implementation — touch points in existing code:
-
internal/resp/server.go — connState struct (line 39): Add authenticated bool field. New connections start with authenticated: false when requirePass is set, or authenticated: true when it's empty.
-
internal/resp/server.go — dispatch method (line 195): Add a new case branch before the existing command dispatch:
case EqualFold(cmd, "AUTH"):
// Validate against requirePass, update st.authenticated
When requirePass is empty, respond +OK\r\n immediately (no-op, backward-compatible).
-
internal/resp/server.go — dispatch guard (line 195): After the AUTH case, add an early return if !st.authenticated:
if !st.authenticated {
return AppendError(out, "NOAUTH Authentication required")
}
This matches Redis's error message for unauthenticated connections.
-
internal/resp/server.go — Server struct (line 46): Add requirePass string field, set from config in NewServer.
-
Password hashing: Hash with bcrypt.GenerateFromPassword at startup (stored as []byte in Server). Per-connection comparison via bcrypt.CompareHashAndPassword. This is called only on AUTH command, never on the hot path — zero overhead for authenticated connections.
-
Logging: Failed AUTH attempts logged at LevelWarn with remote IP (same pattern as the protocol error log at line 156).
-
config/config.go: Add RequirePass string field with --require-pass / TSD_REQUIRE_PASS flag/env.
Commands allowed before AUTH: Per Redis behavior, only AUTH, PING, and QUIT should be accepted on unauthenticated connections. Existing dispatch cases for GET/SET/DEL return NOAUTH error.
Zero-overhead guarantee: When --require-pass is not set, requirePass is empty string, st.authenticated starts as true, and the AUTH case returns +OK in O(1). No bcrypt calls, no state checks beyond the existing switch.
Alternatives considered
No response
Additional context
References:
Area
RESP2 (Redis-compatible)
Problem or motivation
Tellstone's RESP listener accepts connections from any client with no authentication. Anyone who can reach port 6379 can read, write, and delete data. This makes it impossible to expose Tellstone beyond a trusted network boundary. Redis solves this with the AUTH command — users expect the same from a Redis-compatible system
Proposed solution
Implement the
AUTHcommand following Redis AUTH semantics:Wire protocol: RESP2 simple string reply
+OK\r\non success, or RESP2 error-ERR invalid password\r\non failure. This matches the Redis AUTH return specification.Configuration:
--require-pass/TSD_REQUIRE_PASS— server password (plaintext flag, hashed at startup)Implementation — touch points in existing code:
internal/resp/server.go—connStatestruct (line 39): Addauthenticated boolfield. New connections start withauthenticated: falsewhenrequirePassis set, orauthenticated: truewhen it's empty.internal/resp/server.go—dispatchmethod (line 195): Add a newcasebranch before the existing command dispatch:When
requirePassis empty, respond+OK\r\nimmediately (no-op, backward-compatible).internal/resp/server.go—dispatchguard (line 195): After the AUTH case, add an early return if!st.authenticated:This matches Redis's error message for unauthenticated connections.
internal/resp/server.go—Serverstruct (line 46): AddrequirePass stringfield, set from config inNewServer.Password hashing: Hash with
bcrypt.GenerateFromPasswordat startup (stored as[]byteinServer). Per-connection comparison viabcrypt.CompareHashAndPassword. This is called only onAUTHcommand, never on the hot path — zero overhead for authenticated connections.Logging: Failed AUTH attempts logged at
LevelWarnwith remote IP (same pattern as the protocol error log at line 156).config/config.go: AddRequirePass stringfield with--require-pass/TSD_REQUIRE_PASSflag/env.Commands allowed before AUTH: Per Redis behavior, only
AUTH,PING, andQUITshould be accepted on unauthenticated connections. Existingdispatchcases for GET/SET/DEL returnNOAUTHerror.Zero-overhead guarantee: When
--require-passis not set,requirePassis empty string,st.authenticatedstarts astrue, and the AUTH case returns+OKin O(1). No bcrypt calls, no state checks beyond the existing switch.Alternatives considered
No response
Additional context
References:
internal/resp/server.go:195internal/resp/server.go:39internal/shard/runner.go:27-31