Develop

Import your data

Getting an existing dataset — CSV, JSONL, Parquet, or a Postgres table — into the platform, what works today, and how to verify the write landed.

Every other onboarding path assumes data is born inside the platform: a collector pulls it, an agent produces it, a pipeline writes it. This page is for the other case — you already have a dataset (a directory of JSONL, a Parquet export, a Postgres table) and want it queryable here.

The honest state today: there is no first-class bulk file-upload route. You do not POST a CSV and get a dataset back, and there is no generic rac des import <file> verb. What exists is the write path every in-platform producer already uses — typed rows through the data engine — plus the query surface to read them back. Importing your data means mapping it onto a checked-in Rust serde record and driving it through one of the write paths below. That mapping is not overhead you can skip: the engine stores typed, content-addressed rows, not opaque blobs, so a schema is the price of entry and also what makes the data queryable, prunable, and joinable afterward.

The biggest gap — a first-class file-import route so a tenant can hand the platform a Parquet or JSONL file directly — is tracked as planned work; this page documents what runs now.

The shape of an import

Whichever write path you pick, the middle step is the same and is the real work: turn each source record into one Rust serde record.

Follow the raw-plus-per-record split described in Build a vertical. One raw serde type mirrors the source payload as-is (a whole CSV row, one JSON object, one Parquet row group's logical record); a second, per-record message flattens it into exactly one row per logical event. If your source hands back "an array of readings at N timestamps in one object", the raw message keeps the array and the per-record message is one reading at one timestamp. Add these under crates/shared/schema/<yourdomain>/. Everything downstream — the write, the partition pruning, the query — keys off that per-record type.

CSV and Parquet do not carry a Rust schema, so you author it. JSONL that already matches a record type's field names deserializes with far less glue. This is why the format matrix rates JSONL as the lowest-friction source even though none of the four formats has a one-command path.

For a dataset you own and will keep writing to, define the record shape, transform, and narrow read/write routes as one managed application contract. Managed application artifacts describes the schema, pipeline TOML, transform, tests, and API contract that stay visible in Git. During the preview, Redgold coordinates the initial repository shape and dataset name with the customer.

Redgold provisions the dataset, validates the pipeline artifact, and publishes the routes through the managed deployment path after the customer approves the Git change. The route becomes part of the application contract after that deployment and its bounded smoke verification.

This is the right choice when the import is recurring or the dataset is yours long-term: you get a stable route, a named dataset, and the read surface for free.

Path B — the general signed-transaction write

The platform-wide write chokepoint is POST /api/query/transaction/submit. It takes a canonical-CBOR-encoded, signed SubmitTransactionRequest — your rows ride inside a Transaction as TransactionRecordElements, one per record, each carrying a PrimaryKeyId. This is the path the UI and every in-platform producer use, and it is available now without a deploy step, but it is a programmatic path: you construct and sign the transaction with the SDK or native client because the engine verifies the signature over the exact CBOR bytes.

Reach for this when you are writing a one-off batch from your own code and do not want to stand up an app. The record-element registration and PrimaryKeyId construction are the same ones documented in Build a vertical.

Path C — a collector, for recurring external sources

If the data is not a static file you hold but a live external source (an API, a feed, a websocket) you will pull repeatedly, write a collector under crates/data/daq/<source>/ instead of importing by hand. That is the Rust schema → raw JSONL → typed record → Lance path in Build a vertical §3, and it gives you backfill plus live ingestion in one dataset. This is import as an ongoing pipeline rather than a one-time load.

Verify the write landed

In a managed Redgold development environment, the operator can authenticate as the maintained public synthetic principal, list its datasets, and query rows back:

eval "$(rac auth jwt --role public)"   # mints DATA_ENGINE_JWT for the redgold.ai audience
rac des datasets                        # datasets your principal owns
rac des sql "select * from <your_dataset> limit 1"

rac des datasets is owner-scoped: a fresh principal that has not written anything sees {"datasets": []}, which is the expected empty state, not an error. rac des sql lowers a SELECT to a dataflow and runs it against the FROM dataset over the DES request adapter, rendering rows as JSON — this is the reliable read-back and the one to use in a verification loop. Projection-style rac des query --dataset ... exists as well, but resolves a narrower set of datasets, so prefer rac des sql when confirming an import.

rac auth jwt --role public uses repository-managed browser state for the synthetic public test identity. It is an operator verification command rather than customer API-key authentication. Customers verify an onboarded dataset through the approved application's read route after managed publication.

rac des submit looks like a generic import verb but is deliberately scoped to two internal bench datasets; pointing it at any other dataset is rejected. It is not a data-import path — use Path A or Path B.

Format matrix

None of the four common source formats has a first-class one-command import today. Each maps onto the paths above; the difference is how much glue the format costs before it becomes typed rows.

FormatStatus todayHow you get it in
JSONLWorkaround, lowest frictionOne JSON object per line already matches the per-record shape. Deserialize each line into your Rust record type (the checked-in types carry serde derives) and write via Path A or B. Raw JSONL is also the native on-disk format the collector path saves.
CSVWorkaroundNo first-class CSV support. Convert rows to JSONL (or read them directly in a small write program), map each to the per-record Rust type, then as JSONL above. Header names become your record fields.
ParquetWorkaroundParquet/Arrow is the platform's own cold-tier lake format via Rust schema → Arrow → Lance, but there is no route that ingests your Parquet file. Read it, map row → per-record Rust type, write via Path A or B. A first-class Parquet import is the planned gap.
Postgres dumpWorkaroundNo first-class table-import path for tenant data (the engine's own Postgres hot tier is written by the platform, not opened for bulk tenant loads). Export the table to JSONL or Parquet, then follow those rows.

The through-line: every cell routes to "map to a per-record Rust type, then write via Path A or B." The schema authoring is the actual import work; the write mechanics are already there.

Planned: first-class file import

The missing piece is a route that takes a file — Parquet or JSONL first — plus a target dataset and does the map-and-write for you, so a tenant with an existing export does not have to scaffold an app or write an SDK program. This is filed as a backlog issue (2026-07-17-first-class-file-import-route); until it lands, the paths above are the supported ways in.

Copyright © 2026