Aller au contenu principal
Version: 1.0.0

Triggers gouvernés

Un trigger Touba transforme un événement en exécution gouvernée.

Deux chemins V1 sont disponibles :

Forme disponible
schedule -> strategy
request -> strategy

Le premier utilise un runner foreground borné. Le second utilise un service webhook HTTP explicite, avec queue persistante, état TD-1 et trace. Les deux chemins passent par AIR, manifest et vérifications runtime avant d’exécuter la stratégie.

1. Écrire le programme​

src/examples/daily_research.tb
module examples.daily_research

strategy DailyResearch
takes topic as Text
do
show
with message as "Daily research run: " ++ topic

trigger DailyResearchSchedule
on schedule "every 1s"
runs strategy DailyResearch
with topic as "daily review"
limits
max_runs_per_day as 1
timeout_ms as 30000

trigger DailyResearchWebhook
on request POST "/webhook/daily-research"
runs strategy DailyResearch
with topic as payload.topic
limits
max_runs_per_day as 10
timeout_ms as 30000

Le runner accepte ce format :

Standard
every <n>s
every <n>m
every <n>h

Le support cron complet appartient à une couche de planification ultérieure.

2. Compiler​

Terminal
touba compile src/examples/daily_research.tb --out daily_research.air.json

L’AIR contient la déclaration du trigger, sa cible et ses limites. Il ne contient pas de scheduler, de secret ou de payload externe. payload.topic est un binding dynamique du champ de l’événement, pas une valeur factice ni une valeur de repli. Le mapping du manifest doit couvrir ce même argument ; un champ manquant est refusé avant l’exécution.

3. Activer le trigger dans le manifest​

touba.manifest.json
{
"manifest_version": "touba-manifest-v1",
"bindings": {},
"triggers": {
"trigger:examples.daily_research/DailyResearchSchedule": {
"enabled": true,
"source": {
"kind": "schedule"
},
"limits": {
"max_runs_per_day": 1,
"timeout_ms": 30000
}
},
"trigger:examples.daily_research/DailyResearchWebhook": {
"enabled": true,
"source": {
"kind": "request"
},
"payload": {
"format": "json",
"content_types": ["application/json"],
"max_bytes": 4096,
"map": {
"topic": "$.topic"
}
},
"limits": {
"max_runs_per_day": 10,
"timeout_ms": 30000
}
}
}
}

Sans cette entrée, le runtime refuse le trigger avant d’exécuter la stratégie.

4. Lancer le trigger réel​

Terminal
touba trigger run daily_research.air.json \
--manifest touba.manifest.json \
--trigger trigger:examples.daily_research/DailyResearchSchedule \
--max-runs 1 \
--state touba.triggers.sqlite \
--trace-out daily_research.trace.json

Résultat attendu après environ une seconde :

Sortie
Daily research run: daily review

--max-runs fixe le nombre d’exécutions. Le registre SQLite par défaut est .touba/triggers.sqlite, dans le projet ou à côté de l’AIR hors projet. --state permet de choisir un autre fichier .sqlite.

La trace écrite dans daily_research.trace.json contient l’événement reçu, l’acceptation du trigger, le début d’exécution et la fin du run.

Le registre SQLite mémorise les event_id consommés, les refus observés et la clé de jour UTC utilisée pour appliquer max_runs_per_day, sans payload brut. Si un même événement est rejoué, Touba refuse avant de relancer la stratégie avec TriggerReplayRejected. Une acceptation enregistrée un jour précédent ne bloque pas le premier événement du jour courant. Le chemin passé à --state doit se terminer par .triggers.sqlite.

5. Lancer le service webhook​

Terminal
touba trigger serve daily_research.air.json \
--manifest touba.manifest.json \
--state touba.triggers.sqlite \
--queue touba.trigger-queue.sqlite \
--trace-dir traces \
--max-events 1

Dans un autre terminal :

Terminal
curl -X POST http://127.0.0.1:8787/webhook/daily-research \
-H 'content-type: application/json' \
-H 'x-touba-event-id: evt-webhook-001' \
-d '{"topic":"industrial adoption"}'

Le service répond 202 queued, inscrit une entrée dans touba.trigger-queue.sqlite, exécute la stratégie, écrit l’état TD-1 et produit une trace par entrée si --trace-dir est fourni. Le corps HTTP brut n’est pas injecté comme input Touba implicite et n’est pas écrit dans la queue, l’état ou la trace ; la queue conserve la taille, le digest SHA-256 du payload et les arguments explicitement mappés par le manifest. Les champs non listés dans payload.map sont ignorés.

Pour exposer ce webhook à un fournisseur externe, ajouter --signature-secret-env TOUBA_TRIGGER_SIGNATURE_SECRET et envoyer x-touba-signature: sha256=<hmac> calculé sur le corps HTTP exact.

Sur une interface non loopback, ajouter --auth-token-env TOUBA_TRIGGER_TOKEN et envoyer Authorization: Bearer <token> ou x-touba-trigger-token: <token>.

6. Suivre le statut des triggers​

Terminal
curl http://127.0.0.1:8787/status

