Zum Hauptinhalt springen
Automatisierungen sind die Teile einer App, die eigenständig laufen — nach einem Zeitplan oder wenn ein externer Service ein Ereignis sendet. Sie deklarieren sie in einer Process-Komponente in kazzle.config.ts. Eine Komponente kann beliebig viele Trigger haben.

Die Struktur

{
  name: 'events',
  type: 'process',
  path: './components/events/index.ts',
  processMode: 'persistent', // oder 'triggered'
  triggers: [
    { name: 'daily-digest', kind: 'schedule', schedule: '0 9 * * *', path: '/cron/daily-digest' },
    { name: 'stripe',       kind: 'webhook',                          path: '/webhook/stripe' },
  ],
}
Zwei Dinge passieren hier:
  • processMode bestimmt den Lebenszyklus — Langzeitserver oder einmalige Ausführung pro Trigger.
  • triggers[] listet die Ereignisse auf, die diese Komponente auslösen sollen.
Die beiden Teile sind unabhängig. Ein persistenter Server kann einen Cron haben. Ein kurzlebiger Prozess kann einen Webhook haben. Wählen Sie den Lebenszyklus, der zu Ihrer Workload passt, und fügen Sie dann beliebig viele Trigger hinzu.

processMode

ModusWas läuftWann verwenden
persistent (Standard)Ein Langzeit-HTTP-Server. Trigger werden darin gepostet.Die Komponente bedient bereits HTTP oder hält Zustand im Speicher (Warteschlangen, Websockets, Caches).
triggeredDas Entry-Skript wird pro Trigger gestartet und beendet.Reine Hintergrund-Jobs — nächtliche Bereinigung, einzelner Stripe-Webhook-Handler usw. Keine untätigen Server.

Trigger

Jeder Trigger hat einen name (eindeutig innerhalb der Komponente), eine kind und — je nach Modus — einen schedule und/oder path.
FeldErforderlich wennHinweise
nameimmerWird als Webhook-URL-Segment und in Logs verwendet. Kebab-Case.
kindimmer'schedule' oder 'webhook'.
schedulewenn kind: 'schedule'5-Feld-Cron-Ausdruck. Minutenauflösung ist das Minimum.
pathwenn processMode: 'persistent'HTTP-Route auf Ihrem Server, wo der Trigger ankommt.

Persistent-Modus — HTTP zum Server

Wenn ein Trigger für eine persistente Komponente ausgelöst wird, postet Kazzle zu Ihrem Server unter dem deklarierten path. Die Anfrage enthält:
HeaderWas er aussagt
Authorization: Bearer ${KAZZLE_TRIGGER_SECRET}Validieren Sie dies. Lehnen Sie Aufrufe ab, die nicht übereinstimmen.
x-kazzle-trigger-nameDer name des Triggers aus dem Manifest.
x-kazzle-trigger-run-idOpaque ID für Log-Korrelation.
x-kazzle-triggered-bycron | webhook | manual.
Bei Webhook-Triggern wird der ursprüngliche Request-Body als POST-Body weitergeleitet. Bei Schedule-Triggern ist der Body leer.
// components/events/index.ts (persistent mode)
const TRIGGER_SECRET = process.env.KAZZLE_TRIGGER_SECRET ?? '';

Bun.serve({
  port: Number(process.env.PORT),
  hostname: process.env.HOST,
  async fetch(req) {
    const url = new URL(req.url);

    if (req.method === 'POST' && url.pathname === '/cron/daily-digest') {
      if (req.headers.get('authorization') !== `Bearer ${TRIGGER_SECRET}`) {
        return new Response('Unauthorized', { status: 401 });
      }
      await sendDigest();
      return Response.json({ ok: true });
    }

    if (req.method === 'POST' && url.pathname === '/webhook/stripe') {
      if (req.headers.get('authorization') !== `Bearer ${TRIGGER_SECRET}`) {
        return new Response('Unauthorized', { status: 401 });
      }
      const event = await req.json();
      await handleStripe(event);
      return Response.json({ ok: true });
    }

    return new Response('not found', { status: 404 });
  },
});

Triggered-Modus — einmalig pro Trigger

Wenn ein Trigger für eine triggered-Komponente ausgelöst wird, startet Kazzle das Entry-Skript neu und wartet auf seinen Abschluss. Es gibt keinen path; das Skript erfährt, welcher Trigger ausgelöst wurde, über Umgebungsvariablen.
UmgebungsvariableWert
TRIGGER_NAMEDer name des Triggers aus dem Manifest.
TRIGGERED_BYcron | webhook | manual.
RUN_IDOpaque ID für Log-Korrelation.
WEBHOOK_PAYLOADJSON-Body (nur Webhook-Trigger).
// components/events/index.ts (triggered mode)
const trigger = process.env.TRIGGER_NAME;
const runId = process.env.RUN_ID;

if (trigger === 'daily-digest') {
  await sendDigest();
} else if (trigger === 'stripe') {
  const event = JSON.parse(process.env.WEBHOOK_PAYLOAD ?? '{}');
  await handleStripe(event);
}

console.log(`run ${runId} done`);
Triggered-Komponenten haben keine untätigen Maschinen in der Produktion — sie starten pro Aufruf und fahren beim Beenden herunter.

Webhook-URLs

POST https://api.kazzle.app/webhooks/{spaceId}/{appId}/{componentName}/{triggerName}
Das triggerName-Segment muss einem kind: 'webhook'-Eintrag in der triggers[] dieser Komponente entsprechen. Unbekannte Trigger-Namen geben 404 zurück.

Zeitplan-Auflösung

Cron-Ausdrücke sind 5-Feld (Minute, Stunde, Tag des Monats, Monat, Wochentag) und Minutenauflösung ist das Minimum. Sub-Minuten-Zeitpläne werden bei der Manifest-Validierung abgelehnt.

Wie Läufe aufgezeichnet werden

Jeder Trigger-Auslöser schreibt eine process_runs-Zeile mit trigger_name, triggered_by, run_id und dem Exit-Status des Laufs. Sie können diese aus Ihrem eigenen Code abfragen oder in der Runs-Ansicht der App inspizieren.

Guthaben aufgebraucht

Ein fehlgeschlagener Lauf wird aufgezeichnet und protokolliert, aber der Zeitplan läuft weiter nach seinem normalen Rhythmus — ein fehlerhafter Lauf deaktiviert den Trigger nie. Das Einzige, das einen Lauf stoppt, ist das Guthaben: Jeder Trigger-Auslöser wird gegen den Kontostand des Space überprüft, und während der Space kein Guthaben hat (oder keine Abrechnung eingerichtet hat), werden Läufe mit 402 übersprungen. Dies ist selbstheilend — der Zeitplan bleibt aktiv und der nächste Auslöser nach dem Aufladen läuft normal, ohne manuelles Fortsetzen.

Automatisierungen später hinzufügen

Eine einfache App kann ohne Trigger starten und sie später gewinnen — fügen Sie eine tägliche Zusammenfassung hinzu, verbinden Sie Stripe, führen Sie Bereinigung aus. Der Lebenszyklus der Komponente (processMode) und Trigger (triggers[]) sind unabhängig, sodass Sie sie ändern können, ohne den Rest der App umzuschreiben.