Hatchet v2: the orchestration layer for modern apps.
It’s a big day for us at Hatchet. We’re announcing Hatchet v2: a universal orchestration layer that works across your entire stack. You can now use Hatchet’s durability and observability features to coordinate sandboxes, serverless endpoints, container-based runtimes, and everything in between.
Perhaps more importantly, I’ve learned a ton about animating little ASCII and ANSI components to pull together the modern hero’s journey. Christopher Nolan (and Homer?), we’re coming for you:
Alongside Hatchet v2, we’re also announcing our $5.7M seed round. Some investors will say this “buries the lede,” but I’ve never read a fundraising announcement deserving of one. We’d rather talk about what we’ve been building with the cash.
Everything is async now
From the beginning, Gabe and I bet on the nature of software becoming more distributed and asynchronous. New agentic features are rapidly accelerating this trend as applications are doing more work in the background on behalf of a user. While this is most often seen in coding agents, it will become the norm in every software stack across every vertical.
As a result, modern apps are no longer a simple web server. They’re responsible for some combination of data ingestion, inference, reasoning, data processing, and more recently, unsafe arbitrary code execution. These different features require new infrastructure components deployed at an enormous scale.
But running large-scale, distributed, asynchronous apps results in overhead as every tool call, data pipeline, complex workflow, and agent becomes its own distributed system. When deployed correctly, distributed systems can make your system more reliable and scalable; when done incorrectly, they create a house of cards, leading to instability and poor user experience. There are two main challenges:
- Coordinating work across components while keeping your application in a consistent state.
- Properly decoupling components to ensure reliability even if one component fails.
The solution to these problems is orchestration, and we see it as the core service behind every modern application stack. While traditional orchestration systems are coupled to a single compute model or pattern, we’re building Hatchet to be the universal orchestration engine.
Hatchet: a quick refresher
Until now, Hatchet has focused on providing the best durable execution platform for long-lived, container-based applications. This engine processes billions of task runs a month for companies like Linear, Cursor, Greptile, and Enter:
- Feb 20261.42B task runs
- Mar 20262.34B task runs
- Apr 20263.02B task runs
- May 20263.33B task runs
- Jun 20263.58B task runs
- Jul 20263.72B task runs
- Aug 20264.78B task runs
- Sep 20265.06B task runs
- Oct 20265.91B task runs
If you’re unfamiliar with the platform, it’s often helpful to explain what Hatchet is not:
- Hatchet is not an agent framework. We sit a layer lower in the stack: we provide useful primitives for all manner of async and distributed workloads. This happens to be really, really useful for agents, and agentic workloads often inform our features.
- Hatchet does not run your compute. Your workers, sandboxes, and endpoints run on your own infrastructure (or other people’s infrastructure). Hatchet is entirely focused on building the most performant and scalable orchestration engine.
Today, we’re expanding the orchestration capabilities of Hatchet far beyond our existing SDKs and features, using our core engine to build the next generation of use-cases.
Durable Streams
First up is durable streams, a new feature which brings persistence and replay guarantees to messaging in Hatchet.
As applications get more distributed, it’s often necessary to pass messages (like LLM tokens, output such as images and video, signals, and status updates) between different components of your stack. There are generally two approaches to this: pub/sub systems (Redis Pub/Sub, NATS without Jetstream) and distributed event logs (Kafka, RabbitMQ Streams). The first is often used for messages which don’t need persistence, while the latter is used in cases where consumers might need to read messages off the event log at some later point in the future.
We’ve supported the first use-case for a long time; we’ve always provided a way to subscribe and publish ephemeral messages in Hatchet. But this never sat right with us, because our queue is built around the concept of durability, where tasks are persisted, available for future replay and retries. Ephemeral messages are fine if the work can truly be lost without impacting user experience, but there are lots of cases where ephemerality can lead to difficult race conditions and edge cases, like if a consumer subscribes just a fraction of a second too late or disconnects at just the wrong time.
On the other hand, using Kafka or RabbitMQ Streams is the wrong fit for many stacks. These systems work great as massively scalable firehoses of data, where consumers can be decoupled from producers and consume at their own rate, typically for ingestion into a data warehouse. But these systems require partitions to split the event log into manageable chunks, and partitions are not ergonomic. For agentic applications in particular, it’s useful to have a single topic per agent session, where the raw LLM output can be streamed, buffered, and written to a store while the messages get forwarded to other consumers. It’s difficult to model this with partitions, topics, brokers, and consumers.
What developers need is a simple, topic-based API, and that’s what our new durable streams feature provides:
// Publish to a topic, from a task or anywhere else
await hatchet.streams.publish("session-42", "hello");
// Subscribe: every message, in order, from the first one or from a cursor
for await (const event of hatchet.streams.subscribe("session-42", { cursor })) {
console.log(event.payload, event.cursor);
}# Publish to a topic, from a task or anywhere else
await hatchet.streams.publish("session-42", "hello")
# Subscribe: every message, in order, from the first one or from a cursor
async for event in hatchet.streams.subscribe("session-42", cursor=cursor):
print(event.payload, event.cursor)In particular, we’ve optimized the durable streams architecture for:
- A very large cardinality of topics; tens of thousands active at a time. This maps well to how agent sessions behave at scale.
- Strict ordering guarantees, with cursor-based consumers.
Sandboxes
If you’ve been overwhelmed by the number of sandbox providers lately, you’re not alone. We’re perhaps unique in that we’re not interested in providing sandboxes-as-a-service; instead, we’re interested in integrating well with many different sandbox providers, so that users can freely choose the sandbox infrastructure that works best for them.
As a result, we’re releasing a new @hatchet-dev/sandbox package that lets you invoke sandboxes directly from Hatchet tasks. This package integrates with a number of excellent providers already, including E2B, Daytona, Archil, Freestyle, and more.
import { HatchetClient } from "@hatchet-dev/typescript-sdk/v1";
import "@hatchet-dev/sandbox"; // adds hatchet.sandbox to the client
const hatchet = HatchetClient.init();
export const agentSession = hatchet.durableTask({
name: "agent-session",
executionTimeout: "1h",
fn: async (input: { conversationId: string; repo: string }) => {
// Runs on the provider connected to your tenant. Each call is a
// Hatchet task and takes a snapshot, so a crash picks up where it
// left off.
const sandbox = await hatchet.sandbox.create({
id: input.conversationId,
});
await sandbox.exec(`git clone ${input.repo} app`);
const tests = await sandbox.exec("cd app && npm test");
await sandbox.files.write("app/REPORT.md", tests.stdout);
await sandbox.kill(); // the snapshots stay
return { passed: tests.exitCode === 0 };
},
});
// Later, from anywhere with a Hatchet client: pick the session back up.
const again = await hatchet.sandbox.connect("conv-42");
await again.exec("cd app && git pull");After chatting with many of our users, it’s clear that sandboxes are an incredibly useful tool for a lot of agents. Using the sandbox package, we’ve made it easy for users to coordinate sandbox provisioning and lifecycle operations, to track execution alongside other workflows, and to manage long-running agent sessions without relying on a single process to keep everything running.
I hear the objection: what value is there in integrating with sandbox APIs when it takes me just a few minutes to spit out an integration with Claude? Beyond the observability features (tracing and logging), our sandbox package is focused on durability. When a sandbox turn ends, it takes what we call a coordinated snapshot on the sandbox provider to create a snapshot ID which is stored alongside the Hatchet durable event log. This makes your sandboxes automatically resumable and your agent sessions automatically durable.
In other words, we’re extending the same orchestration, observability, and durability guarantees in Hatchet to your sandbox provider of choice.
Serverless Environments
Next, we’re announcing support for serverless functions, like Cloudflare Workers and Vercel. Simply configure your serverless runtime in Hatchet as a new endpoint, and Hatchet will be able to invoke your tasks:
// Cloudflare Workers
import { cloudflare } from "@hatchet-dev/serverless/cloudflare";
import { workflows } from "./tasks";
export default cloudflare({ workflows });
// Vercel (Next.js)
import { vercel } from "@hatchet-dev/serverless/vercel";
import { workflows } from "@/hatchet/tasks";
export const { GET, POST } = vercel({ workflows });Serverless runtimes are fully compatible with Hatchet tasks: you can use Hatchet’s wide array of features, like cron jobs, OpenTelemetry ingestion, durable execution, durable streams, and much more.
This has been a long time coming. For the past 3 years, Hatchet’s worker API has been deployed as a gRPC server. There are lots of reasons for this, like support for long-lived connections, native protobuf serialization, built-in middleware and runtime options. However, gRPC support is quite limited in serverless environments, which has made it difficult to support these runtimes well.
This changed a few months ago, when we migrated to the ConnectRPC protocol instead (a superset of gRPC). The ConnectRPC protocol supports HTTP/1 and HTTP/2 connections, and allows us to make Hatchet engine features available to a number of different clients.
Later this week, we’ll be releasing a set of demos which show just how powerful running serverless endpoints through Hatchet can be.
Operators
Lastly, we’re announcing a new feature for platform teams called Operators. Operators are what make all of these new integrations possible.
In the simplest case, these are a special type of Hatchet worker that are responsible for provisioning infrastructure to create tasks. For example, the sandbox operator is responsible for defining a task in Hatchet called sandbox, and when that task is invoked, it spawns the necessary infrastructure to create tasks.
Operators are special, because they can dynamically register and unregister tasks on the Hatchet engine, and they can hook into lower-level capabilities of our durable execution engine.
One of the particularly interesting architectural shifts in Hatchet v2 is that every DAG (directed acyclic graph) is powered by an operator, creatively called the DAG Operator. For each DAG which is invoked in Hatchet, a durable task is run inside of the Hatchet engine, spawned by this operator. This durable task is responsible for the lifecycle of the DAG.
Using operators, you can extend the features of the Hatchet engine to solve other orchestration problems. For example, you can build an operator which is responsible for spawning Kubernetes jobs as tasks. Or an operator that creates an AWS Batch invocation. Or an operator to kick off a new model training on a fleet of GPUs. The world is your oyster!
Making Hatchet easier to adopt
We want to compete on reliability, performance, and cost-effectiveness, not by restricting access to Hatchet. That’s why Hatchet’s core engine will remain 100% MIT-licensed and why we continue to invest in making it easy to run, operate, and extend yourself.
It’s also why we recently announced our move away from subscription-based plans for Hatchet Cloud to offer pure pay-as-you-go with a generous free tier. We’re already seeing encouraging results from that: signups have more than doubled, conversion from free to paid has tripled, and activation is rocketing up.
We recognize that many enterprises have requirements around data security, privacy, and residency that influence where infrastructure can run. Increasingly, these organizations want to self-host or explore bring-your-own-cloud (BYOC) Hatchet deployments. Our open-source foundation and flexible deployment options allow us to support teams across these different environments.
Where we go from here
The number of ways to execute code will continue to grow, and new runtimes, sandbox providers, and compute platforms will emerge. We don’t think developers should have to rebuild the orchestration layer every time they adopt one.
We want Hatchet to be the layer that makes all these systems work together. We’re opinionated about the primitives applications need (e.g. durable tasks, messages, streams, coordination guarantees) but much less opinionated about how or where developers compose and execute those primitives.
From here, we’ll continue investing in the performance, scalability, reliability, and extensibility of the Hatchet engine, while keeping its core open source and MIT-licensed.
Thanks to everyone building on Hatchet and pushing us to make it better, and to our investors. We couldn’t have gotten this far without you, and we’re so excited to see what you build with v2.
