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.
WebSocket transport (Repl.WebSocket)
Section titled “WebSocket transport (Repl.WebSocket)”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 graphvar 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).
Telnet session protocol (Repl.Telnet)
Section titled “Telnet session protocol (Repl.Telnet)”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:
wscat -c ws://localhost:5000/telnetTerminal size and type are negotiated automatically via NAWS (RFC 1073) and TERMINAL-TYPE (RFC 1091).
Terminal metadata
Section titled “Terminal metadata”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.
Shared vs. per-session state
Section titled “Shared vs. per-session state”| Registration | Scope |
|---|---|
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).
Graceful shutdown
Section titled “Graceful shutdown”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.
Further reading
Section titled “Further reading”- Dependency Injection — service lifetimes and how
AddScopedmaps to per-session state in hosted sessions. - Terminal Integration — terminal capability detection, ANSI support, and window-size negotiation.
- Pipelining & Lifecycle — hosted service integration and
ReplRunOptions.