All docs

Docs · Reference

Datasets

A stack can bundle large, read-only lookup data — a book catalog, airport codes, a GeoIP table — as a SQLite file that deploys WITH the code and is queried locally at runtime. No external API, no separate lookup service.

Authoring

Two files under the reserved DATASETS/ subtree, paired by name:

OPS/<stack>/
  DATASETS/
    books.sqlite    the artifact (any schema; FTS5 supported)
    books.yaml      the named queries

The manifest declares every query the runtime may execute. Nothing else — not even a well-formed SELECT — can run against the artifact, and parameters are always bound:

queries:
  search:
    sql: |
      SELECT b.isbn13, b.title, b.author
        FROM books_fts f JOIN books b ON b.rowid = f.rowid
       WHERE books_fts MATCH ?
       ORDER BY rank
    max_rows: 10          # optional; tightens the node cap, never widens
  by_isbn:
    sql: SELECT * FROM books WHERE isbn13 = ?

Querying

WHEN @web.req.url.query.q.0 != ""
  EXEC "txco://dataset"
    WITH dataset = "books",
         query = "search",
         args = &array(@web.req.url.query.q.0)

The result lands at into (default _dataset, private to the flow):

{
  "_dataset": {
    "dataset": "books",
    "query": "search",
    "rows": [
      {
        "isbn13": "…",
        "title": "…"
      }
    ],
    "count": 1,
    "truncated": false
  }
}

Optional WITH params: args (positional binds), limit (tightens the row cap), stack (read another of your tenant’s stacks), into. Errors arrive as dataset.error.{code,message} — handle with WHEN @dataset.error. Codes: txco_dataset_not_found, txco_dataset_unknown_query, txco_dataset_invalid_arg, txco_dataset_missing_artifact, txco_dataset_store, txco_dataset_query.

What apply does

txco apply hashes each artifact by streaming (never in memory), asks the chassis HEAD /blobs/sha256/{hash}, streams the bytes only when missing, and references them from the version as a fingerprint row. Unchanged artifacts cost one HEAD. txco pull streams them back down the same plane, hash-verified.

Activation is the gate: the artifact must be in the content-addressed store, the manifest must parse, and every declared query must PREPARE read-only against the shipped schema. A typo’d column or a sneaky DELETE fails the deploy — with the file and query named — not the request path.

Runtime model

Artifacts are immutable (the reference is their content hash), so each node opens them read-only + immutable: no locks, no journal, shared across requests. Enforcement is layered: only declared queries run; the connection is query_only; and a SQLite authorizer default-denies everything but reads, so even a write that somehow reached preparation dies with “not authorized”.

Nodes fetch an artifact from the content-addressed store on first use and keep it in a local disk cache (fleet nodes prefetch on activation). When the store is the bundled local-disk backend the artifact is opened in place — zero copies at any size.

Limits

knobdefaultwhat it bounds
--dataset-max-file-bytes4 GiBartifact size at upload + activation
--dataset-max-rows200rows per query (manifest max_rows and WITH limit clamp under it)
--dataset-cache-bytes4 GiBnode-local materialise cache (LRU)
--dataset-cache-dir./chassis/data/datasetswhere cached artifacts live

Responses are additionally capped at 1 MiB of rows (truncated: true when hit). Queries run under the ordinary per-op timeout (WITH timeout raises it, up to the node max).

Superseded artifact versions currently stay in the content-addressed store (no garbage collection yet); at multi-GB scale, expect storage to grow with every changed-artifact deploy.

A worked example lives at examples/dataset-lookup/.

Edit this page · View as markdown