# 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*