How it works
On first launch the SDK generates an ECDSA P-256 keypair inside the device’s secure hardware:- iOS: Secure Enclave when present (most iPhones since the 5s), software Keychain otherwise. Requires iOS 13+.
- Android: StrongBox when supported (Pixel 3+, recent Samsung), TEE-backed Keystore otherwise. Requires API 23+ (Marshmallow).
secure_enclave, tee, strongbox, or
software.
React Native + Expo Go: native modules can’t load in Expo Go, so the SDK
transparently falls back to a software ECDSA P-256 key. You can evaluate the
entire flow in Expo Go and ship hardware-backed with no code change. The backend
records the
software attestation either way.The publishable key
You ship one publishable key (pk_live_…) in your app binary. Its only
capability is letting a fresh install register a device. It cannot read tickets,
list users, or impersonate anyone.
If your key leaks, an attacker can register throwaway devices (rate-limited and
bounded), but they cannot touch any existing user’s data, because every
authenticated call is signed by a private key that never left the original
device’s secure hardware.
You ship the key and your users are secure by default. There’s no separate “secure mode” to enable.
Label-only identity
identify({ externalId, email, traits }) attaches your user’s identity to the
device row. It is pure metadata: it grants no access, because access is already
established by the device key. If you never call identify(), tickets still
work; agents just see an “Unverified device” badge instead of an email.
This is why identify() is safe to call unconditionally on every launch. See
Identity & lifecycle.
When Verified identity is enabled, identityToken is an authorization proof,
not metadata. Invalid proofs fail closed, logout uses clearIdentity(), and
normal device reset is no longer the account-switch primitive.