CLI tool
deskree-inc/blok avatar
deskree-inc/blok

blok, a workflow framework where the unit of work is a node

Blok is an open-source framework that enables developers to build lightweight, modular, and scalable backend applications using nanoservices.

2,311 stars85 forksTypeScriptApache-2.0

At a glance

What is it?
Blok lets you build a backend out of small TypeScript units called nodes, grouped into workflows that start from a trigger, and run them through a local runner on port 4000. The architecture is coherent and the repository is well tooled, but the documentation is thin, the naming is muddled across three identities, and the newest tagged release is a pre-1.0 beta from June 2025.
Who is it for?
Adopt blok if you want backend logic expressed as small composable units with per-unit scaling in mind, and you are prepared to read the source to learn the conventions the docs leave out. Do not adopt it for anything that needs durable execution, since the README documents no retries, no persistence and no resume semantics for a workflow that fails halfway.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 88 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 4, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Nodes, workflows and triggers are the whole vocabulary

Blok's model has three nouns and the README defines all three, which is worth doing precisely because the definitions carry the design.

A node is a small functioning unit designed to perform a specific task within a workflow. A workflow is a collection of nodes grouped together in a certain sequence to create a piece of business logic that starts with a trigger. A trigger is an event or condition that starts the execution of a workflow.

The interesting constraint is in the word collection. A workflow is a sequence, not a graph, and it has exactly one entry point expressed as a trigger. That is a much smaller machine than a general orchestrator, and it is why the project can claim lightweight. It also means the framework is making a bet: most backend logic is a linear pipeline, and the value you get from composition is in the units rather than in the control flow.

The stated motivation follows from that bet. The project frames modern backend development as suffering from over-engineered solutions, resource inefficiencies and complex architectures, and positions blok as a way to divide backend logic into reusable single-responsibility units that can be scaled independently. The single responsibility principle is named explicitly, and the scaling story is per unit rather than per application, which is a different deployment model from the usual monolith-plus-queue arrangement.

For the reader, the practical question is what a node can actually do. The first-party node packages named in the build scripts are an API call node, an if-else node, a react node, a node-name node and a node-ui-name node, and the package filter list is where that inventory comes from. A conditional, an outbound HTTP call and a UI-named node is a small set, which tells you the intended composition starts from primitives you extend yourself.

npx nanoctl@latest create project, then the runner on port 4000

The install is a single command run through npx, with no global install and no clone:

bash
npx nanoctl@latest create project

The README says only to follow the instructions of the CLI after that, which means the scaffolded project is the actual documentation. The docs site at blok.build is linked for a getting started page and for a page on the CLI itself.

To run what you have built, the README gives two steps. Start the runner:

bash
npm run dev

Then exercise the workflow over HTTP with Postman, curl or any other client, against `http://localhost:4000/{workflow-name}`. The workflow name is part of the URL path, which tells you something about the execution model that the prose does not: a running workflow is reachable as a named HTTP endpoint on one port, so a trigger is in practice an incoming request and the rest of the sequence runs server-side.

That is a coherent design for the use case the project names, building HTTP APIs, event processing and scheduled jobs from templates. It is also the point at which you should notice what is missing. The README describes no queue, no worker pool, no retry policy, no timeout configuration and no way to inspect a run in progress. If a node in the middle of a sequence throws, the README does not say what happens, and that question is the difference between a workflow runner and a durable workflow engine.

The docs link in the README for a step-by-step example of nodes, workflows and triggers points at `http://localhost:4000/docs/d/quickstart`, which is a localhost address. That link will not resolve for anyone reading the repository, so the quickstart example has to be reconstructed from the concepts and the two commands above until the hosted version is checked.

How the runner finds nodes and workflows: two environment variables and a Makefile

The mechanism that connects a node on disk to a running workflow is visible in the `Makefile`, and it is short enough to read in full:

bash
echo VITE_WORKFLOWS_PATH=${PWD}/triggers/http/workflows > ${PWD}/core/runner/.env.local
echo VITE_NODES_PATH=${PWD}/triggers/http/src/nodes >> ${PWD}/core/runner/.env.local

Two things follow. First, discovery is filesystem-based: the runner is told where the workflow definitions and the node implementations live, rather than being handed a registry. That is consistent with the rest of the project, where a workflow is a sequence in a directory and a node is a package, and it means adding a capability is adding a file rather than editing a central manifest.

Second, the mechanism is Vite's environment variable convention, so the two paths are baked in at build time rather than read at runtime. The prefix is what gives it away. That has a real consequence: a runner built with one set of paths will not pick up a workflow added to a different directory without a rebuild, and the paths are absolute, resolved from the working directory at the moment `make prepare` ran. For local development the Makefile handles it, since its `prepare` target writes the file and its `cli-dev` target runs the CLI. For any other layout, including a container image where the project is copied to a different path, nothing in the README explains how to set these at build time, and that is the first thing to check if a scaffolded workflow is not discovered.

