added readme.md
This commit is contained in:
parent
f60ca88a09
commit
f44b1f6ebc
351
activator/readme.md
Normal file
351
activator/readme.md
Normal file
|
|
@ -0,0 +1,351 @@
|
|||
# Force Monitor — License Activation Guide
|
||||
|
||||
This document explains how the licensing system works and how to activate **Force Monitor** after the trial period.
|
||||
|
||||
---
|
||||
|
||||
## 📋 Table of Contents
|
||||
|
||||
1. [How Licensing Works](#how-licensing-works)
|
||||
2. [Trial Period](#trial-period)
|
||||
3. [Getting Your Machine Fingerprint](#getting-your-machine-fingerprint)
|
||||
4. [Activating Your License](#activating-your-license)
|
||||
5. [License File Format](#license-file-format)
|
||||
6. [Troubleshooting](#troubleshooting)
|
||||
7. [For Vendors: Generating Licenses](#for-vendors-generating-licenses)
|
||||
|
||||
---
|
||||
|
||||
## How Licensing Works
|
||||
|
||||
Force Monitor uses **Ed25519 cryptographic signatures** to verify licenses. The system supports:
|
||||
|
||||
- **Trial mode** — free for a limited time (default: 7 days)
|
||||
- **Paid license** — perpetual or time-limited, tied to a specific machine
|
||||
|
||||
### Key Concepts
|
||||
|
||||
| Term | Description |
|
||||
|------|-------------|
|
||||
| **Machine Fingerprint** | Unique hardware ID derived from your machine (MAC, motherboard serial, etc.) |
|
||||
| **Public Key** | Embedded in the app; used to verify license signatures |
|
||||
| **Private Key** | Kept secret by the vendor; used to sign licenses |
|
||||
| **Signed License** | JSON file cryptographically signed by the vendor |
|
||||
|
||||
---
|
||||
|
||||
## Trial Period
|
||||
|
||||
When you first run Force Monitor, it automatically starts a **7-day trial**:
|
||||
|
||||
```bash
|
||||
# Check your current license status
|
||||
curl http://localhost:8080/api/license/status
|
||||
```
|
||||
|
||||
**Example response (trial active):**
|
||||
```json
|
||||
{
|
||||
"enabled": true,
|
||||
"mode": "trial",
|
||||
"locked": false,
|
||||
"message": "trial active: 6 day(s) remaining",
|
||||
"days_remaining": 6,
|
||||
"trial_started_at": "2026-04-20T17:00:00Z",
|
||||
"trial_expires_at": "2026-04-27T17:00:00Z",
|
||||
"fingerprint_short": "A1B2C3D4-...",
|
||||
"activation_configured": true
|
||||
}
|
||||
```
|
||||
|
||||
### Trial Expiration
|
||||
|
||||
After the trial expires, if `require_after_trial: true` (default), the app will:
|
||||
- Return **HTTP 403 Forbidden** on all data endpoints
|
||||
- Show a license activation page at `http://localhost:8080/`
|
||||
- Display `"trial expired; activation required"`
|
||||
|
||||
---
|
||||
|
||||
## Getting Your Machine Fingerprint
|
||||
|
||||
To request a license, you need your **machine fingerprint**. Get it via API or the web UI.
|
||||
|
||||
### Method 1: API (Recommended)
|
||||
|
||||
```bash
|
||||
curl -s http://localhost:8080/api/license/request | python3 -m json.tool
|
||||
```
|
||||
|
||||
**Example response:**
|
||||
```json
|
||||
{
|
||||
"app": "force_monitor",
|
||||
"version": "1.0.0",
|
||||
"generated_at": "2026-04-20T17:30:00Z",
|
||||
"hostname": "press-control-01",
|
||||
"platform": "linux/amd64",
|
||||
"fingerprint": "A1B2C3D4E5F6789012345678901234567890ABCDEF...",
|
||||
"fingerprint_short": "A1B2C3D4-E5F67890",
|
||||
"components": [
|
||||
"machineid=1234567890abcdef...",
|
||||
"product_uuid=ABCDEF12-3456-7890-ABCD-EF1234567890",
|
||||
"hostname=press-control-01",
|
||||
"mac=aa:bb:cc:dd:ee:ff"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Method 2: Web UI
|
||||
|
||||
1. Open `http://localhost:8080/` in your browser
|
||||
2. If no `static/index.html` exists, a fallback page shows your **fingerprint** and **license mode**
|
||||
3. Click **GET /api/license/request** to see the full activation request
|
||||
|
||||
---
|
||||
|
||||
## Activating Your License
|
||||
|
||||
Once you receive a signed license from the vendor, activate it using one of these methods:
|
||||
|
||||
### Method 1: Web UI (Easiest)
|
||||
|
||||
1. Open `http://localhost:8080/` in your browser
|
||||
2. Paste the signed license JSON into the textarea
|
||||
3. Click **Activate license**
|
||||
|
||||

|
||||
|
||||
### Method 2: API (cURL)
|
||||
|
||||
```bash
|
||||
# Save your license to a file
|
||||
cat > license.json << 'EOF'
|
||||
{
|
||||
"app": "force_monitor",
|
||||
"license_id": "LIC-2026-001",
|
||||
"customer": "Your Company Name",
|
||||
"fingerprint": "A1B2C3D4E5F6789012345678901234567890ABCDEF...",
|
||||
"issued_at": "2026-04-20T17:00:00Z",
|
||||
"expires_at": "2027-04-20T17:00:00Z",
|
||||
"features": [],
|
||||
"signature": "base64_encoded_ed25519_signature..."
|
||||
}
|
||||
EOF
|
||||
|
||||
# Activate via API
|
||||
curl -X POST http://localhost:8080/api/license/activate \
|
||||
-H "Content-Type: application/json" \
|
||||
-d @license.json
|
||||
```
|
||||
|
||||
**Success response:**
|
||||
```json
|
||||
{
|
||||
"status": "activated",
|
||||
"license": {
|
||||
"mode": "licensed",
|
||||
"locked": false,
|
||||
"message": "license active",
|
||||
"customer": "Your Company Name",
|
||||
"license_id": "LIC-2026-001",
|
||||
"expires_at": "2027-04-20T17:00:00Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Method 3: Direct File Placement
|
||||
|
||||
You can also place the license file directly:
|
||||
|
||||
```bash
|
||||
# Copy the signed license to the license directory
|
||||
cp license.json license/license.json
|
||||
|
||||
# Restart the app
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## License File Format
|
||||
|
||||
A valid signed license is a JSON file with this structure:
|
||||
|
||||
```json
|
||||
{
|
||||
"app": "force_monitor",
|
||||
"license_id": "LIC-2026-001",
|
||||
"customer": "Your Company Name",
|
||||
"fingerprint": "A1B2C3D4E5F6789012345678901234567890ABCDEF...",
|
||||
"issued_at": "2026-04-20T17:00:00Z",
|
||||
"expires_at": "2027-04-20T17:00:00Z",
|
||||
"features": ["premium", "mqtt"],
|
||||
"signature": "base64_encoded_ed25519_signature..."
|
||||
}
|
||||
```
|
||||
|
||||
### Field Descriptions
|
||||
|
||||
| Field | Required | Description |
|
||||
|-------|----------|-------------|
|
||||
| `app` | ✅ | Must match `product_code` in config (`force_monitor`) |
|
||||
| `license_id` | ✅ | Unique license identifier |
|
||||
| `customer` | ✅ | Customer name |
|
||||
| `fingerprint` | ✅ | Must match the machine fingerprint exactly |
|
||||
| `issued_at` | ✅ | ISO 8601 timestamp when license was issued |
|
||||
| `expires_at` | ❌ | Optional expiration date (omitted = perpetual) |
|
||||
| `features` | ❌ | Optional feature flags array |
|
||||
| `signature` | ✅ | Ed25519 signature of the payload, base64-encoded |
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "trial state invalid or tampered"
|
||||
|
||||
**Cause:** System clock was changed, or trial files were modified.
|
||||
|
||||
**Fix:**
|
||||
```bash
|
||||
# Remove trial state and restart
|
||||
rm license/trial_state.json
|
||||
# Restart the app — a new trial will begin
|
||||
```
|
||||
|
||||
### "license fingerprint does not match this machine"
|
||||
|
||||
**Cause:** The license was generated for a different machine.
|
||||
|
||||
**Fix:** Generate a new activation request on this machine and request a new license.
|
||||
|
||||
### "invalid license signature"
|
||||
|
||||
**Cause:** The license was not signed with the correct private key, or was corrupted.
|
||||
|
||||
**Fix:**
|
||||
- Verify the license file wasn't modified
|
||||
- Ensure the vendor used the correct private key matching your `public_key_base64`
|
||||
|
||||
### "no license public key configured"
|
||||
|
||||
**Cause:** `license.public_key_base64` is missing or invalid in `config.yaml`.
|
||||
|
||||
**Fix:** Add the vendor's public key to your config:
|
||||
```yaml
|
||||
license:
|
||||
enabled: true
|
||||
public_key_base64: "YOUR_BASE64_PUBLIC_KEY_HERE"
|
||||
```
|
||||
|
||||
### Check Current Status
|
||||
|
||||
```bash
|
||||
curl -s http://localhost:8080/api/license/status | python3 -m json.tool
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## For Vendors: Generating Licenses
|
||||
|
||||
This repository includes helper functions for license generation. Use them in a **separate, private signing tool**.
|
||||
|
||||
### 1. Generate Ed25519 Key Pair
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/ed25519"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func main() {
|
||||
publicKey, privateKey, err := ed25519.GenerateKey(nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Println("Public Key (base64):")
|
||||
fmt.Println(base64.StdEncoding.EncodeToString(publicKey))
|
||||
|
||||
fmt.Println("\nPrivate Key (base64) — KEEP SECRET:")
|
||||
fmt.Println(base64.StdEncoding.EncodeToString(privateKey))
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Sign a License
|
||||
|
||||
Use the `SignLicenseWithPrivateKey` helper from `license.go`:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
lic := SignedLicense{
|
||||
App: "force_monitor",
|
||||
LicenseID: "LIC-2026-001",
|
||||
Customer: "Customer Name",
|
||||
Fingerprint: "A1B2C3D4E5F6789012345678901234567890ABCDEF...",
|
||||
IssuedAt: time.Now().UTC().Format(time.RFC3339),
|
||||
ExpiresAt: time.Now().AddDate(1, 0, 0).UTC().Format(time.RFC3339),
|
||||
Features: []string{},
|
||||
}
|
||||
|
||||
privateKeyBase64 := "YOUR_PRIVATE_KEY_BASE64"
|
||||
|
||||
signedLic, err := SignLicenseWithPrivateKey(lic, privateKeyBase64)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
out, _ := json.MarshalIndent(signedLic, "", " ")
|
||||
fmt.Println(string(out))
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Distribute the Public Key
|
||||
|
||||
Give customers the **public key** to add to their `config.yaml`:
|
||||
|
||||
```yaml
|
||||
license:
|
||||
enabled: true
|
||||
public_key_base64: "LLQ43Fle4nObHxmMQQsANvPUX5vDxx0TctpvQs+RI4s="
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Configuration Reference
|
||||
|
||||
```yaml
|
||||
license:
|
||||
enabled: true # Enable/disable licensing
|
||||
trial_days: 7 # Trial period duration
|
||||
require_after_trial: true # Lock app after trial expires
|
||||
data_dir: license # Directory for license files
|
||||
public_key_base64: "" # Ed25519 public key for verification
|
||||
product_code: force_monitor # App identifier (must match license)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## API Reference
|
||||
|
||||
| Endpoint | Method | Description |
|
||||
|----------|--------|-------------|
|
||||
| `/api/license/status` | GET | Get current license status |
|
||||
| `/api/license/request` | GET | Get activation request (fingerprint) |
|
||||
| `/api/license/activate` | POST | Submit signed license JSON |
|
||||
|
||||
---
|
||||
|
||||
## License
|
||||
|
||||
This project is proprietary software. Contact your vendor for licensing inquiries.
|
||||
Loading…
Reference in a new issue