/technology/ · How it is built
The engineering, in one place.
Cratefield is a managed service on top of Harness, an open-source Rust backend harness. This section is the technical half of the site: the request path, what a module is, every capability with the evidence behind it, and the row-by-row comparisons — Supabase, Firebase, InsForge and hosted Postgres. None of it is required reading to use the product, and all of it is readable without an account.
Seven pages
Where the detail lives.
The Module trait, the ports a module may ask for, the request lifecycle, and the security rules that ship today.
What a module is, the ones that exist, the six steps to write one, and the two ways to mount it.
Every capability with its status chip and the code or issue that backs it. The longest version of the status table.
The row-by-row comparison, the full D1 limits, and the cases where Supabase is the right answer.
The row-by-row comparison, the Spark plan's exact quotas, and the cases where Firebase is the right answer.
The closest open-source peer, service by service, and where each of us is the better choice.
Neon and RDS beside the harness: what a managed database gives you, and what the harness adds on top.
The composition
One file wires the whole backend.
This is the example venture in the repository, with the comments and the mailer's arguments trimmed to fit. Each .module() is a crate. Each .runtime() call supplies an adapter for a port. .ui() is what turns the modules into pages. Swap Resend for another mailer, or the hosted runtime for a native binary, and the modules do not change.
That is the whole composition. If a module needs a port the runtime doesn't provide, this doesn't compile.
Harness::builder() .venture(Venture::new( "venture-example", "example.factory0.dev") .public_url("https://example.factory0.dev") .cors_origins(["https://example.factory0.dev"])) .module(EmailSignup::new()) .module(Waitlist::new() .products(["kontinuum", "undercover-rockstars"]) .confirm_ttl_days(7)) .templates(templates) .ui(Ui::from_spec(include_str!("../ui.json"))?) .runtime(Cloudflare::new() .db("DB") .mailer(Resend::new(..))) .build()?
The shape of it
Modules in. One binary out. A database each.
01 You pickcrates
02 We compilebuild time
.module(…)
.runtime(…)
.build()?
03 You runone db each
The same picture is the argument. What you assemble is crates, so the checks happen in cargo test rather than in production. What comes out is one binary, so there is nothing to configure at runtime. What runs is one database per backend, so a mistake cannot reach the backend next door. Per-customer databases inside one product are Designed, in epic #23. The request path, in detail.
The argument, technically
A backend you compile, not a platform you configure.
01
Modules are crates, composed at compile time.
A backend lists module crates in Cargo.toml and wires them in one file. The binary contains exactly those modules. Harness::build() refuses a module that needs a port the runtime doesn't provide, two modules claiming the same route prefix or table, or a module built against a different contract version. Misconfiguration fails cargo test, not production.
02
Portability is architectural, not a promise.
Modules never touch a vendor client, a platform binding or an environment variable. They receive trait objects and adapters answer. CI builds the example backend to wasm32 every commit: a dependency pulling tokio, mio or std::fs fails the build. MIT, not BSL.
03
Stateless by construction, at the edge.
Rust compiled to WebAssembly and served at the edge. No static mut, no thread-local outliving a request, no session store. Request scope travels in axum extensions, and the conformance kit ships the concurrent-request test that proves it.
—
Changing modules is a deploy.
Toggling a module would regenerate Cargo.toml and harness.rs and deploy a new backend, with a build log. The artifact would be keyed on the module set, so a compile would happen only for a module set that has not been built before (#59); when one does, it takes seconds (measured). There is no instant config flip, and we won't draw one.
What this does not do
The limits, as numbers.
Read these before the comparison. Full version on the comparison page.
- The database is SQLite, not Postgres. No pgvector, no extensions, no LISTEN/NOTIFY.
10 GB per database, a hard cap the provider does not raise. Single-threaded: throughput tracks query duration, roughly1,000 queries/sec at 1 ms, about 10/sec at 100 ms. - Realtime does not run at the edge yet. The
Realtimeport and its in-process adapter are merged and run on the native runtime; the Cloudflare Durable Object adapter behind the same port is specified and unbuilt (#103). Object storage does ship, as aBlobport over R2 or a directory (#105). - There is no general data browser. The admin UI renders each module's own admin views over its own tables, which is not the same thing as a console onto the database.
- The control plane is not running. It is being built in the open in Cratefield/harness, and it is not available. Early access is early access.
- What to do about it. Run it on Postgres instead. The Postgres adapter and the native tokio runtime are merged, CI tests every module against both engines, and
fz data exportmoves a venture's rows from D1 into Postgres. The 10 GB cap and the single-threaded write path are D1's, not the harness's.
The code is the documentation you cannot be talked out of.