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
vimandtopheld 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-keygendance, no host-key prompts to click past. You runwssh myhostand 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.debthat 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 withwsshctl 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 sessionsshows who's connected and since when (a snapshot with no peer addresses recorded). Manage the root withwsshctl(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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
145eff48407f51f8ff499734869b9c43ca6d54e769b0dc01622a1aa26a4f88de
|
|
| MD5 |
150ce7b31d28cad0370fd82d17aba292
|
|
| BLAKE2b-256 |
c88a6522e1295fc9f6d97e90d3cef1f71e0bf100f7474cd040cb78b1c6e3d174
|
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7d14912826f2bbab60f4e5367219e8d9cc6cc57fc8f754fd6d2bfdc2873bf2f1
|
|
| MD5 |
735debd66c122dd287e97b9bef573aba
|
|
| BLAKE2b-256 |
11afaa17cf1feebf926262090e57e4404b43a64ba27aa2fc273cb9878895636a
|