CliSubprocessCore.Channel is the framed-IO layer above CliSubprocessCore.RawSession. Use it when you need a long-lived subprocess handle with stable mailbox delivery, but you do not want provider-aware parsing from CliSubprocessCore.Session.

When To Use A Channel

Use Channel when you need:

  • long-lived stdin/stdout/stderr ownership
  • stable tagged mailbox delivery without carrying a raw transport ref around
  • the same local-versus-SSH execution surface used by the lower transport APIs

If you only need one-shot execution, use CliSubprocessCore.Command.run/1,2 or ExecutionPlane.Process.Transport.run/2. If you want provider parsing and normalized CliSubprocessCore.Event values, use CliSubprocessCore.Session.

Start A Channel

ref = make_ref()

{:ok, channel, info} =
  CliSubprocessCore.Channel.start_channel(
    command: "sh",
    args: ["-c", "cat"],
    subscriber: {self(), ref},
    stdout_mode: :raw,
    stdin_mode: :raw
  )

IO.inspect(info.delivery)

:ok = CliSubprocessCore.Channel.send_input(channel, "alpha")
:ok = CliSubprocessCore.Channel.close_input(channel)

receive do
  message ->
    case CliSubprocessCore.Channel.extract_event(message, ref) do
      {:ok, {:data, chunk}} -> IO.inspect(chunk)
      {:ok, {:exit, exit}} -> IO.inspect(exit.code)
      :error -> :ignore
    end
end

start_channel/1 returns the pid plus the initial info snapshot. start_link_channel/1 returns the same snapshot while keeping the channel linked to the caller.

Mailbox Contract

Legacy subscribers receive:

{:channel_message, line}
{:channel_data, chunk}
{:channel_stderr, chunk}
{:channel_exit, exit}
{:channel_error, reason}

Tagged subscribers receive:

{:cli_subprocess_core_channel, ref, {:message, line}}
{:cli_subprocess_core_channel, ref, {:data, chunk}}
{:cli_subprocess_core_channel, ref, {:stderr, chunk}}
{:cli_subprocess_core_channel, ref, {:exit, exit}}
{:cli_subprocess_core_channel, ref, {:error, reason}}

Use CliSubprocessCore.Channel.extract_event/2 instead of matching on the outer event atom directly.

Delivery And Metadata

CliSubprocessCore.Channel.info/1 returns:

  • delivery metadata for the effective mailbox contract
  • the normalized invocation
  • subscriber count
  • the underlying raw-session info
  • the transport snapshot, including surface_kind, stderr tail, and adapter metadata

CliSubprocessCore.Channel.delivery_info/1 is the shortest path to the stable tagged-delivery contract.

SSH Surfaces

Channels use the same generic execution-surface options as the transport layer. For an SSH execution surface:

{:ok, channel, _info} =
  CliSubprocessCore.Channel.start_channel(
    command: "sh",
    args: ["-c", "cat"],
    subscriber: {self(), make_ref()},
    stdout_mode: :raw,
    stdin_mode: :raw,
    execution_surface: [
      surface_kind: :ssh_exec,
      transport_options: [
        destination: "channel.test.example",
        ssh_user: "deploy",
        port: 22
      ]
    ]
  )

The core still resolves the SSH adapter internally. Callers stay on one canonical execution_surface.