Skip to content

Releases: startr-trade/sutra

v0.2.0-rc.2

v0.2.0-rc.2 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 06 Sep 14:25

Second release candidate of the Rust-native engine — 148 commits since v0.2.0-rc.1.

The theme is making declarations mean something at runtime. Several things a package
could declare were checked when you sealed it and then quietly ignored by a running
engine. The largest of those is fixed here, together with the class of
silent-success failures that let it hide for so long.

Still a release candidate: APIs may move before 0.2.0. This one carries breaking
changes — see Upgrading at the end.

Files as first-class input

A CSV or fixed-width upload is now a typed message, not a bag of strings. Point a
channel at a schema codec whose manifest declares the format, and the codec validates
every row and every cell against your XSD in one decode — before the process starts:

# schemas/cdr/codec-manifest.yaml
schemaKind: xsd
formats: [csv, fixed-width]

csv:
  delimiter: ","
  header: true

fixed-width:
  fields:
    - {name: recordId,    width: 12}
    - {name: msisdn,      width: 16}
    - {name: durationSec, width:  6}
  • Both wire forms, one schema. Their content types are disjoint, so an upload selects
    its parser unambiguously — one channel serves a CSV feed and a fixed-width feed, and
    nothing downstream knows which arrived.
  • A bad cell names its record: value[3].durationSec, not a byte offset into
    something the engine synthesised.
  • The XSD's leaf types reach the cells — an xs:int column arrives as a number.
  • An empty cell is absence for an element declared minOccurs="0", which is what
    makes optional columns usable at all.
  • fixed-width returns, with the manifest layout block it previously lacked. Because
    that layout is configuration, its columns are checked against your type at package
    time
    — a typo fails sutra lint, not every row in production.

A full walkthrough ships as examples/call-log-load and as a new
worked example in the book.

Declarations that now bind

  • A codec manifest is honoured. The engine hardcoded ["xml","json","yaml"] and never
    opened codec-manifest.yaml, so every formats: declaration was inert once deployed: a
    package sealed cleanly, linted clean, and then refused every upload. Lint and the engine
    now build codecs through one entry point, and a manifest fault is a package-time error.
  • Payloads are validated against a package's schema at runtime — a schema in a package
    previously meant nothing after deploy.
  • Validation fails closed. An intake with a validation contract and no
    <q:onValidation> now refuses a failing payload instead of passing it to the flow.
  • Loops fail closed too. <bpmn:loopDataInputRef> is a FEEL expression now, so a loop
    can iterate payload.value in place — and one that resolves to nothing fails rather
    than iterating zero times and reporting success.

Tooling

  • One generate verb replaces docgen / catalog / schemagen, each keeping its
    --check drift gate. Breaking.
  • sutra codecs — what this binary can actually resolve.
  • sutra create ci scaffolds the pipeline; create app scaffolds deploy/.gitignore.
  • The artifact catalog is a buildable mdBook, with BPMN diagrams auto-laid-out as SVG —
    no BPMNDI needed in your files.
  • A compile-enforced BPMN element-support matrix, generated from the loader itself, so
    the supported-element list cannot drift from the code.
  • One-line install and sutra self-update.
  • generate catalog --clean removes catalog pages whose source file no longer exists.

Errors that speak your format

RFC 7807 keeps its model; how it is serialised now follows the inbound content-type —
JSON (default), XML, YAML, or CSV. A client that posts a 40,000-row CSV and gets three bad
cells back receives a table it can diff against the file it sent, rather than JSON to
re-parse hunting for row 4,217.

Fixed

Three defects in the batch-load path, found by running the shipped example rather than testing
it.
Its end-to-end test booted the engine with no datasource, so every valid upload stopped at
the persistence check — the codec half was covered thoroughly and the load half was structurally
unreachable. A tier-2 suite now boots the same archive against a real database and asserts the
receipt, the transformed rows, and that re-uploading a batch converges.

  • A <bpmn:dataObject> declared on a process was invisible inside its sub-processes, so a store
    write in a multi-instance loop wrote null for every item. BPMN scopes a data object to its
    container and everything nested in it; each container was being indexed in isolation.
  • ack-mode: on-persist threw away a <q:reply continue="true"> receipt. The ack mode settles
    when the caller is answered, not what with — an HTTP channel now answers 202 Accepted
    carrying the render, and a bare 202 only when the process replied nothing. Answering
    immediately, with a document, while the load runs detached is now a supported combination
    instead of a silent contradiction.
  • Intake rejections that were the caller's fault answered 500. The status map keyed on a literal
    REJECTED. segment that SUTRA.INBOUND.VALIDATION_REJECT does not carry, so a malformed batch
    told the sender the engine had broken and the same bytes were worth retrying. Caller faults are
    400 now, with 409/413/429/503 for conflict, size, quota and capacity; anything unmapped
    keeps 500.

The catalog drift gate could not see a stranded page. generate catalog --check reported
"in sync" while a page whose source had been renamed sat in the tree — the writer only visits
pages it produces and the check looped that same set, so neither could see one whose source had
vanished. --check now reports those too, which turned up 42 genuinely stranded pages left
behind by module extractions and file moves.

Beyond that: check_output_conformance checked a <q:send>'s render against the
intake codec instead of its destination; multi-instance loop variables were invisible to
lint (a mandatory declaration produced a false "never initialised"); the @transient
read-after-wait gate was blind to respond-and-continue parks; the xml projection dropped
schema-instance attributes by namespace.

