Security

How OfferMettle protects your account and documents

This page states the security posture exactly as built — what sign-in method exists, how a session is held, how a document is read, and how access is checked — matched to the numbers in the running code, not to what we might build later.

What this page is, and is not

This is a description of what is built, not a certification. OfferMettle has not completed a third-party security audit, does not hold any certification (SOC 2, ISO 27001 or otherwise), has not had a penetration test performed, and this page has not been reviewed by outside counsel. If any of that changes, this page will say so, dated.

No passwords, ever

Signing in uses magic link or WebAuthn — nothing else. There is no password field anywhere on this site, for any account, and no password is ever requested, accepted or stored.

Authentication and session handling
AspectAs built
Sign-in methodsMagic link (email) and WebAuthn. No password field exists anywhere on the site.
Password storageNone. No password is ever requested, accepted or stored, for any account.
Magic-link token storageOnly the SHA-256 hash of the token is stored; the raw token is never persisted.
Magic-link token lifetime15 minutes from issue, then it stops being redeemable.
Magic-link token useSingle-use — the stored token record is deleted the instant it is redeemed, even if it is still inside its 15-minute window.
Magic-link request rate limit3 requests per email address per 15-minute window; further requests in that window are refused.
Unknown email addressesA magic-link request for an address with no account returns the same 200 response and sends nothing — so a request can never be used to test which addresses have an account.
Access token storageHeld only in memory (a Pinia store field), for the lifetime of the page. Never written to localStorage, sessionStorage or a cookie.
Refresh token storageAn httpOnly cookie the server sets and reads directly. It never reaches frontend JavaScript at all.

Document storage and reads

Uploaded documents — an offer letter, recruiter correspondence — live in a private object store, never a public one, and every read of a document is served by a URL that is generated fresh, used once, and expires quickly.

Document storage and access
AspectAs built
Where uploads liveA private object store (MinIO in this deployment) — never a public bucket, never reachable by a guessed URL.
How a document is readA fresh presigned URL is issued per request. No presigned URL is ever written to a database record or stored anywhere.
Presigned URL lifetimeFive minutes or less from the moment it is issued.
Filenames and offer data in URLsNever. A filename or an extracted offer figure never appears in a URL path or a query string.
RetentionDeleted 30 days after a case closes by a scheduled purge job. A delete-now control in the portal removes the object immediately, keeps a dated audit row, and kills the read — it does not wait for the 30-day window.

Authorization: checked on the server, every time

Every case and document route is checked against who is actually signed in, on the server, before any data is returned — not by hiding a link in the interface.

  • Every case and document route checks ownership server-side before returning anything: you can reach only your own case, and no one else can reach it.
  • A request for a case that is not yours is refused before the case is even looked up, and the refusal is identical to the response for a case that does not exist — so probing tells an attacker nothing.
  • A request for someone else's case, or for a case that does not exist, gets the exact same response: a bare 404 with no case data in the body. There is no way to tell, from the response, which of those happened.
  • This negative case is not incidental — it is a specific, required test on every protected route in the codebase.

What we do not claim

We are precise about what has and has not happened:

  • No completed third-party security audit.
  • No certification of any kind — not SOC 2, not ISO 27001, not any other framework.
  • No penetration test has been performed.
  • No attorney or outside counsel has reviewed this page or the practices it describes.

For how we source and date factual claims across the whole site, and where the legal boundary sits, read the editorial policy. For what data we collect, keep and delete, and the exact numbers behind retention, read the privacy page. For where these controls sit inside the paid Sprint's flow end to end, see how the Sprint works.

24-Hour Offer Decision Sprint

Start the 24-Hour Offer Decision Sprint

$599 fixed— $399 for the first 20 clients

Complete intake by the displayed cutoff. If your Decision Brief is not delivered within 24 hours, your fee is refunded by a scheduled sweep — no case-by-case review needed. We guarantee delivery time, never an outcome.

Start the Sprint

Written US offer, at least 24 hours remaining. Eligibility is confirmed before checkout.