feat: Link user console profiles to analytics - #486
Conversation
There was a problem hiding this comment.
🟡 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/sdkand adjust API usage accordingly (e.g.,DeleteInstanceByUUIDcall signature). - Persist
UserUUID/OrganizationUUIDin the CLI profile duringlogin, and initialize telemetry identity from the current profile. - Attach PostHog
Groupsto 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.
7c78830 to
2cad691
Compare
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>
2cad691 to
bc71710
Compare
| @@ -104,7 +104,7 @@ Global flags: | |||
| --profile=<name> ($UNIKRAFT_PROFILE) | |||
| profile.UserUUID = userUUID | ||
| profile.OrganizationUUID = orgUUID |
There was a problem hiding this comment.
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() |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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"` | |||
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
nit: getOrg doesn't get an org any more. getAuthorization?
There was a problem hiding this comment.
Sure, I guess, I kept them cause they were smol 😳
|
Also: follow-up. We should add We might need to run some process to batch import all the existing users/orgs to posthog? |
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