Tonnage-app-IMCO/activator/readme.md

351 lines
8.7 KiB
Markdown
Raw Permalink Normal View History

2026-04-20 16:01:16 +00:00
# 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**
![Activation UI](docs/activation-ui.png)
### 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.