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

# Node.js SDK

> Integrate AuthForge into your Node.js or Electron application with the official npm package.

## Requirements

* Node.js 18.0 or later
* No runtime dependencies beyond Node.js built-ins

## Installation

Install from [npm](https://www.npmjs.com/package/@authforgecc/sdk) as **`@authforgecc/sdk`**:

```bash theme={null}
npm install @authforgecc/sdk
```

The package ships `authforge.mjs` and TypeScript declarations (`authforge.d.ts`).

<Note>
  Prefer a single file in-repo? Copy `authforge.mjs` from [GitHub](https://github.com/AuthForgeCC/authforge-node). The npm package is recommended for versioning and updates.
</Note>

## TypeScript

Use the same import as JavaScript; types resolve from the package:

```ts theme={null}
import { AuthForgeClient } from "@authforgecc/sdk";
```

## Quick start

```javascript theme={null}
import { AuthForgeClient } from "@authforgecc/sdk";
import * as readline from "node:readline/promises";

const client = new AuthForgeClient({
    appId: "YOUR_APP_ID",
    appSecret: "YOUR_APP_SECRET",
    publicKey: "YOUR_PUBLIC_KEY",
    heartbeatMode: "SERVER",
});

const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
const key = await rl.question("Enter license key: ");
rl.close();

if (await client.login(key)) {
    console.log("Authenticated!");
    // Your app logic here - heartbeats run in the background
} else {
    console.error("Invalid license key.");
    process.exit(1);
}
```

## Constructor parameters

```javascript theme={null}
const client = new AuthForgeClient({
    appId: "YOUR_APP_ID",                     // Required - from dashboard
    appSecret: "YOUR_APP_SECRET",             // Required - from dashboard
    publicKey: "YOUR_PUBLIC_KEY",             // Required - Ed25519 public key from your AuthForge dashboard (base64)
    heartbeatMode: "SERVER",                  // Required - "SERVER" or "LOCAL"
    heartbeatInterval: 900,                   // Optional - seconds (default: 900 = 15 min, any value ≥ 1 is supported)
    apiBaseUrl: "https://auth.authforge.cc",  // Optional
    onFailure: null,                          // Optional - callback(reason, exception)
    requestTimeout: 15,                       // Optional - HTTP timeout in seconds
    ttlSeconds: null,                         // Optional - requested session TTL. null = server default (24h). Clamped to [3600, 604800].
    hwidOverride: null,                       // Optional - custom identity (for example "tg:123456789")
});
```

### `ttlSeconds`

Requested session token lifetime in seconds for `/auth/validate`. Pass `null` (or omit) to accept the server default of 24 hours. The server clamps to `[3600, 604800]` (1 hour to 7 days). The requested TTL is preserved across heartbeat refreshes so long-running apps in LOCAL mode can stretch their offline window up to 7 days.

## Billing

* Each successful `login()` or `validateLicense()` costs **1 credit** (one `/auth/validate` debit).
* Heartbeats cost **1 credit per 10 successful calls** (billed on every 10th heartbeat). Any `heartbeatInterval ≥ 1` is economically safe.
* Revocations take effect on the **next** heartbeat regardless of interval.

## Login

```javascript theme={null}
const success = await client.login(licenseKey);
```

Returns `Promise<boolean>`. It resolves to `true` if authentication succeeded, `false` otherwise. On success, the SDK starts background heartbeats automatically.

## Validate license (no heartbeat)

```javascript theme={null}
const result = await client.validateLicense(licenseKey);
if (result.valid) {
  console.log(result.sessionToken, result.expiresIn, result.appVariables, result.licenseVariables);
} else {
  console.error(result.code, result.error);
}
```

Same `/auth/validate` request and Ed25519 verification as `login`, without mutating the client’s stored session or starting the heartbeat timer. Use this for bots and repeated checks; use `login` when you want background heartbeats. Successful results may also include `sessionExpiresAt`, `licenseExpiresAt` (`null` for lifetime keys), `maxHwidSlots`, `hwidCount`, and `licenseLabel` when present in the signed payload; the decoded payload is always available as `sessionData`.

## Failure callback

If authentication or a heartbeat fails, the SDK calls your `onFailure` callback. If no callback is set (or the callback throws), the SDK exits the process.

```javascript theme={null}
function handleFailure(reason, exception) {
    if (reason === "login_failed") {
        console.error("Login failed - check your license key.");
        return;
    }

    if (reason === "heartbeat_failed") {
        console.error("Heartbeat failed - saving state and shutting down.");
        saveApplicationState();
    }
}

const client = new AuthForgeClient({
    appId: "YOUR_APP_ID",
    appSecret: "YOUR_APP_SECRET",
    publicKey: "YOUR_PUBLIC_KEY",
    heartbeatMode: "SERVER",
    onFailure: handleFailure,
});
```

<Warning>
  If you don't set `onFailure`, the SDK terminates the process immediately on any failure. Set a callback in production for graceful shutdown.
</Warning>

## Reading variables

After successful login, app variables and license variables are available on the client:

```javascript theme={null}
if (await client.login(licenseKey)) {
    // App-wide variables (set in dashboard or API)
    const appVars = client.appVariables;
    if (appVars.maintenanceMode) {
        console.log("Server is under maintenance.");
        process.exit(0);
    }

    // Per-license variables
    const plan = client.licenseVariables.plan ?? "basic";
    if (plan === "pro") {
        enableProFeatures();
    }
}
```

## Heartbeat modes

```javascript theme={null}
// SERVER mode - validates with the API every interval
const serverClient = new AuthForgeClient({
    appId: "YOUR_APP_ID",
    appSecret: "YOUR_APP_SECRET",
    publicKey: "YOUR_PUBLIC_KEY",
    heartbeatMode: "SERVER",
    heartbeatInterval: 900,
});

// LOCAL mode - verifies locally, re-validates when the session token expires (default 24h; up to 7d via ttlSeconds)
const localClient = new AuthForgeClient({
    appId: "YOUR_APP_ID",
    appSecret: "YOUR_APP_SECRET",
    publicKey: "YOUR_PUBLIC_KEY",
    heartbeatMode: "LOCAL",
    heartbeatInterval: 900,
});
```

See [Heartbeat Modes](/concepts#heartbeat-modes) for a detailed comparison.

## Full example

```javascript theme={null}
import { AuthForgeClient } from "@authforgecc/sdk";
import * as readline from "node:readline/promises";

function saveState() {
    console.log("Saving application state...");
    // Your save logic here
}

const client = new AuthForgeClient({
    appId: "YOUR_APP_ID",
    appSecret: "YOUR_APP_SECRET",
    publicKey: "YOUR_PUBLIC_KEY",
    heartbeatMode: "SERVER",
    heartbeatInterval: 900,
    onFailure: (reason, exception) => {
        console.error(`Auth failure: ${reason}`);
        if (exception) {
            console.error(`Detail: ${exception.message ?? exception}`);
        }
        saveState();
        process.exit(1);
    },
});

const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
const key = await rl.question("Enter license key: ");
rl.close();

if (!(await client.login(key))) {
    console.error("Invalid license key.");
    process.exit(1);
}

console.log("Licensed and running!");

const minVersion = client.appVariables.minVersion;
if (minVersion && APP_VERSION < minVersion) {
    console.error(`Please update to version ${minVersion} or later.`);
    process.exit(1);
}

process.on("SIGINT", () => {
    saveState();
    process.exit(0);
});

setInterval(() => {
    // Your app logic here
}, 1000);
```

## GitHub

Full source, changelog, and issues: [AuthForgeCC/authforge-node](https://github.com/AuthForgeCC/authforge-node)
