CLI Overview
The @f0rbit/corpus package ships a read-only CLI for inspecting and cloning versioned snapshots. Access production data locally, audit metadata across stores, and clone entire backends to file storage for local testing and debugging.
Installation
The CLI is bundled with the @f0rbit/corpus package — there’s no separate CLI package.
Run it directly, without installing, via the scoped package specifier:
bunx @f0rbit/corpus --helpOr install it as a dev dependency and invoke the corpus bin directly:
bun add -d @f0rbit/corpuscorpus --helpOr define a package script:
{ "scripts": { "corpus": "corpus" }}Then invoke as bun run corpus <command>.
Exit codes
- 0 — Command completed successfully
- 1 — Command encountered an error (configuration, storage, or data error)
- 2 — Usage error (missing required argument, invalid flag)
Commands
All commands are read-only and require backend connectivity. Most work with minimal config (see Configuration), though cat optionally uses corpus.config.ts for codec-based decoding.
stores
List all available stores in the backend.
corpus stores # List store IDscorpus stores --counts # Include version count per storecorpus stores --json # Machine-readable formatcorpus stores --quiet # Result only (no decorative text)Examples:
# Bare environment — works with only env credentials$ export CLOUDFLARE_API_TOKEN="xxx" CLOUDFLARE_ACCOUNT_ID="yyy"$ export CORPUS_D1_DATABASE_ID="zzz" CORPUS_R2_BUCKET="my-bucket"$ corpus storesid──────────────userspostscomments—json output shape:
{ "stores": [ { "id": "users", "version_count": 42, "latest_created_at": "2025-07-19T14:23:10.000Z" } ]}version_count and latest_created_at are only present when --counts is passed; without it, each entry is just { "id": "..." }.
versions
List versions in a store with optional filtering.
corpus versions <store> # All versionscorpus versions <store> --limit 10 # First 10 versionscorpus versions <store> --tag published # Versions with tag "published"corpus versions <store> --after 2025-01-01T00:00:00Z # Versions created after datecorpus versions <store> --before 2025-07-01T00:00:00Z # Versions created before datecorpus versions <store> --json # Machine-readable—json output shape:
{ "store": "users", "versions": [ { "version": "v3", "parents": [], "created_at": "2025-07-19T14:23:10.000Z", "invoked_at": "2025-07-19T14:15:00.000Z", "content_hash": "sha256:abc...", "content_type": "application/json", "size_bytes": 2048, "data_key": "users/sha256:abc...", "tags": ["published", "stable"] } ]}invoked_at and tags are only present when the snapshot carries them.
show
Display full metadata for a snapshot version, optionally including observations.
corpus show <store> <version> # Metadata in table formatcorpus show <store> <version> --observations # Include linked observationscorpus show <store> <version> --json # Machine-readable format—json output shape:
{ "meta": { "store_id": "users", "version": "v3", "parents": [], "created_at": "2025-07-19T14:23:10.000Z", "content_hash": "sha256:abc...", "content_type": "application/json", "size_bytes": 2048, "data_key": "users/sha256:abc..." }, "observations": [ { "id": "obs-123", "type": "quality-score", "created_at": "2025-07-19T14:25:00.000Z", "source": { "path": "$.quality" }, "confidence": 0.95 } ]}cat
Output snapshot data. Supports three render paths:
- With config codec: Use a codec defined in
corpus.config.ts(preferred) - Content-type fallback: Auto-render
text/*andapplication/jsonwithout config (for exploration) - Raw binary: Stream literal bytes with
--raw
corpus cat <store> <version> # Use config codec, or fallbackcorpus cat <store> <version> --raw # Literal bytescorpus cat <store> <version> --json # Machine-readable (all modes)corpus cat <store> latest # `latest` alias for most recent version—json output shape:
All three modes produce the same JSON shape, with encoding indicating interpretation:
{ "store": "posts", "version": "v5", "content_type": "application/json", "size_bytes": 512, "encoding": "utf8", "content": "{\"title\":\"Hello\",\"body\":\"...\"}"}For --raw, encoding is "base64" (literal stored bytes as base64). For codec-decoded or fallback paths, encoding is "utf8".
lineage
Show the parent graph of a snapshot, tracing ancestors up to a depth limit.
corpus lineage <store> <version> # Default depth 10corpus lineage <store> <version> --depth 5 # Custom depthcorpus lineage <store> <version> --json # Machine-readableRenders an ASCII tree (default) or JSON with full parent metadata. Cycles are detected and marked (cycle) to prevent infinite output.
—json output shape:
{ "root": { "store": "summaries", "version": "v2" }, "nodes": [ { "store": "summaries", "version": "v2", "depth": 0, "parents": [ { "store_id": "transcripts", "version": "v10", "role": "source" } ] } ]}A node for an ancestor that couldn’t be loaded additionally carries "missing": true (with parents: []).
clone
Copy snapshots from a source backend to a file backend, incrementally skipping versions and blobs already present.
corpus clone <source> <dest> # Clone all versionscorpus clone <source> <dest> --store users # Specific store(s)corpus clone <source> <dest> --tag published # Versions with tagcorpus clone <source> <dest> --dry-run # Preview without writingcorpus clone <source> <dest> --concurrency 8 # Parallel transfers (default 4)corpus clone <source> <dest> --json # Machine-readable summarySource can be:
- An environment name (resolved from
wrangler.tomlor config):corpus clone remote ~/clones/prod - A file path:
corpus clone ~/old-backup ~/new-backup - The name of a config environment:
corpus clone staging ~/test-data
Destination must be a file path (v1 restriction): corpus clone remote ./local-backup
—json output shape:
{ "stores": ["users", "posts", "comments"], "versions_copied": 15, "versions_skipped": 30, "data_objects_copied": 20, "data_objects_skipped": 18, "bytes_copied": 1048576, "dry_run": false}Output formatting
Coloured output
Tables are coloured when stdout is a TTY (interactive terminal), unless --json is set or NO_COLOR is set. --quiet does not affect table colour — it only suppresses spinners and notes (see below).
The progress spinner is gated separately: it only runs when stderr is a TTY, and is disabled under --json, --quiet, NO_COLOR, or when the CI env var is detected.
Quiet mode
--quiet suppresses decorative output (spinners, progress notes) but keeps result lines and summary tables:
corpus stores --quiet # No spinner, just the store listcorpus clone remote ./dest --quiet # Progress notes suppressedJSON mode
--json emits a single, valid JSON document to stdout with no additional formatting or progress output:
corpus stores --json | jq '.stores | length'corpus versions users --json | jq '.versions[0]'JSON output is always valid and parseable, with no ANSI escape codes, spinners, or progress text.
Piping
Spinners (progress indicators) write to stderr, not stdout, so pipes stay clean:
corpus stores | wc -l # stdout clean, spinner on stderrcorpus cat store ver --raw | sha256sum # raw bytes unpollutedNext steps
- Configure your backend connections and optional store definitions
- Clone documentation for incremental backup and data transfer
- Guides: Cloudflare for production deployment details