Skip to content

feat: add PostHog identify and alias after CLI login to merge anonymous events#146

Open
devin-ai-integration[bot] wants to merge 5 commits intomainfrom
devin/1773151928-posthog-identify-alias-on-login
Open

feat: add PostHog identify and alias after CLI login to merge anonymous events#146
devin-ai-integration[bot] wants to merge 5 commits intomainfrom
devin/1773151928-posthog-identify-alias-on-login

Conversation

@devin-ai-integration
Copy link
Contributor

@devin-ai-integration devin-ai-integration bot commented Mar 10, 2026

Description 📣

Before this change, the CLI uses an anonymous machine-based distinctId (anonymous_cli_ + machineId) for PostHog events captured before login. After login, it switches to the user's email. However, PostHog treats these as two separate persons, so pre-login CLI events are never linked to the authenticated user.

This PR adds an IdentifyUser method to the Telemetry struct that:

  1. Sends a posthog.Identify call with the user's email as the distinctId, enriching the person record with properties
  2. Sends a posthog.Alias call to link the anonymous machine ID to the user's email, merging pre-login events into the same person

Called after successful user login (after credentials are written to config).

Additionally, this PR fixes a pre-existing bug in GetDistinctId where a machineid.ID() error would cause CaptureEvent to silently early-return even when a valid email-based distinctId was available from the config file. Now GetDistinctId only returns an error when no distinctId can be resolved at all.

Companion PR: Infisical/infisical#5643 adds server-side identifyUser in the backend auth hook with Redis dedup.

Updates since last revision

  • Removed defer t.posthogClient.Close() from IdentifyUser: The initial version closed the PostHog client inside IdentifyUser, which would prevent the subsequent CaptureEvent("cli-command:login") from enqueuing. The Identify and Alias messages are now enqueued without closing — the CaptureEvent call that follows in the login flow handles flushing and closing the client.
  • Moved CaptureEvent before --plain early return: CaptureEvent("cli-command:login", ...) now runs before the plainOutput check so that Identify, Alias, and Capture events are all flushed via CaptureEvent's defer Close() regardless of the --plain flag.
  • Fixed GetDistinctId error propagation: Previously returned errors from machineid.ID() even when a valid email was available, which caused CaptureEvent to bail out and silently drop all enqueued events. Now non-critical errors are logged at debug level and only an empty distinctId produces an error.
  • Style: Replaced fmt.Errorf with errors.New for static error string (no format verbs).

Human review checklist

  • No early return between IdentifyUser and CaptureEvent: In login.go, verify there is no code path that returns between Telemetry.IdentifyUser(...) and Telemetry.CaptureEvent(...). Since IdentifyUser deliberately does not call Close(), any return before CaptureEvent would silently drop the Identify/Alias events. The posthog-go client's Close() panics (recovered) on double-call, so it must only be called once.
  • GetDistinctId behavior change is broader than login: The refactored GetDistinctId now returns nil error whenever a valid distinctId is resolved, which affects ALL CaptureEvent callers (not just login). Previously a machineid.ID() failure would suppress events even when the user was logged in. This is a strict improvement, but verify there are no callers that depend on the old error-returning behavior.
  • PostHog Alias is a one-time operation: If a user logs in with different accounts on the same machine, only the first alias is effective. Subsequent alias calls to link the same anonymous ID to a different email will silently fail. This is expected PostHog behavior but worth noting.
  • Only user login is covered: The IdentifyUser call is inside the loginMethod == "user" block, so machine identity logins do not trigger identify/alias. This is intentional per the PR scope.

Type ✨

  • Bug fix
  • New feature
  • Improvement
  • Breaking change
  • Documentation

Tests 🛠️

Manual testing required — this change affects PostHog telemetry, which requires a Cloud instance with a PostHog project.

# 1. Build and run CLI locally
go build -o infisical

# 2. Before login, capture a test event (e.g. `infisical init` or any command)
#    This will generate an anonymous_cli_<machineId> person in PostHog

# 3. Log in via CLI
./infisical login

# 4. Check PostHog:
#    - The person should have email property set
#    - The anonymous_cli_<machineId> person should be merged/aliased to the email person
#    - Pre-login events should now appear under the email person

# 5. Test --plain flag to verify events are still flushed
./infisical login --plain

# 6. Test a CLI command after login (e.g. `infisical secrets`) to verify GetDistinctId
#    fix works - events should be captured even if machineid.ID() fails

No automated tests added — telemetry logic is difficult to test without mocking the PostHog client.


Link to Devin Session: https://app.devin.ai/sessions/6cf49960d59a4f7b974a7fe4861d6206
Requested by: @0xArshdeep


Open with Devin

…us events

Co-Authored-By: arsh <arshsb1998@gmail.com>
@devin-ai-integration
Copy link
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

…login event capture

Co-Authored-By: arsh <arshsb1998@gmail.com>
@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 10, 2026

Greptile Summary

This PR adds a PostHog Identify + Alias call after user login so that pre-login anonymous CLI events (keyed by anonymous_cli_<machineId>) are merged into the authenticated user's person record. It also improves GetDistinctId to stop propagating non-fatal errors when a usable distinct ID has already been resolved, which previously caused CaptureEvent to silently abort even when a valid ID was available.

Key changes:

  • New IdentifyUser(email string) method in telemetry.go enqueues posthog.Identify (enriches the person record) and posthog.Alias (links anonymous_cli_<machineId> → email).
  • In login.go, CaptureEvent is moved before the --plain early return to guarantee all three enqueued events (Identify, Alias, Capture) are flushed by CaptureEvent's deferred Close() — this prevents the Identify/Alias events from being silently dropped.
  • GetDistinctId now returns an error only when no distinct ID can be resolved, rather than surfacing intermediate errors (e.g. a config-file read failure) when the machine ID fallback is still available.
  • Minor: fmt.Errorf with no format verbs in GetDistinctId could be replaced with errors.New.

Confidence Score: 4/5

  • This PR is safe to merge; changes are isolated to telemetry and do not affect core CLI functionality.
  • The logic is sound: IdentifyUser correctly enqueues Identify + Alias without closing the client, and the repositioned CaptureEvent ensures all enqueued events are flushed before any early return. The GetDistinctId refactor is a strict improvement. One minor style issue (fmt.Errorf vs errors.New) prevents a perfect score.
  • No files require special attention beyond the minor fmt.Errorf nit in packages/telemetry/telemetry.go.

Important Files Changed

Filename Overview
packages/telemetry/telemetry.go Adds IdentifyUser method that enqueues PostHog Identify + Alias events, and refactors GetDistinctId to only return an error when no distinct ID can be resolved (previously returned intermediate errors even when a usable ID was available). Logic is correct; fmt.Errorf without format verbs is a minor nit.
packages/cmd/login.go Adds IdentifyUser call after credentials are written to config, and moves CaptureEvent before the --plain early return to ensure all three enqueued events (Identify, Alias, Capture) are flushed by CaptureEvent's deferred Close(). Logic is sound.

Last reviewed commit: 65ced6c

devin-ai-integration[bot]

This comment was marked as resolved.

…/Alias events are flushed

Co-Authored-By: arsh <arshsb1998@gmail.com>
devin-ai-integration[bot]

This comment was marked as resolved.

Previously, GetDistinctId returned errors from machineid.ID() even when
a valid email-based distinctId was available from the config file. This
caused CaptureEvent to early-return and never flush enqueued Identify/
Alias events. Now non-critical errors are logged at debug level and only
a missing distinctId produces an error.

Co-Authored-By: arsh <arshsb1998@gmail.com>
@0xArshdeep
Copy link

@greptileai

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant