Skip to content
Lenso
English
Esc
navigateopen⌘Jpreview
On this page

Host Composition

Understand how a Lenso Host composes Linked Modules and connects independently running Services.

Host composition is the place where a Lenso app decides which modules exist in the running host.

It is deliberately separate from module manifests:

  • a manifest says what one module contributes;
  • host composition says which linked modules the app owns;
  • Service installation state says which providers supply Modules;
  • runtime config can enable or disable modules at startup.

The generated host shape

Generated hosts expose one function used by the API, worker, and migration entrypoints:

mod modules;

use lenso::host::prelude::*;

pub fn host_composition() -> HostComposition {
    HostBuilder::new()
        .linked_module(modules::app::linked_module())
        .build()
}

The API, worker, and migration binaries all call that same composition so HTTP routes, runtime handlers, event handlers, and migrations stay aligned.

Built-in profiles

LENSO_COMPOSITION_PROFILE selects the platform’s linked baseline.

Profile Built-in linked modules
core platform-story
demo auth, auth-anonymous, auth-oauth, auth-password, auth-github, auth-google, auth-oidc, platform-story

Local development defaults to demo. Production-like environments must set the profile explicitly:

APP_ENV=production
LENSO_COMPOSITION_PROFILE=core

Use core for product hosts that install modules intentionally. Use demo for local examples and starter proof paths.

Host-owned linked modules

First-party modules that belong inside the app can be linked into the host:

use lenso::host::prelude::*;

pub fn host_composition() -> HostComposition {
    HostBuilder::new()
        .linked_module(builtins::auth())
        .linked_module(builtins::auth_password())
        .linked_module(modules::app::linked_module())
        .build()
}

A linked module can provide:

  • manifest metadata;
  • migrations;
  • linked HTTP routes;
  • admin data, actions, and queries;
  • runtime function handlers;
  • event handlers;
  • host-only contributions.

Services do not become a second Module source in HostBuilder. An independently running Service owns its process, Store, and delivery; the Host connects to it through provider and System Plane contracts.

Enable and disable modules

Every Linked Module gets a generated startup toggle:

modules.<module>.enabled

Environment values use the matching LENSO_MODULE_*_ENABLED form:

LENSO_MODULE_AUTH_PASSWORD_ENABLED=false
LENSO_MODULE_SUPPORT_ENABLED=false

These toggles are restart-only. Disabling a module stops loading its routes, runtime handlers, admin metadata, story display metadata, and migrations for the current startup path. It does not delete that module’s existing data.

Dependencies

Module dependencies are declared in the manifest:

ModuleManifest::builder("auth-password")
    .dependencies(vec!["auth".to_owned()])
    .build()

If a dependency is disabled, the dependent module is not loaded and Runtime Console shows that dependency state in module metadata.

Service providers

Use lenso service install <manifest> for provider installation and lenso service doctor <provider> --json for readiness and drift. The Service manifest declares its provided Modules; the Host does not reinterpret the Service process as a Remote Module.

What to change where

Need Change
Add an app-owned Rust module src/lib.rs host composition
Add built-in auth to a product host HostBuilder::linked_module(builtins::auth())
Install a Service provider lenso service install <service-manifest-url>
Disable a module at startup LENSO_MODULE_<MODULE>_ENABLED=false
Configure module boot values LENSO_MODULE_<MODULE>__<KEY>=<value>
Make local catalog data discoverable .env LENSO_MODULE_CATALOG_FILE=<path>

Smoke check

After changing composition, run the real host path:

lenso serve

Then open /console and check:

  • Modules shows the expected Linked Modules and installed Service providers;
  • disabled modules have an explainable status;
  • expected routes or admin surfaces appear;
  • Runtime Stories still load.

Last updated on August 1, 2026

Was this page helpful?