AuroraMeter (Aurora Meter v0.3.1)
View SourceAurora Meter — real-time usage metering, plan entitlements, and Stripe-ready billing primitives for Phoenix.
Count, gate, and bill on the BEAM: increments hit an in-memory ETS counter
(microseconds, no database on the hot path), a flusher persists snapshots to
Postgres on an interval, and a broadcaster fans live values out over
Phoenix.PubSub.
Add it to your host application's supervision tree — it validates configuration at boot and starts the metering runtime:
children = [
MyApp.Repo,
{Phoenix.PubSub, name: MyApp.PubSub},
AuroraMeter,
MyAppWeb.Endpoint
]Tenants: the first argument everywhere
Every function takes a tenant first (org in the examples). It is whatever
identifies the customer being metered: the organisation or account that owns
the subscription, not the individual user. Strings, integers and atoms work
as they are ("org_42", 42, :acme); to pass your own struct, configure a
module that implements AuroraMeter.Tenant:
defmodule MyApp.Tenant do
@behaviour AuroraMeter.Tenant
def to_key(%MyApp.Accounts.Org{id: id}), do: "org_#{id}"
def to_key(key) when is_binary(key), do: key
end
config :aurora_meter, tenant: MyApp.TenantThe resolved key must be stable and unique per customer: it is the key for the
ETS counters, the persisted counter rows and the PubSub topics. Subscribe a
plan (subscribe/2) with the same term you meter with.
The public API: track/4, usage/2, usage_all/1, history/3 (metering);
check/2, allowed?/2, entitled?/2, remaining/2, quota/2, reserve/3,
with_quota/4 (entitlements); subscribe/2, plan/1, period/1 (plans).
Summary
Functions
Whether check/2 currently returns :ok.
Checks whether tenant may use feature. See AuroraMeter.Entitlements.check/2.
Whether the plan grants access to feature (ignores quota).
Returns tenant's daily usage of feature as a list of
%{date: Date.t(), value: integer()} points, one per UTC day, oldest first.
Returns the current billing period for tenant (%{start:, end:, source:}).
Returns tenant's current plan. See AuroraMeter.Entitlements.plan/1.
A dashboard-ready quota snapshot. See AuroraMeter.Entitlements.quota/2.
Remaining quota for a hard-limited feature, or :unlimited.
Atomically reserves usage against the plan. See AuroraMeter.Entitlements.reserve/3.
Atomically reserves qty usage against the plan.
Validates configuration and starts the Aurora Meter runtime supervisor.
Assigns plan_id to tenant locally. See AuroraMeter.Entitlements.subscribe/2.
Records qty usage of feature for tenant in the current period.
Returns tenant's usage of feature in the current period.
Returns a map of feature => value for tenant's warm counters this period.
Returns the Aurora Meter version.
Gates, runs, and meters in one step. See AuroraMeter.Entitlements.with_quota/4.
Gates, runs, and meters qty in one step.
Functions
Whether check/2 currently returns :ok.
Checks whether tenant may use feature. See AuroraMeter.Entitlements.check/2.
Advisory: this reads the counter and compares, so two concurrent callers can
both see :ok at the cap. To enforce a hard limit atomically use
reserve/3 or with_quota/4.
Whether the plan grants access to feature (ignores quota).
@spec history(term(), atom(), keyword()) :: [AuroraMeter.Storage.history_point()]
Returns tenant's daily usage of feature as a list of
%{date: Date.t(), value: integer()} points, one per UTC day, oldest first.
Options: :days (default 30, ending today), or explicit :from / :to
dates. Days with no usage are present with a value of 0. Requires
:history (on by default) and schema version 2.
AuroraMeter.history(org, :ai_generations, days: 7)
#=> [%{date: ~D[2026-09-01], value: 12}, ..., %{date: ~D[2026-09-07], value: 3}]
@spec period(term()) :: AuroraMeter.Period.t()
Returns the current billing period for tenant (%{start:, end:, source:}).
@spec plan(term()) :: AuroraMeter.Plan.t() | nil
Returns tenant's current plan. See AuroraMeter.Entitlements.plan/1.
@spec quota(term(), atom()) :: AuroraMeter.Entitlements.quota()
A dashboard-ready quota snapshot. See AuroraMeter.Entitlements.quota/2.
@spec remaining(term(), atom()) :: non_neg_integer() | :unlimited
Remaining quota for a hard-limited feature, or :unlimited.
Atomically reserves usage against the plan. See AuroraMeter.Entitlements.reserve/3.
@spec reserve(term(), atom(), pos_integer()) :: :ok | {:error, :limit_exceeded | :not_entitled}
Atomically reserves qty usage against the plan.
@spec start_link(keyword()) :: Supervisor.on_start()
Validates configuration and starts the Aurora Meter runtime supervisor.
Raises NimbleOptions.ValidationError if the :aurora_meter configuration is
missing a required key or has a value of the wrong type.
@spec subscribe(term(), atom() | String.t()) :: {:ok, AuroraMeter.Schema.Subscription.t()} | {:error, Ecto.Changeset.t()}
Assigns plan_id to tenant locally. See AuroraMeter.Entitlements.subscribe/2.
Records qty usage of feature for tenant in the current period.
tenant is any term that identifies the customer (a string, an id, or your
own struct via a configured AuroraMeter.Tenant); see the module docs.
Runs on the ETS hot path (no database round-trip) unless the feature is durable
(opts[:durable] or configured in :durable_features), in which case a raw
event row is also written. Options: :durable (boolean), :metadata (map).
Returns tenant's usage of feature in the current period.
Returns a map of feature => value for tenant's warm counters this period.
@spec version() :: String.t()
Returns the Aurora Meter version.
Examples
iex> is_binary(AuroraMeter.version())
true
@spec with_quota(term(), atom(), (-> result)) :: {:ok, result} | {:error, term()} when result: term()
Gates, runs, and meters in one step. See AuroraMeter.Entitlements.with_quota/4.
@spec with_quota(term(), atom(), pos_integer(), (-> result)) :: {:ok, result} | {:error, term()} when result: term()
Gates, runs, and meters qty in one step.