WormholeRelay.Contracts
0.2.2
dotnet add package WormholeRelay.Contracts --version 0.2.2
NuGet\Install-Package WormholeRelay.Contracts -Version 0.2.2
<PackageReference Include="WormholeRelay.Contracts" Version="0.2.2" />
<PackageVersion Include="WormholeRelay.Contracts" Version="0.2.2" />
<PackageReference Include="WormholeRelay.Contracts" />
paket add WormholeRelay.Contracts --version 0.2.2
#r "nuget: WormholeRelay.Contracts, 0.2.2"
#:package WormholeRelay.Contracts@0.2.2
#addin nuget:?package=WormholeRelay.Contracts&version=0.2.2
#tool nuget:?package=WormholeRelay.Contracts&version=0.2.2
WormholeRelay
Debug a deployed ASP.NET Core application on your own machine, without redeploying.
Add two lines to your app, connect the desktop client, and incoming HTTP requests are handed to a locally running copy of the same application — so you can set breakpoints on real traffic from a real client. Disconnect and the deployed app serves requests itself again.
It exists for the case where you cannot run the caller locally: the site calling your API is built by another team, or lives in an environment you do not control, and your only feedback loop is a slow deploy.
browser / caller ──▶ your deployed app ──(SignalR)──▶ WormholeRelay client ──▶ localhost
(interceptor) (on your machine) (breakpoints)
Install — two halves, and they only work together
Neither piece does anything on its own. The server package goes into the application you want to debug and can intercept requests, but only ever hands them to a client that has connected. The desktop client runs on your machine, holds the tunnel open and replays each request against your localhost. Without the client, the deployed app behaves exactly as it always did.
| Where it goes | How you get it | |
|---|---|---|
WormholeRelay.Server |
the deployed application | dotnet add package WormholeRelay.Server |
WormholeRelay.exe |
your own Windows machine | latest release |
Both are built and published from the same commit, so keep them on the same version. They agree on
hub method names through the shared WormholeRelay.Contracts assembly; if those names ever change
between versions, a mismatched pair connects and then quietly fails to relay anything.
The client is portable and self-contained: double-click the .exe and it runs. No installer, no
admin rights, and no .NET needed on the machine. Delete the file to uninstall. Its title bar shows
the version, which is the only way to tell two downloaded copies apart.
Wire it up
// Only register it where you intend to allow it. The package treats no environment as special,
// so this check is your policy.
if (builder.Environment.IsEnvironment("Test"))
builder.Services.AddWormholeRelay(builder.Configuration, builder.Environment);
// ...
// Early in the pipeline: after CORS so browser callers still get their headers, but before response
// compression and authentication so requests are forwarded exactly as they arrived.
if (app.Environment.IsEnvironment("Test"))
app.UseWormholeRelay();
Then configure it:
"WormholeRelay": {
"Enabled": true,
"AllowedEnvironments": [ "Test" ], // no default: unset means the relay cannot start
"JoiningKey": "<a long random secret>",
"MaxSessionMinutes": 45,
"RoutePrefixes": [] // empty = all traffic; narrow it on a shared environment
}
Both extension methods are safe to call unconditionally — they do nothing unless enabled — but gating them as above means the relay is not even registered elsewhere.
Using the client
Start your local copy of the application first, then run WormholeRelay.exe, fill in the fields and
press Connect. The status line next to the button reports what is actually happening — connecting,
connected with the session expiry, or the reason it was refused.
| Field | What it is | Example |
|---|---|---|
| Server URL | the deployed application you want to divert | https://your-app.example.com |
| Hub path | must match HubPath on the server |
/hubs/wormhole-relay |
| Joining key | the JoiningKey from the server's configuration |
masked as you type |
| Local redirect URL | your locally running copy, the target for replayed requests | https://localhost:5001 |
| Log folder | where the session log file is written | C:\LogFiles\WormholeRelay |
Remember key stores the joining key between runs; clear it and you retype the key each time. Enable logging writes the file, Verbose adds full header and body detail, and Open opens the log folder in Explorer. The live pane mirrors the same information, with Copy and Clear beside it.
Everything except the key is stored as plain JSON per Windows user under %APPDATA%\WormholeRelay. The
key itself is encrypted with DPAPI, so it is readable only by your Windows account on that machine —
not held in the clear, and not portable to another user or PC.
Press Disconnect when you are done. That releases the session on the server immediately rather than leaving it to time out, and traffic goes back to the deployed app. Closing the window does the same.
What the log gives you
A daily rolling file, with a blank line between requests: method, path, query string, every header and
a body preview, then the response status, headers and how long your local machine took. Authorization
values are recorded as present-but-hidden rather than written out, and binary bodies are noted by size
instead of dumped.
It is the record of what actually crossed the wire, which makes it the first place to look when a request behaves differently through the relay than it did against the deployed app. It also means the file can hold real request data — see below.
Read this before enabling it anywhere shared
- A connected client receives all traffic for the host, not just yours. On a shared test
environment you are diverting your colleagues' requests too. Use
RoutePrefixesto narrow it. - The joining key is a real credential. Anyone holding it can redirect that host's traffic to their own machine. Keep it in a secret store, not in source control, and rotate it if it leaks.
- Sessions expire after
MaxSessionMinutesand only one is active at a time, so a forgotten client cannot hold traffic indefinitely. - Never enable it in production. The package deliberately contains no special case for an environment named "Production" — that policy belongs to your host, so make your registration condition narrow and keep it that way.
- Every failure path falls through to normal handling: no client connected, a request that times out, a body over the size limit, or a local target that cannot be reached all mean "the deployed app answers as usual".
Responsible use, and your own risk
Be clear-eyed about what this tool is. To divert traffic it has to intercept HTTP requests and replay them somewhere else, and the requests it carries are real ones from real callers. That is a debugging aid on a system you control, and it is wiretapping on one you do not. The mechanism does not know the difference — you do.
Use it only where you are authorised to. Run it against systems you own, or where the owner has given you permission to debug. Pointing it at anyone else's service, or using it to capture traffic you have no right to see, is unauthorised interception of communications and is a criminal offence in most jurisdictions. Do not use it to intercept credentials, harvest data, bypass access controls, or watch other people's sessions. Nothing about the tool being open source changes any of that.
It is for debugging, and nothing else. It is not a proxy, not a load balancer, not a tunnelling service, and not a way to expose a private machine to the internet. It has no throughput guarantees, no chunking for large bodies and a hard session timeout, because none of those matter for stepping through a request in a debugger — and all of them matter for anything you might mistake it for.
Real data will pass through your machine. Requests are replayed on your PC and, with logging on, written to a file there. On any environment carrying live or production-like data, that means personal data leaves its controlled environment and lands on a laptop. Check that this is allowed under your organisation's data-protection rules before you connect, keep the log folder off shared drives, and delete logs when you are finished with them.
No warranty. This is MIT-licensed software provided as is, with no warranty of any kind, express or implied. Running it means you accept the risk: a diverted environment is a broken environment for anyone else using it, and misconfiguring it in a place it does not belong is your responsibility. The authors are not liable for lost data, downtime, leaked information or anything else that follows from using it. Read the licence — the disclaimer there is the binding version of this paragraph.
If you are unsure whether a particular use is legitimate, the honest test is simple: could you explain what you are doing to the owner of that system and to whoever's traffic you are diverting, and would both agree? If not, do not do it.
Try it without a deployed app
WormholeRelay.TestHost runs both ends on one machine — a stand-in "remote" on port 5099 with the relay
enabled, and a stub local target on 5098 with a live request monitor page:
dotnet run --project WormholeRelay.TestHost
It prints the exact values to type into the client. Open http://localhost:5098/ to watch requests
arrive in real time, then curl http://localhost:5099/whoami and see the answer change from REMOTE
to LOCAL once you connect.
Projects
| Project | |
|---|---|
WormholeRelay.Server |
the hub, the interceptor and the extension methods — the NuGet package you install |
WormholeRelay.Contracts |
messages shared by both sides, no dependencies |
WormholeRelay.App |
the WinForms client, published as WormholeRelay.exe |
WormholeRelay.TestHost |
the local rig described above |
Limitations
- Bodies larger than
MaxBodyBytes(1 MB by default) are not relayed; those requests are handled by the deployed app instead. Chunking is not implemented. - With in-process SignalR the tunnel lives on one instance, so run a single instance while relaying, or
set
AzureSignalRConnectionStringto let any instance reach the connected client. - WebSockets must be enabled on the host. On Azure App Service they are off by default.
- The client is Windows-only, because it is a WinForms application. The server package is not.
Releasing
Every push to main publishes a release. There is no tag to push and no version file to edit:
the workflow builds, packs, pushes both packages to NuGet, and creates a GitHub release with the
portable executable attached.
Publishing uses trusted publishing,
so no API key is stored here. nuget.org exchanges a GitHub OIDC token for a key that lives one hour,
which requires a policy on nuget.org naming this repository and release.yml.
Versions are <VersionMajorMinor>.<github run number> — VersionMajorMinor lives in
Directory.Build.props and the run number supplies an always-increasing patch. Bump the major or
minor by hand for a deliberate feature or breaking release; the patch takes care of itself.
Work on develop or a feature branch, where build proves the solution compiles, both packages
pack and the executable publishes — without releasing anything. Merge to main when you want it out.
To rebuild a specific version, run the release workflow manually and give it the version.
License
MIT — see LICENSE.
| 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 was computed. 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. |
-
net8.0
- No dependencies.
NuGet packages (1)
Showing the top 1 NuGet packages that depend on WormholeRelay.Contracts:
| Package | Downloads |
|---|---|
|
WormholeRelay.Server
Debug a deployed ASP.NET Core application on your own machine, without redeploying. Add two lines to the app and incoming HTTP requests are handed to a locally running copy, so you can set breakpoints on real traffic from a real caller; disconnect and the deployed app serves requests itself again. This is the half that goes into your application. It pairs with the WormholeRelay desktop client, a portable Windows exe from the GitHub releases page - neither half does anything alone. For debugging only, and only on systems you are authorised to debug. Off unless the host explicitly enables it for a named environment with a joining key, and never to be enabled in production. See the readme before using it on any shared environment. |
GitHub repositories
This package is not used by any popular GitHub repositories.