Lucerna (Lucerna v0.0.1-alpha.0)

Lucerna SDK for Elixir — feature gates, experiments and identity.

Add one child to your supervision tree:

children = [
  {Lucerna, server_key: System.fetch_env!("LUCERNA_SERVER_KEY")}
]

then read anywhere — no process on the hot path, reads never raise:

identity = Lucerna.Identity.new!(user_id: "u_42", email: "sofia@acme.com")

Lucerna.Gates.flag("settings_sso", identity)      #=> true / false
Lucerna.Gates.experiment("checkout_copy", identity) #=> "urgent" / nil
Lucerna.Gates.switch("checkout")                  #=> false means killed

Lucerna.identify(identity)  # self-sync to People — the only call
                            # that transmits email/name

The tree is: a Finch pool (unless you inject your own transport), the ETS snapshot Lucerna.Gates.Store, and the Lucerna.Gates.Sync poller. Sync fetches the compiled runtime at boot and every refresh_interval (default 10s); fetch failures serve the stale snapshot — never "off". See Lucerna.Config for every option.

Multiple isolated instances: give each a :name and pass name: MyName to reads.

Summary

Functions

Delivers queued events (experiment exposures) now instead of on the refresh cadence — for scripts and tests; a running app never needs it. Always :ok, even when the instance isn't running.

Sends an identity to People — the self-sync, and the only path that transmits email/name (gates reads never transmit anything). Accepts a Lucerna.Identity, map, or keyword list; unlike gates reads this validates strictly — a bad or missing identity is a programming error and raises ArgumentError.

Starts a Lucerna instance. Options are validated here — see Lucerna.Config.new!/1; bad options raise at boot, never later.

Functions

flush(opts \\ [])

@spec flush(keyword()) :: :ok

Delivers queued events (experiment exposures) now instead of on the refresh cadence — for scripts and tests; a running app never needs it. Always :ok, even when the instance isn't running.

identify(identity, opts \\ [])

@spec identify(
  Lucerna.Identity.t() | map() | keyword(),
  keyword()
) :: :ok

Sends an identity to People — the self-sync, and the only path that transmits email/name (gates reads never transmit anything). Accepts a Lucerna.Identity, map, or keyword list; unlike gates reads this validates strictly — a bad or missing identity is a programming error and raises ArgumentError.

Delivery is async and fire-and-forget through Lucerna.Reporting under :identity_key (defaults to :server_key): one attempt, no retry — the server upserts idempotently, so the next identify is the retry. An exact resend of the last delivered payload is skipped. Lucerna.flush/1 returns only after every identify queued before it has been attempted.

start_link(opts)

@spec start_link(keyword()) :: Supervisor.on_start()

Starts a Lucerna instance. Options are validated here — see Lucerna.Config.new!/1; bad options raise at boot, never later.