Skip to content

Fadi002/Keyvory

Repository files navigation

Keyvory

Keyvory is a self-hosted license management server. Generate license keys, lock licenses to hardware, manage expirations, track devices, and control feature access through a modern dashboard and secure REST API.

Run it on your own infrastructure with no third-party services, no per-seat pricing, and full control over your data.


Quick Start

git clone https://github.com/Fadi002/Keyvory.git
cd keyvory
pip install -r requirements.txt
python run.py

The first-run setup wizard generates cryptographic keys, creates an admin account, and seeds default license types. After setup, your server is running at http://localhost:5000.


Features

License Management

  • License lifecycle — Create, edit, suspend, ban, extend, or delete licenses from the dashboard or API. Each license has a unique key, status, expiration date, and optional notes.
  • Hardware locking (HWID) — Licenses bind to SHA-256 hardware fingerprints. The server enforces device limits and rejects logins from unauthorized hardware. Admins can reset or remove individual device bindings.
  • License types and templates — Define reusable tiers with configurable duration, device limits, grace periods, and feature flags. Types can be lifetime or time-limited.
  • Application scoping — Licenses are scoped to a specific application. Deactivating an application suspends all its licenses.
  • License groups — Group licenses for bulk suspend, activate, or extend operations. Useful for organization-level deployments or promotional batches.
  • Bulk operations — Create up to 500 licenses at once with full transaction rollback on failure
  • IP and device blacklisting — Blacklist IP addresses or device fingerprints with optional expiration dates.

Security & Anti-Tamper

  • Two-layer sealed envelope API — Requests/responses are encrypted with AES-256-GCM, with the AES key derived from X25519 key exchange (ephemeral keypairs + HKDF). HMAC-SHA256 integrity check over ciphertext. Ed25519 signatures for response authenticity. Timestamp-based replay protection with nonce caching.
  • Rate limiting and IP blocking — Per-endpoint rate limits (20–120 req/min for API, 60/min GET + 20/min POST for login). Automatic IP blocking after 10 failures within 5 minutes, with a 15-minute block duration.
  • Account lockout — Dashboard login locks after 5 failed attempts. Auto-resets after 30 minutes. SecurityAlert generated on lockout.
  • Security event logging — Every API call, auth attempt, and license operation is logged with severity, category, risk score, source IP, geo-location, and device info.
  • Automated threat detection — The server detects brute force attacks, credential stuffing, impossible travel, rapid license activations, API abuse, and repeated invalid license attempts. Security alerts are created with deduplication.
  • Threat intelligence dashboard — Heatmap visualization of attacks over time, timeline charts, top threat actors with risk scores, and attack vector analysis.

Client SDK

  • Single-file Python SDK — Drop keyvory_client.py into your project. Three dependencies: pycryptodome, requests, pynacl.
  • Anti-tamper protection — Module integrity hashing (detects runtime patching), debugger detection via sys.gettrace(), timing-based anomaly detection (5ms threshold), HMAC state chain for in-memory integrity.
  • Automatic session key rotation — The AES session key rotates every 100 heartbeats via the server.
  • Retry with exponential backoff — Configurable retry count and timeout for network failures.

Dashboard

  • Dark and light themes — System preference detection with manual toggle. Persisted in local storage.
  • Customizable layout — Toggle visibility of stats panels, license table columns, recent activity, and security overview.
  • Command paletteCtrl+K / Cmd+K to search licenses, navigate pages.
  • Right sheet detail panel — Click a license row to see full details, device list, and audit history in a slide-in panel.
  • Multi-select bulk actions — Shift-click to select ranges, batch activate/suspend/delete.
  • Real-time security log streaming — Server-sent events for live event updates.

