Soft-Kill-Webhooks
Zuletzt aktualisiert: November 2026
Was ein Soft Kill ist
Mit einem Soft Kill regelt sich Ihr eigener Dienst herunter, bevor die Ausgaben aus dem Ruder laufen (eine teure Funktion abschalten, Worker herunterskalieren, auf eine Wartungsseite umschalten), ohne die Abrechnung zu trennen. Wird ein Budget-Schwellenwert überschritten, dem Sie ihn zugeordnet haben, sendet FireShield eine signierte POST-Anfrage an einen HTTPS-Endpunkt, den Sie kontrollieren. Was danach passiert, bestimmt allein Ihr Code.
Wenn Sie stattdessen einen garantierten Stopp brauchen, nutzen Sie einen Hard Kill, der das Rechnungskonto des Projekts trennt.
1. In FireShield einrichten
- Öffnen Sie das Budget Ihres Projekts (Konfigurieren) und wählen Sie beim gewünschten Schwellenwert + Aktion hinzufügen → Soft Kill (Webhook).
- Geben Sie die URL Ihres Endpunkts ein. Sie muss HTTPS verwenden.
- FireShield erzeugt dafür einen Signaturschlüssel. Klicken Sie bei der Aktion auf Geheimen Schlüssel anzeigen, um ihn zu sehen, und speichern Sie ihn als Secret in Ihrem Dienst (zum Beispiel
FIRESHIELD_WEBHOOK_SECRET). Falls er jemals durchsickert, klicken Sie dort auf Neu generieren: Der alte Schlüssel funktioniert sofort nicht mehr, also aktualisieren Sie Ihren Dienst mit dem neuen. - Klicken Sie auf Speichern, damit das Budget synchronisiert wird. Ein Endpunkt kann bei anderen Schwellenwerten desselben Projekts wiederverwendet werden.
- Sie möchten schrittweise herunterregeln? Fügen Sie mehreren Schwellenwerten einen Soft Kill hinzu (etwa 50 %, 80 % und 100 %) und verzweigen Sie in Ihrem Handler nach
thresholdPercent.
2. Die Anfrage
POST /fireshield/soft-kill HTTP/1.1
Content-Type: application/json
X-FireShield-Timestamp: 1790899412
X-FireShield-Signature: 5f2b1c…e9a0
{
"event": "soft_kill",
"cloudProjectId": "e3e0cc6f0146409c8d54ce43c0757294",
"gcpProjectId": "firewand-prod",
"thresholdPercent": 90,
"costAmount": 90.12,
"budgetAmount": 100,
"currency": "USD",
"reason": "FireWand hit 90% of budget (90.12 / 100 USD)",
"trigger": "BUDGET",
"timestamp": "1790899412"
}Header:
X-FireShield-Timestamp: Unix-Zeit in Sekunden, zu der die Anfrage signiert wurde.X-FireShield-Signature: Hex-HMAC-SHA256 von{timestamp}.{raw body}, mit Ihrem Signaturschlüssel als Schlüssel.
Body (JSON):
event:"soft_kill", wenn ein Schwellenwert überschritten wird, oder"test", wenn Sie in FireShield auf Test senden klicken (siehe unten). Ignorieren Sie Ereignisse, die Sie nicht kennen; antworten Sie einfach mit 2xx.cloudProjectId: Die ID von FireShield für das Projekt, nicht Ihre GCP-Projekt-ID. Nutzen Sie sie, um Projekte zu unterscheiden, wenn ein Endpunkt mehrere bedient.gcpProjectId: Ihre Google-Cloud-Projekt-ID.trigger:"BUDGET", wenn ein Budget-Schwellenwert ausgelöst hat,"ANOMALY", wenn eine Regel für Nutzungsspitzen ausgelöst hat (Pro).thresholdPercent: Der ausgelöste Budget-Schwellenwert als Prozentsatz (z. B.90). Jeder Schwellenwert mit Soft Kill sendet eine eigene Anfrage, nutzen Sie ihn also, um zu entscheiden, wie stark heruntergeregelt wird. Bei einer Nutzungsspitze ist er immer100, sodass ein Handler, der nach Schwellenwert abstuft, seine stärkste Einschränkung anwendet.costAmount: Die bisherigen Ausgaben in diesem Budgetzeitraum, wie von Google gemeldet.nullbei einer Nutzungsspitze: Die Anomalieerkennung misst Nutzung, nicht Kosten.budgetAmount: Der Budgetbetrag für den Zeitraum.nullbei einer Nutzungsspitze.currency: ISO-4217-Code beider Beträge, z. B."USD".nullbei einer Nutzungsspitze.anomaly: Nur bei einer Nutzungsspitze:service(z. B."Firestore"),metric,current(Nutzung in der letzten Stunde),baseline(durchschnittliche stündliche Nutzung der letzten 7 Tage) und dermultiplierder Regel. Nutzen Sie es, um nur den Dienst herunterzuregeln, bei dem die Spitze auftrat.reason: Ein menschenlesbarer Satz für Ihre Logs, z. B. welcher Schwellenwert erreicht wurde und die Ausgaben. Parsen Sie ihn nicht; der Wortlaut kann sich ändern.timestamp: Derselbe Wert wie im Header, als String.
Ein Soft Kill, der von einer Regel für Nutzungsspitzen gesendet wird, sieht so aus:
{
"event": "soft_kill",
"trigger": "ANOMALY",
"cloudProjectId": "e3e0cc6f0146409c8d54ce43c0757294",
"gcpProjectId": "firewand-prod",
"thresholdPercent": 100,
"costAmount": null,
"budgetAmount": null,
"currency": null,
"anomaly": {
"service": "Cloud Firestore",
"metric": "firestore.reads",
"current": 1200000,
"baseline": 100000,
"multiplier": 10
},
"reason": "FireWand: Firestore document reads at ×12 normal (1.2M document reads in the last hour, usually 100K document reads). Rule: ×10.",
"timestamp": "1790899412"
}3. Verifizieren
Bevor Sie auf eine Anfrage reagieren, prüfen Sie beides und lehnen Sie sie andernfalls mit 401 ab:
- Aktualität: Der Zeitstempel liegt innerhalb von 5 Minuten Ihrer Uhrzeit. Das verhindert, dass eine abgefangene Anfrage später erneut abgespielt wird.
- Signatur: Berechnen Sie den HMAC über den Zeitstempel, einen Punkt und den rohen Request-Body genau so, wie er empfangen wurde neu und vergleichen Sie ihn in konstanter Zeit mit dem Header. Erneutes Serialisieren von geparstem JSON verändert die Bytes und bricht die Signatur.
// Firebase Cloud Function (2nd gen). The same logic works in Express:
// sign over the raw body bytes, never over a re-serialized JSON object.
import { onRequest } from "firebase-functions/v2/https";
import { createHmac, timingSafeEqual } from "node:crypto";
const SECRET = process.env.FIRESHIELD_WEBHOOK_SECRET!;
const MAX_AGE_SECONDS = 5 * 60;
export const fireshieldSoftKill = onRequest(async (req, res) => {
const timestamp = req.get("X-FireShield-Timestamp") ?? "";
const signature = req.get("X-FireShield-Signature") ?? "";
const rawBody = req.rawBody.toString("utf8");
// 1. Reject stale or replayed requests.
const age = Math.abs(Date.now() / 1000 - Number(timestamp));
if (!timestamp || Number.isNaN(age) || age > MAX_AGE_SECONDS) {
res.status(401).send("Stale request");
return;
}
// 2. Recompute the signature and compare in constant time.
const expected = createHmac("sha256", SECRET).update(`${timestamp}.${rawBody}`).digest("hex");
const a = Buffer.from(expected, "hex");
const b = Buffer.from(signature, "hex");
if (a.length !== b.length || !timingSafeEqual(a, b)) {
res.status(401).send("Bad signature");
return;
}
// 3. Degrade your service, then answer fast with a 2xx.
const event = JSON.parse(rawBody);
if (event.event === "soft_kill") {
if (event.trigger === "ANOMALY") {
// A usage spike in one service. thresholdPercent is always 100 here,
// so degrade only the service in event.anomaly.service (for example
// "Cloud Firestore" or "Cloud Storage"), not the whole app.
await degradeService(event.anomaly.service); // your code
} else {
// "BUDGET": each threshold with a Soft Kill sends its own request,
// so you can step down gradually as more of the budget is spent.
if (event.thresholdPercent >= 100) await showMaintenancePage(); // your code
else if (event.thresholdPercent >= 80) await disableExpensiveFeatures();
else await pauseBatchJobs();
}
}
res.status(204).send();
});4. Antworten
Antworten Sie mit einem beliebigen 2xx-Status, sobald Sie die Anfrage angenommen haben; erledigen Sie langsame Arbeit asynchron. Jeder andere Status oder ein Netzwerkfehler wird als fehlgeschlagene Aktion erfasst und erscheint in den letzten Protokollen Ihres Projekts.
5. Zustellungsgarantien
- Jede Soft-Kill-Aktion löst einmal pro Budgetzeitraum (Kalendermonat) aus, wenn die von Google gemeldeten Ausgaben erstmals ihren Schwellenwert überschreiten. Eine Änderung des Budgetbetrags setzt sie zurück, sodass sie erneut auslöst, wenn der neue Schwellenwert überschritten wird.
- Fehlgeschlagene Zustellungen werden wiederholt, aber nicht aus einer eigenen Warteschlange: Jede Anfrage ist ein einzelner Versuch, der bis zu 15 Sekunden wartet, ohne sofortige Wiederholungen oder Backoff. Der nächste Versuch kommt mit den nächsten Daten. Bei einem Budget ist das die nächste Kostenaktualisierung von Google (meist etwa jede halbe Stunde). Bei einer Nutzungsspitze ist es die nächste Prüfung (alle 5 Minuten), und nur solange die Nutzung die Regel noch erfüllt: Eine Spitze, die endet, bevor eine Zustellung gelingt, wird nicht erneut gesendet. Antworten Sie mit
2xx, sobald Sie die Anfrage verarbeitet haben, sonst erhalten Sie sie weiterhin. - Google veröffentlicht Kostendaten mit einer Verzögerung von bis zu mehreren Stunden, daher trifft die Anfrage einige Zeit nach dem tatsächlichen Überschreiten des Schwellenwerts ein.
- Verarbeiten Sie die Anfrage trotzdem idempotent: Den eingeschränkten Modus zweimal zu aktivieren, sollte unschädlich sein. Eine anhaltende Nutzungsspitze sendet sie außerdem bei jeder Prüfung erneut.
6. Ihren Endpunkt testen
Am schnellsten geht es so: Klicken Sie im Budget Ihres Projekts unter der Soft-Kill-Aktion auf Test senden. FireShield sendet eine signierte Anfrage genau wie eine echte, mit dem thresholdPercent dieses Schwellenwerts und event auf "test" gesetzt, sodass ein Handler, der nur auf "soft_kill" reagiert, nichts herunterregelt. Sie erfahren sofort, ob Ihr Endpunkt nicht erreichbar ist, die Signatur ablehnt oder nicht mit 2xx antwortet.
Oder senden Sie sich selbst eine korrekt signierte Anfrage mit dem Schlüssel, den FireShield Ihnen gegeben hat:
SECRET='your-signing-secret'
TS=$(date +%s)
BODY='{"event":"soft_kill","cloudProjectId":"test","gcpProjectId":"test","thresholdPercent":90,"costAmount":90,"budgetAmount":100,"currency":"USD","reason":"Manual test","timestamp":"'$TS'"}'
SIG=$(printf '%s.%s' "$TS" "$BODY" | openssl dgst -sha256 -hmac "$SECRET" | sed 's/^.* //')
curl -i -X POST https://your-app.example.com/fireshield/soft-kill \
-H "Content-Type: application/json" \
-H "X-FireShield-Timestamp: $TS" \
-H "X-FireShield-Signature: $SIG" \
--data "$BODY"