Skip to content

Webhook Server

The Logrite Webhook Server listens for GitHub push events on Logrite’s own repositories. It is intentionally minimal — no npm dependencies, only Node.js built-ins.

SettingValue
TechnologyNode.js built-ins only (http, crypto, child_process)
Port9999 (webhook.logrite.ai / dev-webhook.logrite.ai)
RepoDeployd-io/logrite-webhook
Runs asThe webhook compose service (container), code in /app
Config/config/webhook.json, written by entrypoint.sh at every start from the container env (GITHUB_WEBHOOK_SECRET, DISCORD_WEBHOOK_URL, SERVER_IP, SERVER_NAME)
MethodPathDescription
GET/healthHealth check
GET/statusShow registered repos and last deploy times
POST/webhookGitHub push event receiver

All incoming webhook requests are verified using HMAC-SHA256 signature verification:

X-Hub-Signature-256: sha256=<hmac>

The secret is the container’s GITHUB_WEBHOOK_SECRET (copied into /config/webhook.json at start) and must match the GitHub repo’s webhook settings.

On the pre-2026-08-03 host the file also listed repos and their paths, as below. In the container it holds only the four values above, and the repo/branch → service map is built into webhook-server.cjs.

{
"port": 9999,
"secret": "your-github-webhook-secret",
"repos": [
{
"name": "Logrite-UI",
"branch": "main",
"path": "/root/logrite/services/frontend/main/Logrite-UI",
"deployCommand": "npm install && npm run build",
"service": "frontend"
},
{
"name": "Logrite-AI",
"branch": "main",
"path": "/root/logrite/services/backend/main/Logrite-AI",
"deployCommand": "mvn clean package -DskipTests",
"service": "backend"
}
]
}
  1. GitHub push event → POST /webhook
  2. Verify HMAC-SHA256 signature
  3. Match repo + branch to config
  4. Acquire the global deploy lock — waits if any other service is currently deploying
  5. Run git fetch origin && git reset --hard origin/<branch>
  6. npm install (Node.js repos, only if package.json changed)
  7. Run buildCmd (TypeScript compile or mvn package -DskipTests)
  8. The deploy watcher builds the new commit and deploys it through the console
  9. Release the global lock (next queued deploy starts)
  10. Discord notification (start / success / failure)

The webhook server enforces one deployment at a time via a global mutex. When multiple pushes arrive simultaneously (e.g., Logrite-UI + Logrite-AI pushed together):

  • The first push acquires the global lock and starts deploying immediately.
  • 1 more push can queue behind it and wait (MAX_GLOBAL_QUEUE = 1).
  • Any additional pushes beyond that are dropped with a warning log — re-push, or redeploy from the console at https://management.logrite.ai.
  • When the active deployment finishes it releases the lock, and the waiting one starts automatically.

This means at most 2 deployments can be in-flight at any time (1 active + 1 waiting). Any 3rd and beyond are dropped to prevent resource exhaustion.

Additionally, per-service queuing ensures that if the same repo is pushed twice in quick succession, the second push waits for the first to complete before running.

The /status endpoint shows the current queue state:

Terminal window
curl https://dev-webhook.logrite.ai/status
# → {
# "activeDeployments": ["Logrite-UI:main"],
# "globalQueueDepth": 1,
# "globalQueueMax": 1,
# "queuedDeployments": {"Logrite-AI:main": 1},
# "recentDeployments": [...]
# }

Dropped deployments appear in recentDeployments with "status": "dropped".

To change the queue cap, edit MAX_GLOBAL_QUEUE at the top of webhook-server.cjs and redeploy.

The cli/webhook-cli.cjs script drove PM2 and manages nothing now. On the VM:

Terminal window
C=$(docker ps -q --filter label=com.docker.compose.project=logrite --filter label=com.docker.compose.service=webhook)
docker logs -f "$C" # logs
curl -s https://dev-webhook.logrite.ai/status

Redeploy it like any service: the console at https://management.logrite.ai, or from the dev VM curl -sX POST http://127.0.0.1:8090/api/deploy -H 'content-type: application/json' -d '{"service":"webhook","host":"dev","ref":"main"}'.

In each repo’s GitHub Settings → Webhooks:

  • Payload URL: https://webhook.logrite.ai/webhook (dev: https://dev-webhook.logrite.ai/webhook)
  • Content type: application/json
  • Secret: Must match the container’s GITHUB_WEBHOOK_SECRET
  • Events: push (auto-deploy) and pull_request (Agents-flow v1g forwarding — off by default)

Agents-flow v1g — pull_request event forwarding

Section titled “Agents-flow v1g — pull_request event forwarding”

In addition to push-driven auto-deploy, the webhook server forwards pull_request events (action ∈ opened | synchronize | reopened) to the Logrite-AI backend at POST /api/v1/monitoring/pr-event. Other pull_request actions are 200’d silently.

Forwarding is off by default — enable by setting either:

  • env var LOGRITE_PR_EVENTS_ENABLED=true (wins), or
  • webhook.json field pr_events_enabled: true

Backend URL defaults to http://localhost:8080; override with LOGRITE_BACKEND_URL or backend_url in webhook.json.

Forwarded payload shape:

{
"installationId": 555555,
"repoFullName": "Deployd-io/Logrite-AI",
"prNumber": 482,
"headSha": "abc123",
"action": "opened",
"sender": "victor"
}

The forward is async with a 5s timeout. Failures log but don’t retry — GitHub re-delivers on the next PR sync. The webhook server does not verify the backend response or store PR-event state locally; routing / ack-ing is the backend’s job.

See AI Agents → Agents-flow for the backend side.

The backend’s PR-event route requires X-Warden-Internal-Token; the webhook never sent it, so forwarded PR events were refused. notify.cjs now adds the header from WARDEN_INTERNAL_TOKEN (the backend’s value on the same estate). With LOGRITE_PR_EVENTS_ENABLED=true and the token unset, the server logs once at boot [pr-events] enabled but WARDEN_INTERNAL_TOKEN is unset — PR events will not be forwarded and answers each PR event {"status":"pr_events_misconfigured"} without forwarding. Full enablement (backend flag, LOGRITE_BACKEND_URL, comments off): AI coding → PR events.

Documented from logrite-webhook main at 9ceebe25.

  • A failed PR-event forward answers 502. The forward to the backend’s POST /api/v1/monitoring/pr-event is awaited (5 s bound for the whole exchange). A non-2xx answer or a timeout makes the webhook answer GitHub 502 pr_event_forward_failed instead of 202, so GitHub marks the delivery failed and it can be redelivered. The forward carries GitHub’s X-GitHub-Delivery as deliveryId.
  • The signature is checked over the raw bytes. The body is collected as bytes, capped at 25 MiB (413 above it), verified with HMAC over exactly those bytes and parsed only afterwards (the same for /coolify/deploy).
  • The container config is written as JSON by node (JSON.stringify), so a secret with a quote or backslash no longer breaks it.