HookRun

June 24, 2026 · View on GitHub

Release Go Version Go Report Card codecov PkgGoDev License

中文文档 | Website

A lightweight webhook action engine — execute custom commands, scripts, and forward webhooks based on YAML rules when webhook requests arrive.

Single binary under 5MB. No database. No container runtime. Cross-platform (Linux / Windows / macOS).

┌──────────┐     POST /webhook/{name}     ┌─────────────┐     Auth + Filter    ┌────────────┐     Execute    ┌──────────────────┐
│  GitHub  │ ──────────────────────────▶  │   HookRun   │ ──────────────────▶  │   Engine   │ ─────────────▶ │ Commands/Scripts │
│  GitLab  │     Token / HMAC / IP        │  (routing & │     Match rules      │  (policy   │    shell &     │ Webhooks         │
│  Grafana │ ◀──────────────────────────  │   matching) │ ◀──────────────────  │   check)   │ ◀───────────── │ Deploy           │
│  Custom  │       JSON response          │             │     Action result    │            │    stdout      │                  │
└──────────┘                              └─────────────┘                      └────────────┘                └──────────────────┘

Why HookRun?

A purpose-built action engine for webhook automation, compared with general-purpose tools:

HookRunadnanh/webhookn8nHuginn
Binary SizeSingle file ~3MB, zero depsSingle file ~7MB, zero depsDocker + SQLite (default) / PostgreSQLDocker + MySQL / PostgreSQL
Execution Policyblock / always / cooldown — 3 modesNo concurrency control, always triggersPartial (retry / wait)Scenario-based triggers
Hot ReloadAuto-watches directory, zero-config-hotreload flag, specified files onlyRestart requiredRestart required
AuthenticationToken + HMAC + IP whitelist, top-level auth, AND-combinedToken + HMAC, nested in trigger-rule, scattered configBasic / Header / JWT / IP whitelistDevise user auth + OAuth services
Routing/webhook/{filename} targeted, on-demand/hooks/{id} flat, single entryWorkflow engineScenario graph (agent links)
Process MgmtBuilt-in CLI daemon (start/stop/status)Requires external systemd, etc.Docker container managementDocker container management
Config FormatYAML (readable, comments supported)JSON / YAMLVisual editor / JSONJSON (agent config)
RelayBuilt-in multi-target relay + dynamic registry + tag matchingNo relay supportRequires HTTP node workflowManual scenario proxy setup
RetryBuilt-in exponential backoff with jitterNo retry supportRetry on Fail nodePartial (scenario-level)
Health CheckBuilt-in /health endpointNo built-in endpointHas health checkHas health check
LicenseMIT, full freedomMIT, full freedomFair-code (SUL), commercial restrictionsMIT, full freedom
  • Security First — Token auth, HMAC signature verification, and IP whitelisting with AND-combined enforcement
  • Lightweight — One binary, under 5MB, zero dependencies. No database, no container runtime. scp it to your server and run — minimal resources required
  • Full Control — MIT licensed. Self-hosted. Your rules, your data, your infrastructure

Demo

$ hookrun validate
Validating config: config.yaml
PASS: All configurations are valid
  Server port: 9000
  Webhook route: /webhook
  Allow all: false
  Log mode: daily
  Log path: ./logs
  Log retention: 30 days
  Config dir: ./hooks
  Rule files loaded: 1
    - github-auto-deploy (2 rules: push-to-main, tag-release) [auth: token]

$ hookrun start
HookRun started in background (PID: 8421)

$ hookrun status
Status:  running
PID:     8421
Port:    9000
Rules:   2 config(s)
Uptime:  12m30s

# Incoming webhook
$ curl -X POST http://localhost:9000/webhook/github-auto-deploy \
    -H "X-Webhook-Token: your-secret-token" \
    -H "X-GitHub-Event: push" \
    -d '{"ref":"refs/heads/main"}'

→ {"code":200,"message":"ok","config":"github-auto-deploy","rule":"push-to-main","actions":3}

