rmycli
A Rust Redis shell with live command help, metadata-driven argument completion, and type-aware key completion.
Status
The first interactive implementation is available. It connects to Redis,
executes arbitrary commands, discovers the server's command catalog, shows
syntax and argument help while editing, and completes keys using bounded,
cancellable SCAN requests. It also estimates LRANGE replies before execution.
Common redis-cli connection, scripting, and output options are supported,
including RESP3 and cluster redirects. Cluster-wide discovery, replication/RDB
transfers, and estimates for commands other than LRANGE are not implemented.
See Compatibility and limits below for metadata and streaming limitations.
Install and connect
Use Rust 1.88 or newer (the current stable toolchain is recommended).
Once version 0.1.0 is published to crates.io, install it with:
To install from a source checkout instead, run cargo install --path . --locked
in the project directory. Then connect to Redis:
REDISCLI_AUTH=secret
--tls enables certificate-verified TLS for -h/-p connections or upgrades
an existing TCP URI without changing its host or port. System trust roots are
used unless --cacert FILE or --cacertdir DIR supplies PEM CA certificates.
Use --cert FILE --key FILE for a PEM client certificate chain and private key.
These flags also work with rediss:// URLs and TLS instances. --insecure
explicitly disables certificate verification; verification is otherwise on.
A URL supplies connection settings; explicit -n, --user,
and -a/REDISCLI_AUTH override its database and credentials. REDIS_URL is
also supported. Use rmycli --help for all options (-h selects the host).
Commands can also be supplied directly or one per line on stdin:
|
Command-line arguments pass through as supplied by the operating system.
Interactive and stdin input support single/double quotes, backslash escapes,
and \xHH byte escapes. Suggestions round-trip through the same parser, so
spaces, quotes, backslashes, NULs, and non-UTF-8 key bytes remain intact.
Formatted output escapes terminal control sequences. Redirected stdout now
uses raw output by default, like redis-cli; --no-raw restores formatted
output. --raw writes reply bytes verbatim in noninteractive mode. Redis errors
exit noninteractive mode with a nonzero status, with or without -e.
Server errors show the server's message without the Redis library's preamble
(for example, (error) syntax error in the editor). Errors without a message
show their server error code. Client and connection errors retain their context.
Familiar redis-cli options
rmycli --help groups options by purpose and includes connection, scripting,
TLS, and configuration examples. -h selects the host; help is --help.
Options precede the command. Once a command starts, subsequent arguments pass
through unchanged, including -1, --raw, and other option-looking values.
Existing rmycli long names, including --password, remain supported.
The names and core semantics follow the Redis CLI reference.
| Purpose | Supported redis-cli options |
|---|---|
| Connection | -h, -p, -s, -u, -n, -t |
| Authentication | -a, --pass, --user, --askpass, --no-auth-warning |
| Protocol and session | -2, -3, --name, -c, --show-pushes yes/no |
| TLS | --tls, --cacert, --cacertdir, --cert, --key, --insecure |
| Output | --raw, --no-raw, --csv, --json, --quoted-json, -d, -D |
| Scripting | -x, -X, --quoted-input, -r, -i, --eval, -e |
| Key listing | --scan, --pattern, --quoted-pattern, --count |
| Information | --help, --version, -v, --verbose |
|
- RESP2 remains the default.
-3negotiates RESP3 on connection;--jsonand--quoted-jsonalso select RESP3 unless-2is given.-2and-3together are rejected. Auxiliary connections use RESP2 independently. --pass/-atakes precedence overREDISCLI_AUTH;--askpassoverrides every password source. The prompt requires terminal stdin and disables echo; it cannot be combined with-x/-X. UseREDISCLI_AUTHwith piped input. Explicit command-line credentials produce a warning unless suppressed.-tis a connection timeout in seconds, including fractions;-t 0uses the OS timeout. Without-t, the existing--timeout-msdefault of 1,000 ms applies. Auxiliary connections stay bounded by--timeout-mseven with-t 0. Commands that intentionally block still have no read timeout.-xappends all stdin bytes, including trailing newlines, as one argument.-X TAGreplaces the first argument matching TAG and rejects a missing tag or replacement of the command name.--quoted-inputdecodes each supplied argument separately, including\xHHescapes.-r Nrepeats a command in noninteractive mode, with-iseconds between executions.-r 0executes nothing;-r -1repeats until Ctrl-C.--eval FILEsendsEVAL, using the standalone comma to separate keys from arguments; without a comma all trailing arguments are keys.-dseparates raw array elements and-Dseparates raw replies. Empty delimiters are allowed. JSON preserves valid UTF-8; invalid UTF-8 falls back to ASCII byte escapes.--quoted-jsonalways represents byte escapes as literal JSON strings. Nonfinite doubles become strings and nested errors become objects so output remains valid JSON. CSV uses redis-cli's escaped strings, rather than RFC 4180 quoting. The live editor always escapes terminal control bytes, even with--raw; JSON and CSV work in the editor.--show-pushescontrols unsolicited RESP3 messages (default: on for terminal stdout). The async session reader receives messages while idle and wakes the worker when a message or submitted command arrives. Invalidations appear without another command, in bold, while preserving the input and cursor. The default interactive format is-> invalidate: 'key', with one line per key, or-> invalidate: (nil)for a cache-wide invalidation. Keys are escaped safely; binary keys use double quotes and byte escapes. JSON, CSV, and noninteractive output retain their existing formats. Reading sends no Redis commands and also runs during WATCH and MULTI. Hidden pushes are still drained; Pub/Sub messages remain visible. Push styling is currently fixed. Noninteractive push display remains command driven.--scanstreams one SCAN batch at a time until cursor zero without retaining the whole keyspace. Ctrl-C interrupts it;-ithrottles requests.--countis a Redis work hint, not a result limit. Like redis-cli, this explicit listing may contain duplicates and is not a snapshot. It does not use the bounded completion budgets and makes no unique-key-count claim.-cfollows up to five MOVED/ASK redirects, including ASKING before an ASK retry. It preserves TLS, authentication, database, protocol, and client name. It never retries network failures, or redirects while MULTI/WATCH is active. Reconnecting to a redirected node does not restore other connection-local state. Discovery and estimates cover only the currently connected node.-ccannot be combined with-s.
Deliberately omitted and currently unsupported options
Use redis-cli for its separate administration and diagnostic tools:
--cluster and its sub-options; --replica/--slave, --rdb,
--functions-rdb; --pipe/--pipe-timeout; --stat, --latency*,
--intrinsic-latency, --lru-test, --vset-recall*; --bigkeys, --memkeys*,
--keystats*, --hotkeys and their --cursor/--top modifiers; and
--ldb/--ldb-sync-mode. These are distinct workflows beyond the interactive
shell and ordinary command execution.
Address-family selection (-4/-6), a separate --sni, --tls-ciphers, and
--tls-ciphersuites are also currently unsupported: rmycli does not expose
those transport controls. TLS uses the connection hostname for SNI and rustls
cipher defaults. Unsupported switches before the command fail with a usage
error; they are never accepted as no-ops. Full option parity is
not claimed.
The session uses asynchronous TCP, Unix sockets, or TLS, with the published
Redis 1.8 parser and command encoder; no vendored dependency is required.
A dedicated reader keeps partial frames across waits and feeds a bounded
64-message channel. User commands remain serialized on the same connection,
including protocol changes, transactions, RESP2/RESP3 Pub/Sub, and MONITOR.
This raw stream is needed because redis-rs's async multiplexer does not expose
arbitrary streaming replies on an existing connection. Dropping a session
aborts its reader; Ctrl-C still kills the exact server client to cancel a
blocking command. Completion, estimation, and metadata use separate
synchronous connections with timeouts on their worker thread.
Configuration and saved instances
Settings are read once at startup from the first available file, in this order:
$XDG_CONFIG_HOME/rmycli.toml, or~/.config/rmycli.tomlwhenXDG_CONFIG_HOMEis unset, empty, or relative.rmycli.tomlin each directory in$XDG_CONFIG_DIRS, in order (default:/etc/xdg). Relative directory entries are ignored.
Use --config /path/to/settings.toml to select a file explicitly, or
--no-config to use built-in settings. Files are not merged. Missing discovered
files are skipped; a missing explicit file, unreadable file, malformed TOML,
unknown setting, or invalid value causes an error before connecting. No file is
created during normal startup. --help and --version work without loading
settings.
See rmycli.dist.toml for a commented reference covering every
supported setting, defaults, color formats, and authentication examples. Copy
it to ~/.config/rmycli.toml (or $XDG_CONFIG_HOME/rmycli.toml); the distribution
file is not loaded automatically.
For a shorter example, create ~/.config/rmycli.toml:
[]
= "dark_grey"
[]
= "cyan"
= "#89b4fa"
= "green"
= "yellow"
[]
= "redis.qa.example.com"
= 6379
= 0
= "alice"
= "QA_REDIS_PASSWORD"
= false
Select an instance with rmycli --instance qa (also works with a command, such
as rmycli --instance qa PING). Authentication happens automatically on connect,
including background completion and reply-estimation connections. Instances
are selected explicitly, not inferred from host/port. Instance names are case
sensitive. An unknown instance is an error.
All instance fields are optional: host and port default to 127.0.0.1:6379,
database to zero, and TLS to off. Omit user for Redis's default user or
password-only authentication. Set tls = true for certificate-verified TLS.
Use either password = "..." or password-env = "ENVIRONMENT_VARIABLE";
the latter reads the password from that variable when the instance is selected.
A missing password variable is an error unless a higher-priority password is
provided. Protect a file containing passwords with owner-only permissions.
Explicit -h, -p, -n, --user, and -a/--pass override the selected instance's
values. REDISCLI_AUTH overrides its password too; -a takes precedence over
that environment variable. --tls can enable TLS on an instance. --instance
cannot be combined with -u/REDIS_URL or -s; unset REDIS_URL before
selecting an instance. Without --instance, connection behavior is unchanged.
Connection timeouts and discovery/reply limits remain command-line options.
The theme controls the foreground color of inline argument hints. Command
names are case insensitive, with whitespace normalized; a subcommand's color
overrides its parent's color, which overrides inline-hint. Matching uses the
resolved metadata command, not key or argument text. Unknown command names
are allowed so the same file can cover module commands on different servers.
Duplicate names after normalization are errors. Popup colors are unchanged.
Colors accept black, red, green, yellow, blue, magenta, cyan,
white, grey, dark_grey, and the dark_ variants of red, green, yellow,
blue, magenta, and cyan. Also supported: reset (terminal default),
"#RRGGBB", "rgb_(R,G,B)", and "ansi_(N)" (components/index 0–255).
For example, inline-hint = "#ffffff" selects white; hex digits accept either
case. Keep the quotes because an unquoted # starts a TOML comment.
Named colors follow the terminal palette; RGB rendering depends on terminal
support. The built-in hint color remains dark_grey.
Read and edit settings from the command line
Local configuration commands run without connecting to Redis:
get prints the effective value, including built-in defaults and parent-command
color inheritance. Missing optional settings and unknown instances return an
error. It reads the file's settings, without applying connection flags or
resolving password environment variables. set prints the saved value;
password updates print [redacted]. An explicit get of a stored password
prints it.
For color settings, both get and set show the value followed by sample text
rendered in that color when stdout is a terminal. Redirected output is just the
value and a newline. A nonempty NO_COLOR disables automatic previews.
Use --color always to force a preview (including when NO_COLOR is set), or
--color never to suppress it:
Keys use TOML dotted-key syntax; quote a component containing spaces or dots,
as in 'theme.commands."XGROUP CREATE"' or 'instances."qa.eu".host'.
String values are literal arguments, requiring only shell quoting. Ports and
databases must be integers; TLS takes true or false. The complete updated
file must pass validation before it is saved. unset removes an explicit value,
restoring its default/inheritance, and fails if that value was not set.
Edits preserve comments and unrelated settings and replace the file atomically.
By default they write $XDG_CONFIG_HOME/rmycli.toml (normally
~/.config/rmycli.toml), creating directories as needed. If only a system file
exists, its contents are copied into the new user file before editing, so the
other effective settings are retained. System files are not edited implicitly.
--config PATH, before or after config, selects a specific file; set can
create it. Existing symlinks are followed, existing file permissions are retained,
and new files have owner-only permissions on Unix. Changes take effect in new
shell sessions.
Lowercase rmycli config is reserved for these local operations. Use uppercase
rmycli CONFIG GET ... or rmycli CONFIG SET ... for the Redis command.
Interactive Redis commands are unaffected. rmycli config --help lists the
local commands; rmycli --no-config config get ... reads built-in defaults.
Live help and editing
On connection, rmycli loads COMMAND and, when available,
COMMAND DOCS. Remaining arguments appear in grey by default on
the input line, like redis-cli: typing lrange displays lrange key start stop, with
only key start stop supplied by the program. Typing the key changes the hint
to start stop. Hints never become part of the submitted command. They are
hidden while editing inside the line and clipped at the terminal's right edge.
Redraws are buffered and use synchronized terminal updates where supported,
so clearing hints, printing output, and drawing the next prompt appear together.
F1 opens a dismissible help panel with current command documentation, editor
shortcuts, and connection status. HELP command prints full syntax, summary,
version, group, and complexity to the terminal.
Nested alternatives, optional
clauses, repeated arguments, subcommands, and module documentation come from
the server rather than a fixed set of handwritten command signatures.
The complete official Redis command-help catalog is bundled as a fallback.
Live documentation takes precedence, including for module commands. Fallback
help is labeled because it can differ from the connected server's version.
Command names are restricted to the live catalog when COMMAND succeeds.
| Key / command | Action |
|---|---|
| Tab / Shift-Tab | Start completion / cycle through popup candidates |
| Enter with popup open | Accept the candidate; does not execute |
| Enter without popup | Submit the command |
Ctrl+Enter on KEYS <pattern> |
Stream matching keys with SCAN |
| Hold Ctrl (with modifier-event support) | Show [Ctrl] in the prompt |
| Escape | Close the popup or cancel discovery/confirmation |
| Left / Right, Home / End, Ctrl-A / Ctrl-E | Move the cursor |
| Backspace / Delete, Ctrl-U / Ctrl-K | Delete characters or text before/after the cursor |
| Up / Down | Browse in-memory command history |
| F1 | Open or close command help, editor shortcuts, and connection details |
| PageUp / PageDown, Up / Down with help open | Scroll the help panel |
| Enter with help open | Close help without submitting input |
CLEAR, Ctrl-L |
Clear the screen locally without sending a Redis command |
| Ctrl-C | Stop alternate KEYS scanning; otherwise clear input or close an executing/streaming connection |
Ctrl-D, \quit, \q, QUIT, EXIT |
Exit |
HELP command, HELP @group |
Full command help or commands in a group |
HELP, \help |
Open editor help and connection details |
\reconnect |
Open a new connection and refresh metadata |
Experiment with KEYS * or KEYS foo:* followed by Ctrl+Enter to stream
keys using SCAN until cursor zero. The pattern is passed through unchanged:
KEYS foo:* uses SCAN ... MATCH foo:* COUNT 128. Empty batches do not stop
iteration. Output is streamed with bounded buffering; duplicates are possible
and the listing is not a snapshot. It covers the current database on the
connected node, without completion budgets or a unique-key-count claim.
Ctrl-C stops further iterations without exiting or disconnecting. Any
in-flight request finishes first; buffered keys are discarded, and the output
is labeled partial. You can then run another command on the same connection.
Alternate KEYS is unavailable during MULTI, WATCH, or streaming sessions.
Normal Enter still sends KEYS directly; there is no default-remapping setting
yet. With a completion popup open, either Enter only accepts the candidate.
Ctrl+Enter requires a terminal that distinguishes it from Enter. The editor
requests the Kitty keyboard protocol, including modifier presses and releases.
On supporting terminals, holding either Ctrl key changes rmycli> to
rmycli[Ctrl]>; releasing it restores the prompt. Modifier events leave the
input, completion popup, and confirmation unchanged. Shift remains available
for typing case-sensitive keys. Ctrl+Enter with a popup open only accepts the
candidate; release events never submit commands.
Terminals or multiplexers without this support may send plain Enter for both,
which executes ordinary KEYS. They may also omit standalone Ctrl presses and
releases, so the held-key indicator cannot work there. An ordinary Ctrl chord
alone does not turn on the indicator, since its release may never be reported.
If modified Enter acts like Enter, check the whole terminal path, including
tmux or screen. For example, tmux with extended-keys off cannot forward the
distinction. Changing the application shortcut alone cannot fix that. Try a
terminal supporting the Kitty keyboard protocol directly, outside tmux, to
test the held-key indicator. See the keyboard protocol
and tmux modifier-key troubleshooting. The application does
not modify terminal or tmux configuration.
The prompt starts at the terminal's current cursor row and follows replies, like a conventional command-line shell. If existing output ends mid-line, the prompt starts on the next line. There is no startup banner or persistent connection footer. Startup shows a metadata-loading status only after 200 ms or when you type, avoiding a flash on fast connections. The shell uses the normal terminal buffer, so earlier shell output and command replies remain in your terminal's scrollback, including after exit. Use your terminal's scrollback controls to review output.
Completion choices appear in a bordered window below the current argument.
The selected choice is highlighted; long menus scroll as you cycle through
them. Short status messages share the input line when it is empty. Help,
completion, and longer messages reserve space below the prompt only when
visible, scrolling as needed at the bottom of the terminal.
Closing a panel removes it and preserves the input. Errors, discovery progress,
and confirmations still appear when needed. The prompt is rmycli> for database
zero and rmycli[N]> otherwise.
History is bounded to 500 commands and is not saved to disk. Pasting text never submits a command. Output retention is controlled by your terminal's scrollback settings; the application does not keep a separate transcript.
Key completion
For example:
rmycli> LRANGE list:<Tab>
starts discovery on an independent connection with:
SCAN 0 MATCH list:* COUNT 128 TYPE list
Redis key specifications determine argument positions: fixed indexes,
keyword-based starts, key counts, strides, and ranges. Argument trees help
identify key positions in unfinished commands, including trailing timeouts
and stream IDs that have not been typed yet. Legacy first/last/step metadata
is supported. Unknown or incomplete specifications can fall back to
COMMAND GETKEYS when enough of the command has been entered for Redis to
extract its keys; this introspection does not execute the command.
Separate type hints cover lists, hashes, sets, sorted sets, streams, strings,
bitmaps, and HyperLogLogs. Commands accepting multiple types, overwrite
operations, and unknown module types use unfiltered discovery. Existing-key
suggestions never prevent entering a new destination key. If the server
rejects SCAN TYPE syntax, discovery retries without that filter and labels
the candidates as unfiltered.
Completion escapes glob metacharacters in the typed prefix before adding
*. It preserves key case and bytes, deduplicates batches, and continues past
empty batches until cursor zero or a budget is reached. It never uses KEYS.
- Zero matches after a complete scan leaves the input unchanged.
- One match after a complete scan inserts that key.
- Multiple matches open a popup; Tab and Shift-Tab cycle, Enter accepts, and Escape closes it.
- Above the display threshold, confirmation is required before showing keys.
- Partial scans say at least N matches (partial scan). Choose
yto show discovered keys,cto continue discovery, or Enter/Escape/nto cancel. A partial scan with one candidate still requires explicit acceptance.
Default limits and their options:
| Limit | Default | Option |
|---|---|---|
| Display confirmation threshold | 100 keys | --completion-threshold |
| Retained distinct keys | 10,000 | --scan-max-keys |
| Retained raw key bytes | 4 MiB | --scan-max-bytes |
| SCAN requests per discovery step | 32 | --scan-max-batches |
| Time per discovery step | 750 ms | --scan-ms |
| Connection / auxiliary socket timeout | 1,000 ms | --timeout-ms |
A continuation permits another threshold-sized page within the hard key/byte
limits. COUNT is only a Redis work hint. The client checks limits between
requests; an in-flight request can add up to its socket timeout, and a single
reply must be received before its retained-key budget can be checked. Editing
or pressing Escape cancels further requests; generation IDs discard stale
results. Network work runs outside the terminal input loop.
Reply-size guidance and session state
Before ordinary LRANGE execution, an auxiliary LLEN request estimates the
number of elements in the inclusive range, including negative indexes and
bounds. Above --reply-threshold (default 10,000), interactive mode asks:
Continue? [y/N] May return about 3000000 elements (not bytes; data can change).
Declining leaves the command unexecuted and available for editing. In
noninteractive mode, an oversized estimate causes an error before execution;
--yes explicitly bypasses the check. Unknown estimates, unsupported
commands, and failed probes remain unknown and permit manual execution.
These estimates are not memory limits or guarantees.
Discovery and estimation use separate connections with the active database
and credentials. They are paused during MULTI, WATCH, and streaming modes.
SELECT changes are tracked, including successful queued changes at EXEC;
DISCARD leaves the database unchanged. AUTH, HELLO AUTH, and RESET
update auxiliary connection settings. Probes never enter the user's transaction.
Commands queued inside transactions execute without reply-size confirmation.
Connection errors never trigger automatic command replay. Use \reconnect
explicitly; reconnecting starts a fresh session and does not restore transaction,
WATCH, subscription, or other connection-local state. Ctrl-C during execution
uses CLIENT ID and CLIENT KILL ID through the auxiliary connection to close
only this client's socket. If ACLs forbid cancellation, Ctrl-D exits the client.
Closing the socket cannot undo a command the server has already executed.
Compatibility and limits
- Redis 7+ provides the richest metadata. Integration tests run against Redis 8.0.2. Older metadata and ACL-restricted introspection use labeled bundled documentation and available legacy fields; they are tested with fixtures.
- A module must expose key specifications or argument documentation for
completion of an unfinished command.
COMMAND GETKEYSrequires sufficient arguments and permission. Missing metadata never prevents manual submission. - Some partial commands are intrinsically ambiguous: after
XREAD STREAMS k, the next word could be another key or the first ID. Suggestions are advisory, not command validation. Unrecognized option ordering may reduce argument hints. - A scan describes only the connected node and database.
-cfollows command redirects; slot-map routing and cluster-wide discovery are not implemented. Even a completed SCAN is not a snapshot of a changing keyspace. - Pub/Sub and MONITOR replies stream to the transcript. In interactive streaming mode, Ctrl-C ends the stream; reconnect before submitting further commands. Replication protocols and an interactive unsubscribe workflow are not provided.
- History is bounded and displayed output is released by the application, but
command replies are decoded in memory before display. The
LRANGEestimate is not a general reply-memory cap. - No persistent history, SSH tunnels, or full
redis-clioption compatibility is claimed by this version. See the option compatibility section above.
Development
The code separates byte parsing, command metadata and argument grammar, type
hints, scan discovery, estimation, session transport, workers, and the terminal.
Dependencies are redis for RESP and auxiliary connections, tokio for async
session I/O, and combine for the decoder state required by Redis's public
async parser. tokio-rustls, rustls, and rustls-native-certs provide TLS,
its crypto provider, and system trust roots. crossterm handles terminal I/O,
clap handles options, serde_json handles metadata, and unicode-width
handles cursor/layout calculations. toml and serde parse typed configuration
and reject unknown fields and invalid values;
xdg resolves configuration directories. Crossterm's Serde support validates
color values. toml_edit preserves comments during CLI edits, and tempfile
provides temporary files for atomic replacement.
Install redis-server for the isolated integration fixtures, which start their
own Unix-socket servers with persistence disabled. Tests never use an existing
Redis database. Before every commit, including documentation-only commits:
To exercise the actual executable, terminal editing, completion acceptance,
cancellation, streaming, and noninteractive policy, also install redis-cli
and Python 3, then run:
Use the 80-column rustfmt.toml configuration and keep Cargo.lock tracked.
See AGENTS.md, CHANGELOG.md, and the bundled help's
attribution and license.
Publishing
For your first release, sign in to crates.io, verify your
email address, create an API token with permission to publish, and run
cargo login. Paste the token at Cargo's prompt.
Before publishing, move the release's changelog entries from Unreleased into
a version heading dated YYYY-MM-DD, retaining an Unreleased section. Run the
formatting, Clippy, and test checks above and commit the release changes. Then:
Review the package file list before uploading. The manifest explicitly includes
source, bundled help and its license, tests, and project documentation; local
recordings are excluded. The dry run builds the packaged source without
uploading it. After a successful publish, tag the release commit (for example,
git tag v0.1.0). A published version cannot be overwritten; corrections need
a new version. See the Cargo publishing guide for details.
License
The application source code is licensed under the MIT License.
The bundled Redis command documentation in data/commands.json is separately
licensed under CC BY-SA 4.0; see its attribution and license.