# redis/redis — codebase unpack

> Canonical page: https://codevetter.com/unpack/redis

This is a deterministic Repo Unpack scan of redis/redis — a structural map, not a review, security audit, or maintainer-endorsed description.

- Scanned commit: c3215f019a4c11c35d438f241114e94730fe39e5 (2026-09-22)
- Last upstream commit: CI remove unused redis_docs_sync workflow (#15816)
- Generated by codevetter 1.12.0 on 2026-09-22
- 1866 files scanned · 20.8 MB

## Languages

- C: 788 files
- JSON: 465 files
- Shell: 83 files
- Python: 48 files
- Markdown: 27 files
- YAML: 22 files
- Lua: 20 files
- Ruby: 9 files

## Repository structure

- deps/ — 707 files
- src/ — 693 files
- tests/ — 306 files
- modules/ — 66 files
- utils/ — 40 files
- .github/ — 16 files
- scripts/ — 13 files
- tools/ — 1 files

Probable source entrypoint: `src/` (inferred from the tree, not upstream docs)

## Structural health signals

Average score 7.9/10 across 23 files; 3 hotspots. These are deterministic review leads, not proof of a bug.

## Recent history

- c3215f019a4c (2026-09-22) CI remove unused redis_docs_sync workflow (#15816)
- 0d6266f2deb9 (2026-09-21) Fix XTRIM leaving a 0-live stream node that RDB cannot load (#15815)
- 62d1a5b930ed (2026-09-20) Make ASM tests work with --single (#15636)
- 5f08991bce47 (2026-09-20) Fix argument release race in the AOF duration test module (#15805)
- c00f920b2a3d (2026-09-19) redis-cli: use CLIENT NO-TOUCH for key size analysis (#15817)

## Overview

This is the Redis Open Source server (`src/version.h` reports 8.9.241, branch `unstable`) — a single-process, event-loop-driven in-memory data structure server written in C99/gnu11, plus its CLI, benchmark, sentinel, and RDB/AOF checking tools. Beyond upstream Redis it carries a substantial in-tree feature layer: a new `OBJ_ARRAY` sparse-array type (`src/t_array.c`, `src/sparsearray.c`), key eviction protection (`src/bless.c`), CuckooHeavyKeeper hot-key tracking (`src/chk.c`, `src/hotkeys.c`), atomic cluster slot migration (`src/cluster_asm.c`), zstd replication/client compression (`src/client_comp.c`), and multi-threaded IO (`src/iothread.c`). It also ships a bespoke top-level Makefile + `scripts/` orchestration layer that clones, builds, deploys, and tests four external Redis modules (RediSearch, RedisJSON, RedisBloom, RedisTimeSeries) pinned by `modules/modules.yaml`, which upstream Redis does not have.

## System map

The runtime is one `redis-server` binary whose `main()` in `src/server.c` initializes config, ACL, the module system and connection types, then hands control to the `ae` event loop (`aeMain`). The same binary multiplexes into sentinel, `redis-check-rdb` and `redis-check-aof` modes by argv[0] inspection. Storage is entirely in-process (`kvstore`/`dict`/`listpack`/`quicklist`/`rax`/`ebuckets`), persisted by RDB snapshot and/or AOF manifest, and replicated over a custom PSYNC protocol. Build is GNU Make: `src/Makefile` compiles the core and vendored `deps/`, while the top-level `Makefile` delegates to `scripts/*.sh` for the module-bundle workflow. Deployment shape is a bare binary + `redis.conf` (or the generated `redis-full.conf`), with an Ubuntu 24.04 build image in `docker/Dockerfile.noble`.
- `main()` in src/server.c is the single entrypoint for redis-server, redis-sentinel, redis-check-rdb and redis-check-aof: it calls `checkForSentinelMode()` on argv, then dispatches to `redis_check_rdb_main()` / `redis_check_aof_main()` when `strstr(exec_name, "redis-check-rdb"/"redis-check-aof")` matches, before any config parsing. — [src/server.c#L8251-8365](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c#L8251-L8365)
- Startup order in `main()` is fixed and dependency-driven: `initServerConfig()` → `ACLInit()` (commented 'must be initialized ASAP because the basic networking code and client creation depends on it') → `moduleInitModulesSystem()` → `connTypeInitialize()` → `keyMetaInit()`. — [src/server.c#L8330-8340](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c#L8330-L8340)
- The event loop is created in `initServer()` via `aeCreateEventLoop(server.maxclients+CONFIG_FDSET_INCR)`, and per-DB storage is allocated as three parallel structures — `kvstoreCreate()` for `db->keys`, `db->expires`, plus `blessedKvstoreCreate()` for `db->blessed_keys` and `estoreCreate()` for `db->subexpires`. — [src/server.c#L3105-3130](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c#L3105-L3130)
- `main()` ends with `aeMain(server.el)` after `redisSetCpuAffinity(server.server_cpulist)` and `setOOMScoreAdj(-1)`; everything after that point is event-loop driven. — [src/server.c#L8600-8614](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c#L8600-L8614)
- The core links 130+ objects listed in `REDIS_SERVER_OBJ` in src/Makefile, including the non-upstream modules `bless.o t_array.o sparsearray.o chk.o hotkeys.o gcra.o vector.o fwtree.o estore.o client_comp.o cluster_asm.o iothread.o keymeta.o`. — [src/Makefile#L404](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/Makefile#L404)
- Vendored third-party code lives in `deps/` and is built as separate targets: `DEPENDENCY_TARGETS=hiredis linenoise lua hdr_histogram fpconv xxhash tre` — note `tre` (regex) is a dependency of the new array type, which includes `../deps/tre/local_includes/tre.h`. — [src/Makefile#L39](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/Makefile#L39) [deps/](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/deps/) [src/t_array.c#L15-17](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/t_array.c#L15-L17)
- The top-level Makefile is not upstream's: it includes `modules/manifest.mk`, defines a `GOALS_WITH_ARGS` positional-argument capture mechanism, and routes `build`/`clean`/`test`/`deploy`/`bootstrap`/`run`/`tarball` to `scripts/*.sh` rather than recursing into `src/`. — [Makefile#L13-45](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/Makefile#L13-L45) [Makefile#L100-160](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/Makefile#L100-L160)
- `modules/modules.yaml` is the single source of truth pinning four external modules to immutable refs: redisbloom v8.11.81, redisearch v8.10.1 (with `build_env: LTO=1 REDISEARCH_GENERATE_HEADERS=0 INLINE_LSE_ATOMICS=0`), redisjson v8.10.0, redistimeseries v8.10.0. — [modules/modules.yaml](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/modules/modules.yaml) [modules/MODULES.md#L18-40](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/modules/MODULES.md#L18-L40)
- Test entrypoints are four shell wrappers that locate tclsh 8.5–9.0 and invoke `tests/test_helper.tcl`: `./runtest`, `./runtest-moduleapi` (which first runs `$MAKE -C tests/modules` then names 48 `--single unit/moduleapi/*` suites), `./runtest-cluster`, `./runtest-sentinel`. — [runtest](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/runtest) [runtest-moduleapi](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/runtest-moduleapi) [src/Makefile#L529-541](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/Makefile#L529-L541)
- `make -C src test` depends on building redis-server, redis-check-aof, redis-cli, redis-benchmark and `module_tests` before shelling out to `(cd ..; ./runtest)`; `test-modules`, `test-sentinel`, `test-cluster` are separate targets. — [src/Makefile#L529-541](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/Makefile#L529-L541)
- External integrations are network protocols rather than SDKs: the cluster bus (`src/cluster_legacy.c`), the PSYNC replication protocol (`src/replication.c`), TLS via OpenSSL gated on `BUILD_TLS=yes|module` (`src/tls.c`), and optional zstd compression gated on `-DUSE_COMPRESSION=1` detected via pkg-config. — [src/Makefile#L157-169](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/Makefile#L157-L169) [src/Makefile#L343-357](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/Makefile#L343-L357) [src/tls.c](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/tls.c) [src/client_comp.c#L1-6](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/client_comp.c#L1-L6)
- The deployment artifact set is defined by `scripts/deploy.sh` / `scripts/uninstall.sh`: core binaries and symlinks into `$(PREFIX)/bin` (default `/usr/local`) and module `.so` files into `$(PREFIX)/lib/redis/modules`, with `loadmodule` paths rewritten into a generated `redis-full.conf`. — [Makefile#L117-140](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/Makefile#L117-L140) [scripts/deploy.sh](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/scripts/deploy.sh) [scripts/sync-redis-conf.sh](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/scripts/sync-redis-conf.sh)
- `docker/Dockerfile.noble` is the only container definition — an Ubuntu 24.04 multi-arch build environment (amd64+arm64) that installs Redis prereqs and then runs `modules/docker-install-bundled-module-deps.sh` to execute each bundled module's `.install/install_script.sh`. It is a build image, not a runtime image. — [docker/Dockerfile.noble#L1-55](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/docker/Dockerfile.noble#L1-L55)
- Memory allocator selection is a build-time runtime boundary: `MALLOC` defaults to `jemalloc` on Linux and `libc` elsewhere, and is forcibly downgraded to `libc` whenever `SANITIZER` is set. — [src/Makefile#L81-120](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/Makefile#L81-L120)

## Feature catalog

User-facing surface is the RESP command set — 464 JSON command specs under `src/commands/` compiled into the 13k-line `src/commands.def`. Alongside the classic types (string/list/set/zset/hash/stream/HLL/geo/bitmap) this tree adds a first-class array type (AR* commands), key-blessing (BLESS), hot-key tracking (HOTKEYS), INCREX, hash-field expiration (HEXPIRE family), and a compiled-out GCRA rate limiter. Administrative surface spans ACL, CLIENT, CLUSTER (including atomic slot migration), CONFIG, FUNCTION, DEBUG, and the Sentinel command set. Client-side tooling ships as `redis-cli` (with a full cluster manager and ~60 flags), `redis-benchmark`, and the two check tools.
- 464 command specification JSON files under `src/commands/` are the declarative source of the command table; `utils/generate-command-code.py` compiles them into `src/commands.def` (13,236 lines) via the `$(COMMANDS_DEF_FILENAME).def` Make rule. — [src/commands/](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/commands/) [utils/generate-command-code.py#L1-40](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/utils/generate-command-code.py#L1-L40) [src/Makefile#L503-504](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/Makefile#L503-L504)
- A new `OBJ_ARRAY` (type 7) sparse-array data type is exposed via 18 commands — ARSET, ARGET, ARDEL, ARDELRANGE, ARGETRANGE, ARGREP, ARINFO, ARINSERT, ARLASTITEMS, ARLEN, ARMGET, ARMSET, ARNEXT, AROP, ARRING, ARSCAN, ARSEEK, ARCOUNT — implemented in `src/t_array.c` over `src/sparsearray.c`. — [src/server.h#L900-925](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.h#L900-L925) [src/commands/arset.json](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/commands/arset.json) [src/t_array.c#L1-20](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/t_array.c#L1-L20) [tests/unit/type/array.tcl#L1-20](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/tests/unit/type/array.tcl#L1-L20)
- BLESS marks keys as never-evictable: the NO-EVICT bit lives in the kvBits byte after each kvobj, persists through RDB and AOF, and is exposed as BLESS SET/GET/CLEAR/SCAN plus an `blessed_keys` INFO counter backed by a per-DB `db->blessed_keys` index. — [src/bless.c#L1-25](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/bless.c#L1-L25) [src/commands/bless-set.json](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/commands/bless-set.json) [src/commands/bless-scan.json](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/commands/bless-scan.json) [tests/unit/bless.tcl](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/tests/unit/bless.tcl)
- HOTKEYS START/STOP/GET/RESET/HELP expose a CuckooHeavyKeeper top-K sketch (`src/chk.c`, based on the Ngo & Papatriantafilou paper plus a min-heap borrowed from RedisBloom's TopK) that can be scoped to specific cluster slot ranges via a `slotRangeArray`. — [src/chk.c#L1-16](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/chk.c#L1-L16) [src/hotkeys.c#L1-25](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/hotkeys.c#L1-L25) [src/commands/hotkeys-start.json](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/commands/hotkeys-start.json) [tests/unit/hotkeys.tcl](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/tests/unit/hotkeys.tcl)
- INCREX is a new string command returning a two-element reply (`{value, applied}`) that creates a missing key at 0 before incrementing, with an optional increment argument. — [tests/unit/type/increx.tcl#L1-15](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/tests/unit/type/increx.tcl#L1-L15) [src/commands/](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/commands/)
- Hash-field expiration ships as eight commands — HEXPIRE, HEXPIREAT, HPEXPIRE, HPEXPIREAT, HEXPIRETIME, HPEXPIRETIME, HGETEX, HGETDEL — backed by the `ebuckets` radix-of-buckets expiry structure and the per-DB `estore` subexpires store. — [src/commands/hexpire.json](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/commands/hexpire.json) [src/commands/hgetex.json](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/commands/hgetex.json) [src/ebuckets.c](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/ebuckets.c) [src/estore.c#L1-25](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/estore.c#L1-L25) [tests/unit/type/hash-field-expire.tcl](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/tests/unit/type/hash-field-expire.tcl)
- A GCRA (Generic Cell Rate Algorithm) leaky-bucket rate limiter exists as `src/gcra.c` with an `OBJ_GCRA` object type and a 4-arg `GCRA key max_burst tokens_per_period period` command, ported from redis-cell — but it is compiled out by default behind `#ifdef ENABLE_GCRA`. — [src/gcra.c#L1-22](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/gcra.c#L1-L22) [src/server.h#L917-928](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.h#L917-L928) [tests/unit/gcra.tcl#L1-30](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/tests/unit/gcra.tcl#L1-L30)
- Atomic slot migration is a user-facing cluster feature driven by `CLUSTER MIGRATION IMPORT <slots>` run on the destination node, handled by `clusterMigrationCommand()`, with an eight-step protocol documented in the file header. — [src/cluster_asm.c#L18-45](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/cluster_asm.c#L18-L45) [src/cluster_asm.c#L907-1051](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/cluster_asm.c#L907-L1051) [tests/unit/cluster/atomic-slot-migration.tcl](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/tests/unit/cluster/atomic-slot-migration.tcl)
- Lua scripting is offered in two forms: ephemeral EVAL/EVALSHA/SCRIPT via `evalGenericCommand()` and `luaCreateFunction()` in `src/eval.c`, and persistent FUNCTION libraries via a pluggable engine registry in `src/functions.c` whose only registered engine is `LUA_ENGINE_NAME` from `src/function_lua.c`. — [src/eval.c#L436-720](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/eval.c#L436-L720) [src/functions.c#L407-461](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/functions.c#L407-L461) [src/function_lua.c#L512](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/function_lua.c#L512)
- `redis-cli` is a large multi-mode tool (11,561 lines) exposing analysis modes `--bigkeys --memkeys --hotkeys --keystats-samples --scan --latency --latency-dist --latency-history --intrinsic-latency --lru-test --rdb --functions-rdb --pipe --replica --eval --ldb`, RESP3/JSON output modes, and TLS flags (`--tls --cert --key --cacert --cacertdir --insecure`). — [src/redis-cli.c](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/redis-cli.c) [src/redis-cli.c#L176-270](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/redis-cli.c#L176-L270)
- `redis-cli --cluster` embeds a full cluster manager dispatch table of `clusterManagerCommandProc` functions — create, add-node, del-node, info, check, fix, reshard, rebalance — with ~25 `--cluster-*` option flags. — [src/redis-cli.c#L3943-4000](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/redis-cli.c#L3943-L4000)
- Sentinel is a distinct operating mode of the same binary: `initSentinelConfig()` + `initSentinel()` run before config parsing so the config file populates monitored-master state, and `sentinelIsRunning()` replaces the normal ready-to-serve path in `main()`. — [src/server.c#L8342-8352](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c#L8342-L8352) [src/server.c#L8594-8600](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c#L8594-L8600) [src/sentinel.c](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/sentinel.c) [sentinel.conf](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/sentinel.conf)
- Multi-threaded IO is a runtime feature with its own event loops per thread: `initThreadedIO()` is called from `InitServerLast()`, and `readQueryFromClient()` branches on `server.io_threads_num > 1` and atomically tracks `c->pending_read` and per-thread `IOThreads[c->running_tid].io_reads_processed`. — [src/server.c#L3365-3370](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c#L3365-L3370) [src/iothread.c#L991](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/iothread.c#L991) [src/networking.c#L3981-4006](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/networking.c#L3981-L4006)
- Client/replication payload compression is a user-visible option: `clientCompressionProcessPendingData()` runs in `beforeSleep()`, clients carry a `compression_state` and `CLIENT_IO_COMPRESSION_ENABLED` io_flag, and `setReplCompression(server.repl_compression)` is forced off at init when the build lacks zstd. — [src/client_comp.c#L1-25](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/client_comp.c#L1-L25) [src/server.c#L1990-1998](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c#L1990-L1998) [src/server.c#L3090-3094](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c#L3090-L3094)
- `modules/vector-sets/` is an in-tree module (deliberately absent from modules.yaml) implementing HNSW vector search with its own `commands.json`, `hnsw.c`, an expression filter language in `expr.c`, and a word2vec helper `w2v.c`. — [modules/vector-sets/](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/modules/vector-sets/) [modules/modules.yaml#L48-50](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/modules/modules.yaml#L48-L50) [modules/MODULES.md#L38-44](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/modules/MODULES.md#L38-L44)

## Data flow

Input arrives on a connection abstraction (`connection.h` over socket/unix/TLS), is read into a per-client or thread-reusable query buffer by `readQueryFromClient()`, parsed into `pendingCommand` structs by `processInlineBuffer()`/`processMultibulkBuffer()`, dispatched by `processCommand()` and executed by `call()`. Command handlers mutate the authoritative state owner — `server.db[j]` kvstores — and any side effects are queued through `alsoPropagate()`. At the end of the execution unit `propagateNow()` fans out to exactly three sinks: the AOF buffer, the replica stream, and the atomic-slot-migration client. Replies accumulate in per-client output buffers and are flushed in `beforeSleep()` by `handleClientsWithPendingWrites()`, strictly after `flushAppendOnlyFile()` so `appendfsync=always` is honoured before acknowledgement.
- The input boundary is `readQueryFromClient(connection *conn)`, which recovers the client via `connGetPrivateData(conn)` and reads `PROTO_IOBUF_LEN` bytes, except when a multibulk argument exceeds `PROTO_MBULK_BIG_ARG`, in which case it sizes the read to exactly the remaining bulk length to enable zero-copy object creation. — [src/networking.c#L3981-4032](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/networking.c#L3981-L4032)
- Query buffers are pooled per IO thread: a client with no private `querybuf` borrows `thread_reusable_qb` and sets `CLIENT_IO_REUSABLE_QUERYBUFFER`; if `thread_reusable_qb_used` is already set (nested execution via `processEventsWhileBlocked()`) the client allocates a private buffer instead. — [src/networking.c#L4032-4052](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/networking.c#L4032-L4052)
- `processInputBuffer()` parses ahead up to `server.lookahead` commands into `c->pending_cmds`, but clamps the lookahead to 1 for unauthenticated clients because AUTH changes the handling of succeeding commands. — [src/networking.c#L3778-3800](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/networking.c#L3778-L3800) [src/networking.c#L3820-3860](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/networking.c#L3820-L3860)
- Request type is inferred from the first byte: `'*'` selects `PROTO_REQ_MULTIBULK` → `processMultibulkBuffer()`, anything else selects `PROTO_REQ_INLINE` → `processInlineBuffer()`; an unknown `reqtype` triggers `serverPanic("Unknown request type")`. — [src/networking.c#L3828-3862](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/networking.c#L3828-L3862)
- `processCommand()` resolves argv to a `struct redisCommand` through a three-tier lookup: an already-set `c->lookedcmd`, then `isCommandReusable(c->lastcmd, ...)` reusing the previous command, then a full `lookupCommand(c->argv, c->argc)`. — [src/server.c#L4694-4712](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c#L4694-L4712) [src/server.c#L3675-3745](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c#L3675-L3745)
- The command table itself is populated once at startup by `populateCommandTable()` walking the generated `extern struct redisCommand redisCommandTable[]` from commands.def, so adding a command is a data change in `src/commands/*.json` followed by codegen. — [src/server.c#L3556-3570](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c#L3556-L3570) [src/commands.def](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/commands.def) [src/Makefile#L503-504](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/Makefile#L503-L504)
- Module command filters get first look at every non-reprocessed command: `processCommand()` calls `moduleCallCommandFilters(c)` and `reqresAppendRequest(c)` before command lookup, meaning a module can rewrite argv before the core ever resolves the command name. — [src/server.c#L4671-4678](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c#L4671-L4678) [src/module.c#L11960-11970](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/module.c#L11960-L11970)
- `call()` records `dirty = server.dirty` and `old_master_repl_offset = server.master_repl_offset` before invoking the proc, and clears `CLIENT_FORCE_AOF|CLIENT_FORCE_REPL|CLIENT_PREVENT_PROP` so the handler must opt back into propagation. — [src/server.c#L4151-4185](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c#L4151-L4185)
- `server.allowed_propagate_targets` is a global mask narrowed for the duration of each `call()` so that nested `RM_Call`/Lua `redis.set_repl()` restrictions from an outer call cannot be escaped by an inner one — this is what makes effect-command rewriting (SPOP→SREM) honour selective replication. — [src/server.c#L4218-4230](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c#L4218-L4230) [src/server.c#L3070](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c#L3070)
- `propagateNow()` is the single fan-out point to the three output sinks: `feedAppendOnlyFile()` when `server.aof_state != AOF_OFF && target & PROPAGATE_AOF`, then `replicationFeedSlaves(server.slaves, ...)` and `asmFeedMigrationClient(argv, argc)` when `target & PROPAGATE_REPL`. — [src/server.c#L3796-3824](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c#L3796-L3824) [src/aof.c#L1666](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/aof.c#L1666) [src/replication.c#L651](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/replication.c#L651)
- `propagateNow()` asserts that propagation never happens while replica actions are paused — `serverAssert(!(isPausedActions(PAUSE_ACTION_REPLICA) && !server.client_pause_in_transaction))` — because a dataset change during a replica pause could be lost on failover. — [src/server.c#L3813-3817](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c#L3813-L3817)
- Output ordering in `beforeSleep()` is load-bearing and commented as such: `connTypeProcessPendingData()` and `clientCompressionProcessPendingData()` must run before `flushAppendOnlyFile(0)`, which must run before `handleClientsWithPendingWrites()` and `sendPendingClientsToIOThreads()` so `appendfsync=always` is satisfied before the reply is sent. — [src/server.c#L1988-2000](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c#L1988-L2000) [src/server.c#L2060-2072](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c#L2060-L2072) [src/networking.c#L3046](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/networking.c#L3046)
- Blocked-client resumption is sequenced before AOF flushing: `blockedBeforeSleep()` is called with the comment 'must be done before flushAppendOnlyFile, in case of appendfsync=always, since the unblocked clients may write data'. — [src/server.c#L2008-2013](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c#L2008-L2013) [src/blocked.c](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/blocked.c)
- Client-side caching invalidation is a distinct output boundary flushed per-iteration: `beforeSleep()` asserts `server.tracking_pending_keys` and `server.pending_push_messages` are empty, then calls `trackingBroadcastInvalidationMessages(NULL)` for BCAST-mode clients. — [src/server.c#L2048-2058](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c#L2048-L2058) [src/tracking.c](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/tracking.c)
- On startup the persistence-to-memory direction is owned by `loadDataFromDisk()`, which honours a `preload-file` config prefixed `aof:/` or `rdb:/` (dispatching to `loadPreLoadAOFFile`/`loadPreLoadManifestFile`/`rdbLoad`), else falls back to `loadAppendOnlyFiles(server.aof_manifest)` when AOF is on, else `rdbLoad(server.rdb_filename, ...)`. — [src/server.c#L7903-7975](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c#L7903-L7975)

## Behavior traces

Three flows dominate. Startup: `main()` → config → subsystem init → `initServer()` → `InitServerLast()` → `loadDataFromDisk()` → AOF reconciliation → `aeMain()`. Command execution: socket readable → `readQueryFromClient()` → `processInputBuffer()` → `processCommandAndResetClient()` → `processCommand()` → `call()` → handler → `alsoPropagate()` → `propagatePendingCommands()` → `propagateNow()`. Replica bootstrap: `replicaofCommand()` → `replicationSetMaster()` → `connectWithMaster()` → `syncWithMaster()` handshake → `slaveTryPartialResynchronization()` → full sync via `readSyncBulkPayload()`, with the master side running `syncCommand()` → `startBgsaveForReplication()` → `rdbSaveToSlavesSockets()`.
- STARTUP step 1 — `main()` seeds entropy from `time(NULL)^getpid()^tv.tv_usec` into `srand`/`srandom`/`init_genrand64`, calls `crc64_init()`, captures `umask(server.umask = umask(0777))` early to avoid a thread race, and seeds the dict hash with 16 bytes from `getRandomBytes()` — this is what makes hash-flooding attacks impractical. — [src/server.c#L8313-8330](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c#L8313-L8330)
- STARTUP step 2 — argv parsing in `main()` conceptually appends every `--opt value` pair onto the config file text as newline-separated directives, with documented precedence 'File, stdin, explicit options -- last config is the one that matters', plus special-cases for bare `--save` and `--sentinel`. — [src/server.c#L8390-8460](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c#L8390-L8460)
- STARTUP step 3 — `initServer()` installs `SIG_IGN` for SIGHUP/SIGPIPE, runs `setupSignalHandlers()`, `ThreadsManager_init()`, `makeThreadKillable()`, then `hashTemplatesInit()`, `createSharedObjects()`, `adjustOpenFilesLimit()` and `monotonicInit(monotonicLogCallback)` before creating the event loop. — [src/server.c#L3024-3110](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c#L3024-L3110)
- STARTUP step 4 — `initServer()` decides memory accounting once and irreversibly: `server.memory_tracking_enabled = server.key_memory_histograms || clusterSlotStatsEnabled(CLUSTER_SLOT_STATS_MEM)`, documented as disable-able at runtime but never re-enable-able to avoid a full slot re-scan. — [src/server.c#L3084-3090](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c#L3084-L3090)
- STARTUP step 5 — `InitServerLast()` starts the background subsystems last: `bioInit()`, `initThreadedIO()`, `set_jemalloc_bg_thread()`, and records `server.initial_memory_usage = zmalloc_used_memory()` as the baseline for memory reporting. — [src/server.c#L3365-3370](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c#L3365-L3370)
- STARTUP step 6 — the load sequence in `main()` is `aofLoadManifestFromDisk()` → `loadDataFromDisk()` → `aofSetupAfterPreloadFile()` → `aofOpenIfNeededOnServerStart()` → `aofDelHistoryFiles()` → `applyAppendOnlyConfig()`, the last of which exists specifically to apply an `appendonly` config change that arrived while loading. — [src/server.c#L8560-8572](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c#L8560-L8572)
- STARTUP step 7 — in cluster mode `main()` hard-asserts `verifyClusterConfigWithData() == C_OK` after load, then logs 'Ready to accept connections' per non-NULL `server.listeners[j]`, notifies systemd with `READY=1` when `supervised_mode == SUPERVISED_SYSTEMD`, and finally calls `warnAboutInsecureConfig()`. — [src/server.c#L8573-8593](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c#L8573-L8593)
- COMMAND step 1 — `processCommand()` first postpones the client via `blockPostponeClient(c)` if a module is in a blocked yield that excludes `BUSY_MODULE_YIELD_CLIENTS`, then runs `commandCheckExistence()` and `commandCheckArity()`, rejecting via `rejectCommandSds()`. — [src/server.c#L4680-4730](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c#L4680-L4730)
- COMMAND step 2 — commands literally named `host:` or `post` are intercepted before any other handling and routed to `securityWarningCommand(c)` returning `C_ERR`, a cross-protocol-scripting (HTTP-to-Redis) defence. — [src/server.c#L4706-4712](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c#L4706-L4712)
- COMMAND step 3 — commands carrying `CMD_INTERNAL` are made to look nonexistent (`cmd = NULL`) unless the client has `CLIENT_INTERNAL` or `mustObeyClient(c)` is true, so masters and AOF replay are implicitly trusted while ordinary clients cannot even discover them. — [src/server.c#L4714-4720](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c#L4714-L4720)
- COMMAND step 4 — `CMD_PROTECTED` commands are gated by `allowProtectedAction(server.enable_debug_cmd, c)` for `debugCommand` and `server.enable_module_cmd` for `moduleCommand`, with an error naming `enable-debug-command`/`enable-module-command`. — [src/server.c#L4732-4748](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c#L4732-L4748)
- COMMAND step 5 — `call()` sets `CLIENT_EXECUTING_COMMAND` to suppress mid-reply client-side-caching pushes, calls `enterExecutionUnit(1, call_timer)`, and skips slowlog/stat updates entirely when `isAOFLoadingContext()` is true. — [src/server.c#L4155-4215](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c#L4155-L4215)
- COMMAND step 6 — `call()` avoids a `gettimeofday` per command when a hardware monotonic clock is available: it only calls `updateCachedTime(0)` after 10µs of monotonic drift or 25 accumulated calls (`server.accum_call_count_since_ustime > 25`). — [src/server.c#L4185-4208](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c#L4185-L4208)
- REPLICATION (replica side) — `replicaofCommand()` → `replicationSetMaster(ip, port)` → `connectWithMaster()` installs `syncWithMaster()` as the connection handler, which performs the PING/AUTH/REPLCONF handshake then calls `slaveTryPartialResynchronization(conn, read_reply)`; on `+FULLRESYNC` the handler becomes `readSyncBulkPayload()`. — [src/replication.c#L4490](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/replication.c#L4490) [src/replication.c#L3621](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/replication.c#L3621) [src/replication.c#L3538](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/replication.c#L3538) [src/replication.c#L3143-2952](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/replication.c#L3143-L2952)
- REPLICATION (master side) — `syncCommand()` handles PSYNC/SYNC and, when no partial resync is possible, calls `startBgsaveForReplication(mincapa, req)` which selects between `rdbSaveBackground()` (disk-backed) and `rdbSaveToSlavesSockets()` (diskless) based on `repl-diskless-sync`. — [src/replication.c#L1208](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/replication.c#L1208) [src/replication.c#L1123](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/replication.c#L1123) [src/rdb.c#L2226](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/rdb.c#L2226) [src/rdb.c#L5298](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/rdb.c#L5298)
- ATOMIC SLOT MIGRATION — the documented eight-step flow is: destination runs `CLUSTER MIGRATION IMPORT`, source forks and ships a slot snapshot as RESTORE commands over an RDB channel, streams incremental writes over a main channel, pauses writes at catch-up, destination drains and takes ownership, config propagates over the cluster bus, then the source trims migrated keys via `asmTriggerBackgroundTrim()` on a BIO thread. — [src/cluster_asm.c#L18-45](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/cluster_asm.c#L18-L45) [src/cluster_asm.c#L3144](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/cluster_asm.c#L3144) [src/cluster_asm.c#L60-100](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/cluster_asm.c#L60-L100)
- NESTED EVENT LOOP — when `ProcessingEventsWhileBlocked` is set, `beforeSleep()` takes a short-circuit path running only `connTypeProcessPendingData`, `clientCompressionProcessPendingData`, `flushAppendOnlyFile(0)`, `handleClientsWithPendingWrites`, `freeClientsInAsyncFreeQueue`, `processClientsOfAllIOThreads` and `sendPendingClientsToIOThreads`, counting them into `server.events_processed_while_blocked` and returning early. — [src/server.c#L1966-1990](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c#L1966-L1990)

## Testing signals

Testing is a Tcl integration suite: `tests/test_helper.tcl` globs `tests/{unit,unit/type,unit/moduleapi,unit/cluster,integration}/*.tcl` into `::all_tests` and runs them against real spawned `redis-server` processes on ports from 21111. There are no C-level unit test files, but the binary embeds a `REDIS_TEST` mode invoked as `redis-server test <name>|all` for data-structure self-tests. Module API coverage is unusually deep — 48 C test modules under `tests/modules/` paired with 48 `unit/moduleapi/*.tcl` suites. CI runs eight build matrices plus an ASAN build and a commands.def drift check, with lcov/Codecov, CodeQL, Coverity, and a reply-schema linter on separate workflows.
- Test discovery is directory-glob based, not a manifest: `test_helper.tcl` iterates `set test_dirs {unit unit/type unit/moduleapi unit/cluster integration}` and lsorts `*.tcl` into `::all_tests`, so a new `.tcl` file in those dirs is picked up automatically. — [tests/test_helper.tcl#L28-45](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/tests/test_helper.tcl#L28-L45)
- The harness is a client/server fan-out: `test_server_main`, `accept_test_clients`, `read_from_test_client` and `signal_idle_client` distribute test files across parallel Tcl clients, with `run_solo` for tests that must not run concurrently. — [tests/test_helper.tcl#L261-290](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/tests/test_helper.tcl#L261-L290) [tests/test_helper.tcl#L336-380](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/tests/test_helper.tcl#L336-L380) [tests/test_helper.tcl#L465-485](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/tests/test_helper.tcl#L465-L485)
- Support libraries sourced by every suite live in `tests/support/`: `server.tcl` (start_server), `util.tcl`, `cluster_util.tcl`, `aofmanifest.tcl`, `redis.tcl` (the Tcl RESP client), `response_transformers.tcl` (RESP2/RESP3 normalisation), and `cli.tcl`. — [tests/test_helper.tcl#L20-26](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/tests/test_helper.tcl#L20-L26) [tests/support/](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/tests/support/)
- Module API is the most thoroughly covered subsystem: `runtest-moduleapi` enumerates 48 suites explicitly by `--single`, and `tests/modules/` holds 48 matching C test modules built by `$MAKE -C tests/modules` before the run. — [runtest-moduleapi#L21-70](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/runtest-moduleapi#L21-L70) [tests/modules/](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/tests/modules/) [tests/unit/moduleapi/](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/tests/unit/moduleapi/)
- The C code carries an embedded self-test harness rather than a separate unit-test binary: `main()` under `#ifdef REDIS_TEST` accepts `redis-server test <name>|all` with flags `--accurate --large-memory --valgrind --verbose`, iterates a `redisTests[]` table, and prints a pass/fail summary. — [src/server.c#L8256-8305](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c#L8256-L8305)
- Cluster coverage includes 23 dedicated suites under `tests/unit/cluster/`, notably `atomic-slot-migration.tcl`, `internal-secret.tcl`, `packet-validation.tcl`, `tls-peer-impersonation.tcl` and `cluster-bus-port-protected-mode.tcl` — the security-sensitive cluster paths are explicitly exercised. — [tests/unit/cluster/](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/tests/unit/cluster/)
- Integration coverage focuses on durability and replication edge cases: `aof-multi-part.tcl`, `aof-race.tcl`, `corrupt-dump-fuzzer.tcl`, `corrupt-dump.tcl`, `psync2*.tcl` (four files), `replication-rdbchannel.tcl`, `replication-iothreads.tcl`, `failover.tcl`, `shutdown.tcl`. — [tests/integration/](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/tests/integration/)
- The primary CI job `test-ubuntu-latest` builds with `make REDIS_CFLAGS='-Werror' BUILD_TLS=yes` and runs `./runtest --verbose --tags -slow --dump-logs` — meaning slow-tagged tests do not gate PRs. — [.github/workflows/ci.yml#L11-28](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/.github/workflows/ci.yml#L11-L28)
- CI enforces codegen freshness: the `validate commands.def up to date` step touches `src/commands/ping.json`, runs `make commands.def`, and fails if `git diff` is non-empty — a hand-edited commands.def will break the build. — [.github/workflows/ci.yml#L22-28](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/.github/workflows/ci.yml#L22-L28)
- A dedicated `test-sanitizer-address` job builds `SANITIZER=address REDIS_CFLAGS='-Werror -DDEBUG_ASSERTIONS -DREDIS_TEST' BUILD_TLS=module` and reruns the suite, so debug assertions and the embedded C tests are compiled in on that path only. — [.github/workflows/ci.yml#L30-40](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/.github/workflows/ci.yml#L30-L40)
- The build matrix covers portability rather than behaviour: `build-debian-old` (debian:buster container), `build-macos-latest`, `build-32bit` (via gcc-multilib), `build-libc-malloc`, `build-centos-jemalloc` (centos:stream9) and `build-old-chain-jemalloc` — all compile-only, no test step. — [.github/workflows/ci.yml#L42-95](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/.github/workflows/ci.yml#L42-L95)
- Coverage is reported but non-gating: `codecov.yml` sets both `patch` and `project` statuses to `informational: true`, and the workflow runs `make lcov` and uploads `./src/redis.info`, gated on `github.repository == 'redis/redis'` so forks skip it. — [codecov.yml](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/codecov.yml) [.github/workflows/codecov.yml#L14-40](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/.github/workflows/codecov.yml#L14-L40)
- Static analysis runs on separate cadences: CodeQL on every PR plus a weekly `cron: '0 0 * * 0'`, and Coverity daily (`cron: '0 0 * * *'`) with a comment noting the sub-500k-LOC quota of 21 builds/week. — [.github/workflows/codeql-analysis.yml#L1-12](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/.github/workflows/codeql-analysis.yml#L1-L12) [.github/workflows/coverity.yml#L1-10](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/.github/workflows/coverity.yml#L1-L10)
- Command JSON specs have their own path-filtered lint gate: `reply-schemas-linter.yml` triggers only on changes to `src/commands/*.json` and runs `node ./utils/reply_schema_linter.js` after `npm install ajv`. — [.github/workflows/reply-schemas-linter.yml#L1-25](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/.github/workflows/reply-schemas-linter.yml#L1-L25) [utils/reply_schema_linter.js](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/utils/reply_schema_linter.js)
- Heavy/long-running coverage is deferred to `daily.yml`, which runs nightly and on PRs targeting release branches matching `'[0-9].[0-9]'` — so a PR into `unstable` never exercises it. — [.github/workflows/daily.yml#L1-12](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/.github/workflows/daily.yml#L1-L12)
- The GCRA rate-limiter test file is entirely inert: `tests/unit/gcra.tcl` wraps its body in `if 0 { ... }`, matching the fact that `src/gcra.c` is compiled out unless `-DENABLE_GCRA` is passed — so that feature has zero effective coverage. — [tests/unit/gcra.tcl#L1-30](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/tests/unit/gcra.tcl#L1-L30) [src/gcra.c#L9-12](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/gcra.c#L9-L12) [src/server.h#L917-928](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.h#L917-L928)

## Risk map

The dominant risk class is memory-safety in hand-written C parsing code reachable pre-authentication: the RESP parser, the cluster bus packet decoder, and the RDB/DUMP loader all consume untrusted bytes. Authentication is layered and easy to misconfigure — protected-mode, ACL, an auto-generated cluster internal secret, and module-supplied auth callbacks. A second class is coupling risk: the propagation invariants in `call()`/`propagateNow()`, the ordering contract inside `beforeSleep()`, and the object-type registration checklist all fail silently or assert if a contributor misses a step. Finally, there is genuinely dead code — GCRA is compiled out and its test file is `if 0`'d — and a large `DEBUG` surface with 50+ subcommands guarded only by a config flag.
- The RESP parser is the pre-auth attack surface: `processMultibulkBuffer()` and `processInlineBuffer()` parse attacker-controlled bytes into `pendingCommand` structs before any AUTH check, and `readQueryFromClient()` performs manual pointer arithmetic (`(size_t)(c->bulklen+2)-(sdslen(c->querybuf)-c->qb_pos)`) to size big-argument reads. — [src/networking.c#L4008-4022](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/networking.c#L4008-L4022) [src/networking.c#L3215](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/networking.c#L3215) [src/networking.c#L3828-3862](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/networking.c#L3828-L3862)
- The cluster internal secret is auto-generated with `getRandomHexChars(server.cluster->internal_secret, CLUSTER_INTERNALSECRETLEN)` and converged across nodes by a lexicographic `memcmp(...) > 0` rule in the PING extension handler — meaning any node that can join the bus can influence the shared secret used to authenticate internal connections. — [src/cluster_legacy.c#L1048](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/cluster_legacy.c#L1048) [src/cluster_legacy.c#L2700-2760](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/cluster_legacy.c#L2700-L2760) [src/acl.c#L3341-3346](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/acl.c#L3341-L3346)
- Internal-secret password comparison correctly uses `time_independent_strcmp()`, but it is one of several parallel auth paths (`checkPasswordBasedAuth`, `ACLAuthenticateUser`, module auth callbacks) that must all stay consistent — a change to one does not automatically cover the others. — [src/acl.c#L3341-3350](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/acl.c#L3341-L3350) [src/acl.c#L1522-1560](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/acl.c#L1522-L1560) [tests/unit/moduleapi/moduleauth.tcl](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/tests/unit/moduleapi/moduleauth.tcl)
- `moduleLoad()` calls `dlopen(path, RTLD_NOW|RTLD_LOCAL)` on an operator-supplied path, giving any successfully loaded module full in-process code execution; the only guard is that `MODULE` carries `CMD_PROTECTED` and is gated by `server.enable_module_cmd`. — [src/module.c#L13698-13715](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/module.c#L13698-L13715) [src/server.c#L4732-4748](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c#L4732-L4748)
- `warnAboutInsecureConfig()` only logs — it never refuses to start. A server with `DefaultUser` NOPASS, `protected-mode no` and a `0.0.0.0`/`::` bind emits a WARNING and then serves traffic. — [src/server.c#L8593](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c#L8593) [src/server.c](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c)
- `redis-server --test-memory <mb>` and `--check-system` run arbitrary memory-stressing code (`memtest()`, `syscheck()`) directly from `main()` before any config or privilege handling, and `--test-memory` with a non-numeric argument silently becomes `atoi(...) == 0`. — [src/server.c#L8378-8392](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c#L8378-L8392)
- `src/debug.c` dispatches roughly 52 `DEBUG` subcommands via a chain of `strcasecmp(c->argv[1]->ptr, ...)` comparisons; the entire surface is protected only by the `enable-debug-command` config and `allowProtectedAction()`, and several subcommands (JMAP, SEGFAULT, SET-ACTIVE-EXPIRE, QUICKLIST-PACKED-THRESHOLD) can corrupt or crash a live server. — [src/debug.c](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/debug.c) [src/server.c#L4732-4748](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c#L4732-L4748)
- Adding a new object type is a documented multi-file checklist with no compiler enforcement — the `server.h` comment requires coordinated edits to `rdb.c` (save/load, possibly RDB_VERSION), `aof.c` (rewrite), `db.c` (`obj_type_name`, `copyCommand`), `debug.c` (`xorObjectDigest`, `serverLogObjectDebugInfo`) and `defrag.c` (`defragKey`). Missing one produces silent data or defrag corruption, not a build error. — [src/server.h#L928-940](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.h#L928-L940)
- GCRA is dead code in the shipped build: `src/gcra.c` is entirely inside `#ifdef ENABLE_GCRA`, `OBJ_GCRA`/`OBJ_TYPE_MAX 9` are conditional in `server.h`, the guard appears in `rdb.h`, `redismodule.h` and `acl.c`, and `tests/unit/gcra.tcl` disables its whole body with `if 0 {`. Building with `-DENABLE_GCRA` would activate untested code and change `OBJ_TYPE_MAX`, which is an RDB-visible constant. — [src/gcra.c#L9-22](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/gcra.c#L9-L22) [src/server.h#L917-928](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.h#L917-L928) [src/rdb.h#L88-94](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/rdb.h#L88-L94) [tests/unit/gcra.tcl#L1-5](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/tests/unit/gcra.tcl#L1-L5)
- `beforeSleep()` encodes at least four ordering invariants in comments rather than in code structure (TLS pending data before AOF flush; `clusterBeforeSleep()` before `blockedBeforeSleep()`; `blockedBeforeSleep()` before `flushAppendOnlyFile`; AOF flush before `handleClientsWithPendingWrites`). Reordering any of them breaks `appendfsync=always` durability guarantees without failing a test deterministically. — [src/server.c#L1990-2013](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c#L1990-L2013) [src/server.c#L2060-2072](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c#L2060-L2072)
- `propagateNow()` contains two assertions that convert logic errors into crashes rather than data loss: `debugServerAssert(duration != PROP_DURATION_UNKNOWN)` and the replica-pause assert. Both are reachable from any new command that queues propagation incorrectly. — [src/server.c#L3798-3818](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c#L3798-L3818)
- The query-buffer pooling scheme (`thread_reusable_qb` / `thread_reusable_qb_used` / `CLIENT_IO_REUSABLE_QUERYBUFFER`) is shared mutable state whose correctness depends on nested-execution detection; the code comment notes the private-buffer fallback exists only for `processEventsWhileBlocked()` re-entry. — [src/networking.c#L4032-4052](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/networking.c#L4032-L4052)
- `src/module.c` is 16,117 lines — the single largest blast-radius file. It owns command registration, `RM_Call`, command filters, keyspace hooks, data-type serialization and `dlopen`, so nearly every subsystem change can break the stable module ABI in `src/redismodule.h`. — [src/module.c](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/module.c) [src/redismodule.h](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/redismodule.h)
- Atomic slot migration carries its own trim-correctness risk: `ASM_DEBUG_TRIM_DEFAULT` auto-selects between background BIO-thread deletion and main-thread active deletion 'based on module subscriptions and client tracking', so keyspace notifications fire in one mode and not the other. — [src/cluster_asm.c#L54-60](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/cluster_asm.c#L54-L60) [src/cluster_asm.c#L3081-3150](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/cluster_asm.c#L3081-L3150)
- `make` recipes shell out to scripts that perform network operations without review gates: `scripts/modules-update.sh` and `modules/common.mk` clone from GitHub URLs in `modules.yaml`, and `common.mk` resolves ref kind by probing the remote with `git ls-remote` at recipe time — so a `branch:` pin silently follows a moving target. — [modules/modules.yaml#L15-30](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/modules/modules.yaml#L15-L30) [modules/common.mk#L5-20](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/modules/common.mk#L5-L20) [scripts/modules-update.sh](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/scripts/modules-update.sh)
- CI does not run the slow test tag on PRs (`./runtest --verbose --tags -slow`), and the heavy `daily.yml` matrix only triggers on PRs to release branches matching `'[0-9].[0-9]'` — so changes merged to `unstable` get comparatively thin pre-merge validation. — [.github/workflows/ci.yml#L18-21](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/.github/workflows/ci.yml#L18-L21) [.github/workflows/daily.yml#L1-10](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/.github/workflows/daily.yml#L1-L10)
- Sentinel mode initializes before config parsing and before the RDB/AOF check dispatch, and `sentinel.c` is 5,000+ lines sharing `server.c` globals with the normal server path — a startup-order change in `main()` can break sentinel in ways the default `./runtest` (which excludes sentinel) will not catch. (inferred) — [src/server.c#L8342-8352](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c#L8342-L8352) [src/sentinel.c](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/sentinel.c) [runtest-sentinel](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/runtest-sentinel)

## Extension points

Redis has four first-class extension mechanisms. New commands are added declaratively: drop a JSON spec in `src/commands/`, implement the proc, and regenerate `src/commands.def`. New configuration parameters are appended to the `static_configs[]` table in `src/config.c` using the `create*Config` macro family. New runtime behaviour is added out-of-process via the module API (`RM_CreateCommand`/`RM_CreateSubcommand` plus data types, filters, and hooks). New scripting languages plug into `functionsRegisterEngine()`. On top of these, this tree adds a fifth: `modules/modules.yaml` as a declarative registry for bundled external modules.
- Command registration is codegen-driven: `src/commands/*.json` is the input, `utils/generate-command-code.py` is the generator, and `src/Makefile`'s rule `$(COMMANDS_DEF_FILENAME).def: commands/*.json ../utils/generate-command-code.py` regenerates `src/commands.def`. CI fails if the checked-in file drifts. — [src/Makefile#L503-504](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/Makefile#L503-L504) [utils/generate-command-code.py](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/utils/generate-command-code.py) [.github/workflows/ci.yml#L22-28](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/.github/workflows/ci.yml#L22-L28)
- The generator maps declarative JSON to C enums via `ARG_TYPES` (string/integer/double/key/pattern/unix-time/pure-token/oneof/block) and `GROUPS`, which already contains the newer `COMMAND_GROUP_ARRAY` and `COMMAND_GROUP_RATE_LIMIT` — adding a command group means editing this table. — [utils/generate-command-code.py#L7-40](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/utils/generate-command-code.py#L7-L40)
- The runtime command registry is `redisCommandTable[]` from commands.def, loaded once by `populateCommandTable()` into dicts consulted by `lookupCommandLogic()`, `lookupCommandBySds()` and `lookupCommandByCString()` — module commands are inserted into the same dicts. — [src/server.c#L3556-3570](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c#L3556-L3570) [src/server.c#L3675-3745](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c#L3675-L3745)
- Configuration is a single append-only table: `standardConfig static_configs[]` in src/config.c, populated by 119 `createBoolConfig`/`createStringConfig`/`createEnumConfig`/`createSDSConfig`/etc. calls, each taking a flags mask (`MODIFIABLE_CONFIG`, `IMMUTABLE_CONFIG`, `DEBUG_CONFIG`, `HIDDEN_CONFIG`, `SENSITIVE_CONFIG`) plus optional validate and apply callbacks. — [src/config.c#L3445-3490](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/config.c#L3445-L3490) [src/config.c#L3530-3531](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/config.c#L3530-L3531)
- Config apply-callbacks are the hook for cross-subsystem side effects — e.g. `updateClusterFlags` on `cluster-replica-no-failover`, `updateJemallocBgThread` on `jemalloc-bg-thread`, `updateAppendonly` on `appendonly`, `updateRequirePass` on `requirepass`, and `applyClusterBusPortProtectedMode`. — [src/config.c#L3445-3490](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/config.c#L3445-L3490) [src/config.c#L3530-3531](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/config.c#L3530-L3531)
- The module command API is `RM_CreateCommand(ctx, name, cmdfunc, strflags, firstkey, lastkey, keystep)` with `RM_CreateSubcommand(parent, ...)` for containers — the canonical way third-party code adds commands without touching the core tables. — [src/module.c#L1348](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/module.c#L1348) [src/module.c#L1479](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/module.c#L1479) [src/redismodule.h](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/redismodule.h)
- Modules can intercept every command before dispatch by registering a command filter, invoked as `moduleCallCommandFilters(c)` at the top of `processCommand()`; `src/module.c` documents a flag to prevent a module's own `RM_Call()` from re-entering its filter. — [src/server.c#L4671-4678](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c#L4671-L4678) [src/module.c#L11960-11975](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/module.c#L11960-L11975)
- Server lifecycle hooks are exposed as module server events — `beforeSleep()` fires `moduleFireServerEvent(REDISMODULE_EVENT_EVENTLOOP, REDISMODULE_SUBEVENT_EVENTLOOP_BEFORE_SLEEP, NULL)` once per loop iteration when `moduleCount()` is non-zero. — [src/server.c#L2024-2030](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c#L2024-L2030) [tests/unit/moduleapi/hooks.tcl](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/tests/unit/moduleapi/hooks.tcl) [tests/unit/moduleapi/eventloop.tcl](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/tests/unit/moduleapi/eventloop.tcl)
- Scripting languages are pluggable through `functionsRegisterEngine(const char *engine_name, engine *engine)`, which stores into a module-level `static dict *engines`; `FUNCTION STATS` reflects the registry back to clients, and the only current registration is `LUA_ENGINE_NAME` from `luaEngineInitEngine` in function_lua.c. — [src/functions.c#L99-100](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/functions.c#L99-L100) [src/functions.c#L407-420](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/functions.c#L407-L420) [src/functions.c#L455-465](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/functions.c#L455-L465) [src/function_lua.c#L512](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/function_lua.c#L512)
- Keyspace notification is a central hook point via `notifyKeyspaceEvent(type, event, key, dbid)` and the newer `notifyKeyspaceEventWithSubkeys(...)` — new data types must call these to participate in keyspace events and client-side-cache invalidation. — [src/notify.c#L276-285](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/notify.c#L276-L285)
- Connection types are a runtime-registered abstraction: `connTypeInitialize()` in `main()` sets up `server.listeners[j]` for `j < CONN_TYPE_MAX`, with implementations in `src/socket.c`, `src/unix.c` and `src/tls.c` behind `src/connection.h` — TLS can even be built as a loadable module with `BUILD_TLS=module`. — [src/server.c#L8336](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c#L8336) [src/connection.h](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/connection.h) [src/Makefile#L343-357](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/Makefile#L343-L357)
- Bundled external modules are added declaratively by appending an entry to `modules/modules.yaml` with `name`/`repo`/`ref`/`target_module`/`loadmodule` and optional `build_env` — no new Makefile is needed, because `modules/common.mk` derives `MODULE_NAME` from `$(notdir $(CURDIR))` and reads the rest from the manifest. — [modules/modules.yaml#L28-50](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/modules/modules.yaml#L28-L50) [modules/common.mk#L5-20](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/modules/common.mk#L5-L20) [modules/MODULES.md#L18-40](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/modules/MODULES.md#L18-L40)
- All YAML parsing has exactly one implementation: `scripts/lib/manifest.sh`, which is dual-mode — sourced as a shell library (`manifest_modules`, `manifest_field`, `manifest_ref`, `resolve_modules`) and invoked as a CLI by `modules/manifest.mk`. Extending the manifest format means editing that one file. — [modules/MODULES.md#L28-34](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/modules/MODULES.md#L28-L34) [scripts/lib/manifest.sh](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/scripts/lib/manifest.sh) [modules/manifest.mk](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/modules/manifest.mk)
- New top-level Make goals that take positional module names plug into the `GOALS_WITH_ARGS := <goal>:<VAR>` list in the root Makefile; the dispatch and no-op `.PHONY` targets are generated automatically by the `_capture_goal_args` define. — [Makefile#L36-70](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/Makefile#L36-L70)
- New Tcl test suites require no registration — dropping a `.tcl` file into `tests/unit/`, `tests/unit/type/`, `tests/unit/cluster/` or `tests/integration/` is enough, since `test_helper.tcl` globs those directories. The exception is `tests/unit/moduleapi/`, where `runtest-moduleapi` lists each suite explicitly with `--single`. — [tests/test_helper.tcl#L30-44](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/tests/test_helper.tcl#L30-L44) [runtest-moduleapi#L21-70](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/runtest-moduleapi#L21-L70)
- The embedded C test harness is extended through the `redisTests[]` table consumed by `getTestProcByName()` in `main()`, letting a data-structure file register a `redisTestProc` runnable as `redis-server test <name>`. — [src/server.c#L8268-8305](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c#L8268-L8305)

## Contributor handoff

Read `src/server.h` (the 4,976-line master header that every `.c` file includes) and `src/server.c` before touching anything — nearly all global state hangs off the `server` struct declared there. Never hand-edit `src/commands.def`; edit the JSON spec and regenerate. Prefer adding config via the `static_configs[]` table and features via the module API over touching the dispatch path. Build with `make REDIS_CFLAGS='-Werror'` to match CI, and validate with `./runtest --tags -slow` plus `./runtest-moduleapi` if you touched `src/module.c`. Be aware this tree's root `Makefile` is not upstream's: plain `make` runs `scripts/build.sh`, which builds `src/` and then every cloned external module.
- Read first, in order: `src/server.h` (all struct/constant/prototype definitions), `src/server.c` (`main`, `initServer`, `processCommand`, `call`, `beforeSleep`, `serverCron`), `src/networking.c` (client lifecycle and RESP parsing), and `src/config.c` (`static_configs[]`). — [src/server.h](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.h) [src/server.c](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c) [src/networking.c](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/networking.c) [src/config.c](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/config.c)
- Never edit `src/commands.def` by hand. Edit `src/commands/<cmd>.json`, run `make commands.def` (which invokes `utils/generate-command-code.py`), and commit both — CI's `validate commands.def up to date` step diffs the regenerated output and fails on drift. — [src/Makefile#L503-504](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/Makefile#L503-L504) [.github/workflows/ci.yml#L22-28](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/.github/workflows/ci.yml#L22-L28)
- Match CI's build flags locally: `make REDIS_CFLAGS='-Werror' BUILD_TLS=yes`. Use `REDIS_CFLAGS`/`REDIS_LDFLAGS` rather than `CFLAGS`/`LDFLAGS` — the src/Makefile header notes that plain CFLAGS propagate into the `deps/` builds (jemalloc, Lua, hiredis) too. — [src/Makefile#L9-17](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/Makefile#L9-L17) [.github/workflows/ci.yml#L15-17](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/.github/workflows/ci.yml#L15-L17)
- Test commands to run: `make -C src test` (= `./runtest`) for the default suite, `./runtest --tags -slow --dump-logs` to match CI exactly, `./runtest --single unit/type/hash` to scope, `make -C src test-modules`, `test-cluster`, `test-sentinel` for the other three harnesses. — [src/Makefile#L529-541](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/Makefile#L529-L541) [runtest](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/runtest) [.github/workflows/ci.yml#L18-21](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/.github/workflows/ci.yml#L18-L21)
- Trap — building Redis alone is `make -C src all`, not `make`. The root `Makefile`'s default goal is `build`, which runs `scripts/build.sh` and will additionally try to build every module cloned under `modules/*/src`. Use `make build redis` or `make build core` to skip modules. — [Makefile#L13-15](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/Makefile#L13-L15) [Makefile#L105-108](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/Makefile#L105-L108) [scripts/build.sh#L4-12](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/scripts/build.sh#L4-L12)
- Trap — on macOS you must use `gmake` (GNU Make ≥ 4.x), not Apple's `/usr/bin/make` 3.81. The root Makefile explicitly `export MAKE`s so recipe shells inherit the right binary, precisely because non-interactive recipe shells otherwise PATH-resolve to make 3.81. — [Makefile#L3-11](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/Makefile#L3-L11) [modules/MODULES.md#L9-13](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/modules/MODULES.md#L9-L13)
- Trap — adding a new object type requires coordinated edits to `rdb.c`, `aof.c`, `db.c` (`obj_type_name`, `copyCommand`), `debug.c` (`xorObjectDigest`, `serverLogObjectDebugInfo`) and `defrag.c` (`defragKey`), per the checklist comment in server.h. Nothing enforces this at compile time. — [src/server.h#L928-940](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.h#L928-L940)
- Trap — `redis.conf` is the tracked, hand-edited core config with no markers; `redis-full.conf` is untracked and regenerated by `scripts/sync-redis-conf.sh` on every `make modules-update`. Never hand-add `loadmodule` lines to `redis.conf`. — [modules/MODULES.md#L32-38](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/modules/MODULES.md#L32-L38) [scripts/sync-redis-conf.sh](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/scripts/sync-redis-conf.sh) [redis.conf](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/redis.conf)
- Safe edit boundary — prefer adding behaviour through the module API (`RM_CreateCommand`, module data types, command filters, server-event hooks) or a new `static_configs[]` entry over modifying `processCommand()`/`call()`, which carry assertions and propagation invariants. — [src/module.c#L1348](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/module.c#L1348) [src/config.c#L3445](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/config.c#L3445) [src/server.c#L4151-4230](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c#L4151-L4230)
- Do not touch `deps/` — it is vendored third-party source (jemalloc, lua, hiredis, linenoise, hdr_histogram, fpconv, xxhash, tre) built by separate targets listed in `DEPENDENCY_TARGETS`. Changes there are lost on dependency refresh. — [deps/](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/deps/) [src/Makefile#L39](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/Makefile#L39) [deps/README.md](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/deps/README.md)
- Style convention — the codebase is C99/gnu11 compiled with `-pedantic -Wall -W -Wstrict-prototypes -Werror=deprecated-declarations`, and falls back to `-std=c99` (also setting `SKIP_VEC_SETS=yes`) when the compiler lacks C11 `_Atomic`. New code must build under both. — [src/Makefile#L44-70](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/Makefile#L44-L70)
- Licensing convention — every new source file carries the Redis tri-license header (RSALv2 / SSPLv1 / AGPLv3); contributions are governed by the Software Grant and CLA reproduced in full in CONTRIBUTING.md. — [CONTRIBUTING.md#L1-10](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/CONTRIBUTING.md#L1-L10) [src/t_array.c#L1-8](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/t_array.c#L1-L8) [LICENSE.txt](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/LICENSE.txt)
- Security convention — do not report vulnerabilities through normal issues; SECURITY.md directs reports to the Redis Vulnerability Disclosure Program or GitHub's 'Report a Vulnerability', and notes that 32-bit, non-Linux and outdated-toolchain findings may be ruled out of scope. — [SECURITY.md](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/SECURITY.md)
- When adding or changing a command's reply, update the reply schema in its `src/commands/*.json` — `.github/workflows/reply-schemas-linter.yml` triggers on changes to those files and validates them with ajv via `utils/reply_schema_linter.js`. — [.github/workflows/reply-schemas-linter.yml#L1-25](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/.github/workflows/reply-schemas-linter.yml#L1-L25) [utils/reply_schema_linter.js](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/utils/reply_schema_linter.js)
- For debugging a failing Tcl test, `./runtest --dump-logs` plus `tests/support/util.tcl`'s helpers and the `s <field>` / `S <index> <field>` INFO accessors in test_helper.tcl are the standard tools; servers spawn on ports starting at `::baseport 21111`. — [tests/test_helper.tcl#L239-260](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/tests/test_helper.tcl#L239-L260) [tests/test_helper.tcl#L45-50](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/tests/test_helper.tcl#L45-L50) [tests/support/util.tcl](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/tests/support/util.tcl)
- Bumping a bundled module is a one-line change: edit `ref:` in `modules/modules.yaml` then run `make modules-update <name>`. Ref kind is resolved dynamically against the remote in the order tag → branch → commit, and an unmatched ref errors out with 'unknown ref kind'. — [modules/modules.yaml#L15-35](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/modules/modules.yaml#L15-L35) [scripts/modules-update.sh](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/scripts/modules-update.sh)

Source repository: https://github.com/redis/redis
Full evidence corpus: https://github.com/Codevetter/codevetter/tree/main/benchmarks/repo-unpacks/redis.json

## Public product links

- [CodeVetter](https://codevetter.com/)
- [Download](https://codevetter.com/download)
- [Documentation](https://codevetter.com/docs/)
- [Source](https://github.com/Codevetter/codevetter)
