$ guides / offline-licensing

Offline licensing

Mint a signed .authforge file, ship it with your app public key, and verify it on air-gapped machines.

Guide

This walkthrough is for machines that never reach AuthForge. It is a separate mode from the grace period. If the customer’s machine can connect even occasionally, use online login() instead.

1. Mint in the dashboard

  1. Open the license (Applications → app → Licenses → license page) and click Mint .authforge file.
  2. Pick an expiry. The file cannot outlive the license. Lifetime is only available for perpetual licenses.
  3. Bind the file to the customer’s HWID (recommended). Prefer an activation request (.authforge-request) the SDK wrote on the machine over a pasted HWID string — the dashboard checksums the file so email damage cannot silently mint to the wrong id. Collect it from the same SDK language that will call loginFromFile — fingerprints are not portable across SDKs.
  4. Click Mint & download. 1 credit is charged. The file body is never stored server-side, so download it now.

The Developer API equivalent is POST /v1/licenses/{licenseKey}/offline-files with scope write:licenses.

2. Ship the file and the public key

The verifying machine needs three things and nothing else:

  • the .authforge file
  • your app id
  • your app’s Ed25519 public key

Do not embed the App Secret in air-gapped binaries. Offline verification does not use it.

Put the file next to the installer (or behind a USB / data-diode copy step) and embed the public key the same way you already do for online login(). Copy it from Application settings in the dashboard.

3. Verify on the air-gapped machine

Call the SDK’s file-verify entry point at launch. It never starts the grace-period timer or online check-ins, and it never exits the process on its own.

Python
from authforge import AuthForgeClient

# Valid until the file's own expiresAt; cannot be remotely revoked.
client = AuthForgeClient(
    app_id="YOUR_APP_ID",
    app_secret=None,
    public_key="YOUR_PUBLIC_KEY",
)

print("HWID:", client.get_hwid())  # send this to the vendor before they mint

if not client.login_from_file("license.authforge"):
    raise SystemExit("Offline license file rejected")
Node.js
import { AuthForgeClient } from "@authforgecc/sdk";

// Valid until the file's own expiresAt; cannot be remotely revoked.
const client = new AuthForgeClient({
    appId: "YOUR_APP_ID",
    publicKey: "YOUR_PUBLIC_KEY",
});

console.log("HWID:", client.getHwid());

if (!client.loginFromFile("./license.authforge")) {
    console.error("Offline license file rejected.");
    process.exit(1);
}
Go
client, err := authforge.New(authforge.Config{
    AppID:     "YOUR_APP_ID",
    PublicKey: "YOUR_PUBLIC_KEY",
})
if err != nil {
    panic(err)
}
fmt.Println("HWID:", client.HWID())

if _, err := client.LoginFromFile("license.authforge"); err != nil {
    panic(err)
}
C#
var client = new AuthForgeClient(
    appId: "YOUR_APP_ID",
    appSecret: "",
    publicKey: "YOUR_PUBLIC_KEY"
);
Console.WriteLine($"HWID: {client.GetHwid()}");

if (!client.LoginFromFile("license.authforge"))
{
    throw new Exception("Offline license file rejected");
}

Rejection codes, in check order: bad_armor, bad_signature, unsupported_version, malformed_payload, wrong_app, expired, hwid_mismatch.

4. Re-issue before expiry

Treat re-issuing as your revocation lever.

  1. Surface expiresAt after a successful loginFromFile (About / License screen).
  2. Warn the customer at 14 days and again at 3 days.
  3. Mint a new file with the same HWID policy, deliver it out-of-band, and have them replace the old file.
  4. Stop re-issuing when the entitlement should end; the outstanding file lapses on its own clock.

Minting costs 1 credit. Verifying the file on the machine is free.

Next steps

Contact support

Feel free to reach out if you have questions, need help getting set up, or run into something unexpected. We'll get back to you as soon as we can.

Email us at support@authforge.cc