Skip to content

Ymsniper/NoEyes

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

104 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

NoEyes β€” Secure Terminal Chat

End-to-end encrypted group chat, private messages, and file transfer β€” right in your terminal. The server is a blind forwarder: it cannot read a single byte of your messages, even if fully compromised.

See it in action

noeyes_demo.mp4

Security features demo β€” group chat, private messages, identity verification, TOFU key trust:

asciicast


Install demo 1 β€” sh install.sh (no Python required):

asciicast


Install demo 2 β€” python ui/setup.py (guided wizard):

asciicast


What is NoEyes?

NoEyes is a Python terminal chat tool for small groups who need real privacy. Unlike every mainstream chat app, the server never decrypts anything β€” it only sees encrypted bytes and routing headers, then forwards them blindly. You generate the key, share it out-of-band, and the server learns nothing about your conversations.

Who is it for?

  • Developers and teams who want encrypted comms without trusting a third-party server
  • Anyone who wants to self-host a private chat with true end-to-end encryption
  • Security-minded users who want to understand exactly what a server can and cannot see

Features

Feature Details
Blind-forwarder server Zero decryption β€” server sees only routing metadata
Group chat Per-room Fernet keys derived via HKDF β€” rooms cryptographically isolated
Private messages X25519 DH handshake on first contact β€” pairwise key only the two parties hold
File transfer AES-256-GCM streaming β€” any size, low RAM usage
Ed25519 identity Auto-generated signing key β€” all private messages and files are signed
TOFU First-seen keys trusted; key mismatches trigger a visible security warning
Random PBKDF2 salt Each deployment gets a unique random salt β€” rainbow tables are useless
Split sidebar panel Rooms (top) and users (bottom) always visible side by side β€” each half scrolls independently
Free text selection No mouse capture β€” drag to copy text freely on all platforms including Termux
CRT boot animation Full-screen phosphor effect with sound on startup β€” works on all platforms
Guided launcher Arrow-key menu UI β€” no command-line experience needed
Auto dependency installer Detects your platform, installs what's missing, asks before changing anything
Self-updater One command to pull the latest version from GitHub

Quick Start

Option A β€” Guided (recommended for beginners)

# 1. Run the setup wizard β€” installs Python, pip, and cryptography automatically
python ui/setup.py

# 2. Launch NoEyes
python ui/launch.py

ui/launch.py walks you through starting a server or connecting to one β€” no commands to memorize.


Option B β€” If Python isn't installed yet

Platform Run this first
Linux / macOS / Termux / iSH sh install/install.sh
Windows install\install.bat

Both scripts install Python if missing, then hand off to setup.py automatically. install.bat works from both CMD and PowerShell β€” no need to run install.ps1 directly.


Option C β€” Manual (advanced users)

# 1. Install the one dependency
pip install cryptography

# 2. Generate a shared key β€” share this file with all participants out-of-band
python noeyes.py --gen-key --key-file ./chat.key

# 3. Start the server (does NOT need the key file)
python noeyes.py --server --port 5000

# Start without bore tunnel (LAN / static IP / custom tunnel)
python noeyes.py --server --port 5000 --no-bore

# Start without adding a firewall rule (not needed when using bore tunnel)
python noeyes.py --server --port 5000 --no-firewall

# 4. Connect clients
python noeyes.py --connect SERVER_IP --port 5000 --username alice --key-file ./chat.key
python noeyes.py --connect SERVER_IP --port 5000 --username bob   --key-file ./chat.key

Running on Termux (Android) β€” Step by Step

Termux Tips

Download Termux from F-Droid (recommended) or the Play Store. F-Droid link: https://f-droid.org/packages/com.termux/

The F-Droid version is more up to date and receives faster security patches.

Keep the session alive β€” Install tmux so NoEyes keeps running when you switch apps:

pkg install tmux -y
tmux
python ui/launch.py
# Press Volume Down + D to detach (keeps running in background)
# tmux attach   to come back

