SkillAgentSearch skills...

NoEyes

Secure terminal chat. E2E encrypted, zero-metadata blind-forwarder server. PyNaCl XSalsa20-Poly1305 + Ed25519 + forward secrecy. Cross-platform Python.

Install / Use

/learn @Ymsniper/NoEyes
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

NoEyes 🔒 Secure Terminal Chat

End-to-end encrypted group chat, private messages, and file transfer in your terminal. The server is a blind forwarder: it cannot read your messages, does not know your username, does not know the room you are in, and cannot link any two messages to the same person, even if fully compromised.

See it in action

Full Showcase - 3 clients, rooms, private messages, sidebar panel

https://github.com/user-attachments/assets/d9faabfb-73bd-46dd-92b2-23f63daf5b06


install.sh - Bootstrap installer

https://github.com/user-attachments/assets/e8e0220d-cd7d-45a2-9443-9a5f20b57f12


install.py - Universal Python installer

https://github.com/user-attachments/assets/15fb383d-a02a-433e-bbd9-8ebadecf9481


Server - Guided launcher startup

https://github.com/user-attachments/assets/bca10cb1-6959-425d-96d6-fc1fbf845538


What is NoEyes?

NoEyes is a Python terminal chat tool for small trusted groups. The server never decrypts anything and never sees who you are - it only handles opaque tokens and forwards encrypted bytes.

You generate the key, share it out-of-band, and the server learns nothing about your conversations.

Useful for small trusted groups who want encrypted comms without trusting any third-party server, self-hosting a private chat with true end-to-end encryption, or anyone who wants to understand exactly what a server can and cannot see.


Features

| Feature | Details | |---|---| | Zero-metadata server | Server never sees usernames, room names, or public keys, only opaque tokens | | Sealed sender | Sender identity lives inside the encrypted payload, never in the routing header | | Blind-forwarder server | Zero decryption, server forwards encrypted blobs it cannot read | | Forward secrecy | /ratchet start — Sender Keys protocol, each message encrypted with a unique derived key, past messages safe even if current key leaks | | Group chat | Per-room XSalsa20-Poly1305 keys derived via BLAKE2b, rooms cryptographically isolated | | Private messages | X25519 DH handshake on first contact, pairwise key only the two parties hold | | File transfer | ChaCha20-Poly1305 streaming, any size, low RAM usage, pause/resume across reconnects | | Ed25519 identity | Auto-generated signing key, all 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 | | TLS + cert pinning | Transport encrypted, server cert pinned on first contact via TOFU | | Replay protection | Per-room message ID deque, replayed frames silently dropped | | Split sidebar panel | Rooms (top) and users (bottom) always visible, each half scrolls independently | | CRT boot animation | Full-screen phosphor effect with sound on startup | | Ratchet activation animation | Full-screen CRT effect with braille gear art, glitch flicker, spotlight sweep, synced SFX, and TUI chrome transition to red | | 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 |


Quick Start

Option A - Guided (recommended for beginners)

# 1. Run the setup wizard - installs Python, pip, and dependencies 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.


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.


Option C - Manual

# 1. Install dependencies
pip install cryptography PyNaCl

# 2. On the server machine — generate the access key
python noeyes.py --generate-access-key
# Prints an access code hex string — share with clients via USB

# 3. On a client machine — generate chat.key from the access code
python noeyes.py --generate-chat-key <ACCESS_CODE_HEX> --key-file ./chat.key
# Distribute chat.key to all other clients via USB. Never put it on the server.

# 4. 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

# 5. Connect clients - each person needs their own identity file
python noeyes.py --connect SERVER_IP --port 5000 --username alice --key-file ./chat.key --identity-path ~/.noeyes/identity_alice.key
python noeyes.py --connect SERVER_IP --port 5000 --username bob   --key-file ./chat.key --identity-path ~/.noeyes/identity_bob.key

