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

# Core Concepts

> Key terms and concepts in AuthForge: apps, licenses, HWIDs, credits, heartbeats, and more.

## Apps

An app represents the software you're protecting. Each app has:

* **App ID**: A UUID that identifies your app. Public (embedded in your binary).
* **App Secret**: A UUID used to authenticate `/auth/validate` requests. Keep this secret. Rotatable via the dashboard.
* **Public Key**: A base64 Ed25519 public key used by SDKs to verify signed `/auth/validate` and `/auth/heartbeat` success responses.
* **Settings**: Configuration for your app (variables, security rules, webhooks).

You can create multiple apps under one account. Each app has its own license keys, variables, and security settings.

## License Keys

License keys are alphanumeric strings in the format `XXXX-XXXX-XXXX-XXXX` (using A–Z excluding I and O, digits 2–9). Each key is bound to a single app.

A license has the following properties:

| Field          | Description                                               |
| -------------- | --------------------------------------------------------- |
| `licenseKey`   | The key string itself                                     |
| `appId`        | The app this license belongs to                           |
| `status`       | `active` or `revoked`                                     |
| `expiresAt`    | ISO 8601 expiration date, or `null` for lifetime licenses |
| `maxHwidSlots` | How many devices can use this key simultaneously (1–16)   |
| `hwidList`     | Array of bound hardware IDs                               |
| `label`        | Optional label for your records (e.g., order ID)          |
| `createdAt`    | When the license was created                              |

Generate keys in the dashboard or programmatically via the [Developer API](/api/licenses).

## HWID (Hardware ID)

A hardware fingerprint of the user's machine. Each SDK collects stable system identifiers (such as MAC address, CPU, hostname, or disk serial; varying by language and platform) and computes a SHA-256 hash. The result is a 64-character hex string.

The exact inputs differ per SDK to use the most reliable identifiers available in each language's standard library. The backend treats the HWID as an opaque string; it only needs to be stable and unique per machine.

