Skip to content

Commit b3ac7b0

Browse files
teovlclaude
andcommitted
fix(telemetry): add missing consent gates to appstore telemetry events
PILOT-401, PILOT-406, PILOT-407 emitted telemetry unconditionally — calling DefaultEndpoint even when the user had not configured a URL. All three blocks now check consent.GetConsent(home, "telemetry") before sending, consistent with review.go. install.sh also showed the wrong config.json structure (root-level keys instead of the required nested {"consent": {...}} object), and claimed "No data is sent unless explicitly enabled" which contradicts the opt-out model. Both corrected. Also fix the go-fmt pre-commit hook to use `git add -u` instead of `git add -A` so untracked files are never inadvertently staged. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 81b50c1 commit b3ac7b0

5 files changed

Lines changed: 68 additions & 58 deletions

File tree

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ repos:
2020
hooks:
2121
- id: go-fmt
2222
name: go fmt
23-
entry: bash -c 'gofmt -w -s . && git add -A'
23+
entry: bash -c 'gofmt -w -s . && git add -u'
2424
language: system
2525
files: \.go$
2626
pass_filenames: false

cmd/pilotctl/appstore.go

Lines changed: 29 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import (
3737
"github.com/TeoSlayer/pilotprotocol/pkg/telemetry"
3838
"github.com/pilot-protocol/app-store/pkg/ipc"
3939
"github.com/pilot-protocol/app-store/pkg/manifest"
40+
"github.com/pilot-protocol/common/consent"
4041
"github.com/pilot-protocol/common/crypto"
4142
)
4243

@@ -1212,33 +1213,35 @@ func cmdAppStoreInstall(args []string) {
12121213
Reason: reason,
12131214
})
12141215