Features

  • YAML Driven — All rules defined in YAML files, zero coding required
  • Targeted Routing/webhook/{filename} directly routes to specific config for efficient matching
  • Flexible Auth — Token (Header/Query) + HMAC Signature (GitHub/GitLab) + IP whitelist with AND relationship
  • Multi-Condition Filters — Match against Header / Query / Body with operators: eq ne contains regex
  • Execution Policies — Three modes: block (prevent concurrency), always (always execute), cooldown (rate limiting)
  • Policy Inheritance — File-level → Rule-level override
  • File-Level Filters — Global constraints applied to all rules, AND with rule-level filters
  • Parameter Passing — Template variables ({{.body.ref}}) and pass_args to inject request data into commands
  • Webhook Action — Forward webhook payloads to other services with template-based body, header forwarding, and {{.raw_body}} support
  • Relay Action — Multi-target relay to other HookRun instances with static targets, dynamic registration pool, and tag-based discovery
  • Failure Retry — Built-in exponential backoff with jitter for commands and scripts
  • Hot Reload — Reload configs at runtime without restarting
  • Log Management — Daily/single mode with auto-cleanup, per-rule independent log files
  • Health Endpoint — Built-in /health endpoint for monitoring integration (Prometheus, Uptime Kuma, etc.)

Use Cases

Git Auto Deploy

Push to GitHub/GitLab, server automatically pulls, builds, and deploys.

name: "auto-deploy"
auth:
  hmac:
    header: "X-Hub-Signature-256"
    secret: "your-webhook-secret"
    algorithm: "sha256"
execution:
  policy: "block"
rules:
  - name: "deploy-main"
    filters:
      - type: "body"
        key: "ref"
        operator: "eq"
        value: "refs/heads/main"
    actions:
      - type: "command"
        cmd: "cd /var/www/app && git pull origin main"
        timeout: 30
      - type: "command"
        cmd: "cd /var/www/app && npm install --production && npm run build"
        timeout: 120
      - type: "script"
        path: "./scripts/restart.sh"
        timeout: 30

CI/CD Pipeline Trigger

Chain webhook events into multi-step build pipelines with timeout control and concurrency protection.

Monitoring Alert Response

Receive alerts from Grafana, Prometheus, or any service and run automated remediation scripts.

name: "alert-handler"
auth:
  token:
    source: "header"
    key: "Authorization"
    value: "Bearer your-grafana-token"
execution:
  policy: "cooldown"
  cooldown_seconds: 300
rules:
  - name: "high-cpu-response"
    filters:
      - type: "body"
        key: "alerts[0].labels.alertname"
        operator: "eq"
        value: "HighCPU"
    actions:
      - type: "command"
        cmd: "echo 'CPU alert received, scaling up...'"
        timeout: 30
      - type: "script"
        path: "./scripts/scale-up.sh"
        timeout: 120

Custom Automation

Any HTTP POST becomes a trigger. Sync data, send notifications, manage infrastructure, and more.

Multi-Server Deployment

Relay webhook events from a central HookRun to multiple downstream instances across servers. Use static targets, or let instances self-register with tags for dynamic discovery.

# Upstream: relay to all registered "prod" instances
- type: "relay"
  relay:
    targets:
      - tag: "prod"
    forward_headers: ["X-GitHub-Event"]
    timeout: 30
# Downstream (each server): auto-register with upstream on startup
# In config.yaml:
relay_client:
  upstream: "http://10.0.0.1:9000"
  registry_token: "shared-registry-secret"
  tags: ["prod"]
  ttl: 120

Quick Start

Install

# Option 1: One-liner install (recommended — no Go, no Docker)
curl -fsSL https://bluvenr.github.io/hookrun/install.sh | bash

# Option 2: Build from source
git clone https://github.com/bluvenr/hookrun.git
cd hookrun
go build -o hookrun ./cmd/hookrun/

# Option 3: go install
go install github.com/bluvenr/hookrun/cmd/hookrun@latest

# Option 4: Download pre-built binary
# Visit https://github.com/bluvenr/hookrun/releases

Cross-compile for other platforms:

# Linux amd64
GOOS=linux GOARCH=amd64 go build -o hookrun ./cmd/hookrun/

# Linux arm64 (Raspberry Pi, AWS Graviton)
GOOS=linux GOARCH=arm64 go build -o hookrun ./cmd/hookrun/

# macOS (Apple Silicon)
GOOS=darwin GOARCH=arm64 go build -o hookrun ./cmd/hookrun/

# Windows
GOOS=windows GOARCH=amd64 go build -o hookrun.exe ./cmd/hookrun/

Configure

  1. Edit global config config.yaml:
server:
  port: 9000
  route: "/webhook"
  allow_all: false             # require targeted routing (default: false)

log:
  mode: "daily"                # "daily" (default) | "single"
  path: "./logs"
  retention_days: 30           # only for daily mode

