Part of https://github.com/pnpm/tasks/issues/191. Wrong claims: - cli: drop the nonexistent `add-user --password` flag and document the terminal/stdin password read; `oci-gc --min-age` and `cache-gc --max-age` are the flags (the `-secs` forms are aliases); `--from` defaults to the `storage` key, not an internal field; `info` and `http` log the same access records; only global and server options have PNPR_* variables. - configuration: `packumentTtl` defaults to 5m; the minimal config now sets `auth.htpasswd.file`, and the auth section says accounts live in memory without it or a backend; grouped hosted registries default their org to `<ecosystem>~<name>`; the handshake is served for any surface; the pipeline surface also satisfies "something must be served"; add `package.update_maintainers` to the audit list. - tls-and-reverse-proxy: remove the invalid `publicUrl:` YAML key and the nonexistent `rate_limiting` setting. - backup-and-restore: `pnpr verify` hashes by default (`--check-hashes` does not exist, `--skip-hashes` skips only the hash comparison); `migrate-storage --verify` compares bytes. - webhooks: audit events do not cover publishes or token creation; the example uses a real action and the `actor` field. - cli: the `RUST_LOG` example keeps `pnpr::audit=info`. - install-acceleration: the `--public-url` host is exempt from the private-address refusal. - shared-side-effects-cache: `pnpm pipeline` publishes workspace-task artifacts. Missing facts: `routes.allowedPrivateNetworks`, the upstream `search` key and canonical `maxAge`, built-in `npm.jsr.io` route, relative path resolution, `log` defaults and `type`, `${VAR:-default}` substitution and the full secret-field list, `oci` defaults, `artifacts.quota` bounds, `migrate-storage` destination rules, `--osv false`, `--packument-ttl` default, S3 `forcePathStyle`/`allowHttp` defaults, grouped org layout, OIDC key table, container image section, `backend:` ignoring the htpasswd and token files. Duplicates and links: health probe bodies, reload procedure, and the resolver allowlist now link to their one home; broken `#secret` anchor fixed; links into the pnpm docs use absolute pnpm.io URLs. Stale history: drop "pnpr v1" and the per-paragraph "Since alpha.N" / "Since pnpm 12.x" prefixes, and the contributor-only extensibility text. Also corrects the `as_filter_directive` doc comment, which claimed `http` quiets the rest of the crate.
5.0 KiB
id, title
| id | title |
|---|---|
| webhooks | Webhooks and event notifications |
Organizations often need notifications when registry events occur, such as when a new package version is published, dist-tags change, or user permissions are modified.
This guide explains pnpr's event architecture and shows how to integrate external webhook dispatchers and notification pipelines.
Architecture decision
pnpr deliberately does not maintain an in-process HTTP webhook dispatch queue.
Building an internal webhook queue inside a registry server introduces operational drawbacks:
- Delivery guarantees: An in-memory queue inside the server loses pending webhooks if an instance crashes or restarts during a deployment. Storing persistent queue state on local disk complicates running multiple stateless pnpr replicas behind a load balancer.
- Process stability and head-of-line blocking: Slow or unreachable webhook destination endpoints can cause queue buildup, memory pressure, and retry storms inside the registry process, potentially degrading core package download and publish throughput.
- Separation of concerns: Reliable event delivery, deduplication, retry policies, and fan-out are best handled by dedicated event streaming and log shipping systems.
Instead, pnpr provides durable, structured integration points designed to feed external webhook dispatchers and message brokers.
Integration patterns
1. Structured audit logs
Administrative and destructive operations in pnpr emit a structured event under the pnpr::audit tracing target at info level. They cover account, team, rule, and SCIM changes, token revocation, unpublishes, packument and maintainer updates, dist-tag changes, and image deletions. Publishes and token creation are not audited. The full list of actions and their fields is in Admin audit logs.
When configured with log.format: json, pnpr outputs each audit event as a single-line JSON record on stdout:
{
"timestamp": "2026-10-17T01:15:30.123456Z",
"level": "INFO",
"target": "pnpr::audit",
"fields": {
"action": "package.dist_tag_set",
"actor": "alice",
"package": "@mycompany/utils",
"tag": "latest",
"version": "1.2.0",
"registry": "local",
"message": "package dist-tag set"
},
"span": {
"name": "request",
"request_id": "c8a14b09-1a73-42e1-95be-726ff342f534",
"client_ip": "192.168.1.50"
}
}
Lightweight log shippers such as Vector, Fluent Bit, or Fluentd can filter for target == "pnpr::audit" and forward these events to an HTTP webhook endpoint, Slack, or a message broker (such as Kafka, RabbitMQ, or AWS SQS).
Example: Forwarding audit logs to a webhook with Vector
Here is a Vector configuration that extracts pnpr::audit events from pnpr's container logs and forwards them to a webhook URL:
[sources.pnpr_logs]
type = "docker_logs"
include_containers = ["pnpr"]
[transforms.parse_pnpr_logs]
type = "remap"
inputs = ["pnpr_logs"]
source = ". = parse_json!(.message)"
[transforms.filter_audit_events]
type = "filter"
inputs = ["parse_pnpr_logs"]
condition = '.target == "pnpr::audit"'
[sinks.webhook_dispatcher]
type = "http"
inputs = ["filter_audit_events"]
uri = "https://events.example.com/webhooks/registry"
method = "post"
encoding.codec = "json"
[sinks.webhook_dispatcher.request.headers]
Authorization = "Bearer ${WEBHOOK_SECRET}"
Content-Type = "application/json"
2. Object storage bucket notifications
When pnpr is configured with an S3-compatible backend (AWS S3, Cloudflare R2, MinIO), you can configure bucket notifications directly on your object storage bucket.
Because package tarballs are stored as immutable blobs and packuments are written as JSON documents under the bucket prefix, storage notifications provide durable, real-time event triggers:
- AWS S3: Configure an EventBridge or SNS/SQS notification for
s3:ObjectCreated:*events matching the prefix of hosted packages. An AWS Lambda function or worker consumes the event and dispatches your webhook. - MinIO: Use MinIO bucket event notifications to dispatch events directly to an HTTP webhook endpoint.
- Cloudflare R2: Use R2 Event Notifications connected to a Cloudflare Worker or queue to invoke downstream webhooks.
3. CI/CD post-publish steps
For publishing workflows orchestrated by continuous integration (such as GitHub Actions or GitLab CI), invoking notification webhooks immediately after pnpm publish provides immediate, synchronous confirmation without requiring server-side webhooks:
- name: Publish to pnpr
run: pnpm publish --registry https://registry.example.com/npm/
- name: Dispatch notification webhook
if: success()
run: |
curl -X POST https://events.example.com/webhooks/published \
-H "Content-Type: application/json" \
-d '{"package": "${{ github.repository }}", "ref": "${{ github.ref_name }}"}'