Administration

  • First-run setup wizard — Interactive CLI wizard generates Ed25519 keypairs, writes .env, creates the admin account, seeds default license types (Trial, Standard, Premium, Lifetime) and an example application. Password strength meter with real-time validation.
  • Database auto-maintenance — Idempotent SQL migration engine runs on startup. Scheduled background cleanup of old security events, audit logs, resolved alerts, expired licenses, and stale clients. Retention periods are configurable. Includes SQLite VACUUM.
  • Audit trail — Every admin action is logged with user ID, IP address, target, and timestamp.

How It Works

  1. Define a license type — Set duration, device limit, and feature flags.
  2. Create an application — Scope licenses to a specific product.
  3. Generate licenses — Single key or batch, assigned to a type and application.
  4. Integrate the SDK — The client calls init()login()heartbeat().
  5. Validate on each request — Server checks HWID binding, expiration, blacklists, license status, and device capacity.
  6. Monitor activity — Security events, threat alerts, and analytics are available in the dashboard.

Tech Stack

Component Technology Role
Backend Python 3.8+, Flask 3.1 HTTP server and routing
ORM SQLAlchemy with Flask-SQLAlchemy 3.1 Database abstraction
Database SQLite (default), any SQLAlchemy-compatible URI Data storage
Templates Jinja2 Server-side HTML rendering
UI design Custom design system Geist fonts, CSS custom properties, 8pt grid
Icons Material Symbols UI iconography
Charts Chart.js 4.4 Security analytics visualizations
Auth (dashboard) Flask-Login + bcrypt Admin session management
Auth (API) HMAC-SHA256 + Ed25519 Request signing and verification
Encryption AES-256-GCM (PyCryptodome) Payload encryption
Key exchange X25519 (PyNaCl) + HKDF-SHA256 Ephemeral keypairs for envelope key derivation
Signing Ed25519 (PyNaCl) Server response signing
Rate limiting Flask-Limiter Per-endpoint request caps
CSRF Flask-WTF Dashboard form protection
WSGI server Waitress Production serving
Environment python-dotenv .env file loading
Compression Flask-Compress Gzip response compression
SDK Single-file Python Client integration

Configuration

Environment Variables

Variable Required Default Purpose
SECRET_KEY Yes 64-character hex string (32 bytes) used for AES key derivation, HMAC request signing, and client ID encryption
ED25519_PRIVATE_KEY Yes Ed25519 signing key for server response signatures. The SDK verifies these signatures before processing responses.
ED25519_PUBLIC_KEY Yes Ed25519 verify key, sent to clients during the init handshake
DATABASE_URI No SQLite path Any SQLAlchemy-compatible URI. Defaults to sqlite:///server/database/keyvory.db
SERVER_HOST No 127.0.0.1 Bind address for the server
SERVER_PORT No 5000 Port for the server
SESSION_COOKIE_SECURE No false Set to true when serving behind HTTPS

System Configuration (database-backed)

These keys are stored in the system_configs table and configurable via the dashboard:

Key Default Description
LICENSE_PREFIX KEYVORY Prefix for generated license keys
LICENSE_KEY_SEGMENTS 4 Number of 5-character segments after the prefix
MESSAGE_TIMEOUT 30 Max allowed clock skew (seconds) for API requests
DB_CLEANER_INTERVAL 1 How often the database cleaner runs (days)
SECURITY_EVENT_RETENTION 30 Purge security events older than this (days)
AUDIT_LOG_RETENTION 90 Purge audit logs older than this (days)
SECURITY_ALERT_RETENTION 60 Purge resolved alerts older than this (days)
LICENSE_RETENTION 30 Purge expired licenses older than this (days)
CLIENT_RETENTION 90 Purge inactive clients older than this (days)

Usage

Dashboard

The admin dashboard at /admin covers the full license management workflow.

