Backend / Security

Offline-First Software Licensing with Ed25519 Signatures

Licenses that verify locally, work offline, and resist copying.

November 202611 min read

Offline-First Software Licensing with Ed25519 Signatures

Software licensing has a trust problem: how do you let a customer's app verify its license without calling your server every time? And how do you prevent customers from copying licenses between machines?

I built Guard License Server to solve this — Ed25519-signed licenses that verify locally, work offline, and support both node-locked and floating models.

The licensing models

| Model | Description | |-------|-------------| | Node-locked | License tied to one machine (CPU + MAC + disk + hostname). | | Floating | License shared across N machines simultaneously. | | Grace period | App keeps working if the license server is unreachable. |

Why Ed25519 signatures

License keys (random strings) require a server round-trip to validate. Ed25519 signatures let the app verify cryptographically without network access:

1. Server signs: `signature = Ed25519.sign(private_key, license_data)`. 2. App verifies: `Ed25519.verify(public_key, license_data, signature)`.

The app carries the public key embedded in the binary. No server needed for verification.

Signed licenses are also hard to forge — only the holder of the private key can create valid ones.

Machine fingerprinting

To bind a license to one machine, the app collects hardware identifiers:

- CPU model - MAC address - Disk serial - Hostname

These are hashed into a machine fingerprint included in the signed license. Verification checks that the fingerprint matches the current machine.

Offline grace period

License servers go down. The app needs to keep working:

1. App checks license at startup. 2. If server is reachable, verify online and cache the result. 3. If server is unreachable, fall back to the cached verification. 4. Cache expires after a configurable grace period (e.g., 7 days). 5. When the server returns, refresh the cache.

This means brief outages don't break paying customers.

FastAPI middleware re-check

For server-rendered or API-protected features, a FastAPI middleware re-checks the license on each request:

```python
@app.middleware("http")
async def license_check(request, call_next):
    license_header = request.headers.get("X-License")
    if not verify_license(license_header, request.client.fingerprint):
        return JSONResponse({"error": "invalid license"}, status_code=403)
    response = await call_next(request)
    return response
```

Obfuscation with Nuitka

The client SDK is compiled with Nuitka to make reverse engineering harder — protecting the embedded public key and license-checking logic.

Bottom line

Good licensing protects your revenue without frustrating paying customers. Ed25519 signatures + machine fingerprinting + offline grace periods = licenses that are secure, reliable, and user-friendly.

Need a licensing system for your product? [Get in touch](/contact).

Related articles

Want similar work?

I build RAG systems, FastAPI backends, AI agents, and licensing systems for teams worldwide. Let’s discuss your project.

Get in touch