config_dir: "./hooks"          # rule YAML files directory
  1. Create a rule file hooks/my-app.yaml:
name: "my-app-deploy"

auth:
  token:
    source: "header"
    key: "X-Webhook-Token"
    value: "your-secret"

execution:
  policy: "block"

rules:
  - name: "deploy-main"
    filters:
      - type: "header"
        key: "X-GitHub-Event"
        operator: "eq"
        value: "push"
      - type: "body"
        key: "ref"
        operator: "eq"
        value: "refs/heads/main"
    actions:
      - type: "command"
        cmd: "cd /var/www/my-app && git pull origin main"
        timeout: 30
      - type: "command"
        cmd: "cd /var/www/my-app && npm install --production && npm run build"
        timeout: 120
      - type: "script"
        path: "./scripts/deploy.sh"
        args: ["production"]
        timeout: 300

Run

# Validate configuration
hookrun validate

# Start server (daemon mode)
hookrun start

# Foreground mode (for debugging)
hookrun start -f

Docker

docker pull ghcr.io/bluvenr/hookrun:latest

docker run -d --name hookrun \
  -p 9000:9000 \
  -v ./config.yaml:/app/config.yaml \
  -v ./hooks:/app/hooks \
  -v ./logs:/app/logs \
  --restart unless-stopped \
  ghcr.io/bluvenr/hookrun:latest

Available tags:

TagDescription
latestLatest stable release
1.1.3Specific version
1.1Latest patch in minor version

Build from source

FROM golang:1.23-alpine AS builder
WORKDIR /build
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -trimpath -o hookrun ./cmd/hookrun/

FROM alpine:3.20
RUN apk --no-cache add ca-certificates tzdata
WORKDIR /app
COPY --from=builder /build/hookrun /usr/local/bin/hookrun
COPY config.yaml /app/
COPY hooks/ /app/hooks/
EXPOSE 9000
ENTRYPOINT ["hookrun", "start", "-f", "-c", "/app/config.yaml"]
docker build -t hookrun:latest .
docker run -d --name hookrun \
  -p 9000:9000 \
  -v ./hooks:/app/hooks \
  -v ./logs:/app/logs \
  --restart unless-stopped \
  hookrun:latest

Webhook Routing

URL PatternBehavior
/webhook/my-appDirectly route to hooks/my-app.yaml, execute first matching rule
/webhookIterate all configs, stop at first matching rule (controlled by allow_all)
/healthHealth check endpoint for monitoring

CLI Commands

CommandDescription
hookrun initInitialize config with templates (generic/github/gitlab)
hookrun startStart server (daemon by default, -f for foreground)
hookrun stopStop the running server
hookrun restartRestart the server
hookrun statusShow status (PID, port, rules count, uptime)
hookrun reloadHot-reload all YAML configurations
hookrun validateValidate all YAML files
hookrun relay statusShow relay status (role, upstream/downstream info)
hookrun relay targetsList registered downstream targets (token auto-read from config)
hookrun versionShow version information

Configuration Guide

Authentication

Token, HMAC signature, and IP whitelist use AND relationship — all configured checks must pass:

auth:
  token:
    source: "header"           # "header" or "query"
    key: "X-Webhook-Token"
    value: "secret123"
  hmac:
    header: "X-Hub-Signature-256"   # GitHub signature header
    secret: "your-webhook-secret"   # HMAC secret from GitHub webhook settings
    algorithm: "sha256"              # "sha256" (default) | "sha1" | "sha512"
  ip_whitelist:
    - "192.168.1.100"
    - "10.0.0.0/24"            # CIDR supported

Filters

Multiple filters within a rule use AND relationship (all must match):

filters:
  - type: "header"             # "header" | "query" | "body"
    key: "X-GitHub-Event"
    operator: "eq"             # "eq" | "ne" | "contains" | "regex"
    value: "push"
  - type: "body"
    key: "commits[0].message"  # JSON path supported
    operator: "contains"
    value: "release"

Execution Policy

Supports file-level and rule-level inheritance. Rule-level takes priority:

# File-level (default for all rules)
execution:
  policy: "block"              # "block" | "always" | "cooldown"
  cooldown_seconds: 300        # only for cooldown mode

rules:
  - name: "light-task"
    execution:
      policy: "always"         # overrides file-level
    ...
PolicyBehaviorUse Case
blockReject if still running (409)Deploy, build
alwaysAlways spawn new executionStateless notifications
cooldownReject within cooldown period (429)Rate-limited scenarios

