Skip to content

yogeshroyal63-beep/GhostId

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

41 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

GhostID

πŸ‘» GhostID

Continuous Behavioral Session Verification β€” Zero Friction for Real Users

Drop one script into any web app. GhostID silently verifies whether the person currently typing is the same person who logged in. Only impostors get interrupted.


CI License: MIT Python Node ONNX FastAPI

Built by Yogesh Rayal


What It Does Β· Architecture Β· ML Pipeline Β· Quick Start Β· Benchmarks Β· Security Β· Deploy


🧠 What It Does

Traditional session security breaks the moment a session token is stolen or a user steps away from their device. GhostID solves this without cameras, OTPs, or user friction.

After login, GhostID runs silently in the background. Every 60 seconds (configurable), it extracts a 41-feature keystroke vector from the user's typing and compares it against their enrolled behavioral baseline using an LSTM encoder + cosine similarity.

The result is a confidence score from 0–100 that maps to one of four tiers:

Score Tier Action
85–100 βœ… SILENT_PASS Session continues β€” user never knows GhostID ran
70–84 πŸ’¬ SOFT_NUDGE One-tap confirm ("Still you?")
40–69 ⌨️ TYPING_CHALLENGE Re-verify by typing a short phrase
0–39 πŸ”΄ HARD_STOP Session terminated β€” full re-authentication required

No camera. No OTP. No friction for real users. Only impostors get stopped.


🎯 Use Cases

Domain Threat Addressed
πŸŽ“ Online Exams Mid-exam handoff to a different person
🏒 Corporate / HR Ghost employees clocking in for absent colleagues
🏦 Fintech / Banking Cookie hijacking before high-value transactions
πŸ₯ Healthcare / EHR Shared workstation walk-away identity exposure
πŸ” SaaS Platforms Credential sharing across multiple users

πŸ—οΈ Architecture

GhostID is a four-layer system: a browser SDK, a FastAPI inference backend, an ONNX encoder, and a scoring engine.

╔══════════════════════════════════════════════════════════════════╗
β•‘                        USER'S BROWSER                           β•‘
β•‘                                                                  β•‘
β•‘   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β•‘
β•‘   β”‚                    ghostid.js (SDK)                     β”‚   β•‘
β•‘   β”‚                                                          β”‚   β•‘
β•‘   β”‚  β€’ Passively captures keystroke press/release timings   β”‚   β•‘
β•‘   β”‚  β€’ Extracts 41 behavioral features per window           β”‚   β•‘
β•‘   β”‚  β€’ Runs comparison every 60s (tunable interval)         β”‚   β•‘
β•‘   β”‚  β€’ Reacts to tier decision (nudge / challenge / stop)   β”‚   β•‘
β•‘   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β•‘
β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•ͺ════════════════════════════════════════╝
                          β”‚
              POST /enroll  (Γ—2 for baseline)
              POST /score   (every 60 seconds)
                          β”‚
                          β–Ό
╔══════════════════════════════════════════════════════════════════╗
β•‘                   FASTAPI BACKEND (Python)                      β•‘
β•‘                                                                  β•‘
β•‘   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β•‘
β•‘   β”‚  Auth & Rate Limiting                                   β”‚   β•‘
β•‘   β”‚  β€’ API key via X-GhostID-Key header                     β”‚   β•‘
β•‘   β”‚  β€’ 100 req/min per user (configurable)                  β”‚   β•‘
β•‘   β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€   β•‘
β•‘   β”‚  Feature Processing                                     β”‚   β•‘
β•‘   β”‚  β€’ Normalize 41 features with saved scaler_params.json  β”‚   β•‘
β•‘   β”‚  β€’ Fernet-encrypt embeddings before SQLite storage      β”‚   β•‘
β•‘   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β•‘
β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•ͺ════════════════════════════════════════╝
                          β”‚
                          β–Ό
╔══════════════════════════════════════════════════════════════════╗
β•‘                  ONNX ENCODER (Inference)                       β•‘
β•‘                                                                  β•‘
β•‘   Input  β†’ 41 scaled keystroke features                         β•‘
β•‘   Model  β†’ Bidirectional LSTM β†’ 128-dim bottleneck              β•‘
β•‘   Output β†’ 128-dim L2-normalized behavioral embedding           β•‘
β•‘   Size   β†’ ~60 KB  |  Latency β†’ <1ms CPU inference             β•‘
β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•ͺ════════════════════════════════════════╝
                          β”‚
                          β–Ό
