05/22 Update gitignore
This commit is contained in:
+2
-1
@@ -51,4 +51,5 @@ Thumbs.db
|
||||
.pytest_cache/
|
||||
.coverage
|
||||
htmlcov/
|
||||
license_server/
|
||||
license_server/
|
||||
LICENSING_GUIDE.md
|
||||
|
||||
@@ -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*
|
||||
Reference in New Issue
Block a user