Security: h2 0.4.19 (RUSTSEC-2026-0258), chacha20 0.10.2 (0.10.1 was yanked),
jsonwebtoken 10, the container-scan backlog cleared, and the triage record time-boxed so
suppressions expire instead of accumulating.

Known gaps (honesty section)

A multi-instance loop is one durable step: a crash part-way through replays it from the
first item rather than resuming. That converges when every per-record effect converges — a
keyed upsert does, a <q:send> or a counter does not — and <q:process idempotent="true"/>
is how you assert it. Bounding the replay for large batches means delivering them in chunks.

No operations console UI. No published performance numbers.

Upgrading from v0.2.0-rc.1

  1. CLI: sutra docgen|catalog|schemagen → sutra generate docs|catalog|schema-handler.
    Check any Makefile or CI step that calls the old verbs.
  2. Validation posture: run sutra lint. Every affected intake is reported as
    SUTRA.CONFIG.VALIDATION.POSTURE_UNDECLARED, naming both choices — declare
    <q:onValidation mode="route"/> to keep the previous pass-through, or mode="reject" to
    state the new behaviour. Schema-less ingress is unaffected.
  3. Error-body parsing: a non-JSON caller now gets a non-JSON problem document. Callers
    posting application/json, or no content-type, are unchanged.
  4. Loops: a loop over a collection that may legitimately be absent must assign an
    explicit empty list.

Getting started

  • Book: https://sutra.startr.trade — start with Getting started.
  • Build from source: make build (Rust stable); container image via make image.

Licensed under MIT or Apache 2.0, at your option.

v0.2.0-rc.1

v0.2.0-rc.1 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 07 Aug 16:21

The first public release of Sutra — a Rust-native, message-native workflow engine
built on BPMN 2.0 and DMN. One statically-compiled binary, one PostgreSQL, and your
processes as diagrams your reviewers can actually read.

This is a release candidate: the engine is feature-complete for the surfaces below and
gated by a three-tier test ladder (workspace suites, containerized conformance,
Kubernetes integration), but APIs may still move before 0.2.0. Feedback and issues are
very welcome — this release exists to be argued with.

The engine

  • BPMN 2.0 as the programming model — events, gateways, boundary timers, message
    correlation, human decision points; the diagram's semantics are the runtime's.
  • DMN + FEEL decisioning — decision tables and expressions validated against the
    OMG DMN TCK: 3,300+ conformance tests run green in our gates.
  • Message-native by design — a start event binds a channel and a message type;
    the engine decodes the real wire format, validates it against its schema at the door,
    and drives the process with typed data. Violations are routable soft errors, not
    exceptions.
  • Transports in the box — HTTP plus Kafka, RabbitMQ, AMQP 1.0, SQS, GCP Pub/Sub,
    Dapr, Knative, and file channels, all behind one transport SPI; polyglot workers can
    also pull work through the external-task API (fetch-and-lock, budgets, lock
    extension).

Durable execution

  • Typed snapshots at quiescent points — no history replay, no determinism
    constraints on your logic, no history ceiling by construction.
  • Durable timers and schedules — durations, dates, ISO-8601 cycles, and timer start
    events as deployment schedules.
  • Per-task retry policies (<q:retry>) as durable timer parks, with a delivery
    attempt ceiling and dead-letter capture, inspection, and replay.
  • Queryable history — terminal instances retained on your policy; a per-instance
    audit journal survives completion.
  • Failure is a durable state — failed instances persist, can be inspected,
    migrated, and resumed.

Deployment and versioning

  • Sealed .sutra archives — content-addressed deployment identity; what you review
    is what runs.
  • Hot deploy with a two-phase flip — zero dropped requests across a version swap;
    in-flight instances stay pinned to their exact version.
  • An explicit migration API — single, batch, and cross-process migration with
    validation and a dry-run mode; never a silent behavior change under live work.

Operations

  • One binary + PostgreSQL — the whole topology. Your Postgres story (backups, HA)
    is your durability story.
  • Execution lanes — in-process concurrency is a config key (sutra.engine.shards),
    not a cluster decision; replicas coordinate through database-backed ownership claims.
  • Honest health probes — readiness and liveness report real internals, including
    dead execution lanes, so orchestrators restart exactly when they should.
  • Multi-tenancy that reaches the database — per-tenant channels and quotas,
    PostgreSQL row-level security, and tenant-wide erasure that honors in-flight work.
  • OpenTelemetry throughout; container image and Kubernetes deployment modules in
    the repo.

Verification and testing

  • Deploy-time verification — schema-aware lint (every navigation a template or
    expression makes must exist in the message contract), declared coverage for
    compliance-critical paths, fail-closed archive validation.
  • A time-skipping test runtime — inject a virtual clock in tests (unreachable from
    production configuration), fast-forward through a 30-day timer in milliseconds, on
    the real execution path; sutra test simulate drives the same seam from the CLI.

Tooling

  • The sutra CLI — package, lint, deploy, simulate, coverage, audit-replay, and
    the admin surface.
  • A VS Code extension and a bpmn-js modeler plugin for the q: extension
    vocabulary, under tools/.

Known gaps (honesty section)

No operations console UI (the HTTP admin API is complete; a console is roadmap). Worker
helper libraries beyond the HTTP pull API are ecosystem work that hasn't happened yet.
No published performance numbers — we don't publish benchmarks we can't stand behind on
controlled hardware.

Getting started

  • Book: https://sutra.startr.trade — start with Getting started.
  • Build from source: make build (Rust stable); container image via make image.

Licensed under MIT or Apache 2.0, at your option.