Skip to Content
Delmare Digital

Security

Signing, access control, the browser events endpoint, privacy, and the limits you must enforce at your platform.

Signing

The visitor cookie, the x-xp-assignments and x-xp-request headers and preview tokens are signed with HMAC-SHA256, with a separate purpose in each signed input, and verified in constant time. A browser cannot choose an identity, a version, a request context or a preview group by editing a value. The manifest is private: it is read from the database by the proxy, or from an endpoint that needs a key derived from the secret.

Rotate the secret by passing secret: [newSecret, previousSecret]. The first entry signs; every entry verifies.

Access control

  • Every plugin collection and global states each access operation explicitly: create, read, update, delete, readVersions and unlock.
  • Managers operate tests, layers and goals. Reading results and reading identities have separate permissions (access.readResults, access.readIdentities).
  • Writes to events, identities, rounds, snapshots and state through ordinary collection access are denied. Only the plugin's own privileged operations write them, each after its authorised action and in the same transaction.
  • Request handlers and admin views pass req and overrideAccess: false. Content writes when making a winner permanent use the manager's own access.
  • linkIdentity deliberately uses its own insert-ignore connection, so a duplicate link cannot abort a sign-in.

Browser events

POST /xp/events receives viewport, pageview and manual exposures, client conversions and clicks.

RuleValue
AuthenticationA valid signed visitor cookie
Body limit8192 bytes by default (events.maxBodyBytes), read as a stream
Events per body20
Content typeJSON is parsed regardless of content type, for sendBeacon compatibility
Rate limitYour events.rateLimit adapter, when supplied
Response202 {} for both accepted and dropped events; malformed requests can return errors

Exposures must name the current round, policy and the version actually rendered; the server recomputes the assignment and drops mismatches, counting them separately. Only manual, viewport and pageview exposures are accepted. Conversions need a client-writable source and are never revenue; client values are stored as 1 and attributes are allow-listed. Click conversions are accepted only for defined click-goal keys; undefined keys are dropped with 202, and defined keys are cached for up to 30 seconds.

Enforce timeouts at your platform

The real bound on slow request bodies is your firewall and platform request timeout. A stream under the byte limit can stay open, and Payload can read an X-HTTP-Method-Override body before the plugin's streamed limit runs. Add firewall rules and request timeouts for /api/xp/events.

There is no built-in in-memory rate limiter. Supply one that is shared across your instances.

Privacy

  • Visitor ids are random and opaque. Store no personal data beyond opaque unit ids: keep names, emails and other identifying values out of attributes, flag point settings and event data.
  • events.dimensions refuses identifying names such as userId, email and ip at startup.
  • cookie.consent lets you enrol only after consent.
  • eraseUser(payload, { userId }) deletes a user's identity links and events.
  • Events are deleted 180 days after a test's final round ends by default.
  • Plugin logs carry error names, codes and stack frames, not error messages or database details. Results and make-content reports store reason codes, not raw error text.

Bots and staff

Bots matched by the built-in user-agent list, staff identified by your attributesOf, preview renders and HEAD requests never record exposures.

Edit this page on GitHub

On this page