ReverseTunnel.Yarp 0.6.0

There is a newer prerelease version of this package available.
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
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="ReverseTunnel.Yarp" Version="0.6.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="ReverseTunnel.Yarp" Version="0.6.0" />
                    
Directory.Packages.props
<PackageReference Include="ReverseTunnel.Yarp" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add ReverseTunnel.Yarp --version 0.6.0
                    
#r "nuget: ReverseTunnel.Yarp, 0.6.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package ReverseTunnel.Yarp@0.6.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=ReverseTunnel.Yarp&version=0.6.0
                    
Install as a Cake Addin
#tool nuget:?package=ReverseTunnel.Yarp&version=0.6.0
                    
Install as a Cake Tool

<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/>

CI NuGet NuGet Downloads License: MIT

</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 server
  • https://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 includeDefaultUrls parameter to true:

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:

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 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

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