Actions

actions:
  - type: "command"            # "command" | "script" | "webhook" | "relay"
    cmd: "echo hello"
    timeout: 60                # timeout in seconds
    isolate: false             # run in isolated subprocess
    continue_on_error: false   # continue to next action on failure
  - type: "command"
    # Template variables: embed request data directly into commands
    cmd: "git checkout {{.body.ref}}"
  - type: "command"
    # pass_args: extract and append as trailing arguments
    cmd: "echo 'Deploying:'"
    pass_args:
      - source: "body"
        key: "ref"
      - source: "header"
        key: "X-GitHub-Event"
  - type: "script"
    path: "./scripts/deploy.sh"
    args: ["production"]
    timeout: 300
    isolate: true
    # env_from: inject request data as env vars (HOOKRUN_ prefix auto-applied)
    env_from:
      - source: "body"
        key: "ref"
        env: "GIT_REF"            # → $HOOKRUN_GIT_REF
      - source: "header"
        key: "X-GitHub-Event"
        env: "GITHUB_EVENT"       # → $HOOKRUN_GITHUB_EVENT
    # Default env vars always available: $HOOKRUN_RAW_BODY, $HOOKRUN_TRIGGER_IP
  - type: "command"
    cmd: "deploy.sh"
    retry:                      # retry on failure with exponential backoff
      max_attempts: 3
      interval_seconds: 5
  - type: "webhook"
    url: "https://api.example.com/deploy"
    method: "POST"
    headers:
      Authorization: "Bearer your-token"
    forward_headers: ["X-GitHub-Event"]   # whitelist headers to forward
    body: '{"event":"{{.header.X-GitHub-Event}}","payload":{{.raw_body}}}'
    timeout: 30
  - type: "relay"                  # relay to other HookRun instances
    relay:
      targets:
        - url: "http://10.0.0.2:9000/webhook/deploy-app"   # static target
          token: "relay-secret-B"
        - url: "http://10.0.0.3:9000/webhook/deploy-app"
          token: "relay-secret-C"
        - tag: "prod"                                      # dynamic: matches registered instances tagged "prod"
      forward_headers: ["X-GitHub-Event"]
      timeout: 30
      max_relay_hops: 3

Logging

# Global log (config.yaml)
log:
  mode: "daily"                # "daily" | "single"
  path: "./logs"
  retention_days: 30           # daily mode only
  # max_size_mb: 0             # single mode only, 0 = unlimited

# Per-rule log (rule YAML) — dual-write: global + this file
log:
  path: "./logs/my-app.log"

Response Format

All responses are JSON:

{"code": 200, "message": "ok", "config": "my-app", "rule": "deploy-main", "actions": 3}
{"code": 401, "message": "Authentication failed"}
{"code": 409, "message": "Task 'my-app/deploy-main' is running, please try again later"}
{"code": 429, "message": "Task 'my-app/deploy-main' is in cooldown, retry in 120 seconds"}
{"code": 404, "message": "Config 'not-exist' not found"}

Health Check

curl http://localhost:9000/health
{"status": "ok", "uptime": "2h30m15s", "rules": 3, "version": "x.y.z"}

Integrate with Prometheus (blackbox_exporter), Uptime Kuma, Nagios, or any monitoring tool that supports HTTP probes.

Documentation

DocumentDescription
Configuration ReferenceComplete parameter reference for all config fields
Usage GuideCLI commands, routing, response formats, and common scenarios
Deployment GuideBuild, systemd, Docker, Windows, and reverse proxy setup

Project Structure

HookRun/
├── cmd/hookrun/               # CLI entry point
├── internal/
│   ├── config/                # Config parsing & validation
│   ├── server/                # HTTP server & routing
│   ├── engine/                # Matching engine (Auth + Filter + Policy)
│   ├── executor/              # Command/script/webhook/relay executor
│   ├── logger/                # Logging module
│   ├── daemon/                # Daemon process management
│   └── version/               # Version info (ldflags injection)
├── config.yaml                # Global configuration
├── hooks/                     # Rule YAML directory
│   └── example.yaml
└── docs/                      # Documentation

Contributing

Contributions are welcome! Here are some ways you can help:

  • Report bugs — Open an issue with reproduction steps
  • Suggest features — Share your use cases and ideas
  • Improve docs — Fix typos, add examples, translate documentation
  • Submit code — Fork, branch, and send a pull request

Please run hookrun validate and ensure all tests pass before submitting PRs.

License

MIT