webshare: one command to hand a file across the room
A tiny .NET 10 tool that turns any file or folder into a temporary web page on your LAN, then cleans up after itself, firewall rule included.

Every so often I need to move a file from the machine in front of me to a device that's right there. A phone on the couch, a laptop on the kitchen table, a Windows box in the other room. The options are all a little annoying. Cloud drives round-trip my file through someone else's data center to travel eight feet. USB sticks are never where I left them. Setting up a Samba share for one ISO feels like building a bridge to cross a puddle.
What I actually want is the old python -m http.server trick, but polished. It should work on a single file, pick its own port, tell me the address to type, look decent on a phone, and go away on its own when I forget about it.
So I wrote webshare.
webshare ~/Downloads/firmware-2.4.1.bin
webshare --time 15 ~/Pictures/vacation
sudo webshare --open-firewall --port 8080 ./build
Run it with a file or a folder and it starts a temporary web server, prints the local and network URLs, and waits. Folders get a browsable listing. A single file gets a landing page with one big download button. By default the share shuts itself off after 60 minutes (--time 0 if you really want it to run forever).

Small, but not careless
It's a small tool, but anything that serves files to a network deserves some paranoia. A few decisions I'm happy with:
Paths can't escape the share. Every request path is split into segments, any . or .. is rejected outright, and then the canonical result has to still live under the root:
// Reject traversal segments and verify the canonical target remains under the root.
if (segments.Any(segment => segment is "." or ".."))
{
targetPath = string.Empty;
return false;
}
targetPath = Path.GetFullPath(Path.Combine(rootPath, Path.Combine(segments)));
var comparison = OperatingSystem.IsWindows()
? StringComparison.OrdinalIgnoreCase
: StringComparison.Ordinal;
Symlinks are ignored. Every component of a requested path is checked for a reparse point (a symlink, or a junction on Windows). A link inside the shared folder that points at /etc is neither listed nor served.
Strict headers everywhere. Every response gets Cache-Control: no-store, nosniff, no-referrer, and a Content Security Policy that only allows inline styles. The pages have no JavaScript and nothing to inject into.
No authentication, stated plainly. Anyone who can reach the port can download what you're sharing. The README says so, and the tool is meant for a trusted network. I'd rather be honest about that than bolt on a password prompt that gives a false sense of safety.
The firewall problem
The most common way a LAN share fails isn't a bug. The firewall on the sharing machine silently drops every connection from another device, and the server can't reliably detect that from its own side. The only real test is another device trying to connect.
So --open-firewall adds a temporary, narrowly scoped exception and removes it on exit:
- On Windows, it's a
netshrule limited to the Private profile andLocalSubnet. - On Linux, it adds
iptables/ip6tablesrules for each active local subnet it can find.
Both need elevation, and on Linux that means sudo runs the whole app as root, which I don't love and the docs spell out. A normal exit or Ctrl+C removes the rules. A power cut doesn't, so every rule is named webshare-<guid> (as an iptables comment on Linux) to make leftovers easy to find and delete by hand.
Why .NET for a tiny tool?
Honestly, because it keeps getting better at small things. WebApplication.CreateSlimBuilder gives me Kestrel with almost nothing else. The stylesheet is an embedded resource, so there's no asset folder to lose, and the whole thing publishes as a Native AOT single binary for Linux x64 and Windows x64. It starts instantly and has no runtime to install on the machine that's sharing.
make publish-linux && sudo make install
webshare .
That's the whole pitch: one command, a URL, and it cleans up after itself. The code is on GitHub if you want the same thing.