Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.authforge.cc/llms.txt

Use this file to discover all available pages before exploring further.

A standard AuthForge license is bound to a fixed number of HWIDs (1-16, configurable per key via maxHwidSlots). Once those slots fill up, new devices are rejected with hwid_mismatch. That’s the right default for paid software. For the rest of the time - internal tools, beta cohorts, freeware that just wants license-gated features, demo and sandbox keys - you want the opposite: one shared key, any device, any time, no limits. AuthForge supports this as a per-license toggle called unlimited seats.

What it is

When a license is marked as unlimited-seat:
  • The server accepts any HWID that authenticates against this key.
  • The first activator does not get exclusive ownership. There is no “binding” to undo or reset.
  • The hwid_mismatch error is structurally impossible for this license.
  • maxHwidSlots is still stored on the license but is ignored at validation time. (You can flip the toggle off later to re-enforce the cap.)
Everything else is unchanged: app secret verification, nonce replay protection, the signed Ed25519 response payload, expiration, revocation, rate limiting, and webhooks all behave exactly the same as for finite-seat licenses.

When to use it

Reasonable fits:
  • Internal tools. A single shared key for the whole team, no HWID juggling when laptops get reimaged.
  • Beta programs. You want the testers to be the same set of people you control via key issuance, not via per-device caps.
  • Freeware with license-gated extras. The license is a feature flag, not a payment receipt - capping per device adds friction without revenue protection.
  • Demo or sandbox keys. You’re publishing the credentials in docs or a README. Any cap you set will be hit by the first user.

When not to use it

  • Per-seat pricing. If your business model bills per device, finite maxHwidSlots is the seat enforcement. Unlimited would silently waive it.
  • Anti-piracy hardening. Unlimited-seat licenses can be shared like a Netflix password. If pirates getting the key matters more to you than legitimate user friction, keep seats finite.
  • Trial keys with multi-device limits. If you want each trial confined to one machine, leave seats finite (probably maxHwidSlots: 1).

You still get device analytics

Even though no HWIDs are bound to the license, every activation is logged. The auth log records appId, licenseKey, hwid, ip, result, and timestamp - exactly as for seat-enforced licenses. You can:
  • Look at the license’s Activity panel in the dashboard for distinct devices over time.
  • Query the same data via the GET /v1/licenses/:licenseKey/logs developer-API endpoint.
  • Subscribe to license.validated webhooks if you need a real-time stream.
So you do not lose visibility into “how many distinct devices are using this key” - you just don’t reject new ones when the count grows.

It’s a per-license setting, not a per-app setting

Unlimited seats is a flag on the license row, not on the application. One application can have:
  • 100 paid customer licenses with maxHwidSlots: 1,
  • 5 employee licenses with maxHwidSlots: 3,
  • 1 unlimited-seat key for your public sandbox / docs,
all coexisting on the same appId, all using the same SDK build.

How to set it

Dashboard

When generating licenses, toggle Unlimited seats (shared key) in the create form. The “Max devices per key” input is greyed out while the toggle is on (the value is still stored, so flipping the toggle off restores the previous cap).

Developer API

Pass unlimitedHwidSlots: true in the POST /v1/licenses body:
curl -X POST https://api.authforge.cc/v1/licenses \
  -H "Authorization: Bearer af_live_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "appId": "YOUR_APP_ID",
    "quantity": 1,
    "expiresAt": null,
    "unlimitedHwidSlots": true,
    "label": "team-shared"
  }'
The signed validate payload for an unlimited-seat license contains unlimitedHwidSlots: true; SDKs that don’t know about the field simply ignore it, so old SDK versions keep working.