Expand description
cratefield is the harness as one dependency: a facade that re-exports
cratefield-core at the root and every other crate behind a feature of
its own. It contains no code, so cratefield::Harness and
cratefield_core::Harness are one type and a venture can move between
the facade and the parts freely.
There is no default feature: a runtime is a decision, not a default.
§cratefield
The Cratefield harness as one dependency.
A venture needs a core, a runtime, one or more adapters and its modules — six crates for the smallest useful backend, each with a version that has to agree with the others. This crate is that set behind one name and one version.
[dependencies]
cratefield = { version = "0.1", features = ["cloudflare", "resend", "waitlist"] }It adds no code of its own. Everything here is a re-export, so
cratefield::Harness and cratefield_core::Harness are the same type, and
a venture can drop down to the individual crates at any point without
rewriting anything.
§What you get
cratefield-core is re-exported at the root, so the builder, the Module
trait and the port traits are simply cratefield::*. Everything else is a
feature, and each feature names exactly one crate:
| Feature | Crate | Reached as | What it is |
|---|---|---|---|
cloudflare | cratefield-runtime-cloudflare | cratefield::cloudflare | Workers entry points; D1, KV and rate limiting as ports |
native | cratefield-runtime-native | cratefield::native | The same harness as one tokio binary |
sqlite | cratefield-adapter-sqlite | cratefield::sqlite | Database over rusqlite |
postgres | cratefield-adapter-postgres | cratefield::postgres | Database over sqlx |
resend | cratefield-adapter-resend | cratefield::resend | Mailer over the Resend API |
turnstile | cratefield-adapter-turnstile | cratefield::turnstile | Captcha over Cloudflare Turnstile |
ui | cratefield-ui | cratefield::ui | Renders the module surface as HTML |
secrets | cratefield-secrets | cratefield::secrets | Envelope-encrypted secrets |
kms | cratefield-kms | cratefield::kms | The KMS port and its local-file provider |
email-signup | cratefield-module-email-signup | cratefield::email_signup | Double opt-in email signup |
waitlist | cratefield-module-waitlist | cratefield::waitlist | Per-product waitlist |
cms | cratefield-module-cms | cratefield::cms | Small content store |
testing | cratefield-testing | cratefield::testing | The conformance kit; belongs under [dev-dependencies] |
There is no default feature. A runtime is a decision, not a default, and an
empty default is what keeps tokio and sqlx out of a Workers build
(ADR 0001).
The fz binary is not here. Install it separately with
cargo install cratefield-cli.
§On Workers
Three features cannot go to wasm32-unknown-unknown, because of what they
depend on rather than anything this crate does: native (tokio), postgres
(sqlx) and sqlite (rusqlite compiles C). On Workers the database is D1,
which arrives through cloudflare, so none of the three is what you want
there anyway.
Everything else builds for wasm — but your crate has to turn on
getrandom’s wasm backend, because only the final artifact can pick it.
Without this, the build fails inside getrandom with nothing in the error
mentioning Cratefield:
[target.'cfg(target_arch = "wasm32")'.dependencies]
getrandom = { version = "0.4", features = ["wasm_js"] }examples/venture in the repository is a working Workers venture built on
this crate, and CI boots it under wrangler dev on every push.
§Versions
The point of depending on this crate rather than the parts is that the set
is chosen for you: one cratefield version pins a combination that is built
and tested together. docs/COMPATIBILITY.md in the repository lists what
each release resolves to.
Re-exports§
pub use cratefield_runtime_cloudflare as cloudflare;pub use cratefield_runtime_native as native;pub use cratefield_adapter_sqlite as sqlite;pub use cratefield_adapter_postgres as postgres;pub use cratefield_adapter_resend as resend;pub use cratefield_adapter_turnstile as turnstile;pub use cratefield_adapter_apns as apns;pub use cratefield_adapter_stripe as stripe;pub use cratefield_ui as ui;pub use cratefield_secrets as secrets;pub use cratefield_kms as kms;pub use cratefield_module_email_signup as email_signup;pub use cratefield_module_waitlist as waitlist;pub use cratefield_module_cms as cms;pub use cratefield_testing as testing;
Structs§
- Action
- One route the module serves, described for a renderer.
- Blob
Object - A stored object: its bytes and the content type to serve it with.
- Brand
- Venture branding for mail templates (issue #12). Defaults are text-only: factory-zero orange accent, no logo, no footer line.
- Charge
- A created charge/payment-intent and its status as Stripe reported it.
- Checkout
Request - A one-time hosted checkout (Stripe Checkout in
paymentmode). - Checkout
Session - The hosted page to send the browser to, and the session id to reconcile on.
- Column
- A column of a
View::Table. - Config
Error - Accumulates every configuration problem so
Harness::buildcan report them together instead of one at a time. - Connect
Account Link - The Connect account id (persist it) and the hosted onboarding URL.
- Connect
Account Link Request - Onboards a Connect account (a coach) and returns a hosted onboarding link.
- Decision
- Empty
Config - A configuration that always returns
None(tests, offline builds). - Event
Bus - Registry of handlers, built once by
Harness::buildfrom every module’sevents(). Cheap to clone (oneArc). - Form
- A form (
application/x-www-form-urlencoded) extractor whose rejections are problem+json with the same shape asJson’s: body reads fail through the shared 413 slug (the size limit), everything else is a 400 validation problem. Needed for cross-siteform_postcallbacks (issue #46). - Harness
- A built harness: immutable after
build(). - Harness
Builder - Builder:
.venture(..),.module(..),.runtime(..),.template(..), then.build(). - Harness
Config - The harness-level keys, parsed once from the environment
Config(issue #3):HARNESS_SECRET(required, ≥ 32 bytes),HARNESS_SECRET_PREVIOUS(optional),ADMIN_TOKEN(optional),ENV(development|staging|production, defaultdevelopment). - Hmac
Signer - HMAC-SHA256 signer over
HARNESS_SECRET(+ optional previous). - Json
- A
Jsonextractor and response whose rejections and serializations are problem+json (architecture section 6). Deserialization failures become a400 validation-failedproblem listing the field. - Line
Item - One line on a checkout: a name shown to the buyer and its price.
- MapConfig
- A
Configbacked by a map (tests,fz doctorwith process env). - Message
- An outbound mail.
textis always sent alongsidehtml. - Migrations
- The module’s migrations, per dialect.
postgresdiffers fromsqliteonly where the SQL truly differs (ADR 0004). - Module
Config - Typed view over a
Configfor one module: prefixes every key with the module name inSCREAMING_SNAKEand parses values with defaults (issue #3). - Module
Context - Everything a module’s router needs: its declared ports, the typed config, the bus, the templates and the venture identity.
- Module
Surface - One module’s entry in the composed document.
- Money
- An amount in a currency’s minor units (cents), the way Stripe takes and
reports money.
currencyis a lowercase ISO-4217 code ("usd"). - Noop
Defer - Drops deferred futures with a warning. Used when no runtime defer is available; tests that assert on deferred work supply their own.
- Notification
- One notification.
datais the custom key-value payload the app reads;collapse_idcoalesces notifications the user has not seen yet. - Payload
- The signed payload:
{ purpose, subject, exp?, kid }. - Ports
- The per-request bundle of resolved port implementations plus the typed config the runtime built from environment/secrets.
- Problem
- An API error, serialized as
application/problem+json. - Problem
Def - Definition of one problem slug.
- Redacting
Visitor - A
tracingfield visitor that records(name, redacted value)pairs into a map. Runtimes use it in their formatters; tests use it to prove the redaction rules. - Refund
- A created refund.
- Refund
Request - A refund of a prior payment: the whole amount when
amountisNone, else a partial refund. - Rendered
- A rendered mail, ready for
Message. - Rendered
Surface - A document serialized once, with the strong
ETagclients revalidate against. Built atHarness::buildfor both the public and the admin variant. - Row
- One result row: ordered
(column name, value)pairs. - Rows
- A small owned row model. No engine types leak past this point.
- Scope
- Per-request scope, inserted into extensions by the request-id layer
(issue #2). Handlers receive it through the
Scopeextractor; there is no ambient “current request”. - Scoped
Blob - Wraps a
Blobso every key is prefixed with<module>/and no key can escape it. The harness applies this inPorts::view_for, so a module sees a store scoped to itself — the blob equivalent of the table-ownership check. - Sidecar
Mount - One mounted sidecar.
- Sidecar
Mounts - The mount table, parsed from configuration.
- SqlMigration
- One migration step, embedded with
include_str!fromcrates/<module>/migrations/<dialect>/NNNN_name.sql(issue #8). - Statement
- A rendered SQL statement:
(sql, values)with?placeholders, produced by rendering a sea-query query for a dialect. Modules build queries with sea-query and render through the helpers on this type (or let adapters do it); adapters bindvaluespositionally. - Subscription
Checkout Request - A recurring hosted checkout (Stripe Checkout in
subscriptionmode) against a Stripe Price the venture configured (e.g.$15/mowith a trial). - Surface
- What a module declares from
Module::surface. - Surface
Document - The document
GET /__surfaceserves: composed atHarness::build, one entry per module in mount order, modules with an empty surface omitted. - System
Clock - Real wall clock (
timecrate). Its timeout runs futures to completion — tests that need a real timeout supply their own clock. - Template
Registry - Immutable registry: venture overrides are inserted after module defaults
at
Harness::build, so they win on id collision. - Transfer
Charge - A destination charge with an application fee: the buyer is charged
amount,application_feeis kept by the platform, and the remainder is transferred todestination_account(the coach’s Connect account). - UiContext
- What a UI renderer gets from the harness (ADR 0010). Built by
Harness::routerfor every router it assembles. - Ulid
IdGen - Real ULID generator (monotonic per process).
- Venture
- Identity and CORS configuration for the venture this harness serves.
- Venture
Surface - The venture identity a renderer needs.
- Verdict
- A captcha verification verdict.
ok: falsewith areasonfrom the provider’serror-codes; transport-level unavailability surfaces asok: false, reason: "unavailable"unless the adapter is configured fail-open (staging only). - Webhook
Event - A webhook event the adapter has verified (signature + timestamp) before
returning.
kindis Stripe’s event type ("checkout.session.completed");datais the event’sdata.objectfor the module to interpret.
Enums§
- Audience
- Who an action is for. Drives the public/admin split of
/__surfaceand, in the renderer, which pages need the admin session. - Blob
Error - Blob store failures.
- Captcha
Error - DbError
- Database failures, sanitized for logs and problem details.
- Dispatch
Error - Http
Error - Kid
- Which secret a token was signed with. Tokens name their key so rotation never breaks links in flight.
- KvError
- Mail
Error - Mailer failures, mapped by the adapter from provider responses.
- Outcome
- What the browser should do with a successful response.
- Payments
Error - Payment failures.
NotConfiguredlets a venture build and run without Stripe (the port reports it rather than erroring); the rest map an upstream failure.SignatureInvalidis separated so a webhook handler answers400and never processes an unverified event. - Port
- Every port a module can declare in
requires()/optional()(architecture section 4). - Priority
- How urgently the notification should be delivered. Maps to APNs priority
10(deliver now, may wake the device) and5(deliver to save power). - Push
Error - Push failures.
Unregisteredis separated because the caller must act on it — the device token is dead and should be pruned — where the others are transient or a bad request. - Push
Outcome - The result of a send that the provider accepted.
- Rate
Limit Error - Send
Outcome - Result of a send attempt.
- Signature
Error - Failures surfaced by
verifybeyond “the token is simply invalid”, which is reported asNone. - Signer
Error - Errors from constructing an
HmacSigner. - Template
Error - Venture
Env - Deployment environment. Mirrors the
ENVconfig key;Productiondrives the mandatory-captcha rule (architecture section 11). - View
- How actions compose into something to render.
Constants§
- FORMULA_
PREFIXES - Characters that make a cell a formula when it starts with one of them.
- HARNESS_
API - Contract version shared by core and every module.
Harness::buildrejects modules whoseharness_apidiffers. Bumped only on breaking contract changes;cratefield-core’s major follows it. - HARNESS_
SIDECARS - Config key holding the mount table, a JSON object of
{"<module name>": "<service binding>"}. - HINT_
KEYWORDS - The
x-cf-*extension keywords the renderer understands on a field schema. Anything else underx-cf-is ignored, never an error, so a module can target a newer renderer than the one that serves it. - MAX_
BODY_ BYTES - Default request body limit for
/v1/*JSON endpoints. - MAX_
EMAIL_ BYTES - The maximum accepted address length in bytes (RFC 5321 “forward-path”).
- MAX_
LOCAL_ BYTES - The maximum local-part length in bytes (RFC 5321).
- MIN_
SECRET_ BYTES - Minimum secret length.
HARNESS_SECRETmust be at least 32 bytes. - SLUGS
- SURFACE_
API - Contract version of the surface document, independent of
HARNESS_API: a renderer or the control plane checks it before reading the document. - X_
HARNESS_ API - Contract version stamped on every harness response, checked by the host on every forwarded response. Stamping beats a cold-start handshake because an isolate outlives a sidecar redeploy (ADR 0009).
- X_
HARNESS_ MODULE - Module name stamped alongside
X_HARNESS_API. - X_
REQUEST_ ID x-request-id: accepted from the client when it matches^[A-Za-z0-9_-]{8,128}$, otherwise generated as a ULID. Always set on the response (architecture section 6).
Traits§
- Blob
- A blob store. Keys are module-prefixed; the harness wraps this in a
ScopedBlobper module so a module cannot name another’s objects. - Captcha
- Clock
- Config
- Read-only key/value configuration, resolved per runtime from environment
variables and secrets (Workers
Env) or the process environment. - Database
- Execute statements against the venture database. Implementations: D1 (Workers), rusqlite (tests, self-hosted), Postgres (phase 3).
- Defer
- Dispatcher
- Http
Client - IdGen
- KeyValue
- Mailer
- Module
- A Factory Zero module. Object-safe; composed as
Arc<dyn Module>. - Payments
- Moves money for a venture. Stripe today; the trait names only Stripe identifiers and hosted URLs, never card data.
- Push
- Sends notifications to a device. APNs today, FCM later; both over the
runtime’s
HttpClient. - Rate
Limiter - Runtime
- A runtime resolves environment bindings into
Portsand declares statically which ports it can provide, soHarness::buildcan reject a module that requires something the runtime will never hand it (ADR 0002). Reference implementation:cratefield-runtime-cloudflare. - Signer
- Produces and verifies
base64url(json).base64url(mac)tokens where the MAC is computed over the encoded payload string, so a token has exactly one valid encoding (ADR 0006). - Surface
Source - Where the current surface comes from (issue #76). With no sidecar
mounted this is the document composed at build; with sidecars, each
call fetches every mounted sidecar’s
/__surface(public part) and merges it in, so a sidecar redeploy is seen on the next request (ADR 0009). An unreachable sidecar contributes nothing and is logged. - Template
- One template.
datais the caller’s JSON payload;localeis the requested locale tag (en,de, …) used by the implementation for its own variants. - TryFrom
Value - Conversion from a sea-query
SeaValuefor typed row access. - UiMount
- A renderer the venture mounts at
/uiwithHarnessBuilder::ui(ADR 0010). Core defines the seam;cratefield-uiis the implementation, kept out of core so a venture without a UI carries nomaud.
Functions§
- bearer_
token - Extracts the bearer token from
Authorization: Bearer <token>. - card_
data_ hit - The first card-data fragment
textcontains (case-insensitive), orNone. Keeps card numbers, verification codes and full expiry out of migrations and secret names — with a normal Stripe integration none of them should exist (issue #44). - client_
ip - The client IP for rate limiting, from the headers.
- constant_
time_ eq - Constant-time equality of two secrets, via fixed-length digests so timing does not leak the configured token’s length.
- csv_
escape - Escapes one CSV field: formula-guard, then RFC 4180 quoting.
- csv_row
- Escapes and joins one CSV row, with a trailing newline.
- harness_
api_ mismatch - The message
Harness::buildandfz doctorreport for a module whoseModule::harness_apidiffers from core’s: it names the module, the module crate’s version, the API it targets, and thecratefield-corecrate with its version and API (issue #17). - hint_
field - Sets one
x-cf-*(or any) keyword on a field of an object schema after derivation, for hints that only exist at runtime: aselectwhose options are the configured product list. Unknown fields are ignored so a rename in the body type cannot panic at build. - invalid_
email_ problem - A
400 validation-failedproblem for a rejected address. - is_
email_ field - Whether a field is expected to carry an email address.
- is_
secret_ field - Whether a field name marks a secret: matches
(?i)secret|token|key|authorization|password. - is_
valid - A conservative validator: non-empty, one
@, sane lengths, an ASCII local part from the unreserved set, and a dot-separated alphanumeric domain with no empty or hyphen-edge labels. - lint_
card_ data - Card-data column or table names found in
sql, as(fragment, why)pairs. Comments and string literals are ignored, so documenting the rule does not trip it. - lint_
portable_ sql - Returns
(token, explanation)pairs found insql. - migration_
checksum - The sha256 of a migration’s SQL, lowercase hex. Recorded in
harness_migrationswhen the migration is applied, so a later run can tell “already applied” from “applied, then edited” — the rule forward-only migrations rest on, enforced by the database rather than by a lockfile in one repository (issues #28, #34). - migration_
edited - The message a migration whose recorded checksum no longer matches gets. Shared so both engines say the same thing.
- normalize_
email - Trim, Unicode NFC normalise, then lowercase. Idempotent.
- problem_
registry - Every core slug definition, for tests and docs.
- rate_
limit_ keys - The rate-limit keys for one request: always
ip:<ip>(orip:unknownwhen no address is visible), plusemail:<normalized>when an address is known. The limiter is consulted per key, in order. - rate_
limited - A
429 rate-limitedproblem carryingRetry-After: <seconds>when the limiter reported a pause (architecture section 6). - redacted_
value - How one recorded field is stored:
[redacted]for secrets, the hash for emails, the value otherwise. - request_
id_ is_ valid - The character class and length bounds of an accepted request id.
- require_
admin - Checks
Authorization: Bearer <ADMIN_TOKEN>for one admin request. - schema_
for - Generates the schema for
Tthe way every action does: draft 2020-12, definitions inlined so a renderer never has to resolve$ref. - set_
error_ forwarder - Installs a process-wide forwarder for internal-error diagnostics (architecture section 11).
- subject_
hash - The redacted form of an email-ish value: its SHA-256 digest, 12 hex
characters, no
@ever reaches the logs. - timeout
- Typed wrapper over
Clock::timeout_any.Nonemeans the clock abandoned the future afterafter. - validation_
error - The reason an address is rejected, for problem
details.
Type Aliases§
- AnyError
- Error type for handler and scheduled-work results.
- BoxFuture
- An owned dynamically typed
Futurefor use in cases where you can’t statically type your result or need to add some indirection. - Event
Handler - A registered handler: receives the emitting request’s scope and the payload.
- Event
Name - Event names are
"<module>.<event>", e.g.waitlist.confirmed.