╔══════════════════════════════════════════════════════════════════╗
β•‘               SIMILARITY SCORING & TIER ENGINE                  β•‘
β•‘                                                                  β•‘
β•‘   β€’ Cosine similarity: live embedding vs. enrolled baseline     β•‘
β•‘   β€’ Scale raw similarity β†’ 0–100 confidence score              β•‘
β•‘   β€’ Map score β†’ [SILENT_PASS | SOFT_NUDGE | CHALLENGE | STOP]  β•‘
β•‘   β€’ Return action decision to browser SDK                       β•‘
β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

Data Flow Summary

User types  β†’  ghostid.js collects timings
           β†’  41 features extracted client-side
           β†’  POST /score to FastAPI
           β†’  Feature scaling + ONNX inference
           β†’  128-dim embedding compared to baseline
           β†’  Confidence score computed
           β†’  Tier decision returned
           β†’  SDK reacts (silent / nudge / challenge / stop)

πŸ“ Project Structure

GhostId/
β”œβ”€β”€ backend/                        # FastAPI inference server
β”‚   β”œβ”€β”€ app/
β”‚   β”‚   β”œβ”€β”€ core/                  # Config, security, crypto helpers
β”‚   β”‚   β”œβ”€β”€ db/                    # SQLite database layer
β”‚   β”‚   β”œβ”€β”€ models/                # Pydantic request/response schemas
β”‚   β”‚   β”œβ”€β”€ routes/                # API endpoints: /enroll, /score, /status
β”‚   β”‚   β”œβ”€β”€ services/              # Business logic (scoring, enrollment)
β”‚   β”‚   └── middleware/            # API key auth, rate limiting
β”‚   β”œβ”€β”€ ml/                        # ONNX model + scaler_params.json
β”‚   β”œβ”€β”€ tests/                     # pytest suite (10+ test cases)
β”‚   β”œβ”€β”€ main.py                    # FastAPI application entry point
β”‚   β”œβ”€β”€ requirements.txt           # Python dependencies
β”‚   └── .env.example               # Environment variable template
β”‚
β”œβ”€β”€ frontend/                       # React + Vite demo app
β”‚   β”œβ”€β”€ src/
β”‚   β”‚   β”œβ”€β”€ components/            # UI components (tiers, nudge, challenge)
β”‚   β”‚   β”œβ”€β”€ hooks/                 # useKeystroke β€” live feature capture hook
β”‚   β”‚   └── utils/                 # API client, tier mapping utilities
β”‚   β”œβ”€β”€ package.json
β”‚   └── vite.config.js
β”‚
β”œβ”€β”€ sdk/                            # Drop-in browser library
β”‚   β”œβ”€β”€ ghostid.js                 # Main SDK β€” add to any web app
β”‚   └── features.js                # 41-feature extraction engine
β”‚
β”œβ”€β”€ ml/                             # Model training & evaluation
β”‚   β”œβ”€β”€ notebooks/
β”‚   β”‚   └── ghostid_training.ipynb      # Full training pipeline
β”‚   β”œβ”€β”€ models/                    # Saved ONNX + scaler params
β”‚   └── analysis/                  # Benchmark plots, EER curves
β”‚
β”œβ”€β”€ .github/
β”‚   └── workflows/
β”‚       └── ci.yml                 # GitHub Actions: lint, test, build, ONNX validate
β”‚
β”œβ”€β”€ DEPLOYMENT.md                  # Production setup guide (Docker, Railway, Vercel)
β”œβ”€β”€ LICENSE                        # MIT License
└── README.md

🧬 ML Pipeline

GhostID trains an LSTM auto-encoder using ArcFace loss on the CMU DSL Keystroke Dynamics Dataset.

Feature Extraction (41 Features)

Each keystroke window produces 41 features capturing the rhythm of a user's typing:

β€’ Dwell time         β€” how long each key is held down
β€’ Flight time        β€” time between key release and next key press
β€’ Latency metrics    β€” inter-key timing distributions
β€’ Speed-invariant ratios β€” angle features that normalize for typing speed

