From 463ef9d4a80e9824438ca8f45965619bb1a1f4ea Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 10 Mar 2026 14:13:36 +0000 Subject: [PATCH 1/5] feat: add PostHog identify and alias after CLI login to merge anonymous events Co-Authored-By: arsh --- packages/cmd/login.go | 4 ++++ packages/telemetry/telemetry.go | 29 +++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/packages/cmd/login.go b/packages/cmd/login.go index 863abf95..1f3e9de4 100644 --- a/packages/cmd/login.go +++ b/packages/cmd/login.go @@ -241,6 +241,10 @@ var loginCmd = &cobra.Command{ util.HandleError(err, "Unable to write write to Infisical Config file. Please try again") } + // Identify the user in PostHog and alias the anonymous machine ID + // so that pre-login CLI events are merged into the same person record + Telemetry.IdentifyUser(userCredentialsToBeStored.Email) + // clear backed up secrets from prev account util.DeleteBackupSecrets() diff --git a/packages/telemetry/telemetry.go b/packages/telemetry/telemetry.go index ffd74345..fbfea72e 100644 --- a/packages/telemetry/telemetry.go +++ b/packages/telemetry/telemetry.go @@ -56,6 +56,35 @@ func (t *Telemetry) CaptureEvent(eventName string, properties posthog.Properties } } +// IdentifyUser sends a PostHog identify call to enrich the person record +// with user properties, and aliases the anonymous machine ID to the user's +// email so that pre-login CLI events are merged into the same person. +func (t *Telemetry) IdentifyUser(email string) { + if !t.isEnabled || email == "" { + return + } + + // Identify the user with their email as the distinctId + t.posthogClient.Enqueue(posthog.Identify{ + DistinctId: email, + Properties: posthog.NewProperties(). + Set("email", email), + }) + + // Alias the anonymous machine ID to the user's email so that + // any events captured before login are linked to this person + machineId, err := machineid.ID() + if err == nil && machineId != "" { + anonymousId := "anonymous_cli_" + machineId + t.posthogClient.Enqueue(posthog.Alias{ + DistinctId: email, + Alias: anonymousId, + }) + } + + defer t.posthogClient.Close() +} + func (t *Telemetry) GetDistinctId() (string, error) { var distinctId string var outputErr error From a4285e6ef7012f7cfdd9a4c6b9fa2f699ac86801 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 10 Mar 2026 14:15:05 +0000 Subject: [PATCH 2/5] fix: remove Close() from IdentifyUser to avoid closing client before login event capture Co-Authored-By: arsh --- packages/telemetry/telemetry.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/telemetry/telemetry.go b/packages/telemetry/telemetry.go index fbfea72e..fc93eb6c 100644 --- a/packages/telemetry/telemetry.go +++ b/packages/telemetry/telemetry.go @@ -82,7 +82,8 @@ func (t *Telemetry) IdentifyUser(email string) { }) } - defer t.posthogClient.Close() + // Note: no Close() here — the subsequent CaptureEvent call in the login + // flow will flush and close the client after enqueuing the login event. } func (t *Telemetry) GetDistinctId() (string, error) { From 398abdc1ec94521b90f0b953d6e281c7a8b15261 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 10 Mar 2026 15:28:47 +0000 Subject: [PATCH 3/5] fix: move CaptureEvent before --plain early return to ensure Identify/Alias events are flushed Co-Authored-By: arsh --- packages/cmd/login.go | 7 +++++-- packages/telemetry/telemetry.go | 5 +++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/cmd/login.go b/packages/cmd/login.go index 1f3e9de4..dea36b46 100644 --- a/packages/cmd/login.go +++ b/packages/cmd/login.go @@ -248,6 +248,11 @@ var loginCmd = &cobra.Command{ // clear backed up secrets from prev account util.DeleteBackupSecrets() + // Capture login event and flush the PostHog client (including Identify/Alias + // events enqueued by IdentifyUser above). This must run before any early + // returns so that all enqueued events are flushed via CaptureEvent's Close(). + Telemetry.CaptureEvent("cli-command:login", posthog.NewProperties().Set("infisical-backend", config.INFISICAL_URL).Set("version", util.CLI_VERSION)) + if plainOutput { util.PrintlnStdout(userCredentialsToBeStored.JTWToken) return @@ -264,8 +269,6 @@ var loginCmd = &cobra.Command{ plainBold.Println("\nQuick links") util.PrintlnStderr("- Learn to inject secrets into your application at https://infisical.com/docs/cli/usage") util.PrintlnStderr("- Stuck? Join our slack for quick support https://infisical.com/slack") - - Telemetry.CaptureEvent("cli-command:login", posthog.NewProperties().Set("infisical-backend", config.INFISICAL_URL).Set("version", util.CLI_VERSION)) } else { sdkAuthenticator := util.NewSdkAuthenticator(infisicalClient, cmd) diff --git a/packages/telemetry/telemetry.go b/packages/telemetry/telemetry.go index fc93eb6c..0cfbd35b 100644 --- a/packages/telemetry/telemetry.go +++ b/packages/telemetry/telemetry.go @@ -82,8 +82,9 @@ func (t *Telemetry) IdentifyUser(email string) { }) } - // Note: no Close() here — the subsequent CaptureEvent call in the login - // flow will flush and close the client after enqueuing the login event. + // Note: no Close() here — the caller is responsible for ensuring + // CaptureEvent (which calls Close) runs after IdentifyUser to flush + // all enqueued events (Identify, Alias, and Capture). } func (t *Telemetry) GetDistinctId() (string, error) { From 65ced6c4a36d32bd37438c5d1b90ab1f6baf543a Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 10 Mar 2026 15:35:24 +0000 Subject: [PATCH 4/5] fix: GetDistinctId only returns error when no distinctId can be 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 --- packages/telemetry/telemetry.go | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/packages/telemetry/telemetry.go b/packages/telemetry/telemetry.go index 0cfbd35b..c01b9ef7 100644 --- a/packages/telemetry/telemetry.go +++ b/packages/telemetry/telemetry.go @@ -1,6 +1,8 @@ package telemetry import ( + "fmt" + "github.com/Infisical/infisical-merge/packages/util" "github.com/denisbrodbeck/machineid" "github.com/posthog/posthog-go" @@ -89,25 +91,29 @@ func (t *Telemetry) IdentifyUser(email string) { func (t *Telemetry) GetDistinctId() (string, error) { var distinctId string - var outputErr error machineId, err := machineid.ID() if err != nil { - outputErr = err + log.Debug().Err(err).Msg("failed to get machine ID for telemetry") } infisicalConfig, err := util.GetConfigFile() if err != nil { - outputErr = err + log.Debug().Err(err).Msg("failed to get config file for telemetry") } if infisicalConfig.LoggedInUserEmail != "" { distinctId = infisicalConfig.LoggedInUserEmail } else if machineId != "" { distinctId = "anonymous_cli_" + machineId - } else { - distinctId = "" } - return distinctId, outputErr + // Only return an error if we could not resolve any distinctId. + // Non-critical errors (e.g. machineid failure when email is available) + // are logged above but should not prevent event capture. + if distinctId == "" { + return "", fmt.Errorf("unable to resolve a distinct ID for telemetry") + } + + return distinctId, nil } From a75fb7ebdc88eee384a8e42dd1eb4a60d513a8b7 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 10 Mar 2026 15:45:48 +0000 Subject: [PATCH 5/5] style: use errors.New instead of fmt.Errorf for static error string Co-Authored-By: arsh --- packages/telemetry/telemetry.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/telemetry/telemetry.go b/packages/telemetry/telemetry.go index c01b9ef7..d40f5437 100644 --- a/packages/telemetry/telemetry.go +++ b/packages/telemetry/telemetry.go @@ -1,7 +1,7 @@ package telemetry import ( - "fmt" + "errors" "github.com/Infisical/infisical-merge/packages/util" "github.com/denisbrodbeck/machineid" @@ -112,7 +112,7 @@ func (t *Telemetry) GetDistinctId() (string, error) { // Non-critical errors (e.g. machineid failure when email is available) // are logged above but should not prevent event capture. if distinctId == "" { - return "", fmt.Errorf("unable to resolve a distinct ID for telemetry") + return "", errors.New("unable to resolve a distinct ID for telemetry") } return distinctId, nil