The CLV1 protocol is now publicRead the spec
Browse the library

Documentation librarySecurity

Alerts and channels

Alerts let Protect 24/7 tell you the moment an app sees something suspicious, by email, Discord, Telegram or a signed webhook. You set up the channels that receive messages, then write rules that decide which signals, how many, over what window, and where they go.

9 min read

Overview

Everything about alerts lives in Monitoring & alerts › Security, tab Alerts, and applies to the whole workspace: one set of channels and rules shared by all apps, with each rule optionally narrowed to a single app. The page has two parts:

  • Channels: where messages are delivered (email, Discord, Telegram, webhook).
  • Rules: the conditions for sending and the channels that receive.

Two more mechanisms run on their own with no setup: a built-in rule that emails owners and admins while the workspace has no rules, and a morning digest summarizing the last 24 hours.

Owners and Admins can add, delete, edit and test. Members can view the page, but the server refuses those actions for them.

Every alert names the app, the signal, its severity, the incident's event count with country and IP when known, and a link that opens the incident in App security › Protection. Message text is currently in English; an email subject looks like [HIGH] Your App: Someone is guessing keys.

Alert channels

KindPlanWhat you enter
EmailEvery planSend to: up to 10 addresses
DiscordPremiumDiscord webhook URL
TelegramPremiumBot token and Chat id
WebhookPremiumEndpoint URL; every message carries a Clavis-Signature

A workspace can have up to 20 channels.

Adding a channel

  1. Open Monitoring & alerts › Security, tab Alerts.
  2. In the Channels section, click Add channel. The New channel dialog opens.
  3. Pick the kind. On Basic, Discord, Telegram and Webhook are disabled with the note "Discord, Telegram and webhooks need Premium".
  4. Give it a Name (up to 60 characters). Left empty, the channel is named after its kind, for example "Email".
  5. Fill in the details for that kind, described below.
  6. Click Add channel. The dashboard confirms with "Channel added".

Details per kind:

  • Email: separate addresses with commas, semicolons or spaces. A shared on-call inbox such as oncall@example.com works well.
  • Discord: in Discord, create a webhook under the target channel's Integrations settings and paste its URL. It must start with https://discord.com/api/webhooks/ or https://discordapp.com/api/webhooks/. Messages arrive from "Protect 24/7", colored by severity.
  • Telegram: the Bot token is the one BotFather gave you, shaped like 123456789:AA.... The Chat id is the numeric chat id (groups usually have a negative id), or @ plus a public channel's username, such as @my_alerts. The bot must be in that group or channel and allowed to post.
  • Webhook: enter your system's Endpoint URL. Right after creation, the Channel signing secret dialog shows a secret starting with whsec_. It is shown once: copy it somewhere safe, then click Saved it.

Channel settings are encrypted at rest. Afterwards the list only shows a summary: the email addresses, the last few characters of a Discord URL, the Telegram chat id, or the webhook's host name. Channels cannot be edited. To change a URL, token or address, delete the channel and create a new one.

Testing and channel status

Each channel row is marked Verified or Not tested. Email channels are verified as soon as they are created. Discord, Telegram and webhook channels become Verified only after a delivery to them succeeds.

Click Test on a row to send a sample message: title "Test alert", low severity, app "ClavisAUH", with a link back to the Alerts page. The "Test sent" toast means the message was queued, not that it arrived. For Discord, Telegram and webhooks, reload the page after a few seconds: if the row still says Not tested, check the URL, token, chat id or your endpoint.

Deleting a channel

Click the trash icon (Delete channel) and confirm "Delete this channel?". The channel is removed from every rule that used it, and those rules keep sending to their remaining channels. A rule left with no channel sends nothing, and you need to pick a new channel for it before you can save changes to it.

Alert rules

A rule decides when a signal is worth interrupting you. Add rule is only clickable once the workspace has at least one channel. A workspace can have up to 50 rules.

