Skip to content

Hosting Remote Sessions

Repl can host multiple concurrent sessions over any transport that delivers a stream pair — a reader and a writer. Each session gets its own scope, history, and context; shared state is opt-in via DI singletons.

The built-in packages cover the most common cases:

  • Repl.WebSocket — WebSocket transport, served from ASP.NET Core. Good for browser clients, reverse-proxy friendly.
  • Repl.Telnet — Telnet session protocol (framing + NAWS terminal-size negotiation) layered on top of a WebSocket transport. For terminal emulators and Telnet clients.

Any other transport — SSH, named pipes, TCP sockets, gRPC streams — works by instantiating StreamedReplHost with a TextWriter, pushing received text via EnqueueInput, and calling RunSessionAsync.

Add Repl.WebSocket and wire it into ASP.NET Core:

using Repl.WebSocket;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddReplWebSocket();
// register shared services — must be thread-safe (see caution below)
builder.Services.AddSingleton<IContactStore, ContactStore>();
var app = builder.Build();
app.UseWebSockets();
app.MapReplWebSocket("/repl"); // WebSocket endpoint
// define command graph
var repl = app.Services.GetRequiredService<IReplApp>();
repl.Context("client", client =>
{
client.Map("list", (IContactStore store) => store.All());
// ...
});
app.Run();

Connect with any WebSocket client (browser, wscat, terminal emulator).

Repl.Telnet layers Telnet framing and NAWS window-size negotiation on top of the WebSocket transport. The network layer is still WebSocket; Telnet is the session protocol above it:

using Repl.Telnet;
app.MapReplTelnet("/telnet");

Connect with a Telnet-to-WebSocket bridge or a terminal emulator that supports WebSocket:

Terminal window
wscat -c ws://localhost:5000/telnet

Terminal size and type are negotiated automatically via NAWS (RFC 1073) and TERMINAL-TYPE (RFC 1091).

Each session carries terminal metadata — capabilities and window size, injectable via ITerminalInfo (namespace Repl.Interaction):

using Repl.Interaction;
app.Map("info", (ITerminalInfo terminal) => new
{
terminal.IsAnsiSupported,
terminal.CanReadKeys,
terminal.WindowSize,
});

Handlers can adapt output to the terminal width, or degrade gracefully when ANSI is unavailable.

RegistrationScope
AddSingleton<T>Shared across all sessions
AddScoped<T>One instance per session
AddTransient<T>New instance per command invocation

Use AddScoped<T> for per-user state (shopping cart, selection, cursor position).

When the host process receives a shutdown signal, active sessions drain gracefully. CancellationToken is cancelled in all running handlers, giving them time to complete or clean up before the process exits.