본문으로 건너뛰기

가이드

웹훅

웹훅 엔드포인트는 워크스페이스의 경보 규칙이 그곳으로 보내는 것을 받습니다. 차단된 릴리스, 사람을 기다리는 결과, 끝난 관리형 실행, 프로덕션 모니터의 경보입니다. 각 전달은 엔드포인트의 비밀로 서명된 JSON 본문이므로, 서버는 그에 따라 행동하기 전에 그것이 귀하의 워크스페이스에서 왔는지 확인할 수 있습니다.

엔드포인트 추가

워크스페이스의 Integrations 페이지에서 Owner나 Admin이 이름과 포트 443 또는 8443의 공개 https URL로 서명된 웹훅을 추가합니다. 그러면 페이지에 whsec_로 시작하는 엔드포인트의 서명 비밀이 표시됩니다. 그때 복사하세요. 비밀은 암호화되어 저장되며 다시 표시되지 않습니다. 교체하려면 엔드포인트를 삭제하고 다시 추가합니다.

그런 다음 Alerts 페이지에서 그 엔드포인트를 하나 이상의 규칙의 채널로 선택합니다. "Send a test"는 oloproof.test 전달을 보내고 서버가 응답한 상태를 보여 줍니다.

전송되는 내용

{
  "type": "oloproof.gate_blocked",
  "id": "dlv_8c1f0a2b3c4d5e6f7a8b9c0d",
  "created_at": "2026-10-26T14:03:00.000Z",
  "workspace": "acme",
  "rule": { "id": "rul_1a2b3c4d5e6f7a8b9c0d1e2f", "name": "Do not ship" },
  "event": "gate_blocked",
  "items": [
    {
      "project": "support",
      "kind": "run",
      "id": "run_20261026T140100_4f2a",
      "release_action": "BLOCK",
      "rules": [["faithfulness", "FAIL"]],
      "href": "https://oloproof.com/app/workspaces/acme/projects/support/runs/run_20261026T140100_4f2a"
    }
  ],
  "more": 0
}

항목에는 id, 엔진의 결정 상태, 릴리스 조치, 작업의 상태와 결과, 엔진이 쓴 그대로의 모니터 수준, 임계값, 구간, 그리고 링크가 담깁니다. 케이스의 입력, 출력, 기대 답변, 레이블은 절대 담기지 않습니다. 한 번의 전달에는 최대 50개 항목이 들어가며, more는 그 이상의 이벤트 수를 셉니다.

전달 검증

모든 전달에는 Standard Webhooks 형식의 헤더 세 개가 있습니다.

  • webhook-id: 전달의 id로, 재시도마다 같습니다.
  • webhook-timestamp: 이 시도가 전송된 시각으로, 1970년 이후의 초 단위입니다.
  • webhook-signature: v1,과, 비밀의 바이트(whsec_ 뒤 부분을 base64로 디코딩한 것)를 키로 한 id.timestamp.body의 base64 HMAC-SHA256.

받은 그대로의 원시 본문으로 서명을 계산하고, 상수 시간으로 비교하며, 귀하의 시계와 5분 넘게 차이 나는 타임스탬프는 거부하세요.

import base64, hashlib, hmac, time

def verify(secret: str, headers: dict, body: bytes) -> bool:
    key = base64.b64decode(secret.removeprefix("whsec_"))
    msg = f"{headers['webhook-id']}.{headers['webhook-timestamp']}.".encode() + body
    expected = base64.b64encode(hmac.new(key, msg, hashlib.sha256).digest()).decode()
    fresh = abs(time.time() - int(headers["webhook-timestamp"])) <= 300
    given = [part.split(",", 1)[-1] for part in headers["webhook-signature"].split()]
    return fresh and any(hmac.compare_digest(expected, sig) for sig in given)
import { createHmac, timingSafeEqual } from "node:crypto";

export function verify(secret, headers, body) {
  const key = Buffer.from(secret.replace(/^whsec_/, ""), "base64");
  const id = headers["webhook-id"], ts = headers["webhook-timestamp"];
  const expected = createHmac("sha256", key).update(`${id}.${ts}.${body}`).digest("base64");
  const fresh = Math.abs(Date.now() / 1000 - Number(ts)) <= 300;
  return fresh && headers["webhook-signature"].split(" ").some((part) => {
    const sig = Buffer.from(part.split(",").pop());
    return sig.length === expected.length && timingSafeEqual(sig, Buffer.from(expected));
  });
}

Standard Webhooks 라이브러리도 같은 헤더를 검증합니다.

전달

10초 안에 2xx 상태로 응답하세요. 그 밖의 응답은 리디렉션을 포함해 모두 실패한 시도이며, 1, 5, 30, 120, 360분 후에 재시도된 뒤 포기됩니다. 서버가 처리한 뒤 응답이 유실될 수 있으므로 같은 전달이 두 번 도착할 수 있습니다. webhook-id로 중복을 버리세요. 규칙은 이벤트마다 최대 한 번만 실행되므로, 새 id는 새 전달입니다.

각 시도 전에 엔드포인트의 이름이 다시 확인되며, 요청은 공개 주소로만 갑니다. 루프백, 사설, 링크 로컬, 공유, 예약, 클라우드 메타데이터 주소는 거부되며, 이를 담은 IPv6 형식도 마찬가지입니다. 연결은 확인된 주소로 이루어지고, 인증서는 이름에 대해 확인됩니다.

Slack

Slack 엔드포인트는 수신 웹훅 URL입니다. 이벤트마다 한 줄의 짧은 영어 메시지를 받으며, 각 결과로 가는 링크가 있습니다. Slack은 서명 헤더를 확인하지 않지만, 헤더는 그래도 전송됩니다.