Creating a rule

  1. In the Rules section, click Add rule. The New rule panel opens.
  2. Enter a Name (up to 80 characters), for example "Key guessing burst".
  3. App: choose Every app (which includes apps you create later) or a single app.
  4. Signals: tick the signal types to watch. Leave all unticked for every signal.
  5. From severity: Any, Low, Medium or High. Only signals at or above this level trigger the rule.
  6. Count and Within (min): the rule sends once at least N signals arrived in the last M minutes. The default is 1 within 5 minutes. Count ranges from 1 to 10,000 and Within (min) from 1 to 1,440 (24 hours).
  7. Send to: tick the channels, up to 10. The first channel in the list is preselected.
  8. Turn on Quiet hours if you want them (see below).
  9. Click Save rule. The dashboard confirms with "Rule saved".

Signals and severity

Every signal type has a fixed severity, which is what From severity and quiet hours look at. What each signal means is covered in Protect 24/7.

SignalSeverity
Forged signatureHigh
Replayed requestHigh
Clock driftMedium
Key brute-forcingHigh
Key sharingHigh
Impossible travelMedium
Session overflowMedium
Unknown buildHigh
Tampering detectedLow
Blocked by an access ruleLow
Offline clock rollbackMedium
Denial spikeMedium

How a rule is evaluated

Each time Protect 24/7 records a signal on an app in Advanced Protection, every enabled rule in the workspace (set to every app, or to that app) is checked in turn:

  1. The signal type is in the rule's Signals list, or the list is empty.
  2. The signal's severity meets From severity.
  3. If Count is above 1, ClavisAUH counts the signals of the selected types recorded within the last Within (min) minutes, on the rule's app (or the whole workspace), including the one that just arrived. Below the count, nothing is sent. This count does not filter by From severity.
  4. Each rule sends at most one message every 5 minutes. Signals that land in that pause are not sent later, but the next message carries the incident's latest event count.
  5. During quiet hours, anything below High severity is skipped.
  6. The message goes to every channel of the rule.

For example, a rule "Key guessing burst" with Key brute-forcing, Count 20, Within (min) 10 and the on-call Telegram channel sends one message once 20 or more guessing attempts land within 10 minutes, then at most one message every 5 minutes while the attack continues.

Worth knowing:

  • Several rules can match the same signal. Each sends its own message and keeps its own 5-minute pause.
  • To keep floods in check, Protect 24/7 records at most 30 events per minute for the same signal type from the same source. Anything beyond that is not recorded, so it does not count toward Count either.
  • When a workspace is no longer on Premium, its Discord, Telegram and webhook channels stay in the list but rules stop sending to them. Email channels keep working.

Quiet hours

Turn on Quiet hours and pick a start and end time, 23:00 → 07:00 by default. The range may cross midnight. The time zone is your browser's time zone when you save the rule, shown next to the time fields; saving the rule again from a machine in another time zone switches it to that one.

During quiet hours only High severity signals go out. Low and Medium signals are skipped, not held back for later. The morning digest still counts them.

Editing, pausing and deleting rules

Each rule row shows a summary of its conditions (signals, count and window, quiet hours), plus Last sent with a time, or Never sent.

  • Click the rule's name to open Edit rule, make changes, then click Save rule.
  • Flip the switch at the end of the row to pause or resume the rule without losing its settings.
  • Click the trash icon (Delete rule) and confirm "Delete this rule?".

The built-in rule: email to owners and admins

While the workspace has no rules, the Rules section shows The built-in rule is on. It needs no channel: whenever a new High severity incident opens on an app in Advanced Protection, every Owner and Admin of the workspace gets an email, at most once every 5 minutes. Repeat signals do not open new incidents, so they send no extra email: Protect 24/7 folds signals of the same type on the same subject (license, device or IP) into the open incident as long as no more than 30 minutes passed since the previous one.

The morning digest

Every day at 08:00 Vietnam time (UTC+7), ClavisAUH emails a Protect 24/7 summary to every Owner and Admin of each workspace that saw at least one signal in the last 24 hours, from an app in either mode. Quiet workspaces get no email. The digest contains:

  • The number of incidents opened and the total number of signals in the last 24 hours.
  • Up to 5 incidents with the most events among those active in that window, each with the app name, incident title and event count.
  • A button that opens Monitoring & alerts › Security.

