A portable, modular shell environment and developer-tooling framework for local-first development.
Mantle gives you a user-owned shell runtime, an extensible mantle CLI, and reusable installer primitives that keep workstation setup deterministic, portable, and explicit across local development, containers, and CI.
Overview Β· Project status Β· Features Β· Supported environments Β· Quick start Β· Release lifecycle Β· CLI usage Β· Configuration Β· Architecture Β· Testing Β· Contributing
Mantle is a shell-first environment framework for people who want their development environment to be portable, inspectable, and owned by the user instead of hidden behind machine-specific bootstrap scripts.
Today, Mantle is composed of five main surfaces:
- A public
.shellrcentrypoint for Bash and Zsh. - A native Fish runtime entrypoint at
runtime/shells/fish/runtime.fish. - Shared libraries and modules that establish XDG, privacy, PATH, history, and platform behavior.
- A public
mantleCLI inbin/mantle. - Private command and installer implementations under
libexec/mantle/.
That separation matters: runtime initialization configures the current shell session, reusable libraries provide the foundations, the mantle CLI is the supported command-line interface, and libexec/ remains an internal implementation detail.
Mantle is local-first and repository-owned. Startup is designed to stay deterministic, quiet, and free from implicit network access, privileged operations, or unverified remote-script execution.
Mantle is an actively developed project with a real CLI, runtime modules, platform adapters, and a multi-layer test suite. It is not yet presented here as a general-purpose production-ready shell distribution.
- Bash and Zsh initialization through the repository-level
.shellrc. - Native Fish runtime loading through
runtime/shells/fish/runtime.fish. - Portable shared/runtime bootstrapping, XDG directory management, privacy defaults, PATH construction, and interactive history policy.
- Public
mantle help,mantle version, andmantle installcommand surfaces. - Dynamic installer discovery and tested dry-run/help flows for representative installers such as
eza,shfmt,shdoc,pyenv,linuxbrew, andtalisman. - CI-backed validation on Linux and macOS, plus static validation for shell syntax, ShellCheck, and formatting when local tools are available.
- Windows support is currently limited to Unix-compatible environments such as MSYS2 and Git Bash through
platforms/windows/runtime.sh; native PowerShell is out of scope. - Fish support has a native runtime, but its entrypoint differs from the Bash/Zsh
.shellrcflow and requires explicitMANTLE_ROOTconfiguration. - Some optional policies, such as automatic update-check suppression and project-local PATH injection, are opt-in by environment variable.
- Broader platform validation coverage, especially around Windows environments.
- A first tagged release has not yet been published. The signed-release workflow,
deterministic source package, and verification guide are ready for the first
v*tag. - Additional documentation depth and future permanent branding.
- Public
.shellrcentrypoint that must be sourced, not executed. - Idempotent initialization with explicit
initialized,initializing, andfailedstates. - Separate loading phases for shared runtime, POSIX baseline, active-shell runtime, modules, and platform adapters.
- Interactive-only loading for aliases and history behavior.
- Native Fish runtime with its own functions, completions, and
conf.dfragments.
- XDG-compliant config, cache, data, state, and runtime directory defaults.
- Deterministic PATH construction that prefers user-owned tool directories and Mantle's own
bin/. - Optional project-local PATH support gated behind
MANTLE_ENABLE_PROJECT_PATH=1. - Privacy-conscious defaults that disable telemetry for many supported tools by default.
- Separately configurable automatic update-check suppression.
- Public
mantledispatcher with validated command-name routing. - Dynamic help output from executable command implementations.
- Installer discovery from
libexec/mantle/installers/without maintaining a duplicate hard-coded list. - Dry-run support for installers that surfaces the planned download, version, asset, and destination.
- Reusable install primitives under
lib/install/for archive, checksum, GitHub release, filesystem, and platform tasks.
- Transactional module loader with cycle detection and retryable failed loads.
- Opt-in extension loader for optional libraries in
lib/extensions/. - Current shipped extensions for fonts, GitHub helpers, and Wget workflows.
| Environment | Status | Runtime | Notes |
|---|---|---|---|
| Bash | Supported | .shellrc |
Public entrypoint; tested locally and in CI. |
| Zsh | Supported | .shellrc |
Public entrypoint; tested locally and in CI when Zsh is available. |
| Fish | Supported with separate entrypoint | runtime/shells/fish/runtime.fish |
Requires explicit MANTLE_ROOT; does not use .shellrc. |
| POSIX shell | Baseline/shared support | runtime/shells/posix/runtime.sh |
Shared layer loaded before shell-specific runtime; not a standalone public entrypoint here. |
| Linux | Supported | platforms/linux/runtime.sh |
Covered by CI and used for Linux/WSL detection. |
| macOS | Supported | platforms/darwin/runtime.sh |
Covered by CI; intended for Apple Silicon and Intel. |
| Windows / MSYS2 / Git Bash | Experimental | platforms/windows/runtime.sh |
Minimal Unix-compatible adapter only; native PowerShell is out of scope. |
| WSL | Supported through Linux adapter | platforms/linux/runtime.sh |
Runtime environment can classify WSL; platform adapter normalizes to Linux. |
- Git
- Bash, Zsh, or Fish
- Python 3.9+ to build release archives (not needed to install one)
- Optional: Bats, ShellCheck, and shfmt for local validation
The repository-root installer keeps Mantle in a user-owned prefix and can manage
Bash, Zsh, and Fish activation for you. It targets Bash 3.2+ so it can run with
the stock /bin/bash on macOS.
./install.sh
./install.sh --dry-run
./install.sh --status
./install.sh --doctor
./install.sh --disable --shell bash
./install.sh --uninstallUseful installer options:
--method copyis the safe default;--method symlinkis the explicit development mode.--prefix "/custom/path"installs into a custom user-owned location.--shell bash|zsh|fish|allchooses which startup files are managed.--no-shell-hookis the non-mutating activation opt-out for CI and containers.--environment-diffprints the environment delta without mutating the parent shell.--pin VERSIONverifies that a downloaded release archive is exactly the expected version.--update --pin VERSIONatomically replaces an existing installer-owned prefix while leaving its activation hooks unchanged.
git clone "https://github.com/egohygiene/mantle.git"
cd "mantle"
source "./.shellrc"That flow is non-destructive: it initializes Mantle for the current shell session without overwriting your existing shell configuration.
mantle help
mantle version
mantle install --listFor Fish, set MANTLE_ROOT to the absolute path of your clone and source the native runtime:
set -gx MANTLE_ROOT (pwd)
source "./runtime/shells/fish/runtime.fish"- For the current shell: start a new shell session without sourcing Mantle.
- For persistent dotfiles: remove or comment out the line that sources Mantle from your personal shell configuration.
Mantle does not require replacing your existing shell startup files, and this README does not recommend editing them until you decide how you want to integrate it locally.
For production or Realm consumption, install an exact signed release archive rather than a mutable Git branch. The release guide covers checksum, Sigstore, and provenance verification plus pinned install, update, doctor, disable, enable, uninstall, rollback, and devcontainer paths.
See the release and recovery guide.
The supported public interface is bin/mantle, usually invoked as mantle after initialization or by running ./bin/mantle from the repository.
mantleresolves its own executable path.- It derives
MANTLE_ROOTfrom that path. - If
MANTLE_ROOTis already set, it must match the resolved installation root or the command exits with an error. - It dispatches only validated public commands from
libexec/mantle/commands/. - Internal scripts under
libexec/are implementation details, not the normal user interface.
mantle help
mantle help install
mantle version
mantle version --short
mantle doctor
mantle install --help
mantle install --list
mantle install eza --help
mantle install talisman --dry-runmantle helplists executable public commands dynamically.mantle help <command>delegates to the command's own--helpoutput.mantle versionprintsmantle <version>.mantle version --shortprints only the resolved version identifier.- Version resolution prefers
MANTLE_VERSION, thenVERSION, then Git metadata, thendevelopment. mantle doctorvalidates the installed payload and runs the isolated shell diagnostics without editing a startup file.
mantle install --listprints one discovered installer name per line fromlibexec/mantle/installers/.mantle install TOOL [INSTALLER_OPTIONS]forwards installer-specific options unchanged.- Dry-run support is installer-specific; for
talisman,--dry-runprints the planned version, asset, URL, archive format, and destination instead of performing an install. - Invalid command names and unknown global options exit with status
64, and non-executable internal command implementations exit with status70.
Installer destinations are configurable per installer where supported. For example, installers may expose flags such as --install-dir, --destination, --target, --ref, or tool-specific home-directory options.
Mantle uses standard environment variables rather than a repository-specific config file. The table below focuses on public or user-relevant variables that shape runtime behavior.
| Variable | Default | Purpose | Example |
|---|---|---|---|
MANTLE_ROOT |
Resolved from the entrypoint or CLI | Absolute path to the Mantle installation root | export MANTLE_ROOT="$HOME/src/mantle" |
MANTLE_SHELL_NAME |
Detected (bash, zsh, fish) |
Active shell runtime identifier | export MANTLE_SHELL_NAME="bash" |
MANTLE_INTERACTIVE |
Detected from the current shell | Distinguishes interactive and noninteractive initialization | export MANTLE_INTERACTIVE="1" |
MANTLE_DISABLE_TELEMETRY |
1 |
Enables Mantle's telemetry opt-out defaults when set to 1 |
export MANTLE_DISABLE_TELEMETRY="1" |
MANTLE_DISABLE_AUTOMATIC_UPDATE_CHECKS |
0 |
Opts into the update-check suppression module | export MANTLE_DISABLE_AUTOMATIC_UPDATE_CHECKS="1" |
MANTLE_ENABLE_PROJECT_PATH |
0 |
Prepends "$PWD/bin" and "$PWD/node_modules/.bin" when enabled |
export MANTLE_ENABLE_PROJECT_PATH="1" |
MANTLE_CREATE_XDG_DIRECTORIES |
1 |
Creates missing XDG directories and XDG_BIN_HOME |
export MANTLE_CREATE_XDG_DIRECTORIES="0" |
MANTLE_HISTORY_SIZE |
50000 |
Controls interactive shell and REPL history sizing | export MANTLE_HISTORY_SIZE="100000" |
MANTLE_DEBUG |
0 |
Enables additional debug logging and loader diagnostics | export MANTLE_DEBUG="1" |
XDG_CONFIG_HOME |
"$HOME/.config" |
XDG config root | export XDG_CONFIG_HOME="$HOME/.config" |
XDG_CACHE_HOME |
"$HOME/.cache" |
XDG cache root | export XDG_CACHE_HOME="$HOME/.cache" |
XDG_DATA_HOME |
"$HOME/.local/share" |
XDG data root and default base for many managed tool directories | export XDG_DATA_HOME="$HOME/.local/share" |
XDG_STATE_HOME |
"$HOME/.local/state" |
XDG state root, including history storage | export XDG_STATE_HOME="$HOME/.local/state" |
XDG_RUNTIME_DIR |
Platform-specific secure fallback | Private runtime directory for transient state | export XDG_RUNTIME_DIR="/run/user/1000" |
XDG_BIN_HOME |
"$HOME/.local/bin" |
User-owned executable destination added to PATH | export XDG_BIN_HOME="$HOME/.local/bin" |
TALISMAN_HOME |
"$XDG_DATA_HOME/talisman" |
Example installer destination override for talisman |
export TALISMAN_HOME="$HOME/.local/share/talisman" |
MANTLE_GITHUB_CONNECT_TIMEOUT |
10 |
Optional GitHub extension request connect timeout | export MANTLE_GITHUB_CONNECT_TIMEOUT="5" |
MANTLE_GITHUB_REQUEST_TIMEOUT |
30 |
Optional GitHub extension request total timeout | export MANTLE_GITHUB_REQUEST_TIMEOUT="15" |
Notes:
- Many other exported variables in
modules/tooling.sh,modules/privacy.sh, andmodules/history.share downstream tool settings rather than Mantle-specific controls. - Variables such as
MANTLE_INITIALIZATION_STATE,MANTLE_LAST_ERROR_STATUS,MANTLE_LOADED_MODULES, and loader sentinels are internal runtime state and should not be treated as stable configuration APIs.
.
βββ .shellrc
βββ bin/
β βββ mantle
βββ init/
β βββ bootstrap.sh
β βββ init.sh
β βββ load-core.sh
β βββ load-extensions.sh
β βββ load-platform-runtime.sh
βββ lib/
β βββ core/
β βββ extensions/
βββ libexec/
β βββ mantle/
β βββ commands/
β βββ installers/
βββ modules/
βββ platforms/
β βββ darwin/
β βββ linux/
β βββ windows/
βββ runtime/
β βββ shared/
β βββ shells/
βββ tests/
/.shellrcβ public Bash/Zsh entrypoint./bin/mantleβ public CLI dispatcher./init/β initialization orchestration and loader boundaries./lib/core/β reusable shell libraries for detection, guards, logging, time, colors, and Bash helpers./lib/extensions/β optional libraries loaded explicitly throughmantle_load_extension./modules/β runtime policies for XDG, privacy, tooling, PATH, cache, aliases, history, and update checks./platforms/β OS-specific adapters loaded after portable modules./runtime/shared/and/runtime/shells/β shared and shell-specific runtime behavior./libexec/mantle/commands/β supported public command implementations./libexec/mantle/installers/β private installer entrypoints discovered bymantle install./tests/β unit, integration, contract, and static validation harnesses.
mantle install talisman
β
βΌ
bin/mantle
β
βΌ
libexec/mantle/commands/install.sh
β
βΌ
libexec/mantle/installers/talisman.sh
β
βΌ
lib/install/
At a high level, Mantle initialization follows this sequence:
- The public
.shellrcentrypoint is sourced. - Mantle resolves
MANTLE_ROOTfrom the source path and classifies the current shell as interactive or noninteractive. init/init.shloads core libraries and runtimes throughinit/load-core.sh.init/load-extensions.shdefines the opt-in extension loader.lib/modules.shdefines the transactional module loader.init/bootstrap.shloads required noninteractive modules in order:xdg,privacy,cache,tooling, andenvironment.- The platform adapter is loaded through
init/load-platform-runtime.sh. - Interactive-only modules such as
aliasesandhistoryload whenMANTLE_INTERACTIVE=1. - If everything succeeds,
.shellrcrecordsMANTLE_INITIALIZATION_STATE=initialized.
Operational guarantees from the current implementation:
- Initialization is idempotent: sourcing
.shellrcagain after success returns immediately. - Recursive initialization is rejected with an error.
- Failed loads remain retryable; Mantle records a failed state instead of pretending initialization succeeded.
- Extensions are opt-in and are sourced at most once after a successful load.
- Startup does not install software, prompt for privilege escalation, or perform network access by default.
Use the public mantle install interface rather than invoking scripts in libexec/mantle/installers/ directly.
mantle install --list
mantle install eza --help
mantle install shfmt --helpmantle install talisman --dry-runFor validated installers such as talisman, dry-run reports the planned tool name, version, download URL, archive format, and install destination without mutating the system.
- Installer-specific help is implemented by each installer script.
- Some installers support version or ref selection with flags such as
--versionor--ref. - Destination control is installer-specific and may use flags such as
--install-dir,--destination,--target, or dedicated tool-home variables. - Installers are tested to avoid silent
sudousage. - Mantle ships reusable install libraries under
lib/install/for archive extraction, GitHub release metadata, checksums, and filesystem operations.
Because the installer set is discovered dynamically from libexec/mantle/installers/, the canonical way to see the current catalog is:
mantle install --listMantle's extension model is explicit and opt-in. Loading .shellrc does not automatically load extension libraries from lib/extensions/.
Current extensions in the repository:
fontsgithubwget
Typical loading pattern:
source "./.shellrc"
mantle_load_extension "wget"
mantle_wget_helpRepresentative extension capabilities:
fontsexposes font inventory helpers for Linux, macOS, and Windows-compatible environments.githubprovides release and tag-query helpers for GitHub repositories.wgetprovides a large set of reusable download/mirroring helpers and its ownmantle_wget_helpreference.
Treat these functions as optional libraries loaded into the current shell, not as always-on runtime behavior.
Mantle's current implementation makes several concrete promises that are worth calling out:
- Mantle itself does not perform startup-time network requests during normal shell initialization.
- Telemetry opt-out defaults are enabled by
modules/privacy.shunlessMANTLE_DISABLE_TELEMETRYis changed. - Automatic update-check suppression is separate and opt-in through
MANTLE_DISABLE_AUTOMATIC_UPDATE_CHECKS=1. - Platform runtimes must remain quiet, idempotent, and free from install, prompt, privilege, and preference-mutation side effects.
- Installers are contract-tested to avoid
sudousage. - XDG runtime directories are created with private permissions when Mantle needs a fallback.
- Public command, module, and extension names are validated before dispatch/loading.
- User-owned XDG locations are the default destination model for runtime state and many tool directories.
These are implementation-backed boundaries, not absolute security guarantees. Review individual installer help and source before using them in sensitive environments.
Mantle ships a canonical test runner at ./tests/run.sh.
./tests/run.sh
./tests/run.sh unit
./tests/run.sh integration
./tests/run.sh contract
./tests/run.sh static
./tests/run.sh formatWhat the current test harness provides:
- Bats-based unit, integration, and contract tests.
- Static validation for Bash syntax, optional Zsh/Fish syntax, optional ShellCheck, optional shdoc parsing, and optional shfmt checks.
- Canonical shell formatting through the root
.editorconfig. - Hermetic temporary-home isolation for tests that interact with shell state.
- Root-installer integration coverage for copy installs, symlink installs, shell activation, dry-run, pinned update, status, doctor, disable, uninstall, and rollback.
- Deterministic source-distribution packaging and isolated release-lifecycle verification.
- CI coverage for:
staticon Ubuntutest-linuxon Ubuntu with Bash, Zsh, and Fishtest-macoson macOS with Bash and Zsh
For local setup details, fixture guidance, command stubs, and debugging failed tests, see tests/README.md.
Tests are intended to avoid real installations and should stub external tools or network interactions when exercising installer flows.
A focused development workflow for this repository looks like this:
- Clone the repository.
- Create a branch for one focused change.
- Update the smallest relevant runtime, CLI, installer, or documentation surface.
- Add or update tests when behavior changes.
- Run the canonical validation commands from
./tests/run.sh. - Use a Conventional Commit for your commit message.
- Open a pull request with a concise summary of user-visible behavior and validation.
Current development tooling mentioned in the repository includes Bats, ShellCheck, shfmt, and shdoc-oriented help annotations in shell scripts.
If you want to contribute:
- Report bugs and request features through GitHub issues.
- Propose new installers by following the existing
libexec/mantle/installers/patterns and validating them with./tests/run.sh integration. - Add runtime capabilities through focused modules, shared libraries, or platform adapters instead of broad startup scripts.
- Add or update tests alongside behavior changes, especially for public entrypoints and installer contracts.
- Avoid committing machine-specific state, credentials, local config files, or generated artifacts.
This repository does not currently include a dedicated CONTRIBUTING.md, so the README is the primary contributor entrypoint for now.
Near-term work that can be inferred from the current repository and issue context includes:
- Broader Windows and cross-platform validation coverage.
- More complete contributor and architecture documentation.
- Permanent visual branding to replace the placeholder README banner.
- Publish the first signed release from a
v*tag. - Expanded command surfaces and shell ergonomics where they fit the current modular architecture.
These are directional items, not dated promises.
Mantle is available under the MIT License.
Mantle builds on established shell and Unix conventions, including the XDG Base Directory specification, Bats for behavioral testing, and ShellCheck for static shell validation.