The module declares go 1.27.1 and the adapter image follows. go fix
applied the modernizers and left ptrString and ptrBool with no callers,
so they are gone; omitzero is not applied, since it changes what the
JSON says.
Co-authored-by: Hanzo Dev <[email protected]>
Adds a Phase 1 implementation of the Server SDK Feature Flag
Evaluations RFC, mirroring posthog-js#3476 and posthog-python#539.
Client.EvaluateFlags returns a FeatureFlagEvaluations snapshot built
from at most one /flags request. The snapshot powers IsEnabled,
GetFlag, and GetFlagPayload checks, fires deduped $feature_flag_called
events with full v4 metadata (id, version, reason, request_id), and can
be attached to a Capture event via the new Capture.Flags field to
populate $feature/<key> and $active_feature_flags with no extra
network call.
The dedup logic for $feature_flag_called is extracted into
captureFlagCalledIfNeeded so the existing single-flag path and the new
snapshot path share the same per-distinct_id LRU cache and behave
identically.
OnlyAccessed and Only return filtered child snapshots with independent
access tracking, so filtering for a Capture does not back-propagate to
the parent. A new Config.FeatureFlagsLogWarnings option silences their
warnings for callers that prefer quieter helpers.
Capture.SendFeatureFlags is unchanged and not deprecated; Phase 2 will
follow up with deprecations and migration guidance.
Task-Id: b9d98122-fe61-462a-bfb3-6e1be3a8966a
* add semver targeting to local evaluation
* chore: bump version to 1.11.0 [version bump]
* address PR feedback from Copilot
- Change "person property value" to "property value" in error messages
since matchProperty is used for both person and group properties
- Validate core version format before stripping pre-release/build suffixes
to reject malformed inputs like "1-2.3" or "-alpha"
- Return error when a version component is present but empty (e.g., "1..3"
or "1.") instead of silently defaulting to 0
- Add tests for newly rejected malformed inputs
* fix: run go fmt
Rename Go package from posthog to insights across all 80+ files.
Consumers now import as `insights "github.com/hanzoai/insights-go"`
and use insights.Client, insights.Config, insights.Capture, etc.
- Rename posthog.go -> insights.go, posthog_test.go -> insights_test.go
- Update package declaration from posthog to insights in all .go files
- Update SDKName from posthog-go to insights-go
- Update DefaultEndpoint from app.posthog.com to insights.hanzo.ai
- Update all FieldError/ConfigError type strings
- Update User-Agent headers, log prefixes, comments, and docs
- Update JSON test fixtures to match new SDK name
- Update CLI, examples, extras, and sdk_compliance_adapter
- Fix pre-existing go vet warnings (non-constant format strings)
Add support for passing device_id to the PostHog /flags endpoint.
This allows clients to identify devices separately from users when
evaluating feature flags.
- Add DeviceId field to FlagsRequestData (omitted when nil)
- Add DeviceId to FeatureFlagPayload and FeatureFlagPayloadNoKey
- Thread device_id through all flag evaluation paths
- Add tests verifying device_id is passed in request payload
* refactor: Rename `FeatureFlagResult` -> `featureFlagEvaluationResult`
It's an internal type used to track request metadata to be reported via
$feature_flag_called. This shouldn't be conflated with the
`getFeatureFlagResult()` API.
* feat: Add `GetFeatureFlagResult` method to client
This method returns an object indicating if the flag is enabled, what
the variant is, and a method for parsing the payload as a given type. It
emits a 'feature flag called' event by default, and will become
the canonical method of fetching a feature flag
* fix: Always return nil if a flag doesn't exist
* doc: Update CHANGELOG
* intitial refactor:
- Add Context-based graceful shutdown and drain phase for enqueued requests
- Use buffered channels vs. mutex for concurrency mgmt.
- Use atomic.Pointer vs. locks for FF mgmt
- Use Callback ifc vs. lossy internal tracking for unit test result validation
- Add new APIs mirroring current that accept caller's Context to control
operation timeouts
* expose batch submission timeout with default equiv to old hardcoded timeout
* restructure event submission and batching pipeline
* overhaul tests; implement new stress tests and address coverage gaps
* parallelize tests where safe to do so (3x CI time savings!)
* introduce faster pure Go JSON seralization library
* more refactors: unit tests & benchmarks
* DRY up shutdown req drain and prepareForSend serializer
* use go-json in CLI utils too; remove redundant helpers
* move error type to proper pkg
* DRY up loop() and fix deadlock in old test case
* normalize CI and Makefile test and benchmark target behavviors
* more realistic feature flags benchmark
* replace leaky time.After pattern with idiomatic safer Timers
* internal queue size minimums decoupled from batch and worker configs
* remove dead comments
* fix test bug
* update default http.Transport cfg to avoid conn pool as bottleneck; update batch enqueue limiting strategy and tests/benches
* adjust stress test defaults to match prod
* update flaky tests and rm stale file
* make graceful shutdown timeout opt-in for now
* fix tests that reverted to hold helper utils during rebase
* fix flaky test due to overhead from -race detector now default in test flags
* add more graceful shutdown test cases
* Add ETag support for local evaluation polling
Use conditional requests with If-None-Match header to reduce bandwidth
when polling for feature flag definitions. The server returns 304 Not
Modified when flags haven't changed, avoiding unnecessary data transfer.
- Store ETag from server responses
- Send If-None-Match header on subsequent requests
- Handle 304 Not Modified by keeping cached flags
- Clear ETag on quota limit (402) or when server stops sending it
* Fix response body leak on 304/402 responses
Move defer res.Body.Close() immediately after the error check to ensure
the response body is closed for all status codes, not just 200 OK.
Previously, early returns for 304 Not Modified and 402 Payment Required
would skip the defer, causing connection pool leaks over time.
* Replace time.Sleep with deterministic synchronization in tests
- Use GetFeatureFlags() which blocks until initial fetch completes
- Use waitForCondition() helper with polling for ForceReload operations
- Use atomic.Value instead of mutex for thread-safe string storage
- Tests are now faster (0.06s vs 1.22s) and more reliable
* Add test for 304 response without ETag header
Verify that the stored ETag is preserved when the server returns a 304
Not Modified response without including an ETag header. This is an edge
case but ensures defensive behavior.
* Update comment per code review
* Add ETag polling test example
Adds an interactive example to demonstrate ETag support for local
evaluation polling. The example polls every 5 seconds and logs
the If-None-Match header sent and the response status (200 vs 304)
to show when cached flags are used.
* Test FeatureFlagsPoller directly
Per review feedback, this makes the tests simpler.
* Fix static cohort evaluation in multi-condition flags
When a feature flag has multiple conditions and one contains a static
cohort (not available locally), the SDK now correctly falls back to the
API for the entire flag evaluation instead of skipping the cohort
condition and evaluating the next condition locally.
Previously, if condition 1 had a static cohort and condition 2 matched
locally, the SDK would incorrectly return the variant from condition 2,
even though the user might actually be in the static cohort.
Changes:
- Add RequiresServerEvaluationError type to distinguish missing server
data from evaluation errors
- Update matchCohort() to return RequiresServerEvaluationError when
cohort not found locally
- Update error handling in matchPropertyGroup() to immediately propagate
RequiresServerEvaluationError
- Update matchFeatureFlagProperties() to trigger API fallback on
RequiresServerEvaluationError
- Add test case verifying correct API fallback behavior
This ensures the correct variant is returned by consulting the server
when static cohorts are involved.
Follows same pattern as Node.js SDK fix: PostHog/posthog-js#2465
* Add test for GetFeatureFlagPayload with static cohorts
Adds TestGetFeatureFlagPayloadFallbackToAPIWhenFlagHasStaticCohort
to verify that GetFeatureFlagPayload correctly falls back to the API
when a flag contains a static cohort.
This test ensures the fix in featureflags.go:418-436 works correctly
for GetFeatureFlagPayload, matching the behavior of GetFeatureFlag.
When local evaluation fails due to a missing cohort (indicating a
static cohort), the SDK should fall back to the API and return the
API payload rather than failing or returning incorrect data.
* Move URL parsing errors to init, validate endpoint when creating client
* Proper logger
* fix issues with timeout test
* do not waste go routines (fix bug with infinite goroutines / http requests)
* minor fixes to errors
* simplify the hash calculation
* Fixes the feature flag local eval
* fix the hash when the group name is used for computation
* bump version & add changelog item for 1.5.5
Co-authored-by: Phil Haack <[email protected]>
* Consolidate decide code
There were two implementations of decide. That's not the end of the world, but I'm about to implement a new decide format (`/decide?v=4`) and I don't want to have to do it in two places.
So this adds a new file `decide.go` with a new interface `decider` and an implementation `decideClient` that both `Client` and `FeatureFlagsPoller` uses.
* Remove redundant client
* Use `io.ReadAll`
`ioutil.ReadAll` is deprecated.
* Pass the already created decider into the poller
* Move decide types into `decide.go`
* Remove unused method
The words "true" and "false" are valid variant keys. It's incorrect to convert them to `true` and `false`. This could result in a case where `isEnabled` returns `false` when in fact, it's `true` but the variant selected is named "false".
Added a test to confirm this change.
* feat(flags): Add method for fetching decrypted remote config flag payload
* fix tests
* fix broken test
* Address comment around user agent header
* tweak
* bump minor version
* Use flag key instead of id
* Add json examples
* address comment
* Clean up method name
fix: fixes a nil panic when unable to fetch feature flags and err != nil which means res.Body will be nil at defer time because we aren't returning early enough from the method