Agent skill

Harbor Webhooks Skill

Receive and authenticate Harbor container registry webhooks. Use when setting up a Harbor project webhook endpoint, checking the Auth Header sent as the Authorization header, debugging 401s, or handling PUSH_ARTIFACT, PULL_ARTIFACT, DELETE_ARTIFACT, SCANNING_COMPLETED and QUOTA_EXCEED events in the Default or CloudEvents payload format.

Install this skill

npx skills add hookdeck/webhook-skills --skill harbor-webhooks


Harbor (the open source container registry) sends project webhooks as an HTTP POST when artifacts are pushed, pulled, deleted or scanned, when a quota is hit, and on replication and tag retention. Payloads are not signed. The only authentication is the optional Auth Header you enter on the webhook, which Harbor sends verbatim as the Authorization header.

Use the HTTP notify type and paste the Auth Header value exactly as set in Harbor, including any "Bearer " or "Basic " prefix.

When to Use This Skill

  • How do I receive Harbor webhooks?
  • How do I verify the Harbor webhook Auth Header (Authorization)?
  • Does Harbor sign webhook payloads? (No: shared credential header only)
  • Why does my Harbor webhook endpoint return 401?
  • How do I handle PUSH_ARTIFACT and SCANNING_COMPLETED events?
  • How do I handle Harbor's CloudEvents payload format?

Verification (core)

There is no signature. Compare the whole Authorization header to the value you typed in Harbor's Auth Header field, scheme prefix included (for example Bearer <token> or Basic <base64>). Don't strip, parse or base64-decode the prefix:

const crypto = require('crypto');

// Check lengths first: timingSafeEqual throws on a length mismatch.
function safeEqual(a, b) {
  const ba = Buffer.from(String(a), 'utf8');
  const bb = Buffer.from(String(b), 'utf8');
  return ba.length === bb.length && crypto.timingSafeEqual(ba, bb);
}

function verifyHarborRequest(headers, expected = process.env.HARBOR_AUTHORIZATION) {
  if (!expected) return false;                 // never accept when unconfigured
  const received = headers['authorization'];   // full value, e.g. "Bearer 5f1c..."
  return Boolean(received) && safeEqual(received, expected);
}

Return 401 when this fails, 400 for a body that isn't a JSON object, and 200 once handled (any 2xx counts as delivered). Read the body raw (express.raw({ type: '*/*' })) and parse it only after the check. In Python use hmac.compare_digest on bytes (encode both sides; a non-ASCII str raises). Python and Next.js equivalents are in references/verification.md.

A static shared credential authenticates the sender, not the body, and gives no replay protection (no signature, no timestamp). Use HTTPS and rotate the Auth Header in Harbor if it leaks.

For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.

Event Types

The Default format is { type, occur_at, operator, event_data } (occur_at is Unix seconds). The CloudEvents format is a CloudEvents 1.0 envelope (specversion, id, source, type, time, data, operator). Pick the format per webhook in Harbor; handle both by dispatching on type:

Default typeCloudEvents typeFired when
PUSH_ARTIFACTharbor.artifact.pushedAn artifact is pushed
PULL_ARTIFACTharbor.artifact.pulledAn artifact is pulled
DELETE_ARTIFACTharbor.artifact.deletedAn artifact is deleted
SCANNING_COMPLETEDharbor.scan.completedA scan completes
SCANNING_STOPPEDharbor.scan.stoppedA scan is stopped
SCANNING_FAILEDharbor.scan.failedA scan fails
QUOTA_EXCEEDharbor.quota.exceededThe project quota is exceeded
QUOTA_WARNINGharbor.quota.warnedThe project quota nears its threshold
REPLICATIONharbor.replication.status.changedA replication status changes
TAG_RETENTIONharbor.tag_retention.finishedA tag retention run finishes

Environment Variables

HARBOR_AUTHORIZATION=Bearer 5f1c0f3e8b7a4d2c9e6a1b3d   # exactly what you typed in Harbor's Auth Header field

Local Development

npx hookdeck-cli listen 3000 harbor --path /webhooks/harbor

No account required. Use the printed URL as the Endpoint URL of the Harbor webhook.

Resources


Repository

hookdeck/webhook-skills

v0.1.0 · MIT · Updated Nov 10, 2026

View on GitHub →