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
pnpm add @lucerna-dev/identitynpm install @lucerna-dev/identitybun add @lucerna-dev/identityOn a Ruby server?
Identity ships inside the lucerna gem — Lucerna::Identity is the same primitive, and Lucerna.identity.identify delivers it to People.
Use it
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 ididentify()— who the user is.userIdis your app's id (u_42), never an email — see Privacy & GDPR.trait()/traits()— what's true about them. Values are stored as strings;nulldeletes 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:identifyworks — 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: trueif 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:
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
- Quickstart — identify your first user in under a minute.
- Reference — every option and method.
- Privacy & GDPR — the PII rules and the erasure endpoint.