The target paths also reveal the default shape of a generated project. Workflows live under `triggers/http/workflows` and nodes under `triggers/http/src/nodes`, which means the HTTP trigger is the built-in default and that the directory naming encodes the trigger type. The top-level tree agrees: `triggers/`, `nodes/`, `workflows/`, `core/`, `runtimes/`, `sdk/`, `templates/`, `infra/` and `dockerfiles/` are all separate directories, so the trigger, the node implementations, the workflow definitions, the runner, the execution environments and the deployment assets are separable concerns.

Nx, pnpm workspaces, and a lint command that rewrites your files

The repository is a pnpm workspace with an Nx task graph on top, and the root scripts show how the pieces are meant to be driven.

json
"test": "nx run-many -t test",
"lint": "biome check --write --no-errors-on-unmatched --files-ignore-unknown=true",
"doc:generate": "npx typedoc --skipErrorChecking && node ./docs/generateRefLinks.js",
"ci:publish": "pnpm build && pnpm publish -r --access public"

Three observations come out of those four lines.

Running tests is a task-graph operation, not a single command. `nx run-many -t test` executes the `test` target across every project the graph knows about, which is the right shape for a monorepo where the runner, the helper, the shared package and each node have their own tests. The named build filters confirm the package inventory: `nanoctl` for the CLI, `@nanoservice-ts/runner`, `@nanoservice-ts/shared` and `@nanoservice-ts/helper` for the core, `@nanoservice-ts/trigger-http` for the built-in trigger, and the individual node packages built in their own pass.

The lint script is worth reading twice. `biome check --write` means the command modifies files rather than only reporting, so it is a formatter-and-fixer in CI's clothing and will change your working tree on a pre-commit hook. And `--no-errors-on-unmatched` means that if the file globs match nothing, the command still succeeds, which is a convenient way to keep a monorepo green while a package is being added and a quiet way for a broken lint setup to pass unnoticed. Neither is wrong, but a team that assumes `pnpm lint` is a read-only check will be surprised.

The documentation command is the third signal. `npx typedoc --skipErrorChecking` tells TypeDoc to keep going when it cannot resolve a type, which is the same trade the reference implementations in a research library make and the same trade a PHPStan baseline makes: API docs get generated now, at the cost of some pages being wrong or empty. The follow-up step, a script that generates reference links, is a Mintlify convention, which matches the `docs` directory and the `npx mintlify dev` documentation workflow in the README.

Around those are the usual pieces: husky for hooks, a `.changeset/` directory for version bumps, `biome.json` for the lint configuration, `nx.json` for the task graph, `pnpm-workspace.yaml`, `SECURITY.md`, `CODE_OF_CONDUCT.md` and a `CONTRIBUTING.md`.

blok, nanoservice-ts and nanoctl: three names for one project

The naming is the most immediate friction for anyone reading the repository, and it is worth mapping before you touch anything.

The product and the repository are `blok`, the documentation site is blok.build, and the README's own sentences use it as a noun and a verb interchangeably, including the project's own one-line description, which says the framework helps you build backend applications using Blok. The root package in `package.json` is not called blok at all: it is `nanoservice-ts`, at version 0.1.0, authored by Deskree Technologies Inc. The CLI is a third name, `nanoctl`, and the scoped packages are `@nanoservice-ts/runner`, `@nanoservice-ts/shared`, `@nanoservice-ts/helper` and `@nanoservice-ts/trigger-http`.

So the public name is blok, the internal package namespace is nanoservice, and the tool you type is nanoctl. That is survivable once you know, and the README does not explain it anywhere. The same pattern shows up in the small print. The table of contents lists the NPX Package entry and the TS Helpers entry with the same label, Templates. The licence section points to `LICENSE.txt` while the top-level file is `LICENSE`. And the quickstart link points at localhost, as noted earlier.

None of these are serious on their own. Together they are the signature of a README that has been assembled from templates and not fully edited, which matters because the README is otherwise the only user-facing documentation in the repository. The contribution instructions make the same point: the standard fork, branch, commit and push dance with a placeholder commit message, a request to give the project a star, and then ten numbered steps whose real content is running `npx mintlify dev`.

The one substantive claim that goes unsupported is the community library. The README advertises a growing library of community-created nodes and workflows that you can share and benefit from, but it documents no registry, no install command and no index for them, unlike a project whose community extension story is real. Treat that line as aspiration until you find the catalogue.

blok against a plain HTTP service, and against a durable workflow engine

The first alternative is not a framework at all. A single Node or TypeScript service with a router, a couple of functions and a database is less machinery, easier to debug, and has none of the discovery, build-time path and monorepo questions above. For a service with three endpoints and a database table, blok is a net cost. The case for it is the unit of reuse: when the same sequence of logic appears in five places, or when parts of it need to scale or fail independently, composing named nodes beats copy-paste, and that is a real threshold rather than a preference.