Model Architecture

Input (41 features)
       β”‚
       β–Ό
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚           Feature Scaling (saved scaler)        β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                             β”‚
                             β–Ό
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚         Bidirectional LSTM Encoder              β”‚
  β”‚   β€’ 2Γ— BiLSTM layers with dropout               β”‚
  β”‚   β€’ Bottleneck: 128-dimensional embedding       β”‚
  β”‚   β€’ L2 normalization on output                  β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                             β”‚
                             β–Ό
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚   ArcFace Loss (angular margin m = 0.5)         β”‚
  β”‚   β€’ Pulls same-user embeddings together         β”‚
  β”‚   β€’ Pushes different-user embeddings apart      β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                             β”‚
                             β–Ό
  128-dim L2-normalized behavioral embedding
  β†’ enables fast cosine similarity at inference time

Training Steps

Step 1 β€” Download the dataset

# CMU DSL Keystroke Dynamics Dataset
# File: DSL-StrongPasswordData.csv
# Available on Kaggle: search "CMU DSL Keystroke Dynamics"
cd ml/

Step 2 β€” Run the training notebook

  1. Open the Kaggle Notebook for GhostID v3 Training
  2. Attach dataset: CMU-DSL/DSL-StrongPasswordData.csv
  3. Run all cells (~5 minutes with GPU T4)
  4. Download outputs: ghostid_encoder.onnx and scaler_params.json

Step 3 β€” Deploy the models

cp ghostid_encoder.onnx ../backend/ml/
cp scaler_params.json   ../backend/ml/

Mock mode: Without these files in backend/ml/, the backend auto-falls back to placeholder scoring β€” great for frontend development without a trained model.


πŸ“Š Benchmark Results

Evaluated on the CMU DSL Keystroke Dynamics Dataset (51 users, 400 sessions/user, 20,400 sessions total):

Metric Value What It Means
FAR (False Accept Rate) 2.1% Impostors accepted as legitimate
FRR (False Reject Rate) 3.8% Legitimate users incorrectly challenged
EER (Equal Error Rate) ~3.0% Balanced operating point
Inference Latency < 1ms Per scoring query on CPU
Model Size ~60 KB ONNX format β€” tiny and deployable
Enrollment Sessions 2 Minimum required to build baseline
Baseline Validity 7 days Before re-enrollment is recommended

At EER ~3.0%, GhostID is more accurate than many OTP systems while requiring zero user action for the 96%+ of sessions that are legitimate.


πŸš€ Quick Start

Prerequisites

  • Python 3.11+ with pip
  • Node.js 20+ with npm
  • SQLite (bundled with Python)

1️⃣ Backend Setup

cd backend

# Create and activate virtual environment
python -m venv .venv
source .venv/bin/activate          # macOS/Linux
# .venv\Scripts\activate           # Windows

# Install dependencies
pip install -r requirements.txt

# Configure environment
cp .env.example .env
# Edit .env β€” set API_KEY and EMBEDDING_ENCRYPTION_KEY

# Initialize the database
python -c "from app.db.database import init_db; init_db(); print('βœ… DB initialized')"

# Start the API server
python main.py

Backend: http://localhost:8000 Β· API Docs: http://localhost:8000/docs


2️⃣ Frontend Setup

cd frontend

npm install
cp .env.example .env
# Set VITE_API_URL=http://localhost:8000 and VITE_API_KEY

npm run dev

Frontend: http://localhost:5173


3️⃣ SDK Integration (Drop-in)

Add GhostID to any existing web app in two lines:

<script src="ghostid.js"></script>
<script>
  GhostID.init({
    userId: 'user-123',
    apiUrl: 'https://your-backend.railway.app',
    apiKey: 'your-api-key',
    intervalSeconds: 60,
    onAction: (tier, score) => {
      if (tier === 'HARD_STOP') redirectToLogin();
    }
  });
</script>

4️⃣ Run Tests

cd backend

# Full test suite
pytest tests/test_endpoints.py -v

# With coverage report
pytest tests/test_endpoints.py --cov=app --cov-report=term-missing

