Repository Export
A repository export is one repository rendered as canonical NDJSON at an exact repository sequence: a header line, one row per item, and a trailer carrying the record count and a SHA-256 over the row bytes. Nothing is retained — the export is folded on request and streamed straight to you.
Export commands require unrestricted repo:read plus repo:checkpoint-read, the same gate a checkpoint download passes. A narrowed or deny-all read matcher is insufficient, including on a public repository. Any personal access token (PAT) missing repo:checkpoint-read must be reissued.
Export a repository
Section titled “Export a repository”wh repo export acme/widgets --output widgets.ndjson--output is required for synchronous, --async --wait, and --token download paths. It refuses to overwrite an existing path, so an interrupted export is never silently discarded. Use --output - to write the stream to stdout; that requires --format pretty, because JSON and JSONL output cannot carry export bytes.
When --async is used without --wait, --output must be omitted — the command prints an export token and exits immediately without downloading anything.
The stream is verified as it arrives: the header must name the repository and mode you asked for, every row must decode canonically, and the closing trailer’s record count and digest must match what was actually read. Nothing is reported until the trailer proves the stream.
On success the command prints the repository, mode, delta base, repository sequence, record count, and digest. With --format json the same fields arrive as one object.
| Flag | Meaning |
|---|---|
--output PATH|- | Where to write the export. Required for synchronous, --async --wait, and --token download paths. Must be omitted when using --async without --wait. |
--mode heads|ops | Export mode. heads, the default, is the current version of every item. ops is not enabled yet; asking for it fails with the error listed under Failures. |
--since-repo-seq N | Export only what changed after sequence N. |
--at-repo-seq N | Pin the export to sequence N. Omit it and the server pins the current committed sequence and echoes it in the header. |
--async | Prepare the export server-side and print an export token, then exit. Do not supply --output with this flag unless also using --wait. |
--async --wait | Prepare the export server-side and download it immediately when ready. --output is required. |
--token TOKEN | Redeem a previously prepared export token and download the result. --output is required. |
--resume | Continue an interrupted export already at --output PATH. |
Two exports at the same sequence produce byte-identical rows, so the digest is a stable identity for the export’s content and not merely a transport checksum.
Row kinds
Section titled “Row kinds”Every line in the export body (excluding the header and trailer) is a row object with a kind field that identifies its type. Export format version 6 defines two row kinds:
kind | Meaning |
|---|---|
"shape" | A shape definition row. |
"thing" | A thing row, including any assertion metadata. |
Row shape: the shapes field
Section titled “Row shape: the shapes field”Every active kind: "thing" row carries a shapes field. In export format version 6, shapes is an array of pinned Shape certificates identifying the shapes the row conformed to at the export sequence. Each entry is a Shape name and version, such as Facility@v2; a Shape from another repository keeps its repository prefix, such as wh:acme/world/Arc@v1. A full export carries every version of every Shape in the repository, retracted Shapes included, so a certificate for one of the repository’s own Shapes always matches a Shape row in the same file. The array is empty ("shapes": []) when the row conforms to no shapes. Tombstone rows ("active": false) do not carry a shapes field.
Assertion rows
Section titled “Assertion rows”Assertions are surfaced as kind: "thing" rows. In addition to the standard thing fields, an assertion row carries two paired fields:
| Field | Type | Meaning |
|---|---|---|
about | object | A reference object identifying the shape or thing this assertion is about. Contains wref (string) and durableId (string), and an optional v field. |
affirmed | array | An array of reference objects (each with wref, durableId, and optional v) identifying the versions this assertion affirms. Each entry is a specific version of a thing or Shape, and v is its version number; an entry whose target was removed has its durable ID as its wref and no v. |
Both fields are present together or absent together. A kind: "thing" row without about and affirmed is a plain thing, not an assertion.
Export a delta
Section titled “Export a delta”wh repo export acme/widgets --output delta.ndjson --since-repo-seq 120A delta carries only what changed after the named sequence, with deletions as tombstone rows ("active": false). Fold a delta onto a base by durable identity — active rows upsert, tombstones remove. The SDKs do exactly that for you.
A delta base older than the server’s epoch floor is refused, and the answer is a full export, not a retry.
Resume an interrupted export
Section titled “Resume an interrupted export”An export interrupted mid-stream keeps the verified rows it had already written:
wh repo export acme/widgets --output widgets.ndjson --resumeResume reads the partial file back, replays it through the same verifier, and asks the server to continue after the last whole row at the sequence the original header pinned. There is no sidecar state file: the partial export describes itself.
Because the file supplies the window, --resume refuses --mode, --since-repo-seq, and --at-repo-seq rather than silently ignoring them, and it refuses a file whose header names a different repository. It also needs a real file — stdout cannot be continued.
Queue a large export
Section titled “Queue a large export”One synchronous export streams per organization at a time. A second concurrent request is refused, and the CLI names the overflow path.
Use --async to prepare the export server-side without taking the synchronous slot. The command prints an export token and exits immediately — do not supply --output in this form:
wh repo export acme/widgets --asyncTo download the result in the same invocation, add --wait:
wh repo export acme/widgets --output widgets.ndjson --async --waitTo redeem a token printed by an earlier --async run:
wh repo export acme/widgets --output widgets.ndjson --token <TOKEN>--async and --resume are mutually exclusive: a resume continues an already-pinned stream, not a new job.
Verify a saved export
Section titled “Verify a saved export”wh repo verify widgets.ndjsonverify re-reads a saved export and checks framing, canonicality, record count, sequence agreement, and the content digest. It resolves no profile, authenticates nothing, and contacts no server — it reads only the named local file.
Distinct failures are reported distinctly: a mangled row, a miscounted trailer, a wrong digest, and a stream that never reached its trailer are four different messages.
Verify before you convert an export to another format. Parquet and CSV carry no trailer, so once the NDJSON is transformed the digest can no longer be checked against it.
Failures
Section titled “Failures”| Condition | The CLI says |
|---|---|
| The delta base has expired | The delta base has expired; run a full export. |
| Another export is already streaming for the organization | Another repository export is already streaming for this organization. Retry with --async --wait to queue this export instead. |
--mode ops is not enabled on the deployment | Export mode “ops” is not enabled on this deployment. |
--async is not enabled on the deployment | Asynchronous repository export is not enabled on this deployment. |
| The queued export job failed | The repository export job failed. Re-running the command starts a new job. |
Limits
Section titled “Limits”--asyncprepares the export in the background and prints an export token you can redeem later with--token TOKEN. Add--waitto download immediately in the same invocation.- Exports are not retained. There is no listing and no re-download by sequence. You can hand an export token from
--asyncto another process or machine. Whoever redeems it with--tokenneeds bothrepo:checkpoint-readand unrestrictedrepo:readon the repository; read access limited to some things is refused. - There is no cancellation command. Abandon the stream instead.
- Verification cannot be skipped. The digest is computed over lines that are being decoded anyway.