Base Architecture
August 17, 2026 · View on GitHub
Status: maintained architecture reference Last reviewed: 2026-07-25
Overview
Base is an opinionated macOS-primary implementation of a local operating contract for independent Git repositories. It provides the inventory, readiness, trust, onboarding, and handoff evidence needed to operate that repo set through one CLI interface. The current implementation support contract covers macOS plus narrow Ubuntu/Debian source-checkout runtime and apt-backed setup paths. Broader Linux distribution support, WSL, and Windows are not in scope today.
The governing philosophy: solve your own problem elegantly first. Base is built for a specific workflow: multiple peer Git repositories under a shared parent directory, each declaring their dependencies through a simple manifest, all managed through a unified interface. GitHub is the first-class forge automation target today. Non-GitHub Git repositories can still use Base's local project loop after they exist locally, but GitHub workflow automation remains GitHub-specific. The exact contract lives in Source Control And Forge Support.
Product Direction
Base's long-term product shape is a local operating contract for deterministic
readiness and handoff across independent Git repositories. It should make a
folder of sibling repositories understandable, locally ready, explicitly
trusted, easy to onboard, and transferable without becoming a replacement for
Homebrew, mise, Docker, GitHub CLI, IDEs, agent runtimes, or project-owned
build systems.
The coherent product loop is:
inventory -> prepare -> verify -> trust -> onboard -> hand off
Here, deterministic means explicit inspection order, stable findings or machine-readable structures, and clear next actions from declared inputs and observable local state. It does not mean hermetic builds, byte-for-byte environments, or transactional multi-repository mutation.
The product responsibility tiers are:
- The core outcome: deterministic local readiness and handoff.
- The enabling execution contract:
base_manifest.yaml,basectl,base-wrapper, activation, and declared project commands. - Supporting workflow packs: repository baselines plus GitHub and release conventions.
- Adapters: environment managers, IDEs, containers, Nix/devenv, and AI tools.
These are architecture responsibility tiers, not separately installed
packages. Major features should strengthen the outcome loop. A command does not
become core merely because basectl dispatches it.
Core Principles
- Opinionated over flexible — Base makes decisions for you. Fewer choices means less complexity and easier maintenance. If you disagree with the decisions, use a different tool.
- Problem-first — technology choices follow real problems encountered during development, not the other way around.
- Ship incrementally — start minimal, use it yourself, let the tool grow organically through real use.
- Idempotent by design — running any setup command multiple times should produce the same result safely.
- Orchestrate, do not replace — Base should discover, sequence, validate, and explain mature tools rather than absorb their full configuration models.
- Observable and diagnosable — Base commands should make local state and failures understandable through clear output, stable finding IDs, JSON where useful, and inspectable logs.
Repository Structure
Base is a public GitHub repository. The primary dogfood workspace is a set of GitHub repositories checked out as peers under a shared parent directory:
~/work/ ← shared workspace root
base/ ← the Base repository itself
myproject-a/ ← peer project with base_manifest.yaml
myproject-b/ ← peer project with base_manifest.yaml
banyanlabs/ ← peer project with base_manifest.yaml
Base discovers peer repositories by scanning the workspace root for Git
repositories that contain base_manifest.yaml. The local project loop does not
require the remote to be hosted on GitHub. Repository creation, configuration,
issue, pull-request, Project, and release automation do require GitHub today.
Shell Support
Base supports two shells:
- bash — primary scripting shell. All orchestration scripts run in bash. Default for all base internals.
- zsh — supported for interactive use. Power users who prefer zsh for their interactive shell are accommodated.
Fish, tcsh, ksh, and other shells are explicitly out of scope for now. If real demand emerges, support can be added later.
Command Surface — basectl as Control Plane
The most important command-surface decision in Base: the product is Base, the
control-plane command is basectl.
basectl is the public entrypoint. It is a normal executable command that
establishes the Base runtime before dispatching to Bash scripts or future Python
layers. This keeps the product name and the control-plane action separate:
basectl setupbasectl checkbasectl doctorbasectl update-profilebasectl projects listbasectl workspace statusbasectl activatebasectl runbasectl testbasectl buildbasectl demobasectl releasebasectl logs
Shebang-based Bash scripts can also use:
#!/usr/bin/env basectl
In that mode, basectl wraps the script in the Base environment, sources it,
and calls its main function.
The current dispatch and runtime contract is documented in execution-model.md.
Project Activation Model — Subshell Design
Why Subshells
Activating a project environment means setting shell variables, aliases, functions, and activating a Python virtual environment. The naive approach of activate/deactivate (like Python venv) does not scale to the full richness of a shell environment. Tracking and restoring arbitrary shell state on deactivation is complex and error-prone.
The solution: spawn a Bash runtime shell when activating a project. The
project environment lives inside that shell. The user works in that shell. When
done, they exit (or Ctrl-D) and return to their base shell. No deactivation
logic required. No state restoration complexity.
This does not require a distinct shell function. A normal basectl activate <project>
command can validate the target and launch the Bash runtime shell.
Activation Flow
basectl activate myproject
↓
1. Look up BASE_HOME, resolve the workspace root, and scan known projects
2. Validate myproject exists and has a valid manifest
3. Set BASE_PROJECT=myproject
4. Spawn a new Bash runtime shell
5. In that shell: load the Base runtime and user Bash startup with guardrails
6. In that shell: activate the project's Python virtual environment
7. In that shell: source manifest-declared activate.source scripts
8. Update the prompt to reflect the active project
9. User works in the Bash runtime shell
10. User exits → returns to base shell, prompt resets
basectl activate — Intelligence
- Takes a project name as argument (not a directory path)
- Works from any current directory — the user does not need to be in the project folder
- Base locates projects from explicit
--workspace, configuredworkspace.root, or the parent of$BASE_HOMEas a source-checkout fallback - Validates that the target is a recognized Base project with a valid manifest
Shell Environment Layers
Base separates ordinary shell startup from Base runtime activation. These shell environment layers are distinct from the product responsibility tiers above; their numbering is local to shell startup and runtime state.
Shell Layer 1 — Dotfile Integration
Applied by the user's normal Bash/Zsh startup files after running
basectl update-profile.
Contains:
- login-shell handoff for Bash (
~/.bash_profilesources~/.bashrcwith guardrails) - interactive Bash/Zsh guardrails
BASE_HOMEderived from the sourced Base snippet- Base
bin/added toPATHsobasectlis available after login - optional sibling
base-platform-tools/binadded toPATHwhen present - optional shell defaults when the user runs
basectl update-profile --defaults
This layer must not source base_init.sh and must not establish the full Base
runtime contract. It is only about Bash/Zsh startup behavior plus launcher
availability.
Shell Layer 2 — Base Runtime Environment
Applied when the user invokes basectl, basectl activate <project>, or
basectl /path/to/script.sh. Invoking basectl with no arguments in a terminal
starts the Base project runtime while preserving the caller's current
directory.
Contains:
- exported Base path contract such as
BASE_HOME,BASE_BIN_DIR,BASE_BASH_LIB_DIR,BASE_BASH_LIBS_DIR, andBASE_BASH_LIBS_SOURCE - OS and host metadata such as
BASE_OS,BASE_PLATFORM,BASE_HOST_ENV, andBASE_HOST - Base's Bash standard library
import_base_libfor convention-based Base Bash library imports from the resolved reusable Bash library root- PATH additions for Base's own executable entrypoints
- optional PATH additions for the local Base Platform Tools companion repo
This layer is established by base_init.sh, which is sourced only through the
basectl command path. The canonical variable reference and mutability policy
live in Runtime Environment. The standalone
base-bash-libs install path and post-migration contract live in
Base Bash Libraries.
Shell Layer 3 — Project-Specific Environment
Applied inside the project subshell when basectl activate <project> is run.
Contains:
- Project-specific PATH additions
- Project-specific environment variables
- Project-specific aliases and functions
- Project-specific Python virtual environment activation
- Manifest-declared
activate.sourcescripts BASE_PROJECTupdated to the project name
Project-specific settings layer on top of the Base runtime environment. Settings not overridden by the project inherit from Base. When the subshell exits, the project layer disappears naturally — no explicit deactivation needed.
Dotfile Management
Base updates the user's real dotfiles by managing small marked sections. The preferred adoption model is:
basectl update-profile
By default, Base updates all four files:
| Dotfile | Base snippet | Purpose |
|---|---|---|
~/.bash_profile | lib/shell/bash_profile | Login Bash bridge into ~/.bashrc |
~/.bashrc | lib/shell/bashrc | Interactive Bash startup |
~/.zprofile | lib/shell/zprofile | Thin Zsh login startup |
~/.zshrc | lib/shell/zshrc | Interactive Zsh startup |
Base does not symlink over the user's dotfiles and does not own content outside its clearly marked managed sections. The markers are intentionally explicit, for example:
# >>> base: bashrc managed >>>
# <<< base: bashrc managed <<<
Optional Base shell defaults are enabled explicitly with basectl update-profile --defaults.
Directory Change Behavior
Changing directory does not trigger environment changes.
This is a deliberate design decision. Auto-activating environments on cd is confusing
because the intent behind a cd is ambiguous — the user may be casually navigating,
not intending to switch project context. Background logic running on every cd also
slows the shell and is error-prone.
The only things that change on cd:
$PWDupdates (built-in shell behavior)- The directory portion of the prompt updates
- The git branch portion of the prompt updates dynamically (see Prompt section)
Everything else stays stable until the user explicitly runs basectl activate <project>.
Prompt Design
The prompt shows three things, always:
[myproject: main] ~/projects/myproject/src $
| Element | Source | Behavior |
|---|---|---|
| Project name | $BASE_PROJECT | Static — set at activation, stays until subshell exits |
| Git branch | Dynamic query | Updates on every prompt render |
| Current directory | $PWD | Updates on every cd |
Project Name in Prompt
BASE_PROJECT is set by basectl activate. When the user invokes basectl
with no arguments in an interactive terminal, Base discovers the nearest
base_manifest.yaml above the current directory and activates that project
while preserving the current directory. If no manifest is found, it falls back
to the base project.
Once the subshell starts, BASE_PROJECT stays fixed until the shell exits. It
does not change dynamically when the user later runs cd.
Git Branch in Prompt
The git branch is not stored in a variable. It is queried dynamically each time the
prompt renders. This ensures the prompt reflects reality when the user runs git checkout to switch branches inside the subshell.
Implementation in PS1:
_base_git_branch() {
git symbolic-ref --quiet --short HEAD 2>/dev/null ||
git rev-parse --short HEAD 2>/dev/null
}
PS1='[${BASE_PROJECT}: $(_base_git_branch)] \w $ '
Key decision: the branch is queried from the current directory at prompt render time. This keeps the prompt honest when a Base runtime shell is started from a nested project directory or when the user moves between repositories inside the same shell.
Why Not Show Python Venv in Prompt
The project name in the prompt implies the Python virtual environment — if a project is active, its venv is active. Showing both would be redundant. The prompt stays clean.
Python Virtual Environments
Base Venv
- Created once during
basectl setup - Lives at
~/.base.d/base/.venv - Used to run Base's own Python orchestration code (manifest parsing, project discovery, etc.)
- Not activated in the user's interactive shell by default — it runs internally when Base needs it
Project Venv
- Created per project during
basectl setup <project>orbasectl setupwhen scanning all projects - Lives at
<project-root>/.venvfor non-Base projects by default - Can be kept at
~/.base.d/<project>/.venvwithpython.venv_location: external - Activated automatically when
basectl activate <project>spawns the project subshell - Deactivated automatically when the subshell exits
Key Distinction
Only one Python venv can be active at a time. Base venv runs quietly in the background for Base's own tools. Project venv is what the user interacts with. The two never conflict because Base venv is not surfaced in the interactive shell.
Project Manifest
Each Base-managed project declares its dependencies in a YAML manifest file at the project root. Base reads this manifest to know what to install and configure.
File: base_manifest.yaml
Current and planned structure:
schema_version: 1
project:
name: myproject
languages:
- python
- javascript
brewfile: Brewfile
mise: .mise.toml
artifacts:
- type: python-package
name: requests
version: latest
health:
required_env:
- DATABASE_URL
- REDIS_URL
required_ports:
- name: postgres
host: 127.0.0.1
port: 5432
state: listening
- name: app
port: 8000
state: free
activate:
source:
- .base/activate.sh
test:
command: pytest tests/
commands:
dev: uvicorn app:app --reload
lint: ruff check .
schema_version is a manifest compatibility marker. Missing values are treated
as schema version 1, which keeps existing project manifests valid. Base rejects
manifests with a schema version newer than the installed Base understands so
future team-facing manifest expansion can fail with a clear upgrade message
instead of ambiguous parser behavior.
project.languages is additive project taxonomy. It is an explicit,
allowlisted list that may describe a polyglot repository; it does not infer
toolchain ownership or make unsupported language setup automatic. The
python.manager: uv field remains the separate Python environment contract.
The Python layer interprets this declarative manifest and translates it into orchestration actions. The design rule is delegation-first:
- Use Homebrew's own
Brewfile/brew bundleflow for ordinary macOS packages. - Use
misefor tool versions, language runtimes, environment variables, and future tasks when a project opts into it. Base runsmise installduring setup and does not reimplement mise's version management. - For Go and Java projects, put runtime pins such as
go = "1.22"andjava = "temurin-21"in.mise.toml. Keep system tools inBrewfile, Go dependencies ingo.mod/go.sum, and Java dependencies in Maven or Gradle project files. Base should orchestrate those contracts rather than add generic language package artifact types. - Use a project-owned
testcontract forbasectl test <project>delegation. Projects can declare eithertest.commandfor a shell command ortest.misefor amise run <task>delegation. Extra arguments afterbasectl test <project> --are passed through to the delegated command. - Use a project-owned
commandsmap for additional named commands thatbasectl run <project> <command>can execute from the project root. These commands use the same Base project environment and virtual environment contract asbasectl test; the command nametestis reserved for the top-leveltestcontract. - Use
health.required_envfor local environment contracts thatbasectl checkandbasectl doctorshould validate without exposing secret values. - Use
health.required_portsfor local TCP port contracts that should be explicitlylisteningorfree. Base checks connection state only; it does not mutate local services, inspect process ownership, or replace future Docker Compose health checks. - Use
activate.sourcefor explicit project activation scripts that need to affect the interactive runtime shell, such as local environment loading, aliases, or functions. Source paths must be relative to the project root and must resolve inside that root. - Let Base own Base-aware package reconciliation and any project virtual
environment explicitly requested by
python:orpython-packagemanifest contracts. Shell-only manifests use the Base runtime for control-plane work. - Do not run arbitrary project setup hooks until Base has a clear safety contract for dry-run behavior, interactivity, setup diagnostics, and broader side effects. See setup-hooks.md for the setup no-hooks decision and future reconsideration criteria.
Base owns the curated tool artifact registry only for things it must manage
directly. The current registry is cli/python/base_setup/registry.py.
The optional top-level brewfile field delegates ordinary Homebrew dependencies
to Homebrew's native brew bundle flow. The path is relative to the project root
and must stay inside the project. Base runs brew bundle --file=<path> during
setup before reconciling Base-managed artifacts.
python-package artifacts are pass-through PyPI package names and install into
the project virtual environment at <project-root>/.venv for non-Base projects
by default. Base's own project venv remains ~/.base.d/base/.venv, and a
non-Base project can explicitly keep the historical external location with
python.venv_location: external. The wrapper bin/base-wrapper runs Python
packages through that project-scoped venv.
A structured python: manifest section owns Python project runtime policy.
python.manager: uv delegates environment setup to uv. python.requires_python
lets a project select a supported Python 3.10 through 3.13 minor for
Base-managed virtualenv creation, while check/doctor distinguish unsupported
requests from supported-but-unavailable interpreters. See
python-manifest.md for the current contract and migration
boundary.
A manifest requires project Python when it contains a top-level python:
mapping, including an explicit empty python: {}, or declares at least one
python-package artifact. project.languages: [python] remains taxonomy and
does not independently create an environment. When neither project-Python
contract is present, setup, check, and doctor run Base-owned parsing and
reconciliation from ~/.base.d/base/.venv, skip Base's default project Python
bootstrap artifacts, and do not require <project-root>/.venv. Workspace
status reports that venv as not_applicable rather than missing.
This is the shell-only vertical slice of the broader runtime-boundary work in #1611. Activation and project-owned run, test, build, and demo command environment semantics retain their existing routing in this slice; the remaining migration should continue to separate Base control-plane execution from project-owned runtime execution without adding another special-purpose runtime.
Homebrew-managed tool artifacts currently support version: latest. If a
project requests a pinned Homebrew version, setup fails clearly instead of
silently installing a different version. For version: latest, check and doctor
report installed-but-outdated Homebrew packages, and setup upgrades them. New
ordinary Homebrew tools should prefer Brewfile delegation over registry growth.
Richer version conflict handling across projects is a later iteration, not part
of the initial build.
Default artifacts can be marked with bootstrap: true when they are required to
run Base's Python CLI layer inside a project virtual environment before the rest
of that project's artifacts are reconciled. In the current default manifest,
click and PyYAML carry this marker.
Artifact install commands keep stdout attached to the terminal so long-running
tools such as brew and pip remain live and readable while setup runs. Base's
persistent log records the command intent and captures stderr on failures. If
Base later needs full install transcripts, it should add tee-style streaming so
users still see progress while stdout is also preserved in the log.
Project Model Scope
Base's project model is deliberately flat and simple: one repository equals one project, and all projects are peer siblings under a shared parent directory.
This constraint is a feature, not a limitation. It keeps the manifest readable, the discovery logic fast, and the activation model unambiguous. The four most common requests to extend this model — and why each is out of scope:
Parent-child manifest inheritance
A child project that inherits its parent's manifest looks attractive for sharing
common artifacts across related projects. Base already has two manifest layers
that address this need: lib/base/default_manifest.yaml for shared bootstrap
defaults and each project's own base_manifest.yaml. A third layer creates an
inheritance chain where diagnosing "why is this artifact installed?" requires
tracing provenance across files rather than reading one manifest. It also
introduces discovery-order coupling — a child is broken in a non-obvious way
when its parent is not checked out.
The right escalation path: if the need is for shared Homebrew tools, use Brewfile delegation. If the need is for shared Python packages, declare them in each project's manifest. If Base ever needs an org-level layer, introduce a workspace-level manifest at the parent directory rather than reaching for inheritance.
Project groups sharing a manifest
If two projects share a manifest they are probably not two separate projects —
they are one project with two source trees. A group concept introduces a new
entity (group vs. project vs. member) with no clear semantics for which project
gets activated when the user runs basectl activate. Closely coupled components
should either be treated as one project or kept as independent projects that
happen to declare the same artifacts.
Multiple projects within a single repository
Scanning a repository tree for nested base_manifest.yaml files makes
basectl projects list traverse potentially thousands of directories. It also
conflicts with the activation model — basectl activate <project> assumes a
project root is a repository root, so one active context maps cleanly to one
directory. Sub-repo manifests blur that mapping in ways that affect the prompt,
PATH manipulation, and virtual environment selection.
What already works: discover_manifest in the standalone
base-cli package
walks upward from the current directory, so a manifest anywhere in a repository subtree
is found when basectl is invoked from that directory. The gap is only in
basectl projects list, which enumerates workspace siblings rather than trees.
If a specific monorepo layout genuinely needs enumeration, the least-invasive
path is to scan exactly one additional level and require explicit opt-in from a
parent manifest rather than auto-discovering everything.
Workspace manifest
A workspace manifest is a team-shared repository set contract. It is distinct from
each project's base_manifest.yaml: the workspace manifest says which
repositories should belong together, while project manifests say how each
repository participates in Base.
Workspace commands operate on discovered local projects by default. Configuring
workspace.manifest or supplying --manifest <path> adds expected-repository
awareness without changing the default discovered-project behavior. The
command-line manifest takes precedence over user config. See
Workspace Manifest.
Caching project definitions
Base keeps project discovery flat: it scans direct children of the workspace
root for base_manifest.yaml files instead of traversing repository trees. That
keeps discovery predictable, but repeated YAML parsing still shows up in
project-aware commands and shell completion.
The implemented cache is intentionally narrow. Base stores discovered project
metadata under base/cache/discovery/, keyed by the resolved workspace path.
On each discovery run it still checks the immediate workspace
children, but it reuses cached project names and paths when the manifest path,
mtime, and size set is unchanged. A new checkout, manifest edit, or manifest
removal changes that key data and forces a fresh parse.
This cache is an optimization, not an authority. The manifest files remain the source of truth, and corrupt or unwritable cache files are ignored.
Workspace Operations
Workspace operations supply the inventory and onboarding evidence for the core outcome. The command surface should make local state visible before it mutates many projects.
Current workspace-oriented commands include:
basectl projects list
basectl workspace status
basectl workspace check
basectl workspace doctor
basectl workspace onboarding
basectl workspace agent-brief
basectl workspace clone
basectl workspace pull
basectl workspace update
basectl workspace init
basectl workspace configure
basectl workspace setup
Workspace commands are read-first and mutate only through explicit init, clone,
pull, update, configure, or setup commands. basectl workspace status reports project manifest
state, virtual environment state, and Git state across discovered projects,
including invalid manifests without stopping the whole scan. With
workspace.manifest or --manifest <path>, workspace commands also report
missing required repositories, missing optional repositories, and discovered
Base-managed projects outside the expected repository set. basectl workspace check
and basectl workspace doctor run project checks and diagnostics across
discovered projects. basectl workspace onboarding is a shipped read-only text
or JSON summary of ready, needs-setup, invalid-manifest, missing-required, and
missing-optional first-day repository state; it does not clone repositories or
run setup. basectl workspace agent-brief is a separate read-only text or JSON
projection over manifest and local filesystem state. It includes expected and
extra Base-managed repositories, reports baseline, agent-guidance, AI-context,
environment, and validation signals, and recommends explicit next commands
without executing them or making network calls. Environment evidence only
checks for the expected executable interpreter file and reports it as
unverified; the brief does not run repository-local code. basectl workspace clone
materializes missing expected repositories only when invoked
directly. basectl workspace pull updates only the local workspace manifest
after validating an explicit or configured source. basectl workspace init
bootstraps a workspace from a workspace configuration repository and can
materialize member repositories. basectl workspace configure applies the
existing repo configure repair path across discovered Base-managed workspace
repositories. basectl workspace update walks the manifest in order and runs
git pull --ff-only for present repositories, continuing after individual
failures. It skips the active BASE_HOME control plane when that checkout is
the manifest's base target; a separate workspace checkout is eligible for
update. basectl workspace setup walks the manifest in order and
delegates eligible local repositories to basectl setup, forwarding --yes
when requested. It continues after individual failures and returns a failure
when any setup target fails; dry-run reports required missing or invalid targets
as failures without modifying repositories. JSON output is part of the
status/check/doctor/onboarding/agent-brief contract so automation and future CI
smoke checks can use the same data.
Future workspace commands should follow the same principles:
- start with read-only status, check, doctor, onboarding, and handoff behavior
- require explicit commands and dry-run paths before mutating many repositories or local workspace metadata
- treat partial failure as normal in multi-repo workspaces
- keep sibling repositories under a shared workspace root as the default discovery model
- report machine-readable summaries early
Doctor And Observability
basectl doctor is a core trust-building feature. It should explain what is
wrong, why it matters, whether Base can fix it, and the safest next command.
Doctor output must stay non-mutating unless a future explicit fix command is
designed with dry-run behavior.
Doctor findings use stable identifiers documented in Doctor Finding IDs. Automation and runbooks should match on those IDs instead of human-readable messages.
Base should also remain locally observable. basectl logs exposes recent Base
CLI runtime logs from the Base cache root, basectl history lists the
structured local command-history index, and basectl history --report
summarizes recent local activity without sending telemetry anywhere.
Useful command metadata includes the command, target project or workspace,
start and end time, exit code, manifest version where relevant, external tools
invoked, and log file paths.
The goal is explainability, not surveillance. Base should help users understand what happened on their own machine.
These surfaces also provide today's handoff evidence: onboarding reports explain
initial repo state, workspace agent-brief summarizes repository handoff
signals and ordered next actions, check/doctor provide current readiness,
history reports show recent local command outcomes, and .ai-context exports
carry canonical orientation. The issue-oriented handoff bundle planned in
#1562 remains separate; it
may compose issue, branch, history, diagnostics, and context evidence, but is
not a shipped command or stable schema.
Mac Bootstrap Sequence
On a fresh Mac, installation and first-run setup use this sequence:
- Check for Homebrew — install if missing
- Check for Xcode CLI tools — install if missing
- Install Python (target version) via Homebrew
- Create Base's own virtual environment at
~/.base.d/base/.venv - Install Base's Python bootstrap dependencies into
~/.base.d/base/.venv - After
basectl setupfinishes, explicitly runbasectl update-profileas a separate one-time shell-profile step; run it again only when updating Base-managed shell dotfiles - Scan the parent directory for peer repos with base manifests
- For a shell-only setup target, run manifest reconciliation from Base's own
venv without creating a project venv. For a target that explicitly declares
project Python, seed its project venv with
bootstrap: truedefault artifacts, then run reconciliation throughbase-wrapper --project <project>.
Homebrew installation follows Homebrew's official install/HEAD/install.sh
bootstrap command. That means Base intentionally trusts Homebrew's mutable
installer entry point instead of pinning a commit SHA. Pinning would reduce
installer mutability, but would also make Base responsible for tracking
Homebrew installer updates and could diverge from Homebrew's documented support
path. Environments with stricter supply-chain policy should preinstall Homebrew
through managed workstation provisioning before invoking basectl setup.
GitHub and Repository Conventions
These conventions are a supporting workflow pack for the operating contract; they are not the core readiness category.
- Base uses a public GitHub repository at
basefoundry/base. - GitHub Issues are the official product backlog for bugs, feature requests, release work, maintenance, and documentation follow-up.
basectl releasemanages annotated tags and GitHub Releases for Base using the manifest-owned release metadata inbase_manifest.yaml.- The Homebrew tap at
basefoundry/homebrew-baseowns the formula that installs published Base releases. - Homebrew tap updates happen after each GitHub Release and remain a manual
handoff.
basectl releaseprints the required tap follow-up when the project manifest declares Homebrew metadata.
Adoption Signals
Base should measure product readiness through concrete local workflows:
- one-command install works on a clean supported macOS machine
- a public demo project can be cloned, set up, checked, tested, diagnosed, and demonstrated by Base
- a technically adjacent user can complete guided onboarding without reading private internal notes
- a second human or coding agent can reconstruct readiness and recent local evidence without an undocumented maintainer briefing
- multiple project types are supported through adapters instead of special-case code
doctoridentifies and explains the most common local failure modes- JSON output is stable enough for automation and CI smoke checks
These are not analytics goals. They are acceptance signals for whether Base is becoming useful beyond one personal machine.
Utility Scripts and Extras
Base ships with a small collection of utility scripts useful for day-to-day Mac development:
- Shell helper functions for common operations
- Python library utilities for unified CLI development (shared across Base-managed projects)
- Git convenience helpers (branch management, PR workflows)
- Potentially: a base-provided Python CLI framework so that projects built within the Base ecosystem share a consistent CLI style
These extras emerge organically from real needs — they are not designed upfront.
What Base Is Not
For the canonical non-goals and ecosystem boundary, see Tool Boundaries.
Settled Design References
Several questions from the early architecture pass now have dedicated reference documents:
- Workspace manifest location, trust, clone, pull, and team-onboarding policy are covered by Workspace Manifest.
- Non-Python projects stay inside the Base manifest command contract and use
project-owned tools through
basectl run,basectl test,mise, or direct shell commands. See Tool Boundaries and Python Manifest Section. - Docker and dev container integration remain coexistence and orchestration topics rather than Base-owned container management. See Tool Boundaries.
Remaining questions should be tracked as GitHub Issues when they become concrete enough to affect a project or release.
Relationship to Banyanlabs
Base is the prerequisite for banyanlabs. Banyanlabs (a multi-cloud, polyglot DevOps
learning environment) will be a Base-managed project — it will have a base manifest
declaring all its infrastructure tool dependencies. Base handles the bootstrapping.
Banyanlabs handles the learning environment. A Banyanlabs-specific installer can
bootstrap or locate Base, clone the project, and call basectl setup with
friendlier product-specific messaging. See Project Installers
for that boundary. Base must ship first.
This is a living document. See CHANGELOG.md for the dated history of architecture and command-surface changes.