Storage permissions β€” file transfer will fail if u don't grant storage access(also clone NoEyes inside /storage/shared/ to access files easily):

termux-setup-storage

In-Chat Commands

Command Description
/help Show all commands
/quit Disconnect and exit
/clear Clear screen
/users List users in current room
/nick <n> Change your display name
/join <room> Switch to a room (created automatically)
/leave Return to the general room
/msg <user> <text> Send an E2E-encrypted private message
/send <user> <file> Send an encrypted file
/whoami Show your identity fingerprint
/trust <user> Trust a user's new key after they reinstall
/anim on|off Toggle the decrypt animation
/notify on|off Toggle notification sounds

TUI Keyboard Shortcuts

Key Action
\u2191 / \u2193 Scroll chat up / down
PgUp / PgDn Scroll chat one page
^P (Ctrl+P) Show / hide the sidebar panel
^C Quit

Sidebar panel

The chat window has a narrow sidebar on the left that always shows two sections:

  • Top half \u2014 ROOMS \u2014 all rooms you have joined this session. The active room is highlighted with \u25b6.
  • Bottom half \u2014 USERS \u2014 everyone currently in your active room.

Each half scrolls independently with the mouse wheel. If there are more items than fit, a +N more indicator appears at the bottom of that half.

The panel is display-only \u2014 no clickable elements, so you can drag to select and copy text freely on all platforms including Termux (no Shift+drag needed). Use /join <room> and /msg <user> to interact.

Press ^P to hide the panel and get a full-width chat view. Press again to bring it back. Works identically on Linux, Windows Terminal, Termux, and Terminus \u2014 no function key required.


Message Tags

Prefix any message with a !tag to color it for everyone and trigger a notification sound on the receiver's machine. Tags travel inside the encrypted payload β€” the server never sees them.

Tag Color Sound Use for
!ok <msg> 🟒 Green ok Success, confirmed, done
!warn <msg> 🟑 Yellow warn Warning, heads up, be careful
!danger <msg> πŸ”΄ Red danger Critical, urgent, emergency
!info <msg> πŸ”΅ Blue info Status update, FYI
!req <msg> 🟣 Purple req Request, needs someone's action
!? <msg> 🩡 Cyan ask Question, asking for input

Examples:

!danger server is going down in 5 minutes
!req    can someone review my PR?
!ok     deployment successful
!warn   disk at 90% on prod
!?      anyone know why the build is failing?

Sounds play from sounds/ folder next to noeyes.py. Drop in .wav, .mp3, .ogg, .aiff, .flac, or .m4a files named after the sound type (e.g. sounds/danger.wav). If no file is found, falls back to the terminal bell. Use /notify off to disable all sounds.


Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Alice ───────────────────────────────────────── Bob         β”‚
β”‚    β”‚          Encrypted payload (opaque)           β”‚         β”‚
β”‚    β”‚                     β”‚                         β”‚         β”‚
β”‚    └───────────► SERVER β”€β”΄β—„β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜         β”‚
β”‚                     β”‚                                        β”‚
β”‚               Blind forwarder:                               β”‚
β”‚               reads routing header only                      β”‚
β”‚               { "type":"chat", "room":"general" }            β”‚
β”‚               forwards encrypted bytes verbatim              β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

WHAT THE SERVER SEES:          WHAT THE SERVER CANNOT SEE:
  Β· Usernames                    Β· Message content
  Β· Room names                   Β· File contents
  Β· Event types (join/leave)     Β· Private message bodies
  Β· Frame byte length            Β· DH key exchange values
                                 Β· Ed25519 signatures

Key derivation chain

chat.key (shared secret)
    β”‚
    β”œβ”€ HKDF("general") ──► room_key["general"]   (isolated per room)
    β”œβ”€ HKDF("dev")     ──► room_key["dev"]
    └─ HKDF("ops")     ──► room_key["ops"]

