Unified multi-repo development environment for the Logos project. All ~43 repos live under repos/ as git submodules. A Nix flake wires them together so that local changes propagate through the dependency chain automatically.
# 1. Clone this repo
git clone --recurse-submodules git@github.com:logos-co/logos-workspace.git
cd logos-workspace
# 2. Or if already cloned, initialize submodules
./scripts/ws init
# 3. Add scripts to your PATH (ws + CLI tools like lm, lgx, logoscore)
export PATH="$PWD/scripts:$PATH"
# Add this to your ~/.zshrc or ~/.bashrc with the full path to persist it
# 4. Enter the dev shell (optional, adds ws to PATH automatically)
ws developThe examples in this README assume flakes are enabled globally. If they aren't, you can pass the flags explicitly:
nix build '.#logos-liblogos' --extra-experimental-features 'nix-command flakes'To enable globally so you don't need these flags for each command, add the following to ~/.config/nix/nix.conf (create the file if it doesn't exist):
experimental-features = nix-command flakes
The workspace flake uses follows declarations to create a single dependency graph. When you override one input, the change propagates to every downstream consumer automatically.
Example: Edit logos-cpp-sdk, then build logos-app-poc using your local version:
# Edit something in logos-cpp-sdk
vim repos/logos-cpp-sdk/src/something.cpp
# Build logos-app-poc — it will use YOUR local logos-cpp-sdk,
# and so will every intermediate dep (logos-liblogos, logos-module, etc.)
ws build logos-app-poc --auto-local
# Or be explicit about which repos to override
ws build logos-app-poc --local logos-cpp-sdk logos-liblogosThe --auto-local flag scans the workspace for repos that have uncommitted changes or unpushed commits, and automatically overrides those deps. So if you're editing logos-cpp-sdk, you don't need to specify --local logos-cpp-sdk — --auto-local detects it for you.
Under the hood, ws build logos-app-poc --local logos-cpp-sdk runs:
nix build .#logos-app-poc \
--override-input logos-cpp-sdk path:./repos/logos-cpp-sdkBecause the workspace flake declares logos-liblogos.inputs.logos-cpp-sdk.follows = "logos-cpp-sdk" (and similar for all other repos), this single override propagates through the entire dependency tree. You don't need to override each intermediate dependency separately.
| Command | Description |
|---|---|
ws init [jobs] |
Clone all submodules (default: 4 parallel jobs) |
ws list |
List all repos and their clone/flake status |
ws groups |
List all defined repo groups and their members |
ws build <repo...|--group G> [opts] |
Build one or more repos via nix |
ws run <repo> [opts] |
Build and run a repo (single repo only) |
ws develop [repo...|--group G] [opts] |
Enter a nix devShell |
ws test [repo...|--group G|--all] [--type T] [--auto-local|--local ...] |
Run checks/tests |
ws status |
Git status across all repos |
ws dirty |
Show dirty repos and what they affect |
ws graph [repo...|--group G] |
Show dependency graph |
ws override-inputs <repo...|--group G> [opts] |
Preview override flags |
ws update [repo...|--group G|--all] [--deep] |
Update flake.lock inputs (--deep: also sub-repos) |
ws loc [repo...|--group G|--all] [--no-nix] |
Count lines of code (uses tokei) |
ws watch test <repo...|--group G> |
Re-run tests on file changes |
ws watch build <repo...|--group G> |
Re-build on file changes |
ws watch run <cmd> [-w repo] |
Run command on file changes |
ws foreach <cmd> |
Run a command in every repo |
ws worktree add <name> [-b br] |
Create a worktree with submodules and ws/<branch> branches |
ws worktree list |
List all worktrees |
ws worktree remove <name> |
Remove a worktree |
ws sync-graph |
Regenerate nix/dep-graph.nix from repo flake.nix files |
--quiet,-q— Suppress informational output (headers, progress, hints) and strip colors. Useful for CI pipelines and scripting. Primary output (test results, status lines, data) is still shown.
# Machine-friendly status output (no colors, no header)
ws --quiet status
# Quiet test run — only PASS/FAIL lines and summary
ws test --all --quietThese are also in scripts/ and auto-build from the local repo on first use. They rebuild automatically when source files change. All are also available as ws subcommands (e.g. ws lgx, ws lm, ws logoscore).
Loads modules and calls their methods without the full GUI. Essential for testing.
logoscore [options]
-m, --modules-dir <path> Directory to scan for plugins (repeatable)
-l, --load-modules <mod1,mod2> Comma-separated modules to load (auto-resolves deps)
-c, --call <module.method(args)> Call a method after loading (repeatable, sequential)
-h, --help Show help
--version Show version
Method call syntax for -c: module_name.method(arg1, arg2)
- Type auto-detection:
true/false→ bool,42→ int,3.14→ double, else → string @filenamereads file content as the argument (e.g.@config.jsonfor JSON configs)- 30-second timeout per call; exit code 1 on any failure
# Load a module (auto-resolves transitive dependencies)
logoscore -m ./modules --load-modules my_module
# Load and call a method
logoscore -m ./modules -l my_module -c "my_module.doSomething(hello)"
# Sequential calls with a file parameter
logoscore -m ./modules -l storage_module \
-c "storage_module.init(@config.json)" \
-c "storage_module.start()"
# Multiple modules — dependencies resolved automatically
logoscore -m ./modules -l waku_module,chat,my_moduleIntrospects compiled Qt plugin files to show metadata and method signatures.
lm [command] <plugin-path> [options]
Commands:
(default) Show both metadata and methods
metadata Show plugin metadata only (name, version, description, author, type, deps)
methods Show exposed Q_INVOKABLE methods only (name, signature, return type, params)
Options:
--json Output structured JSON
--debug Show Qt debug output during plugin loading
-h, --help
-v, --version
lm ./result/lib/my_module_plugin.so # everything
lm metadata ./result/lib/my_module_plugin.so # metadata only
lm methods ./result/lib/my_module_plugin.so --json # methods as JSONCreates and manages .lgx packages (gzip tar archives with platform-specific variants).
lgx <command> [options]
Commands:
create <name> Create empty .lgx package
add <pkg> -v <variant> -f <path> Add files to a variant (replaces if exists)
--variant, -v <name> e.g. linux-x86_64, darwin-arm64
--files, -f <path> File or directory to add
--main, -m <relpath> Main entry point (required if --files is a dir)
--yes, -y Skip confirmation prompts
remove <pkg> -v <variant> Remove a variant
extract <pkg> [-v <variant>] [-o <dir>] Extract variant(s)
verify <pkg> Validate against LGX spec
sign <pkg> (not yet implemented)
publish <pkg> (not yet implemented)
lgx create my_module
lgx add my_module.lgx -v linux-x86_64 -f ./result/lib/my_module_plugin.so
lgx add my_module.lgx -v darwin-arm64 -f ./result/lib/my_module_plugin.dylib
lgx verify my_module.lgx
lgx extract my_module.lgx -v linux-x86_64 -o ./extractedInstalls, searches, and manages module packages. Fetches from GitHub releases with automatic dependency resolution.
lgpm [global-options] <command> [options]
Global options:
--modules-dir <path> Target directory for core modules
--ui-plugins-dir <path> Target directory for UI plugins
--release <tag> GitHub release tag (default: latest)
--json Output JSON format
-h, --help
Commands:
search <query> Search packages by name/description
list [--category <cat>] [--installed] List packages
info <package> Show package details
categories List available categories
install <pkg> [pkgs...] Install packages (auto-resolves deps)
--file <path> Install from local .lgx file instead
lgpm search waku
lgpm list --installed
lgpm info my_module
lgpm --modules-dir ./modules install my_module # from registry
lgpm --modules-dir ./modules install --file ./my_module.lgx # from local file
lgpm --modules-dir ./modules --release v2.0.0 install my_module--workspace,-w— Use all local workspace repos as dependencies (ignoresflake.lockpins). Use this in CI where the submodule pointers define the intended dependency versions.--auto-local,-a— Auto-detects repos with uncommitted changes or unpushed commits and uses those as local overrides. Useful during development — edit a dep and it gets picked up automatically.--local,-l <repo1> <repo2> ...— Explicitly specify which repos to use as local overrides. For when you want precise control.
These flags work with ws build, ws run, ws develop, and ws test.
Named sets of repos for batch operations. Groups can include other groups (resolved recursively). Use --group/-g with most commands that accept repos.
Built-in groups:
| Group | Members |
|---|---|
core |
logos-cpp-sdk, logos-module, logos-liblogos, logos-module-builder, logos-capability-module, logos-package-manager-module, logos-test-modules |
chat |
core + logos-chat-module, logos-chatsdk-module, logos-waku-module, logos-irc-module |
package-manager |
logos-package, logos-package-manager-module, logos-package-manager-ui, nix-bundle-lgx |
app |
core + logos-app-poc, logos-package-manager-ui |
# List all groups and their expanded members
ws groups
# Build all core repos
ws build --group core
# Test chat repos with auto-local overrides
ws test --group chat --auto-local
# Watch and rebuild core repos on change
ws watch build --group core --auto-local
# Multiple groups in one command
ws build --group core chat
# Show dependency graph for a group
ws graph --group coreGroups are defined in the REPO_GROUPS array at the top of scripts/ws.
ws test --all— Runnix flake check(all repos)ws test --all --type cpp— Run checks only for C++ reposws test logos-cpp-sdk— Run checks for a specific repows test logos-test-modules --local logos-liblogos— Test with local dependency overridews test --all --workspace— Test using the workspace's dependency versions (see--workspaceabove)- Types:
cpp,rust,nim,js,qml
Repos that expose tests follow two conventions so that ws test can discover and run them:
-
Add a
checks.<system>.testsoutput to the repo'sflake.nix. This derivation should build and run the test binary (not just compile it). Seerepos/logos-liblogos/flake.nixorrepos/logos-module/flake.nixfor examples. -
Run
ws sync-graphto regeneratenix/dep-graph.nix. This scans every repo'sflake.nixto detect deps and whether it has achecksoutput (hasTests). WithouthasTests = true,ws testskips the repo entirely to avoid expensive nix evaluation.
# After adding checks to a repo's flake.nix:
ws sync-graphRepos with hasTests = false (the default for most repos) are skipped by ws test --all.
The workspace flake re-exports everything from the sub-repo flakes:
# Build any repo
nix build .#logos-cpp-sdk
nix build .#logos-app-poc
# Enter a repo's own devShell
nix develop path:./repos/logos-cpp-sdk
# or: ws develop logos-cpp-sdk
# Run all checks
nix flake check
# Manual override
nix build .#logos-app-poc \
--override-input logos-cpp-sdk path:./repos/logos-cpp-sdk \
--override-input logos-liblogos path:./repos/logos-liblogosnixpkgs
└── logos-cpp-sdk
├── logos-module
├── counter, counter_qml
└── logos-liblogos
├── logos-capability-module
├── logos-package → logos-package-manager-module → logos-package-manager-ui
│ └── nix-bundle-lgx (LGX bundler, uses nix-bundle-dir)
├── nix-bundle-dir (portable bundling for Nix derivations)
├── logos-module-builder → logos-libp2p-module
├── logos-webview-app
├── logos-waku-module → logos-chat-module, logos-chat-legacy-module,
│ logos-waku-ui, logos-chat-tui, logos-chat-ui,
│ logos-irc-module
├── logos-accounts-module → logos-accounts-ui
├── logos-wallet-module → logos-wallet-ui
├── logos-storage-module → logos-storage-ui
├── logos-chatsdk-module → logos-chatsdk-ui
├── logos-module-viewer
├── logos-nim-sdk, logos-js-sdk
└── logos-app-poc (aggregates many of the above)
Standalone: logos-rust-sdk, logos-design-system
Generate a visual graph: ws graph | dot -Tsvg > graph.svg
- Foundation: logos-cpp-sdk, logos-module, logos-liblogos, logos-capability-module, logos-package, logos-module-builder
- Packaging: nix-bundle-dir, nix-bundle-lgx
- Modules: logos-accounts-module, logos-blockchain-module, logos-chat-module, logos-chat-legacy-module, logos-chatsdk-module, logos-irc-module, logos-waku-module, logos-package-manager-module, logos-storage-module, logos-wallet-module, logos-libp2p-module, logos-template-module
- Apps: logos-app-poc, logos-accounts-ui, logos-chat-tui, logos-chat-ui, logos-chatsdk-ui, logos-waku-ui, logos-package-manager-ui, logos-storage-ui, logos-wallet-ui, logos-webview-app
- UI: logos-design-system
- SDKs: logos-cpp-sdk, logos-js-sdk, logos-nim-sdk, logos-rust-sdk
- Docs: logos-docs, logos-website, logos-tutorial
- Other: counter, counter_qml, node-configs, logos-modules, logos-module-viewer
The most common workflow: you're changing a library (e.g. logos-cpp-sdk) and want to see the effect in something that uses it (e.g. logos-liblogos or logos-app-poc).
# 1. Make your changes
cd repos/logos-cpp-sdk
vim cpp/logos_api.h
# 2. Build a downstream repo using your local changes
ws build logos-liblogos --auto-local
# 3. Or test a downstream repo with your changes
ws test logos-liblogos --auto-local
# 4. Check what else your changes affect
ws dirty--auto-local detects every dirty repo and overrides it in the build. If you only want to override specific repos:
ws build logos-app-poc --local logos-cpp-sdk logos-liblogos# Test a single repo
ws test logos-module
# Test multiple repos at once
ws test logos-module logos-liblogos --auto-local
# Test with local dependency overrides
ws test logos-test-modules --local logos-liblogos
# Test with auto-detected dirty repos as overrides
ws test logos-test-modules --auto-local
# Run specific test groups (logos-test-modules supports: basic, extlib, ipc, multi, errors)
TEST_GROUPS=ipc ws test logos-test-modules --local logos-liblogos logos-cpp-sdk logos-capability-module
# Test all repos that have tests
ws test --all
# Test only C++ repos
ws test --all --type cpp
# Test only Rust repos
ws test --all --type rust
# Test all repos using workspace dependency versions (used in CI)
ws test --all --workspacews test only runs repos that have hasTests = true in nix/dep-graph.nix. Repos without tests are skipped instantly.
The --auto-local and --local flags work the same as for ws build — they pass --override-input flags to nix so your local changes are used in the test build. The --workspace flag goes further and overrides all deps with local workspace repos, ensuring tests reflect the workspace's dependency versions rather than each repo's pinned flake.lock.
Auto-run tests or builds whenever you save a file — no manual re-running:
# Re-run tests every time you save a file in logos-module
ws watch test logos-module
# Re-run tests with local overrides on save
ws watch test logos-test-modules --local logos-liblogos
# Auto-build with local overrides on save
ws watch build logos-app-poc --auto-local
# Watch specific repos and run a custom command
ws watch run 'ws test logos-module --auto-local' -w logos-module -w logos-cpp-sdk--auto-local and --local flags pass through to the underlying ws build / ws test commands.
Combine with --auto-local to get a live feedback loop: edit a library, save, and see the downstream build or test result immediately.
# Build and run logos-app-poc using your local logos-cpp-sdk
ws run logos-app-poc --auto-local
# Or with explicit overrides
ws run logos-app-poc --local logos-cpp-sdk logos-liblogosBefore pushing, see which repos depend on the code you changed:
# Show all dirty repos and their downstream dependents
ws dirty
# Show the full dependency tree for a specific repo
ws graph logos-cpp-sdk
# Preview the exact nix --override-input flags that would be used
ws override-inputs logos-app-poc --auto-local# Pull latest code in every repo
ws foreach git pull
# Update a single flake input to its latest upstream
ws update logos-cpp-sdk
# Update everywhere — workspace + all sub-repo flake.locks
ws update --deep logos-cpp-sdk
# Update all flake inputs
ws update --all
# Check clone/branch status across all repos
ws status# Run any command in every repo
ws foreach git status -s
ws foreach git stash
# Chain multiple commands with quotes (|| true skips repos where nothing happens)
ws foreach 'git add . && git commit -m "your commit message" || true'
# Check which repos have local changes or unpushed commits
ws statusUse worktrees to work on feature branches that span multiple repos without disturbing your main workspace:
# Create a worktree for a feature branch
ws worktree add my-feature -b my-feature-branch
# Creates ../logos-workspace--my-feature/ with:
# - Workspace on branch: my-feature-branch
# - All repos on branch: ws/my-feature-branch
# Work in the worktree
cd ../logos-workspace--my-feature
# Make changes in repos/logos-cpp-sdk, repos/logos-liblogos, etc.
ws build logos-app-poc --auto-local
# Commit and push changes in modified repos
ws foreach 'git add . && git commit -m "my changes" && git push origin ws/my-feature-branch || true'
# When done, remove the worktree
cd ../logos-workspace
ws worktree remove my-featureFor claude-docker or similar tools: the repo includes .claude-docker/post-worktree.sh which automatically initializes submodules and creates ws/<branch> branches after a worktree is created.
The workspace dev shell comes with modern CLI tools (eza, bat, fzf, delta, starship, zoxide, neovim) and handy aliases (gst, ga, gc, gd, gl, wss, wsd, etc.).
For the best experience, install a Nerd Font (e.g. FiraCode Nerd Font) and set it as your terminal font. This enables file icons in ls and other tools.
# Workspace dev shell (has ws on PATH)
ws develop
# A specific repo's dev shell (has that repo's build tools)
ws develop logos-module
# Dev shell with local overrides for dirty repos
ws develop logos-app-poc --auto-local# Full breakdown for the whole workspace
ws loc
# Lines of code for a single repo
ws loc logos-cpp-sdk
# Per-repo breakdown sorted by LOC
ws loc --all
# Exclude nix/config files — just the actual code
ws loc --all --no-nix# Print DOT format to stdout
ws graph
# Generate an SVG
ws graph | dot -Tsvg > graph.svg
# See deps and dependents of a specific repo
ws graph logos-liblogos# See which repos are cloned and which have flakes
ws listWhen a repo gains tests for the first time:
- Add a
checks.<system>.testsoutput to the repo'sflake.nix. This derivation should build and run the tests, not just compile them. Example fromlogos-module/flake.nix:
checks = forAllSystems ({ pkgs }:
let
common = import ./nix/default.nix { inherit pkgs; };
src = ./.;
in {
tests = import ./nix/all.nix { inherit pkgs common src; };
}
);- Regenerate the dependency graph so
ws testpicks it up:
ws sync-graph- Verify it works:
ws test <your-repo>After adding a new repo, changing a repo's flake inputs, or adding tests:
ws sync-graphThis scans every repo's flake.nix and regenerates nix/dep-graph.nix with accurate deps and hasTests values. Commit the result.
- Add the git submodule:
git submodule add --depth 1 git@github.com:logos-co/my-new-repo.git repos/my-new-repo-
Add an entry to the
REPOSarray inscripts/ws(follow the existing format). -
If the repo has a
flake.nix, add it as an input in the workspaceflake.nixwith appropriatefollowsdeclarations so its deps chain through the workspace:
my-new-repo = {
url = "github:logos-co/my-new-repo";
inputs.nixpkgs.follows = "nixpkgs";
inputs.logos-liblogos.follows = "logos-liblogos";
# ... other follows as needed
};- Regenerate the dependency graph:
ws sync-graph- Commit the changes to
.gitmodules,flake.nix,scripts/ws, andnix/dep-graph.nix.
# 1. Remove the submodule
git submodule deinit -f repos/my-repo
git rm -f repos/my-repo
rm -rf .git/modules/repos/my-repo
# 2. Remove its entry from the REPOS array in scripts/ws
# 3. Remove its input and follows declarations from flake.nix
# 4. Regenerate the dependency graph
ws sync-graph
# 5. Commit all changesIf --auto-local overrides something you didn't intend (e.g. you have a stale change in a repo):
# See exactly which repos are dirty and why
ws status
# Preview the override flags without building
ws override-inputs logos-app-poc --auto-local
# Use --local instead to control exactly what gets overridden
ws build logos-app-poc --local logos-cpp-sdkIf a build fails unexpectedly with overrides, check that the dirty repo is in a buildable state — --auto-local uses whatever is on disk, including broken or half-finished work.
The workspace has GitHub Actions CI (.github/workflows/ci.yml) that runs on pull requests to master:
- Change detection — identifies which repos changed (submodule pointer diffs) and expands to include downstream dependents via
dep-graph.nix - Infrastructure changes (flake.nix, nix/, scripts/) trigger a build of the core chain: logos-cpp-sdk, logos-module, logos-liblogos, logos-package, logos-app-poc
- Repo changes trigger builds of only the affected repos and their dependents
- Docs-only PRs (.md/.txt) skip builds entirely
- Validates that
dep-graph.nixis up to date (ws sync-graphproduces no diff) - Runs all tests via
ws test --all
ws develop drops you into a modern shell environment with zsh, tmux, and a curated set of tools. For the best experience, install a Nerd Font (e.g. Monaco Nerd Font or FiraCode Nerd Font).
The first run takes a few seconds while nix fetches the tools. After that, the shell is cached and starts near-instantly. Use ws develop --fresh to force rebuild the shell if you've changed the configuration.
| Shortcut | Action |
|---|---|
Ctrl-A | |
Split pane horizontally |
Ctrl-A - |
Split pane vertically |
Ctrl-A c |
New tab |
Ctrl-A n / Ctrl-A p |
Next / previous tab |
Ctrl-A 1-9 |
Switch to tab by number |
Ctrl-A h/j/k/l |
Navigate panes (vim-style) |
Ctrl-A d |
Detach (re-enter with ws develop) |
Ctrl-A z |
Zoom current pane (toggle fullscreen) |
| Shortcut / Command | Action |
|---|---|
Ctrl-R |
Fuzzy search command history |
Ctrl-T |
Fuzzy find files |
z <partial> |
Jump to a previously visited directory |
| Command | Tool | Description |
|---|---|---|
ls |
eza | File listing with icons (supports -ltra, -la, etc.) |
ll |
eza | Long listing with all files |
lt |
eza | Tree view (3 levels deep) |
cat <file> |
bat | Syntax-highlighted file viewer |
less <file> |
bat | Paged syntax-highlighted viewer |
rg <pattern> |
ripgrep | Fast recursive search |
fd <pattern> |
fd | Fast file finder |
dust <dir> |
dust | Visual disk usage |
duf |
duf | Disk free overview |
| Alias | Command |
|---|---|
gst |
git status |
gs |
git status -s |
ga |
git add |
gc |
git commit |
gcm |
git commit -m |
gd |
git diff (side-by-side via delta) |
gds |
git diff --staged |
gl |
git log --oneline --graph --decorate |
glog |
git log --graph --oneline --all |
gp |
git pull |
gps |
git push |
gco |
git checkout |
gb |
git branch |
| Alias | Command |
|---|---|
wss |
ws status |
wsb |
ws build |
wst |
ws test |
wsd |
ws dirty |
wsg |
ws graph |
# Render a markdown file
glow README.md
# Interactive git TUI
lazygit