The digest runs independently of channels, rules and quiet hours, and there is no separate setting to turn it off yet.

Alert webhooks and signatures

A webhook channel delivers each alert as a JSON POST to your endpoint with the headers Content-Type: application/json, User-Agent: ClavisAUH-Webhooks/1 and Clavis-Signature: t=<unix>,v1=<hex>.

{
  "type": "protect.alert",
  "createdAt": "2026-11-02T08:15:30.000Z",
  "data": {
    "app": "My App",
    "title": "Someone is guessing keys",
    "severity": "high",
    "detail": "24 event(s) · VN · 203.0.113.7",
    "link": "https://…/dashboard/apps/app_…/protect?incident=…"
  }
}

type is always protect.alert, severity is low, medium or high, and title is the signal's English title. A test message has the same shape, with title set to Test alert and severity to low.

Verifying the signature

v1 is the hex HMAC-SHA256 of <t>.<body>, where t is the signing time in Unix seconds, body is the raw request body exactly as received, and the key is the channel's full whsec_… secret. Verify against the raw body before parsing JSON, compare in constant time, and reject old timestamps to stop replays.

import { createHmac, timingSafeEqual } from "node:crypto";
import express from "express";

// Clavis-Signature: t=<unix>,v1=<hex>
export function verifyAlert(rawBody, header, secret) {
  const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
  if (!parts.t || Math.abs(Date.now() / 1000 - Number(parts.t)) > 300) return false; // 5 minutes of tolerance
  const expected = createHmac("sha256", secret).update(`${parts.t}.${rawBody}`).digest();
  const received = Buffer.from(parts.v1 ?? "", "hex");
  return received.length === expected.length && timingSafeEqual(received, expected);
}

// Express: keep the raw body for verification
const app = express();
app.post("/hooks/clavis-alerts", express.raw({ type: "application/json" }), (req, res) => {
  const raw = req.body.toString("utf8");
  if (!verifyAlert(raw, req.get("clavis-signature") ?? "", process.env.CLAVIS_ALERT_SECRET)) {
    return res.sendStatus(401);
  }
  const alert = JSON.parse(raw); // alert.data.severity, alert.data.link
  res.sendStatus(204);
});
import hashlib, hmac, time

def verify_alert(raw_body: bytes, header: str, secret: str) -> bool:
    parts = dict(p.split("=", 1) for p in header.split(","))
    t = parts.get("t", "")
    if not t.isdigit() or abs(time.time() - int(t)) > 300:
        return False
    expected = hmac.new(secret.encode(), t.encode() + b"." + raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, parts.get("v1", ""))

What your endpoint must do

  • Answer with a 2xx status within 10 seconds. Redirects (3xx) are not followed and count as a failure.
  • Use https and resolve to the public internet. Private network addresses and localhost are refused at send time.
  • Each alert to Discord, Telegram or a webhook is sent once, with no retries, unlike the app's event webhooks. For alerts that matter, add an email channel to the rule as well.
  • The signing secret cannot be shown again. If you lose it, delete the channel, create a new one and update the secret on the receiving side.

How can we help?

Frequently asked

What is ClavisAUH?

A licensing and authentication service for desktop software: license keys, device binding, end-user sign-in and signed responses. All run from one dashboard

Why can't a cracker fake your server?

The SDK only trusts answers signed with your app's private key. That key never leaves our servers, so a fake server can't sign and gets rejected

What ends up in my binary?

Your app id and public key. Nothing secret, because each device makes its own key pair on first run

Does it work offline?

Yes. Every plan gets a signed offline token for 7 days, and your app checks it locally. Premium lets you choose how long. A revoked license can keep working offline for that time, unless Online-Only Mode is on

Which languages are supported?

JavaScript and TypeScript today. C++, C# and Python are next, on the same open protocol

Is Basic really free?

Yes. No time limit, no card. Move to Premium when you need more

Get in touch