X25519 DH (per user pair, automatic on first /msg)
    alice_ephemeral + bob_ephemeral ──► shared_secret
                                              β”‚
                                         HKDF-SHA256
                                              β”‚
                                       pairwise_key    (private messages)
                                              β”‚
                                  HKDF(transfer_id) ──► aes_gcm_key   (files)

Passphrase β†’ key derivation (when using --key PASSPHRASE)

passphrase + random_salt (32 bytes, os.urandom)
    β”‚
    └─ PBKDF2-HMAC-SHA256 (390,000 iterations)
              β”‚
         derived_key  ──► saved to ~/.noeyes/derived.key
                                      β”‚
                          loaded directly on every subsequent run
                          (no PBKDF2 re-derivation, no static salt)

Every deployment gets a unique random salt β€” precomputed rainbow tables are useless. After the first run, share the key file, not the passphrase.


Security Summary

Layer Mechanism Notes
Group chat Fernet (AES-128-CBC + HMAC-SHA256) Per-room key via HKDF
Private messages Fernet with X25519 pairwise key Ed25519 signed, TOFU verified
File transfer AES-256-GCM Per-transfer key, Ed25519 signed
Identity Ed25519 keypair Auto-generated at ~/.noeyes/identity.key
Key derivation PBKDF2-HMAC-SHA256 + random salt Unique salt per deployment β€” no rainbow tables
Server Blind forwarder Zero decryption β€” server never holds any keys
Room isolation HKDF(master_key, room_name) Cryptographically isolated
Transport TLS (on by default) TOFU cert pinning β€” MITM triggers visible warning
Replay protection Per-room message ID deque Replayed frames silently dropped
Rate limiting Separate chat / control buckets DH flood cannot exhaust chat quota

Project Structure

NoEyes/
β”œβ”€β”€ noeyes.py              Entry point and CLI argument parser
β”œβ”€β”€ requirements.txt       pip dependencies (just: cryptography)
β”‚
β”œβ”€β”€ core/
β”‚   β”œβ”€β”€ encryption.py      All crypto: Fernet, HKDF, X25519, Ed25519, AES-256-GCM
β”‚   β”œβ”€β”€ identity.py        Ed25519 keypair generation and TOFU pubkey store
β”‚   β”œβ”€β”€ utils.py           Terminal output, ANSI colours, decrypt animation
β”‚   └── config.py          Configuration loading and CLI parsing
β”‚
β”œβ”€β”€ network/
β”‚   β”œβ”€β”€ server.py          Async blind-forwarder server (zero decryption)
β”‚   └── client.py          Terminal chat client (E2E, DH, TOFU, file transfer)
β”‚
β”œβ”€β”€ ui/
β”‚   β”œβ”€β”€ launch.py          β˜… Guided launcher β€” arrow-key menu UI
β”‚   └── setup.py           β˜… Dependency wizard β€” auto-installs everything needed
β”‚
β”œβ”€β”€ install/
β”‚   β”œβ”€β”€ install.sh         Bootstrap for Linux / macOS / Termux / iSH
β”‚   β”œβ”€β”€ install.bat        β˜… Bootstrap for Windows (CMD and PowerShell)
β”‚   β”œβ”€β”€ install.ps1        Called automatically by install.bat
β”‚   └── install.py         Cross-platform Python installer
β”‚
β”œβ”€β”€ tests/
β”‚   └── selftest.py        Automated test suite
β”‚
β”œβ”€β”€ docs/
β”‚   β”œβ”€β”€ README.md
β”‚   └── CHANGELOG.md
β”‚
β”œβ”€β”€ update.py              Self-updater β€” pulls latest from GitHub
└── sfx/                   Notification sounds

Supported Platforms

setup.py automatically detects your platform and installs what's missing:

Platform Package manager used
Ubuntu / Debian / Mint apt-get
Fedora / RHEL / CentOS dnf / yum
Arch / Manjaro pacman
Alpine / iSH (iOS) apk
openSUSE zypper
Void Linux xbps-install
macOS Homebrew (auto-installed if missing)
Android (Termux) pkg
Windows winget / Chocolatey / Scoop

