Skip to content

feat: Link user console profiles to analytics - #486

Open
craciunoiuc wants to merge 3 commits into
prod-stagingfrom
craciunoiuc/posthog-user-integration
Open

feat: Link user console profiles to analytics#486
craciunoiuc wants to merge 3 commits into
prod-stagingfrom
craciunoiuc/posthog-user-integration

Conversation

@craciunoiuc

@craciunoiuc craciunoiuc commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

First bumps the sdk.

Links user accounts to the data they were previously sending anonymously.

If a user does not have an uuid saved, it will use the unique machine id instead for tracking.

This needs merging and redeployment to stable first:
Depends-on: https://github.com/unikraft-cloud/console/pull/1150

Closes: TOOL-1131

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

There are correctness issues that can break builds/behavior (notably telemetry payload type incompatibility and a login edge case that can skip organization resolution when the org name is missing).

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR updates the CLI telemetry pipeline to associate PostHog analytics events with a logged-in user/organization when available (falling back to an anonymous machine fingerprint otherwise), by persisting user/org UUIDs on login and attaching them to emitted events.

Changes:

  • Bump unikraft.com/cloud/sdk and adjust API usage accordingly (e.g., DeleteInstanceByUUID call signature).
  • Persist UserUUID / OrganizationUUID in the CLI profile during login, and initialize telemetry identity from the current profile.
  • Attach PostHog Groups to emitted telemetry events; update help/golden outputs and add telemetry unit tests.
File summaries
File Description
internal/volimport/volimport.go Update DeleteInstanceByUUID calls to match the updated SDK signature.
internal/telemetry/track.go Include PostHog Groups when emitting command/crash events.
internal/telemetry/telemetry.go Change telemetry initialization to accept a profile and derive distinct_id/groups; include groups in SendEvent.
internal/telemetry/telemetry_test.go Add unit coverage for identity/group propagation in telemetry payloads.
internal/telemetry/detach.go Change detached payload marshaling to use the local EventPayload (now including groups).
internal/config/profile.go Extend profile schema with user_uuid and organization_uuid.
internal/cmd/root.go Update global telemetry flag help text to remove “anonymous” wording.
internal/cmd/login/login.go Capture user/org UUIDs from auth response; refactor org lookup to return authorization data.
go.mod Bump unikraft.com/cloud/sdk version.
go.sum Update sums for the bumped SDK version.
cmd/unikraft/testdata/TestHelp/volumes Update golden help output for telemetry flag help text.
cmd/unikraft/testdata/TestHelp/services Update golden help output for telemetry flag help text.
cmd/unikraft/testdata/TestHelp/run Update golden help output for telemetry flag help text.
cmd/unikraft/testdata/TestHelp/resources Update golden help output for telemetry flag help text.
cmd/unikraft/testdata/TestHelp/instances Update golden help output for telemetry flag help text.
cmd/unikraft/testdata/TestHelp/images Update golden help output for telemetry flag help text.
cmd/unikraft/testdata/TestHelp/general Update golden help output for telemetry flag help text.
cmd/unikraft/testdata/TestHelp/config Update golden output for new profile fields and telemetry help text.
cmd/unikraft/testdata/TestHelp/certificates Update golden help output for telemetry flag help text.
cmd/unikraft/testdata/TestHelp/build Update golden help output for telemetry flag help text.
cmd/unikraft/testdata/TestHelp/auth Update golden help output for telemetry flag help text.
cmd/unikraft/testdata/TestHelp/api Update golden help output for telemetry flag help text.
cmd/unikraft/main.go Initialize telemetry using the current profile and update messaging/comments to reflect non-anonymous identity when logged in.
Review details
  • Files reviewed: 22/23 changed files
  • Comments generated: 3
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread internal/telemetry/detach.go
Comment thread internal/telemetry/telemetry.go
Comment thread internal/cmd/login/login.go Outdated
@craciunoiuc
craciunoiuc force-pushed the craciunoiuc/posthog-user-integration branch from 7c78830 to 2cad691 Compare September 3, 2026 07:59
@craciunoiuc
craciunoiuc marked this pull request as ready for review September 3, 2026 08:17
Profiles only store the organization name, which is mutable and is
not the identity the console uses when it reports PostHog events.

Add user_uuid and organization_uuid to the profile so a login can
record the same identifiers the console reports and telemetry can
read them back later.

Signed-off-by: Cezar Craciunoiu <cezar@unikraft.io>
The login flow only kept the organization name, and the token path
never asked the control plane for organization details when the
name was given on the command line.

Record the user and organization UUIDs from the login check, and
query the control plane for the organization UUID whenever it is
still missing. A failed query is only fatal when the name is also
unknown, which keeps the previous behaviour.

Signed-off-by: Cezar Craciunoiu <cezar@unikraft.io>
Every CLI event used a machine fingerprint hash as its distinct ID,
so PostHog could not connect CLI usage to the person and company the
console already reports.

Use the profile user UUID as the distinct ID when it is known and add
an organization group from the profile org UUID. Groups travel in
their own payload field to the detached sender, so they do not depend
on the client folding them into the event properties.

Signed-off-by: Cezar Craciunoiu <cezar@unikraft.io>
@craciunoiuc
craciunoiuc force-pushed the craciunoiuc/posthog-user-integration branch from 2cad691 to bc71710 Compare September 4, 2026 10:48
@@ -104,7 +104,7 @@ Global flags:
--profile=<name> ($UNIKRAFT_PROFILE)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

task golden-update 🙏

Comment on lines +133 to +134
profile.UserUUID = userUUID
profile.OrganizationUUID = orgUUID

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bug: --token logins never set userUUID — only the browser path does, and getOrg can't help because GetAuthorizationResponseData has no user_uuid on it. So findOrCreateProfile finds an existing profile and this wipes a UUID we'd already stored.


// Generate anonymous distinct ID from machine fingerprint
// Use the user UUID when known, otherwise the machine fingerprint.
distinctID = generateDistinctID()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Can we set $process_person_profile: false when we fall back to the fingerprint? Right now PostHog builds a Person per machine, which is the one thing we don't want persisted — and on a shared box or CI runner that Person is several humans.

Note

Maybe we should wipe all the old CLI people?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess we discuss the wiping part on discord

@@ -55,11 +58,13 @@ type EventPayload struct {
DistinctID string `json:"distinct_id"`
SessionID string `json:"session_id"`

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ai nit: SessionID is dead — detach.go doesn't populate it and SendEvent doesn't read it, the real one rides in Properties. Every payload just carries a top-level "session_id": "".

Timestamp is the same story: no Track* func sets it, so posthog-go backfills it in the subprocess.

}

func (cmd *LoginCmd) getOrg(ctx context.Context, profile *config.Profile) (string, error) {
func (cmd *LoginCmd) getOrg(ctx context.Context, profile *config.Profile) (*controlplane.GetAuthorizationResponseData, error) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: getOrg doesn't get an org any more. getAuthorization?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO, these tests don't add much?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I guess, I kept them cause they were smol 😳

@jedevc

jedevc commented Sep 7, 2026

Copy link
Copy Markdown
Member

Also: follow-up. We should add identify calls to the console backend when users+orgs are created/updated. This way, we make sure that all users get this info, and isn't reliant on the frontend to do it (e.g. users might deny/block these scripts).

We might need to run some process to batch import all the existing users/orgs to posthog?

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.

3 participants