Skip to main content

Concepts

Nomaflow has a small vocabulary — six concepts that the rest of the documentation builds on. Read this once and every other page (the Job editor, the Run detail, the recipes) will read in one pass.


The six pieces

Nomaflow vocabulary — one diagramJOBid · description · schedulea workflow you define onceSTEPtype · name · configone unit of work inside a jobSCHEDULEcron + timezonewhen the job auto-firesTRIGGERcron · manual · APIwhat fired this runRUNone execution of a jobQUEUED → RUNNING → terminal statePARAMETERSshared params + step kwargsdata the steps receiveHOW THEY RELATEA JOB has many STEPS and an optional SCHEDULE. Each TRIGGER creates a RUN; the run executes the steps in order, merging the job's PARAMETERS with any per-fire overrides.

Job

A job is the unit you define and operate. One job has:

PropertyWhat it means
IdURL-safe identifier (my-job, nomajde-daily-sync). Used in run URLs and in the database as the foreign key for every run. Immutable once created — rename it via an export-then-recreate.
DescriptionFree text shown on the catalogue card. Use it to explain why the job exists — your future self at 3 AM will thank you.
ScheduleOptional cron expression. Empty = the job is manual-only.
TimezoneIANA name (Europe/Paris, UTC, America/New_York). The cron is evaluated in this zone — important if the framework runs on UTC but your business hours don't.
TagsFree labels — etl, nightly, legal. Shown as chips on the catalogue card; useful for grouping in the search bar.
EnabledWhen false, the schedule is ignored. Manual ▶ Run now still works.
StepsOrdered list of work units (described below). At least one step is required.
Retry policyOptional. Applies on step failure, not on whole-job failure.
AlertsOptional. Notifications on failure or on long-running detection.
ParametersOptional shared kwargs (described below).
Log levelINFO (default) or DEBUG. Per-fire overridable in the Run-with-parameters modal.

A job is defined once and runs many times. Each fire is a new run.


Step

A step is one unit of work inside a job. Steps run in order — step 2 starts only after step 1 succeeds. There's no parallel branching inside a single job (use chained jobs for that, or a Python step that fans out internally).

The five step types:

TypeWhat it does
sql_queryRuns one SQL statement on a connector. Captures the row count.
sql_copyStreams rows from one connector + schema + table to another. Handles type coercion, batched inserts, atomic swap.
pythonCalls a Python function in your plugins (module.path:function). The escape hatch — anything declarative steps can't express.
httpCalls an HTTP endpoint with optional headers / body.
ldap_syncPulls a directory subtree, maps attributes through a config block, upserts into a connector.

Each step has a name (label in the run history), a type and the type-specific configuration. Steps can be individually disabled — useful for chained jobs where only some phases need re-running after an upstream failure.

The full reference is on the Steps page.


Schedule

A schedule is a 5- or 6-field cron expression that tells Nomaflow when to auto-fire the job.

FieldRangeExample
Minute0-590 = on the hour.
Hour0-232 = 02:00.
Day of month1-311 = the 1st.
Month1-12* = every month.
Day of week0-6 (Sunday=0)1 = Monday.
(optional) Second0-59rarely used; only set if the schedule must trigger more than once per minute.

A few common patterns:

GoalCron
Every day at 02:00.0 2 * * *
Every Monday at 09:30.30 9 * * 1
Every hour, on minute 15.15 * * * *
Every 5 minutes.*/5 * * * *
First day of each month at midnight.0 0 1 * *

Empty schedule = manual-only. The job is in the catalogue, ▶-runnable, but never auto-fires. This is the right shape for one-off rebuilds, operator-driven sends and any workflow where "the human decides when".

The Schedule view (a calendar of upcoming fires across every job) and the full cron syntax reference live on Jobs → Schedules.


Trigger

Every run records what fired it — the triggered_by field on the run row.

TriggerSource fieldWhen
cronAuto-fire on schedule.Happens silently inside the framework.
user:<name>An operator clicked ▶ Run now or ran with parameters.Their account name lands in the field for the audit trail.
apiAn external caller hit POST /admin/jobs/<id>/run.Useful when an external scheduler (Airflow, Dagster, a CI pipeline) drives Nomaflow as a step runner.
cliA shell call.Power-user / scripting path.

All four go through the same dispatch — same step engine, same retry policy, same persistence. The trigger source is the only thing that varies.


Run

A run is one execution of a job. Each run has:

FieldWhat it carries
Run idA short identifier (run_a8c4d) used in URLs and logs.
Job idThe job this run belongs to.
StateQUEUEDRUNNINGSUCCEEDED / FAILED / CANCELED.
Started at / Finished atTimestamps in the application's timezone.
Triggered byOne of cron, user:<name>, api, cli.
Parameters snapshotThe merged params + op_kwargs the run actually used — the audit answer to "what kwargs did this run see?" three weeks later.
Per-step rowsFor each step: name, type, started_at, finished_at, state, rows_affected, error (if any).
Log streamEvery log.info() / log.warning() / log.error() the step emitted, plus the framework's own progress markers.

