> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ancestraldev.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Avalex License Validation in Rust Using reqwest and serde

> Add Avalex license validation to your Rust application using reqwest and serde. Includes async validation with typed request and response structs.

This guide walks you through adding Avalex license validation to a Rust application. You will define typed structs for the request and response, generate a SHA-256 HWID from stable system identifiers, and perform an async HTTP POST using `reqwest`. The result is a single `validate_license` function you can `await` at startup before your application proceeds.

## Cargo.toml Dependencies

Add the following entries to your `Cargo.toml` before writing the validation code:

```toml theme={null}
[dependencies]
reqwest = { version = "0.11", features = ["json"] }
serde = { version = "1", features = ["derive"] }
sha2 = "0.10"
whoami = "1"
tokio = { version = "1", features = ["full"] }
```

* **`reqwest`** — async HTTP client with built-in JSON serialization support.
* **`serde`** — derive macros for serializing the request struct and deserializing the response.
* **`sha2`** — pure-Rust SHA-256 implementation used to hash the raw hardware identifier.
* **`whoami`** — cross-platform access to the hostname and username without unsafe FFI.
* **`tokio`** — async runtime required by `reqwest`.

## Full Example

```rust theme={null}
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use std::error::Error;

#[derive(Serialize)]
struct ValidateRequest<'a> {
    #[serde(rename = "licenseId")]
    license_id: &'a str,
    #[serde(rename = "productId")]
    product_id: &'a str,
    hwid: &'a str,
}

#[derive(Deserialize)]
struct ValidateResponse {
    valid: bool,
}

fn get_hwid() -> String {
    let mut hasher = Sha256::new();
    hasher.update(whoami::fallible::hostname().unwrap_or_default());
    hasher.update(whoami::username());
    format!("{:X}", hasher.finalize())[..32].to_string()
}

pub async fn validate_license(license_id: &str) -> Result<bool, Box<dyn Error>> {
    let client = reqwest::Client::new();
    let hwid = get_hwid();
    let req_body = ValidateRequest {
        license_id,
        product_id: "prod_enterprise_2026",
        hwid: &hwid,
    };
    let res = client
        .post("http://localhost:8080/licenses/validate")
        .json(&req_body)
        .send()
        .await?;
    if res.status().is_success() {
        let resp: ValidateResponse = res.json().await?;
        Ok(resp.valid)
    } else {
        Ok(false)
    }
}
```

## Code Walkthrough

**HWID generation (`get_hwid`)**

`get_hwid` feeds the system hostname and username into a `Sha256` hasher sequentially using `hasher.update(...)`. Because `update` can be called multiple times, there is no need to allocate an intermediate concatenated string. The finalized digest is formatted as uppercase hex with `{:X}` and sliced to 32 characters. If `whoami::fallible::hostname()` fails (e.g., in a sandboxed environment), `unwrap_or_default()` returns an empty string rather than panicking, keeping the function infallible.

**Typed request and response structs**

`ValidateRequest` derives `Serialize` and uses `#[serde(rename = "...")]` to emit the camelCase field names that the Avalex API expects. `ValidateResponse` derives `Deserialize` and maps the `valid` boolean from the JSON response. Using lifetime parameters (`<'a>`) on `ValidateRequest` avoids unnecessary heap allocations by borrowing the string slices passed in by the caller.

**Async validation (`validate_license`)**

`validate_license` constructs a one-shot `reqwest::Client`, builds the typed request body, and sends it with `.json(&req_body)` — which sets the `Content-Type: application/json` header and serializes the struct automatically. The `?` operator propagates any transport error up to the caller as a `Box<dyn Error>`. A non-success HTTP status (including `429`) returns `Ok(false)` so the caller can apply its own grace-period logic without treating it as a fatal error.

<Note>
  Replace the hardcoded `http://localhost:8080/licenses/validate` URL with your production HTTPS endpoint. Store it in a configuration file or environment variable rather than embedding it as a string literal, which makes obfuscation easier and deployment more flexible.
</Note>
