Skip to main content

Passwordless, CA-free ssh/sshd on the WireSeal wire: auto/tcp/udp transport, SOCKS -D, safe rendering.

Project description

wireshell

A passwordless SSH replacement whose sessions survive changing networks.

Open a shell on a server. Close your laptop. Walk from wifi to cellular. Open it again — you're still in the same shell, mid-command, nothing lost. No reconnect, no re-login, no known_hosts prompt. wssh myhost is the whole client: no passwords, no certificate authority, no key-exchange ceremony. A key is the identity.

Your shell lives on the server, like tmux — so when the link dies (a flight, hours offline, a dead router) wireshell just reattaches and repaints the exact screen the moment the network comes back.

pip install wireshell
wsshd            # server: binds [::]:2022, generates its identity, serves
wssh myhost      # client: that's it

Tested on real hardware:

  • wifi → LTE handovers across NAT, with vim and top held open through the change
  • long network drops on both client and server
  • long sleep (suspend/resume) on both client and server

Why wireshell

  • It roams. Sessions bind to your key, not your IP address. Change networks mid-session and the shell follows you — mosh's best trick, but without needing SSH to log in or tmux to persist. It's one tool.
  • No ceremony. No passwords, no CA, no ssh-keygen dance, no host-key prompts to click past. You run wssh myhost and you're in.
  • Persistent by default. The shell runs server-side. Detach, reattach, survive a multi-hour outage — built in, not bolted on.
  • Self-sovereign access control. Users self-enroll into a pending queue; you approve them by fingerprint. No directory service, no coordination server, no cloud — just local files you own.
  • Fast. A roughly one-round-trip Noise handshake plus predictive local echo make it feel quicker than SSH, most noticeably on high-latency links.
  • Self-contained. Pure-Python payload; the single compiled dependency comes from your distribution. One pip install, or a .deb that runs on any Python 3.11+ and any CPU architecture.
  • Batteries included. Port forwarding (-L/-D/-R), a SOCKS5 proxy, file copy (wscp), tmux-style named sessions, and an admin tool (wsshctl) — all in the box.

Quick start

Server — just run it:

wsshd

It binds [::]:2022, creates or loads its identity under $WSSH_HOME (announcing which), and serves. Everything else is optional and can live in a config file it finds automatically ($WSSH_HOME/wsshd.conf or /etc/wireshell/wsshd.conf).

Client — find your key, ask to join, connect:

wssh myhost --whoami          # prints the key the server will see for you
wssh myhost --request alice   # queue for approval under the handle "alice"
#   on the server:  wsshctl approve alice
wssh myhost                   # you're in — and you stay in

That's the entire flow: no password to set, no key to copy into a file by hand, no CA to stand up.


Roaming and transports

The handshake always runs over TCP; the session then runs over one of:

  • auto (default) — TCP bootstrap, then a ~1s authenticated UDP probe. If UDP answers, the session runs over UDP with roaming: it survives IP changes and reattaches. If UDP is blocked, it runs over the TCP stream already open, without roaming. The probe is authenticated with the session keys, so it can't be spoofed to steer the choice.
  • --udp — force UDP; fail fast if it's blocked.
  • --tcp — force TCP; works through anything TCP does; no roaming.

Roaming needs UDP because a TCP connection is pinned to its address. auto gives you roaming wherever UDP is open and graceful fallback where it isn't — you don't have to think about it.

Session persistence

Your shell lives on the server, not in the client. When the transport drops, the shell keeps running (background jobs included) and the server holds it. Once you've connected, wssh reconnects on its own for as long as it takes — a flight, a link dead for hours — then reattaches by your key and repaints the exact screen when the network returns (Ctrl-C quits while waiting). If the client process itself is gone, run wssh myhost again and you reattach the same way.

Keep several shells at once and name them, like tmux:

wssh myhost              # resume most-recent (or start one)
wssh myhost -t build     # attach/create the session named "build"
wssh myhost --new        # a fresh session alongside the others
wssh myhost --list       # list your sessions
wssh myhost --kill build # remove one

Sessions are private to your key. Two inherent limits: a server reboot loses held sessions (they live in memory, like tmux/screen), and detached scrollback is capped (you get the live screen plus recent output, not hours of history).

