Skip to main content

Composition And coil

Coil gives you two composition styles:

  • broad convenience through coil
  • explicit crate-by-crate selection in your customer workspace

Both are valid. The right choice depends on how much control you need now.

What This Page Helps You Decide​

Use this page when you want to answer:

  • should my customer binary depend on coil
  • when should I link only selected modules
  • how do compile-time linking and runtime enablement differ
  • what do Shoppr and Gitly actually do

The Two Layers Of Composition​

There are always two decisions:

  1. What your customer workspace links into the binary.
  2. What your manifest and config enable at runtime.

Those are not the same thing.

Concrete example:

  • apps/shoppr/Cargo.toml links a broad workspace with coil.
  • apps/shoppr/app.toml and apps/shoppr/platform.dev.toml decide the actual installed modules.

Fastest Path: coil​

Use coil when you want a believable product quickly.

This is the pattern in both demo apps:

apps/shoppr/crates/shoppr-app/Cargo.toml
[dependencies]
coil.workspace = true
coil-app.workspace = true
coil-auth.workspace = true
coil-runtime.workspace = true

Why teams start here:

  • shortest learning path
  • broad official module availability
  • less crate-selection friction while the product shape is still moving

Narrower Path: Selective Linking​

Use explicit linking when:

  • you already know the exact product surface
  • you want a smaller dependency graph
  • you want hard compile-time limits on which modules may be enabled

A selective customer app still needs:

  • coil-app
  • coil-auth
  • coil-runtime
  • whichever official modules you genuinely intend to support
  • any customer-owned linked backend crates

Runtime Enablement Still Matters​

Even with coil, modules are not installed automatically.

Shoppr still has to say this explicitly in apps/shoppr/app.toml:

[modules]
enabled = ["cms", "media", "commerce", "commerce-payments-stripe", "memberships", "events", "admin", "ops"]

Gitly does the same in apps/gitly/app.toml:

[modules]
enabled = ["admin", "cms", "media", "gitly-showcase"]

That is why “linked” and “enabled” must stay separate in your mental model.

Common Composition Patterns​

Broad product app​

Use a broad convenience stack when the product is still discovering its boundaries.

Shoppr is the example:

  • broad official stack
  • linked Rust backend
  • runtime-installed WASM
  • multiple sites and locales

Narrow non-commerce app​

Gitly shows the second pattern:

  • broad runtime battery still linked for convenience
  • only a narrow enabled module set
  • customer-owned routes doing most of the product work

Controlled platform app​

This is the pattern to adopt later:

  • explicit module dependencies
  • explicit customer crates
  • manifest enablement limited to what the binary truly supports

Failure Mode To Avoid​

Do not enable a module in app.toml that the binary did not link.

The customer-root runtime builder explicitly checks for this. That is part of what keeps customer apps honest and upgradeable.

Choosing Intentionally​

Choose coil if:

  • you are learning Coil
  • you want the fastest path to a real app
  • you expect to use many official modules

Choose selective composition if:

  • you already know the product boundary
  • you want tighter release control
  • you want the binary to prove the supported module set