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

# Customer compute

> Run bulk computation in your own infrastructure and bring the results back into your app

Bijection runs your app's transactions, queries, operations and maps. Bulk and
specialist computation runs in your own infrastructure: model training, simulations,
spatial joins over millions of rows, raster tiling, routing and optimization, file
conversion. Your job reads [datasets](/datasets/overview) at exact snapshots, writes
its result to a table in your own catalog, and submits it. Bijection checks the
result before your app sees it.

| Step | Who | How |
| - | - | - |
| Read inputs | Your job | A [checkpoint](/datasets/overview#read-an-exact-snapshot) of the datasets it needs |
| Write the result | Your job | One snapshot of an external dataset's table, with your own catalog credentials |
| Submit it | Your job | A producer grant, never a deployment administrator key |
| Verify it | Bijection | The snapshot's schema, identity and keys against the dataset's declaration |
| Use it | Your app | A collection sync, or a synced table, from the accepted version |

## Declare the result

An external dataset declares the result's columns and their stable field IDs:

```ts bijection/coverage.ts theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
import { defineDataset } from "@bijection/datasets";
import { geoShape } from "@bijection/datasets/geometry";
import { v } from "bijection/values";

export const orderCoverage = defineDataset({
  key: "orderCoverage",
  row: v.object({
    cell: v.string(),
    orders: v.int64(),
    area: geoShape({ types: ["Polygon"] }),
  }),
  fieldIds: { cell: 1, orders: 2, area: 3 },
  rowKey: ["cell"],
  semantics: { kind: "currentState", businessTime: { kind: "none" } },
  origin: { kind: "external" },
});
```

Bind it to the table your job writes. Bijection records the table but never creates
or writes it; a keyed dataset names 1 to 32 logical buckets:

```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
bijection dataset bind orderCoverage --catalog analytics --table results.order_coverage --buckets 4
```

## Give your job a producer grant

Mount the producer route on your HTTP router:

```ts bijection/http.ts theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
import { httpRouter } from "bijection/server";
import { datasetProducerHttpAction } from "@bijection/datasets/producer-http";
import { components } from "./_generated/api";

const http = httpRouter();
http.route({
  path: "/datasets/producer",
  method: "POST",
  handler: datasetProducerHttpAction(components.datasets.producer),
});
export default http;
```

Then issue a grant for the datasets the job produces. The token is printed once:

```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
bijection dataset grant producer --datasets orderCoverage --expires 30d
bijection dataset grant revoke producer --digest <digest>
```

A grant names at most 16 datasets and lasts at most 30 days. It authorizes only
describing those datasets, submitting their snapshots and, if your app declares them,
recording their map archives.

## Submit a snapshot

Your job writes one snapshot of the bound table and submits its ID, naming the
dataset versions it read as provenance. The example producer
(`examples/iceberg-consumers/bijection_producer.py`) does this in Python with
PyIceberg:

```python theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
from bijection_producer import ProducerClient, checkpoint_inputs, ensure_table, write_snapshot

client = ProducerClient("https://<name>.bijection.run/datasets/producer", token)
described = client.describe("orderCoverage")       # contract and bound table
table = ensure_table(catalog, described)            # exact field IDs on first use
snapshot = write_snapshot(table, rows, described["definition"])
[orders] = checkpoint_inputs("manifest.json", ["orders"])
client.submit("orderCoverage", key="run-2026-10-09", snapshot_id=snapshot,
              provenance=[orders["version"]])
print(client.wait("orderCoverage", "run-2026-10-09"))
```

Bijection accepts the snapshot as the dataset's next version only if:

* the table names the dataset in its `bijection.dataset` table property and is the
  same table earlier versions came from;
* the snapshot's schema is exactly the declared fields at their field IDs, types and
  nullability;
* its files carry no delete files;
* a keyed snapshot holds each key once.

Otherwise the submission is refused with the reason, and the dataset keeps its
current version. A dataset with markings must carry every marking of its provenance.
Retrying with the same key returns the same submission, so a lost reply is safe.

## Use the result in your app

A collection sync keeps one of your collections equal to the latest accepted
version, admitting every geometry and computing its spatial index cell; see
[geospatial data](/geospatial/overview). For a keyed result that should arrive as
incremental changes, a [synced table](/integrations/synced-tables) can read it
through a dataset reader that you run next to your lake, with a reader grant
(`bijection dataset grant reader`).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.