Integrate in Python
Minimal shape
import asyncio
from triggers import NATS, Service
svc = Service("flow_lab")
svc.ingress(NATS)
@svc.action("parse")
async def parse(ctx, payload: ParseInput) -> ParseResult:
return ParseResult(message=payload.message, length=len(payload.message))
asyncio.run(svc.run())
Full-surface example
import asyncio
from triggers import HTTP, KAFKA, NATS, Service
svc = Service("notify")
svc.ingress(
NATS,
KAFKA,
HTTP,
)
svc.enable_dedup()
svc.data_source(
"templates",
load_templates,
description="Notification templates for the selected tenant",
)
@svc.action("send_inapp")
async def send_inapp(ctx, payload: SendInAppInput) -> SendInAppResult:
...
asyncio.run(svc.run())
HTTP ingress ownership
HTTP uses the canonical /v2/<service>/inbound path automatically.
When you need a different path, use HTTP.at("/custom/path") or HTTP.at("https://...").
For both forms, the SDK publishes the absolute HTTP endpoint by using:
TRIGGERS_HTTP_PUBLIC_BASE_URLwhen it is set- otherwise
TRIGGERS_HTTP_ADDR(default:8081)
Real services in this repo now use bare HTTP unless they truly need a custom route.
For simple local runs, TRIGGERS_HTTP_ADDR is usually enough on its own. Add TRIGGERS_HTTP_PUBLIC_BASE_URL only when the advertised URL should differ from the bind-address fallback.
TriggersSettings
Most production services can rely on env defaults directly:
svc = Service("notify")
Use settings=TriggersSettings(...) when you need a test or example-local override without mutating the real process env.
That includes the intentional HTTP exception: the canonical path still comes from bare HTTP, while TriggersSettings(http_public_base_url=...) can override the published absolute endpoint for a narrow example or test.
Typical env
TRIGGERS_NATS_URL=nats://127.0.0.1:4222
TRIGGERS_KAFKA_BROKERS=127.0.0.1:9092
TRIGGERS_REDIS_URL=redis://127.0.0.1:6379/0
TRIGGERS_HTTP_ADDR=:8081
TRIGGERS_SERVICE_VERSION=local-dev
Wiring env map
| Var | Used for | When to set it |
|---|---|---|
TRIGGERS_NATS_URL | NATS registration, heartbeats, broadcasts, and NATS ingress | Required when the service runs in the real fleet |
TRIGGERS_NATS_CREDS_PATH | Optional NATS auth file | Only when the NATS cluster requires creds |
TRIGGERS_KAFKA_BROKERS | Kafka ingress wiring and canonical-topic ensure | Only when code declares KAFKA |
TRIGGERS_KAFKA_INGRESS_TOPIC_PARTITIONS | Canonical Kafka ingress topic partitions | Optional override; default 1 |
TRIGGERS_KAFKA_INGRESS_TOPIC_REPLICATION_FACTOR | Canonical Kafka ingress topic replication factor | Optional override; default 1 |
TRIGGERS_KAFKA_INGRESS_TOPIC_RETENTION_MS | Canonical Kafka ingress topic retention in ms | Optional override; default 604800000 |
TRIGGERS_HTTP_ADDR | HTTP bind address and fallback advertised base | Usually set for HTTP ingress; local default is :8081 |
TRIGGERS_HTTP_PUBLIC_BASE_URL | Public HTTP base used in the manifest | Only when the advertised URL should differ from TRIGGERS_HTTP_ADDR |
TRIGGERS_SERVICE_VERSION | Manifest/runtime version string | Recommended in CI and shared environments |
TRIGGERS_REDIS_URL | Final Redis URL | Fastest local-dev option and highest-precedence runtime input |
TRIGGERS_REDIS_URL_SECRET_NAME | Key Vault secret name containing the full Redis URL | Use when platform stores the full URL as one secret |
TRIGGERS_REDIS_HOST | Literal host[:port] or redis[s]://... base | Use when the platform exposes host separately |
TRIGGERS_REDIS_HOST_SECRET_NAME | Key Vault secret name for host/base | Recommended repo pattern in Kubernetes when host is secret-backed |
TRIGGERS_REDIS_PASSWORD | Literal Redis password | Use only with host-based composition |
TRIGGERS_REDIS_PASSWORD_SECRET_NAME | Key Vault secret name for Redis password | Recommended repo pattern in Kubernetes when password is secret-backed |
TRIGGERS_REDIS_DB | Redis DB suffix when composing a URL from host/password inputs | Set it only when the chosen Redis host does not already include a DB path |
KEY_VAULT_URL | Azure Key Vault base URL for any *_SECRET_NAME lookup | Required whenever any Redis secret-name var is set |
If the service enables enable_dedup() or enable_rate_limit(), Kubernetes can provide Redis through TRIGGERS_REDIS_URL, TRIGGERS_REDIS_URL_SECRET_NAME, or host/password inputs plus optional TRIGGERS_REDIS_DB. The SDK resolves that contract lazily, TRIGGERS_REDIS_URL still wins by precedence, and any Redis *_SECRET_NAME input requires KEY_VAULT_URL.
In this repo, the usual Kubernetes pattern is:
envFromSecrets:
- azure-app
environment:
TRIGGERS_REDIS_HOST_SECRET_NAME: "<env-specific-redis-host-secret>"
TRIGGERS_REDIS_PASSWORD_SECRET_NAME: "<env-specific-redis-password-secret>"
TRIGGERS_REDIS_DB: "<service-specific-db>"
azure-app typically injects the Azure credential env plus KEY_VAULT_URL, so the SDK can resolve the Redis secret-name inputs without service-specific bootstrap code.
For Kafka ingress, keep the declaration canonical and code-owned: KAFKA always advertises triggers_v2_<service>_inbound. Released flows may point Kafka delivery at another topic, but that retarget belongs only in released flow config. The SDK ensures only the canonical topic before it starts the consumer. If it has to create that topic, it uses explicit retention.ms plus cleanup.policy=delete, with defaults of 1 partition, replication factor 1, and 604800000 ms retention unless you override them with the TRIGGERS_KAFKA_INGRESS_TOPIC_* env vars. Invalid TRIGGERS_KAFKA_INGRESS_TOPIC_* values fail fast during config parsing. Non-canonical flow targets must already exist; the SDK only probes them for existence and will not create them. If a retargeted topic is missing, reconcile fails closed: the runtime closes the stale Kafka consumer instead of silently staying on the old topic, so the service can remain up while Kafka ingestion is unavailable until the topic exists or the flow is corrected.
Current repo examples
These are examples from the current repo, not universal defaults for every new service:
| Service | Environment | Suggested Redis wiring |
|---|---|---|
notify-sender | spark-dev1 | TRIGGERS_REDIS_HOST_SECRET_NAME=spark-dev-redis-host, TRIGGERS_REDIS_PASSWORD_SECRET_NAME=spark-dev-redis-password, TRIGGERS_REDIS_DB=3 |
notify-sender | spark-prod-mexico1 | TRIGGERS_REDIS_HOST_SECRET_NAME=spark-mexico1-prod-redis-host, TRIGGERS_REDIS_PASSWORD_SECRET_NAME=spark-mexico1-prod-redis-password, TRIGGERS_REDIS_DB=3 |
spark_bonus | spark-dev1 | TRIGGERS_REDIS_HOST_SECRET_NAME=spark-dev-redis-host, TRIGGERS_REDIS_PASSWORD_SECRET_NAME=spark-dev-redis-password, TRIGGERS_REDIS_DB=5 |
spark_bonus | spark-prod-mexico1 | TRIGGERS_REDIS_HOST_SECRET_NAME=spark-mexico1-prod-redis-host, TRIGGERS_REDIS_PASSWORD_SECRET_NAME=spark-mexico1-prod-redis-password, TRIGGERS_REDIS_DB=5 |
Do not reuse a DB index just because another service already uses it. Keep the existing service-specific DB assignment unless operations explicitly approve a different one.
Local verification
await svc.run()starts cleanly with noServiceError.build_manifest()shows the expectedservice_sources,transports, andsupports_nodes.- A harness or real broker test can drive at least one success path and one failure path.