Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ Implemented:
- GoReleaser/GitHub Releases install and update workflow
- Makefile build/test/e2e workflow
- independent telemetry ingestion backend with strict schema validation,
bounded in-memory batching, TiDB storage, and personless PostHog forwarding
bounded in-memory batching, and TiDB storage

There are no registered placeholder commands at the current stage. Implemented
mutating commands support `--dry-run` where their command contract declares
Expand Down Expand Up @@ -363,7 +363,7 @@ internal/query/ JMESPath query application
internal/secretinput/ no-echo secret input helper
internal/settings/ global settings parsing and legacy logging migration
internal/telemetry/ CLI eligibility, identity, event, and delivery path
internal/telemetrybackend/ telemetry API, batcher, TiDB, and PostHog sinks
internal/telemetrybackend/ telemetry API, batcher, and TiDB sink
internal/update/ GitHub Releases update checks and self-update logic
internal/version/ build version metadata
scripts/ installer scripts
Expand Down
4 changes: 3 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@ e2e: build
telemetry-e2e: build build-telemetry-backend build-telemetry-migrator
@test -f "$(TELEMETRY_E2E_ENV)" || { echo "missing $(TELEMETRY_E2E_ENV); set TI_TEST_TELEMETRY_TIDB_DSN in that ignored file" >&2; exit 2; }
@set -a; . "$(TELEMETRY_E2E_ENV)"; set +a; \
TI_E2E_BIN="$(abspath $(TI_BIN))" TI_TELEMETRY_BACKEND_E2E_BIN="$(abspath $(TELEMETRY_BACKEND_BIN))" TI_TELEMETRY_MIGRATOR_E2E_BIN="$(abspath $(TELEMETRY_MIGRATOR_BIN))" TI_TELEMETRY_E2E=1 $(GO) test ./e2e -count=1 -v -run '^TestTelemetryDeliveryToTiDB$$'
TI_TEST_TELEMETRY_TIDB_DSN="$${TI_TEST_TELEMETRY_TIDB_DSN:-$${TDC_TEST_TELEMETRY_TIDB_DSN:-}}"; \
test -n "$$TI_TEST_TELEMETRY_TIDB_DSN" || { echo "TI_TEST_TELEMETRY_TIDB_DSN is required in $(TELEMETRY_E2E_ENV)" >&2; exit 2; }; \
TI_TEST_TELEMETRY_TIDB_DSN="$$TI_TEST_TELEMETRY_TIDB_DSN" TI_E2E_BIN="$(abspath $(TI_BIN))" TI_TELEMETRY_BACKEND_E2E_BIN="$(abspath $(TELEMETRY_BACKEND_BIN))" TI_TELEMETRY_MIGRATOR_E2E_BIN="$(abspath $(TELEMETRY_MIGRATOR_BIN))" TI_TELEMETRY_E2E=1 $(GO) test ./e2e -count=1 -v -run '^TestTelemetryDeliveryToTiDB$$'

