Manifest Reference
Understand the serializable ModuleManifest contract that every Lenso module exposes.
ModuleManifest is the pure-data contract for a module. It describes what the
host and Lenso Console can see: Business API routes, runtime functions,
runtime schedules, events, lifecycle work, Console surfaces, dependencies, and
capabilities.
Executable behavior does not live in the manifest. Linked Modules attach
behavior through LinkedBinding. Independently running Services expose their
own provider APIs and declare which Modules they provide. The pure manifest
remains serializable in either topology.
Minimal manifest
use lenso::{
ConsoleSurface, ConsoleSurfacePresentation, ModuleHttpMethod,
ModuleHttpRoute, ModuleManifest, ModuleRequirement,
};
pub fn manifest() -> ModuleManifest {
ModuleManifest::builder("acme/billing")
.capabilities(vec![
"billing.invoice.read".to_owned(),
"billing.invoice.write".to_owned(),
])
.requires(vec![
ModuleRequirement::new("lenso/auth", "*").expect("valid requirement"),
])
.http_routes(vec![ModuleHttpRoute {
method: ModuleHttpMethod::Get,
path: "/v1/billing/invoices".to_owned(),
capability: Some("billing.invoice.read".to_owned()),
display_name: Some("List invoices".to_owned()),
story_title: Some("Billing invoices".to_owned()),
operation: None,
}])
.console(vec![ConsoleSurface {
name: "billing-invoices".to_owned(),
label: "Invoices".to_owned(),
route: "/billing/invoices".to_owned(),
presentation: ConsoleSurfacePresentation::Esm {
entry: "invoices".to_owned(),
},
icon: Some("receipt".to_owned()),
required_capabilities: vec!["billing.invoice.read".to_owned()],
navigation: None,
}])
.build()
}
Fields
| Field | Purpose |
|---|---|
module_id |
Canonical fully qualified Module id, such as lenso/auth or acme/billing. |
requires |
Other fully qualified Modules that must be installed first, with an explicit version requirement and optional required capabilities. |
capabilities |
Dot-separated permission names that routes, runtime work, or Console pages can reference. |
http_routes |
Module-owned HTTP route metadata. Linked Modules mount real Axum routes through bindings; Service-provided Modules retain the same declaration. |
runtime |
Runtime function and schedule declarations. The declaration is manifest data; handlers live in the loading source and scheduling stays host-owned. |
events |
Event handler declarations. The host registers and invokes the backing handler through the source binding. |
lifecycle |
Startup checks and activation jobs. The host validates and schedules these entries instead of giving modules arbitrary startup callbacks. |
console |
Declarative or ESM Console surfaces. Executable UI is bound by digest in the same Module Release, never by a same-origin package export. |
story_display |
Console story-display metadata for timeline and inspection surfaces. |
Builder methods
ModuleManifest::builder("acme/billing")
.requires(vec![
ModuleRequirement::new("lenso/auth", "*").expect("valid requirement"),
])
.capabilities(vec!["billing.invoice.read".to_owned()])
.http_routes(vec![/* ModuleHttpRoute */])
.runtime(/* RuntimeSurface */)
.events(/* EventSurface */)
.lifecycle(/* LifecycleSurface */)
.console(vec![/* ConsoleSurface */])
.story_display(vec![/* StoryDisplayDescriptor */])
.build();
Capability naming
Use dot-separated lowercase names:
<module>.<resource>.<action>
Good examples:
billing.invoice.read
billing.invoice.write
support.tickets.assign
auth.session.revoke
Avoid names such as BillingRead, billing/read, or read_invoice. Manifest
lints warn when a capability name is not dot-separated lowercase, and they also
warn when routes or Console permissions reference capabilities that the manifest
does not declare.
Route metadata
ModuleHttpRoute is metadata:
ModuleHttpRoute {
method: ModuleHttpMethod::Post,
path: "/v1/billing/invoices/{invoice_id}/void".to_owned(),
capability: Some("billing.invoice.write".to_owned()),
display_name: Some("Void invoice".to_owned()),
story_title: Some("Invoice voided".to_owned()),
operation: None,
}
Keep these values operator-friendly:
pathshould be the host-visible path.capabilityshould match a value incapabilities.display_nameshould be compact enough for timeline nodes.story_titleshould read naturally when the route starts a business story.
Runtime schedules
RuntimeSurface.schedules declares when the host should enqueue a runtime
function:
ScheduledFunctionDeclaration {
name: "send-daily-digest".to_owned(),
function_name: "billing.invoice.send_digest.v1".to_owned(),
cron: "0 8 * * MON-FRI".to_owned(),
input: serde_json::json!({ "window": "previous_day" }),
}
Schedules use 5-field UTC cron. The scheduled function must also appear in
RuntimeSurface.functions; the host validates that relationship, persists
schedule state, and enqueues normal runtime.function_runs when the schedule is
due.
Lint categories
The host exposes manifest_lints through module metadata, and Lenso Console
groups them into categories:
| Category | Common issue |
|---|---|
module |
Missing module name. |
capability |
Bad naming or referenced-but-undeclared capability. |
routes |
Duplicate method/path, missing display metadata, or missing remote route capability. |
runtime |
Missing function names, duplicate functions, bad queue names, or invalid retry policy. |
runtime.schedule |
Missing schedule names, invalid cron expressions, or schedules that reference unknown functions. |
events |
Missing event names or duplicate handler names. |
lifecycle |
Startup checks or activation jobs that reference unknown runtime functions. |
console |
Duplicate routes, missing labels, invalid package shape, or reserved navigation workspace ids. |
Treat error lints as blockers. Treat warning lints as operator-experience
debt unless the page explicitly says the warning is acceptable for a local spike.
When to change the manifest
Change the manifest whenever the host or Console needs new metadata:
- a new public route or route capability;
- a new Business API operation or operation capability;
- a new module-owned Console page;
- a new runtime function, schedule, or event handler;
- a lifecycle startup check or activation job;
- a new dependency such as
auth; - a new capability that will be referenced by another surface.
After changing manifest shape, run the relevant local checks and open Lenso Console to verify the host-visible result.