The company

Delivering next-generation
developer tools.

bckground builds developer tools for engineering resilient software. Tourbillon, our durable execution framework, gives developers the right tools to deliver business critical processes in any environment.

Where we're headed
Agentic engineering CI / CD Developer tooling
Meet Tourbillon
Durable Execution Framework

Focus on success.

Tourbillon makes your code resilient to failures — without asking you to think about them. Write the happy path.


Tourbillon — bckground Open source · Self-hosted · Public beta

Distributed systems fail.

Networks drop. Processes crash. Cloud providers go down. At scale, these aren't edge cases — they're certainties.

Conventional queues, cron jobs, and retried HTTP calls leave your business logic scattered across logs, dead-letter queues, and manual runbooks. Every failure becomes an incident.

You end up writing the same defensive boilerplate repeatedly: checkpoints, idempotency keys, state machines, retry logic, timeout handling. Infrastructure code that has nothing to do with your actual problem.

Process crashes mid-execution No record of progress. Start from scratch.
Network timeout after step 3 of 12 Which steps ran? Did the payment go through?
External API flaps for 90 seconds Exponential backoff? Which version did you write?
Deploy rolls out mid-workflow Twelve workflows frozen, indefinitely.

Everything you need to succeed.

Tourbillon handles the primitives so your team can focus on what actually matters.

01 — RESILIENCE
Automatic failure recovery

Infrastructure fails — your workflow doesn't. Tourbillon persists the result of every function call as it completes. On failure, it replays your code, skipping calls it already completed, and resumes from exactly where it left off.

02 — DURABILITY
Transparent persistence

Every function call result is persisted immediately. Your code is unchanged — it just works, whether it runs once or is replayed across different workers and deployments.

03 — SIMPLICITY
Write functions, not state machines

Define workflows as plain functions; no idempotency boilerplating. Tourbillon injects durability at the infrastructure layer, invisibly, with observability conventions built-in.

04 — OBSERVABILITY
Every run, fully traced

Tracing is built-in; no additional code is needed to get your telemetry from the start. OpenTelemetry traces and logs, for each operation in your workflow, are shipped to your existing observability platform.

05 — VERSIONING
Versioning made simple

Your executions are tied directly to the git revision it was triggered with; no surprises at runtime, even if your execution is long lived.

06 — SDK-FREE
No SDK required

There's no SDK to install or keep in version lockstep - Tourbillon services are HTTP & gRPC native - reducing the overhead in integrating with your existing systems.

Write the happy path.
We handle the rest.

Define your workflow as a plain function. Tourbillon automatically persists every function call result — on failure, completed calls are skipped and execution resumes exactly where it left off. No retries to configure, no idempotency to reason about.

→ 01
Write your logic, nothing else
Write plain functions. No framework wrappers, no retry decorators, no idempotency keys. Your code looks exactly like it would without Tourbillon — because as far as you're concerned, it is.
→ 02
Every call is persisted
Tourbillon records the return value of every function call the moment it completes. A crash, restart, or deploy mid-run changes nothing — the engine replays and skips calls it already has results for.
→ 03
Replay without surprises
On recovery, completed calls are fast-forwarded — never re-executed. No doubled charges, no phantom writes, no inconsistent state. Your function only runs again when it genuinely needs to.
load("error", "codes")
load("fs", "fs")
load("http", "http")
load("json", "json")
load("openapi", "openapi")

def _prompt(client, model, content)!:
    response = client.chat.create_chat_completion(
        model = model,
        messages = [{"role": "user", "content": content}],
    )

    if response.status_code != http.STATUS_OK:
        return codes.INTERNAL(message = "prompt failed")

    return response

def _content(body)!:
    body = json.decode(body)

    if len(body["choices"]) == 0:
        return codes.INTERNAL(message = "No content")

    reply = ""
    for choice in body["choices"]:
        reply += choice["message"]["content"]

    return reply

def Poem(ctx, input)!:
    client = openapi.client(
        spec = fs.read_all("//openai/specs/openapi.yml"),
        base_url = input.llm_url,
        http_client = _http_client,
    )

    prompt = "Can you write me a poem about {topic}".format(topic = input.topic)

    resp = try _prompt(client, input.model, prompt)
    poem = try _content(resp.body)

    pkg = proto.package("openai", contracts = ctx.contracts)
    return pkg.PoemResponse(poem = poem)
load("http", "http")
load("time", "time")

_client = http.client(timeout = time.SECOND * 30)

def BookTrip(ctx, input)!:
    flight = try _reserve_flight(_client, input.flight)
    errdefer cancel_flight()

    hotel = try _reserve_hotel(_client, input.hotel)
    errdefer cancel_hotel()

    payment = try _charge(_client, input.payment, input.total)
    errdefer cancel_payment()

    pkg = proto.package("travel", contracts=ctx.contracts)
    return pkg.Booking(
        "flight_id":      flight["id"],
        "hotel_id":       hotel["id"],
        "transaction_id": payment["id"],
    )
load("error", "codes")
load("http", "http")
load("json", "json")
load("retry", "retry")
load("time", "time")

_retry = retry.with_max_retries(5, retry.with_capped_duration(
    time.SECOND * 30,
    retry.exponential(time.SECOND),
))

_client = http.client(
    timeout = time.SECOND * 30,
    retry_strategy = _retry,
)

def Checkout(ctx, input)!:
    # Create the order in the Checkout service.
    checkout_resp = _post_json(
        _client, input.checkout_url,
        {"customer_id": input.customer_id, "items": input.items},
    )
    if checkout_resp.status_code != http.STATUS_CREATED:
        return codes.INTERNAL(message = "checkout failed")

    order = json.decode(checkout_resp.body)

    # Charge the customer via the Payments service.
    payment_resp = _post_json(
        _client, input.payments_url,
        {"order_id": order["id"], "amount": order["total"],
         "payment_method": input.payment_method},
    )
    if payment_resp.status_code != http.STATUS_OK:
        return errors.INTERNAL(message = "payment failed")

    payment = json.decode(payment_resp.body)

    pkg = proto.package("checkout", contracts=ctx.contracts)
    return pkg.CheckoutResponse(
        "order_id":   order["id"],
        "payment_id": payment["id"],
    )
load("error", "codes")
load("http", "http")
load("json", "json")

_client = http.client(timeout = time.SECOND * 30)

def SyncOrders(ctx, input)!:
    # Pull every order updated since the last successful run.
    resp = _get_json(
        _client,
        input.source_url + "?since=" + input.cursor,
    )
    if resp.status_code != http.STATUS_OK:
        return codes.INTERNAL_ERROR

    page = json.decode(resp.body)

    for order in page["orders"]:
        row = _transform(order)
        _submit_upsert(_client, "orders", row, key = "id")

    pkg = proto.package("sync", contracts=ctx.contracts)
    return pkg.SyncResult(
        "synced":      len(page["orders"]),
        "next_cursor": page["cursor"],
    )

Ready?

Everything you need to build durable workflows with Tourbillon — guides, API reference, and examples.

Open source · Self-hosted · Public beta