Access control

Two modes:

  • Classic — an SSH-style flat file (--authorized authorized_keys), one key per line, with optional per-key options: command="...", no-pty, from="cidr,...", expires="YYYY-MM-DD", permitopen="host:port,...".
  • Tier (--root DIR --enroll) — the WireSeal identity engine. An unknown key self-registers into a pending queue (wssh HOST --request <handle>); you vouch it with wsshctl approve <key> [member|admin]. Admission is by tier — owner/admin/ member get a shell; banned and unapproved get nothing. Approvals are live, no restart. wsshctl sessions shows who's connected and since when (a snapshot with no peer addresses recorded). Manage the root with wsshctl (pending/approve/deny/ban/members/review/watch).

Port forwarding and file copy

wssh myhost -L 8080:localhost:80      # local port -> target reached from the server
wssh myhost -D 1080                   # local SOCKS5 proxy, dynamic targets
wssh myhost -R 9000:localhost:3000    # reverse: server port -> target reached from you
wscp myhost:/etc/hostname ./          # copy from server
wscp ./build.tar.gz myhost:/tmp/      # copy to server

Forwards multiplex over the session, ride whichever transport it negotiated, and survive UDP roaming.

Safe rendering

--safe-render routes server output through a private terminal emulator and emits only a safe subset of escape sequences, so a malicious server can't inject terminal escapes into your terminal. Trade-off: it shows only what the emulator models and disables predictive echo. Raw passthrough is the default.


Security status

The WireSeal transport cryptography — the TCP-bootstrap Noise IK handshake and the UDP session handshake — has NOT been independently reviewed or audited. The test suites prove the code survives the specific attacks it was tested against (no crashes, hangs, or resource exhaustion; escape injection is closed under --safe-render). That is operational hardening, not cryptographic clearance.

Treat wireshell as promising, real-hardware-tested software for experimentation — not for high-stakes secrecy — until the handshake has had independent review.

Authenticity

Releases are signed with wsident, WireSeal's Ed25519 authorship tool: https://git.disroot.org/wireseal/wsident

Verify against the author's offline cold-key fingerprint — not against any email, host, or channel:

3aa0a2db b80573ff eb319965 05f369ba 3421584e 5c26653f ce079a8a ab55fe50

To verify a release, download it alongside MANIFEST and MANIFEST.wsig, then:

sha256sum -c MANIFEST
python3 wsident.py verify MANIFEST.wsig \
  3aa0a2dbb80573ffeb31996505f369ba3421584e5c26653fce079a8aab55fe50

AUTHENTIC means the release is genuinely from the holder of that cold key. See AUTHENTICITY.txt in the tree for the current certificate.


Apache-2.0 · cc <wireseal@proton.me> · https://git.disroot.org/wireseal/wireshell

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

wireshell-0.14.3.tar.gz (101.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

wireshell-0.14.3-py3-none-any.whl (99.5 kB view details)

Uploaded Python 3

File details

Details for the file wireshell-0.14.3.tar.gz.

File metadata

  • Download URL: wireshell-0.14.3.tar.gz
  • Upload date:
  • Size: 101.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.5

File hashes

Hashes for wireshell-0.14.3.tar.gz
Algorithm Hash digest
SHA256 145eff48407f51f8ff499734869b9c43ca6d54e769b0dc01622a1aa26a4f88de
MD5 150ce7b31d28cad0370fd82d17aba292
BLAKE2b-256 c88a6522e1295fc9f6d97e90d3cef1f71e0bf100f7474cd040cb78b1c6e3d174

See more details on using hashes here.

File details

Details for the file wireshell-0.14.3-py3-none-any.whl.

File metadata

  • Download URL: wireshell-0.14.3-py3-none-any.whl
  • Upload date:
  • Size: 99.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.5

File hashes

Hashes for wireshell-0.14.3-py3-none-any.whl
Algorithm Hash digest
SHA256 7d14912826f2bbab60f4e5367219e8d9cc6cc57fc8f754fd6d2bfdc2873bf2f1
MD5 735debd66c122dd287e97b9bef573aba
BLAKE2b-256 11afaa17cf1feebf926262090e57e4404b43a64ba27aa2fc273cb9878895636a

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page