2026-05-05 08:48:29 +00:00
2026-05-05 08:44:55 +00:00
2026-05-05 08:44:55 +00:00
2026-05-05 08:44:55 +00:00
2026-05-05 08:44:55 +00:00
2026-05-05 08:44:55 +00:00
2026-05-05 08:44:55 +00:00
2026-05-05 08:44:55 +00:00
2026-05-05 08:44:55 +00:00
2026-05-05 08:44:55 +00:00
2026-05-05 08:44:55 +00:00
2026-05-05 08:48:29 +00:00

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

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:

{ "valid": true, "type": "expiry" }
{ "valid": false, "message": "license expired" }

Go client example

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).

S
Description
User owned license server
Readme 62 KiB
Languages
Go 58.3%
HTML 41%
Dockerfile 0.7%