.--~~~~~~~~~~~~~------.
                           /--===============------\
                           | |```````````````|     |
                           | |               |     |
                           | |      >_<      |     |
                           | |               |     |
                           | |_______________|     |
                           |                   ::::|
                           '======================='
                           //-"-"-"-"-"-"-"-"-"-"-\\
                          //_"_"_"_"_"_"_"_"_"_"_"_\\
                          [-------------------------]
                          \_________________________/


                          Secure Shell Developer Guide

Introduction

Secure Shell is a Chrome App (currently a “v1.5” app, soon to become a “v2” or Platform App) that combines hterm with a NaCl build of OpenSSH to provide a PuTTY-like app for Chrome users.

See /HACK.md for general information about working with the source control setup.

Building the dependencies

The Secure Shell app depends on some library code from libapps/libdot/ and the hterm terminal emulator from in libapps/hterm/. To build these external dependencies, run...

nassh$ ./bin/mkdeps.sh

This will create the nassh/js/nassh_deps.concat.js file containing all of the necessary libdot and hterm source. If you find yourself changing a lot of libdot or hterm code and testing those changes in Secure Shell you can run this script with the “--forever” (aka -f) option. When run in this manner it will automatically re-create nassh_deps.concat.js file whenever one of the source files is changed.

The NaCl plugin dependency

Secure Shell depends on a NaCl (Native Client) plugin to function. This plugin is a port of OpenSSH. You'll have to find or create a version of this plugin, and copy it into libapps/nassh/plugin/.

Your options are:

  1. Build it yourself from ssh_client.

  2. Copy it from plugin/ directory of the latest version of Secure Shell. If you have Secure Shell installed, the plugin can be found in your profile directory, under Default/Extensions/pnhechapfaindjhompbnflcldabbghjo/<version>/plugin/.

Dev-Test cycle

The ./bin/run_local.sh script can be used to launch a new instance of Chrome in an isolated profile, with the necessary command line arguments, and launch the Secure Shell app. You can run this script again to rebuild dependencies and relaunch the Secure Shell app.

Source Layout

Keep in mind that the NaCl ssh_client code does not live here.

The vast majority of the code here lives under js/.

JavaScript Source Layout

There are a few specialized modules that are not relevant to the core Secure Shell logic.

NaCl/JS Life cycle

When the extension is launched (e.g. a new connection is opened), the background page is automatically created. This is used to monitor global state like extension updates and coordinate SFTP mounts. The logic lives in nassh_background.js and takes care of creating a new instance of nassh.App which it saves in the background page‘s app variable. If you aren’t looking at the SFTP logic, you can probably ignore this code.

When the extension is run, a new nassh.html window is shown. If no connection info is provided via the URL, then an iframe is created to show nassh_connect_dialog.html. Here the user manages their saved list of connections and picks the one they want to connect to. This logic is in nassh_connect_dialog.js. Once the user makes a selection (either connecting or mounting), a message is sent to nassh_command_instance.js. There the connection dialog is closed, the NaCl plugin is loaded, and the streams are connected to hterm.

JS->NaCl API

Here is the API that the JS code uses to communicate with the NaCl ssh_client module.

The nassh.CommandInstance.prototype.sendToPlugin_ function in nassh_command_instance.js is used to package up and make all the calls. Helper functions are also provided in that file to avoid a JS API to callers.

At the lowest level, we pass a JSON string to the plugin. It has two fields, both of which must be specified (even if arguments is just []).

  • name: The function we want to call (as a string).
  • arguments: An array of arguments to the function.

The name field can be any one of:

Function nameDescriptionArguments
startSessionStart a new ssh connection!(object session)
onOpenFileOpen a new file.(int fd, bool success, bool is_atty)
onOpenSocketOpen a new socket.(int fd, bool success, bool is_atty)
onReadSend new data to the plugin.(int fd, base64 data)
onWriteAcknowledgeTell plugin we've read data.(int fd, int count)
onCloseClose an existing fd.(int fd)
onReadReadyNotify plugin data is available.(int fd, bool result)
onResizeNotify terminal size changes.(int width, int height)
onExitAcknowledgeUsed to quit the plugin.()

The session object currently has these members:

  • str username: Username for accessing the remote system.
  • str host: Hostname for accessing the remote system.
  • int port: Port number for accessing the remote system.
  • int terminalWidth: Initial width of the terminal window.
  • int terminalHeight: Initial height of the terminal window.
  • bool useJsSocket: Whether to use JS for network traffic.
  • object environment: A key/value object of environment variables.
  • array arguments: Extra command line options for ssh.
  • int writeWindow: Size of the write window.
  • str authAgentAppID: Extension id to use as the ssh-agent.

NaCl->JS API

Here is the API that the NaCl ssh_client code uses to communicate with the JS layers.

At the lowest level, we pass a JSON string to the JS code. It has two fields, both of which must be specified (even if arguments is just []).

  • name: The function we want to call (as a string).
  • arguments: An array of arguments to the function.

The name field can be any one of:

Function nameDescriptionArguments
openFilePlugin wants to open a file.(int fd, str path, int mode)
openSocketPlugin wants to open a socket.(int fd, str host, int port)
readPlugin wants to read data.(int fd, int count)
writePlugin wants to write data.(int fd, base64 data)
closePlugin wants to close an fd.(int fd)
isReadReadyPlugin wants to know read status.(int fd)
exitThe plugin is exiting.(int code)
printLogSend a string to console.log.(str str)