diff --git a/.gitignore b/.gitignore index cc129c8..2a3cc28 100644 --- a/.gitignore +++ b/.gitignore @@ -51,4 +51,5 @@ Thumbs.db .pytest_cache/ .coverage htmlcov/ -license_server/ \ No newline at end of file +license_server/ +LICENSING_GUIDE.md diff --git a/LICENSING_GUIDE.md b/LICENSING_GUIDE.md deleted file mode 100644 index 2a31c67..0000000 --- a/LICENSING_GUIDE.md +++ /dev/null @@ -1,386 +0,0 @@ -# TechDesk Licensing — Vendor Operations Guide - -This document covers everything you need to do as the vendor: -setting up the license server, generating keys, publishing the app to customers, -and issuing keys. Read it top to bottom the first time, then jump to the relevant -section for ongoing operations. - ---- - -## Table of Contents - -1. [One-Time Setup (Do This First)](#1-one-time-setup-do-this-first) -2. [Deploy the License Server](#2-deploy-the-license-server) -3. [Publish TechDesk to a Customer](#3-publish-techdesk-to-a-customer) -4. [Issue a Key — Current Customer (First Time)](#4-issue-a-key--current-customer-first-time) -5. [Customer Applies the Key](#5-customer-applies-the-key) -6. [Renew an Expiring Key](#6-renew-an-expiring-key) -7. [Ongoing Operations Checklist](#7-ongoing-operations-checklist) - ---- - -## 1. One-Time Setup (Do This First) - -This section is done **once, ever**. It generates the RSA key pair that secures -all license keys. Do it on a machine you control — your development machine or -the server where the license server will run. - -### Step 1.1 — Install dependencies - -```bash -cd license_server -pip install -r requirements.txt -``` - -### Step 1.2 — Generate the RSA key pair - -```bash -python generate_keys.py -``` - -Output: -``` -Key pair generated successfully. - Private key : private_key.pem (KEEP SECRET) - Public key : public_key.pem (safe to embed in TechDesk) -``` - -Two files are created: - -| File | What it is | Who sees it | -|---|---|---| -| `private_key.pem` | Signs all license keys | You only. Never leave this machine. | -| `public_key.pem` | Verifies license keys | Embedded in TechDesk. Customers have it. | - -> **Critical:** `private_key.pem` must NEVER be committed to git, sent to -> customers, or included in any build. If it leaks, anyone can forge unlimited -> valid license keys. Back it up securely (encrypted drive, password manager). - -### Step 1.3 — Embed the public key in TechDesk - -Open `app/services/license_service.py` and find the `PUBLIC_KEY_PEM` constant -near the top. Replace the placeholder with the full contents of `public_key.pem`: - -```python -PUBLIC_KEY_PEM = """-----BEGIN PUBLIC KEY----- -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA... -(your actual key here) -... ------END PUBLIC KEY-----""" -``` - -Include the `-----BEGIN PUBLIC KEY-----` and `-----END PUBLIC KEY-----` lines. - -> After this change, TechDesk will validate license keys against your real -> public key. Before this step, all keys fail and the app runs in Community mode. - -### Step 1.4 — Commit the public key change - -```bash -git add app/services/license_service.py -git commit -m "Embed RSA public key for license validation" -``` - -This is the only license-related change that goes into the customer-facing -codebase. `private_key.pem` stays off git permanently — add it to `.gitignore`: - -```bash -echo "license_server/private_key.pem" >> .gitignore -echo "license_server/licenses.db" >> .gitignore -git add .gitignore -git commit -m "Ignore license server secrets from git" -``` - ---- - -## 2. Deploy the License Server - -The license server is a small Flask app that runs **on your machine or a private -server**. It does not need to be reachable from the internet — you only access -it when generating new keys. - -### Option A — Run locally on your development machine (simplest) - -Good for low volume (fewer than 50 customers). Run it only when you need to -generate a key, not 24/7. - -```bash -cd license_server - -# Set a strong admin password (or set the env var in your shell profile) -export LICENSE_SERVER_PASSWORD="your-strong-password-here" - -python app.py -``` - -Open `http://127.0.0.1:5001` in your browser. Log in with the password you set. - -### Option B — Run on a private VPS (recommended for production) - -If you prefer a persistent server so you can generate keys from anywhere: - -1. Copy the `license_server/` directory to your VPS (via `scp` or git) -2. Copy `private_key.pem` to the VPS **via a secure channel** (not email) -3. Set environment variables: - -```bash -export LICENSE_SERVER_PASSWORD="your-strong-password-here" -export SECRET_KEY="random-64-char-string-here" -``` - -4. Run with gunicorn behind nginx (or just run directly if access is restricted - to your IP via firewall): - -```bash -pip install -r requirements.txt -python app.py -``` - -5. Restrict access to your IP in the VPS firewall — port 5001 should never be - publicly reachable. - -> **Important:** The license server has no multi-user auth, no HTTPS, and no -> rate limiting. It is designed for internal use only. Never expose port 5001 -> to the internet. - ---- - -## 3. Publish TechDesk to a Customer - -When you deliver TechDesk to a new customer, you give them the codebase. -Here is exactly what to include and exclude. - -### What to include - -Everything in the project **except** the items listed below. - -### What to EXCLUDE - -| Path | Why | -|---|---| -| `license_server/private_key.pem` | Signing key — leaking it lets anyone forge keys | -| `license_server/licenses.db` | Your customer database — not their business | -| `license_server/` (entire folder) | Customers don't need the server | -| `.env` | Contains your secrets, not theirs | -| `logs/` | Your logs | -| `app/static/uploads/` | Your data | -| `migrations/` (optional) | Only needed if they'll run migrations manually | - -### Recommended delivery method - -**Option A: Git archive (cleanest)** - -```bash -# From your project root — produces a zip with no git history and no excluded files -git archive --format=zip --output=techdesk-v1.0.zip HEAD -``` - -Then manually remove `license_server/` from the zip if it's tracked in git: - -```bash -zip -d techdesk-v1.0.zip "license_server/*" -``` - -**Option B: Manual zip** - -```bash -# Exclude everything sensitive -zip -r techdesk-v1.0.zip . \ - --exclude "*.git*" \ - --exclude "*.env*" \ - --exclude "license_server/*" \ - --exclude "logs/*" \ - --exclude "app/static/uploads/*" -``` - -**Checklist before sending:** - -- [ ] `app/services/license_service.py` contains the real public key (not the placeholder) -- [ ] `license_server/` is not in the archive -- [ ] No `.env` file in the archive -- [ ] No `private_key.pem` anywhere in the archive - ---- - -## 4. Issue a Key — Current Customer (First Time) - -Your existing customer is running TechDesk without a license key right now -(Community mode). Here is how to issue them a key. - -### Step 4.1 — Start the license server - -```bash -cd license_server -export LICENSE_SERVER_PASSWORD="your-password" -python app.py -``` - -Open `http://127.0.0.1:5001` and log in. - -### Step 4.2 — Fill in the Generate form - -| Field | What to enter | -|---|---| -| Customer / Company Name | Their company name (e.g. `LT Services Inc`) | -| Contact Email | Their admin's email address | -| Tier | `Business` (recommended starting tier) | -| Duration | `1 year` (365 days) — gives you an annual renewal cycle | - -Click **Generate Key**. - -### Step 4.3 — Copy the key - -The full key appears on screen immediately after generation, like: - -``` -TDESK-eyJjdXN0b21lciI6Ikx....(long string).... -``` - -**Copy the entire key** — it is shown in full only once on screen. The database -stores only a short preview for your records. If you lose it, generate a new one. - -### Step 4.4 — Send the key to the customer - -Send the key via email, Slack, or your preferred channel. Include: - -- The full `TDESK-...` key string -- The expiry date (1 year from today) -- A link to the setup instructions below (or paste them directly) - -**Example email:** - -> Subject: Your TechDesk License Key -> -> Hi [Name], -> -> Your TechDesk Business license key is below. It is valid until [expiry date]. -> -> ``` -> TDESK-eyJjdXN0b21lciI6... -> ``` -> -> To activate: -> 1. Open your `.env` file on the TechDesk server -> 2. Add this line: `LICENSE_KEY=TDESK-eyJ...` (the full key) -> 3. Restart TechDesk: `sudo systemctl restart gunicorn` -> 4. Visit Admin → License to confirm activation -> -> Let me know if you have any questions. - -### Step 4.5 — Verify the key was recorded - -Back in the license server UI, confirm the new row appears in the Issued Licenses -table with the correct customer name, tier, and expiry date. This is your audit -trail. - ---- - -## 5. Customer Applies the Key - -Share these instructions with the customer. They only need to do this once (and -again on renewal). - -### Customer instructions - -**On the TechDesk server:** - -1. Open the `.env` file in the TechDesk installation directory: - ```bash - nano /home/it-ticket/myapp/.env - ``` - -2. Add or update this line (paste the full key you received): - ``` - LICENSE_KEY=TDESK-eyJjdXN0b21lciI6... - ``` - -3. Save the file and restart TechDesk: - ```bash - sudo systemctl restart gunicorn - ``` - -4. Verify activation — log in to TechDesk as admin and go to - **Admin → License**. The page should show: - - A **Business** (or Enterprise) tier badge - - Your company name and email - - The expiry date - - Green checkmarks on all licensed features - -5. The warning banner at the top of the page (if it was showing) will disappear. - -**If something goes wrong:** - -| Symptom | Likely cause | Fix | -|---|---|---| -| Banner still shows "No license key" | `.env` not saved or service not restarted | Re-check `.env`, restart gunicorn | -| Banner shows "License key invalid" | Key was truncated or modified during copy | Re-copy the full key, ensure no line breaks | -| License page shows "Public key not configured" | The public key placeholder was not replaced | Contact vendor — the app build is missing the public key | - ---- - -## 6. Renew an Expiring Key - -30 days before expiry the customer will see a blue banner: _"Your license expires -in X days."_ When expiry passes, it turns to a yellow warning and features lock -down to Community tier. - -### Renewal process - -1. Start the license server (same as Step 4.1) -2. Generate a new key for the customer (same as Step 4.2–4.3) - - Use the same customer name, email, and tier - - Choose the same duration (1 year) -3. Send the new key to the customer (same as Step 4.4) -4. Customer replaces the old `LICENSE_KEY=` line in `.env` with the new key and - restarts — same as Step 5 - -> The old key and new key are independent — the customer simply overwrites the -> `.env` value. There is no overlap or grace period to manage on your end. - ---- - -## 7. Ongoing Operations Checklist - -### When you generate a new version of TechDesk - -- [ ] Ensure `app/services/license_service.py` still contains the real `PUBLIC_KEY_PEM` - (it should, unless you accidentally reverted it) -- [ ] Exclude `license_server/` and `.env` from the customer build -- [ ] Test: start the app with no `LICENSE_KEY` → confirm Community mode banner appears -- [ ] Test: start the app with a valid key → confirm correct tier and features - -### When a customer reports a licensing issue - -1. Ask them to go to **Admin → License** and send you a screenshot -2. Check the reason shown: - - `no_key` → they haven't added the `.env` line or haven't restarted - - `expired` → they need a renewal key - - `invalid_signature` → the key was corrupted in transit; re-send it - - `no_public_key` → their build is missing the public key; send a fixed build - -### If `private_key.pem` is ever compromised - -All previously issued keys remain valid (you cannot invalidate them — offline -model has no revocation). Your only recourse: - -1. Generate a new RSA key pair (`python generate_keys.py` after deleting the old files) -2. Embed the new `public_key.pem` in TechDesk -3. Re-issue keys to all existing customers -4. Send them the updated build with the new public key - -This is the main limitation of offline keys — treat `private_key.pem` with the -same care as a master password. - -### Backing up `private_key.pem` - -Store at least two encrypted copies: -- A password manager (Bitwarden, 1Password, etc.) as a secure note -- An encrypted USB drive stored offline - -If you lose `private_key.pem` you cannot issue new keys and cannot recover -existing ones — you would need to do a full key rotation (same as the compromise -scenario above). - ---- - -*Last updated: 2026-05-22*