Obelisk runs durable workflows out of a WASM component sandbox
Durable & Deterministic Workflow Engine
At a glance
- What is it?
- Obelisk is a Rust workflow runtime built on the WASM Component Model where deterministic workflows replay from a persisted execution log while activities run sandboxed with automatic retries. It suits teams that need recoverable background jobs with an auditable trace, and it is a pre-release whose CLI, gRPC, WIT and database schema are all still moving.
- Who is it for?
- Adopt Obelisk when you need background work that survives a crash, retries safely, and leaves an auditable per step record, and when you are willing to write activities against WIT and accept a pre-release API. Do not adopt it if you need a stable public interface today, since the project warns to expect changes in the CLI, gRPC, WIT and database schema, and do not adopt it as a general job queue when a database backed queue would do.
- Can I use it commercially?
- Yes, with strict conditions. AGPL-3.0 is a network copyleft licence: if people use a modified version over a network, for example as a hosted service, you must offer them its source code under the same licence.
- Is it still maintained?
- Yes. The repository received new commits within the last day.
- What is it written in?
- Mainly Rust, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Determinism is the contract, not an optimisation
Obelisk is a deterministic workflow runtime built on the WASM Component Model, and the whole design follows from that one word. The core principles section lists replayable workflows, with deterministic execution ensuring reliable recovery, debugging and auditing, and runtime guaranteed determinism with a fully replayable persistent execution log and thus crash resilience. The distinction from a normal job runner is where the value sits. A conventional background worker re-executes code when it retries, which means a crash mid-task restarts the logic and any side effect that already happened happens again. Obelisk separates the two halves. The workflow is deterministic code whose decisions are recorded, so it can be replayed to reconstruct exactly what it decided and when. The activities, the parts that touch the outside world, are retried. That is the standard durable functions shape, expressed with WASI components rather than with a language specific SDK, and the project names sandboxing and auditing of AI-assisted code, periodic tasks, background jobs, batch jobs and end to end testing as its use cases.
Activities must be idempotent, and the runtime will not fix that
The activity contract is stated in one line and it is the load bearing one. Activities must be idempotent, meaning retriable, and this contract must be fulfilled by the activity itself. The runtime offers automatic retries on errors, timeouts and panics described as WASM traps, and persistent execution results, and support for HTTP requests through the WASI 0.2 HTTP client. So the runtime will happily call your activity again after a timeout or a trap, and if that activity charged a card or sent an email, it does so twice unless you wrote it to be safe. That is not a gap in the implementation, it is the division of responsibility. The workflow decides what should happen, the activity performs it, and the activity has to tolerate being performed again. A team that ignores this will find that the retry policy is the thing that breaks their integration, and the durable execution log will faithfully record the double write. Idempotency keys, a lookup before the side effect, or a naturally idempotent operation are the three answers, and all three are your code rather than the engine's.
A web UI ships in the binary, and it is a time machine
Every release binary includes the web UI, served at http://localhost:8080 by default, and the feature set is more than a dashboard. You can explore deployments and component dependencies, inspect execution traces, replay workflows, and browse recorded events and application logs. The control interfaces section adds that the web UI can view and submit function executions, view execution logs and WIT definitions, and provides a time-traveling debugger showing backtraces and sources of recorded events. The screenshots the README links give a sense of the granularity. There is a deployment graph, an execution list with lifecycle filters and results, an execution trace showing a durable agent session with child activities and recorded events, a log view of persisted workflow events and their parameters, and separate views for application logs and system logs. A dependency graph plus a replayable event log is what makes a workflow engine auditable rather than merely reliable, and shipping it inside the binary means you get it without a second service to deploy.
WASM components and WIT define the API surface
The type safety story runs through the component model. The schema-first design with end to end type safety uses the WASM Component Model and WIT IDL for generating API bindings, and the repository has a wit/ directory at the top level to hold those interface definitions. The design consequence is that your workflow and your activities talk to each other through a typed interface rather than through a shared language runtime, which is what lets a workflow written in one language drive an activity written in another, and what makes the WIT definitions the contract you can read. There is a proto/ directory alongside it, and the runtime exposes a gRPC API for programmatic interaction. The activities and webhook endpoints are WASI components, with webhook endpoints mounted as a URL path serving HTTP traffic and with support for spawning child executions. Deterministic workflows also support spawning child executions with structured concurrency, which is the mechanism for a parent execution to wait for the children it started, and the work stealing executor provides concurrency limits and customizable retry handling.
Installing it, and the two databases behind the log
The runtime is a single binary that executes deterministic workflows, activities and webhook endpoints, persisting steps in an execution log using SQLite or PostgreSQL, and the supported platforms are Linux x64 and arm64 with musl, glibc v2.35 or later and NixOS, plus macOS on x64 and arm64. A prebuilt binary is one script:
curl -L --tlsv1.2 -sSf https://raw.githubusercontent.com/obeli-sk/obelisk/main/download.sh | bashBuilding from source needs protoc and cargo:
cargo install --locked --git https://github.com/obeli-sk/obelisk --branch latest obeliskThe Docker route generates a server configuration and then runs the server, with the container publishing the web UI on 8080 and the gRPC API on 5005:
mkdir config
docker run getobelisk/obelisk generate server-config > config/server.tomlThe default storage is SQLite, which suits a single node and a local install, and PostgreSQL is the option when the execution log needs to be shared or the write volume is real. The repository has separate server-testing-wasm-postgres.toml and server-testing-wasm.toml configuration files, so both paths are exercised.
The persisted value limit is the setting to read before you deploy
One configuration detail deserves its own attention, because it changes what your workflows can persist. The server defaults to a 1 MiB compact JSON-encoded limit for each newly persisted execution parameter array, result, stub result, persisted history value or failure diagnostic. Self-hosted operators can set a positive value in server.toml:
[limits]
max_persisted_value_size_bytes = 1048576The interesting part is what happens when you change it. Each top-level execution snapshots the effective limit, and child and scheduled executions inherit it, so a configuration change applies only to new execution trees and cannot change replay behaviour. That is the correct design for a durable engine, since changing a limit mid flight would mean a replay no longer matches the original run, but it has a practical consequence. If you raise the limit for new work, the old executions keep the old limit, and if you lower it, the new ones cannot store what the old ones could. Existing databases require no schema migration, and executions created by older releases retain their legacy behaviour. Read that limit against your actual payloads before the first large run, because a truncated result is exactly the kind of failure that a replay will faithfully reproduce.
What this is still missing, and the pre-release warning
The warning at the top of the README is the thing to plan around. This is a pre-release, and it says to expect changes in the CLI, the gRPC interface, the WIT and the database schema. Four surfaces changing at once is unusual even for a pre-release, and it means the WIT definitions you write against today are not a stable contract. The licence is AGPL-3.0, with the LICENSE-AGPL file at the root, which is the copyleft choice and a decision any organisation deploying it as a service should look at. The release cadence shows the same story, with v0.43.0-rc.6, v0.43.0-rc.5 and v0.43.0-rc.4 published on consecutive days in mid October 2026, and the last push on 2026-10-20, so the project is moving fast. The repository is a large Rust workspace with crates/, src/, tests/, scripts/, examples/ and multiple app and deployment template configurations for exec, JavaScript, VM and WASM targets, plus a ROADMAP.md and a MIGRATING.md. That last file is worth reading before an upgrade, since it exists for exactly the version transitions the pre-release warning predicts.
Editorial conclusion
Adopt Obelisk when you need background work that survives a crash, retries safely, and leaves an auditable per step record, and when you are willing to write activities against WIT and accept a pre-release API. Do not adopt it if you need a stable public interface today, since the project warns to expect changes in the CLI, gRPC, WIT and database schema, and do not adopt it as a general job queue when a database backed queue would do. Verify first that your activities can be made idempotent, which the contract requires of them, that the persisted value limit fits the payloads you intend to store, and that the 1 MiB default is not going to truncate a result you need to replay.
Frequently asked questions
What does Obelisk do that a normal background job runner does not?
Obelisk keeps deterministic workflows in a persistent execution log so they can be replayed for recovery, debugging and auditing, while activities run sandboxed and are retried automatically on errors, timeouts and WASM traps. The runtime guarantees determinism for the workflow half and pushes the retry safety onto the activity, which must be idempotent.
How do I install the Obelisk runtime?
You can run the download script, curl -L --tlsv1.2 -sSf https://raw.githubusercontent.com/obeli-sk/obelisk/main/download.sh | bash, use the getobelisk/obelisk Docker image, or build from source with cargo install --locked --git https://github.com/obeli-sk/obelisk --branch latest obelisk, which requires protoc. There is also a Nix option using the obeli-sk Cachix cache.
What is the default limit on persisted values in Obelisk?
The server defaults to a 1 MiB compact JSON-encoded limit for each newly persisted execution parameter array, result, stub result, persisted history value or failure diagnostic. Operators can change it with max_persisted_value_size_bytes under the limits section of server.toml, and each top-level execution snapshots the effective limit so the change only affects new execution trees.
Is Obelisk stable enough to depend on?
The README marks it as a pre-release and warns to expect changes in the CLI, gRPC, WIT and database schema. Recent releases are release candidates, v0.43.0-rc.4 through rc.6, published within days of each other, and the project carries a MIGRATING.md for version transitions.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/obeli-sk-obelisk)Was this useful?