Security and trust
How the system is built, what is verified, and what is not yet certified.
This page says what is true, including where the answer is "not yet". A trust page that only lists strengths is not useful to anyone doing diligence.
Certifications
| Standard | Status |
|---|---|
| SOC 2 Type II | Not certified. No audit has begun |
| ISO 27001 | Not certified. No audit has begun |
| Penetration test | Not performed by an external firm |
We would rather tell you this than display a badge we have not earned. If a certification is a requirement for you, it is a conversation to have before you build against us, not after.
How the system is built
Fail closed, not open. A check that cannot run is treated as *not passed*, never as a zero score. An unconfigured provider returns not_configured and blocks automatic approval. Silence is never a pass — this is the single most important behaviour in the system.
Sanctions screening runs against the official lists, loaded directly from OFAC and the European Commission and refreshed daily — see service status. It was built only after the matcher had been tested against the real data, which exposed two faults that no synthetic list would have: non-Latin names normalised to an empty string and then matched everybody, and a short designation substring-matched any name containing it. Together they flagged essentially every applicant, which in a fail-closed engine blocks every approval and teaches reviewers that hits are noise. Both are fixed; the matcher is token-level Jaro-Winkler with a coverage requirement on both sides.
Blocks that no setting can disable. A confirmed sanctions match, a document with tampering indicators, an apparently under-age applicant, and a required check that did not run each prevent automatic approval regardless of how a client is configured or what the API is asked to do. An earlier version had a hole here — turning screening off also removed the block on a confirmed match — and it was closed by making the verdicts unconditional and only their mandatoriness configurable.
Tenant isolation on every query. A client's data is scoped in the query itself. Sending another client's identifier in a header returns 403, not their data.
An audit trail that includes reading. Decisions, configuration changes and every read of an applicant's record are logged. Reading somebody's identity documents is itself an auditable event.
Identity and access
- scrypt password hashing; HMAC-signed, revocable session tokens
- TOTP two-factor for any operator, and a client's go-live checklist tracks whether every one of them has it on
- Five roles, with an auditor role that reads everything and changes nothing
- API keys scoped to named permissions, hashed at rest, shown once
- Setting a password revokes existing sessions
Data protection
Personal data fields are encrypted at rest with AES-256-GCM under a key separate from the one signing sessions, so compromising one does not compromise the other. Two retention clocks run automatically, swept every six hours: document images are erased 90 days after the decision by default, and the applicant record after seven years — the usual AML floor. Each sweep writes to the audit log. See the privacy notice for what that means for an applicant and the DPA for the settings that control it.
Things we have found and fixed
Published because a security page with no history is either very new or not being looked at.
- Unauthenticated cross-tenant writes. The applicant submission endpoint took the tenant from the request body when no verification session was supplied, so anyone could write records into any client's queue. A valid session is now required and every link is single-use.
- Server-side request forgery via webhooks. A webhook URL was checked only for
https://, so a client could aim one at a cloud metadata address and read the response from the delivery log. Loopback, link-local, private and unique-local ranges and internal-only names are now rejected, in both IPv4 and IPv6 forms. - Over-broad API key scopes. Keys accepted any scope string, including
*. Scopes are now validated against a known list. - The sanctions matcher flagged everybody. Found the day a real list was first loaded into it: every non-Latin designation matched every applicant, because the name normaliser stripped those scripts to an empty string and an empty string is contained in everything. A screening control that fires on all 100% of applicants is worse than none, because it trains people to dismiss it.
Reporting something
Write to hello@ai-sns.io with what you found and how to reproduce it. We will confirm receipt, tell you what we intend to do, and credit you if you want that. Please do not test against another client's data — ask us for a sandbox account and we will give you one.
What is still outstanding
- No external penetration test and no SOC 2 programme yet
- Edge rate limiting: in-process limits stop a script, not a botnet
- One database in one region; data residency options are not built
- No formal DPIA template for clients to start from