Webhook Server
Overview
Section titled “Overview”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.
| Setting | Value |
|---|---|
| Technology | Node.js built-ins only (http, crypto, child_process) |
| Port | 9999 (webhook.logrite.ai / dev-webhook.logrite.ai) |
| Repo | Deployd-io/logrite-webhook |
| Runs as | The 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) |
Endpoints
Section titled “Endpoints”| Method | Path | Description |
|---|---|---|
GET | /health | Health check |
GET | /status | Show registered repos and last deploy times |
POST | /webhook | GitHub push event receiver |
Security
Section titled “Security”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.
Configuration (historical host layout)
Section titled “Configuration (historical host layout)”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" } ]}Deploy Flow
Section titled “Deploy Flow”- GitHub push event → POST
/webhook - Verify HMAC-SHA256 signature
- Match repo + branch to config
- Acquire the global deploy lock — waits if any other service is currently deploying
- Run
git fetch origin && git reset --hard origin/<branch> npm install(Node.js repos, only ifpackage.jsonchanged)- Run
buildCmd(TypeScript compile ormvn package -DskipTests) - The deploy watcher builds the new commit and deploys it through the console
- Release the global lock (next queued deploy starts)
- Discord notification (start / success / failure)
Serial Deployment Queue
Section titled “Serial Deployment Queue”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:
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.
Operating it
Section titled “Operating it”The cli/webhook-cli.cjs script drove PM2 and manages nothing now. On the VM:
C=$(docker ps -q --filter label=com.docker.compose.project=logrite --filter label=com.docker.compose.service=webhook)docker logs -f "$C" # logscurl -s https://dev-webhook.logrite.ai/statusRedeploy 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"}'.
GitHub Setup
Section titled “GitHub Setup”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) andpull_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.jsonfieldpr_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.
PR forwards authenticate (2026-10-09)
Section titled “PR forwards authenticate (2026-10-09)”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.
Audit changes (2026-10-10)
Section titled “Audit changes (2026-10-10)”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-eventis awaited (5 s bound for the whole exchange). A non-2xx answer or a timeout makes the webhook answer GitHub 502pr_event_forward_failedinstead of 202, so GitHub marks the delivery failed and it can be redelivered. The forward carries GitHub’sX-GitHub-DeliveryasdeliveryId. - 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.