* 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
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
- Composer resolves and downloads packages directly from WordPress.org (dist URLs point to
downloads.wordpress.org). - After install, Composer POSTs to
/downloadswith the list of installed packages. - The app ingests events inline with
INSERT OR IGNORE(no background queue). - 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 IGNOREis 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
/downloadsendpoint 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-installshasn't run — check the timer/cron.