Skip to content

Quickstart

Identify your first user and pass them to Gates in under a minute.

1. Get a key

Identity syncs every change to People, so createIdentity requires an apiKey. Any key granted people:identify works:

  • In the browser — the publishable client key (ck_client_…) from Settings → API keys. It's safe to ship: it is write-only and can never read profile data back.
  • On a server — your People server key or a scoped key.

2. Install

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

3. Create one identity

In the browser there's one user, so create one shared instance in a module of its own:

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

export const identity = createIdentity({
  apiKey: import.meta.env.VITE_LUCERNA_CLIENT_KEY,
});

A new identity starts anonymous with a stable anon_… id — Gates rollouts and experiment variants are already sticky for logged-out visitors, before any wiring.

4. Identify on login

When the user signs in, tell identity who they are:

ts
identity.identify({
  userId: "u_42", // your app's id — never an email
  email: "ada@example.com", // PII: routed to encrypted People fields
  name: "Ada Lovelace",
  traits: { plan: "pro" }, // targeting data: no PII here
});

identity.trait("beta", true); // update one trait later

That's the whole write path: every change upserts the person to People in the background — fire-and-forget, batched, never blocking your code. The userId/traits split matters for GDPR — read the two rules before shipping.

5. Pass it everywhere

Every Lucerna SDK accepts the same object:

ts
// Browser — Gates subscribes and refetches decisions on every change:
const gates = createGates({ clientKey, identity });

// Server — pass a snapshot per check:
gates.flag("new_billing", identity.current());

6. Reset on logout

ts
identity.reset();

Traits are cleared and a fresh anonymous id is minted, so two people on one device are never linked.

Persisting across page loads

By default nothing touches the device — the anonymous id lives for the session. To keep it across reloads, wire storage after the user consents (a persisted id counts like a cookie under GDPR):

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

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