Project Structure

Lux tries to express structure in source paths and module directories first. The manifest exists for project-level choices, not for manually listing every file.

my_addon/
  lux.toml
  src/
    inventory/
      module.lux
      sh_items.lux
      sv_store.lux
      cl_panel.lux
    hud/
      cl_module.lux
      cl_paint.lux
  generated/
    lua/...

source_root points at src. out is where Lux writes GLua artifacts. Keeping generated output separate from source makes it safe to clean and review.

Manifest

package_id = "my_addon"
bundle_id = "my_addon"

[target.gmod]
source_root = "src"
out = "generated/lua"
runtime_base = "lux/my-addon"
autorun = true
source_comments = "readable"

source_root and out are required for manifest builds. out is the physical output root. runtime_base is the GMod relative path used by generated include and AddCSLuaFile calls. autorun = true writes an addon-style forwarder under out/autorun; autorun = false leaves startup to your own entry point. bundle_id controls generated registry names; set it when emitted names must remain stable across machines.

This split is deliberate: out can point at a generated folder, a live addon Lua root, or a gamemode integration path, while generated Lua still contains only GMod-relative paths. Do not treat runtime_base as an operating-system directory or addon root.

package_roots is optional and normally unnecessary for installed packages. lux.lock roots are loaded automatically after luxc install. Use package_roots for local package development checkouts. Duplicate package ids are rejected.

Module Discovery

Each immediate module directory under source_root becomes a Lux module. A module may contain several part files:

src/inventory/module.lux      -> module inventory, shared entry part
src/inventory/sv_store.lux    -> module inventory, server part
src/inventory/cl_panel.lux    -> module inventory, client part
src/inventory/sh_items.lux    -> module inventory, shared part

Nested directories can express submodules:

src/mgfx/widgets/module.lux   -> module mgfx/widgets
src/mgfx/text/module.lux      -> module mgfx/text

A module is not a file. It is the set of all part files under the module directory.

Module Entries

A multi-file module must have exactly one entry part:

module.lux
cl_module.lux
sv_module.lux
sh_module.lux

The entry is where module-level metadata belongs. It is also the file a developer should open first when trying to understand the module.

Single-file modules do not need an entry file. A multi-file module with no entry is a compile error.

Part Order

Complete part ordering belongs in the entry part.

Complete part order
part order {
  "module",
  "sh_items",
  "sv_store",
  "cl_panel",
}

Local constraints are useful when a part only cares about nearby order.

Local part constraints
part after "sh_items"
part before "cl_install"

Use a complete part order when you need full choreography. before and after are auxiliary constraints, not a pleasant way to encode an entire module.

Realm Inference

Lux understands both GMod-style prefixes and directory conventions:

cl_*.lux        client
sv_*.lux        server
sh_*.lux        shared
client/**/*.lux client
server/**/*.lux server
shared/**/*.lux shared

If a prefix conflicts with the nearest realm directory, compilation fails. Unmarked files default to shared.

Output Tree

Generated files live under out:

generated/
  lua/
    autorun/
      my_addon.lua
    lux/
      my-addon/
        loader_shared.lua
        loader_client.lua
        loader_server.lua
        client/
        server/
        shared/

The loader tree starts at runtime_base. The autorun file is only a forwarder that includes the generated loaders. With autorun = false, Lux still emits the same loader tree and module files, but no out/autorun entry; your gamemode, framework, or hand-written Lua can include the generated loaders explicitly.

Generated output should be treated as build output. Do not edit generated files by hand.

Package Roots

Package roots let projects point at local package development checkouts:

[target.gmod]
package_roots = "local-packages"

Example:

local-packages/
  acme/ui/
    src/module.lux

Package ids are resolved from lux.package.toml when a package set manifest is present, or from directory paths for convention-only roots. The same package id may not appear twice across locked packages and project package roots.