Requirements
- Go 1.21 or later
- No external dependencies (standard library only)
Installation
The module path isgithub.com/AuthForgeCC/authforge-go/v2. Install the latest release:
@latest with a v2.x.y tag (for example @v2.0.0). Go puts the major version in the module path from v2 on; 1.x releases stay at github.com/AuthForgeCC/authforge-go.
Imports always use github.com/AuthForgeCC/authforge-go/v2 (the package name is still authforge). Run go mod tidy after editing go.mod.
- Local replace (forks / air-gap)
replace at a directory that contains the SDK’s go.mod, then run go mod tidy.Quick start
Config reference
SessionTTL (grace period)
Requested lifetime of the session token returned by /auth/validate. This is the grace period: how long the app keeps running on the signed session without contacting AuthForge. Leave at 0 to accept the server default (24 hours). The server clamps the final value to [1h, 7d]. The requested TTL is preserved across heartbeat refreshes, and apps relying on the grace period alone can extend their window up to 7 days:
Billing
- Each successful
Login()orValidateLicense()costs 1 credit (one/auth/validatedebit). - The grace period is free: local session re-verification makes no network calls and consumes no credits.
- Online check-ins (if enabled) cost 1 credit per 10 successful heartbeats (billed on every 10th call).
- With online check-ins, revocations take effect on the next check-in regardless of interval; without them, at session expiry.
- A new self-serve offline activation (
ActivateOffline) costs 2 credits; activating the same machine again is free.
Validate license (no session)
ValidateLicense performs the same /auth/validate request and signature verification as Login, but does not persist session fields on the client, start the background goroutine, or invoke OnFailure for validate failures (network errors still return an error; repeated network failure does not call OnFailure when using ValidateLicense).
The SDK recognizes AUTHFORGE_SDK_TEST_NONCE for integration tests only; never set it in production (it pins the validate nonce).
Methods reference
Login and ValidateLicense return a LoginResult with:
SessionTokenExpiresInAppVariablesLicenseVariablesRequestID
Error handling
The Go SDK exposes sentinel errors so you can branch witherrors.Is:
os.Exit: Login and ValidateLicense return errors, and background failures go to your callback. Server and transport failures are *authforge.Error values (1.4.0+): authforge.ErrorCode(err) returns the machine-readable code (invalid_key, hwid_mismatch, network_error, http_error_502, …) and authforge.IsTransient(err) its classification, including on wrapped errors. errors.Is against the sentinels above keeps working, and server codes this SDK version doesn’t know yet are passed through unchanged.
Request retries are automatic: rate_limited (or HTTP 429 without an error code) is retried after 2s, then 5s; no_credits, app_burn_cap_reached, and demo_quota_exceeded share HTTP 429 but are not retried, and a background check tries again at the next interval; a network failure is retried once after 2s.
Background check failures
Each failed background check (online check-in or grace period expiry) produces an*authforge.Error with Code and IsTransient() / IsFatal(). It reaches OnHeartbeatFailure when that is set, otherwise OnFailure as err.Error() (the same message as earlier releases, for example authforge: license revoked: revoked). Without a callback (1.4.1+), a transient failure prints AuthForge: background check failed (<code>); retrying next interval to stderr and checks in again next interval; a fatal one clears the session and stops background checks without output, and the process keeps running, so check IsAuthenticated() or set a callback.
A failed check-in counts as an AuthForge verdict only when the body is a JSON object with
"status": "failed" and a non-empty string error. Any other failure body (for example {"error":"revoked"} without a status) is reported as unexpected_response, and the message keeps the raw status / error values. A heartbeat network failure is reported once, not as a separate network_error string first.
Callbacks run on the background check goroutine with no SDK lock held, so calling Logout(), IsAuthenticated(), or any other client method from them is safe; Logout() there stops further checks. A check-in still in flight when Logout() or Login runs never writes its result back to the client.
To tolerate short outages but shut down on a definitive answer, have the callback signal your main goroutine and let it save and exit:
context.Context built with context.WithCancelCause and cancel it from the callback. Calling os.Exit(1) inside the callback is a last resort: deferred functions do not run, so save the user’s work first.
Grace period and online check-ins
By default, the app runs through the grace period after activation: the SDK re-verifies the signed session locally, makes no network calls, and stops when the session expires. Opt in to online check-ins for fast revocation and concurrent-use detection:Offline license files (.authforge)
For machines that never connect to the internet, the operator mints a signed offline license file in the dashboard or Developer API (1 credit). The SDK verifies it locally with your public key and the machine HWID; no network, no check-ins, and online Login is untouched. Leave AppSecret empty so the air-gapped binary does not contain the App Secret.
errors.Is, in check order: ErrOfflineBadArmor, ErrOfflineBadSignature, ErrOfflineUnsupportedVersion, ErrOfflineMalformedPayload, ErrOfflineWrongApp, ErrOfflineExpired, ErrOfflineHwidMismatch; authforge.OfflineErrorCode(err) returns the cross-SDK string and OnFailure receives offline_login_failed: <code>. client.VerifyLicenseFile(pathOrText) and the package function authforge.VerifyLicenseFile(text, opts) run the same checks without touching state. Issued files cannot be revoked remotely; they stay valid until their own expiry. CreateActivationRequest(opts) produces the .authforge-request the operator uploads; hostname is omitted unless IncludeMachineName is set. See Activation requests.
Migrating from HeartbeatMode
HeartbeatMode ("server" / "local") is deprecated and may now be left empty:
HeartbeatMode: "local": remove the field; the grace period is now the default.HeartbeatMode: "server": useOnlineHeartbeat: trueinstead.
Self-serve offline activation
err := client.ActivateOffline(licenseKey, path) turns a license key into a lifetime .authforge file bound to this machine, with one online call to POST /auth/offline/activate. After that the app starts with LoginFromFile and never needs the network. Use it for one-time-purchase apps that should run offline forever. See Self-serve activation and the walkthrough Sell offline apps.
- Needs the App Secret. Set
Config.AppSecret(air-gapped builds leave it empty). - Your app must opt in (off by default), and the license must be perpetual and seat-limited.
- The SDK sends the same HWID as
login, verifies the returned file (signature, app id, this machine’s HWID) before saving it, writes it to the path atomically (creating parent folders), and leaves the client in an offline session exactly as afterLoginFromFile: variables available, no grace-period timer, no check-ins. - Billing: 2 credits per new machine (one validation plus one mint), charged to you. The same machine activating again gets the identical file back for free. Every refusal is free.
- A license can activate at most
maxHwidSlotsdistinct machines, ever. HWID resets do not free machine slots. - A refund or chargeback revokes the license online but cannot reach files already issued. For a lifetime file, that means the buyer keeps the app.
*authforge.Error values: authforge.ErrorCode(err) gives the code, authforge.IsTransient(err) / IsFatal() the classification, and errors.Is matches the sentinels ErrOfflineActivationDisabled, ErrOfflineActivationRequiresPerpetual, ErrOfflineActivationRequiresSeats, ErrOfflineActivationLimitReached and ErrOfflineFileRejected as well as the existing ones (ErrHwidMismatch, ErrInvalidKey, …). The codes are the server’s (offline_activation_disabled, offline_activation_requires_perpetual, offline_activation_requires_seats, offline_activation_limit_reached, hwid_mismatch when no seat is free, invalid_key, revoked, no_credits, rate_limited, …) or the SDK’s: offline_file_rejected (fatal; the server’s file failed local verification, reason in the message) and file_write_failed (transient; the file could not be written, and calling again returns the same file for free). The method never calls OnFailure and never exits the process; nothing is written on failure and any existing session is left alone. On offline_activation_limit_reached, tell the user to contact you. Messages to show: Self-serve activation errors.
LoginFromFile can report a missing or rejected file to OnFailure as offline_login_failed. If you set a callback, return normally for that reason so the app reaches the key prompt.