Security
Granite stores the documents you would never put on a sticky note in a coffee shop. Treating them with that level of care is the product, not a feature. This page describes how.
Last updated 2026-05-25
Encrypted at rest
Every uploaded file is wrapped with per-upload envelope encryption on our servers before it lands in storage. A fresh AES-256-GCM data-encryption key (DEK) is generated per upload; the DEK is wrapped with a per-user key-encryption key (KEK) derived via HKDF from a master KEK that lives only in our application servers' environment. The bytes stored at our blob provider are ciphertext.
Encrypted in the database
Vendor names, dollar amounts, account numbers, document titles, filenames, OCR text, AI-generated summaries, and the raw entity lists we extract from each document are stored with column-level AES-256-GCM encryption and per-column key derivation. Authentication artifacts (session IPs, user-agents, TOTP secrets, magic-link delivery secrets, audit-log details) are encrypted the same way.
Exports stay sealed
User-triggered vault exports are wrapped with their own per-export DEK before they land in storage. The download is decrypted and streamed by our application after authenticating the requesting user — never a presigned URL to the raw bytes.
Defense in depth
Some derived data must remain queryable inside our database to power search:
These are stored unencrypted and treated as defense-in-depth concerns. We restrict access to the database via the same controls as the rest of the application; we plan to revisit this with private-projection embeddings as the product matures.
Authentication
Passwords are stored as bcrypt hashes (work factor 12). Magic-link sign-in is supported as a passwordless alternative. Time-based one-time-password (TOTP) MFA is scaffolded and will be enforceable per-user before launch.
Session cookies are encrypted, HttpOnly, Secure, and SameSite=Lax, with a 30-day TTL. Sessions are revoked when a user changes their password, resets a forgotten password, or signs a session out from settings. Authentication endpoints are rate-limited per source IP and per submitted email to keep credential stuffing impractical.
Magic-link and password-reset tokens are delivered as URL query parameters so they are automatically redacted from server-side request logs.
Storage and isolation
Original documents are encrypted before they leave our application and stored in object storage as ciphertext. End users never receive a direct storage URL for an original document — every download is re-decrypted and streamed by our application after authenticating the requesting user.
Internal pre-signed fetch URLs have a five-minute expiry. Archive exports run through the same envelope-encryption pipeline and the same authed-download path. The bucket is configured with object-versioning off, no public access, and TLS-only requests.
Audit log
Authentication events (sign-in, sign-out, password change, MFA changes), document views and downloads, exports, corrections, entity edits, and email changes are recorded in an append-only audit log. Audit-log details (including request IP and user-agent) are stored encrypted at rest. We retain audit entries for one year and purge older entries automatically.
Vulnerability reporting
If you find a security issue, please send a private report to security@granite.co. We will acknowledge within two business days, work with you on disclosure, and credit you publicly if you’d like. We do not currently run a public bug bounty, but we are happy to compensate responsibly disclosed reports of real impact.
Compliance
Granite is not yet SOC 2 audited. We are tracking the controls and intend to pursue Type I within the first year of operation. If your purchasing process requires specific compliance posture, email security@granite.co and we will share what we have today.
Questions about anything on this page? Email security@granite.co.