Important: Every user must have their own identity file. Two clients sharing the same identity file get the same inbox token and the server will reject the second one as a duplicate session. The identity file is auto-generated on first run, just pass a unique --identity-path per user.


Running on Termux (Android)

Download Termux from F-Droid (recommended): https://f-droid.org/packages/com.termux/

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 without this:

termux-setup-storage

In-Chat Commands

| Command | Description | |---|---| | /help | Show all commands | | /quit | Disconnect and exit | | /clear | Clear messages from screen | | /users | List users in current room | | /join <room> | Switch to a room (warns if in active ratchet) | | /leave | Return to the general room (warns if in active ratchet) | | /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 | | /notify on\|off | Toggle notification sounds | | /ratchet start | Propose forward-secrecy rolling keys to all room members (all must confirm) | | /ratchet invite <u> | Re-invite a user to the ratchet after they rejoin (triggers full restart — no chain keys forwarded) | | /proceed | During migration wait, vote to drop an offline peer and resume |


TUI Keyboard Shortcuts

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

Sidebar panel

  • Top half - ROOMS - all rooms joined this session. Active room highlighted with .
  • Bottom half - USERS - everyone currently in your active room.

Each half scrolls independently. Press ^P to hide the panel for a full-width chat view.


Message Tags

Prefix any message with a !tag to color it for everyone and trigger a notification sound. Tags travel inside the encrypted payload, the server never sees them.

| Tag | Color | Use for | |---|---|---| | !ok <msg> | 🟢 Green | Success, confirmed, done | | !warn <msg> | 🟡 Yellow | Warning, heads up | | !danger <msg> | 🔴 Red | Critical, urgent, emergency | | !info <msg> | 🔵 Blue | Status update, FYI | | !req <msg> | 🟣 Purple | Request, needs action | | !? <msg> | 🩵 Cyan | Question, asking for input |

Examples:

!danger server is going down in 5 minutes
!ok     deployment successful
!req    can someone review my PR?

Sounds play from sfx/ folder. Drop in .wav, .mp3, .ogg, .aiff, .flac, or .m4a files named after the tag (e.g. sfx/danger.wav). Falls back to terminal bell if not found. Use /notify off to disable all sounds.


Architecture

┌──────────────────────────────────────────────────────────────────────┐
│  Alice ──────────────────────────────────────────── Bob              │
│    │          Encrypted payload (opaque)              │              │
│    │                      │                           │              │
│    └────────────► SERVER ─┴◄──────────────────────────┘              │
│                      │                                               │
│            Zero-metadata blind forwarder:                            │
│            routes by opaque inbox tokens only                        │
│            { "to": "3f9a1c...", "type": "privmsg" }                  │
│            forwards encrypted bytes verbatim                         │
└──────────────────────────────────────────────────────────────────────┘

WHAT THE SERVER SEES:              WHAT THE SERVER NEVER SEES:
  · Encrypted bytes it can't read    · Usernames or display names
  · Opaque inbox tokens (blake2s)    · Room names
  · Opaque room tokens (blake2s)     · Who is messaging whom
  · Frame byte length                · Message content
  · Connection timing                · File contents
                                     · Ed25519 public keys
                                     · DH key exchange values

Zero-metadata routing model

Every client computes two opaque tokens locally before connecting:

inbox_token = blake2s(identity_vk_bytes, digest_size=16)
room_token  = blake2s((room_name + group_key_hex).encode(), digest_size=16)

The server routes all frames by these tokens only. It never stores display names, room names, or public keys. Sender identity travels inside the encrypted payload (sealed sender), not in the routing header.

Key derivation chain

chat.key (shared secret)
    │
    ├─ BLAKE2b("general") ──► room_key["general"]   (isolated per room)
    ├─ BLAKE2b("dev")    
View on GitHub
GitHub Stars73
CategoryDevelopment
Updated2h ago
Forks3

Languages

Python

Security Score

80/100

Audited on Mar 30, 2026

No findings