Security
Consumer reporting agency
Gram exists to restate claims at their verifiable information value, so this page is written under the same rule it applies to everyone else. Every statement below describes something that is implemented today. Where a control is partial, it says so; where something does not exist, it says that too.
Access and authentication
Sign-in is handled by Clerk; Gram never sees or stores a password. Every review route resolves the review by id and by the signed-in owner in the same lookup — a review belonging to another account returns the same 404 as a review that does not exist, so the API does not confirm that someone else's review exists.
Transport
The app and the marketing site are served over TLS and send HSTS (max-age two years). The API at api.nolarping.com is served over TLS and redirects plain HTTP to HTTPS, but does not currently send an HSTS header — a first request typed as http:// is redirected rather than refused. Database connections require TLS.
Browser hardening
The app sends X-Content-Type-Options: nosniff, X-Frame-Options: DENY, a strict-origin-when-cross-origin referrer policy, and a Permissions-Policy denying camera, microphone and geolocation.
Its Content-Security-Policy is currently report-only: violations are not blocked. No report collector is configured yet, so this header is currently a compatibility rehearsal rather than an active monitoring control. It is deliberately not enforcing because the auth provider loads scripts and XHR from hosts a static policy can get wrong by one origin, and a wrong policy locks users out of sign-in. The public marketing site does enforce its policy.
Storage
Data lives in a managed PostgreSQL instance on Azure, encrypted at rest by the platform and reachable only from the Azure services that run Gram — it is not exposed to the public internet. Automated backups and point-in-time restore are enabled at the managed service's configured retention.
Integration credentials — ATS API keys and Google refresh tokens — are sealed with Fernet (AES-128-CBC plus HMAC) under a key held in the runtime environment before they are written to a row, and unsealed only at the moment of use. Disk encryption alone does nothing against a leaked dump; this does. Document text is deliberately not encrypted at the application layer: the pipeline must read it, so its protection is deletion and retention, not encryption, and it would be dishonest to describe it otherwise.
Retention and deletion
Reviews are kept until you delete them by default. Settings can instead apply a 30-day or 90-day automatic-deletion window to every existing and future review in the account. The dashboard shows the resulting deletion date, and a daily cleanup removes past-retention documents, reports, reviewer decisions, and applicant-bearing labels from retained billing metadata. An open dispute, active adverse-action process, or required report delivery pauses deletion of its affected review and report until the process closes or the delivery finishes.
An account-wide purge is also available from Settings: it erases every operational review, document and report held under the account, disconnects and deletes every stored integration credential, and then tells you how many reviews were removed. The account itself survives, so you stay signed in. Compliance and audit records, plus non-applicant billing and usage records, remain on their separate retention schedules as described in the privacy notice. Operational deletion is not reversible and does not wait for the review-retention window, but it will not remove a report held by an active applicant process or required delivery.
Prompt injection
Applicant text is untrusted input. It reaches a model only inside <document> tags, and before wrapping, the wrapper's own tag, common chat and instruction delimiters, and invisible characters (zero-width, bidi controls, and the Unicode Tags block that mirrors ASCII invisibly) are stripped, so text cannot close its wrapper or hide an instruction from a human reader that a model still sees.
Instructions addressed to an automated screener are treated as findings, not commands: extraction is told never to obey them and to report them as hidden-instruction claims, and a literal wrapper tag in the document forces such a claim in code regardless of what the model decided — because model-based detection was measured to be real but intermittent. Detection of the remaining cases (white text, instruction text with no markup) still depends on the model, which is why the check pipeline holds no side-effecting tools: it can read the web and return text, and that is all it can do.
Outbound fetches
When a resume is pulled from an ATS, the attachment URL is remote data and is treated as such: HTTPS only, every hop's host resolved and refused unless the address is public (private, loopback, link-local, reserved and multicast ranges are blocked), the resolved address pinned so no second lookup exists for a DNS-rebinding answer to win, redirects re-checked at every hop, and the response streamed under a 15 MB cap. Source pages fetched during checking go through the same guards.
Abuse and spend limits
The endpoints that spend model tokens or provider search quota — upload, reanalysis and retry, and integration preview/import/sync — are rate-limited per account in a sliding window. The limits are set far above normal use: they exist to bound runaway loops, not to police a recruiter working through a large batch in one sitting. They are enforced in-process, so across replicas the effective ceiling is the limit times the replica count.
Subprocessors
These are the service providers and connected systems that receive customer or candidate data in the current product. Public-source websites can also receive claim-derived search terms and ordinary request metadata when Gram checks evidence.
- OpenAI— the model API behind extraction, claim checking and the server-side web search those checks run. It receives document text and claim text, and, when a claim is about a person, the candidate's name and identifying anchors as search vocabulary. Candidate names therefore appear in web-search queries.
- Clerk — authentication. Receives reviewer account identity (email, credentials, session data). No candidate data.
- SendGrid (Twilio) — delivers applicant notices. It receives the recipient address, notice text, and any report or rights-document attachments the notice carries.
- Microsoft Azure — hosts the API, the analysis workers and the database. Everything Gram stores rests here.
- Vercel — hosts the web app and the marketing site. Serves the interface and sees request metadata; candidate data reaches it only in transit as part of what the browser sends and receives.
- Google— only when a reviewer connects a Google Form. Gram reads that form's questions and responses (which contain applicant answers and any attached files) under read-only Forms scopes, plus the reviewer's own email for display.
- Stripe — billing. Receives subscription and payment details. No candidate data and no document text.
- Sentry — optional browser error reporting when a deployment configures it. It receives exception and page context with default PII collection disabled; session replay and performance tracing are not enabled.
- Your ATS, if you connect one — an integration you own. Gram reads candidates and attachments from it with the key you supply; nothing is written back.
- Public-source hosts — scholarly indexes, code-hosting sites, and source pages consulted during a check receive the query or URL needed for that lookup and ordinary network request metadata.
Compliance
Gram is not SOC 2 certified. No audit is underway and no report exists. Certification will be pursued when an enterprise customer requires it. Anyone telling you otherwise — including any part of this site — would be doing exactly what this product was built to catch.
Reporting a vulnerability
Send it to nathan@nolarping.com. Gram is a small operation with no bug-bounty program and no guaranteed response time, but reports are read by a human and acted on. Please do not test against other people's accounts or data.
How the documents themselves are handled is on the privacy page.