Pixelex.Identity (Pixelex v0.1.0)

Copy Markdown View Source

Who a visitor is, without storing anything on their device.

visitor_id = base64url(first 64 bits of HMAC-SHA256(salt_of_day, site_id | ua | ip))

Why HMAC and not a plain hash

A plain sha256(salt <> data) is length-extendable and, worse, invites the concatenation bug below. HMAC is a keyed PRF and is what "keyed hash" should mean. Plausible uses SipHash for speed; :crypto.mac/4 is stdlib, needs no dependency, and at even a thousand events a second the difference is far below the cost of the database write that follows it.

Why the separator is load-bearing

Without a delimiter, site_id = "ab" with ua = "c" and site_id = "a" with ua = "bc" hash identically. Two different visitors on two different sites become one person. The | costs nothing and closes it.

64 bits

Truncated to 8 bytes. Collisions are a birthday problem at ~4 billion visitors within one day, since the salt rotates — comfortably beyond anything this library will see, and short enough to keep the index small on the table that takes every write.

Both ids, every time

visitor/3 returns the id under today's salt and yesterday's. Session lookup needs both: at 00:00 UTC the salt changes and every open session would otherwise be attributed to a new person. See Pixelex.Sessions.resolve/3.

Summary

Functions

The id under one explicit salt. Exposed for tests that need to reason about a specific day rather than "now".

The visitor's id under today's salt, and under yesterday's.

Types

ids()

@type ids() :: %{id: String.t(), previous_id: String.t() | nil}

Functions

hash(salt, site_id, user_agent, ip)

@spec hash(binary(), String.t(), String.t() | nil, String.t() | nil) :: String.t()

The id under one explicit salt. Exposed for tests that need to reason about a specific day rather than "now".

visitor(site_id, user_agent, ip)

@spec visitor(String.t(), String.t() | nil, String.t() | nil) :: ids()

The visitor's id under today's salt, and under yesterday's.

previous_id is nil on a site's first day, when there is no yesterday.