
安全なターミナルチャット。 E2E暗号化、ゼロメタデータのブラインドフォワーダーサーバー。 PyNaCl XSalsa20-Poly1305 + Ed25519 + forward secrecy。 クロスプラットフォーム Python。
エンドツーエンド暗号化されたグループチャット、プライベートメッセージ、ファイル転送をターミナルで。サーバーはブラインドフォワーダーです:メッセージを読むことができず、ユーザー名を知らず、参加しているルームを知らず、たとえ完全に侵害されても、同じ人物からの2つのメッセージを関連付けることができません。
https://github.com/user-attachments/assets/d9faabfb-73bd-46dd-92b2-23f63daf5b06
https://github.com/user-attachments/assets/e8e0220d-cd7d-45a2-9443-9a5f20b57f12
https://github.com/user-attachments/assets/15fb383d-a02a-433e-bbd9-8ebadecf9481
https://github.com/user-attachments/assets/bca10cb1-6959-425d-96d6-fc1fbf845538
NoEyesは、信頼できる少人数グループ向けのPythonターミナルチャットツールです。サーバーは決して何も復号化せず、あなたの身元を知ることもありません - 不透明なトークンを処理し、暗号化されたバイトを転送するだけです。
あなたが鍵を生成し、帯域外で共有すれば、サーバーはあなたの会話について何も知ることはありません。
第三者サーバーを信頼せずに暗号化通信を望む信頼できる少人数グループ、真のエンドツーエンド暗号化でプライベートチャットをセルフホスティングしたい方、またはサーバーが何を見ることができて何を見ることができないかを正確に理解したい方に役立ちます。
python ui/setup.py
python ui/launch.py
`ui/launch.py` は、サーバーを起動するか、既存のサーバーに接続する手順を案内します。
---
### オプション B - Python がまだインストールされていない場合
| プラットフォーム | 最初に実行するもの |
|---|---|
| Linux / macOS / Termux / iSH | `sh install/install.sh` |
| Windows | `install\install.bat` |
どちらのスクリプトも、Python が不足している場合はインストールし、自動的に `setup.py` に処理を引き継ぎます。
---
### オプション C - 手動```bash
# 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
重要: 各ユーザーは独自のIDファイルを持たなければなりません。2人のクライアントが同じIDファイルを共有すると、同じ受信トレイトークンが取得され、重複セッションとしてサーバーに拒否されます。IDファイルは初回実行時に自動生成されます。ユーザーごとに一意の
--identity-pathを指定してください。
Termux は F-Droid(推奨)からダウンロード:https://f-droid.org/packages/com.termux/
セッションを維持 - tmux をインストールして、アプリを切り替えても NoEyes が実行され続けるようにします:```bash pkg install tmux -y tmux python ui/launch.py
**ストレージ権限** - これがないとファイル転送は失敗します:```bash
termux-setup-storage
▶で強調表示。各半分は独立してスクロールします。**^P**を押すとパネルを非表示にして全幅チャットビューを表示します。
メッセージの先頭に!tagを付けると、全員に対して色を付け、通知音をトリガーします。タグは暗号化ペイロード内部で伝送され、サーバーがそれらを見ることはありません。
例:``` !danger server is going down in 5 minutes !ok deployment successful !req can someone review my PR?
サウンドは `sfx/` フォルダから再生されます。タグ名に対応する `.wav`、`.mp3`、`.ogg`、`.aiff`、`.flac`、または `.m4a` ファイルを配置します(例:`sfx/danger.wav`)。見つからない場合はターミナルベルにフォールバックします。全サウンドを無効にするには `/notify off` を使用します。
---
## アーキテクチャ
> 🗺️ **[ライブインタラクティブセキュリティマップ](https://ymsniper.github.io/NoEyes/)** — 完全な暗号化アーキテクチャ、脅威モデル、ゼロメタデータルーティングのビジュアルな内訳をインタラクティブなダイアグラムで提供します。```
┌──────────────────────────────────────────────────────────────────────┐
│ 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
接続前に各クライアントは、ローカルで2つの不透明なトークンを計算します。``` inbox_token = blake2s(identity_vk_bytes, digest_size=16) room_token = blake2s((room_name + group_key_hex).encode(), digest_size=16)
サーバーはこれらのトークンのみで全てのフレームをルーティングします。表示名、ルーム名、公開鍵を保存することはありません。送信者の身元はルーティングヘッダーではなく、暗号化されたペイロード(シールド送信者)**の中を**伝わります。
### 鍵導出チェーン```
chat.key (shared secret)
│
├─ BLAKE2b("general") ──► room_key["general"] (isolated per room)
├─ BLAKE2b("dev") ──► room_key["dev"]
└─ BLAKE2b("ops") ──► room_key["ops"]
X25519 DH (per user pair, automatic on first /msg)
alice_ephemeral + bob_ephemeral ──► shared_secret
│
BLAKE2b
│
pairwise_key (private messages)
│
BLAKE2b(transfer_id) ──► chacha20_key (files)
password + random_salt (32 bytes, os.urandom) │ └─ BLAKE2b(password, key=salt, person="identity_v2") │ derived_key ──► encrypts Ed25519 signing key at rest
各IDファイルにはユニークなランダムソルトが割り当てられ、レインボーテーブルは無効です。
---
## セキュリティ概要
| レイヤー | メカニズム | 備考 |
|---|---|---|
| 前方秘匿性(ラチェット) | Sender Keys — BLAKE2b chain KDF + XSalsa20-Poly1305 per message | メッセージごとにユニークな鍵、取りこぼしたメッセージの高速転送 |
| グループチャット | XSalsa20-Poly1305 (PyNaCl secretbox) | ルームごとの鍵をBLAKE2bで生成 |
| プライベートメッセージ | XSalsa20-Poly1305 with X25519 pairwise key | Ed25519署名付き、TOFU検証済み |
| ファイル転送 | ChaCha20-Poly1305 | 転送ごとの鍵をBLAKE2bで生成、Ed25519署名付き、再接続をまたいだ一時停止/再開 |
| 送信者ID | Sealed sender | ユーザー名と署名を暗号化ペイロード内に含み、ルーティングヘッダーには決して含まれない |
| ID | Ed25519 keypair | ユーザーごとのIDファイル、BLAKE2bとランダムソルトでパスワード暗号化 |
| 鍵導出 | BLAKE2b (PyNaCl) | パーソナライゼーションパラメータでドメイン分離、レインボーテーブル無効 |
| サーバールーティング | Opaque blake2s tokens | サーバーはユーザー名、ルーム名、公開鍵を決して保存しない |
| トランスポート | TLS (on by default) | TOFU証明書ピンニング、フィンガープリント不一致で接続中断 |
| DH整合性 | Ed25519-signed DH pubkeys | ペア鍵交換におけるMITMを防止 |
| リプレイ保護 | Per-room message ID deque | リプレイされたフレームは静かに破棄 |
| DoS保護 | Connection cap + join timeout + rate limiting | 最大200接続、10秒の参加タイムアウト |
| ルーム分離 | `BLAKE2b(master_key, room_name)` | ルームごとに暗黙的に分離 |
### 脅威モデル
NoEyesは**少人数の信頼されたグループ**向けに設計されています。以下に対する強力な保護を提供します:
- 受動的ネットワーク観測者 - すべてのトラフィックはTLS + E2E暗号化
- 侵害されたbore.pubリレー - リレーは暗号化バイトと接続タイミングのみを参照
- 侵害されたサーバーマシン - サーバーはゼロ知識、RAMに有用なものはなし
- 接続のMITM - TLS証明書ピンニング + Ed25519署名付きDH鍵
- デバイスの盗難 - ID鍵は保存時にパスワード暗号化
- リプレイ攻撃 - MIDベースのルームごとのリプレイ保護
---
## サーバーをオンラインで実行する(bore pub)
自宅でNoEyesサーバーを起動すると、マシンはローカルIPを取得します。外部のネットワークから接続するには通常、ルーターでポート転送を行う必要がありますが、CGNATやキャリアレベルのブロッキングにより失敗することがよくあります。
**bore pub**は、マシンから公開リレーへの安全なトンネルでこれを解決し、ルーターに触れることなくサーバーに即座に公開アドレスを提供します。
**bore**は[Eric Zhang (@ekzhang)](https://github.com/ekzhang/bore)によるオープンソースのTCPトンネルツールです。NoEyesサーバーを実行すると自動的に起動します:```
bore local 5000 --to bore.pub
リレーはランダムなポートを割り当て、bore.pub:12345 のようなアドレスを表示します。それをグループで共有してください:```bash
python noeyes.py --connect bore.pub --port 12345 --key-file ./chat.key --username alice --identity-path ~/.noeyes/identity_alice.key
すべては依然としてエンドツーエンド暗号化されており、boreは生のバイトを転送するだけです。
### boreポート変更時の自動再接続
bore.pubはサーバー再起動ごとに**ランダムなポート**を割り当てます。通常は毎回全員とアドレスを共有し直す必要があります。NoEyesはこれを3つの復旧層で自動的に処理します:
**1. 移行イベント(即時)**
boreがポートを再割り当てすると、サーバーは接続中の全クライアントに新しいポート番号を含む署名済みの`migrate`イベントをブロードキャストします。クライアントは静かに切断し、ポートを更新し、自動的に再接続します。15秒の静寂期間により参加/離脱のノイズが抑えられ、チャット画面がちらつきません。
**2. ディスカバリサービス(移行を見逃したクライアント向け)**
ポート変更時にクライアントがオフラインだった場合、再接続試行のたびに無料の匿名キーバリューサービス(`keyvalue.immanuel.co`)にポーリングします。サーバーはboreが再起動するたびに新しいboreポートを自動的にそこに投稿します。ルックアップキーはグループキーから派生し、アカウントや登録は不要で、完全に匿名です。
**3. `auth_ok`内のポート(クラッシュリカバリ)**
クライアントがすべてを見逃した場合(サーバークラッシュ、移行ブロードキャスト未送信)、サーバーは`auth_ok`ハンドシェイク応答に現在のboreポートを含めます。クライアントは次回の成功した接続時に自己修正します。
bore.pubのポート変更はユーザーにとって透過的です。チャットは数秒以内に自動的に継続され、ファイル転送は中断したところから再開されます。
ディスカバリを無効にするには(エアギャップ環境またはプライベートリレー):```bash
python noeyes.py --connect bore.pub --port 12345 --key-file ./chat.key --no-discovery
| 制限 | 詳細 |
|---|---|
| 稼働時間の保証なし | bore.pub はボランティアサービスであり、ダウンする可能性があります |
| ポートはランダム | サーバー起動のたびに異なるポートが割り当てられるため、アドレスを共有し直す必要があります |
| 本番環境向けではない | 恒久的なセットアップには、--no-bore オプションを使用してVPSを利用してください |
約10人以上のユーザー、24時間365日の稼働時間、または安定したホスト名が必要な場合は、安価なVPS(Hetzner €4/月、DigitalOcean $4/月、Oracle Cloud無料ティア)で実行してください:```bash python noeyes.py --server --port 5000 --no-bore
### Firewallの注意事項
boreトンネルを使用する場合、ファイアウォールルールは**必要ありません**。直接接続(LAN、静的IP、手動ポート転送)の場合のみ必要です:```bash
python noeyes.py --server --port 5000 --no-firewall # bore tunnel, skip firewall rule
python noeyes.py --server --port 5000 --no-bore --no-firewall # VPS, manage firewall separately
python noeyes.py --generate-access-key
python noeyes.py --generate-chat-key <ACCESS_CODE_HEX> --key-file ./chat.key
python ui/launch.py # → Generate Key
cp ~/.noeyes/identity.key /backup/identity.key
cat ~/.noeyes/tofu_pubkeys.json
---
## プロジェクト構造```
NoEyes/
├── noeyes.py Entry point and CLI argument parser
├── requirements.txt pip dependencies (just: cryptography)
│
├── core/
│ ├── encryption.py All crypto: XSalsa20-Poly1305, ChaCha20-Poly1305, X25519, Ed25519, BLAKE2b
│ ├── ratchet.py Sender Keys forward secrecy: SenderChain + RatchetState
│ ├── animation.py CRT boot and ratchet activation animations with SFX
│ ├── sounds.py Cross-platform sound playback (WAV/MP3, Linux/macOS/Windows)
│ ├── identity.py Ed25519 keypair generation and TOFU pubkey store
│ ├── utils.py Terminal output, ANSI colours, TUI chrome
│ └── config.py Configuration loading and CLI parsing
│
├── network/
│ ├── server.py Async zero-metadata blind-forwarder server
│ ├── client.py Terminal chat client (E2E, DH, TOFU, file transfer)
│ ├── client_ratchet.py RatchetMixin — /ratchet command flow, migration wait
│ ├── client_dh.py X25519 DH handshake mixin
│ ├── client_send.py Outgoing message encryption (static + ratchet paths)
│ ├── client_recv.py Incoming frame routing and decryption
│ └── client_commands.py Input loop, command dispatch, help
│
├── ui/
│ ├── launch.py Guided launcher, arrow-key menu UI
│ └── setup.py Dependency wizard, auto-installs what's needed
│
├── install/
│ ├── install.sh Bootstrap for Linux / macOS / Termux / iSH
│ ├── install.bat Bootstrap for Windows (CMD and PowerShell)
│ ├── install.py Cross-platform Python installer
│ └── uninstall.py Remove all NoEyes dependencies for clean reinstall
│
├── docs/
│ ├── README.md This file
│ └── CHANGELOG.md Version history
│
├── update.py Self-updater, pulls latest from GitHub
└── sfx/ Notification sounds
PyNaCl (XSalsa20-Poly1305, BLAKE2b) + cryptography (ChaCha20-Poly1305, X25519, Ed25519, TLS)threading (クライアントごとの受信 + 入力 + 送信スレッド), サーバー側でasynciotermios⚠️ 研究および教育目的のみ - 実験的なプロジェクトです。
| 機能 | 詳細 |
|---|
| ゼロメタデータサーバー | サーバーはユーザー名、ルーム名、公開鍵を決して見ず、不透明なトークンのみ扱う |
| シールドセンダー | 送信者の身元は暗号化ペイロード内にあり、ルーティングヘッダーには決して含まれない |
| ブラインドフォワーダーサーバー | 復号化はゼロ、サーバーは読み取れない暗号化ブロブを転送する |
| 前方秘匿性 | /ratchet start — Sender Keysプロトコル、各メッセージは一意の派生鍵で暗号化され、現在の鍵が漏洩しても過去のメッセージは安全 |
| グループチャット | ルームごとにBLAKE2bで派生したXSalsa20-Poly1305鍵、ルームは暗号的に分離 |
| プライベートメッセージ | 最初の接触時にX25519 DHハンドシェイク、ペアワイズ鍵は二者のみが保持 |
| ファイル転送 | ChaCha20-Poly1305ストリーミング、任意のサイズ、低RAM使用、再接続時の一時停止/再開 |
| Ed25519アイデンティティ | 自動生成された署名鍵、すべてのメッセージとファイルは署名される |
| TOFU | 初回に見た鍵を信頼;鍵の不一致は目に見えるセキュリティ警告を引き起こす |
| ランダムPBKDF2ソルト | 各デプロイは一意のランダムソルトを取得し、レインボーテーブルは無効 |
| TLS + 証明書ピンニング | トランスポートは暗号化、サーバー証明書は初回接触時にTOFUでピン留め |
| リプレイ保護 | ルームごとのメッセージIDデック、再生されたフレームは静かにドロップ |
| 分割サイドバーパネル | ルーム(上)とユーザー(下)は常に表示、各半分は独立してスクロール |
| CRT起動アニメーション | 起動時にサウンド付きのフルスクリーン蛍光体効果 |
| ラチェット起動アニメーション | 点字ギアアート、グリッチフリッカー、スポットライトスイープ、同期されたSFX、TUIクロムが赤に遷移するフルスクリーンCRT効果 |
| ガイド付きランチャー | 矢印キーメニューUI、コマンドラインの経験は不要 |
| 自動依存関係インストーラ | プラットフォームを検出し、不足しているものをインストールし、変更前に確認を求める |
| コマンド | 説明 |
|---|
/help | すべてのコマンドを表示 |
/quit | 切断して終了 |
/clear | 画面からメッセージを消去 |
/users | 現在のルームのユーザーを一覧表示 |
/join <room> | ルームに切り替え(アクティブなラチェットがある場合は警告) |
/leave | 一般ルームに戻る(アクティブなラチェットがある場合は警告) |
/msg <user> <text> | E2E暗号化されたプライベートメッセージを送信 |
/send <user> <file> | 暗号化ファイルを送信 |
/whoami | 自分のアイデンティティフィンガープリントを表示 |
/trust <user> | ユーザーが再インストール後に新しいキーを信頼する |
/notify on|off | 通知音の切り替え |
/ratchet start | 転送秘密性のローリングキーをすべてのルームメンバーに提案(全員が確認する必要あり) |
/ratchet invite <u> | ユーザーが再参加した後にラチェットに再招待(フル再起動をトリガー、チェーンキーは転送されません) |
/proceed | 移行待機中、オフラインのピアをドロップして再開する投票 |
| キー | アクション |
|---|
↑ / ↓ | チャットを上下にスクロール |
PgUp / PgDn | チャットを1ページスクロール |
^P (Ctrl+P) | サイドバーパネルの表示/非表示 |
^C | 終了 |
| タグ | 色 | 使用目的 |
|---|
!ok <msg> | 🟢 緑 | 成功、確認、完了 |
!warn <msg> | 🟡 黄 | 警告、注意 |
!danger <msg> | 🔴 赤 | 重大、緊急、緊急時 |
!info <msg> | 🔵 青 | ステータス更新、参考情報 |
!req <msg> | 🟣 紫 | リクエスト、アクションが必要 |
!? <msg> | 🩵 シアン | 質問、意見を求める |
| プラットフォーム | 使用するパッケージマネージャ |
|---|
| 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 (未インストール時は自動インストール) |
| Android (Termux) | pkg |
| Windows | winget / Chocolatey / Scoop |