Agent skill
1Password Device Trust (Kolide) Webhooks Skill
Receive and verify 1Password Device Trust (Kolide) webhooks. Use when setting up Kolide webhook handlers, debugging Authorization header signature verification, or handling device and compliance events such as devices.created, issues.new, device_trust.status_changed, and auth_logs.failure.
Install this skill
npx skills add hookdeck/webhook-skills --skill kolide-webhooks
When to Use This Skill
- How do I receive 1Password Device Trust (Kolide) webhooks?
- How do I verify the Kolide webhook signature in the
Authorizationheader? - How do I handle
devices.created,issues.new,device_, ortrust.status_ changed auth_logs.failureevents? - Why does my Kolide webhook signature verification fail, or why does
Bearerparsing break on it? - How do I send the Kolide
webhook.testevent to my endpoint?
Essential Code (USE THIS)
Every delivery is an HTTP POST with Authorization: <hex>. The value is the bare lowercase hex HMAC-SHA256 of the raw request body, keyed with the endpoint's signing secret (used as-is, as UTF-8 bytes). There is no Bearer scheme, no prefix, and no timestamp; only the body is signed, so there is no replay window to check. Despite its name, the header is not a credential to look up: it is the signature.
Verification (Node.js)
const crypto = require('crypto');
// rawBody: Buffer of the exact request bytes (express.raw({ type: '*/*' }), not express.json)
function verifyKolide(rawBody, authorization, secret) {
if (!secret || typeof authorization !== 'string') return false;
const received = authorization.trim();
// 64 hex chars first: Buffer.from(x, 'hex') silently drops bad characters
if (!/^[0-9a-f]{64}$/i.test(received)) return false;
const expected = crypto.createHmac('sha256', secret) // secret as UTF-8, not decoded
.update(rawBody).digest();
return crypto.timingSafeEqual(Buffer.from(received, 'hex'), expected);
}
Verification (Python)
import hashlib, hmac, re
def verify_kolide(raw_body: bytes, authorization: str | None, secret: str) -> bool:
if not secret or authorization is None:
return False
received = authorization.strip()
if not re.fullmatch(r"[0-9a-fA-F]{64}", received): # exact length and alphabet
return False
expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
# bytes on both sides: a non-ASCII header gives a rejection, not a 500
return hmac.compare_digest(received.lower().encode(), expected.encode())
A missing, empty, malformed, or mismatching Authorization value gets a 401.
For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.
Setup with Hookdeck
Hookdeck's KOLIDE source type (labelled "1Password Device Trust (Kolide)") verifies this signature: HMAC, SHA-256, header authorization, hex encoding. Paste the endpoint's signing secret into the source and register the Hookdeck source URL as the endpoint URL. See references/setup.md.
Event Envelope and Types
Every event is { event, id, timestamp, data }. Branch on the top-level event field and dedupe on id. Delivery retries, timeout, and accepted status codes are not documented, so return 200 quickly and keep handlers idempotent.
| Event | Triggered When |
|---|---|
devices.created | A device is created |
devices.registered | A device is registered to an owner |
devices.destroyed | A device is deleted |
device_ | A device's trust status changes (for example to blocked) |
issues.new | A check finds a new issue on a device |
issues.resolved | An issue is resolved |
auth_logs.success / auth_logs.failure | A device-trust authentication succeeds or fails |
audit_log.recorded | An admin action is recorded in the audit log |
admin_users.created | An admin user is created |
requests.registration / requests.issue_exemption | An end user asks to register a device or for an issue exemption |
webhook.test | You send the test event from the admin console |
See references/overview.md for payload shapes.
Important Headers
| Header | Description |
|---|---|
Authorization | Bare hex HMAC-SHA256 of the raw body, no Bearer or other prefix |
X-Kolide-Webhook-Identifier | Identifies the webhook endpoint; changes when the secret is rotated |
Environment Variables
KOLIDE_WEBHOOK_SECRET=your_endpoint_signing_secret # "Reveal Secret" on the webhook settings screen
Local Development
# Start tunnel (no account needed)
npx hookdeck-cli listen 3000 kolide --path /webhooks/kolide
Reference Materials
- references/overview.md - Kolide webhook concepts, events, payloads
- references/setup.md - Registering the endpoint, the signing secret, Hookdeck setup
- references/verification.md - Signature details and debugging