Version 0.5.0 · Apache-2.0

Write the workflow once. Run it reliably, in every language.

ORBIT is a declarative language and durable runtime for work that spans services. You describe the steps, their order and what happens when they fail. The compiler checks every step against its worker's contract before anything runs, and the runtime carries it through retries, timeouts, crashes and compensation.

curl -fsSL https://github.com/AjmalX0/Orbit/releases/latest/download/install.sh | sh
order.orbit
workflow OrderProcessing {
    version "1.0.0"
    input  { order_id: String, amount: Float }

    step analyze_risk {
        worker "risk.analyze"
        using python
        with { amount: input.amount }
        retry { attempts: 3, backoff: exponential }
    }

    step charge {
        worker "payments.charge"
        using typescript
        when analyze_risk.score < 0.8
        with { order_id: input.order_id, amount: input.amount }
        idempotency_key: "charge-" + input.order_id
        timeout 30s
        compensate {
            worker "payments.refund"
            with { transaction_id: charge.transaction_id }
        }
    }

    return { transaction_id: charge.transaction_id }
}

How it works

  1. 01

    Declare

    Task contracts describe what each worker accepts and returns. Workflows describe steps, dependencies, conditions, retries, timeouts and compensation. Dependencies follow from the data a step reads.

  2. 02

    Check

    The compiler type-checks every step input, every reference and every optional value, rejects cycles, and lowers the workflow to a versioned, content-hashed intermediate form.

  3. 03

    Run

    The runtime records every decision as an event before acting on it. Workers in any language poll for tasks over HTTP or gRPC. A crash at any point resumes from history.

Mistakes surface before deployment

A step that can be skipped produces an optional value. Using it where a value is required is a compile error, with the exact location and a suggested fix, not a null pointer at three in the morning.

Every diagnostic has a stable code and is documented in the diagnostics reference. The same checks run in your editor through the language server.

$ orbit check order.orbit
error[E0405]: value may be `null` but `String` is required
  --> order.orbit:19:25
   |
19 |         with { message: charge.transaction_id }
   |                         ^^^^^^^^^^^^^^^^^^^^^
   = help: provide a fallback with `coalesce(value, default)`,
     or declare the target as optional with `?`

Failures are part of the program

When a payment is declined, ORBIT runs the compensations of the steps that already succeeded, in reverse order, and records every decision. The history is the source of truth: inspect it with orbit logs, or export it as OpenTelemetry traces.

Read the delivery guarantees

#13  StepDispatched         step=process_payment attempt=1
#14  StepAttemptFailed      error=CARD_DECLINED
#15  StepFailed             step=process_payment
#17  CompensationStarted    reason=step `process_payment` failed
#18  StepScheduled          step=compensate:reserve_inventory
#20  StepSucceeded          step=compensate:reserve_inventory
#21  CompensationCompleted
#22  WorkflowFailed         error=CARD_DECLINED

People in the loop, as part of the language

Approvals, reviews and replies from other systems are typed signal declarations. An await step pauses durably until the signal arrives, or fails after its timeout and compensates what already happened. A query exposes the execution's state to anyone who asks.

The compiler checks both: a signal's payload is a typed record, every reader of it is type-checked, and the runtime rejects a malformed payload before it is recorded. Signals and queries

approval.orbit
signal review { approved: Bool, reviewer: String }

step assess {
    compute { needs_approval: input.amount >= 1000.0 }
}

step decision {
    await review
    when assess.needs_approval
    timeout 2d
}

query status = {
    approved: decision.approved
    reviewer: decision.reviewer
}

Checked beyond types

A task contract can declare the task that undoes it. Every step that calls it must then compensate with that task, or orbit check --strict fails the build. The same analysis reports steps that can never run, signals nothing waits for, and waits that could last forever.

Run it in CI and a workflow cannot reach production with an effect it has no way to undo. Static safety checks

$ orbit check --strict checkout.orbit
warning[W0601]: step `charge` calls `payments.charge` but has no compensation
  --> checkout.orbit:13:16
   |
13 |         worker "payments.charge"
   |                ^^^^^^^^^^^^^^^^^
   = help: add `compensate { worker "payments.refund" with { ... } }`

error: 1 warning(s) treated as errors (--strict)

Change workflows without breaking running work

Every running execution keeps the exact definition it started with. orbit compat compares two versions and tells you whether callers, signal senders and query readers will still work, and which version number the change requires.

Compatibility checks

$ orbit compat old.orbit new.orbit
error   workflow Invoice 1.4.0 → 1.5.0: input.amount: Float → Int: incompatible type: expected `Int`, found `Float`; breaking for callers without a major version bump; hint: bump the major version (1.4.0 → 2.0.0)
error   workflow Invoice 1.4.0 → 1.5.0: signal approve.approver: (added) → String: required field added without a default; breaking for senders of the signal without a major version bump; hint: bump the major version (1.4.0 → 2.0.0)
error: compatibility check failed: 2 error(s), 0 warning(s)

Test workflows without infrastructure

Tests are written in ORBIT next to the workflow: mock what each task returns or how it fails, send signals, and state the outcome you expect. orbit test runs them in-process against the real runtime, with no server and no workers, and fast-forwards durable time, so a two-day timeout takes milliseconds.

orbit simulate explores a workflow the same way from the command line, and orbit replay re-runs a recorded execution against the current runtime to show any decision it would now make differently. Testing workflows

