January 7, 2023·4 min read·engineeringrqlitecliconfiguration

Disabling rqlite CLI history now removes its file

rqlite 7.13.1 corrects the filesystem behavior of disabled CLI history. The small fix distinguishes retaining no commands from retaining no history file.

Setting a command-history limit to zero can leave two different outcomes on disk. A client can retain an empty history file, or it can remove the file altogether. rqlite's new persistent command history exposed that distinction almost immediately. In issue 1145, jamielinux reported that setting RQLITE_HISTFILESIZE=0 still created an empty .rqlite_history file. The report concerned version 7.13.0 and explicitly described a zero-byte file. It did not describe SQL commands leaking into a supposedly disabled history.

The 7.13.1 release fixes that behavior. The change is small enough to explain through its implementation, which also establishes the limits of what the setting promises. Disabling persistence now includes deleting the history file during normal CLI shutdown. It does not mean that the process never opens a history file at any point during its lifetime.

I value rqlite's proportionate approach to operating a relational database. That preference extends to its administrative tools. An environment variable that disables a convenience should have a predictable effect on the files that convenience creates, especially when the correction requires little additional machinery.

How the empty file appeared

The versioned changelog records persistent CLI history as a new feature in 7.13.0, released in December 2022. The January bug report therefore concerns the behavior of a recently introduced capability. Remembering commands across sessions makes repeated interactive work easier, but it also introduces local state outside the database.

The history implementation separates the size limit from writing commands. Size() begins with a default limit of 100 commands and reads RQLITE_HISTFILESIZE from the environment. An integer value supplies the configured limit. Write() returns without writing commands when that limit is zero or negative. At this level, zero already prevents command output.

Opening the destination is a separate operation. The non-Windows file helper opens the history path with os.O_RDWR|os.O_CREATE. Creating a file does not require subsequently writing bytes into it. A caller can therefore obtain a writer, pass it to a function that writes nothing, close it, and still leave a new directory entry behind. That is the behavior the report identified.

The distinction matters when interpreting the patch. A zero-length file demonstrates that the command-writing limit worked for the reported session. It also demonstrates that the surrounding file lifecycle did not match the reporter's expectation. Calling this a failure to suppress command contents would overstate the evidence and obscure the actual repair.

Cleanup belongs to the CLI lifecycle

Pull request 1146, merged on January 4, adds the missing cleanup. The CLI source in the release obtains the writer and history size when the interactive loop ends. It writes the retained history and closes the writer. If the size is nonpositive, it then calls history.Delete().

The deletion helper resolves the same history path and calls os.Remove. The path implementation places .rqlite_history in the user's home directory. This makes deletion apply to the CLI user's local history file. It does not operate on a database file or on another cluster member's storage.

A session can request the behavior with an environment variable scoped to the client invocation. The following command uses the shell's env utility and leaves the client's ordinary connection defaults in place.

env RQLITE_HISTFILESIZE=0 rqlite

The setting belongs on the rqlite client process. Putting it only into the environment of rqlited does not configure a separately launched client. The patch changes the interactive client's exit path, so the relevant upgrade is the CLI binary used for that session.

The implementation also places useful limits on the interpretation of disabled history. Startup still attempts to read existing history before the interactive loop. Cleanup happens after the loop ends. Consequently, the patch should not be described as preventing every history-file read or as guaranteeing cleanup after abrupt process termination. Those stronger properties would need different control flow.

What remains useful about retained history

For sessions that retain commands, the implementation applies a defined sequence of transformations. It filters empty and whitespace-only entries, then removes consecutive duplicates. If the remaining command count exceeds the configured maximum, it keeps the final entries. The history tests include cases for filtering blank entries, preserving separated repetitions, truncating to the final two commands, and writing nothing at a zero limit.

Consecutive deduplication has a narrower meaning than global deduplication. A command repeated immediately can collapse into one entry. The same command separated by another command remains part of the sequence. That preserves some context about interactive work while reducing repetition. It also means the configured limit describes the processed command list, rather than every line originally accumulated in memory.

I prefer the zero setting to remove the persisted file because it makes the resulting local state easier to inspect. That judgment does not require treating ordinary command history as a database audit log. This code filters entries and suppresses repetitions, so its purpose is interactive recall. Anyone needing a complete record of database operations needs a mechanism with an explicit completeness contract.

The patch leaves that distinction intact. Positive limits continue to support command recall, while a nonpositive limit requests no retained history and triggers file removal on normal exit. For this release, the practical change is a more complete implementation of the existing configuration choice, at the point where the CLI finishes using its local history file.