packages.wenpai.net/docs/telemetry.md
Ben Word 7cb8fef01b
WP Packages rename (#42)
* Update all import paths

* Rename directory cmd/wpcomposer/ → cmd/wppackages/

* Rename import alias wpcomposergo → wppackagesgo in main.go and migrate_test.go

* Makefile — binary name wpcomposer → wppackages

* Update Air path

* Global replace repo.wp-composer.com → repo.wp-packages.org

* Global replace cdn.wp-composer.com → cdn.wp-packages.org

* Global replace wp-composer.com → wp-packages.org (remaining)

* Composer repo key in templates/docs: repositories.wp-composer → repositories.wp-packages

* Rename columns on the existing schema

* Update all Go code referencing these column names

* Routes & SEO

* Templates & front-end

* Admin UI

* Documentation

* CI/CD

* Config defaults

* Rename role directory

* Rename all systemd template files inside the role

* Update contents of all .j2 templates — service names, binary paths, descriptions

* Update tasks/main.yml and handlers/main.yml in the role

* Update deploy/ansible/roles/app/tasks/main.yml and deploy.yml

* Update deploy/ansible/group_vars/production/main.yml

* Additional renames/fixes

* Additional renames/fixes

* Additional renames/fixes

* not needed
2026-03-19 11:50:12 -05:00

3.1 KiB

Telemetry

Install telemetry records installs via Composer's notify-batch mechanism.

Goal

  • Count installs initiated through WP Packages package metadata.
  • No impact on download latency (notification is sent after install).
  • Avoid counting rapid duplicate retries from the same client.

How It Works

Composer has built-in support for install notifications. When packages.json includes a notify-batch URL, the Composer client POSTs install data after downloading packages.

packages.json

{
  "notify-batch": "https://app.example.com/downloads",
  "packages": [],
  ...
}

The notify-batch URL must be absolute and point to the app domain (not the R2/CDN domain).

Notification Flow

  1. Composer resolves and downloads packages directly from WordPress.org (dist URLs point to downloads.wordpress.org).
  2. After install, Composer POSTs to /downloads with the list of installed packages.
  3. The app ingests events inline with INSERT OR IGNORE (no background queue).
  4. If the server is unreachable, the install still succeeds — notifications are best-effort.

POST Format

Composer sends:

{
  "downloads": [
    {"name": "wp-plugin/akismet", "version": "5.0.0"},
    {"name": "wp-theme/astra", "version": "4.0.0"}
  ]
}

Response

{"status": "ok"}

Always returns 200 — best-effort, non-blocking for client workflows.

Event Ingestion

Data model:

  • Table: install_events
  • Columns: package_id, version, ip_hash, user_agent_hash, dedupe_bucket, dedupe_hash, created_at

Dedupe Strategy (SQLite)

Uses a rolling time bucket and unique constraint instead of advisory locks:

dedupe_bucket = floor(unix_timestamp / dedupe_window_seconds)
dedupe_hash   = sha256(ip_hash + package_id + version + user_agent_hash)
  • Unique constraint on (dedupe_hash, dedupe_bucket).
  • Ingest via INSERT OR IGNORE — duplicates within the same bucket are silently dropped.
  • Default dedupe_window_seconds: 3600 (1 hour).

Tradeoffs

  • Dedupe at bucket granularity (close to the Laravel app's rolling-window behavior).
  • Simpler and lock-free under SQLite's write serialization.
  • No background queue needed — inline INSERT OR IGNORE is fast enough for expected traffic.

Aggregation

Periodic command (run hourly via systemd timer or cron):

wppackages aggregate-installs

Rollups written to packages:

Column Description
wp_packages_installs_total All-time install count
wp_packages_installs_30d Installs in the last 30 days
last_installed_at Timestamp of most recent install

UI Integration

Public browser and detail pages expose:

  • WordPress.org download count (from package metadata).
  • WP Packages install counters (when non-zero).
  • Sort by Composer installs.

Operational Notes

  • The /downloads endpoint is lightweight — validates the POST and inserts inline.
  • If the endpoint is down, Composer installs are unaffected (users still download directly from WordPress.org).
  • Stale counters indicate aggregate-installs hasn't run — check the timer/cron.