Route Purpose
/admin/ Dashboard with stats, recent activity, quick actions
/admin/licenses License table with search, multi-select, pagination. Click a row for the detail sheet.
/admin/licenses/add Create single licenses or generate up to 500 in batch
/admin/applications Manage applications (create, edit, toggle active, delete)
/admin/license-types Manage license type templates with features and defaults
/admin/groups Manage license groups with bulk suspend/activate/extend
/admin/blacklist IP blacklist management
/admin/blacklist/devices Device (HWID) blacklist management
/admin/security-logs Security event log with filters, export, threat detection, and analytics
/admin/customize_dashboard Toggle dashboard panels and table columns
/admin/profile Change password
/admin/api/users/unlock Manually unlock a locked admin account (POST, 10/min rate limit) (I know thats dumb but remove it if you want)

API

The REST API lives under /api/v1/. Each request requires the following headers:

Header Description
X-Client-ID Client identifier, AES-encrypted with the shared secret
X-Timestamp Unix timestamp. Rejected if more than 30 seconds from server time.
X-Signature HMAC-SHA256 of the request body, client ID, and timestamp
X-Signature-Version Must be "2"
X-Device-ID SHA-256 hardware fingerprint
X-Platform Platform identifier (e.g., Windows, Linux)
X-Client-Version SDK version string
Method Endpoint Rate Limit Description
POST /api/v1/license/init 30/min Bootstrap handshake. Returns AES-encrypted session key, X25519 private key (encrypted), server Ed25519 and X25519 public keys. Client uses X25519 for all subsequent envelope encryption.
POST /api/v1/license/login 20/min Validate a license key and bind the hardware ID. Returns license type, expiration, features, and device limit inside a sealed X25519 envelope.
POST /api/v1/license/heartbeat 120/min Keepalive ping with optional license re-validation. Returns {"status": "ok"} or {"status": "revoked", "reason": "..."} inside a sealed X25519 envelope.
POST /api/v1/license/info 30/min Query license metadata without modifying the HWID lock. Caller must own the license (client_id match enforced).
POST /api/v1/license/rotate-key 10/min Request a new AES session key. The old key encrypts the new key in transit.

Python SDK

The SDK is a single file: example/python/keyvory_client.py. Copy it into your project alongside three dependencies (pycryptodome, requests, pynacl — all already in requirements.txt).

from keyvory_client import KeyvoryClient, LicenseStatus

with KeyvoryClient(
    server_url="http://localhost:5000",
    secret_key="your-secret-key",  # same SECRET_KEY from server .env
) as client:
    # 1. Handshake — required before any other call
    if not client.initialize():
        raise RuntimeError("Cannot reach license server")

    # 2. Validate a license key
    result: LicenseStatus = client.login("KEYVORY-XXXXX-XXXXX-XXXXX-XXXXX")

    if result.is_valid:
        print(f"License type: {result.license_type}")
        print(f"Expires: {result.expiration_date}")
        print(f"Max devices: {result.max_devices}")
        if result.has_feature("export_pdf"):
            enable_export()
    else:
        print(f"Denied: {result.status} ({result.code})")

    # 3. Keep the session alive
    while app_running:
        do_work()
        client.heartbeat(license_key="KEYVORY-XXXXX-XXXXX-XXXXX-XXXXX")

SDK anti-tamper protections (enabled automatically):

  • Module integrity — SHA-256 hash of the SDK source file at import time. Detects runtime patching.
  • Debugger detectionsys.gettrace() checked before every sensitive operation.
  • Timing anomaly — Measures SHA-256 computation time. Threshold: 5ms. Catches single-step debuggers.
  • HMAC state chain — Session state (key, version, initialized flag) is HMAC-chained. Detects in-memory tampering between calls.
  • Automatic key rotation — Session key rotates every 100 heartbeats via the server.

Full SDK reference: example/python/README.md

Example Demo

cd example/python
KEYVORY_SERVER=http://localhost:5000
KEYVORY_SECRET=your_secert_key
python example_client.py

Prompts for a license key and walks through the full flow: init handshake, login validation, info query, heartbeat keepalive, and session key rotation.


