A remote shell you can drop into any .NET app — Android included — in one line, with no server to run, no port to open, and no keys to manage by hand.
Meowshell packages tailscale/tailcat
(netcat over Tailscale's data plane, without its control plane) together with
meowshell, a companion launcher that fixes up the shell environment tailcat
hands a session so it works on Android too, not just desktop. Add the
Meowshell package, call one method, and you have a real interactive shell —
PTY, tab completion, job control — reachable from anywhere, torn down on
your own schedule.
var options = MeowshellOptions.Create(TimeSpan.FromMinutes(5)) with
{
AuthorizedKeys = "alice@github", // fetches alice's SSH keys from GitHub
};
await using var server = await MeowshellServer.StartAsync(options);
Console.WriteLine(server.Address); // tailcat ssh <address>, from anywhereAuthorizedKeys is the mode to reach for by default. InsecureNoAuth exists
for when the address itself is the only credential you want — pair it with
AllowClientKeys so a leaked address alone isn't enough to get a shell.
That one call is the common case. The same package is a lot more than a shell:
- No system
ssh/scp/sftpanywhere. Every client operation — interactive sessions, SFTP, file transfer — is a native Go implementation under the hood, not a wrapper around a binary your platform may not even have. That's what makes any of this possible from inside an Android app sandbox in the first place. - One login, everything multiplexed.
MeowshellAgentConnectionopens a single connection and shares it across a shell, the full SFTP verb set (Upload/Download/Mkdir/Rename/Chmod/Symlink/...), and-L/-R/-Dport forwarding — instead of a fresh process and handshake per operation. - It's also a real SSH client, not just a tailcat one. That same
connection type reaches a general SSH host by hostname, with real
host-key verification (
known_hosts, trust-on-first-use),ProxyJumpchaining, and password/keyboard-interactive/certificate auth — including keys backed by Android's Keystore, where the private key never leaves secure hardware and never touches your process memory. - A SOCKS5 proxy and TCP port forwarding, loopback-restricted by default with optional token auth, and available over a Unix domain socket instead of TCP on platforms where "every other app on the device can reach your loopback port" is a real threat model, not a hypothetical.
- Typed results and real errors, not scraped stdout. Addresses, ping
results, file listings, and environment diagnostics all come back as
real C# types; every failure is one
TailcatExceptioncarrying the actual diagnostics, not a bare non-zero exit code to guess at. - Orphan-proof by construction. Every long-lived session arms
parent-death protection (a Job Object on Windows,
PDEATHSIGon Linux/Android) before it ever starts listening, so a crashed, OOM-killed, or force-stopped host process can't leave an unauthenticated shell running behind it — the exact failure mode that matters most on Android, where the OS kills app processes far more readily than a desktop or a server ever would. - A shell isn't the only thing you can serve, either. Forced-command sessions, an SFTP-only file service, and an "exit node" mode that lets an authorized client reach any port the host machine can dial are all one option away — combine or use standalone.
Also one-shot operations for key management, address parsing/resolution,
and connectivity checks (tailcat ping, genkey, printpub). See
dotnet/README.md for the full surface, every option,
and the one thing (a console-attached ssh client, as opposed to a
programmatic session) an Android app sandbox can't run.
Meowshell— the .NET library above (dotnet add package Meowshell): shell/SFTP/forced-command sessions, a SOCKS5 proxy, port forwarding, and one-shot key/address/file operations. Seedotnet/README.mdfor the full API.Meowshell.Runtime.{linux,windows,android}— the nativetailcatandmeowshellbinaries for each platform, pulled in automatically as a dependency ofMeowshell.Meowshell.Demo— a real, installable Android app: one button generates a throwaway shell address.Meowshell.AndroidProbe— a minimal app that only proves the packaged binaries are found and run on a real device; what CI checks.meowshell, the CLI — a standalone launcher/shim aroundtailcat, for use outside .NET (Termux,adb shell, scripts).
| Platform | Architectures |
|---|---|
| Android | arm64-v8a, armeabi-v7a, x86_64, x86 |
| Linux | amd64, arm64, armv7, 386 |
| Windows | amd64, arm64 |
./build.sh # every target the toolchain allows
PLATFORMS=linux ./build.sh # just oneClones tailscale/tailcat, builds it alongside this repo's meowshell, and
writes binaries to dist/. Android needs the NDK (ANDROID_NDK_HOME, r19+)
for cgo-based DNS resolution; Linux and Windows are pure Go. See
./verify-binaries.sh and e2e/ for how CI checks the result.
This isn't an unreviewed pile of process-spawning code: a full security
review found and fixed eleven real issues, from an unencrypted HTTPS proxy
path to release builds tracking a mutable upstream branch. See
SECURITY_REVIEW.md for the current threat model,
findings, accepted risks, and verification status. The accompanying
REVIEW_MAP.md groups every first-party file by runtime
boundary and records the order and focus of the repository review.
MIT for this repo's own code. The vendored/patched
tailscale/tailcat source it builds against is upstream's own,
BSD-3-Clause-licensed work.