A real-time, end-to-end encrypted chat application built around post-quantum cryptography. Messages are encrypted on the sender's device using CRYSTALS-Kyber for key encapsulation and AES-GCM for symmetric encryption, then signed with CRYSTALS-Dilithium. The server never sees plaintext.
- Post-quantum encryption — ML-KEM-768 (CRYSTALS-Kyber) for key encapsulation, AES-GCM for message encryption
- Digital signatures — ML-DSA-65 (CRYSTALS-Dilithium) to authenticate every message
- Passwordless login — WebAuthn with biometrics or hardware security keys
- End-to-end encryption — private keys never leave the browser; the server stores only ciphertext
- Real-time messaging — WebSocket-based with online status indicators and chat history
| Layer | Technology |
|---|---|
| Frontend | React + Vite |
| Backend | FastAPI (Python) |
| Database | MongoDB |
| Crypto (JS) | @noble/post-quantum |
| Auth | WebAuthn + JWT (HTTP-only cookies) |
Registration — the browser generates three key pairs: a WebAuthn credential, a Kyber key pair (encryption), and a Dilithium key pair (signing). Private keys stay in the browser; public keys go to the server.
Sending a message
- Fetch the recipient's Kyber public key from the server.
- Encapsulate to produce a one-time shared secret and a KEM ciphertext.
- Encrypt the plaintext with AES-GCM using the shared secret.
- Sign the plaintext with the sender's Dilithium private key.
- Send the KEM ciphertext, AES ciphertext, IV, and signature over WebSocket.
Receiving a message
- Decapsulate the KEM ciphertext with your Kyber private key to recover the shared secret.
- Decrypt with AES-GCM.
- Fetch the sender's Dilithium public key and verify the signature.
- Node.js 18+ and npm
- Python 3.10+
- MongoDB Atlas account or a local MongoDB instance
mkcert(or any tool that can issue a locally trusted TLS certificate)
Both the frontend dev server and the backend need HTTPS locally because WebAuthn requires a secure context. Generate a certificate for localhost:
mkcert localhostThis creates localhost.pem and localhost-key.pem. Place copies in both backend/ and frontend/.
cd backend
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txtCopy .env.example to .env and fill in your values:
cp .env.example .envDATABASE_URL=mongodb+srv://<user>:<password>@cluster.mongodb.net/
SECRET_KEY=<long-random-string>
ALGORITHM=HS256
ACCESS_TOKEN_EXPIRE_MINUTES=10080
RP_ID=localhost
RP_NAME=Vajra
FRONTEND_ORIGIN=https://localhost:5173Start the server:
uvicorn src:app --reload --ssl-keyfile localhost-key.pem --ssl-certfile localhost.pemThe API is available at https://localhost:8000.
cd frontend
npm install
cp .env.example .envVITE_BACKEND_URL=https://localhost:8000npm run devOpen https://localhost:5173 in your browser.
vajra/
├── backend/
│ ├── src/
│ │ ├── __init__.py # FastAPI app, router registration
│ │ ├── config.py # Pydantic settings (reads .env)
│ │ ├── db/
│ │ │ └── main.py # MongoDB client and collections
│ │ ├── models/
│ │ │ └── auth.py # Pydantic request/response models
│ │ ├── routes/
│ │ │ ├── auth.py # Registration and login endpoints
│ │ │ ├── chat.py # WebSocket and message history
│ │ │ └── user.py # Profile, connections, public keys
│ │ ├── utils/
│ │ │ ├── auth.py # JWT creation/decoding, user lookup
│ │ │ ├── email.py # Transactional email (optional)
│ │ │ ├── login.py # WebAuthn login flow
│ │ │ └── webauthn.py # WebAuthn registration flow
│ │ └── ws/
│ │ └── connection_manager.py # WebSocket connection state
│ ├── middleware.py # CORS configuration
│ ├── requirements.txt
│ └── .env.example
│
└── frontend/
├── src/
│ ├── components/ # UI components (chat, sidebar, navbar)
│ ├── context/ # Zustand stores
│ ├── pages/ # Route-level page components
│ └── utils/
│ ├── crypto.js # PQ crypto: keygen, encrypt, sign, verify
│ ├── chatStorage.js # IndexedDB wrapper for local message store
│ └── base64.js # Base64url helpers for WebAuthn
├── vite.config.js
└── .env.example
| Variable | Description |
|---|---|
DATABASE_URL |
MongoDB connection string |
SECRET_KEY |
Secret used to sign JWTs |
ALGORITHM |
JWT algorithm (use HS256) |
ACCESS_TOKEN_EXPIRE_MINUTES |
Session duration in minutes |
RP_ID |
WebAuthn relying party ID (your domain or localhost) |
RP_NAME |
Display name shown during WebAuthn prompts |
FRONTEND_ORIGIN |
Exact origin of the frontend (e.g. https://localhost:5173) |
| Variable | Description |
|---|---|
VITE_BACKEND_URL |
Base URL of the FastAPI backend |