HMAC-SHA256 signatures, with rotatable keys.
The QR on a voteMe ticket is a base64url-encoded JSON payload β ticket id, event id, signing version, holder email hash, issued-at β concatenated with an HMAC-SHA256 signature.
Signature verification runs in the gate scanner before it touches the server. A tampered QR fails client-side and never produces a network call. A valid signature proceeds to server-side validation: status (issued / used / void / refunded / transferred), event timing, and a status transition to used inside a Firestore transaction so two scanners at two gates cannot both claim the same ticket simultaneously.
The signing version on the event's ticketing settings is the rotation primitive. Bump the version and all newly-issued tickets sign with the new key; outstanding tickets remain valid because the validator checks every known version. Rotate without invalidating sold tickets β something most ticketing systems cannot do without a migration.