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 | shiwr https://github.com/AjmalX0/Orbit/releases/latest/download/orbit-x86_64-pc-windows-msvc.zip -OutFile orbit.zip; Expand-Archive orbit.zip .cargo install orbit-langdocker run -p 7233:7233 -e ORBIT_TOKEN=change-me ghcr.io/ajmalx0/orbit:latestworkflow 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
-
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.
-
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.
-
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.
#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
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.
$ 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
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.
| Language | Package | Install |
|---|---|---|
| Python | orbit-lang-sdk | pip install orbit-lang-sdk |
| TypeScript | orbit-lang | npm install orbit-lang |
| Go | sdks/go | go get github.com/AjmalX0/Orbit/sdks/go |
| Rust | orbit-worker | cargo add orbit-worker |
| Java | orbit-sdk | io.github.ajmalx0:orbit-sdk:0.5.0 |
| C# | OrbitLang.Sdk | dotnet 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.