App::BCVI - Back-channel vi, a shell utility to proxy commands back over ssh


The bcvi utility works with SSH to allow commands issued on the SSH server host to be sent back to the SSH client host over a port-forwarded 'back channel'. A few examples might help clarify how bcvi is used (note you can read an illustrated version of the following examples at:

Example 1

A user 'sally' opens a gnome-terminal window on her workstation and uses the SSH command to log in to the host 'pluto'. She then types a command to edit a file:

  ~$ ssh pluto
  sally@pluto:~$ vi .bashrc

Through the magic of bcvi, the result is that the file is opened for editing in a 'gvim' editor window on Sally's workstation. Note, this does not use X-forwarding. The GUI editor process is running on Sally's workstation. The file is copied transparently to and from the server pluto using scp (via gvim's 'netrw' network transport layer).

Compared to running vim on the remote server in the terminal window, bcvi provides these advantages to Sally:

  • gvim on Sally's workstation has all her preferred key mappings, custom macros, plugins and scripts

  • gvim is a GUI app that responds to mouse input for scrolling, selecting text, copying and pasting

  • gvim knows when Sally is pasting so it disables autoindent automatically and avoids the dreaded stair-step effect often seen when pasting into vim in a terminal window

  • because gvim is running locally (rather than via X-forwarding) the application loads quickly and is responsive to user input

  • no GUI apps or libraries need to be installed on the server*

*You might argue that bcvi itself will need to be installed on the server, but Sally can do that from her workstation with one simple command (and no need for root access):

  ~$ bcvi --install pluto
  Creating ~/bin directory on pluto
  Copying bcvi to remote bin directory on pluto
  Creating plugins directory on pluto
  Copying plugin files to pluto
  Added bcvi commands to /home/sally/.bashrc

How Example 1 Worked

The bcvi utility was not responsible for copying files to and from the server 'pluto' (gvim can already do that). Rather, bcvi was used to establish a communications channel from 'pluto' back to Sally's workstation. This back channel was used to send a message triggering the launching of gvim and the loading of the specified file.

The example above assumed:

  • a bcvi 'listener' process had been launched by Sally's X session startup scripts

  • the 'ssh' command used to connect to 'pluto' was actually a shell alias that set up the environment and invoked the real ssh command with additional parameters for port forwarding the 'back channel'

  • Sally's login script on 'pluto' invoked bcvi to unpack the environment and set up the required authentication key

  • the 'vi' command used to edit the file on pluto was actually a shell alias that invoked bcvi to pass a message over the backchannel to the listener process on Sally's workstation

  • the listener process unpacked the message to extract the hostname and filename information needed to launch this command:

      gvim scp://pluto//home/sally/.bashrc

For more information on setting up the listener and aliases, see "INSTALLATION" below.

Example 2

Our friend Sally is logged on to the server 'pluto' and is trying to configure the 'Acme CRM' package. She explores the filesystem and locates a useful file in the documentation directory:

  sally@pluto:~$ cd /usr/share/doc/acmecrm/
  sally@pluto:acmecrm$ ls
  changelog.Debian.gz  copyright    README
  changelog.gz         manual.pdf   README.Debian
  sally@pluto:acmecrm$ bcp manual.pdf

In the final command above, Sally used the bcp command to copy the PDF file back to the desktop on her workstation. Then she was able to simply double-click the desktop icon to open it in her PDF viewer.

How Example 2 Worked

This second example used all the same infrastructure as the first (listener process, shell aliases and port forward) but added the command bcp. Once again, this is a shell alias that invokes bcvi to send a message back to the listener process. The only difference is that this time the message instructs the listener process to run this command:

  scp -q pluto:/usr/share/doc/acmecrm/manual.pdf /home/sally/Desktop

Note, for security reasons, the bcvi process running on pluto is not allowed to specify the command that gets executed on the workstation. It simply sends a request which includes hostname and filename details. The listener process determines which types of requests it will accept and which commands it will run to handle them.

Example 3

Sally is now making progress setting up the Acme CRM package. The next step is to restore a database dump. This will take some time and Sally has other things to get on with so she kicks off this command (actually, two commands separated by a semicolon):

  sally@pluto:~$ pg_restore -d acmecrm crm.pgdump; bnotify 'DB is restored!'

Sally then minimises her shell/ssh window and gets on with some other important work. Some minutes later, a desktop notification window pops up on her screen:

  | Notification from pluto |
  | DB is restored!         |

Sally can now return to her number one priority - completing the set up of the Acme CRM software on pluto.

How Example 3 Worked

Once again, this example used all the same back channel infrastructure used by the previous examples, but this one also used bcvi plugins.

The bcvi script itself requires no extra CPAN modules, but the interface to the desktop notifications API requires the Desktop::Notify module from CPAN. It also requires a small 'plugin' module to provide the glue between the listener process and the additional modules. Plugins are described in more detail in App::BCVI::Plugins.


The bcvi program is a standalone script with no companion modules and no non-core dependencies. To install it, simply copy the bin/bcvi file from the distribution to a directory in your search PATH. Alternatively, you can use the standard CPAN installation procedure to install the script to your site bin directory:

    perl Makefile.PL
    make test
    make install

The 'back channel' protocol requires a client and a server - the bcvi script performs both roles. The server runs on your workstation and is typically launched by adding a command to your X session startup. For example under Ubuntu/GNOME you might use the 'System' menu and select Preferences > Startup Applications and then use the 'Add' button to add this command:

    bcvi --listener

If you start a listener manually from a shell window you will want to append an ampersand (&) to put the command in the background.

When connecting to a server you will want to use this command to wrap the SSH command and add the required port forwarding options:

    bcvi --wrap-ssh -- hostname

It is probably more convenient to set up an alias so that this happens on every SSH connection. Use this command to add the appropriate aliases to your bash startup scripts:

    bcvi --add-aliases

Now that you have the server set up and ssh connection wrapping in place, you need to install bcvi on the machine you will ssh to:

    bcvi --install HOSTNAME

At this point it should all work. When you log in to the machine using SSH, a number of shell aliases will be available to you:


Invokes gvim on your workstation, passing it an scp://... URL of the file(s) you wish to edit


Same as above, but uses sudoedit so system files (requiring root access) can be edited too


Copies the named file back to your workstation desktop

Note: you may like to try SSHMenu ( which can invoke the ssh wrapper automatically when connecting to servers.


If you successfully followed the installation instructions above, you can probably skip this section.

When the listener process starts, it generates a random authentication key which is saved in the file: $HOME/.config/bcvi/listener_key

The process id of the listener is saved in $HOME/.config/bcvi/listener_pid. If you start a new listener, it will automatically kill off the old one.

The listener process then opens a local TCP port (by default, your user ID, with a 9 appended, but you can use --port to override it), saves the port number in $HOME/.config/bcvi/listener_port and waits for incoming connections.

When you initiate an SSH connection using the shell alias, a command like this is generated:

  ssh -R 10569:localhost:10569 HOSTNAME

The first port number is the local port that the listener will accept connections on. The second port number is the port on the remote machine that the bcvi client will connect to and which SSH will forward back to the listener. You can override the second port number when you connect. The first port number will be read from the listener_port file.

The remote host needs to know three things in order to use the back channel:

  • The hostname/FQDN that the server is known by from the originating workstation's perspective

  • The port number on the server that SSH will forward back to the listener

  • The random authentication key from the listener_key_file

The ssh wrapper command arranges for these pieces of information to be forwarded to the remote host. If you don't want to know how it does that then please skip the rest of this paragraph. WARNING: It's not pretty. OK, so you really want to know? Don't say I didn't warn you. SSH does not normally pass environment variables from client to server unless you customise the ssh config files on the client and the server. However, SSH does pass the TERM variable. So, bcvi appends all the extra info to the end of the TERM variable before invoking SSH. This 'overstuffed' TERM variable then needs to be unpacked by the user's shell startup script on the server. If this is not done, then your term variable will be wrong and you'll need to set it manually before editing your .profile to fix it.

Unpacking the environment is achieved by running bcvi with the --unpack-term option to generate a few lines of Bash script. Those lines then need to be eval'd in the shell. The standard installation procedure achieves this by adding this line to your shell startup script:

  test -n "$(which bcvi)" && eval "$(bcvi --unpack-term)"

This line assumes that bcvi is in your path. Normally bcvi will be in your $HOME/bin directory and normally this will be in your $PATH, but it's something to check if things go wrong.

The standard installation will also set up the shell aliases listed above, notably vi, suvi and bcp, however plugin modules can install additional aliases.

When one of these aliases is invoked, bcvi connects to the listener via the port-forward and sends a request similar to this:

  Auth-Key: 90a5aa7b5d55159b92828d4ba955fe75
  Host-Alias: pluto
  Command: vi
  Content-Length: 20

The wire protocol is intended to be UTF8 encoded with Content-Length specified in bytes rather than characters.


The bcvi script includes built-in documentation which you can access with this command:

  bcvi --help

The documentation displayed will be customised to describe all options and commands available - including those provided by plugin modules.

This documentation and more details on plugins are available via:

    perldoc App::BCVI
    perldoc App::BCVI::Plugins

You can also refer to:


Copyright 2007-2012 Grant McLean <grantm at>

This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.