Aether’s hexagonal architecture defines ten extension interfaces beyond the executor, store, and broker. Each interface isolates a single infrastructure concern behind a minimal Go contract. All of them are wired into the engine via functional options, and all are optional except where noted. This page covers each interface, its method signatures, the circumstances under which you would implement it, and the correspondingDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/BabySid/aether/llms.txt
Use this file to discover all available pages before exploring further.
WithXxx option.
idgen.Generator — unique ID generation
idgen.Generator — unique ID generation
WorkflowRun and TaskRun needs a globally unique string ID. The engine calls Generate whenever it creates one of these records.Context argument lets implementations embed structured information into the ID — for example, a UUID that encodes the workflow kind, or a Snowflake ID that embeds the task name as a prefix. Implementations that only need globally unique strings can ignore ctx entirely.When to implement: Always — WithIDGenerator is required. Your choices are UUIDs (github.com/google/uuid), Snowflake IDs, or a monotonic counter for testing.Engine option:AtomicIDGen:AtomicIDGen generates monotonically increasing string IDs starting from "1". It is safe for concurrent use — atomic.Uint64.Add is a single instruction. In production, replace it with a UUID or a distributed ID scheme that is unique across engine replicas.expr.Evaluator — expression evaluation
expr.Evaluator — expression evaluation
Evaluator for when conditions (task-level guards), repeatCondition (loop termination), phaseConditions (custom phase mapping), and valueFrom.expression (parameter computation). Without an evaluator, all expression fields must be absent or the engine returns an error.env is a flat map[string]any containing all variables in scope at the call site: workflow inputs, task outputs, loop iteration variables, and any values contributed by registered vars.Source implementations. The return value is compared as a boolean for when/repeatCondition, or used as a string/numeric value for valueFrom.expression.When to implement: Any workflow that uses when, repeatCondition, phaseConditions, or valueFrom.expression requires an evaluator. Common choices: expr-lang/expr, google/cel-go, or a simple custom parser for constrained DSLs.Engine option:SimpleEvaluator:SimpleEvaluator handles true, false, a == b, and a != b. Tokens wrapped in single or double quotes are treated as string literals; bare tokens are resolved from env.hook.Notifier — lifecycle notifications
hook.Notifier — lifecycle notifications
Notify at key moments in a workflow or task lifecycle. Implement Notifier to integrate with external systems — webhooks, message buses, observability platforms, or audit logs.| Constant | Fires when |
|---|---|
hook.OnStart | A workflow or task begins |
hook.OnSuccess | A workflow or task succeeds |
hook.OnFailure | A workflow or task fails (business failure) |
hook.OnError | A workflow or task errors (system failure) |
hook.OnSuspend | A task is suspended (ExecCodeSuspended) |
hook.OnResume | A suspended task is resumed |
hook.OnCancel | A workflow or task is cancelled |
hook.OnExit | A workflow exits (terminal, regardless of outcome) |
hook.ScopeWorkflow or hook.ScopeTask.Fan-out with CompositeNotifier: To send hooks to multiple destinations, compose notifiers:CompositeNotifier.Notify calls all members and joins errors — individual failures do not prevent other notifiers from running.Engine option:timeout.Watcher — deadline expiry detection
timeout.Watcher — deadline expiry detection
Watcher detects when tasks or workflow runs exceed their configured deadlines and emits TimeoutEvent values for the engine to consume. The engine’s only job is to react to these events — it never polls the store for expired deadlines itself.- Events are delivered at-least-once. The engine’s timeout handler is idempotent.
- The watcher does not modify any state — it only discovers and emits.
- Deadline data lives in the store (
store.TaskRun.Deadline,store.WorkflowRun.Deadline), so the watcher is stateless and survives engine restarts without losing pending timeouts.
timeout field. Without a Watcher, timeout fields are stored but deadlines are never enforced.Playground example — PollingWatcher:PollingWatcher scans the store at a fixed interval, comparing each active entity’s Deadline to time.Now(). It emits events to a buffered channel (capacity 256) and drops events if the buffer is full (consumer too slow).artifact.Repository — artifact upload and download
artifact.Repository — artifact upload and download
Repository to integrate with object storage, local filesystem, or an HTTP endpoint.config is opaque JSON parsed from the workflow’s artifact.source.config field. The model layer defines three config shapes: OSSSourceConfig, LocalSourceConfig, and HTTPSourceConfig — but your implementation may define its own schema.Engine option:artifacts fields on tasks or templates). The artifact interface is currently stored by the engine but not yet wired into the execution path — it is reserved for the upcoming artifact upload/download feature.secret.Provider — secret retrieval
secret.Provider — secret retrieval
Provider to resolve secretKeyRef parameter values at task dispatch time. This lets workflow authors reference secrets by name and key without embedding credential values in workflow documents.name is the secret’s logical name (e.g. "my-db-creds"); key is the field within that secret (e.g. "password"). This maps naturally to Kubernetes Secrets, HashiCorp Vault, AWS Secrets Manager, or any key-value secret store.A workflow parameter using a secretKeyRef looks like:secretKeyRef. Without a provider, resolution of these parameters will fail.vars.Source — custom variable namespaces
vars.Source — custom variable namespaces
Source contributes a flat key→value map to the workflow evaluation environment. Variables from registered sources are available as {{namespace.key}} placeholders in workflow template parameters.Vars() is called each time the evaluation environment is built, so it may return dynamically computed values. Engine-level sources have lower priority than per-call sources — if WorkflowArgsSource and a custom source both produce the same key, the per-call value wins.Built-in: SystemSource:{{system.os}} and {{system.arch}} available in all workflow templates.Custom example — per-run tenant context:WithVarsSource multiple times to register multiple sources. Sources are injected into every variable resolution call for every workflow run.worker.Registry — worker registration and discovery
worker.Registry — worker registration and discovery
Register at startup with their Info (ID, supported executor types, and full schemas for each type). The engine can then call ListByExecutorType to route tasks to capable workers. Heartbeat keeps registrations alive; implementations may remove stale workers that miss heartbeats.worker.Registry manages worker identity and capabilities. Task lifecycle (dispatch, fetch, complete) is still handled by broker.TaskBroker. These two interfaces are intentionally separate.errsink.ErrorSink — internal error observation
errsink.ErrorSink — internal error observation
ErrorSink is a non-blocking observation outlet for engine-internal errors. It never influences scheduling decisions — it exists purely to feed external monitoring and alerting systems.| Level | Meaning | Example |
|---|---|---|
SeverityInfo | Expected, benign | Token mismatch from concurrent writers |
SeverityWarning | Non-critical | Hook notification failure |
SeverityError | Scheduling-path failure | dispatchLeafTask error |
SeverityCritical | Potential permanent hang | advanceScope failure without fallback |
- Must be safe for concurrent use.
- Must return quickly. Offload long-running work (HTTP calls, disk I/O) to a background goroutine or a buffered channel to avoid blocking the engine’s scheduling loop.
cron.Scheduler — cron-based workflow scheduling
cron.Scheduler — cron-based workflow scheduling
Scheduler provides the scheduling backend for CronWorkflow resources. Without it, SubmitCronWorkflow, GetCronWorkflow, and related engine methods return ErrNotSupported.Stop is called. Safe to call only once; idempotent implementations may log a warning on repeated calls.id is the CronWorkflow’s system-generated ID. schedule is a standard cron expression (five fields). timezone is an IANA timezone string (e.g. "UTC", "Asia/Shanghai"). callback is invoked on each schedule match — the engine provides this function and it submits a new WorkflowRun.Engine.Start calls Scheduler.Start; Engine.Stop calls Scheduler.Stop. During startup, the engine calls ListCronWorkflows on the store and re-registers each active cron entry with Add — this restores schedules after a crash or restart.Engine option:CronWorkflow resources to trigger workflows on a schedule. A robust production implementation would use a distributed cron library with leader election to prevent duplicate triggers across engine replicas — for example robfig/cron for single-process, or a Redis-backed scheduler for distributed deployments.Summary: required vs optional
- Required
- Optional
aether.New will return an error without them.| Interface | Option |
|---|---|
store.Store | WithStore |
broker.TaskBroker | WithTaskBroker |
idgen.Generator | WithIDGenerator |