# `Drafter.Server`
[🔗](https://github.com/jaman/drafter/blob/main/lib/drafter/server.ex#L1)

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"`.

# `start_ssh`

```elixir
@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, %{}}`.

# `start_telnet`

```elixir
@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`

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

Stops what `start_ssh/2` returned: one listener, or every listener of a list.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
