Develop a Logos module with Logos Scaffold
Build a module, install it into Basecamp, and iterate on it.
This document is accurate for logos-scaffold 0.4.0 built from master at commit d4b15ff, and Basecamp 0.3.0.
Logos Scaffold drives the loop a module author repeats often: build the .lgx package, install it into a Basecamp instance, restart Basecamp, and check the result. This guide describes that loop, including running two instances side by side to exercise peer-to-peer features.
- Linux (x86_64 or aarch64) or macOS (arm64 or x86_64). Scaffold is Unix-only.
- Nix with flakes enabled. Required by every
basecampsubcommand. git, and Rust 1.81 or newer withcargo, to install scaffold.- The Unix process helpers
lsof,ps, andkill, which scaffold uses to track the processes it starts. - A module project that exposes
packages.<system>.lgxfrom aflake.nix, built withlogos-module-builder0.3.0, the release whose SDK matches Basecamp 0.3.0. See Pinned versions.-
If you do not have a module yet, start from a 0.3.0 template and pin the builder in the generated
flake.nix, whose URL the template leaves unpinned:mkdir my_ui && cd my_ui && git initnix flake init -t github:logos-co/logos-module-builder/0.3.0#ui-qmlsed -i.bak 's#github:logos-co/logos-module-builder"#github:logos-co/logos-module-builder/0.3.0"#' flake.nixrm flake.nix.bakgit add -A -
The
ui-qmltemplate produces a QML-only module that shows up in the Basecamp sidebar, with the 256×256 icon builder 0.3.x requires insrc/icons/icon.png. Setnameanddisplay_nameinmetadata.json, and moveMain.qmlinto a sub-directory such asqml/(updatingview) if the module will have more than one QML file. -
The same release has
default(a minimal core module),with-external-lib,ui-qml-backend,rust, andrust-with-external-libtemplates.
-
- About 5 GB of free memory for the first
lgs basecamp setup, which evaluates the Basecamp 0.3.0 flake before building anything. On a smaller machine, fetch the prebuilt Basecamp first as described in Basecamp setup is killed. - A graphical environment. Basecamp is a desktop application.
What to expect
- You can build your module and run it in isolated Basecamp profiles.
- You can apply a source change and see it in a running Basecamp.
- You can run two Basecamp instances at once and exercise peer-to-peer features between them.
Step 1: Install Logos Scaffold
-
Install both binaries from the scaffold repository, pinned to the commit this guide was tested with:
cargo install --locked --git https://github.com/logos-co/scaffold --rev d4b15ff logos-scaffoldThis installs
logos-scaffoldand its shorter aliaslgs. They are functionally identical; this guide useslgs.The
basecampcommands behave the same in thelogos-scaffold0.4.0 release on crates.io, socargo install logos-scaffoldalso works for this guide. Use the pinned commit if you also follow Write and deploy an LEZ program withlogos-scaffold. -
Confirm the install:
lgs --version
lgs completions bash and lgs completions zsh print shell completion scripts that cover both binary names. Each subcommand also documents copy-paste examples under --help.
Step 2: Prepare the module project
Run these commands from the root of your module project.
-
Create
scaffold.tomlif the project does not have one:lgs initinitalso creates.scaffold/, appends.scaffoldto.gitignore, and installs AI-assistant instructions under.claude/skills/,.cursor/rules/, andAGENTS.md.- A
scaffold.tomlfrom an older schema is migrated in place, with ascaffold.toml.baknext to it.
warninginitoverwrites an existingAGENTS.mdat the project root without prompting, and unlikescaffold.tomlit leaves no.bakcopy. If your project already has anAGENTS.md, commit it or copy it somewhere safe before you runinit, then merge your content back into the new file. -
Pin and build Basecamp and
lgpm, and seed thealiceandbobprofiles:lgs basecamp setup- The first run evaluates and builds for a long time; later runs reuse the build and print
basecamp (.#app) already built at /nix/store/…; skipping nix build. - Neither binary lands on your
PATH; both live under scaffold's cache root. Build logs go to.scaffold/logs/<timestamp>-setup-*.log.
If the build fails with
signal: 9 (SIGKILL), the machine ran out of memory. See Basecamp setup is killed. - The first run evaluates and builds for a long time; later runs reuse the build and print
-
Capture the modules to install, then review what was captured:
lgs basecamp moduleslgs basecamp modules --showbasecamp modulesdiscovers theflake.nixat the project root that exposespackages.<system>.lgx, or, when the root has none, every immediate sub-directory flake that does. It resolves the dependencies each module declares inmetadata.jsonand writes the result to[modules]inscaffold.toml:[modules.tictactoe]flake = "path:./tictactoe#lgx"role = "project"[modules.delivery_module]flake = "github:logos-co/logos-delivery-module/<rev>#lgx"role = "dependency"role = "project"marks a module you build locally,role = "dependency"a runtime companion. The keys are described in the configuration reference.- The table is hand-editable, and re-running
basecamp modulesnever overwrites an existing entry. - Modules that Basecamp bundles itself, such as
package_manager, are never captured.
Expected result:
--showlists your module underproject_sources, for examplehello_ui = path:.#lgx.
lgs basecamp docs prints the full module-project contract, including the dependency-resolution rules. It works outside a scaffold project too, so you can read the contract before running lgs init.
Step 3: Build and install the module
-
Build every captured module and install the results into both profiles:
lgs basecamp install- Dependencies are built and installed first, then project modules; the first failure stops the run.
lgpmvalidates each package's structure and content hashes as it installs it.- Output goes to a timestamped log under
.scaffold/logs/. Pass--print-outputto stream the Nix output instead.
Expected result:
install complete. -
Check the project's Basecamp health:
lgs basecamp doctordoctorchecks thatsetupbuilt Basecamp andlgpmand seeded the profiles. It also reports the pin set, the captured modules, the platform variant of each installed core module, and drift between[modules]and what the project would capture today. Add--jsonfor machine-readable output.Expected result:
Doctor status: Ready.
Step 4: Launch Basecamp
-
Start a profile:
lgs basecamp launch alicelaunchstops the instance this profile started last time, removes the profile's data and cache, rebuilds and reinstalls every captured module, and starts Basecamp with the profile's launch environment. It fails before deleting anything if[modules]is empty or the profile's runtime directory cannot be used.- Before starting Basecamp, it prints a variant check of the installed core modules, such as
launch: profile alice has 3 module(s); all linux-amd64-dev variants present ✓. The count covers core modules only, so a project of QML-only modules reports0 module(s). - Add
--log-fileto also write the output to.scaffold/basecamp/profiles/<profile>/basecamp.log, or--log-file=PATHto pick the file.
- Before starting Basecamp, it prints a variant check of the installed core modules, such as
-
Confirm your module appears in the Basecamp window. A UI module gets an entry in the sidebar, showing its icon, or its initials when it ships none; click it to open the module.
Every launch starts from a clean profile, so identity keys, conversations, other in-app state, per-module state in module_data/, and Basecamp's own logs/ are discarded. That is what makes runs reproducible. If you need state that survives a restart, run Basecamp yourself with --user-dir, as in Step 6.
To see the directories and environment a profile resolves without launching it, run lgs basecamp paths alice --json. To add your own variables to the launch, see Launch environment.
Step 5: Iterate on a source change
Building a module does not update a running Basecamp instance. Basecamp loads modules once, at startup, from the copies installed in its base directory, so a rebuilt .lgx reaches a running instance only after it is reinstalled there and Basecamp restarts.
-
Edit your source. If you add a file, stage it before the next build:
git add -Awarningnix build .#lgx,lgs basecamp build, andbuild-portableonly see files that git tracks, so a new file you have not staged is silently left out of the package they produce.lgs basecamp installandlaunchbuild from the directory instead and do include it, which means a module can work in your profiles while the package you ship is missing aqmldir, a QML file, or a configuration file. Stage every new file before you build a package to share. See A new file is missing from the built package. -
Relaunch every profile you are testing.
launchrebuilds the module, reinstalls it into the profile, and starts a fresh instance:lgs basecamp launch alice- Repeat it for each profile. Relaunching
aliceleaves a runningbobon the previous build.
- Repeat it for each profile. Relaunching
Iterate faster on a single module
These loops skip a full Basecamp launch while you work on one module. Use them for iteration, then go back to Basecamp to verify the module in its real host.
-
lgs basecamp run <module>runs a captured module in its own standalone app throughnix run, without the rest of the stack. -
For QML-only changes,
logos-module-builder0.3.0 has aui-devtarget that reloads QML from your working tree on every save. It printshot-reloading QML from <project>/qml, thenQML reloaded …after each edit:nix build .#ui-dev./result/bin/run-logos-standalone-ui -
lgs basecamp develop <module>opens that module's Nix dev shell when you need to build by hand. -
lgs basecamp buildbuilds the captured project modules without installing them, and links the packages under.scaffold/basecamp/lgx/and.scaffold/basecamp/portable/in load order.
Iterate without scaffold
To do the same by hand, rebuild the .lgx, install it into every base directory you test against with an lgpm built from the same revision as your Basecamp, and restart Basecamp:
nix build .#lgx
lgpm --modules-dir /tmp/basecamp-a/modules \
--ui-plugins-dir /tmp/basecamp-a/plugins \
install --file result/logos-<module-name>-module.lgx
# Then close and restart the Basecamp instance that uses /tmp/basecamp-a.
The Package Manager UI inside Basecamp installs into the base directory of the instance you click in; the restart requirement is the same.
Step 6: Run two instances side by side
Peer-to-peer features need two instances, each with its own base directory and its own runtime directory. Choose one of the two options below.
Option A—Scaffold profiles
-
In one terminal at the project root, launch the first profile:
lgs basecamp launch alice -
In a second terminal at the project root, launch the second profile:
lgs basecamp launch bobExpected result: two Basecamp windows, each with its own data under
.scaffold/basecamp/profiles/<profile>/, its own runtime directory under/tmp, andLOGOS_PROFILEset to the profile name. If your module implements peer communication, an action inaliceshould be observable inbob. Theui-qmltemplate only logs its button clicks locally, so with it you can check isolation but not peer communication.
To add more profiles, such as a maker and a taker, declare them under [basecamp.profiles.<name>] in scaffold.toml; see [basecamp.profiles.<name>]. Unknown profiles are seeded on first launch.
Option B—Your own Basecamp with --user-dir
-
Install your module into two base directories, one per instance, as in Iterate without scaffold, with
/tmp/basecamp-aand/tmp/basecamp-bas the base directories. -
Start each instance on its own base directory and its own short temporary directory:
mkdir -p /tmp/bc-a /tmp/bc-bTMPDIR=/tmp/bc-a XDG_RUNTIME_DIR=/tmp/bc-a LogosBasecamp --user-dir /tmp/basecamp-a &TMPDIR=/tmp/bc-b XDG_RUNTIME_DIR=/tmp/bc-b LogosBasecamp --user-dir /tmp/basecamp-b &- Keep the temporary directories short: module sockets live there, and socket paths are capped at about 100 bytes.
- The executable is
./result/bin/LogosBasecampfor a Nix build,./logos-basecamp.AppImagefor the Linux release, or the application bundle on macOS. - Each base directory holds its own copy of the module. After a rebuild, reinstall into both and restart both.
State in these directories survives restarts. Without --user-dir, Basecamp picks a default base directory that differs between development and release builds; see Base directory.
Step 7: Test against a released Basecamp
Profiles run the Basecamp build that basecamp setup pinned. To test your module against a released AppImage or DMG instead, build the portable variant and load it by hand.
-
Build the portable variant of every project module:
lgs basecamp build --variant lgx-portablelgs basecamp build-portableis the back-compatible alias.- Add
--module <name>to build one module instead of all of them. role = "dependency"entries are skipped, because the released build ships its own copies.- A flake that does not expose
lgx-portablefails the build instead of falling back tolgx.
Expected result: scaffold prints the built packages in load order and links them into
.scaffold/basecamp/portable/as<NN>-<module_name>.lgx. -
In the released Basecamp instance, install each package through its install button, in the printed order.
To run a portable Basecamp instance in your scaffold profiles instead, set attr in [repos.basecamp]; see [repos.basecamp] and [repos.lgpm].