The run terminates in one of four states:

StateMeaning
SUCCEEDEDEvery step ran and returned without raising.
FAILEDA step raised after all retries were exhausted. The remaining steps did not run.
CANCELEDAn operator clicked ✕ Cancel while the run was in flight. In-flight steps roll back where they can.
QUEUEDThe dispatcher accepted the run but execution hasn't started yet — visible for at most a few milliseconds in practice.

The Run detail page shows the per-step timeline, the inputs and outputs, and the live log tail. See Runs → History.


Parameters

Parameters are the data steps receive at run time. There are two layers:

LayerWhere it livesWhat it's for
Job-level paramsSection on the job editor.Values shared across every step — typical: apps_id, source_connector, target_connector.
Step-level op_kwargsInside each Python step.Values scoped to one step — typical: a per-step flag, a query parameter.

At runtime the runner merges the two: job-level params are passed first, step-level op_kwargs override them on key conflict. So a job-wide target_connector = "nomasx1" can be overridden in one step with target_connector = "nomasx1-backup".

Per-fire overrides

The Run-with-parameters modal (opened by ▶ Run now when a job has params, op_kwargs or multiple steps) lets the operator change any of these for one fire only, without editing the job definition. The form is type-aware — booleans render as checkboxes, numbers as number inputs, and keys ending in _connector show a connector picker (saves typos vs. free text).

Layer order at runtime (later wins on conflict):
1. job.params ← saved in the job editor
2. step.op_kwargs ← saved per step
3. modal params override ← one fire only
4. modal op_kwargs override ← one fire only

A typical use: a nomasx1-security job with [params] apps_id = 10 runs nightly against production; the operator opens the modal and overrides apps_id = 99 to run the same job against a sandbox tenant without editing TOML.


Retry policy

A retry policy applies to steps, not to the whole job.

FieldWhat it means
attemptsTotal tries (1 = no retry). 2 = initial try + one retry.
backofffixed (constant wait) or exponential (doubling wait).
base_secondsWait before the first retry. With exponential, the second wait is doubled, the third quadrupled.

If step 1 has attempts = 3 and fails three times, the run moves to FAILED — the remaining steps don't execute. If step 1 succeeds and step 2 fails three times, step 1's effect stays committed (Nomaflow has no run-wide rollback — design idempotent steps).

The default is no retry (attempts = 1). For network-touching steps (HTTP, LDAP) a attempts = 3 with exponential backoff is the most common setting.


Log level

Per-run logging verbosity. INFO (default) gives operator-level signal — row counts, business progress markers. DEBUG also emits the full SQL of every query — useful when troubleshooting a specific run.

Two ways to set it:

  • Per job in the editor → applies to every fire.
  • Per fire in the Run-with-parameters modal → only this one run.

The DEBUG setting is for investigation, not for steady state — debug logs are verbose and the framework retains them for the same 90 days as INFO, so a runaway DEBUG job can swell the log table noticeably.


Alerts

A job's alerts block routes failure (and long-running) events to the framework's notification channels.

FieldWhat it means
on_failureWhen true, a FAILED run emits an alert. Default true once an alerts block is declared.
on_long_run_minutesIf the run is still in flight after N minutes, emit an alert. The run keeps going — this is a heads-up, not an abort.
recipientsChannel-specific identifiers (Slack handles, email addresses, webhook IDs) — overrides the framework defaults.

The transports themselves (Slack workspace, SMTP server, webhook URLs) are configured once at framework level; the job just picks recipients. See Notifications for the wiring.


How they relate — a concrete example

You define a job nightly-reporting-refresh:

JOB nightly-reporting-refresh
├── description: "refresh the reporting materialised views from the OLTP DB"
├── schedule: "0 2 * * *" (timezone Europe/Paris)
├── tags: [etl, nightly]
├── params: { target_connector: "reporting" }
├── retry: { attempts: 2, backoff: fixed, base_seconds: 60 }
└── steps:
1. refresh-orders (sql_query, connector "reporting")
2. refresh-customers (sql_query, connector "reporting")
3. send-summary (python, callable "reports:summary")

Every night at 02:00 Paris time, the scheduler creates a new run of nightly-reporting-refresh. The run executes step 1 — if the SQL query fails, the runner waits 60 seconds and retries once before declaring the step FAILED. If it succeeds, step 2 runs. Then step 3.

When step 3 runs, the Python function receives target_connector="reporting" from the job's params, plus anything in its own op_kwargs. The function emits log.info("summary email sent") — that line lands in the run's log stream.

The operator opens the Runs page the next morning, sees the run with a green SUCCEEDED badge, clicks it, sees three green checkmarks and the log. Everything you'd want to know about last night's refresh is on one screen.


What's next