mirror of
https://github.com/hanzoai/flags.git
synced 2026-10-30 18:36:27 +00:00
cloud linked a Rust staticlib (native/flags) over cgo to decide flags. That made every consumer of the flags path a cgo build, needed a prebuilt libhanzo_flags.a on the link line, and put a second language in the release pipeline for one pure function. This is that function in Go, with no dependencies at all — the rollout hash, the full property-operator set, semver ranges, the date operators, and the serde-compatible value rendering the operators compare through. The rollout hash decides which identities fall inside a percentage, so drift would split cohorts differently here than in the rest of the fleet. The port is therefore pinned to the Rust implementation's own answers: testdata/cases.json is a 621-case table covering every operator, eight rollout percentages against forty identities each, multivariate bucketing, group aggregation and the numeric/escaping edges; testdata/oracle.json is what the Rust evaluator returned for all of them; parity_test.go asserts this package still agrees. Four deliberate mutations confirm the table has teeth: shifting the hash by one bit (27 cases), changing the variant salt (27), dropping case folding from exact matching (2), and dropping the '+' from positive float exponents (4). Two divergences the port found and had to reproduce rather than fix: an integer JSON literal and a float one render differently forever (1e3 is 1000.0, never 1000), and distinct_id must be present but may be empty. Co-authored-by: Hanzo Dev <[email protected]>
25 lines
952 B
Go
25 lines
952 B
Go
package flags
|
|
|
|
// The rollout hash. This is the single most parity-critical function in the
|
|
// package: it decides which identities fall inside a percentage rollout, so any
|
|
// drift here silently splits a cohort differently than the rest of the fleet
|
|
// (and than Hanzo Insights, which is PostHog-compatible).
|
|
//
|
|
// sha1("{prefix}{identifier}{salt}"), first 8 bytes big-endian, shifted right 4
|
|
// bits (= the first 15 hex characters), scaled into [0, 1).
|
|
|
|
import (
|
|
"crypto/sha1"
|
|
"encoding/binary"
|
|
)
|
|
|
|
// longScale is 15 hex digits of headroom — the divisor PostHog picked.
|
|
const longScale = float64(0xfffffffffffffff)
|
|
|
|
// hashOf takes prefix "{flag_key}." (the trailing dot is part of the wire
|
|
// contract) and salt "" for rollout gating or "variant" for variant selection.
|
|
func hashOf(prefix, identifier, salt string) float64 {
|
|
sum := sha1.Sum([]byte(prefix + identifier + salt))
|
|
return float64(binary.BigEndian.Uint64(sum[:8])>>4) / longScale
|
|
}
|