Rootbeer is a rust library and command line tool that executes a user-provided lua script in a sandboxed environment, creating a system configuration. Think of it akin to a dotfile manager like home-manager or chezmoi.
crates/rootbeer-cli: The command line tool the user interacts withcrates/rootbeer-core: The core library that runs the lua script
User configuration is provided through a layering system in the core library, where the base fundamentals (such as symlinking files, creating new files, running commands, etc.) are provided by the library, callable from the user's lua script.
Because the lua scripts are meant to run with 0 external dependencies, the core library also builds up fundamentals such as JSON serialization, string formats, and more (akin to Nix's stdlib).
The highest API layer is mostly defined in Lua and wraps the lower level APIs
in nice types, functions, and design patterns. For example, a zsh module is
defined in the highest layer which consumes the lower level APIs, allowing the
user to follow a nicely typed API to manage their zsh configuration.
This pattern needs to remain consistent across all modules and there are a few different tools built around these assumptions defined below.
Standard Lua dot-separated require paths are used everywhere — stdlib modules, user scripts, docs, and examples:
require("rootbeer.git")— dot syntax, used in all code.require("helper")— resolves from the user's source directory.
A Rust-level require wrapper in vm.rs translates dot paths to Luau-native
@-prefixed paths before they reach Luau's C++ layer. This is a hidden
implementation detail — all Lua files look like vanilla Lua and work with
lua-language-server without special configuration. Never use @-prefixed
paths in .lua files.
The LSP setup (rb lsp / rb init) writes type definitions to
~/.local/share/rootbeer/typedefs/ and a .luarc.json with
workspace.library pointing there. A generated init.lua lets
require("rootbeer") resolve to the rootbeer class type. No LuaLS plugin
is needed — standard workspace.library resolution handles everything.
- I/O operations run in a plan/execute mode, where calls only append to a log of operations that need to be executed on the apply stage.
- The lua language server is used to automatically generate markdown docs for
the documentation site defined in the
docsdirectory (built with Vitepress). Thedocs/api/_generated/directory is auto-generated fromlua/rootbeer/*.luameta files viascripts/lua2md.ts. NEVER hand-edit files in_generated/and NEVER manually write API field tables in doc pages. Instead, update or create the correspondinglua/rootbeer/*.luameta file with@class/@fieldannotations, and use<!--@include: ../api/_generated/<name>.md-->in the doc page (also taking care to ensure the path is added to the sidebar if necessary). - When updating native API functions, the
core.luameta file should contain the correct type-signatures for the language server to pick up on. Each module (core, host, git, zsh, etc.) has its own meta file inlua/rootbeer/.
The docs site (docs/) follows a strict organizational structure:
docs/guide/— Core principles and getting started. Hand-written prose only. Keep pages concise and scannable. Currently: getting-started, core-concepts, multi-device (profiles).docs/modules/— One page per integration module (zsh, git, ssh, brew, etc.). Each page should have a brief hand-written intro + examples at the top, with<!--@include: ../api/_generated/<name>.md-->at the bottom for autogenerated API reference.docs/reference/— Low-level API reference pages (core, host). Same pattern: hand-written context + autogenerated reference via include.docs/contributing/— Developer docs (setup, architecture).docs/api/_generated/— Autogenerated only. Never hand-edit.
When adding a new module doc page:
- Create
docs/modules/<name>.mdwith concise intro, one or two examples, and the<!--@include: ../api/_generated/<name>.md-->footer. - Add it to the correct category in
.vitepress/config.tssidebar under "Modules". Categories usecollapsed: truefor scalability. Current categories: Shell, Developer Tools, Package Managers. Create new categories as needed. - Ensure the corresponding
lua/rootbeer/<name>.luameta file exists for autogeneration.
Documentation principles:
- Be concise. Short paragraphs, let code examples speak.
- Never duplicate API field tables by hand — always use the generated include.
- Guide pages explain concepts; module pages show usage + reference.
- Cross-reference between guide and module pages where relevant.