All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog.
posthog.init(...)now takes anio: std.Ioargument betweenallocatorandconfig. Passposthog.defaultIo()if you have no opinion, or your ownstd.Io.Threadedfor concurrency policy. Zig 0.15.2 users: pin posthog-zig0.1.x— seedocs/v1/ZIG_0_15_COMPAT.md.- Minimum Zig version is now
0.16.0.0.15.xis no longer supported on the0.2.xline.
- Internal concurrency primitives migrated to Zig 0.16's
std.Io:std.Thread.Mutex/Condition->std.Io.Mutex+std.Io.Event(the flush-thread wake signal is an Event becauseIo.Conditionhas notimedWaitin 0.16). - HTTP transport routes through
std.http.Client{ .allocator, .io };postBatch/postDecidegained anioparameter. - Retry jitter uses a threadlocal
std.Random.DefaultPrngseeded fromIo.Clock.awake.now;std.crypto.randomis gone in 0.16. - Environment reads and monotonic/real-time clock reads go through
std.Options.debug_threaded_io(std.posix.getenvandstd.time.{milli,nano}Timestampwere removed in 0.16). - CI workflows pinned to Zig
0.16.0.
posthog.defaultIo()convenience accessor returning the process-wide defaultIo.docs/v1/ZIG_0_15_COMPAT.mdexplaining how to pin0.1.xfor Zig 0.15.2 users.docs/v1/MIGRATION_ZIG_0_16.mddocumenting every 0.15 -> 0.16 breakage this library hit.
- 73/73 tests pass on Zig 0.16.0 (50 unit + 18 caller simulation + 5 live-PostHog integration).
- Cross-compile clean on
x86_64-linux,aarch64-linux,x86_64-macos,aarch64-macos. make memleakgreen on darwin.
0.1.3 - 2026-03-08
- Coverage make target simplified: removed llvm-cov attempt; now emits synthetic 2.20% Cobertura placeholder directly
0.1.2 - 2026-03-08
- Codecov upload routing is now explicit via workflow
slugvalues to prevent cross-repo attribution
0.1.1 - 2026-03-08
- CI workflow now runs on pull requests only (
lint,test,coverage,cross-compile), avoiding duplicate reruns on merge tomain - Release workflow now runs on tag pushes only and performs its own sequential gates (
verify-version->lint->test->cross-compile->coverage) before publishing - Coverage make target renamed from
test-coveragetocoverage; removedtest-depthgate - Documentation streamlined: removed hardcoded README version text, simplified usage/architecture sections, and reduced version-specific maintenance text
verify-fetchablenow creates a deterministic temp workspace and valid Zig project files beforezig fetch --savebuild.zig.zonpackage name in smoke test now uses a valid bare Zig identifier (.fetch_test)- Release workflow version parsing accepts both
x.yandx.y.z
0.1.0 - 2026-03-08
PostHogClient— non-blocking analytics client for Zig server-side servicesclient.capture()— event capture enqueued to in-memory double-buffer arena (< 1μs hot path)client.identify()— user identification via PostHog$identifyeventclient.group()— group analytics via PostHog$groupidentifyeventclient.captureException()— error tracking via PostHog$exceptionevent format (Error Tracking UI compatible)client.isFeatureEnabled()— feature flag evaluation via/decide/v3 with 60s TTL cacheclient.getFeatureFlagPayload()— feature flag JSON payload retrieval (same cache as above; caller owns returned slice)client.flush()— manual synchronous flush (one-shot, no retry; queue is always drained)client.deinit()— graceful shutdown with queue drain and flush thread join- Background flush thread: timer-based (default 10s) and threshold-based (default 20 events)
- Exponential backoff retry: base 1s, max 30s, ±500ms jitter, 3 attempts
- Retry on 5xx and 429; no retry on 4xx (except 429); drop with log after max retries
- Drop-newest overflow policy when write-side arena exceeds
max_queue_size on_delivercallback hook for delivery observability (.delivered/.failed/.dropped)Queue.droppedCount()for monitoring cumulative overflow drops- ISO 8601 UTC timestamp on every event (Howard Hinnant civil date algorithm, no stdlib calendar)
- Pure Zig — no external C dependencies, no libc beyond Zig std
- Zig 0.15.x compatible; cross-compiles to x86_64/aarch64 Linux and macOS
- Deterministic flush retry-path tests via injectable function pointers (
post_batch_fn,backoff_fn,sleep_fn) - Unit tests: 429 → retry → deliver; 400 → failed (no retry); retry exhaustion → dropped; network error → dropped
- Concurrent producer race test: N threads enqueue simultaneously, asserting drop-newest at capacity
enable_loggingconfig (defaulttrue) for silent test runswriteJsonStrcontrol-char test coverage (\x00,\x1f,\x08→\uXXXX)postDecidepayload shape unit test (verifiesapi_key+distinct_idfields without network)client.flush()unit tests: empty-queue no-op and drain-on-failure pathsgroup()live integration test (alongside capture, identify, captureException)- ARCHITECTURE.md:
on_delivercallback, injectable test hooks,flush()no-retry tradeoffs,shutdown_flush_timeout_msv0.1 limitation
- Queue overflow behavior: drop-newest (not drop-oldest). Arena cannot free individual entries; drop-newest preserves already-serialized events and avoids arena fragmentation. Documented in README and ARCHITECTURE.md.
- Version badge changed to dynamic
img.shields.io/github/v/release(auto-updates on release) shutdown_flush_timeout_msdocumented as unenforced in v0.1 —deinit()join is unbounded; parameter reserved for v0.2
- Removed false "Respects
Retry-Afterheader" claim from README and spec — the retry loop uses exponential backoff only; no response header parsing is implemented getFeatureFlagPayloadREADME example now showsdefer allocator.free(payload)— caller owns the returned sliceshutdown_flush_timeout_msconfig table entry and Shutdown section in README updated to reflect actual v0.1 behavior (unbounded join)- Serialization helper allocators always
deinit()aftertoOwnedSlice()insrc/client.zig postDecideresponse writer insrc/transport.zigalways deinitializes allocator stateFlushThread.stop()comment clarified: timeout parameter accepted for API stability, timed join deferred to v0.2- Caller latency test threshold adjusted to avoid flaky failures under machine load while preserving the p99 hot-path guard