live-e2e: build
$(LIVE_E2E_RUN) -run '^TestLive'
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -434,7 +434,7 @@ Build the independently deployed telemetry ingestion service:
make build-telemetry-backend
```

The backend binary is written to `bin/ti-telemetry-backend`. Its API, privacy contract, TiDB/PostHog batching behavior, and Docker deployment are documented in [Telemetry Backend Design](docs/telemetry-backend-design.md).
The backend binary is written to `bin/ti-telemetry-backend`. Its API, privacy contract, TiDB batching behavior, and Docker deployment are documented in [Telemetry Backend Design](docs/telemetry-backend-design.md).

## Test

Expand Down
15 changes: 2 additions & 13 deletions cmd/ti-telemetry-backend/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,26 +35,15 @@ func main() {

tidbSink := telemetrybackend.NewTiDBSink(db)

postHogSink, err := telemetrybackend.NewPostHogSink(
config.PostHogAPIHost,
config.PostHogProjectToken,
config.Environment,
&http.Client{},
)
if err != nil {
logger.Error("initialize PostHog sink failed")
os.Exit(1)
}

metrics := &telemetrybackend.Metrics{}
batcher := telemetrybackend.NewBatcher(
config,
[]telemetrybackend.Sink{tidbSink, postHogSink},
[]telemetrybackend.Sink{tidbSink},
logger,
metrics,
)
batcher.Start()
api := telemetrybackend.NewServer(config, batcher, tidbSink, postHogSink, logger, metrics)
api := telemetrybackend.NewServer(config, batcher, tidbSink, logger, metrics)
httpServer := &http.Server{
Addr: config.BindAddr,
Handler: api.Handler(),
Expand Down
2 changes: 0 additions & 2 deletions deploy/telemetry/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,3 @@ TELEMETRY_RATE_LIMIT_PER_MINUTE=60
TELEMETRY_RATE_LIMIT_BURST=120
TELEMETRY_TRUSTED_PROXY_CIDRS=172.16.0.0/12
TIDB_DSN=tdc_telemetry:replace-me@tcp(gateway01.us-east-1.prod.aws.tidbcloud.com:4000)/tdc_telemetry?tls=true&parseTime=true
POSTHOG_API_HOST=https://us.i.posthog.com
POSTHOG_PROJECT_TOKEN=replace-me
14 changes: 7 additions & 7 deletions docs/spec/done/0022-telemetry.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

Collect minimal, privacy-preserving CLI telemetry that helps improve tdc reliability and command UX without capturing sensitive user data or adding telemetry management commands to the public CLI surface.

Telemetry is routed only through a product-owned HTTPS backend. The CLI never sends events directly to PostHog or another third-party analytics endpoint.
Telemetry is routed only through a product-owned HTTPS backend and stored only in TiDB. The CLI and backend do not send events to a third-party analytics endpoint.

## Product Decisions

Expand All @@ -14,8 +14,8 @@ Telemetry is routed only through a product-owned HTTPS backend. The CLI never se
- Do not add `tdc cli describe-telemetry`, `tdc cli enable-telemetry`, `tdc cli disable-telemetry`, or another telemetry command.
- `tdc update`, help, version, and commandless usage invocations never send telemetry.
- Telemetry is best-effort and lossy. Delivery must not change command stdout, stderr, output format, exit code, or user-visible result.
- The backend returns `202 Accepted` after validated events enter its bounded in-memory batcher. This does not guarantee that TiDB or PostHog has completed its sink write.
- No local durable queue, MQ, Kafka, SQS, Pub/Sub, or TiDB-to-PostHog consumer is required for MVP.
- The backend returns `202 Accepted` after validated events enter its bounded in-memory batcher. This does not guarantee that TiDB has completed its sink write.
- No local durable queue, MQ, Kafka, SQS, Pub/Sub, or downstream forwarding consumer is required for MVP.

## Eligible Commands

Expand Down Expand Up @@ -231,7 +231,7 @@ Delivery behavior:
6. The CLI boundary maps the result to stable exit and application error codes.
7. The telemetry package constructs one allowlisted event and posts it to `POST /v1/telemetry/batch`.
8. The backend validates the schema, enqueues accepted events, and returns `202 Accepted`.
9. The backend flush loop independently writes the same sanitized batch to TiDB and PostHog.
9. The backend flush loop writes the sanitized batch to TiDB.
10. The CLI ignores the delivery result except for optional redacted debug diagnostics.

## Package Design
Expand All @@ -256,8 +256,8 @@ The CLI depends on these guarantees:
- valid event batches are acknowledged with `202 Accepted` after entering a bounded in-memory buffer;
- unknown or prohibited fields are rejected;
- accepted events are best-effort and may be lost before sink flush;
- TiDB and PostHog receive the same sanitized event batch through independent sink attempts;
- PostHog person profiles are disabled with `$process_person_profile = false`;
- TiDB is the only persistent telemetry destination;
- the backend does not forward telemetry to third-party analytics services;
- no CLI-shipped backend credential is required.

## Acceptance Criteria
Expand Down Expand Up @@ -297,4 +297,4 @@ The CLI depends on these guarantees:
- User-configurable telemetry endpoints.
- Capturing command output, API response bodies, SQL text, paths, file contents, credentials, flag values, raw errors, host identity, or cloud resource IDs.
- Local durable telemetry queues.
- MQ, Kafka, SQS, Pub/Sub, durable outbox tables, or TiDB-to-PostHog consumer workflows.
- MQ, Kafka, SQS, Pub/Sub, durable outbox tables, or downstream forwarding workflows.
7 changes: 3 additions & 4 deletions docs/spec/done/0025-telemetry-environment-metadata.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ ALTER TABLE telemetry_events

The implementation must account for repeated startup and partially applied migration state rather than assuming a new database. Do not index `extra_json`; ad hoc JSON inspection is allowed, but a repeatedly queried key should become a separately designed first-class field later.

TiDB receives `tag` and complete `extra_json`. PostHog receives `tag` and `extra` as nested event properties. The backend must not flatten arbitrary extra keys into top-level PostHog properties or use either field as `distinct_id` or person properties.
TiDB receives `tag` and complete `extra_json`. The backend must not flatten arbitrary extra keys into first-class columns or use either field as an identity.

## Failure Behavior

Expand Down Expand Up @@ -189,10 +189,9 @@ Backend tests must cover:
- strict UTF-8, size, depth, prohibited-key, and unknown-field rejection;
- TiDB migration behavior for a new and existing schema;
- TiDB batch insertion preserving JSON type;
- PostHog nested properties with person profiles disabled;
- independent TiDB and PostHog sink failure behavior remaining unchanged.
- TiDB sink failure behavior remaining best-effort and non-fatal to the CLI.

Black-box `make e2e` uses a local telemetry receiver to inspect schema v2 payloads without contacting production. A separate opt-in `make telemetry-e2e` loads the ignored `e2e/.env.telemetry` file and requires a test-only `TDC_TEST_TELEMETRY_TIDB_DSN` with database create/drop privileges. It creates a unique empty database, migrates it through legacy schema version 1, inserts a legacy event, migrates to the latest version, proves that event is preserved, then starts a local telemetry backend and fake PostHog receiver. It executes a no-side-effect CLI dry run against that local backend, verifies the stored schema v2 event and extra JSON, and drops only its temporary database. Ordinary `make test`, `make e2e`, and all live-e2e targets must not read the dotenv file or require a live TiDB instance.
Black-box `make e2e` uses a local telemetry receiver to inspect schema v2 payloads without contacting production. A separate opt-in `make telemetry-e2e` loads the ignored `e2e/.env.telemetry` file and requires a test-only `TI_TEST_TELEMETRY_TIDB_DSN` with database create/drop privileges; the test target also accepts the legacy pre-v0.2 `TDC_TEST_TELEMETRY_TIDB_DSN` name. It creates a unique empty database, migrates it through legacy schema version 1, inserts a legacy event, migrates to the latest version, proves that event is preserved, then starts a local telemetry backend. It executes a no-side-effect CLI dry run against that local backend, verifies the stored schema v2 event and extra JSON, and drops only its temporary database. Ordinary `make test`, `make e2e`, and all live-e2e targets must not read the dotenv file or require a live TiDB instance.

## Documentation Updates

Expand Down
Loading