cratefield-cli (fz)
The venture CLI: fz migrations collect, fz migrations apply, fz data export / fz data import, fz doctor, fz modules.
fz links against your venture's compiled-in harness, so it runs as a bin
target inside the venture repo — the pattern the venture template
ships:
# venture Cargo.toml
[[]]
= "fz"
= "src/fz_main.rs"
[]
= "0.1"
// venture src/fz_main.rs
Then:
fz migrations collect [--dialect sqlite] [--out migrations]
Walks the harness's modules in config order and writes
migrations/<GGGG>_<module>_<NNNN>_<name>.sql — the format
wrangler d1 migrations apply executes in lexical (apply) order.
migrations/.harness-lock.json pins <module>/<migration-id> to the
global file name plus a sha256 of its content:
- locked entries are never renamed or renumbered;
- new migrations append with the next
GGGG; - exits non-zero when a locked file is missing or was edited after being applied (restore the file or add a new migration instead).
--dialect postgres for collect is not a thing: collect writes the
wrangler/D1 (sqlite) flow. Postgres migrations apply directly — see
fz migrations apply below.
fz migrations apply [--dialect postgres] --url <URL>
Applies the harness's module migrations directly to a Postgres database
(issue #18): per module the postgres migration set when shipped, else
the sqlite set when it passes the portable-SQL lint, in lock order
(the order migrations collect pins), tracked idempotently in
harness_migrations(id, applied_at). The native counterpart of
wrangler d1 migrations apply.
Requires building fz with the crate's postgres feature (so sqlx and
tokio stay out of the default, wasm-safe dependency graph):
[]
= { = "0.1", = ["postgres"] }
fz data export [--plan] --db <PATH> --out <FILE.jsonl> / fz data import [--append] [--plan] --url <URL> <FILE.jsonl>
Moves a venture's D1 data to Postgres (issue #21). Export reads a
venture SQLite database — D1 is SQLite; the Cloudflare-side step is
wrangler d1 export loaded into a local file (docs/DATA-MOVE.md is
the whole runbook) — and writes one JSON Lines file: a manifest line
(per-table row counts and sha256, tables in lock order), then one
{"table","row"} record per row. --plan prints the per-table summary
without writing.
Import (built with the crate's postgres feature, like migrations apply) loads that file into Postgres in lock order:
- every table's sha256 is verified against the manifest before anything is written — a tampered or truncated file leaves the target untouched;
- a non-empty table is refused without
--append(naming the table and the flag) before any write;--appendadds to it, and overlapping primary keys fail loudly rather than duplicating; - inserts are batched multi-row statements inside one transaction per table; values bind by the target column's Postgres type (typed NULLs included), and the portable subset is TEXT/INTEGER/REAL/BOOLEAN — anything else fails by name;
- after the writes, row counts are verified against the manifest;
--planprints the target's state (tables, incoming rows, existing rows, refusal warnings) and writes nothing.
The manifest must be exactly this venture's tables — another venture's export file is refused before anything touches the network.
fz doctor [--out migrations]
Fails when:
- a module's
harness_apidiffers from thecratefield-coreit linked against (the message names the module, its version and the core crate;Harness::buildalready refuses this, the doctor re-asserts it — issue #17); - a module with
public_writes()runs in aproductionventure without the Captcha port (section 11); - any module migration is not collected yet, a locked file is missing or edited;
- a migration contains non-portable SQL:
AUTOINCREMENT,datetime(,SERIAL,NOW(),json_extract, or backtick quoting.
fz modules
Prints name version /v1/<name> emits=[…] tables=[…] per module.
Applying migrations (wrangler)
Staging and production run the same commands from the venture repo:
# staging (first time / new migrations)
# production
Locally: wrangler d1 migrations apply <database-name> --local.