108 lines
2.3 KiB
Markdown
108 lines
2.3 KiB
Markdown
# LicenseVault
|
|
|
|
Self-hosted license management server with web UI.
|
|
Built with Go + HTMX + SQLite. Runs on Proxmox via Docker.
|
|
|
|
## Stack
|
|
- **Go** — HTTP server (chi router)
|
|
- **HTMX** — reactive UI without a JS framework
|
|
- **SQLite** — zero-config database
|
|
- **Caddy** — automatic HTTPS
|
|
|
|
## Deploy on Proxmox
|
|
|
|
### 1. Point your domain
|
|
Create an A record: `licenses.yourdomain.com → your-proxmox-vm-ip`
|
|
|
|
### 2. Edit Caddyfile
|
|
```
|
|
licenses.yourdomain.com {
|
|
reverse_proxy app:8080
|
|
}
|
|
```
|
|
|
|
### 3. Deploy
|
|
```bash
|
|
git clone <this-repo>
|
|
cd licenseServer
|
|
docker compose up -d
|
|
```
|
|
|
|
### 4. First-time setup
|
|
Open `https://licenses.yourdomain.com/setup` and create your admin account.
|
|
|
|
---
|
|
|
|
## REST API (for client apps)
|
|
|
|
### Validate a license
|
|
```
|
|
POST /api/v1/validate
|
|
Content-Type: application/json
|
|
|
|
{
|
|
"license_key": "XXXX-XXXX-XXXX-XXXX",
|
|
"machine_id": "sha256-hardware-fingerprint",
|
|
"hostname": "customer-pc",
|
|
"os": "windows"
|
|
}
|
|
```
|
|
|
|
Response:
|
|
```json
|
|
{ "valid": true, "type": "expiry" }
|
|
{ "valid": false, "message": "license expired" }
|
|
```
|
|
|
|
### Go client example
|
|
```go
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"net/http"
|
|
"github.com/example/license/hwid"
|
|
)
|
|
|
|
fp, _ := hwid.Collect()
|
|
|
|
body, _ := json.Marshal(map[string]string{
|
|
"license_key": licenseKey,
|
|
"machine_id": fp.Hash(),
|
|
"hostname": fp.Hostname,
|
|
"os": fp.OS,
|
|
})
|
|
|
|
resp, _ := http.Post(
|
|
"https://licenses.yourdomain.com/api/v1/validate",
|
|
"application/json",
|
|
bytes.NewReader(body),
|
|
)
|
|
|
|
var result struct {
|
|
Valid bool `json:"valid"`
|
|
Message string `json:"message,omitempty"`
|
|
Type string `json:"type,omitempty"`
|
|
Expires *time.Time `json:"expires,omitempty"`
|
|
}
|
|
json.NewDecoder(resp.Body).Decode(&result)
|
|
|
|
if !result.Valid {
|
|
log.Fatalf("License invalid: %s", result.Message)
|
|
}
|
|
```
|
|
|
|
## Environment Variables
|
|
|
|
| Variable | Default | Description |
|
|
|-----------|------------------|------------------------|
|
|
| PORT | 8080 | HTTP port |
|
|
| DB_PATH | ./licenses.db | SQLite database path |
|
|
|
|
## License Types
|
|
|
|
**Expiry** — valid until a date. Machine binding optional.
|
|
**Usage** — valid for N activations. Each `/api/v1/validate` call counts as one use.
|
|
|
|
Machine ID is the SHA-256 hardware fingerprint from the `hwid` package.
|
|
Leave blank when issuing to allow any machine (floating license).
|