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

# Rust SDK

> Integrate AuthForge into your Rust application with a lightweight Cargo crate.

## Requirements

* Rust 1.70 or later
* Crates used by the SDK:
  * `hmac`
  * `sha2`
  * `serde`
  * `serde_json`
  * `ureq`
  * `base64`
  * `mac_address`
  * `hostname`

## Installation

The crate is published on [crates.io/crates/authforge](https://crates.io/crates/authforge) as **`authforge`**.

```bash theme={null}
cargo add authforge
```

Or set a semver range in `Cargo.toml` (for example `1.0` for compatible `1.x` releases):

```toml theme={null}
[dependencies]
authforge = "1.0"
```

<Tabs>
  <Tab title="Git (pre-release)">
    ```toml theme={null}
    [dependencies]
    authforge = { git = "https://github.com/AuthForgeCC/authforge-rust" }
    ```

    Optional: pin `branch`, `tag`, or `rev`.
  </Tab>

  <Tab title="Path (vendored)">
    ```toml theme={null}
    [dependencies]
    authforge = { path = "../authforge-rust" }
    ```

    Adjust the path to your checkout (for example `vendor/authforge-rust`).
  </Tab>
</Tabs>

## Quick start

```rust theme={null}
use authforge::{AuthForgeClient, AuthForgeConfig, HeartbeatMode};

fn main() {
    let client = AuthForgeClient::new(AuthForgeConfig {
        app_id: "your-app-id".into(),
        app_secret: "your-app-secret".into(),
        public_key: "YOUR_PUBLIC_KEY".into(),
        heartbeat_mode: HeartbeatMode::Server,
        on_failure: Some(Box::new(|err| {
            eprintln!("Auth failed: {}", err);
            std::process::exit(1);
        })),
        ..Default::default()
    });

    match client.login("XXXX-XXXX-XXXX-XXXX") {
        Ok(result) => {
            println!("Authenticated!");
            println!("Session expires at unix time: {}", result.expires_in);
        }
        Err(err) => {
            eprintln!("Login failed: {:?}", err);
            std::process::exit(1);
        }
    }
}
```

## Config struct reference

```rust theme={null}
AuthForgeConfig {
    app_id: "your-app-id".into(),             // Required
    app_secret: "your-app-secret".into(),     // Required
    public_key: "YOUR_PUBLIC_KEY".into(),     // Required - Ed25519 public key from your AuthForge dashboard (base64)
    heartbeat_mode: HeartbeatMode::Server,    // Local or Server
    heartbeat_interval: 900,                  // Optional, seconds (any value ≥ 1)
    api_base_url: "https://auth.authforge.cc".into(), // Optional
    on_failure: None,                         // Optional callback
    request_timeout: 15,                      // Optional, seconds
    session_ttl_seconds: None,                // Optional - requested session TTL. None = server default (24h). Clamped to [3600, 604800].
    hwid_override: None,                      // Optional - custom identity (for example "tg:123456789")
}
```

| Field                 | Type                                  | Default                        | Description                                                                                                |
| --------------------- | ------------------------------------- | ------------------------------ | ---------------------------------------------------------------------------------------------------------- |
| `app_id`              | `String`                              | required                       | Application ID from your AuthForge dashboard                                                               |
| `app_secret`          | `String`                              | required                       | Application secret from your AuthForge dashboard                                                           |
| `public_key`          | `String`                              | required                       | Ed25519 public key from your AuthForge dashboard (base64)                                                  |
| `heartbeat_mode`      | `HeartbeatMode`                       | `Local`                        | `Local` or `Server` heartbeat mode                                                                         |
| `heartbeat_interval`  | `u64`                                 | `900`                          | Seconds between heartbeat checks (any value ≥ 1)                                                           |
| `api_base_url`        | `String`                              | `https://auth.authforge.cc`    | AuthForge API base URL                                                                                     |
| `on_failure`          | `Option<Box<dyn Fn(&str)+Send+Sync>>` | `None`                         | Invoked when heartbeat fails                                                                               |
| `request_timeout`     | `u64`                                 | `15`                           | Timeout for API requests                                                                                   |
| `session_ttl_seconds` | `Option<u64>`                         | `None` (server default: 86400) | Requested session token lifetime. Server clamps to `[3600, 604800]`; preserved across heartbeat refreshes. |
| `hwid_override`       | `Option<String>`                      | `None`                         | Optional custom identity string sent as HWID (for example `tg:123456789`).                                 |

## Validate license (no heartbeat)

`validate_license` runs the same `/auth/validate` request and signature checks as `login`, but does **not** store a session on the client or start the heartbeat thread. It returns the same `LoginResult` on success.

```rust theme={null}
match client.validate_license("XXXX-XXXX-XXXX-XXXX") {
    Ok(result) => { /* use result.session_token, result.app_variables, … */ }
    Err(err) => { /* same AuthForgeError variants as login */ }
}
```

## Billing

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

## Methods reference

```rust theme={null}
pub fn new(config: AuthForgeConfig) -> Self;
pub fn login(&self, license_key: &str) -> Result<LoginResult, AuthForgeError>;
pub fn validate_license(&self, license_key: &str) -> Result<LoginResult, AuthForgeError>;
pub fn logout(&self);
pub fn is_authenticated(&self) -> bool;
pub fn get_session_data(&self) -> Option<serde_json::Value>;
pub fn get_app_variables(&self) -> Option<std::collections::HashMap<String, serde_json::Value>>;
pub fn get_license_variables(&self) -> Option<std::collections::HashMap<String, serde_json::Value>>;
```

## Error enum reference

```rust theme={null}
AuthForgeError::InvalidApp
AuthForgeError::InvalidKey
AuthForgeError::Expired
AuthForgeError::Revoked
AuthForgeError::HwidMismatch
AuthForgeError::NoCredits
AuthForgeError::Blocked
AuthForgeError::RateLimited        // validate only; never returned from heartbeat
AuthForgeError::ReplayDetected     // validate only; never returned from heartbeat
AuthForgeError::SignatureMismatch
AuthForgeError::NetworkError(String)
AuthForgeError::Other(String)
```

## Heartbeat modes

```rust theme={null}
// SERVER mode: network heartbeats via /auth/heartbeat
let server_client = AuthForgeClient::new(AuthForgeConfig {
    app_id: "...".into(),
    app_secret: "...".into(),
    public_key: "YOUR_PUBLIC_KEY".into(),
    heartbeat_mode: HeartbeatMode::Server,
    ..Default::default()
});

// LOCAL mode: local expiry checks with no heartbeat network calls
let local_client = AuthForgeClient::new(AuthForgeConfig {
    app_id: "...".into(),
    app_secret: "...".into(),
    public_key: "YOUR_PUBLIC_KEY".into(),
    heartbeat_mode: HeartbeatMode::Local,
    ..Default::default()
});
```

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