1215-
// Emit a telemetry event for the successful install (consent-gated —
1216-
// no-op when PILOT_TELEMETRY_URL is empty or identity.json is absent).
1217-
// Best-effort: a send failure is logged but not fatal — the install
1218-
// itself already succeeded on disk.
1216+
// Emit a telemetry event for the successful install.
1217+
// Consent-gated (telemetry flag, default on). Best-effort: a send
1218+
// failure is logged but not fatal — the install already succeeded on disk.
12191219
{
1220-
url := os.Getenv("PILOT_TELEMETRY_URL")
1221-
if url == "" {
1222-
url = telemetry.DefaultEndpoint
1223-
}
1224-
sourceStr := "catalogue"
1225-
if source == installSourceLocal {
1226-
sourceStr = "local"
1227-
}
1228-
payload, _ := json.Marshal(map[string]string{
1229-
"app_id": m.ID,
1230-
"version": m.AppVersion,
1231-
"source": sourceStr,
1232-
})
1233-
identityPath := configDir() + "/identity.json"
1234-
client := telemetry.NewClientFromIdentity(url, identityPath, 0)
1235-
err := client.Send(telemetry.Event{
1236-
Kind: "app_installed",
1237-
TS: time.Now().UTC().Format(time.RFC3339),
1238-
Payload: payload,
1239-
})
1240-
if err != nil {
1241-
slog.Warn("telemetry send failed, install still successful", "app", m.ID, "err", err)
1220+
home, _ := os.UserHomeDir()
1221+
if consent.GetConsent(home, "telemetry") {
1222+
url := os.Getenv("PILOT_TELEMETRY_URL")
1223+
if url == "" {
1224+
url = telemetry.DefaultEndpoint
1225+
}
1226+
sourceStr := "catalogue"
1227+
if source == installSourceLocal {
1228+
sourceStr = "local"
1229+
}
1230+
payload, _ := json.Marshal(map[string]string{
1231+
"app_id": m.ID,
1232+
"version": m.AppVersion,
1233+
"source": sourceStr,
1234+
})
1235+
identityPath := configDir() + "/identity.json"
1236+
client := telemetry.NewClientFromIdentity(url, identityPath, 0)
1237+
err := client.Send(telemetry.Event{
1238+
Kind: "app_installed",
1239+
TS: time.Now().UTC().Format(time.RFC3339),
1240+
Payload: payload,
1241+
})
1242+
if err != nil {
1243+
slog.Warn("telemetry send failed, install still successful", "app", m.ID, "err", err)
1244+
}
12421245
}
12431246
}
12441247

cmd/pilotctl/appstore_catalogue.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ import (
5353

5454
"github.com/TeoSlayer/pilotprotocol/internal/catalogtrust"
5555
"github.com/TeoSlayer/pilotprotocol/pkg/telemetry"
56+
"github.com/pilot-protocol/common/consent"
5657
)
5758

5859
// defaultCatalogueURL points at the canonical catalogue.json on main.
@@ -235,11 +236,11 @@ func cmdAppStoreSignCatalogue(args []string) {
235236
}
236237

237238
func cmdAppStoreCatalogue(_ []string) {
238-
// Emit a telemetry event for the catalogue page view (consent-gated —
239-
// no-op when PILOT_TELEMETRY_URL is empty or identity.json is absent).
240-
// Best-effort, non-blocking: a send failure is logged but doesn't
241-
// prevent the catalogue from rendering.
242-
{
239+
// Emit a telemetry event for the catalogue page view.
240+
// Consent-gated (telemetry flag, default on). Best-effort: a send
241+
// failure is logged but doesn't prevent the catalogue from rendering.
242+
home, _ := os.UserHomeDir()
243+
if consent.GetConsent(home, "telemetry") {
243244
url := os.Getenv("PILOT_TELEMETRY_URL")
244245
if url == "" {
245246
url = telemetry.DefaultEndpoint

cmd/pilotctl/appstore_view.go

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929
"github.com/pilot-protocol/app-store/pkg/manifest"
3030

3131
"github.com/TeoSlayer/pilotprotocol/pkg/telemetry"
32+
"github.com/pilot-protocol/common/consent"
3233
)
3334

3435
// installedAppFacts is the verified, local-only band of `view` — derived
@@ -179,27 +180,29 @@ func cmdAppStoreView(args []string) {
179180
"app %q not found in catalogue or install root", appID)
180181
}
181182

182-
// Emit a telemetry event for the detail view (consent-gated —
183-
// no-op when PILOT_TELEMETRY_URL is empty or identity.json is absent).
184-
// Best-effort: a send failure is logged but not fatal — the view
185-
// itself already resolved and rendered below.
183+
// Emit a telemetry event for the detail view.
184+
// Consent-gated (telemetry flag, default on). Best-effort: a send
185+
// failure is logged but not fatal — the view already resolved and rendered below.
186186
{
187-
url := os.Getenv("PILOT_TELEMETRY_URL")
188-
if url == "" {
189-
url = telemetry.DefaultEndpoint
190-
}
191-
payload, _ := json.Marshal(map[string]string{
192-
"app_id": appID,
193-
})
194-
identityPath := configDir() + "/identity.json"
195-
client := telemetry.NewClientFromIdentity(url, identityPath, 0)
196-
err := client.Send(telemetry.Event{
197-
Kind: "appstore_view",
198-
TS: time.Now().UTC().Format(time.RFC3339),
199-
Payload: payload,
200-
})
201-
if err != nil {
202-
slog.Warn("telemetry send failed, view still shown", "app", appID, "err", err)
187+
home, _ := os.UserHomeDir()
188+
if consent.GetConsent(home, "telemetry") {
189+
url := os.Getenv("PILOT_TELEMETRY_URL")
190+
if url == "" {
191+
url = telemetry.DefaultEndpoint
192+
}
193+
payload, _ := json.Marshal(map[string]string{
194+
"app_id": appID,
195+
})
196+
identityPath := configDir() + "/identity.json"
197+
client := telemetry.NewClientFromIdentity(url, identityPath, 0)
198+
err := client.Send(telemetry.Event{
199+
Kind: "appstore_view",
200+
TS: time.Now().UTC().Format(time.RFC3339),
201+
Payload: payload,
202+
})
203+
if err != nil {
204+
slog.Warn("telemetry send failed, view still shown", "app", appID, "err", err)
205+
}
203206
}
204207
}
205208

install.sh

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -730,20 +730,23 @@ echo " reviews — occasional prompt to review installations"
730730
echo " skillinject — auto-inject the Pilot Protocol skill into"
731731
echo " agent toolchains"
732732
echo ""
733-
echo " Disable any feature by setting \"false\" in config.json:"
733+
echo " Disable any feature by adding \"false\" entries under the \"consent\""
734+
echo " key in config.json:"
734735
echo " ${PILOT_DIR}/config.json"
735736
echo ""
736-
echo " \"telemetry\": false # suppress interest signals"
737-
echo " \"broadcasts\": false # block agent-directed broadcasts"
738-
echo " \"reviews\": false # suppress review prompts"
737+
echo " {\"consent\": {"
738+
echo " \"telemetry\": false, // suppress interest signals"
739+
echo " \"broadcasts\": false, // block agent-directed broadcasts"
740+
echo " \"reviews\": false // suppress review prompts"
741+
echo " }}"
739742
echo ""
740743
echo " Skillinject has its own CLI commands:"
741744
echo " pilotctl skills disable # remove injected skills, stop future ticks"
742745
echo " pilotctl skills enable # re-install and re-enable"
743746
echo ""
744747
echo " Config changes take effect on daemon restart (or immediately for"
745-
echo " skillinject). No data is sent to Pilot Protocol LLC or any third"
746-
echo " party unless you explicitly enable it."
748+
echo " skillinject). Telemetry features are ON by default (opt-out model)."
749+
echo " Set the consent flags above to false to disable them."
747750
echo ""
748751
echo "============================================"
749752
echo ""

0 commit comments

Comments
 (0)