Drafter.Server (drafter v0.5.0)

Copy Markdown View Source

Start SSH and Telnet servers for hosting TUI applications over the network.

Each connecting client gets their own session. Two modes are supported:

  • :isolated (default) — each client runs an independent app instance with its own state. Ideal for single-user tools, games, and dashboards.

  • :shared — all clients share the same app state. Input from any client mutates the shared state and all clients re-render. Ideal for collaborative apps, multi-player games, and shared dashboards.

SSH Example

Drafter.Server.start_ssh(ChatApp,
  port: 2222,
  mode: :shared,
  auth: [{"alice", "pass1"}, {"bob", "pass2"}]
)

Telnet Example

Drafter.Server.start_telnet(MyApp, port: 2323, mode: :isolated)

Generating SSH host keys

If no system_dir: is provided, host keys are automatically generated and cached in a temp directory. For production use, generate persistent keys:

ssh-keygen -t rsa -b 2048 -f /etc/drafter/ssh_host_rsa_key -N ""

Then pass system_dir: "/etc/drafter".

Summary

Functions

Starts an SSH server hosting app_module, linked to the caller.

Starts a Telnet server hosting app_module, linked to the caller.

Stops what start_ssh/2 returned: one listener, or every listener of a list. Each connected session ends first, its client's terminal restored and notice printed on it (default "The server closed this session."), then its connection closes. Returns once every session has ended, so call it from a process the sessions do not wait on as they end.

Closes the port of what start_ssh/2 returned, so it can be listened on again at once, leaving the sessions already connected running until stop_ssh/2.

Functions

start_ssh(app_module, opts \\ [])

@spec start_ssh(
  module(),
  keyword()
) :: {:ok, pid() | [pid()]} | {:error, term()}

Starts an SSH server hosting app_module, linked to the caller.

app_module is a module that does use Drafter.App. Returns {:ok, pid} for the listener — {:ok, [pid]} when :ip is a list — or {:error, reason}; stop_ssh/1 takes either. The listener runs until it is stopped; each client that connects gets its own session process.

Options

  • :port - TCP port. Default 2222.
  • :ip - what to bind: an IPv4 or IPv6 address tuple, {0, 0, 0, 0} for every IPv4 interface, {0, 0, 0, 0, 0, 0, 0, 0} for every IPv6 interface, :any for both families on every interface, or a list of these — say two of a machine's six addresses — each bound by a daemon of its own on the same port. Default {127, 0, 0, 1}, which accepts only local connections.
  • :mode - :isolated (default) gives each client its own app state; :shared runs every client against one shared state.
  • :auth - [{username, password}] tuples for password authentication, or the atom :anonymous to accept any username and password. Default [{"admin", "admin"}].
  • :system_dir - path to the directory holding the SSH host keys. Default: a drafter_ssh directory under the system temp directory, with RSA host keys generated by ssh-keygen on first use.
  • :mount_props - map() handed to each session's mount/1. Default %{}. The authenticated username is added to it under :username as a string, so every SSH session's mount props carry that key whether or not you set it. An :auth entry of the form {username, password, props} merges its props map in as well, for that user only.
  • :tunnel - boolean(), default false. true accepts ssh -R from clients: the daemon listens on the port the client names and forwards each connection to it back over the ssh connection.
  • :register_as - with auth: {:accounts, server}, a username that, with any password, opens the registration form instead of a session; the account it makes logs in from then on. Default nil: nobody registers over ssh.
  • :register_app - {module, props}, the app that form is. Default {Drafter.Accounts.RegisterApp, %{}}.
  • :send_buffer - bytes of the operating system's send buffer for each connection. Output beyond it waits, so a session drawing faster than its connection carries skips frames instead of queueing them. Default nil, the system's own, which may grow to megabytes and hold seconds of frames on a slow connection.
  • :login_timeout - milliseconds a client has from connecting to logging in, answering ssh's host key question and typing the password included; past it the connection is closed. Default nil, :ssh's two minutes.

start_telnet(app_module, opts \\ [])

@spec start_telnet(
  module(),
  keyword()
) :: {:ok, pid()}

Starts a Telnet server hosting app_module, linked to the caller.

app_module is a module that does use Drafter.App. Always returns {:ok, pid} for the acceptor process, which is linked to the caller. The socket is opened inside that process, so a port already in use surfaces as an exit from the acceptor rather than an {:error, reason} return. Each client that connects gets its own session process.

Telnet carries no authentication or encryption; bind it only where that is acceptable.

Options

  • :port - TCP port. Default 2323. Bound on all interfaces.
  • :mode - :isolated (default) gives each client its own app state; :shared runs every client against one shared state.
  • :mount_props - map() handed to each session's mount/1. Default %{}.

stop_ssh(listener)

@spec stop_ssh(pid() | [pid()]) :: :ok

Stops what start_ssh/2 returned: one listener, or every listener of a list. Each connected session ends first, its client's terminal restored and notice printed on it (default "The server closed this session."), then its connection closes. Returns once every session has ended, so call it from a process the sessions do not wait on as they end.

stop_ssh(listener, notice)

@spec stop_ssh(pid() | [pid()], String.t()) :: :ok

stop_ssh_listening(listener)

@spec stop_ssh_listening(pid() | [pid()]) :: :ok

Closes the port of what start_ssh/2 returned, so it can be listened on again at once, leaving the sessions already connected running until stop_ssh/2.