---
title: Module Authoring
description: Add a business capability with an explicit manifest, Business API, and owner-local checks.
---

A Module is a stable business capability inside a Lenso app. Its identity and
contracts stay the same whether the implementation is linked into the Host or
delivered by a Provider Service.

Start with one real workflow: one typed Business API, its implementation, and a
smoke check that fails when the Module is absent.

## Use the Rust facade

```sh
cargo add lenso --features host
```

The public facade exposes serializable Module declarations, manifest linting,
HTTP route metadata, runtime functions, event handlers, lifecycle declarations,
and Console Surfaces. The `host` feature enables the public HTTP and linked
Module authoring helpers used by a runnable Host.

## Declare the Module

```rust
use lenso::{
    ModuleHttpMethod, ModuleHttpRoute, ModuleManifest, ModuleManifestLintSeverity,
    lint_module_manifest,
};

pub fn manifest() -> ModuleManifest {
    ModuleManifest::builder("support/tickets")
        .capabilities(vec![
            "support.tickets.read".to_owned(),
            "support.tickets.write".to_owned(),
        ])
        .http_routes(vec![ModuleHttpRoute {
            method: ModuleHttpMethod::Post,
            path: "/v1/support/tickets".to_owned(),
            capability: Some("support.tickets.write".to_owned()),
            display_name: Some("Create ticket".to_owned()),
            story_title: Some("Ticket created".to_owned()),
            operation: None,
        }])
        .build()
}

#[test]
fn manifest_lints_cleanly() {
    let issues = lint_module_manifest(&manifest());
    assert!(issues
        .iter()
        .all(|issue| issue.severity != ModuleManifestLintSeverity::Error));
}
```

The manifest declares the public shape. `support/tickets` is the fully qualified
business identity used by the App Composition, Module Release, and Business API
bindings. Route handlers, SQL, and runtime implementations remain app-owned
code.

## Scaffold

```sh
lenso module create billing
```

The current scaffold command accepts a runnable unqualified local slug. It is a
generic starting point; it does not replace a product-owned fully qualified
identity such as `support/tickets`.

Add Module-owned Console UI only when the operator workflow needs a distinct
interactive Surface:

```sh
lenso module create billing --with-console-ui
```

The generated UI must be published as a digest-bound `console_ui_esm` artifact
in the same Module Release. It uses the generated Business API client through
Surface Gateway; it does not receive target credentials.

## Implement the Business API

Keep operation names domain-specific: `listTickets`, `createTicket`,
`updateTicket`, and `closeTicket` describe the Support Ticket lifecycle. Define
actor, tenant, deadline, idempotency, revision, and typed error behavior in the
committed OpenAPI contract.

Linked implementations attach the route contribution to `LinkedBinding`.
Provider implementations publish the same Module as an exact
`lenso.module-release.v1` release and serve its locked runtime through
`lenso.provider.v1` at `/lenso/provider/v1`. The compatibility
`lenso.service.v1` manifest carries process and packaging metadata; it is not
the runtime Module discovery or digest authority. Neither delivery mode changes
the Module name or operation IDs.

## Minimal vertical slice

1. Declare one capability and one Business API operation.
2. Implement the route and persistence behavior.
3. Generate the client from the committed contract.
4. Add a smoke check through the public API.
5. If UI is needed, bind one exact Console Surface and its narrow Surface Grant.
6. Compose a new `lenso.app.json` revision and start it through `lenso dev up`.
7. Connect Console and inspect the Module's direct state.

## Completion criteria

- manifest lint is clean;
- the public Business API completes one useful workflow;
- the smoke check fails when the Module is not selected and bound by the
  composed app;
- generated clients match the committed contract;
- a selected Console Surface is bound to the exact Module Release and artifact;
- no browser or test bypasses the public API to reach the Store.

For exact declaration fields, read [Manifest Reference](/docs/manifest-reference).
For Surface design, continue with
[Business APIs and Console Surfaces](/docs/admin-surfaces) and
[Module Console UI](/docs/console-packages). For a runnable product seam, use
[Examples](/docs/examples).
