pub trait Module:
Send
+ Sync
+ 'static {
Show 19 methods
// Required methods
fn name(&self) -> &'static str;
fn version(&self) -> &'static str;
fn requires(&self) -> &'static [Port];
fn migrations(&self) -> Migrations;
fn validate_config(&self, cfg: &dyn Config) -> Result<(), ConfigError>;
fn router(&self, ctx: ModuleContext) -> Router;
// Provided methods
fn harness_api(&self) -> u32 { ... }
fn optional(&self) -> &'static [Port] { ... }
fn depends_on(&self) -> &'static [&'static str] { ... }
fn tables(&self) -> &'static [&'static str] { ... }
fn personal_data(&self) -> &'static [PersonalDataSet] { ... }
fn emits(&self) -> &'static [&'static str] { ... }
fn public_writes(&self) -> bool { ... }
fn public_write_policy(&self) -> RoutePolicy { ... }
fn self_check(&self) -> Vec<String> { ... }
fn well_known(&self) -> Option<Router> { ... }
fn surface(&self) -> Surface { ... }
fn events(&self) -> Vec<(EventName, EventHandler)> { ... }
fn scheduled<'a>(
&'a self,
ctx: &'a ModuleContext,
cron: &'a str,
) -> BoxFuture<'a, Result<(), AnyError>> { ... }
}Expand description
A Factory Zero module. Object-safe; composed as Arc<dyn Module>.
Handlers get the request scope as an axum extractor:
async fn join(scope: Scope, State(ctx): State<Arc<ModuleContext>>, ...).
There is no ambient “current request” (ADR 0007).
Required Methods§
Sourcefn requires(&self) -> &'static [Port]
fn requires(&self) -> &'static [Port]
Ports the module cannot run without; missing = build error.
Sourcefn migrations(&self) -> Migrations
fn migrations(&self) -> Migrations
The module’s migrations, embedded per dialect.
Sourcefn validate_config(&self, cfg: &dyn Config) -> Result<(), ConfigError>
fn validate_config(&self, cfg: &dyn Config) -> Result<(), ConfigError>
Rejects invalid configuration: missing required keys or malformed values, reported together with the module name.
When it runs. The conformance kit calls it, and a module’s own
tests should. It cannot run at Harness::build or in fz doctor,
because neither has the deploy config — the values live on the runtime
Env and only exist per request. The Cloudflare runtime therefore runs
it once at cold start and logs any failure loudly (console_error!,
so it reaches Workers Logs); it does not fail the boot, so a
misconfigured module still degrades per request (issue #101) rather
than taking the whole Worker down. Turning that into a hard boot
failure is a deployment decision for an ADR.
§Errors
Err listing every invalid or missing key for this module.
Sourcefn router(&self, ctx: ModuleContext) -> Router
fn router(&self, ctx: ModuleContext) -> Router
The module’s router, nested under /v1/<name>.
Provided Methods§
Sourcefn harness_api(&self) -> u32
fn harness_api(&self) -> u32
Contract version, checked by Harness::build.
Sourcefn depends_on(&self) -> &'static [&'static str]
fn depends_on(&self) -> &'static [&'static str]
Modules this one’s schema sits on top of, by name.
A partial order, not a load order for routes: it exists so
migrations apply in an order where a foreign key can point at a
table another module owns (RECONCILIATION.md §2). Naming a module
the venture did not compose is a build error, and so is a cycle —
both at HarnessBuilder::build, never at boot, because a boot
error is one a deployment discovers in production.
Declaring nothing is the common case and costs nothing: with no dependencies anywhere the resolved order is composition order, unchanged.
Sourcefn tables(&self) -> &'static [&'static str]
fn tables(&self) -> &'static [&'static str]
Table names this module owns; duplicates across modules are a build error.
Sourcefn personal_data(&self) -> &'static [PersonalDataSet]
fn personal_data(&self) -> &'static [PersonalDataSet]
What this module holds about a person, per table.
Read by cratefield-module-privacy to export a subject’s data, to
erase it, and to publish the “what is stored” table. Declaring it here
rather than in the privacy module is what makes that module reusable:
it never names a venture’s tables, so a schema change cannot leave it
describing something that no longer exists.
Validated at HarnessBuilder::build: a table this module does not
own is an error, and so is a declaration that cannot mean anything —
an Anonymise naming no columns, a Retain with no reason, a
published description left blank. See PersonalDataSet::none for a
table that legitimately holds nothing personal, which says so rather
than staying silent.
Defaulting to empty keeps every existing module compiling unchanged,
and a module with no tables has nothing to say. A module that owns
tables and declares nothing is a different thing, and since issue #244
it fails cratefield_testing::conformance rather than the build:
see undeclared_tables for why the kit is
the place that refuses it and a running deployment is not.
Sourcefn emits(&self) -> &'static [&'static str]
fn emits(&self) -> &'static [&'static str]
Event names this module emits ("<module>.<event>"), listed by
/__health.
Sourcefn public_writes(&self) -> bool
fn public_writes(&self) -> bool
Whether the module has public write endpoints.
Advisory since issue #133. The enforcement point is each
route’s RoutePolicy on the declared surface, collected by
WriteGuards::collect and gated at HarnessBuilder::build.
A module that writes publicly but declares no policy on any
action counts as a HumanForm writer (the conservative
fallback for pre-surface modules); declaring an explicit
Open/Signature policy on every action opts out of the
fallback.
Sourcefn public_write_policy(&self) -> RoutePolicy
fn public_write_policy(&self) -> RoutePolicy
What actually protects those public writes, for a module with no
ADR 0010 surface to hang a per-action RoutePolicy on
(issue #143).
Consulted only when public_writes is true and the surface
declares no guarded action, so a module with a surface is
unaffected and the conservative default is unchanged: say nothing
and you are still a HumanForm writer. The auth login methods
are the reason it exists — they are public writers whose proof is
a single-use challenge or a signed link, never a CAPTCHA widget,
and before this they could only be filed under a gate they can
never satisfy.
Sourcefn self_check(&self) -> Vec<String>
fn self_check(&self) -> Vec<String>
Problems the module can find in itself, with no configuration and no environment: its own embedded data, checked against its own declarations.
This is the half of Module::validate_config that fz doctor
can actually run. The doctor has no deploy config — those values
live on the runtime Env and exist only per request — so it cannot
ask a module whether its deployment is valid. It can ask whether
what the module compiled in is coherent, and that answer is the
same on a laptop, in CI and in production.
The notifications module answers with every translation its catalog is missing (issue #190), so a translation gap is a pull request rather than a person reading a message id on a lock screen.
Each string is one problem, already naming the module. Return identifiers, never values: this is printed by a CLI and copied into issues, so nothing read from the environment, a recipient or a URL may appear in one. Default: nothing, so no existing module changes.
Sourcefn well_known(&self) -> Option<Router>
fn well_known(&self) -> Option<Router>
Routes this module serves at the root under /.well-known, for
spec-mandated discovery documents (OIDC openid-configuration,
jwks.json) that must live outside /v1 (issue #46). Paths are
relative to the prefix: register /jwks.json, not
/.well-known/jwks.json.
At most one module may provide one: discovery URLs are a singleton
namespace, so Harness::build fails (naming every provider) when
two modules return a router here. None by default.
Sourcefn surface(&self) -> Surface
fn surface(&self) -> Surface
The module’s UI surface (ADR 0010): the actions a renderer may
offer and the views that compose them. Input schemas come from
the handler’s own body types (Action::input::<Body>()), so the
declaration cannot drift from the route. Harness::build
validates it; GET /__surface serves the composition. Default:
nothing, and a module that declares nothing renders nothing.
Sourcefn events(&self) -> Vec<(EventName, EventHandler)>
fn events(&self) -> Vec<(EventName, EventHandler)>
Handlers for events other modules emit; registered at
Harness::build.
Dyn Compatibility§
This trait is dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".