방문자가 구독하게 하기

공개 상태 페이지 방문자는 페이지를 계속 확인하는 대신 일이 생길 때 알림을 받을 수 있습니다. 게시된 인시던트 업데이트와, 예약되거나 시작되거나 끝나거나 취소된 점검 시간마다 다음 세 가지 방법 중 하나로 전달됩니다.

  • 이메일: 페이지의 양식에서 신청합니다.

  • 서명된 웹훅: 이벤트를 자기 도구로 받고 싶은 팀을 위한 방법입니다.

  • Atom 피드: 페이지 옆의 feed.atom으로, 어떤 피드 리더에서든 볼 수 있습니다.

초안은 전송되지 않습니다. 모니터의 초안 인시던트는 사용자가 게시할 때에만 구독자에게 전달됩니다. 페이지에서 따로 켤 것은 없습니다. Mocco 서버가 이메일을 보낼 수 있으면 양식이 나타납니다.

1. 이메일로 구독하기

페이지의 컴포넌트 아래에 Get updates 양식이 있습니다. 방문자는 주소를 입력하고, Only some components를 열어 자신이 쓰는 서비스 부분의 소식만 받도록 정할 수 있습니다. 특정 컴포넌트와 무관한 인시던트와, 어떤 컴포넌트도 포함하지 않는 점검은 모든 구독자에게 전달됩니다.

컴포넌트 아래에 Get updates 양식이 있는 공개 상태 페이지

방문자에게 받은편지함을 확인하라는 안내가 나옵니다.

신청 후의 양식: 받은편지함에서 확인 링크를 확인하세요

주소의 주인이 확인 메일의 링크를 열기 전까지는 그 주소로 아무것도 전송되지 않습니다(double opt-in). 링크는 7일 동안 유효하고, 확인되지 않은 신청은 그 후에 삭제됩니다. 링크를 열면 구독이 확인됩니다.

확인 링크가 여는 페이지: 구독되었습니다

그 뒤로 모든 메일에는 무엇이 바뀌었는지(인시던트와 그 상태, 또는 점검 시간과 UTC 기준 시각), 영향받는 컴포넌트가 적히고, 마지막에 Unsubscribe 링크가 붙습니다. 메일 클라이언트가 제공하는 구독 해지 버튼도 한 번의 클릭으로 동작합니다. 구독자는 영어 또는 한국어 메일을 선택할 수 있습니다. 양식은 방문자를 영어로 등록합니다.

양식은 JavaScript 없이도 동작하고, Mocco에 연결할 수 없을 때는 "We couldn't sign you up right now. Try again later."라고만 안내합니다. 페이지의 나머지는 계속 동작합니다. 페이지는 양식에 의존하지 않습니다.

같은 주소로 다시 신청하면 10분 안에는 확인 메일을 다시 보내지 않으며, 이미 페이지를 구독 중인 주소는 그대로 둡니다. 그래서 다른 사람의 선택을 바꿀 수 없습니다. 양식은 어떤 경우에도 같은 응답을 하므로 누가 페이지를 구독하는지 알아내는 데 쓸 수 없습니다. 신청은 방문자별, 주소별로 횟수가 제한됩니다.

2. 웹훅으로 구독하기

웹훅은 양식이 아니라 요청으로 신청합니다.

curl -X POST https://<your Mocco host>/api/ext/v1/status-pages/<page address>/subscribers \
  -H 'Content-Type: application/json' \
  -d '{ "channel": "webhook", "url": "https://hooks.example.com/mocco-status" }'

일부 컴포넌트의 소식만 받으려면 "componentIds": ["…"]를 추가하세요. ID는 페이지의 snapshot.json에 있습니다. 응답에는 웹훅의 signing secret이 들어 있고, 이때 한 번만 보입니다.

{ "status": "pending_confirmation", "secret": "whsec_…" }

수신기가 읽을 수 있는 곳에 저장하세요. Mocco는 이를 암호화해 보관하며 다시 보여 주지 않습니다. 교체하려면 해당 URL의 구독을 해지하고 다시 신청하세요.

URL은 https여야 하고, 사용자 이름이나 비밀번호를 포함하지 않아야 하며, 공개 주소를 가리켜야 합니다. Mocco는 신청할 때와 연결할 때마다 사설, 루프백, 링크 로컬, 클라우드 메타데이터 주소를 거부하며 리디렉션은 따라가지 않습니다.