Database Migrations

Migrations are idempotent SQL files in server/migrations/. They are applied automatically on startup.

To add a migration:

  1. Create a file with a numeric prefix (e.g., 002_add_column_x.sql)
  2. Write standard SQL statements separated by ;
  3. The engine tracks applied versions in a schema_migrations table and only runs new ones

Deployment

Waitress (default)

python run.py

Uses Waitress, a production-grade WSGI server with multi-threaded serving (threads = CPU count x 2). Suitable for low-to-medium traffic without a reverse proxy.

Behind Nginx

server {
    listen 443 ssl;
    server_name licenses.yourdomain.com;

    location / {
        proxy_pass http://127.0.0.1:5000;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header Host $host;
    }
}

Set SESSION_COOKIE_SECURE=true in .env when terminating TLS at the proxy.

Docker

FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
# Provide these via environment variables or a mounted .env file
# ENV SECRET_KEY="your-64-char-hex"
# ENV ED25519_PRIVATE_KEY="your-hex-key"
# ENV ED25519_PUBLIC_KEY="your-hex-key"
EXPOSE 5000
CMD ["python", "run.py"]

Project Structure

keyvory/
├── run.py                              # Entry point: setup wizard + Waitress
├── requirements.txt                    # Python dependencies
├── server/
│   ├── app.py                          # Flask app factory, security headers
│   ├── first_run.py                    # Interactive CLI setup wizard
│   ├── extensions.py                   # Flask-Limiter instance
│   ├── models/                         # SQLAlchemy models
│   │   ├── license.py                  # License keys, HWID, features, metadata
│   │   ├── client.py                   # Client sessions with encrypted keys + X25519 public key
│   │   ├── user.py                     # Admin users (Flask-Login)
│   │   ├── application.py              # Application scoping
│   │   ├── license_type.py             # License type templates
│   │   ├── license_group.py            # License groups
│   │   ├── blacklist.py                # IP and device blacklists
│   │   ├── security_event.py           # Security event log
│   │   ├── security_alert.py           # Security alerts
│   │   ├── audit_log.py                # Admin audit trail
│   │   └── system_config.py            # Key-value configuration store
│   ├── routes/
│   │   ├── api.py                      # Sealed envelope REST API
│   │   ├── admin.py                    # Dashboard routes and JSON admin API
│   │   └── auth.py                     # Login/logout with account lockout
│   ├── services/
│   │   ├── license_service.py          # License validation, HWID locking, CRUD
│   │   ├── security_log_service.py     # Event logging, threat detection, alerts
│   │   ├── audit_service.py            # Admin audit trail
│   │   ├── auth_service.py             # Password hashing and verification
│   │   ├── config_service.py           # System config get/set
│   │   └── db_maintenance.py           # Migration engine + scheduled cleanup
│   ├── utils/
│   │   ├── encryption.py               # AES-GCM, X25519 key exchange, Ed25519, HKDF, nonce store
│   │   └── security.py                 # Auth decorator, IP blocker, signature verification
│   ├── templates/                      # Jinja2 templates
│   ├── static/
│   │   ├── css/                        # Design system, components, layout
│   │   ├── fonts/                      # Geist Sans + Geist Mono
│   │   └── js/                         # app.js, command-palette.js, right-sheet.js
│   └── migrations/                     # Idempotent SQL migration files
│       ├── 001_add_device_tracking.sql  # device_id, platform, client_version (example)
└── example/python/
    ├── keyvory_client.py               # Python SDK with anti-tamper
    ├── example_client.py               # Interactive CLI demo
    └── README.md                       # SDK documentation

Contributing

See CONTRIBUTING.md for development setup, coding conventions, and how to submit pull requests.

Bug reports and feature requests are welcome via GitHub Issues.


License

MIT — see LICENSE for details.

About

open source license management system

Topics

Resources

License

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Packages

 
 
 

Contributors