ReverseTunnel.Yarp.Grpc 0.7.0-alpha-g8d794815c7

This is a prerelease version of ReverseTunnel.Yarp.Grpc.
dotnet add package ReverseTunnel.Yarp.Grpc --version 0.7.0-alpha-g8d794815c7
                    
NuGet\Install-Package ReverseTunnel.Yarp.Grpc -Version 0.7.0-alpha-g8d794815c7
                    
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.Grpc" Version="0.7.0-alpha-g8d794815c7" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="ReverseTunnel.Yarp.Grpc" Version="0.7.0-alpha-g8d794815c7" />
                    
Directory.Packages.props
<PackageReference Include="ReverseTunnel.Yarp.Grpc" />
                    
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.Grpc --version 0.7.0-alpha-g8d794815c7
                    
#r "nuget: ReverseTunnel.Yarp.Grpc, 0.7.0-alpha-g8d794815c7"
                    
#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.Grpc@0.7.0-alpha-g8d794815c7
                    
#: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.Grpc&version=0.7.0-alpha-g8d794815c7&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=ReverseTunnel.Yarp.Grpc&version=0.7.0-alpha-g8d794815c7&prerelease
                    
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

Transports and replica sets

WebSocket remains the default tunnel transport. An optional gRPC transport and the replica-set owner-forwarding model are documented in Transports and replica sets.

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. They can run with the default WebSocket transport or the optional gRPC transport; see Samples.

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

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.7.0-alpha-g8d794815c7 91 7/6/2026
0.7.0-alpha 58 8/31/2026