Options¶
Pass options once, to init. They are read when the SDK starts. Passing different options to a
later init call has no effect, because the running instance is returned.
import { init, CaptureEvent, Profile } from '@dea/sdk';
const dea = init({
appId: 'YOUR_APP_ID',
appKey: 'YOUR_APP_KEY',
profile: Profile.Desktop,
events: [CaptureEvent.PointerDown, CaptureEvent.PointerUp, CaptureEvent.Wheel],
motionRate: 0,
targets: false,
requireConsent: true,
modelUpdates: true,
onLog: (entry) => myPanel.add(entry),
logLevel: 'info',
});
myPanel stands for your own code.
Core options¶
appId¶
| Type | Default | Required |
|---|---|---|
string |
'unknown-app' |
Yes |
Your DEA application id, issued with appKey — see
Accounts and keys. The SDK signs it into every proof, and
reads its prefix to know whether it runs for a production app; see
How the SDK reads an appId.
Always pass it: the default is a placeholder that runs as a production app DEA has not registered.
appKey¶
| Type | Default | Required |
|---|---|---|
string |
none | Yes, for Attest and Ads |
The app key DEA issued together with appId. The SDK sends it in the X-App-Key header of its
requests to DEA's service: attest() and ad proofs. Model downloads carry no key. It identifies
the app and is not a secret.
Without it, on a production appId, attest() answers 'failed' without making a request, and
every ad proof is refused before it is signed, so ads.session_opened() resolves null. Scoring
and getClassification() work either way. dea.appKey reads back the key in use,
or null when there is none.
Logging options¶
What the SDK logs is decided by the package you install, not by an option. The production package
carries no log at all. The staging package — the same SDK with the log switched on, for use while
you integrate (see Verify your setup) — logs INFO and above.
Each entry is one state change — the SDK started, a device key was registered, a proof was
accepted — in the same form on every platform.
| Package | Levels written |
|---|---|
| production | none |
| staging | INFO, WARN, ERROR |
Every entry is written to the browser console as one line:
dea 2026-10-16T01:39:29.731Z INFO sdk.ready ok session.id=ee0962fd-… core=web-desktop profile=web-desktop | SDK ready: core web-desktop, profile web-desktop
onLog¶
| Type | Default | Required |
|---|---|---|
(entry: LogEntry) => void |
none | No |
Called with every entry, as JSON with OpenTelemetry field names: timestamp, severityText,
severityNumber, eventName, body, attributes, resource. Use it for a log panel or to
forward entries to your own logger. The SDK never sends an entry anywhere itself. On the
production package it is never called.
import { init } from '@dea/sdk';
init({
appId: 'YOUR_APP_ID',
appKey: 'YOUR_APP_KEY',
onLog: (entry) => myLogger.log(entry.severityText, entry.eventName, entry.attributes),
});
formatLine(entry), exported by the package, renders an entry as the console line above.
logLevel¶
| Type | Default | Required |
|---|---|---|
'debug' \| 'info' \| 'warn' \| 'error' \| 'off' |
'debug' |
No |
Raises the level entries are written from. It can only quiet the package's own levels, never add
one: logLevel: 'warn' on the staging package drops INFO, and nothing turns logging on in the
production package.
Capture options¶
profile¶
| Type | Default | Required |
|---|---|---|
Profile |
Detected | No |
Whether the page is treated as a desktop or a mobile browser. The profile decides which input is
captured, whether motion sensors are requested, and which scoring engine scores the session.
Detection picks Profile.Mobile when the browser reports a coarse pointer ((pointer: coarse))
and at least one touch point, and Profile.Desktop otherwise.
| Profile | Captures | Motion sensors |
|---|---|---|
Profile.Desktop ('web-desktop') |
Pointer, wheel, physical keyboard (keydown, keyup) |
No |
Profile.Mobile ('web-mobile') |
Pointer, wheel, on-screen keyboard (beforeinput) |
Yes |
When the page always serves one kind of device, import the matching entry point instead of passing
profile. It skips detection:
Note: On iPhone and iPad, browsers deliver motion data only after the user allows it. With
Profile.Mobile, the SDK asks the browser for that permission when the user first taps the page, and the browser shows its own prompt. To never ask, passmotionRate: 0.
events¶
| Type | Default | Required |
|---|---|---|
CaptureEvent[] |
Set by the profile | No |
The input events to capture. Overrides the profile's list. Capturing fewer kinds reduces work on the page but also reduces the evidence the classification is built on.
| Value | Browser event |
|---|---|
CaptureEvent.PointerDown |
pointerdown |
CaptureEvent.PointerMove |
pointermove |
CaptureEvent.PointerUp |
pointerup |
CaptureEvent.PointerCancel |
pointercancel |
CaptureEvent.Wheel |
wheel |
CaptureEvent.KeyDown |
keydown |
CaptureEvent.KeyUp |
keyup |
CaptureEvent.BeforeInput |
beforeinput (typing only: not a paste, a drop or an autocorrect) |
Do not list BeforeInput together with KeyDown or KeyUp: typing on a physical keyboard fires
both, and each keystroke would be recorded twice. From any key event the SDK records only the
kind of key (a letter, a digit, a space…), the modifier keys held and its timing, never the
character, and nothing at all from a keystroke in an <input type="password">.
motionRate¶
| Type | Default | Required |
|---|---|---|
number (Hz) |
50 on mobile, 0 on desktop |
No |
0 turns motion capture off: the SDK neither listens for devicemotion nor asks for permission.
Any other value turns it on. Browsers deliver devicemotion at a rate of their own choosing, which
this option does not change; the value is recorded as the session's sensor rate and reported in
getSessionInfo().device.sensor_rate once the first reading arrives (0 until then). Values
below 25 are raised to 25.
targets¶
| Type | Default | Required |
|---|---|---|
boolean |
false |
No |
Records what kind of element each press landed on — a button, a link, an input, a scroll area, a canvas. Only the category is recorded, never the element's text or attributes. Resolving the category asks the browser for layout information once per press, so it is opt-in.
Consent options¶
requireConsent¶
| Type | Default | Required |
|---|---|---|
boolean |
false |
No |
Turns on the consent gate: nothing is recorded or scored, and no scoring model is downloaded,
until the page calls setConsent(true). See User Consent.
consentStore¶
| Type | Default | Required |
|---|---|---|
ConsentStore |
The browser's localStorage, under the key deapnetwork.consent |
No |
Where the user's answer is kept between visits: an object with read(), which returns
'unknown', 'granted' or 'denied', and write(decision). Pass your own to keep the answer
beside your consent platform's record. memoryConsentStore() keeps it for the current page only.
Model options¶
modelUpdates¶
| Type | Default | Required |
|---|---|---|
boolean |
true |
No |
Whether the SDK keeps its scoring model current. On, every init asks
https://sdk.dep-protocol.com whether DEA has published a newer model for this profile, after the
SDK is ready and without delaying anything. A newer model is downloaded, verified inside the SDK —
it must be signed by DEA and newer than the one running — and kept in the browser's IndexedDB for
the next visit. With requireConsent: true, the check waits until the user agrees. Off, the SDK
deletes any model it kept, makes no request, and scores with the model built into the package.
dea.model() reports the model in use: { version, source }, where source is 'embedded',
'cache' or 'network'.
Loading options¶
coreUrl¶
| Type | Default | Required |
|---|---|---|
string \| URL |
The engine embedded in the package | No |
Where to load the SDK's WebAssembly scoring engine from. By default it is embedded in the
package's JavaScript as a data: URL: there is no .wasm file to copy or host, and you do not
need this option. Set it only when your Content Security Policy cannot allow data: in
connect-src; allow the URL you set instead (see
Content Security Policy).
The file at the URL must be the engine built for the profile the session runs on, from the same
release as the package. The SDK refuses a file built for the other profile, or one this release
cannot read, and reports it on dea.error. One URL serves one profile, so pin the profile when
you set it.
Note: DEA's release publishes only the mobile-profile engine as a file of its own:
https://sdk.dep-protocol.com/0.19.0/core/wasm32-unknown-unknown/dea_core.wasm. Use it with@dea/sdk/mobileorprofile: Profile.Mobile, either at that URL, which DEA serves to any origin, or copied to your own host. The desktop-profile engine is not published as a separate file, so there is no file to pointcoreUrlat for a desktop session.
Options a production appId ignores¶
DeaConfig also declares integrations, core, attest, ads and model. They are for DEA's
own testing and are not part of the production integration: leave them unset. On a production
appId the SDK ignores them.