Encryption and Hashing
Authentication, integrity checks, and secure comparisons all need cryptographic primitives. Slingshot exposes a small, audited set: argon2id password hashing through the runtime, HMAC-SHA256 signing for arbitrary payloads, SHA-256 hashing, and timing-safe equality.
What you can do
Section titled “What you can do”| Primitive | Use it for |
|---|---|
runtime.password.hash | Hashing user passwords (argon2id by default in Bun) |
runtime.password.verify | Verifying a plaintext password against a stored hash |
hmacSign | Signing webhook payloads, cookies, cursors |
hmacVerify | Verifying inbound HMAC signatures |
sha256 | Non-keyed hashing — fingerprints, dedup keys |
timingSafeEqual | Comparing secrets without leaking timing |
Password hashing
Section titled “Password hashing”Password hashing happens through the runtime so it stays portable across Bun, Node, and edge hosts. The auth plugin uses these methods internally for credential authentication — you only call them yourself when you’re rolling custom credential handling:
// @skip-typecheckimport { bunRuntime } from '@lastshotlabs/slingshot-runtime-bun';
const runtime = bunRuntime();
const hash = await runtime.password.hash('correct horse battery staple');const ok = await runtime.password.verify('correct horse battery staple', hash);// ok === trueThe Bun runtime uses Bun.password.hash (argon2id by default with sane parameters).
The Node runtime uses argon2 via the same interface. Stored hashes are
self-describing — they include the algorithm and parameters, so a future parameter
upgrade verifies old hashes correctly.
HMAC signing
Section titled “HMAC signing”For signing arbitrary payloads — webhook bodies, cursor tokens, presigned URLs, or
anything you’ll later verify — use hmacSign and hmacVerify:
// @skip-typecheckimport { hmacSign, hmacVerify } from '@lastshotlabs/slingshot';
const secret = process.env.WEBHOOK_SECRET!;const payload = JSON.stringify({ event: 'order.shipped', orderId: 'ord_123' });
const sig = hmacSign(payload, secret);// Send `payload` and `sig` to the recipient.
const valid = hmacVerify(payload, sig, secret);// valid === trueBoth functions accept secret: string | string[]. Pass an array when you’re rotating
keys — the active key is at index 0, and rotated keys live after. New signatures use
the active key; verification accepts any key in the list. This makes key rotation a
two-step deploy: add the new key at index 0, wait for outstanding signatures to age out,
then drop the old key.
SHA-256 and timing-safe compare
Section titled “SHA-256 and timing-safe compare”For non-keyed hashes (fingerprints, deduplication keys, content hashes):
// @skip-typecheckimport { sha256 } from '@lastshotlabs/slingshot';
const fingerprint = sha256(`${userAgent}|${ipAddress}|${acceptLanguage}`);When comparing any value that came from the network — signatures, tokens, secrets —
use timingSafeEqual, never ===:
// @skip-typecheckimport { timingSafeEqual } from '@lastshotlabs/slingshot';
if (timingSafeEqual(providedToken, expectedToken)) { // safe to proceed}What’s next
Section titled “What’s next”- Cookies —
signCookieValueis built onhmacSign - Request Signing & Webhook Auth — HMAC verification for inbound webhooks
- Authentication — credential auth that uses the runtime password helpers