kioai / artifacts /api-server /src /guardId.ts
kinaiok
Initial deployment setup for Hugging Face Spaces
5ef6e9d
/**
* geminigen.ai x-guard-id header generator
*
* Reverse-engineered from geminigen.ai/_nuxt/nBh81x6X.js
* Constants extracted from: window.__NUXT__.config.public.antibot
*
* Algorithm:
* stableId = 22-char alphanumeric string (persistent per server instance)
* timeBucket = Math.floor(Date.now() / 60000) β€” changes every 60 seconds
* domFp = "0".repeat(32) β€” server has no DOM
*
* hmacKey = SHA256(`${SECRET_KEY}:${stableId}`).slice(0, 32)
* signHash = SHA256(`${path}:${METHOD}:${hmacKey}:${timeBucket}:${SECRET_KEY}`)
*
* payload = [0x01, ...hexBytes(hmacKey), // 16 bytes
* ...uint32BE(timeBucket), // 4 bytes
* ...hexBytes(signHash), // 32 bytes
* ...hexBytes(domFp)] // 16 bytes β†’ 69 bytes total
*
* x-guard-id = base64url(payload) (no padding)
*/
import { createHash, randomBytes } from "crypto";
const SECRET_KEY = "45NPBH$&";
const TIME_BUCKET_MS = 60_000;
const STABLE_ID_LEN = 22;
const DOM_FP = "0".repeat(32); // 32 hex zeros β†’ 16 zero bytes
// ── One stable-id per server instance ────────────────────────────────────────
function makeStableId(): string {
const rand = randomBytes(16).toString("hex");
const hash = createHash("sha256").update(`server:${rand}`).digest("hex");
// Take alphanumeric chars from the hex (all are [0-9a-f] so fine for [A-Za-z0-9])
return hash.slice(0, STABLE_ID_LEN);
}
let _stableId: string = makeStableId();
/** Replace the stable-id (e.g., if you want to rotate it). */
export function rotateStableId(): void { _stableId = makeStableId(); }
// ── Helpers ───────────────────────────────────────────────────────────────────
/** SHA-256 of a UTF-8 string β†’ lowercase hex (64 chars). */
function sha256(str: string): string {
return createHash("sha256").update(str, "utf8").digest("hex");
}
/** Convert a hex string to a byte array (every 2 hex chars β†’ 1 byte). */
function hexToBytes(hex: string): number[] {
const bytes: number[] = [];
for (let i = 0; i < hex.length; i += 2) {
bytes.push(parseInt(hex.substring(i, 2 + i), 16));
}
return bytes;
}
/** Encode uint32 as 4 big-endian bytes. */
function uint32BE(n: number): number[] {
return [(n >>> 24) & 255, (n >>> 16) & 255, (n >>> 8) & 255, n & 255];
}
// ── Public API ─────────────────────────────────────────────────────────────────
/**
* Generate the `x-guard-id` header value for a given API path + HTTP method.
*
* @param path e.g. "/api/video-gen/grok-stream"
* @param method e.g. "post"
*/
export function generateGuardId(path: string, method: string): string {
const stableId = _stableId;
const timeBucket = Math.floor(Date.now() / TIME_BUCKET_MS);
// hmacKey: first 32 hex chars of SHA256("${SECRET_KEY}:${stableId}") β†’ 16 bytes
const hmacKey = sha256(`${SECRET_KEY}:${stableId}`).slice(0, 32);
// signHash: full 64-hex SHA256 β†’ 32 bytes
const signHash = sha256(`${path}:${method.toUpperCase()}:${hmacKey}:${timeBucket}:${SECRET_KEY}`);
const payload: number[] = [
1, // version byte (LK = 1)
...hexToBytes(hmacKey), // 16 bytes
...uint32BE(timeBucket), // 4 bytes
...hexToBytes(signHash), // 32 bytes
...hexToBytes(DOM_FP), // 16 bytes
];
return Buffer.from(payload).toString("base64url"); // Node β‰₯ 16, no trailing =
}