> ## Documentation Index
> Fetch the complete documentation index at: https://docs.blockx.chaintable.com/llms.txt
> Use this file to discover all available pages before exploring further.

# What is BlockX

> BlockX is a function execution layer for on-chain data: it receives tasks, expands trigger data into calls, runs lightweight functions in parallel in a long-lived Python Executor pool, and has a Writer Plugin write the results back to BlockDB in one shot or return them to the caller.

BlockX is a function execution layer. It stores no data and orchestrates no jobs: BlockDB handles table storage, access, and subscriptions; the Client generates tasks, orchestrates DAGs, and triggers them; BlockX only does the computation in the middle. It receives a task, expands the trigger data into a set of mutually independent function calls, runs them in parallel in the Python Executor process pool on the Worker's own machine, and then hands all the return values to the Writer Plugin in one shot to persist or return.

The core computation model is single-row state computation from `onchain table -> onchain table`: each row in one block of the source table goes through one lightweight function on its own and yields one row of the target table.

## Function execution layer

In the whole data pipeline, BlockX takes on only the "compute" step:

* **Upstream**: BlockDB block tables, normal tables, or parquet partitions on S3 split by bundle provide the input rows; the Client (a notebook, the blockx-py `Pipeline`, or a scheduling system) decides when to compute what for which block.
* **BlockX**: receives the task, scans or receives the trigger data, turns each row into one function call, executes the calls in parallel in the Executor pool, and aggregates the return values.
* **Downstream**: the Writer Plugin writes the aggregated results back to a BlockDB block table or normal table, or returns them to the Client as is.

User-written functions are read-only. A function can read BlockDB through the SDK, call chain node RPC, and call other functions, but all writes are done in one place by the Writer Plugin the task declares, after the functions finish executing.

## The three phases of a task

| Phase   | What it does                                                                                                                                      | Who runs it                                | Output                                                |
| ------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------ | ----------------------------------------------------- |
| Builder | Expands the task's `functionCallConfig` into a call list: reads block rows from BlockDB, or directly adopts the argument list carried by the task | Call Builder, runs serially once           | `call list`                                           |
| Calls   | Delivers each call to a Python Executor to run the user function; IO the function issues is intercepted back to the Worker for unified handling   | Dispatcher + Executor pool, fully parallel | `outputs` (the return values of all successful calls) |
| Plugin  | Writes `outputs` to the target table in one shot, or packages them for return                                                                     | Writer Plugin, runs serially once          | `TaskResult`                                          |

Only flat results pass between the three phases: the Builder does not know what the function will return, and the Writer Plugin does not care which call a given row came from. See [Task and call](/en/concepts/task-and-call) for details.

## Three kinds of input and three kinds of result handling

A task declares "what to compute" and "how to handle the result" with two `(type, config)` pairs. `type` is a name in the Worker's registry; `config` is JSON that only the corresponding implementation parses.

The Call Builder decides where the input comes from:

| `functionCallConfig.type` | Input source                                                                          | Typical use                                            |
| ------------------------- | ------------------------------------------------------------------------------------- | ------------------------------------------------------ |
| `BlockTableCallConfig`    | Reads trigger rows from a BlockDB block table by block (dbScan)                       | Real time: run once per new block                      |
| `InputsCallConfig`        | The argument list carried by the task, one call per row                               | One-off computation, dry-run, offline batch processing |
| `BlockBundleCallConfig`   | Streams trigger rows from S3 parquet by bundle, a segment of 1000 blocks (bundleScan) | Historical backfill                                    |

The Writer Plugin decides where the result goes:

| `resultHandler.type`      | Result destination        | Notes                                                                                                                     |
| ------------------------- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| `BlockTableWriteHandler`  | BlockDB block table (L2)  | The block cluster writes by block, the bundle cluster writes in batches by bundle; the two implementations share one name |
| `NormalTableWriteHandler` | BlockDB normal table (L1) | Upserts by primary key, optionally with a conditional update strategy                                                     |
| `ReturnValueHandler`      | Returned to the Client    | Writes no table; joins all return values into one JSON array and puts it in `TaskResult`                                  |

Each task has exactly one Call Builder and at most one Writer Plugin. See [Call Builder and Writer Plugin](/en/concepts/builders-and-handlers) for details.

## Two invocation entry points

| Entry point            | Protocol                                                    | Semantics                                                                                                                                           | Suited to                                           |
| ---------------------- | ----------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------- |
| task                   | `WorkerService.SubmitTask` + `WatchTasks` / `GetTaskResult` | Asynchronous: submission returns `RUNNING`, and the terminal result is subscribed to or queried separately; one task can contain thousands of calls | Batch computation by block or by bundle             |
| Synchronous invocation | `SyncInvokerService.Invoke`                                 | Synchronous: one RPC runs one function and returns the result on the spot                                                                           | Page debugging, Open API, single online invocations |

Both share the same Python Executor, Function Code View, and read-only IO subsystem. The Sync Invoker does not go through the Coordinator, and has no slot and no Writer Plugin.

## Two clusters

BlockX deploys the same code as two clusters with different process profiles:

| Cluster | Processes                                      | Registered Call Builders                                            | Registered Writer Plugins                                                                   | Intended for                                               |
| ------- | ---------------------------------------------- | ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | ---------------------------------------------------------- |
| block   | `cmd/coordinator` + `cmd/worker`               | `BlockTableCallConfig`, `InputsCallConfig`, `BlockBundleCallConfig` | `BlockTableWriteHandler` (writes by block), `NormalTableWriteHandler`, `ReturnValueHandler` | Real-time single-block computation and one-off computation |
| bundle  | `cmd/bundle_coordinator` + `cmd/bundle_worker` | `BlockBundleCallConfig`, `InputsCallConfig`                         | `BlockTableWriteHandler` (writes by bundle), `NormalTableWriteHandler`                      | Large-scale historical backfill                            |

blockx-py picks the cluster automatically based on the task's config and handler; tasks that need results returned (`ReturnValueHandler`) always go to the block cluster. See [Runtime](/en/concepts/runtime) for details.

## Summary

* BlockX is a function execution layer: it stores no data and does no orchestration; it only expands a task into calls, computes them in parallel, and writes back in one place.
* A task goes through three phases, Builder → Calls → Plugin, and only the flat `call list` and `outputs` pass between phases.
* Three kinds of Call Builder decide where input comes from, and three kinds of Writer Plugin decide where results go; the user function itself is read-only.
* Asynchronous tasks and synchronous `Invoke` are two entry points; block and bundle are two clusters distinguished by profile.

Continue reading:

* [Task and call](/en/concepts/task-and-call): task fields, states, results, and invariants.
* [Function code](/en/concepts/function-code): entry point conventions, arguments and return values, available SDKs, and static auditing.
* [Call Builder and Writer Plugin](/en/concepts/builders-and-handlers): the config shapes of the three kinds of input and three kinds of result handling.
* [Runtime](/en/concepts/runtime): Coordinator, Worker, slot, Executor, and the two clusters.
* [Architecture overview](/en/architecture/overview): component diagram, core constraints, and the reading order for contributors.
