블로그 · 가이드 · 2026년 10월 29일
서명된 웹훅으로 AEGIS 워크플로 시작하기
공개: AEGIS는 100monkeys.ai가 만들고 있으므로 이 가이드는 만든 쪽의 설명으로 읽어 주세요. 이 가이드는 저희 AI 에이전트 하나가 공개 AEGIS 문서(영어)인 Configuring Webhooks 페이지를 바탕으로 초안을 쓰고 회사가 검토했으며, 이 한국어판은 영어 원문 가이드의 번역입니다. 아래 명령과 코드 블록은 모두 그 페이지에서 복사했고, 이 가이드를 위해 실행한 것은 없습니다. 인용은 영어 그대로 두고 옆에 번역을 붙였습니다. 문서가 답하지 않는 질문은 마지막 절에서 밝힙니다.
AEGIS by 100monkeys.ai, in beta는 다른 시스템이 이벤트를 보내면 워크플로를 시작할 수 있습니다. GitHub의 push, 끝난 CI 작업, HTTP 요청을 보낼 수 있는 어떤 서비스든 됩니다. 보내는 쪽은 여러분의 노드에 계정이 필요 없습니다. 문서의 표현으로는 "Authentication is handled by HMAC-SHA256 signature verification, not OAuth."(인증은 OAuth가 아니라 HMAC-SHA256 서명 검증으로 처리됩니다.) 이 가이드는 공유 비밀 값부터 시험한 요청까지 소스 하나를 처음부터 끝까지 설정하고, 그 과정에서 만날 오류를 다룹니다.
1단계. 소스 이름 정하기
모든 웹훅은 POST /v1/webhooks/{source}로 들어옵니다. 소스는 "a lowercase slug that identifies the system sending the event"(이벤트를 보내는 시스템을 가리키는 소문자 슬러그)이며, 예를 들면 github, stripe, my-ci입니다. 보내는 시스템마다 하나씩 고르세요. 비밀 값, 라우트, 중복 검사가 모두 이것을 기준으로 합니다.
2단계. 오케스트레이터에 공유 비밀 값 설정하기
비밀 값은 테넌트와 소스 단위로 나뉘므로, 소스를 둘 다 github라고 부르는 두 테넌트도 "completely independent secrets"(완전히 독립된 비밀 값)를 가집니다. 각 쌍은 오케스트레이터의 환경 변수 하나이며, 이름은 다음 패턴을 따릅니다.
# Pattern: AEGIS_WEBHOOK_SECRET_<UPPER_TENANT>_<UPPER_SOURCE>
# Hyphens and dots in tenant id and source name are replaced with underscores
export AEGIS_WEBHOOK_SECRET_U_ABC123_GITHUB="your-shared-secret-here"
export AEGIS_WEBHOOK_SECRET_U_ABC123_STRIPE="another-secret"
export AEGIS_WEBHOOK_SECRET_U_DEF456_MY_CI="third-secret"
따라서 테넌트 u-abc123의 소스 my-ci는 AEGIS_WEBHOOK_SECRET_U_ABC123_MY_CI를 읽습니다. 결정된 쌍에 대한 변수가 없으면 요청은 401 secret_not_found로 거부됩니다. 같은 비밀 값은 보내는 쪽에만 주고 다른 누구에게도 주지 마세요.
3단계. 소스를 워크플로로 라우팅하기
직접 라우트는 소스를 워크플로 하나에 연결하므로 라우팅이 "instant and deterministic"(즉각적이고 결정적)입니다. 현재 릴리스에서 직접 라우트는 "configured via orchestrator workflow routing configuration"(오케스트레이터의 워크플로 라우팅 설정으로 구성)되며, 페이지는 "there is no aegis stimulus routes CLI command yet"(aegis stimulus routes CLI 명령은 아직 없습니다)이라고 적어 둡니다. 직접 라우트가 없으면 파이프라인은 설정된 RouterAgent로 넘어가고, 이것이 언어 모델로 이벤트를 분류합니다. 둘 다 없으면 요청은 422 no_router_configured로 실패합니다. 알려진 보내는 쪽이 하나뿐이라면 직접 라우트가 맞습니다. 같은 이벤트가 언제나 같은 워크플로를 시작합니다.
4단계. 보내는 쪽에서 원본 본문에 서명하기
보내는 쪽은 공유 비밀 값으로 원본 요청 본문의 HMAC-SHA256을 계산하고, sha256= 접두사를 붙여 X-Aegis-Signature에 담아 보냅니다. 문서의 Python 예시입니다.
import hashlib
import hmac
import json
import requests
def sign_payload(body: bytes, secret: str) -> str:
sig = hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()
return f"sha256={sig}"
payload = json.dumps({"event": "push", "ref": "refs/heads/main"}).encode()
signature = sign_payload(payload, WEBHOOK_SECRET)
requests.post(
"https://your-aegis-node/v1/webhooks/github",
data=payload,
headers={
"Content-Type": "application/json",
"X-Aegis-Signature": signature,
},
)
페이지는 같은 내용을 Node.js로도 보여 줍니다. 굵게 강조한 규칙은 하나입니다. 파싱하기 전에 원본 본문 바이트로 HMAC을 계산하세요. "Computing over a re-serialized object will produce a different signature."(다시 직렬화한 객체로 계산하면 다른 서명이 나옵니다.) 보내는 바로 그 바이트에 서명하세요.
5단계. curl로 엔드포인트 시험하기
# Generate a test signature
SECRET="your-shared-secret-here"
BODY='{"event":"push","ref":"refs/heads/main"}'
SIG="sha256=$(echo -n "$BODY" | openssl dgst -sha256 -hmac "$SECRET" | awk '{print $2}')"
# Send the test webhook
curl -X POST https://your-aegis-node/v1/webhooks/github \
-H "Content-Type: application/json" \
-H "X-Aegis-Signature: $SIG" \
-d "$BODY"
성공하면 ID 두 개로 답합니다.
{
"stimulus_id": "stim-uuid",
"workflow_execution_id": "wfex-uuid"
}
두 번째가 이벤트가 시작한 워크플로 실행입니다. 다른 실행처럼 따라가면 됩니다.
6단계. 중복 전달 막기
보내는 쪽은 재시도합니다. 제공자의 전달 ID를 X-Idempotency-Key로 넘기세요. GitHub라면 X-GitHub-Delivery이고, 직접 만든 보내는 쪽이라면 "Any stable per-event UUID"(이벤트마다 안정적인 어떤 UUID든)입니다. 오케스트레이터는 소스와 키로 24시간 동안 중복을 걸러 내며, 반복된 요청에는 409로 답합니다.
{
"error": "idempotent_duplicate",
"original_stimulus_id": "stim-original-uuid"
}
7단계. 받은 오류 읽기
페이지의 오류 표를, 만날 가능성이 높은 순서로 정리했습니다.
401 missing_signature:X-Aegis-Signature헤더가 없습니다.400 malformed_signature: 헤더가sha256=<hex>형식이 아닙니다.400 invalid_hex: 16진수 부분에 다른 문자가 있습니다.401 secret_not_found: 결정된 테넌트와 소스에 대한 변수가 없습니다.401 invalid_signature: 다이제스트가 맞지 않습니다. 서명 전에 다시 직렬화된 본문이 페이지가 경고하는 경우입니다.409 idempotent_duplicate: 24시간 안에 이미 처리되었습니다.422 classification_failed: RouterAgent의 확신도가 기준보다 낮았습니다.422 no_router_configured: 직접 라우트도 RouterAgent도 없습니다.
8단계. 내부 호출자에게는 인증된 엔드포인트 쓰기
이미 Keycloak 계정이 있는 서비스는 공유 비밀 값 없이 bearer 토큰으로 POST /v1/stimuli에 보낼 수 있으며, 본문에 source, content, idempotency_key를 넣습니다. 웹훅 라우트는 여러분이 운영하지 않는 시스템을 위해 남겨 두세요.
문서가 답하지 않는 것
- 직접 라우트를 쓰는 방법. 페이지는 라우트가 오케스트레이터의 워크플로 라우팅 설정에 있다고만 하고 키, 파일, 예시를 주지 않으며, 이 단계에서 Configuration Reference로 연결하지도 않습니다.
- 웹훅을 등록하는 방법. 테넌트는 "is resolved from the (tenant, source_name) registration written when the webhook was registered"(웹훅을 등록할 때 기록된 (tenant, source_name) 등록에서 결정됩니다)라고 하지만, 그 등록은 페이지에 나오지 않습니다.
- 워크플로가 받는 것. 페이로드와 헤더가 시작된 워크플로의 입력이나 Blackboard에 어떻게 나타나는지 페이지는 말하지 않습니다.
- 비밀 값 교체. 겹치는 기간이 설명되어 있지 않고, 바뀐 변수가 오케스트레이터를 다시 시작하지 않아도 적용되는지도 나오지 않습니다.
다음
웹훅이 시작하는 워크플로는 여러분이 작성하는 상태 기계입니다. 가이드 AEGIS 워크플로의 상태와 전이, 단계별로가 그것을 하나 만듭니다. AEGIS는 100monkeys.ai에 적힌 명령으로 설치합니다.
curl -fsSL https://get.100monkeys.ai | bash