확인하기

URL이 처음 받는 이벤트는 subscription.confirmation입니다. 그 이벤트의 links.confirm URL을 열면(수신기에서 GET을 보내거나 직접 열기) 구독이 시작됩니다. 그 전까지 URL은 다른 이벤트를 받지 않습니다.

이벤트 형태

모든 이벤트는 JSON 본문이 있는 POST입니다.

{
  "type": "incident.updated",
  "sentAt": "2026-10-06T09:00:00.000Z",
  "page": { "slug": "acme", "title": "Acme Status" },
  "data": {
    "incidentTitle": "Elevated errors",
    "status": "identified",
    "body": "A bad deploy. Rolling back.",
    "components": ["API"],
    "postedAt": "2026-10-06T09:00:00.000Z"
  },
  "links": { "unsubscribe": "https://…/subscribers/unsubscribe?token=…" }
}

type은 subscription.confirmation, incident.updated, maintenance.updated 중 하나입니다. 점검 이벤트의 data에는 점검의 title, status(scheduled, in_progress, completed, canceled), body, components, scheduledStart, scheduledEnd가 들어 있습니다.

아무 2xx로나 응답하세요. Mocco는 타임아웃, 408, 429, 5xx를 백오프로 재시도합니다(8회). 그 외의 4xx나 리디렉션은 재시도하지 않으며, 410 Gone은 해당 URL의 구독을 해지합니다. 각 이벤트는 최대 한 번만 전송되고, 재시도해도 webhook-id가 같으므로 중복을 쉽게 걸러낼 수 있습니다.

서명 확인하기

이벤트는 Standard Webhooks를 따릅니다. webhook-id, webhook-timestamp(유닉스 초), 그리고 id.timestamp.body의 HMAC-SHA256을 base64로 인코딩한 webhook-signature: v1,<base64 HMAC-SHA256>가 헤더에 담기며, 키는 시크릿에서 whsec_ 접두사를 뺀 값입니다. Node에서는 다음과 같이 확인합니다.

import { createHmac, timingSafeEqual } from 'node:crypto';

function isFromMocco(headers: Record<string, string>, rawBody: string, secret: string): boolean {
  const key = Buffer.from(secret.slice('whsec_'.length), 'base64');
  const signed = `${headers['webhook-id']}.${headers['webhook-timestamp']}.${rawBody}`;
  const expected = Buffer.from(`v1,${createHmac('sha256', key).update(signed).digest('base64')}`);
  const given = Buffer.from(headers['webhook-signature'] ?? '');
  const isFresh = Math.abs(Date.now() / 1000 - Number(headers['webhook-timestamp'])) < 300;
  return isFresh && expected.length === given.length && timingSafeEqual(expected, given);
}

본문은 파싱하기 전에, 도착한 그대로 검증하세요.

3. 구독 해지하기

모든 메일과 이벤트에는 구독 해지 링크가 들어 있습니다. 링크를 열면 먼저 확인을 묻는 페이지가 나타나므로, 링크를 여는 메일 스캐너가 구독을 해지하는 일은 없습니다. 그 페이지의 버튼, 메일 클라이언트의 원클릭 구독 해지, 웹훅 수신기에서 링크로 보내는 POST 중 하나로 구독이 끝납니다. 이전 확인 링크로는 해지된 주소를 되살릴 수 없습니다. 방문자가 페이지에서 다시 신청해야 합니다.

4. 자체 호스팅

호스팅된 Mocco는 모든 것을 대신 보내 줍니다. 자체 호스팅 서버에는 다음이 필요합니다.

용도설정
이메일(그리고 페이지의 양식)EMAIL_DRIVER=smtp, EMAIL_SMTP_URL(smtp://user:pass@host:587 또는 smtps://…:465), EMAIL_FROM(Acme Status <status@acme.example>). 설정하지 않으면 페이지에 양식이 없고 이메일 신청은 503으로 응답합니다.
웹훅각 웹훅의 시크릿을 봉인하는 SECRETS_ENCRYPTION_KEYS. 설정하지 않으면 웹훅 신청은 503으로 응답합니다.
둘 다모든 확인 링크와 구독 해지 링크에 서명하는 AUTH_SECRET.

개발 환경에서는 EMAIL_DRIVER=log가 메일을 보내는 대신 링크를 포함한 각 메일을 서버 로그에 기록합니다.