Security model
The boring kind of security.
No vague trust badges. No "enterprise-grade" hand-waving. Here's the actual cryptography we use, the threats we explicitly defend against, and the things we deliberately do not claim.
Security model
Cryptographically sound. Deliberately stateless.
We're a security layer, so the security has to be the boring part. Here's exactly what we do — no buzzwords, no certifications we don't have.
AES-256-GCM at rest
Your rules and signing secrets are encrypted before they touch the database. The encryption key never leaves the engine process.
HMAC-SHA256 in flight
Every response is signed with your project's secret. The SDK verifies before honouring — so a man-in-the-middle can't forge an ALLOW.
Stateless on your traffic
We never persist request bodies, headers, or response payloads. /check carries a small fingerprint that we evaluate and forget.
Fail-open by default
If our API is unreachable, the SDK returns ALLOW so an Acrossed outage cannot take your app down. Flip a flag to fail-closed if you want stricter behaviour.
Threats we defend against
| Threat | How Acrossed handles it |
|---|---|
| Man-in-the-middle forging an ALLOW | Every response is signed with HMAC-SHA256 over (timestamp + body) using your project's signing secret. The SDK rejects the response if the signature doesn't verify. The secret never leaves your servers. |
| Replayed response from a previous ALLOW | The HMAC payload includes a timestamp. The SDK rejects responses older than 60 seconds. Tighten the window per project if you need to. |
| Database compromise leaking your rules or secret | Both your rule JSON and your signing secret are encrypted with AES-256-GCM before insert. The encryption key is held in API process env vars and never written to disk in plaintext. A stolen DB dump returns ciphertext. |
| Rogue admin reading customer rules | Same answer — the only place plaintext exists is the API process memory after key-derivation at boot. No log line, no audit trail entry contains the cleartext. |
| Acrossed outage taking down your app | All SDKs fail open by default. If our API is unreachable, your request passes. You can opt into fail-closed if your threat model requires it. |
| Quota exhaustion silently letting traffic through | When you hit your monthly cap, /check returns HTTP 402. The SDK treats that as a deny by default — you have to explicitly opt in to allow-on-quota-exceeded behaviour. |
Want decision logs? Use your own database.
By default we store nothing about your traffic — only aggregate counters (allowed / denied / monthly total). If you want a full audit trail, you can plug your own Postgres URL into any project from the dashboard. We'll INSERT one row per decision into your database, asynchronously, off the hot path. The connection string is encrypted with AES-256-GCM before it hits our database.
- • You own the schema. We auto-create one table the first time you enable it.
- • You own retention. We never read or trim the data — that's your DB.
- • Disable any time. Existing rows stay where they are.
- • Works with any Postgres-compatible host: Neon, Supabase, RDS, self-hosted.
Decision writes are fire-and-forget — the /check response never waits on your DB. If your DB is down, decisions still flow; we just drop the log row and surface the last error in your dashboard.
What we do NOT claim
- • No SOC 2 / ISO 27001 yet. We're a small operation — those certifications cost real money and we'd rather be transparent than fake them.
- • No formal SLA. We run on a single VPS with monitored uptime. If you need a contractual SLA, write us — we'll talk Enterprise.
- • No SSO / SAML. We use Clerk for auth. If you need SSO via Clerk, you can configure it on your Clerk org. We don't broker enterprise IdP flows ourselves.
- • No regional data residency. All traffic terminates at one region today. We'll add regions when traffic warrants it.
We'd rather lose a deal because we don't have a checkbox than win one and ship something that's not real.