Running a Server Online β€” bore pub

The problem: port forwarding is often blocked

When you start a NoEyes server at home, your machine gets a local IP (e.g. 192.168.1.5). For someone outside your network to connect, you would normally need to open a port on your router and expose your public IP. In practice this almost always fails because:

  • Many ISPs (especially mobile data providers) put customers behind CGNAT β€” you don't even have a real public IP to forward
  • Even with a home router you control, the firewall rules are fiddly and the IP changes
  • Mobile networks routinely block inbound connections at the carrier level, regardless of what your router does

bore pub solves this by creating a secure tunnel from your machine to a public relay, giving your server an instant public address without touching your router.


What is bore?

bore is an open-source TCP tunnel tool written in Rust by Eric Zhang (@ekzhang).

When you run the NoEyes server, it automatically tries to start:

bore local 5000 --to bore.pub

This punches a tunnel from your local port 5000 to bore.pub, a free public relay. The relay assigns you a random port and prints an address like:

bore.pub:12345

You share that address with your friends β€” they connect with:

python noeyes.py --connect bore.pub --port 12345 --key-file ./chat.key

Everything is still end-to-end encrypted. bore only forwards raw bytes β€” it cannot read your messages.

Credit: bore is created and maintained by Eric Zhang. Source: https://github.com/ekzhang/bore


bore pub limitations

Limitation Details
No uptime guarantee bore.pub is a volunteer service β€” it can go down at any time
Shared bandwidth Heavy traffic can affect other bore users
Not for production For a team or community, host your own server
Port is random Each server start gets a different port β€” reshare the address
No authentication Anyone who knows your bore.pub address can attempt to connect (your key file still protects all content)

When to use a VPS instead

Situation Recommendation
More than ~10 concurrent users VPS
Server always online 24/7 VPS
Stable hostname VPS
Short session / demo bore.pub is fine

Cheap VPS options: Hetzner (€4/mo), DigitalOcean ($4/mo), Vultr ($2.50/mo), Oracle Cloud (free tier)

# On the VPS β€” no bore needed, it has a real public IP
python noeyes.py --server --port 5000 --no-bore

Disabling bore

python noeyes.py --server --port 5000 --no-bore

Firewall rules

When you start a server, NoEyes can automatically add a firewall rule to open the server port so clients can connect directly. The launcher will ask whether you want this and explain when it is needed.

You do NOT need a firewall rule if you are using bore tunnel β€” clients connect via bore.pub and never touch your machine's firewall directly.

You need a firewall rule if clients connect to your IP directly (LAN, static IP, or manual port forwarding).

# Skip firewall rule (always safe when using bore tunnel)
python noeyes.py --server --port 5000 --no-firewall

# Skip both bore and firewall rule (VPS with its own firewall managed separately)
python noeyes.py --server --port 5000 --no-bore --no-firewall

Keeping NoEyes Up to Date

python update.py           # update to latest version
python update.py --check   # just check β€” don't change anything

Key Management

# Generate once, share out-of-band (USB / Signal / encrypted email)
# NEVER share over NoEyes itself or in plaintext
python noeyes.py --gen-key --key-file ./chat.key

# Backup your identity key
cp ~/.noeyes/identity.key /backup/identity.key

# View who you currently trust (TOFU store)
cat ~/.noeyes/tofu_pubkeys.json

Tech Stack

  • Language: Python 3.9+
  • Encryption: cryptography library β€” Fernet, X25519, Ed25519, AES-256-GCM, HKDF, PBKDF2
  • Networking: Raw TCP sockets with a custom length-prefixed framing protocol
  • Concurrency: threading (recv + input threads per client), asyncio on the server
  • Terminal: ANSI escape codes, termios for raw keypress input


⚠️Research & Educational Use Only β€” experimental project.

About

Secure terminal-based chat tool with end-to-end encryption. Cross-platform Python chat over TCP/IP using Fernet cryptography.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Languages