**Important:** HWID strings are **SDK-specific**. The same physical computer can produce different HWIDs when using different AuthForge SDKs (for example, Python vs C#). See [Hardware locking (HWID)](/features/hwid-locking) for how that affects seats, support, and security lists.

The HWID is sent during license validation. If the license hasn't seen this HWID before and has available slots, the server binds it. If all slots are full and the HWID doesn't match any bound device, authentication fails with `hwid_mismatch`.

## HWID Slots

The number of machines a single license key can be active on simultaneously. When a user authenticates from a new device:

* If there's an open slot, the HWID is bound automatically.
* If all slots are full, authentication fails.

To let a customer move to a new machine, reset their HWID bindings from the dashboard or via the API (`reset-hwid` action).

## Credits

Credits are the unit of billing in AuthForge. Every billable operation deducts credits from your account.

| Operation                     | Cost     |
| ----------------------------- | -------- |
| Successful license validation | 1 credit |
| 10 successful heartbeats      | 1 credit |

Heartbeat billing is debited on every 10th successful heartbeat. Heartbeat credits are **not** prepaid; the session itself is short-lived (see [Session TTL](#session-tokens)), so "every 10th heartbeat" is literally how many billable events happened.

Purchase credits in the dashboard. Available tiers:

| Tier | Credits   | Price |
| ---- | --------- | ----- |
| 10k  | 10,000    | \$10  |
| 30k  | 30,000    | \$15  |
| 100k | 100,000   | \$30  |
| 500k | 500,000   | \$100 |
| 1M   | 1,000,000 | \$150 |

Set up [auto-refill](/best-practices/credit-management) to automatically purchase credits when your balance drops below a threshold.

## Heartbeats

After a successful login, the SDK runs periodic background checks to verify the license is still valid. The default interval is **15 minutes**.

If a heartbeat detects the license has been revoked, expired, or the session is invalid, the SDK triggers your `onFailure` callback (or terminates the process if no callback is set).

### Heartbeat Modes

<Tabs>
  <Tab title="SERVER mode">
    On each interval, the SDK sends `POST /auth/heartbeat` with the session token. The server verifies the token + signature chain, checks the license status, and returns a new signed response. SDKs verify every heartbeat signature with your app's Ed25519 public key.

    **Pros:** Catches revocations immediately on the next heartbeat. Real-time enforcement. Any heartbeat interval ≥ 1s is economically safe; heartbeats cost 1 credit per 10 calls.

    **Cons:** Requires network connectivity.

    **Use when:** You need instant revocation enforcement, your users are always online, or you're protecting high-value software.
  </Tab>

  <Tab title="LOCAL mode">
    On each interval, the SDK re-verifies the stored session signature locally without making a network call. When the session token expires (default 24 hours, configurable per-SDK up to 7 days), the SDK triggers the failure handler. In SERVER mode, each successful heartbeat mints a fresh session token, so long-running apps never expire.

    **Pros:** Works offline after initial login. Zero heartbeat credit consumption.

    **Cons:** Revocations don't take effect until the next server-side check. If the app stays offline longer than the session TTL (\~24h by default) without a successful heartbeat, the local session expires and the SDK triggers the failure handler.

    **Use when:** Your users may be offline, you want to minimize credit usage, or instant revocation isn't critical.
  </Tab>
</Tabs>

## Session Tokens

A successful `/auth/validate` response includes a signed **session token** that the SDK stores and replays on every heartbeat. The token carries the HWID binding, license key, and (if the SDK requested a custom lifetime) a `ttl` claim.

| Property         | Value                                                                                   |
| ---------------- | --------------------------------------------------------------------------------------- |
| Default lifetime | 24 hours                                                                                |
| Minimum lifetime | 1 hour (3600s)                                                                          |
| Maximum lifetime | 7 days (604800s)                                                                        |
| Refresh          | Every successful heartbeat mints a fresh token, preserving the originally requested TTL |
| Revocation       | Revoking a license invalidates all of its tokens on the next heartbeat                  |

SDKs expose a `ttlSeconds` / `session_ttl_seconds` / `SessionTTL` configuration option so you can tune how long the SDK can keep running without reaching the server (LOCAL mode) before forcing a re-login. The server silently clamps out-of-range values.

### Signed payload fields

Successful `/auth/validate` and `/auth/heartbeat` responses include a **signed JSON payload** (base64 in the `payload` field) in addition to the nested **session** JWT in `sessionToken`. Besides `appVariables` / `licenseVariables` on validate, the payload includes:

| Field              | Meaning                                                                                                    |
| ------------------ | ---------------------------------------------------------------------------------------------------------- |
| `expiresIn`        | Unix **seconds** when the **session** token expires (historical field; same instant as `sessionExpiresAt`) |
| `sessionExpiresAt` | ISO 8601 **session** expiry (for display and clarity)                                                      |
| `licenseExpiresAt` | ISO 8601 **license** expiry, or `null` for lifetime keys                                                   |
| `maxHwidSlots`     | Seat limit for this license                                                                                |
| `hwidCount`        | Number of HWIDs currently bound                                                                            |
| `licenseLabel`     | Optional dashboard label (only present when set)                                                           |

Heartbeats repeat the entitlement fields when applicable so long-running clients see updates (for example if an admin changes seats or expiry) without calling validate again.

## Developer API

A server-to-server REST API for automating license management from your own backend. Authenticated with API keys (prefixed `af_live_`).

Use the Developer API to:

* Create licenses programmatically (e.g., after a Stripe payment)
* Revoke, activate, extend, or reset HWID on licenses
* List and query licenses
* Manage app variables, webhooks, and security settings

See the [API Reference](/api/overview) for full documentation.

## App Variables

Key-value pairs set per app, delivered to every SDK client during authentication in the `appVariables` field of the signed payload.

Use cases:

* **Feature flags**: `"maintenanceMode": true`
* **Remote config**: `"maxUploadSizeMb": 50`
* **Messages**: `"motd": "v2.0 releasing Friday!"`
* **Version gating**: `"minVersion": "1.5.0"`

Limits: max 50 keys, 4 KB total, flat values only (string, number, or boolean).

Set via the dashboard or the [Variables API](/api/variables).

## License Variables

Key-value pairs set per license, delivered only to that specific license holder during authentication in the `licenseVariables` field.

Use cases:

* **Plan tiers**: `"plan": "pro"`
* **Per-user limits**: `"maxProjects": 10`
* **Custom metadata**: `"customerName": "Acme Corp"`

Same limits as app variables. Set via the dashboard or the [Variables API](/api/variables).

## Webhooks

Real-time HTTP notifications sent to your server when license events occur. Each delivery is HMAC-SHA256 signed with your webhook secret for verification.

Supported events:

| Event                | Trigger                           |
| -------------------- | --------------------------------- |
| `license.validated`  | Successful authentication via SDK |
| `license.created`    | License key generated             |
| `license.revoked`    | License revoked                   |
| `license.activated`  | Revoked license re-activated      |
| `license.hwid_bound` | HWID bound to a license slot      |
| `license.hwid_reset` | HWID bindings cleared             |
| `license.deleted`    | License permanently deleted       |

Max 5 webhooks per app. See [Webhooks](/features/webhooks) for setup and verification details.

## Blacklists & Whitelists

Per-app access control lists for HWIDs and IP addresses:

* **HWID blacklist**: Block specific hardware IDs from authenticating.
* **HWID whitelist**: When set, only listed HWIDs can authenticate (allowlist mode).
* **IP blacklist / whitelist**: Same concept for IP addresses.

Blacklist takes precedence over whitelist. Max 1,000 entries per list.

See [Security](/features/security) for configuration details.
