ReverseTunnel.Yarp
0.6.0
See the version list below for details.
dotnet add package ReverseTunnel.Yarp --version 0.6.0
NuGet\Install-Package ReverseTunnel.Yarp -Version 0.6.0
<PackageReference Include="ReverseTunnel.Yarp" Version="0.6.0" />
<PackageVersion Include="ReverseTunnel.Yarp" Version="0.6.0" />
<PackageReference Include="ReverseTunnel.Yarp" />
paket add ReverseTunnel.Yarp --version 0.6.0
#r "nuget: ReverseTunnel.Yarp, 0.6.0"
#:package ReverseTunnel.Yarp@0.6.0
#addin nuget:?package=ReverseTunnel.Yarp&version=0.6.0
#tool nuget:?package=ReverseTunnel.Yarp&version=0.6.0
<div align="center">
<img src="ReverseTunnel.Yarp.Logo.png" width="220" alt="ReverseTunnel.Yarp Logo" />
<h3><b>Active Reverse Tunnel for YARP (ARTY)</b></h3> <i > Outbound-only secure connectivity for ASP.NET Core </i> <br/> <br/>
</div>
Note: This is a fork of UnifiedFX/UFX.Relay, rebranded and enhanced for broader community use.
Active Reverse Tunnel for YARP (ARTY)
Overview
ReverseTunnel.Yarp connects two ASP.NET Core Middleware pipelines using a single WebSocket connection, extending a cloud application to an on-premise application instance. This is similar to services like ngrok, but rather than requiring an external 3rd party service, ReverseTunnel.Yarp is a self-contained pure ASP.NET Core solution.
The Server/Forwarder end leverages YARP (Yet Another Reverse Proxy) to forward ASP.NET Core requests to the on-premise application via the WebSocket connection. At the lowest level, YARP converts an HTTPContext to an HTTPClientRequest and sends it to the on-premise application via the WebSocket connection, which uses a MultiplexingStream to allow multiple requests to be sent over a single connection.
Note: This implementation uses YARP DirectForwarding to forward requests to the on-premise application. Any YARP cluster configuration will not be used.
Key Components
ReverseTunnel.Yarp comprises three main components:
- Forwarder - Uses YARP DirectForwarding to forward requests over the tunnel
- Listener - Receives requests over the tunnel and injects them into the ASP.NET Core pipeline
- Tunnel - A logical layer on top of a WebSocket connection that multiplexes multiple requests
Quick Start
Installation
dotnet add package ReverseTunnel.Yarp
Minimal Server Configuration (Forwarder)
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddTunnelForwarder();
var app = builder.Build();
app.MapTunnelHost();
app.MapTunnelForwarder();
app.Run();
Minimal Client Configuration (Listener)
var builder = WebApplication.CreateBuilder(args);
builder.WebHost.AddTunnelListener(options =>
{
options.DefaultTunnelId = "123";
});
builder.Services.AddTunnelClient(options =>
options with
{
TunnelHost = "wss://localhost:7200",
TunnelId = "123"
});
Core Concepts
Forwarder
The forwarder uses YARP DirectForwarding to forward requests over the tunnel connection to be received by the listener.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddTunnelForwarder();
var app = builder.Build();
app.MapTunnelForwarder();
app.Run();
Listener
The listener receives requests over the tunnel from the forwarder and injects them into the ASP.NET Core pipeline.
var builder = WebApplication.CreateBuilder(args);
builder.WebHost.AddTunnelListener(options =>
{
options.DefaultTunnelId = "123";
});
Reconnect Backoff (Optional)
If you expect repeated connection failures (e.g., temporary network issues or misconfiguration), you can enable exponential backoff for reconnect attempts:
builder.WebHost.AddTunnelListener(options =>
{
options.DefaultTunnelId = "123";
// Enable exponential backoff for reconnect attempts
options.EnableReconnectBackoff = true;
// Cap the maximum backoff delay (default: 2 minutes)
options.MaxReconnectInterval = TimeSpan.FromMinutes(5);
});
Tunnel
The Tunnel is a logical layer on top of a WebSocket connection that allows for multiple requests to be multiplexed over a single connection.
Tunnel Client
The client requires the TunnelHost and TunnelId to be specified in order to connect to the Tunnel Host.
builder.Services.AddTunnelClient(options =>
options with
{
TunnelHost = "wss://localhost:7400",
TunnelId = "123"
});
Tunnel Host
The tunnel host is added as a minimal API endpoint to the application pipeline, accepting websocket connections on /tunnel/{tunnelId} by default.
var app = builder.Build();
app.MapTunnelHost();
app.Run();
Sample Projects
The sample Client and Server projects demonstrate how to use ReverseTunnel.Yarp to connect a cloud application to an on-premise application with simple association using a TunnelId.
Once the sample projects have started, requests to https://localhost:7200/ will be forwarded to the client application:
https://localhost:7200/server- Handled by the serverhttps://localhost:7200/client- Forwarded to the client and returned via the server
Configuration
Client Configuration
The minimal configuration for the client:
builder.WebHost.AddTunnelListener(options => { options.DefaultTunnelId = "123"; });
builder.Services.AddTunnelClient(options =>
options with
{
TunnelHost = "wss://localhost:7200"
});
This creates a Kestrel Listener that will inject requests (from the forwarder) into the client ASP.NET Core pipeline received over the WebSocket connection to the server.
Note: When a code-based listener is added to Kestrel, it will disable the use of the default Kestrel listener configuration. If you require the default listener to be enabled, set the
includeDefaultUrlsparameter totrue:
builder.WebHost.AddTunnelListener(options =>
{
options.DefaultTunnelId = "123";
}, includeDefaultUrls: true);
Server Configuration
The minimal configuration for the server:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddTunnelForwarder();
var app = builder.Build();
app.MapTunnelHost();
app.Run();
Requests sent to the server with a TunnelId header will be forwarded to the corresponding listener. If a DefaultTunnelId is set in the configuration, requests without a TunnelId header will be forwarded to the listener with the DefaultTunnelId:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddTunnelForwarder(options =>
{
options.DefaultTunnelId = "123";
});
var app = builder.Build();
app.MapTunnelHost();
app.Run();
You can also use a transformer (courtesy of YARP) to modify the behavior of the Forwarder:
builder.Services.AddTunnelForwarder(options =>
{
options.Transformer = transformBuilderContext =>
{
transformBuilderContext.UseDefaultForwarders = true;
};
});
Advanced Topics
For more detailed configuration and advanced use cases, see the documentation:
- Blazor Support - Configuration for Blazor apps, runtime options, and connection state monitoring
- Advanced Configuration - Path prefix transformers, WebSocket authentication, and tunnel request detection
- Connection Aggregation - Aggregating multiple on-prem connections via a single cloud connection
- Multi-hop / Chained Routing - Composing ReverseTunnel.Yarp with additional reverse-proxy hops
Future Enhancements
- Scaling across multiple instances of the cloud service could be achieved by using Microsoft.Orleans to store the TunnelId to instance mapping and redirect clients to the correct instance
- Add an example of client certificate authentication for the WebSocket connection
- Consider adding TCP/UDP Forwarding over the tunnel
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
For information about publishing releases to NuGet.org, see the Publishing Guide.
License
This project is licensed under the MIT License - see the LICENSE file for details.
Acknowledgments
This project is a fork of UnifiedFX/UFX.Relay. Thanks to the original authors for their excellent work.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Nerdbank.Streams (>= 2.11.74)
- Yarp.ReverseProxy (>= 2.1.0)
-
net8.0
- Nerdbank.Streams (>= 2.11.74)
- Yarp.ReverseProxy (>= 2.1.0)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on ReverseTunnel.Yarp:
| Package | Downloads |
|---|---|
|
ReverseTunnel.Yarp.Grpc
Active Reverse Tunnel for YARP (ARTY) - WebSocket Relay between ASPNet Core pipelines |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.7.0-alpha-gf14fc6fb0c | 125 | 7/5/2026 |
| 0.7.0-alpha-g8d794815c7 | 126 | 7/6/2026 |
| 0.7.0-alpha-g56c2545d0e | 125 | 7/6/2026 |
| 0.7.0-alpha | 103 | 8/31/2026 |
| 0.6.0 | 1,409 | 4/16/2026 |
| 0.6.0-alpha-g414ad8d48d | 127 | 4/16/2026 |
| 0.6.0-alpha-g07e609b548 | 120 | 4/15/2026 |
| 0.6.0-alpha-g0713360efe | 123 | 4/13/2026 |
| 0.5.2 | 135 | 4/13/2026 |
| 0.5.2-beta-gfa2686301a | 126 | 4/13/2026 |
| 0.5.2-beta-gb8b9dbbd73 | 4,147 | 2/13/2026 |
| 0.5.2-beta-g9106f64553 | 127 | 3/30/2026 |
| 0.5.2-beta-g4db51a667a | 135 | 2/13/2026 |
| 0.5.2-beta-g04adc873c4 | 134 | 3/30/2026 |