Small relay for routing GitLab pipeline failure webhooks into OpenClaw sessions.
This project sits between GitLab and OpenClaw, filters noisy webhook traffic down to the failed pipeline events you actually care about, and wakes the right OpenClaw session or agent for triage.
GitLab can already emit webhooks for pipeline events, but raw webhook payloads are a lousy final destination.
What you usually want is:
- accept only failed pipeline events
- dedupe retries and duplicates
- route each repo to the right worker session
- wake OpenClaw with a compact, actionable summary
- let the agent decide whether humans need to be interrupted
That is what this relay does.
- exposes
POST /gitlab/pipeline - validates
X-Gitlab-Token - accepts only configured GitLab event types
- ignores non-failed pipelines
- dedupes repeated failed pipeline notifications in memory
- maps
project.path_with_namespaceto a configured OpenClaw session target - shells out to
openclaw cron addto wake a stable session with anagentTurn
This could eventually become a native OpenClaw plugin. A standalone relay is the easier starting point:
- deploy and debug with curl independently of OpenClaw internals
- configure per repo without touching plugin packaging or reload cycles
- throw it away or replace it without breaking anything else
- Node.js 20 or newer
openclawavailable on the PATH of the relay host- an OpenClaw setup that accepts
openclaw cron add ... - GitLab project or group webhook access
git clone <repo-url>
cd gitlab-openclaw-relay
cp config.example.json config.json
pnpm install
pnpm startDo not commit config.json.
This repo is meant to keep only config.example.json under version control. Your real webhook secret, routing targets, and local relay settings should live in the ignored config.json file.
Edit config.json.
Example:
{
"listen": {
"host": "127.0.0.1",
"port": 4318
},
"gitlab": {
"secret": "replace-me",
"acceptEvents": ["Pipeline Hook"],
"dedupeWindowMs": 900000
},
"openclaw": {
"binary": "openclaw"
},
"routes": [
{
"project": "group/project-one",
"sessionTarget": "session:project-one-build-failures",
"agentId": "default-agent"
},
{
"project": "group/project-two",
"sessionTarget": "session:project-two-build-failures",
"agentId": "ops-agent"
}
],
"fallback": {
"sessionTarget": "session:build-failures",
"agentId": "default-agent"
}
}host: bind addressport: bind port
secret: webhook secret token expected fromX-Gitlab-TokenacceptEvents: allowed GitLab event names, usuallyPipeline HookdedupeWindowMs: in-memory duplicate suppression window
binary: optional path to theopenclawbinary
List of exact project-path matches.
Each route supports:
project: GitLabpath_with_namespacesessionTarget: OpenClaw target session key likesession:my-repo-buildsagentId: optional agent to wake, defaults todefault-agentchannel: optional announce channel passed through toopenclaw cron addaccountId: optional announce account id passed through toopenclaw cron add
Used when no explicit route matches.
In GitLab project or group settings:
- URL:
https://your-host/gitlab/pipeline - Secret token: same value as
gitlab.secret - Trigger:
Pipeline events
GET /healthzReturns:
{ "ok": true }If you are running this on macOS, use launchd instead of a loose terminal process.
Included files:
bin/run-relay.sh— wrapper that discovers the repo root, setsRELAY_CONFIG, and writes logs to.run/deploy/launchd/com.example.gitlab-openclaw-relay.plist— example LaunchAgent plist; replace placeholder paths before loading it
Example setup:
cd gitlab-openclaw-relay
cp config.example.json config.json
pnpm install
chmod +x bin/run-relay.sh
cp deploy/launchd/com.example.gitlab-openclaw-relay.plist ~/Library/LaunchAgents/
# Edit the copied plist and replace /ABSOLUTE/PATH/TO/... with your real checkout path.
launchctl bootout gui/$(id -u) ~/Library/LaunchAgents/com.example.gitlab-openclaw-relay.plist 2>/dev/null || true
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.example.gitlab-openclaw-relay.plist
launchctl kickstart -k gui/$(id -u)/com.example.gitlab-openclaw-relayUseful checks:
launchctl print gui/$(id -u)/com.example.gitlab-openclaw-relay
curl http://127.0.0.1:4318/healthzLogs land in .run/, which is intentionally ignored by git.
For each accepted failure, the relay creates a one-shot cron job like this:
openclaw cron add \
--name gitlab-failure-... \
--at 10s \
--delete-after-run \
--session session:project-one-build-failures \
--message "GitLab pipeline failure..." \
--agent default-agent \
--announceThat wakes a stable OpenClaw session with enough context to inspect the failure and continue triage.
Current limitations:
- dedupe is in-memory only
- pipeline failures only, not standalone job webhooks
- exact project matching only
- delivery currently shells out to the OpenClaw CLI instead of using direct gateway RPC
- persistent dedupe store
- job webhook support
- branch-specific routing
- glob or regex project matching
- direct gateway RPC delivery
- optional native OpenClaw plugin version
- optional fetch of failed job logs before wakeup
MIT