Skip to content

Identity

@lucerna-dev/identity answers one question for every Lucerna SDK: who is the current user? Set it once, pass it everywhere — Gates targets with it, and upcoming SDKs (Beacon, Replay, Pulse) will use the same object.

Install

bash
pnpm add @lucerna-dev/identity
bash
npm install @lucerna-dev/identity
bash
bun add @lucerna-dev/identity

On a Ruby server?

Identity ships inside the lucerna gemLucerna::Identity is the same primitive, and Lucerna.identity.identify delivers it to People.

Use it

ts
import { createIdentity } from "@lucerna-dev/identity";

const identity = createIdentity({
  apiKey: "ck_client_prod_…", // browser: the publishable client key
});

identity.identify({ userId: "u_42" });
identity.trait("plan", "pro");

gates.flag("new_billing", identity.current()); // just works

identity.reset(); // on logout — traits cleared, new anonymous id
  • identify() — who the user is. userId is your app's id (u_42), never an email — see Privacy & GDPR.
  • trait() / traits() — what's true about them. Values are stored as strings; null deletes a trait.
  • current() — the { userId, traits } object every SDK accepts.
  • onChange(listener) — fires whenever the user changes; returns an unsubscribe function.
  • reset() — call on logout. Traits are cleared and a fresh anonymous id is minted, so two people on one device are never linked.

Syncing to Lucerna

The apiKey is required — identity keeps People up to date by itself. Every identify() and trait change upserts the person, in the background:

  • Any key granted people:identify works — the publishable client key in browsers, a server key or a scoped key on servers. No special key type.
  • Fire-and-forget: never throws, never blocks, resends on the next change if the network hiccups.
  • Anonymous visitors are not synced (no ghost profiles). Opt in with syncAnonymous: true if you want them.

Anonymous users

A new identity starts anonymous with a stable anon_… id. Passed to Gates, it gives logged-out visitors sticky rollouts and experiment variants — no userId wiring needed before login.

Persisting across page loads

By default nothing is stored on the device — the anonymous id lives for the session. To keep it across reloads:

ts
const identity = createIdentity({ apiKey, storage: localStorage });

Only wire storage after the user consents — a persisted id counts like a cookie under GDPR. Storage failures (privacy mode, quota) are swallowed; identity keeps working in memory.

One identity per request on servers

In the browser, one shared identity is right — there's one user. On a server, create the identity inside the request handler: a module-level identity would target every request as the same user.

Next

Lucerna Developer Docs