12 KiB
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
- One-Time Setup (Do This First)
- Deploy the License Server
- Publish TechDesk to a Customer
- Issue a Key — Current Customer (First Time)
- Customer Applies the Key
- Renew an Expiring Key
- 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
cd license_server
pip install -r requirements.txt
Step 1.2 — Generate the RSA key pair
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.pemmust 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:
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
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:
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.
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:
- Copy the
license_server/directory to your VPS (viascpor git) - Copy
private_key.pemto the VPS via a secure channel (not email) - Set environment variables:
export LICENSE_SERVER_PASSWORD="your-strong-password-here"
export SECRET_KEY="random-64-char-string-here"
- Run with gunicorn behind nginx (or just run directly if access is restricted to your IP via firewall):
pip install -r requirements.txt
python app.py
- 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)
# 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:
zip -d techdesk-v1.0.zip "license_server/*"
Option B: Manual zip
# 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.pycontains the real public key (not the placeholder)license_server/is not in the archive- No
.envfile in the archive - No
private_key.pemanywhere 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
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:
- Open your
.envfile on the TechDesk server- Add this line:
LICENSE_KEY=TDESK-eyJ...(the full key)- Restart TechDesk:
sudo systemctl restart gunicorn- 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:
-
Open the
.envfile in the TechDesk installation directory:nano /home/it-ticket/myapp/.env -
Add or update this line (paste the full key you received):
LICENSE_KEY=TDESK-eyJjdXN0b21lciI6... -
Save the file and restart TechDesk:
sudo systemctl restart gunicorn -
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
-
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
- Start the license server (same as Step 4.1)
- 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)
- Send the new key to the customer (same as Step 4.4)
- Customer replaces the old
LICENSE_KEY=line in.envwith the new key and restarts — same as Step 5
The old key and new key are independent — the customer simply overwrites the
.envvalue. 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.pystill contains the realPUBLIC_KEY_PEM(it should, unless you accidentally reverted it) - Exclude
license_server/and.envfrom 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
- Ask them to go to Admin → License and send you a screenshot
- Check the reason shown:
no_key→ they haven't added the.envline or haven't restartedexpired→ they need a renewal keyinvalid_signature→ the key was corrupted in transit; re-send itno_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:
- Generate a new RSA key pair (
python generate_keys.pyafter deleting the old files) - Embed the new
public_key.pemin TechDesk - Re-issue keys to all existing customers
- 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