Reference

REPL syntax

RemoteREPL syntax is just normal Julia REPL syntax, the only minor difference is that ?expr produces help for expr, but we don't have a separate help mode for this.

Security considerations

Note that any logged-in users on the client or server machines can execute arbitrary commands in the serve_repl() process. For this reason, you should avoid using RemoteREPL on shared infrastructure like compute clusters if you don't trust other users on the system. (In the future perhaps we can avoid this by forwarding between socket files?)

This package uses an SSH tunnel by default to forward traffic when host != Sockets.localhost, so it should be quite secure to use over an open network. If both client and server are on a secure network, it's possible to skip the tunnel to avoid setting up SSH. However, if anyone breaks into your network you'll be left with no security whatsoever.

TLDR; this package aims to provide safe defaults for single-user machines. However, do not expose the RemoteREPL port to an open network. Abitrary remote code execution is the main feature provided by this package!

Interrupting remote evaluation

When the RemoteREPL client is waiting on a response, it will catch InterruptException and forward it to the server as an interruption message. This allows blocking operations such as IO to be interrupted safely.

However, this doesn't work for non-yielding operations such as tight computational loops. For these cases, pressing Control-C three times will disconnect from the server, leaving the remote operation still running.

API reference

RemoteREPL.connect_replFunction
connect_repl([host=localhost,] port::Integer=27754;
             use_ssh_tunnel = (host != localhost) ? :ssh : :none,
             ssh_opts = ``, repl=Base.active_repl)

Connect client REPL to a remote host on port. This is then accessible as a remote sub-repl of the current Julia session.

For security, connect_repl() uses an ssh tunnel for remote hosts. This means that host needs to be running an ssh server and you need ssh credentials set up for use on that host. For secure networks this can be disabled by setting tunnel=:none.

To provide extra options to SSH, you may pass a Cmd object in the ssh_opts keyword, for example an identity file may be set with ssh_opts = `-i /path/to/identity.pem`. For a more permanent solution, add a Host section to your ssh config file.

You can also use the following technologies for tunneling in place of SSH:

  1. AWS Session Manager: set tunnel=:aws. The optional region keyword argument can be used to specify the AWS Region of your server.
  2. kubectl: set tunnel=:k8s. The optional namespace keyword argument can be used to specify the namespace of your Kubernetes resource.

See README.md for more information.

RemoteREPL.serve_replFunction
serve_repl([address=Sockets.localhost,] port=27754; [on_client_connect=nothing])
serve_repl(server)

Start a REPL server listening on interface address and port. In normal operation serve_repl() serves REPL clients indefinitely (ie., it does not return), so you will generally want to launch it using @async serve_repl() to do other useful work at the same time.

The hook on_client_connect may be supplied to modify the ServerSideSession for a client after each client connects. This can be used to define the default module in which the client evaluates commands.

If you want to be able to stop the server you can pass an already-listening server object (the result of Sockets.listen()). The server can then be cancelled from another task using close(server) as necessary to control the server lifetime.

Security

serve_repl() uses an unauthenticated, unecrypted protocol so it should not be used on open networks or multi-user machines where other users aren't trusted. For open networks, use the default address=Sockets.localhost and the automatic ssh tunnel support provided by the client-side connect_repl().

RemoteREPL.connect_remoteFunction
connect_remote([host=localhost,] port::Integer=27754;
             tunnel = (host != localhost) ? :ssh : :none,
             ssh_opts = ``)

Connect to remote server without any REPL integrations. This will allow you to use @remote, but not the REPL mode. Useful in circumstances where no REPL is available, but interactivity is desired like Jupyter or Pluto notebooks. Otherwise, see connect_repl.

RemoteREPL.@remoteMacro
@remote ex

Execute expression ex on the other side of the current RemoteREPL connection and return the value.

This can be used in both directions:

  1. From the normal julia> prompt, execute ex on the remote server and return the value to the client.
  2. From a remote prompt, execute ex on the client and push the resulting value to the remote server.

Examples

Push a value from the client to the server:

julia> client_val = 1:100;

julia@localhost> server_val = @remote client_val
1:100

Fetch a pair of variables (x,y) from the server, and plot them on the client with a single line:

# In two lines
julia> x,y = @remote (x, y)
       plot(x, y)

# Or as a single expression
julia> plot(@remote((x, y))...)
RemoteREPL.remote_evalFunction
remote_eval(cmdstr)
remote_eval(host, port, cmdstr)

Parse a string cmdstr, evaluate it in the remote REPL server's Main module, then close the connection. Returns the result which the REPL would normally pass to show() (likely a Text object).

For example, to cause the remote Julia instance to exit, you could use

using RemoteREPL
RemoteREPL.remote_eval("exit()")