Easily create private zsh sessions for SwayWM

Binding the creation of an historyless terminal to a simple keystroke

Today, we are going to see how to create historyless terminal sessions using zsh, sway and alacritty.

Rationale

Have you ever regretted entering some commands in your terminal and see them appear in command history afterwards? It can be passing passwords or other secrets as arguments to some script (which should use stdin or files for this), or interacting with some darkweb haxxor remotes?

I do often, and this is why I took the time to add a new shortcut to my swaywm setup so that by pressing a simple keystroke, a "private session" terminal will appear on the screen with history saving disabled.

This is inspired from the private tab feature of many major web browsers that we make use of everyday in order to not pollute the page history.

The result

I often like to see what an article can achieve before digging any further, so here is a screenshot of what I have achieved within something like one hour of my time.

Private terminal session

A purple status bar, like for private browsing, indicates an historyless session

Implementation details

For detailing the implementation I will aggregate some diffs of my dotfiles together with explanations of how it works.

Disabling the history file at zsh startup

We need to find a way to start zsh and tell it to disable the $HISTFILE variable. The fact of unsetting this will make the shell to not record any command into an history file.

We cannot just do something like unsetting the environment variable and then call zsh, because by reading the dotfiles it will recreate this variable. We have to find a way to tell our dotfiles to not set an history file.

Here is the little implementation that wrote:

diff --git a/zsh/.zsh/configs/40-history.zsh b/zsh/.zsh/configs/40-history.zsh
index 3025a80..1156768 100644
--- a/zsh/.zsh/configs/40-history.zsh
+++ b/zsh/.zsh/configs/40-history.zsh
@@ -1,6 +1,12 @@
-HISTFILE=~/.zhistory
-HISTSIZE=4096
-SAVEHIST=4096
+
+# Disable history if NOHISTFILE is passed via environment variable
+if [ ! -z $NOHISTFILE ] && $NOHISTFILE; then
+       unset HISTFILE
+else
+       HISTFILE=~/.zhistory
+       HISTSIZE=4096
+       SAVEHIST=4096
+fi

By passing an environment variable NOHISTFILE=true, the dotfiles will not only not set the $HISTFILE variable, but also unset it. This is required because at the start of the shell, $HISTFILE will be set by default and we need to explicitly unset it.

Now, by calling NOHISTFILE=true zsh, we can verify that the variable is correctly unset using echo $HISTFILE. We also do a quick test the other way around to verify that the history is still saved correctly when calling zsh without this environment variable.

Colorizing the zsh prompt

I am using a combination of oh-my-zsh and my custom agnoster2 theme in order to obtain the prompt that you can see in the screenshot.

We can now colorize the prompt if we detect that the $HISTFILE environment variable is not set.

diff --git a/oh-my-zsh-agnoster2/.oh-my-zsh/themes/agnoster2.zsh-theme b/oh-my-zsh-agnoster2/.oh-my-zsh/themes/agnoster2.zsh-theme
index f1ce181..297fa04 100644
--- a/oh-my-zsh-agnoster2/.oh-my-zsh/themes/agnoster2.zsh-theme
+++ b/oh-my-zsh-agnoster2/.oh-my-zsh/themes/agnoster2.zsh-theme
@@ -92,7 +92,12 @@ prompt_context() {
        if [[ -n "$SSH_CLIENT" ]]; then
                host="%{%F{yellow}%}@%m"
        fi
-       prompt_segment black default "$USER$host"
+
+       if [ -z $HISTFILE ]; then
+               prompt_segment magenta default "$USER@$host"
+       else
+               prompt_segment black default "$USER@$host"
+       fi
 }

It is important to check the $HISTFILE variable instead of the $NOHISTFILE one, because it allows us to check the effect instead of the cause of what we want to do.

If there is a bug in the zsh script that has to disable the $HISTFILE variable, we can immediatly see the impact by not obtaining a purple prompt, thus warning us that the shell history is still active for this session.

Adding the new shortcut in sway

Finally, we can add a new shortcut to start an alacritty terminal (which is defined as $TERMINAL so I can change terminal easily) that is passing this new environment variable to the shell.

diff --git a/sway/.config/sway/config b/sway/.config/sway/config
index ecf03a2..4ac94ec 100644
--- a/sway/.config/sway/config
+++ b/sway/.config/sway/config
@@ -87,6 +87,9 @@ floating_modifier $mod
 # start a terminal
 bindsym $mod+Return exec $TERMINAL

+# start a private terminal
+bindsym $mod+Shift+Return exec $TERMINAL -e dash -c "NOHISTFILE=true zsh"
+
 # kill focused window
 bindsym $mod+q kill

If the Shift key is pressed while I create a new terminal using my standard $mod+Enter shortcut, then the private terminal will pop in with a purple bar, indicating that unsetting the $HISTFILE has correctly worked.

I like to use dash instead of bash and sh. On Archlinux, sh is an alias of bash so we cannot benefit from the improved startup time if using sh in the command line or in script shebangs.

Caveats

Since the session is started without an history file, it is not possible to recall previous commands using ctrl+R shortcut. This can mitigated if, rather that not specifing an history file, we could use an option to tell zsh to use the history file in read only mode and forbid writes to it.

Une question ou remarque ? N'hésitez pas à me contacter en envoyant un mail à microjoe, suivi d'un arobase, puis encore microjoe et enfin un « point org ».