Security
Your app talks to Onelo with an API key — but a publishable key ships inside your app, so on its own it isn't a secret. What actually protects the connection is that Onelo binds each key to where and what is allowed to use it, and enforces that server-side on every call. This section covers the model, how you configure it, and the signals Onelo surfaces when something looks wrong.
The model — three layers, two keys
Onelo's client-facing security is three layers. Each one answers a different question about a request:
| Layer | Where | What it proves |
|---|---|---|
| Allowed domains | Web (client) | The request came from your own domain — the browser’s Origin is on your verified list. |
| App identity & attestation | Native / desktop (client) | The request came from a genuine, unmodified build of your app — App Attest (Apple), codesign, or Play Integrity (Google), against your registered Bundle ID. |
| Secret key | Your backend | The request came from your server — the real trust boundary. Sensitive work is gated here, not in the client. |
Two kinds of key
| Key | Where it lives | What it is |
|---|---|---|
onelo_pk_live_* | Compiled into the client app | Publishable. Public by design — it identifies the app and ships in the binary; on its own it is not a secret. The layers above are what bind it to your app. |
onelo_sk_live_* | Your server only | Secret. Grants server trust (SSE stream, identify, verification). Stored only as a hash — never in plaintext; shown once at creation, then only the last few characters. Never ship it to a client. |
The init handshake — a different proof per app type
Before the per-request checks, the very first call the SDK makes (fetching its config) is gated by a proof chosen from your app type — set when you create the app. This is what stops a copied publishable key from working somewhere it shouldn't.
| App type | Must present | Checked against |
|---|---|---|
| Web | the browser's Origin | your verified domains — a live web app with none configured is rejected outright. |
| Server | an X-Client-Secret header | the app’s client secret, stored only as a hash. Wrong or missing → rejected. |
| Mobile / desktop | a PKCE code_challenge | a one-time challenge bound to this app, so an intercepted handshake can’t be replayed (the same PKCE behind Onelo’s hosted sign-in). |
Only native apps use PKCE — they can't safely hold a secret, so they prove themselves with a one-time challenge. After the handshake, mobile/desktop apps additionally carry per-request attestation.
Explore
Bind your web publishable key to your own domains — DNS TXT or well-known verification, localhost for dev, and what gets rejected.
Bundle IDs + per-request attestation (App Attest, codesign, Play Integrity), the discovery window, and the gotchas that waste the most hours.
App Mode (dev vs production) and the Production Readiness gate that blocks live keys until your app is verified.
The workspace-level feed of every security signal — auth rejections, rate limits, brute force, bypass attempts, and verifications.
Automatic detection of a leaked or abused live key — IP/UA spread, brute-force ratios, sustained volume — filed in your Security events feed.
userIdHash reference in an old snippet, ignore it.