Security review checklist
What a platform or security team can verify about this SDK, what it cannot, and which questions belong to the application team instead.
- Status
- pre-1.0
- Applies to
- 0.1.0
- Platforms
- Expo · Browser · Node
- Prereqs
- None — written for a reviewer, not an integrator
- Reading time
- 13 min
This page helps a reviewer decide whether to use the SDK. It groups verifiable evidence, unknowns, and disqualifying limits, including our own. Read these limits before week three of an evaluation.
Maturity, stated first because it constrains every other answer: 0.1.x. Public APIs and persisted formats may change before 1.0.
What evidence exists
An open implementation. The published source is the library, its documentation, and its generated API reference, under AGPL-3.0-or-later. You can read the protocol code, the adapter boundaries, and the validation paths.
A versioned protocol profile with revision pins.
The profile names X3DH Revision 1, PQXDH Revision 3, and Double Ratchet Revision 4.
It also names Sesame Revision 2 and ML-KEM Braid Revision 1. It does not describe the profile as "the Signal Protocol" generally.
The upgrades table gives each date. Reviewers can check a profile against named revisions.
A written threat model with an explicit out-of-scope list. In-scope risks include passive network observation and active tampering. They include replay, reordering, duplication, truncation, and malicious or stale relay responses. The model covers identity substitution after trust pinning and malformed protocol input.
It covers loss and out-of-order delivery within configured bounds. It also covers later compromise followed by an uncompromised ratchet recovery step.
Out-of-scope risks include compromised endpoints, JavaScript engines, dependencies, and build pipelines. They include hostile same-process code and high-assurance timing attackers. The model does not guarantee secret erasure from managed memory. It excludes hardware, physical, speculative-execution, and microarchitectural attacks.
It excludes traffic-analysis resistance beyond implemented controls. It excludes Signal Messenger interoperability and authenticity recovery on unauthenticated first contact. See threat model.
Documented limitations rather than absent ones. Limits and metadata documents metadata exposure, sealed-sender limits, and necessary relay observations. First contact is UNVERIFIED_TOFU, not authenticated identity.
Explicit adapter boundaries. ISignalProtocolLocalStore is device-local protocol state. ISignalProtocolRelayServer is an authenticated device, prekey, and envelope service. SignalProtocolRemoteObjectStore provides brokered encrypted-object operations. You can substitute and review each interface independently.
Reproducible public checks. Continuous integration provides clickable logs for every push and pull request. It runs npm ci against the lockfile, npm run build, and npm run typecheck. It also runs npm audit --omit=dev at moderate severity against production dependencies. These checks show that the code compiles and its types are consistent. They also identify known production advisories at moderate or higher severity.
A larger suite you cannot run. The public repository is a mechanized export of a private engineering repository. An allowlist selects each published file. The automated checks use unpublished internal paths and stay outside the export. The most recent full run was 2026-08-10. It executed 384 modules and 6,893 assertions in 330 s.
The run passed 6,891 assertions, skipped 2, and failed 0. An export comes only from a passing revision. Coverage includes conformance, published cryptographic vectors, protocol behavior, property checks, integrations, adapters, and the public API. CI runs the README quickstart as written after each change.
Treat that as our claim, not as evidence that you verified. Reviewers can request a walkthrough of the assurance material, including the conformance scenarios and their results, at security@open-e2ee.dev.
What does not exist
No audit by an independent firm. Every change passes an adversarial AI review before it merges, and recurring whole-codebase AI audit passes run against the engineering repository. What that covers — and what it does not — is stated in the SDK's docs/ASSURANCE.md. No independent firm has audited the SDK, and none is engaged. Treat the package as reviewed by machines and unaudited by a firm. We will not describe it any other way, and you should discount any material that does.
No FIPS 140 validation, and this is structural. The cryptography is pure JavaScript. Pure-JS crypto lacks FIPS 140 validation and cannot get it in this form. No SDK setting changes that fact.
Be clear about a quote you will encounter: "PQXDH uses standardized FIPS 203 ML-KEM-1024 behavior." That is a statement about algorithm behaviour matching a published standard. It is not a validation claim and does not become one.
This SDK does not satisfy a FIPS 140 module requirement. The same limitation can affect CJIS, CMMC, or FedRAMP evaluations. Module validation is separate from protocol strength. A qualified native, WASM, Rust, or Swift backend does not exist today. Such a backend is roadmap work for stronger timing or erasure assurance. If your rules require module validation, stop the evaluation here.
No timing proof. See below.
No compatibility guarantee with Signal Messenger. Conformance work checks this profile against the specifications it cites. It does not establish general wire compatibility, which this project does not claim. Public keys and ciphertexts require exactly 0x0A || raw ML-KEM-1024 bytes, and "the 0x0A encoding is distinct from Signal Messenger deployments that use round-3 Kyber1024 tagged 0x08."
Constant-time and side-channel posture
The honest statement is in the SDK's own security model: "Browser, Node, and Expo/React Native JavaScript engines provide no machine-level constant-time contract."
The code applies best-effort source patterns on selected paths. It fully scans equal-length MACs and identity bytes. It uses fixed work to derive both decapsulation candidates before masked selection. It pads selected replay and authentication rejection paths with equal work. It also uses fixed-work rejection handling on selected authentication paths.
And immediately, the limit: "These patterns do not prove timing equivalence. Secret-influenced remainder and compression arithmetic, JIT compilation, allocation, garbage collection, cache effects, and host scheduling remain observable. The current threat model does not qualify this JavaScript profile for hostile same-process code or a high-assurance co-resident timing adversary."
Memory erasure has similar limits. secureZeroBytes() overwrites the exact mutable typed array owned by the caller. It cannot erase earlier or implicit copies. It also cannot erase immutable strings, engine temporaries, registers, JIT artifacts, garbage-collected pages, or persisted adapter copies. If your threat model includes same-process code execution or co-resident timing measurement, do not use a JavaScript implementation.
Dependency posture
We check six direct production dependencies, which resolve to six packages in total, with npm audit --omit=dev at moderate severity on every push. The check uses a committed lockfile. The repository publishes third-party notices.
Form your own view anyway: pull the lockfile and run your own SCA. Check who maintains each production dependency and where it came from. The SDK's threat model lists a compromised build pipeline and compromised dependencies as out of scope. Your supply-chain controls therefore carry this risk.
The responsibility split: quote this to your application team
- "The client owns protocol coordination; the host application owns persistence, authentication, authorization, and product policy."
- "Your app owns decrypted message rows and local files after the Signal Protocol client decrypts or stages them."
- "TLS, authorization, abuse controls, atomic prekey consumption, and access logging remain deployment responsibilities. The relay is not an identity trust anchor."
- "The SDK creates comparison data; the application owns QR rendering, scanning, the verification user experience, and storage of the user's trust decision."
- "Account reset must clear the device-ID cache, platform secret storage, and protocol store as one product-level lifecycle."
- "Backend authentication must bind registration, provisioning, unlink, and removal to the owning account."
Most of what determines whether a deployment is actually secure lives on the right-hand side of those sentences.
Questions to ask the application team, not the SDK
- Where do decrypted message rows live, how are they encrypted at rest, and what is their retention?
- What does your telemetry emit? Show the redaction function. Is it an allowlist? (See observability: Element leaked backup private key material to its rageshake servers, affecting 156 unique Matrix IDs.)
- Does the relay consume one-time prekeys atomically under concurrency, and can you show evidence?
- Is unlink authenticated against the owning account at the backend?
- What happens on
IdentityKeyChangedError? Show the screen. Who can approve trusting a changed key? - Which storage adapter ships, and is its documented status supported or experimental? If it is the bare React Native store, show the run of the exported backend-conformance kit against the backend you supply.
- Is the object store brokered, with provider credentials confined to the backend?
- What is the recovery policy, who chose it, and where is it written down?
- Does group member removal rotate the sender key, and is there a test asserting
generationadvanced? - What is the version-negotiation policy across a mixed device population?
- What is in your crash-reporting pipeline, and who can read it?
- Which license applies, and who approved it? See licensing.
On compliance, precisely
E2EE is a compliance accelerator and a liability shift. It is not a mandate. Almost no regulation requires it, and this documentation makes no compliance claim of any kind.
A server compromise exposes ciphertext instead of content. This can reduce the number of exposed records and shift exposure to endpoints you do not operate. This result does not provide a control mapping.
HIPAA is often misdescribed. A vendor that handles protected health information remains an in-scope business associate with a BAA. End-to-end encryption does not change that status. It can reduce breach exposure, but it does not remove the vendor from scope.
Opacity ledger
| Artifact | Stays on device | Sent to relay | In object store | Visible as metadata |
|---|---|---|---|---|
identityKeyPair private half | yes | no | no | no |
identityKeyPair public half | yes | yes | no | yes |
EcSignedPreKey public half + signature | yes | yes | no | yes |
KyberPreKey public half (0x0A-tagged) | yes | yes | no | tag and length |
Session record (version: 4) | yes | no | no | no |
| Message plaintext | yes | no | no | no |
| Message ciphertext | yes | yes | no | envelope size, timing, routing |
| Encrypted attachment object | staged locally | no | yes | object size, request timing |
| Device registration and presence | yes | yes | no | device count, activity |
| Encrypted device name | yes | yes | no | its existence |
| Decrypted message rows | application-owned | no | no | your design |
Next
Protocol and version upgrades
What changes when you upgrade the SDK, what happens to persisted sessions, and who owns version negotiation across a mixed device population.
Guides
Complete end-to-end builds, each one a working application rather than a fragment — with every key custody decision stated where it is made.