mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-05-05 23:36:37 +00:00
privacy(telemetry): reduce commercial detail
This commit is contained in:
parent
59bf0c9cee
commit
be5982dcae
6 changed files with 79 additions and 25 deletions
|
|
@ -37,8 +37,8 @@ Every field is listed below — nothing else leaves your server:
|
|||
| Relay enabled | `true`/`false` | Whether remote access is enabled |
|
||||
| SSO enabled | `true`/`false` | Whether OIDC/SSO is configured |
|
||||
| Multi-tenant | `true`/`false` | Whether multi-tenant mode is on |
|
||||
| License tier | `free`, `pro`, etc. | Current license tier |
|
||||
| API tokens | `3` | Number of API tokens configured |
|
||||
| Paid license | `true`/`false` | Whether a paid license is active |
|
||||
| Has API tokens | `true`/`false` | Whether any API tokens are configured |
|
||||
|
||||
### What is NOT sent
|
||||
|
||||
|
|
|
|||
|
|
@ -125,6 +125,12 @@ owner for privacy-document URLs, while `frontend-modern/public/docs/PRIVACY.md`
|
|||
is the version-matched asset served by the running build. Privacy disclosures
|
||||
must not drift back to GitHub `main` links that can describe a different
|
||||
revision than the installed runtime.
|
||||
That same disclosure boundary now also fixes the telemetry payload floor:
|
||||
commercial and auth-adjacent telemetry may report only coarse posture signals
|
||||
such as whether a paid license is active or whether any API tokens exist.
|
||||
Exact license tiers and exact API-token counts are not part of the canonical
|
||||
anonymous telemetry contract and may not be reintroduced without updating this
|
||||
trust boundary and the governed privacy disclosure together.
|
||||
That same rule also applies inside shipped security guidance itself:
|
||||
`SECURITY.md` and the synced `frontend-modern/public/docs/SECURITY.md` copy may
|
||||
not bounce the operator back to GitHub `main` for section references that the
|
||||
|
|
|
|||
|
|
@ -37,8 +37,8 @@ Every field is listed below — nothing else leaves your server:
|
|||
| Relay enabled | `true`/`false` | Whether remote access is enabled |
|
||||
| SSO enabled | `true`/`false` | Whether OIDC/SSO is configured |
|
||||
| Multi-tenant | `true`/`false` | Whether multi-tenant mode is on |
|
||||
| License tier | `free`, `pro`, etc. | Current license tier |
|
||||
| API tokens | `3` | Number of API tokens configured |
|
||||
| Paid license | `true`/`false` | Whether a paid license is active |
|
||||
| Has API tokens | `true`/`false` | Whether any API tokens are configured |
|
||||
|
||||
### What is NOT sent
|
||||
|
||||
|
|
|
|||
|
|
@ -23,8 +23,8 @@
|
|||
// - Whether relay/remote access is enabled
|
||||
// - Whether SSO/OIDC is configured
|
||||
// - Whether multi-tenant mode is enabled
|
||||
// - License tier (free/pro/etc.)
|
||||
// - Number of API tokens configured
|
||||
// - Whether a paid license is active
|
||||
// - Whether any API tokens are configured
|
||||
//
|
||||
// # What is NOT sent
|
||||
//
|
||||
|
|
@ -101,13 +101,13 @@ type Ping struct {
|
|||
KubernetesClusters int `json:"kubernetes_clusters"`
|
||||
|
||||
// Feature usage (booleans and counts — no content)
|
||||
AIEnabled bool `json:"ai_enabled"`
|
||||
ActiveAlerts int `json:"active_alerts"`
|
||||
RelayEnabled bool `json:"relay_enabled"`
|
||||
SSOEnabled bool `json:"sso_enabled"`
|
||||
MultiTenant bool `json:"multi_tenant"`
|
||||
LicenseTier string `json:"license_tier"` // "free", "pro", "pro_annual", "lifetime", etc.
|
||||
APITokens int `json:"api_tokens"`
|
||||
AIEnabled bool `json:"ai_enabled"`
|
||||
ActiveAlerts int `json:"active_alerts"`
|
||||
RelayEnabled bool `json:"relay_enabled"`
|
||||
SSOEnabled bool `json:"sso_enabled"`
|
||||
MultiTenant bool `json:"multi_tenant"`
|
||||
PaidLicense bool `json:"paid_license"`
|
||||
HasAPITokens bool `json:"has_api_tokens"`
|
||||
}
|
||||
|
||||
// Snapshot holds the dynamic state gathered at ping time.
|
||||
|
|
@ -126,8 +126,8 @@ type Snapshot struct {
|
|||
RelayEnabled bool
|
||||
SSOEnabled bool
|
||||
MultiTenant bool
|
||||
LicenseTier string
|
||||
APITokens int
|
||||
PaidLicense bool
|
||||
HasAPITokens bool
|
||||
}
|
||||
|
||||
// SnapshotFunc returns the current state snapshot for telemetry.
|
||||
|
|
@ -287,8 +287,8 @@ func applySnapshot(base Ping, fn SnapshotFunc) Ping {
|
|||
ping.RelayEnabled = s.RelayEnabled
|
||||
ping.SSOEnabled = s.SSOEnabled
|
||||
ping.MultiTenant = s.MultiTenant
|
||||
ping.LicenseTier = s.LicenseTier
|
||||
ping.APITokens = s.APITokens
|
||||
ping.PaidLicense = s.PaidLicense
|
||||
ping.HasAPITokens = s.HasAPITokens
|
||||
return ping
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -92,8 +92,9 @@ func TestApplySnapshot(t *testing.T) {
|
|||
VMs: 10,
|
||||
Containers: 5,
|
||||
AIEnabled: true,
|
||||
LicenseTier: "pro",
|
||||
ActiveAlerts: 2,
|
||||
PaidLicense: true,
|
||||
HasAPITokens: true,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -111,8 +112,11 @@ func TestApplySnapshot(t *testing.T) {
|
|||
if !ping.AIEnabled {
|
||||
t.Fatal("AIEnabled should be true")
|
||||
}
|
||||
if ping.LicenseTier != "pro" {
|
||||
t.Fatalf("LicenseTier = %q, want %q", ping.LicenseTier, "pro")
|
||||
if !ping.PaidLicense {
|
||||
t.Fatal("PaidLicense should be true")
|
||||
}
|
||||
if !ping.HasAPITokens {
|
||||
t.Fatal("HasAPITokens should be true")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -171,6 +175,51 @@ func TestSend_Success(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestSend_UsesReducedCommercialSignals(t *testing.T) {
|
||||
var rawBody []byte
|
||||
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
rawBody, _ = io.ReadAll(r.Body)
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
origEndpoint := pingEndpoint
|
||||
pingEndpoint = ts.URL
|
||||
defer func() { pingEndpoint = origEndpoint }()
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
send(ctx, Ping{
|
||||
InstallID: "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
|
||||
Version: "6.0.0",
|
||||
Event: "heartbeat",
|
||||
Platform: "binary",
|
||||
OS: "linux",
|
||||
Arch: "amd64",
|
||||
PaidLicense: true,
|
||||
HasAPITokens: true,
|
||||
})
|
||||
|
||||
var payload map[string]any
|
||||
if err := json.Unmarshal(rawBody, &payload); err != nil {
|
||||
t.Fatalf("Unmarshal: %v", err)
|
||||
}
|
||||
if _, ok := payload["license_tier"]; ok {
|
||||
t.Fatal("legacy license_tier field should not be sent")
|
||||
}
|
||||
if _, ok := payload["api_tokens"]; ok {
|
||||
t.Fatal("legacy api_tokens field should not be sent")
|
||||
}
|
||||
if got, ok := payload["paid_license"].(bool); !ok || !got {
|
||||
t.Fatalf("paid_license = %#v, want true", payload["paid_license"])
|
||||
}
|
||||
if got, ok := payload["has_api_tokens"].(bool); !ok || !got {
|
||||
t.Fatalf("has_api_tokens = %#v, want true", payload["has_api_tokens"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestJitteredHeartbeat_WithinBounds(t *testing.T) {
|
||||
min := heartbeatInterval - maxHeartbeatJitter
|
||||
max := heartbeatInterval + maxHeartbeatJitter
|
||||
|
|
|
|||
|
|
@ -446,9 +446,8 @@ func Run(ctx context.Context, version string) error {
|
|||
}
|
||||
|
||||
snap := telemetry.Snapshot{
|
||||
MultiTenant: currentCfg.MultiTenantEnabled,
|
||||
APITokens: len(currentCfg.APITokens),
|
||||
LicenseTier: "free",
|
||||
MultiTenant: currentCfg.MultiTenantEnabled,
|
||||
HasAPITokens: currentCfg.HasAPITokens(),
|
||||
}
|
||||
|
||||
// Resource counts come from the tenant-aware monitor aggregate, not the
|
||||
|
|
@ -476,11 +475,11 @@ func Run(ctx context.Context, version string) error {
|
|||
snap.SSOEnabled = ssoCfg.HasEnabledProviders()
|
||||
}
|
||||
|
||||
// License tier.
|
||||
// Coarse commercial posture only; telemetry does not send exact tiers.
|
||||
if router != nil && router.GetLicenseHandlers() != nil {
|
||||
if svc := router.GetLicenseHandlers().Service(context.Background()); svc != nil {
|
||||
if lic := svc.Current(); lic != nil {
|
||||
snap.LicenseTier = string(lic.Claims.Tier)
|
||||
snap.PaidLicense = lic.Claims.Tier != pkglicensing.TierFree
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue