Authentication

Each hook can require authentication. Three modes are available.

None (default)

[hooks.auth]
mode = "none"

No authentication required. Anyone who knows the URL can trigger the hook.

Bearer Token

[hooks.auth]
mode = "bearer"
token = "my-secret-token"

Callers must include the token in the Authorization header:

curl -X POST http://localhost:8080/hook/deploy \
  -H "Authorization: Bearer my-secret-token"

The token can reference an environment variable:

[hooks.auth]
mode = "bearer"
token = "${{DEPLOY_TOKEN}}"

HMAC Signature

[hooks.auth]
mode = "hmac"
header = "X-Hub-Signature-256"
algorithm = "sha256"
secret = "${{WEBHOOK_SECRET}}"

The caller signs the request body with the shared secret. Sendword verifies the signature using constant-time comparison to prevent timing attacks.

This is the standard approach for GitHub, GitLab, and most webhook providers:

BODY='{"ref": "refs/heads/main"}'
SIG=$(echo -n "$BODY" | openssl dgst -sha256 -hmac "$SECRET" | cut -d' ' -f2)

curl -X POST http://localhost:8080/hook/github \
  -H "X-Hub-Signature-256: sha256=$SIG" \
  -H "Content-Type: application/json" \
  -d "$BODY"

Failed authentication

Requests that fail authentication are logged as trigger attempts with an auth_failed status. They never reach the executor.