The second alternative is a durable workflow engine, and the difference is the important one. Engines built for long-running processes give you execution history, retries with backoff, timeouts, and the ability to resume a workflow after a process crash. Blok's documented model is a sequence of nodes in a running process behind one HTTP port. Nothing in the README mentions persistence, replay, compensation or retry, and the presence of `runtimes/` and `dockerfiles/` at the top level suggests containerised execution but says nothing about what survives a restart.

That gap is not a criticism of the design, it is a scope boundary, but it is the boundary to name before you adopt. A workflow that sends an email, charges a card and writes a row is fine if a crash means the caller retries the whole thing. The same workflow is wrong if a crash halfway through leaves a customer charged and a row missing, because nothing in the documented model will reconcile it.

The other place blok is the wrong tool is anything where a node needs to be isolated. Independent scaling per unit is the project's stated advantage, and it is real, but isolation in the sense of a separate process with its own failure domain is a different claim, and the README's phrase is containerised execution with independent scalability for each blok, which leaves the deployment topology to you.

Apache-2.0, changesets, and a release line that stopped in June 2025

The licence is the Apache License 2.0, stated in the manifest and in the README, which is permissive, allows commercial use and modification, and carries the patent grant and notice requirements. That is a licence a company can adopt without much discussion, which is a genuine advantage over copyleft alternatives for infrastructure tooling. This is a reading of the licence terms rather than legal advice.

The versioning story is the part that needs attention. The two most recent tags are v0.0.1-beta.1 on 2025-06-13 and v0.0.1-beta.2 on 2025-06-16, three days apart, both pre-release betas of the same version. The last push to the repository was on 2026-07-09 and the repository is not archived, so the code has moved substantially since the last published artefact, roughly thirteen months of drift between what you can install and what is in the tree.

A `.changeset/` directory is present, which means the project uses changesets for version bumps, and that is normally a good sign: pending changes accumulate as changeset files and are turned into a release deliberately. Combined with the release history above, the most likely reading is that a large amount of work is sitting unreleased. The root manifest's own version, 0.1.0, sits between the 0.0.1 beta tags and the current state, which is consistent with a workspace version that is not the published version of anything.

So the practical guidance is narrow and specific. If you evaluate this project, work from a clone on the main branch rather than from a published package, read the top-level directory list as the architecture map since the prose does not provide one, and expect to learn the conventions from `core/runner` and from the first-party node packages rather than from the documentation. Before depending on it, ask whether the unreleased delta is acceptable, because that is the only question the release history raises and it is a question only the maintainers can answer.

Editorial conclusion

Adopt blok if you want backend logic expressed as small composable units with per-unit scaling in mind, and you are prepared to read the source to learn the conventions the docs leave out. Do not adopt it for anything that needs durable execution, since the README documents no retries, no persistence and no resume semantics for a workflow that fails halfway. Verify first by running npx nanoctl@latest create project, starting the runner with npm run dev, and calling http://localhost:4000/{workflow-name} directly, then read the runner code in core/runner before you rely on how a failed node is handled.

Frequently asked questions

How do I create a new blok project?

Run npx nanoctl@latest create project and follow the instructions of the CLI. The README gives no other setup path, and points to blok.build for the getting started and CLI documentation pages.

What are nodes, workflows and triggers in blok?

A node is a small functioning unit that performs a specific task within a workflow. A workflow is a collection of nodes grouped in a certain sequence that creates a piece of business logic and starts with a trigger. A trigger is the event or condition that starts the workflow's execution.

How do I run and test a blok workflow locally?

Start the runner with npm run dev, then call the workflow over HTTP with Postman, curl or any HTTP client at http://localhost:4000/{workflow-name}. The workflow name is part of the URL path on port 4000.

How does the blok runner find nodes and workflows?

Through filesystem paths passed as VITE_WORKFLOWS_PATH and VITE_NODES_PATH, which the Makefile's prepare target writes into core/runner/.env.local using the triggers/http/workflows and triggers/http/src/nodes directories. Because these are Vite variables, the paths are fixed at build time rather than read at runtime.

What licence is blok released under?

The Apache License 2.0, declared in the package manifest and described in the README as distributed under the Apache License 2.0. The README refers to a LICENSE.txt file while the top-level file in the repository is named LICENSE.

When was the last blok release, and does it match the code?

The most recent tags are v0.0.1-beta.1 on 2025-06-13 and v0.0.1-beta.2 on 2025-06-16, both pre-1.0 betas, while the last push to the repository was on 2026-07-09. A .changeset directory is present, so there is likely a substantial unreleased delta between the published artefact and the main branch.

Official sources

  1. deskree-inc/blok on GitHub
  2. License: Apache-2.0
  3. Project website
  4. README
  5. Releases
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.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/deskree-inc-blok.svg)](https://hysenlabs.com/projects/deskree-inc-blok)