approval.test.orbit
test "large orders wait for approval" {
    workflow OrderApproval
    input { order_id: "T1", customer_id: "c-1", amount: 2500.0 }
    mock "payments.charge" returns { transaction_id: "tx-" + input.order_id, status: "captured" }
    mock "notifications.send" returns { delivered: true }
    signal review { approved: true, reviewer: "dana" }
    expect status COMPLETED
    expect output { status: "charged", transaction_id: "tx-T1" }
    expect step decision SUCCEEDED
}

Keep running when a server does not

Run several orbit server --cluster processes on one PostgreSQL database. In standby mode one is active and the others take over about a second after it stops, fenced so that a deposed server can never write. In shared mode (--cluster-mode shared) every server is active: executions are owned per server and taken over when one stops, tasks are claimed from a shared queue, and any server accepts any request behind a round-robin load balancer.

Resource limits cap running executions, fan-out, history length and payload size, and client identities give each application only the workflows and actions it needs. Running several servers · Performance

# node-1, the leader, is killed with SIGKILL at 13:55:46.19
# node-2, standby until now:
13:55:46.810072Z INFO orbit_runtime::cluster: elected leader; recovering executions from history node_id=node-2 term=2
13:55:46.859078Z INFO orbit_runtime::engine: recovered active executions from history executions=1 in_flight=1
13:55:46.860022Z INFO orbit_runtime::cluster: leader is serving node_id=node-2 term=2 active_executions=1 recovery_ms=49
13:55:46.886881Z INFO orbit_runtime::engine: worker registered worker_id=orders-python runtime=python tasks=5
13:55:46.966959Z INFO orbit_runtime::engine: worker registered worker_id=orders-typescript runtime=typescript tasks=4

What teams use it for

ORBIT is built for processes that must be readable, checkable and testable as a whole. One workflow definition, six worker languages, and no contract mismatches between them.

Business process contracts

Onboarding, KYC, lending, claims and refunds as typed definitions an auditor can read: every call matches its contract and every side effect has an undo action.

Cross-language orchestration

One workflow calls services in Python, TypeScript, Go, Rust, Java and C#. The compiler checks every call, whatever language implements it.

Human approvals

Typed decisions, multi-level approvals, comments at any time, deadlines that escalate, and a record of who decided what.

Sagas

Reserve, charge, ship, and undo in reverse when a later step fails. Missing compensations are build errors under --strict.

AI agents with guardrails

Durable, typed tool calls with bounded fan-out, budgets, guardrail checks, human review and compensation for side effects.

Durability for small teams

One binary with SQLite or PostgreSQL. No platform to run before the first workflow; several servers when you need them.

What the language covers

Typed contracts

Schemas, optional types, records, lists and maps. Inputs and outputs are validated by the compiler and again by the runtime.

Retries and timeouts

Attempt limits, fixed or exponential backoff, non-retryable error codes, per-attempt, queue and workflow deadlines.

Compensation

Declare the action that undoes a step. On failure, cancellation or timeout, completed steps are compensated in reverse.

Fan-out

for item in list runs a step per element with its own retries and idempotency key, limited by max_parallel.

Child workflows

Start another workflow as a durable child with a deterministic id, type-checked against its input and output.

Durable timers

sleep 24h survives restarts and needs no worker. Conditions decide at run time whether a step runs.

Signals and queries

Typed messages a workflow waits for, and typed read-only views of its state, declared in the workflow itself.

Compute steps

Small deterministic transformations with map, filter and sum, evaluated by the runtime and type-checked like everything else.

Handlers

on event runs steps every time a signal arrives. on failure, on cancel and on timeout run after compensation, with a typed failure value.

Limits and permissions

Caps on running executions, fan-out, history and payload size, per server or per workflow, and client identities limited to the workflows and actions they need.

Tests in the language

Mocks, signals and expectations in test blocks, type-checked against the workflow and run with orbit test in milliseconds.

Web UI

Built into the server: executions with their history and step graph, workflows, workers and schedules, with queries, signals and cancellation.

Schedules and long runs

Cron and interval schedules that start each occurrence exactly once, even across servers, and continue_as_new for workflows that run for months.

Process as code

Diagrams, reference docs, JSON Schema and process diffs generated from the definition, and organisation policies checked in CI.

Safe evolution

Immutable versions, content hashes, and orbit compat to catch breaking changes to inputs, outputs, signals and queries.

Workers in the language you already use

Business logic stays in your services. Each SDK registers task handlers, receives work, and reports results; all six are tested against the real runtime, including over mutual TLS.

LanguagePackageInstall
Pythonorbit-lang-sdkpip install orbit-lang-sdk
TypeScriptorbit-langnpm install orbit-lang
Gosdks/gogo get github.com/AjmalX0/Orbit/sdks/go
Rustorbit-workercargo add orbit-worker
Javaorbit-sdkio.github.ajmalx0:orbit-sdk:0.5.0
C#OrbitLang.Sdkdotnet add package OrbitLang.Sdk

Guarantees, stated precisely

Durable
Every state change is committed to SQLite or PostgreSQL before it takes effect. State is rebuilt from history on restart.
At-least-once
ORBIT never claims exactly-once. Each task carries an idempotency key that is stable across retries and redeliveries, so workers can make side effects safe.
Immutable versions
A workflow name and version are bound to a content hash. Running executions keep the definition they started with.
Secure by configuration
Admin, shared-worker and per-worker credentials, TLS and mutual TLS, with tasks restricted per identity.

Build your first workflow in ten minutes.

The tutorial takes you from installation to a running workflow with a Python or TypeScript worker, a failure and its compensation.