React Native / Expo
React Native secrets management — keys that never ship in your bundle
Anything you put in your app's JS bundle — .env values inlined at build time, keys in app.json extras, 'hidden' constants — can be extracted from the APK or IPA in minutes with free tools. Obfuscation raises the effort, not the outcome. Koove takes the secret out of the binary entirely.
The uncomfortable truth about secrets in mobile apps
In React Native and Expo, .env doesn't behave like on a server: values get INLINED into the JavaScript bundle at build time. Your 'environment variable' ships inside the artifact you upload to the store, readable by anyone who downloads it.
Obfuscation and string encryption are deterrence — they raise the attacker's cost but cannot eliminate extraction, because the app must eventually hold the plaintext. Any vendor claiming otherwise is selling you theater.
The only honest architecture: the secret never travels in the binary. It lives encrypted server-side and decrypts ONLY on a device that cryptographically proved it runs your genuine, unmodified app.
How Koove works in a React Native app
The SDK generates a device identity (X25519), proves genuineness via Apple App Attest or Google Play Integrity, and only then does your app's writer wrap secrets for that device. Decryption happens in memory, behind Face ID / fingerprint.
The full flow, live-verified end-to-end on a physical iPhone (App Attest, no bypass): challenge → Secure Enclave attestation → server-side certificate chain verification → device blessed as recipient → local decryption behind biometrics.
npm install @koove/sdk expo-local-authentication expo-secure-store
import { KooveClient } from '@koove/sdk';
const koove = new KooveClient({
apiUrl: 'https://koove.io/api',
appId: process.env.EXPO_PUBLIC_KOOVE_APP_ID, // public identifiers only
appToken: process.env.EXPO_PUBLIC_KOOVE_TOKEN, // gate, not a secret
});
// 1) Attest & register this device (App Attest / Play Integrity)
await koove.init();
// 2) Decrypt on-device, behind biometrics. Plaintext stays in memory.
const apiKey = await koove.decryptSecret(envelope);What you need to know (honest edition)
Requires an Expo development build or bare workflow — the native attestation modules don't run in Expo Go. iOS 14+ for App Attest. Both platforms are verified on physical hardware: App Attest on an iPhone and Play Integrity on a Pixel, no dev bypass.
A rooted/jailbroken device or a determined attacker with YOUR running app can still read what that app can read — no client is trustworthy. That's why the real controls are server-side: attestation gating, per-device revocation with cryptographic kill, anomaly detection and canary tokens a compromised client cannot switch off.
And revocation is honest: a secret already decrypted and cached on a device is not un-delivered. Total kill = rotate the value.
Approaches to mobile secrets, compared honestly
| Approach | What actually happens |
|---|---|
| .env / app.json extras | Inlined into the bundle. Extractable in minutes. Fine ONLY for non-secret config. |
| Obfuscation / string encryption | Deterrence. Raises effort; plaintext still recoverable from a running app. |
| Proxy everything through your backend | Valid! But now every 3rd-party call costs a round-trip, and the backend holds all keys. |
| Koove (attestation + envelope encryption) | Secret never in the binary; decrypts only on verified devices, behind biometrics; per-device revocation. |
Frequently asked questions
Does it work with Expo Go?
No — attestation requires custom native modules, so you need an Expo development build (npx expo prebuild + run) or a bare workflow app. That's a one-time setup step.
What about jailbroken or rooted devices?
The SDK hard-fails locally on detection (deterrence), but honestly: a fully compromised device can read what your app reads. Server-side anomaly detection and canaries exist precisely because the client can never be trusted.
Is the appToken in my bundle a secret then?
No. The appToken identifies your app and rate-limits access to CIPHERTEXT only. Without an attested device's private key, envelopes are unreadable. That's the point of the architecture.
iOS and Android parity?
Yes: App Attest (iOS) and Play Integrity (Android) are both verified end-to-end on physical hardware — real attestation, no dev bypass, verdict stored server-side. We publish the walkthroughs with real timestamps.
Ship a React Native app that doesn't leak keys
npm install @koove/sdk — free plan, working example app on GitHub, honest docs.