Le service répond avec touba-trigger-status-v1. Cette sortie donne une vue compacte des triggers déclarés : type request ou schedule, statut, limites, compteurs d’acceptation/refus et état de queue. Elle ne contient ni token d’authentification, ni secret HMAC, ni payload HTTP brut. Quand le service est protégé par token, /status demande la même authentification que les routes webhook.

7. Tester les checks sans exécuter​

Terminal
touba run daily_research.air.json \
--manifest touba.manifest.json \
--trigger-event-json '{"trigger_ref":"trigger:examples.daily_research/DailyResearchSchedule","event_id":"evt-001","source_kind":"schedule"}' \
--trigger-dry-run

Résultat attendu :

  • le trigger existe dans l’AIR ;
  • le manifest l’active ;
  • la source événementielle correspond ;
  • les limites du manifest n’élargissent pas celles de l’AIR ;
  • la stratégie cible n’est pas lancée en dry-run.

8. Exécuter avec un événement explicite​

Terminal
touba run daily_research.air.json \
--manifest touba.manifest.json \
--trigger-event-json '{"trigger_ref":"trigger:examples.daily_research/DailyResearchSchedule","event_id":"evt-002","source_kind":"schedule"}' \
--output trace

Ce mode sert aux tests d’hôte ou d’adapter. Il fournit l’événement à la main au lieu d’attendre l’intervalle déclaré.

9. Inspecter​

Terminal
touba inspect daily_research.air.json
touba inspect trace.json
touba trigger state touba.triggers.sqlite --trigger trigger:examples.daily_research/DailyResearchSchedule
touba inspect daily_research.air.json --manifest touba.manifest.json

L’inspection affiche ce qui existe dans les artefacts fournis. Pour l’état durable de trigger (TD-1), elle affiche les compteurs d’acceptations, de refus et d’event_id consommés. Le service webhook existe quand il est lancé explicitement par touba trigger serve ; la supervision de déploiement relève de l’environnement hôte.

Conservation et migration​

L’admission du déclencheur est enregistrée avant le lancement de sa stratégie. Un arrêt du processus conserve ainsi l’identité consommée. Les compteurs sont calculés par jour UTC ; les identifiants anti-rejeu restent conservés après archivage.

Le registre est limité à 512 Mio par défaut. --state-max-bytes ajuste ce plafond (minimum 65 536 octets). Un registre plein bloque une nouvelle admission.

Consulter une page
touba trigger state touba.triggers.sqlite --limit 100
touba trigger state touba.triggers.sqlite --after 100 --limit 100

Utilisez la valeur next renvoyée comme curseur de la page suivante. --trigger <référence> filtre les événements et ajoute les compteurs du trigger.

Archiver les événements terminés
touba trigger archive-state touba.triggers.sqlite --before 2026-10-01T00:00:00Z
touba trigger archive-state touba.triggers.sqlite --before 2026-10-01T00:00:00Z \
--out archives/triggers-octobre.sqlite --apply

La première commande prévisualise. La seconde copie puis vérifie l’archive avant de retirer les détails des événements réussis et des refus. Les événements actifs, suspendus ou d’issue incertaine, les compteurs et les identifiants consommés restent dans le registre. L’archive se consulte avec touba trigger state.

Pour convertir un ancien état JSON, arrêtez son service puis lancez :

Migration explicite
touba trigger migrate-state ancien.trigger-state.json --out touba.triggers.sqlite
touba trigger migrate-state ancien.trigger-state.json --out touba.triggers.sqlite --apply

La migration crée un nouveau registre et une copie exacte .source-backup.json. Elle conserve le fichier d’origine et refuse d’écraser une destination existante. Les états migrés restent d’issue incertaine, car l’ancien registre conservait l’admission mais pas le résultat final. Aucun événement n’est relancé.

Refus utiles​

SituationComportement attendu
Trigger absent de l’AIRrefus avant exécution
Trigger désactivé dans le manifestrefus avant exécution
Source événementielle différenterefus avant exécution
Manifest qui élargit une limite AIRrefus avant exécution
event_id absentrefus avant exécution
event_id déjà consommé dans --staterefus replay avant exécution
Service webhook sans --queuefile SQLite dans .touba/trigger-queue.sqlite
Payload request non conforme au manifestrefus avant mise en queue
Signature webhook invaliderefus avant mise en queue
Service webhook exposé hors loopback sans tokenrefus CLI avant démarrage

Portée​

info
  • Le runner foreground exécute des entrées planifiées gouvernées.
  • Le service webhook exécute les entrées request -> strategy.
  • La queue durable suit touba-trigger-queue-v1.
  • Le statut compact du service suit touba-trigger-status-v1.
  • Le manifest active explicitement chaque trigger.
  • L’état de trigger enregistre les event_id consommés et les refus de replay.
  • Les contrats de payload du manifest mappent des champs JSON explicites vers les arguments de la stratégie cible.
  • Les signatures HMAC, retries bornés, reprises d’entrées processing et états dead-letter sont supportés par le service webhook.
  • Les schedulers distribués, queues distribuées, adapters cloud, auto-discovery de manifest, déclenchements directs d’agents et garanties réglementaires relèvent des couches plateforme environnantes.