Test coverage includes:

  • βœ… Health check endpoint
  • βœ… User enrollment (single & multiple sessions)
  • βœ… Enrollment status retrieval
  • βœ… Profile deletion
  • βœ… Scoring for enrolled and non-enrolled users
  • βœ… Input validation and error handling

πŸ” Security

GhostID is built security-first:

Feature Details
API Key Auth Bearer token via X-GhostID-Key header on every request
Rate Limiting 100 requests/min per user β€” configurable
Encrypted Embeddings Fernet symmetric encryption for all stored embeddings
CORS Protection Configurable allowed origins list
Input Validation Strict 41-feature vector schema via Pydantic
No Raw Keystrokes Stored Only statistical feature vectors β€” GDPR friendly
Secure Defaults .env.example template with guidance on key generation

🚒 Deployment

Docker (Recommended)

# Configure environment
cp backend/.env.example backend/.env
# Edit backend/.env with your keys

# Build and run everything
docker compose up --build

Services start on:

  • Backend API: http://localhost:8000
  • Frontend: http://localhost:5173

Production: Backend β†’ Railway

# In Railway dashboard:
# New Project β†’ Deploy from GitHub β†’ Root: backend/
# Start command:
uvicorn main:app --host 0.0.0.0 --port $PORT

# Environment variables to set:
EMBEDDING_ENCRYPTION_KEY=<generated-key>
API_KEY=<your-api-key>
DATABASE_URL=sqlite:///ghostid.db

Production: Frontend β†’ Vercel

# In Vercel dashboard:
# Root: frontend/ | Build: npm run build | Output: dist/

# Environment variables:
VITE_API_URL=https://your-backend.up.railway.app
VITE_API_KEY=your-api-key

Post-Training Deployment Checklist

☐  Run ml/notebooks/ghostid_training.ipynb on Kaggle (GPU ~5 min)
☐  Copy ghostid_encoder.onnx   β†’ backend/ml/
☐  Copy scaler_params.json     β†’ backend/ml/
☐  Copy analysis PNGs          β†’ ml/analysis/
☐  Re-run CI β€” ONNX validation job should pass
☐  Set production environment variables
☐  Deploy backend to Railway
☐  Deploy frontend to Vercel

❓ FAQ

Q: Does GhostID work on mobile browsers? Yes β€” the JavaScript SDK captures touch-keyboard keystroke timings. Feature extraction adapts to on-screen keyboards automatically.

Q: What if the ONNX model files aren't present? The backend runs in mock mode with placeholder scoring. This lets frontend developers build and test the UI without a trained model.

Q: Can GhostID detect copy-paste attacks? Yes β€” copy-pasted input produces zero inter-key flight time, which scores as a significant behavioral anomaly.

Q: Is keystroke data stored in the clear? No. Raw keystrokes are never transmitted. Only the 41-feature statistical vector is sent, and embeddings stored in the database are Fernet-encrypted.

Q: How long does enrollment take? Just 2 typing sessions. The baseline is valid for 7 days by default, after which re-enrollment is recommended to account for natural behavioral drift.

Q: What's the performance impact of running GhostID? Negligible. The JS SDK runs asynchronously, ONNX inference is <1ms CPU, and scoring only fires every 60 seconds.


πŸŽ₯ Demo

GhostID Demo

Live demo: ghost-id-rho.vercel.app


🀝 Contributing

Contributions are welcome β€” bug fixes, new features, documentation, and test coverage.

# 1. Fork the repo and clone your fork
git clone https://github.com/your-username/GhostId.git

# 2. Create a feature branch
git checkout -b feature/my-improvement

# 3. Make your changes and commit
git commit -m "feat: add my improvement"

# 4. Push and open a Pull Request
git push origin feature/my-improvement

All PRs must pass CI (lint, tests, build, ONNX validation) before merging.


πŸ“„ License

This project is licensed under the MIT License β€” see LICENSE for details.

You are free to use, modify, and distribute this project commercially or privately. Just include the license notice.


πŸ“ž Support & Docs


GhostID β€” Your users have a typing fingerprint. GhostID watches it.

Built with ❀️ by Yogesh Rayal

About

AI-powered continuous authentication that prevents session hijacking using behavioral biometrics with zero user friction.

Topics

Resources

License

Code of conduct

Contributing

Stars

1 star

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors