Reference Client
Vampauth ships a Luau reference client. It posts tokey/check, verifies the Ed25519 signature inside the executor, and only reports success when the response is genuine. A thin wrapper handles the request, nonce, and signature gates; the crypto core (Ed25519, SHA-256, SHA-512, Base64 — adapted from rbx-cryptography, MIT) is fetched separately and verified against a pinned SHA-256 digest before it is trusted. Nothing is hand-rolled in the security-critical code.
What it does
Check(key) returns true, payload only when every gate passes:
- the request returned
2xx, nonce_echomatches the nonce this call sent,- the response carries
signatureandproject_id, - the Ed25519 signature verifies over
sha256(nonce|hwid|expiresAtUnix|project_id), - and the key is not locally expired.
false, reason. Nothing runs on a fail-closed path.
Not secure out of the box. The client verifies the server’s signature only; it does not stop anyone editing or deleting the gate in your script. Embed it and obfuscate the whole script before shipping. The default HWID is basic — pass your own fingerprint for real binding.On success the client also caches the full response in
Vampauth:State() under
VAMP_* keys — VAMP_STATUS, VAMP_KEY, VAMP_EXPIRES_AT, VAMP_CREATED_AT,
VAMP_DISCORD_ID, VAMP_DISCORD_USERNAME, VAMP_IS_PREMIUM, and
VAMP_HWID_BOUND. VAMP_REASON is "Key validation". Because the key maps
read-through, fields the API does not yet populate (e.g. Discord identity for
plain keys) are nil/false.
Crypto core
The wrapper is intentionally thin. Ed25519 verification, SHA-256/SHA-512, and Base64 live in a separate crypto core that the wrapper fetches on first use fromhttps://vampauth.com/client/vampauth-crypto.lua. Before that code runs,
the wrapper computes its SHA-256 and compares it to a digest pinned into the
wrapper at build time — a tampered or replaced core is rejected with
false, "crypto mismatch". The core is public MIT math (no secrets), so the
wrapper is the only piece you must obfuscate along with your script.
The crypto core is also cached on the executor environment
(getgenv().VampauthCrypto) and can be supplied explicitly via
Check(key, { crypto = myCryptoTable }), so a loader can fetch it once and
share it across many embedded clients.
Executor support (UNC-style)
HttpFetch tries request, then http_request, then syn.request, and falls
back to HttpService:RequestAsync; it reads both StatusCode/statusCode and
Body/body forms. HWID helpers use getgenv (falling back to _G) and,
when present, readfile/writefile. The client runs on any executor that
exposes the standard UNC surface.
Configuration
The default HWID is the Roblox analytics client id — a per-install string, not a strong unique. The client uses it as the default fingerprint, falling back to a persisted file (when the executor has
readfile/writefile) and then a fresh per-session id. Do not rely on it for real binding; pass your own fingerprint via Vampauth.new({ hwid = ... }) / SetHWID(...). For a stronger fingerprint see Integration snippets.
Embed, then obfuscate
The recommended workflow:- Embed the client inside your script (paste the module body, not a remote
loadstring). - Embed your keycheck logic, project key, and public signing key in the same file.
- Obfuscate the whole thing with script protection.
Threat model
What verifying the signature stops:- Fake responses — a malicious server or an injected HTTP hook cannot sign a response; only Vampauth holds the project’s signing private key.
- Replays — each call uses a fresh nonce, so a captured
validresponse no longer verifies on the next call. - Edited responses — changing
expires_atorkeyinvalidates the signature.
- The user removing the check — a client executed locally can have the keycheck patched out. This is what obfuscation is for.
- Weak HWID fingerprints — HWID is passthrough; Vampauth only compares the string. The default (analytics client id) is basic; real binding requires your own fingerprint via
SetHWID. - Replacing the crypto core only — the digest pin blocks swapped crypto, but this stops the wrapper, not your whole script. Obfuscating the wrapper with your script is still the real defense.
Source
The wrapper lives atsdk/reference/wrapper.luau in the repository and builds to sdk/reference/vampauth.lua (sdk/reference/vampauth-crypto.lua for the crypto core). Both are served to in-game clients from /client/vampauth.lua and /client/vampauth-crypto.lua. The wrapper is verified on the real-luau Delta executor (sdk/vampauth-delta-test.lua); scripts/verify-reference-client.js is a smoke harness.
See also
- key/check — the endpoint contract.
- Signature verification — the signed payload.
- Integration snippets — building your own client.