Project Installers
August 16, 2026 ยท View on GitHub
Base should stay a developer workspace engine. Project-specific installers should own the friendlier, product-specific onboarding experience.
This is an intentional product boundary. Base should not add basectl onboard <project> for now. That command would make Base responsible for every
project's product narrative, repository bootstrap choices, credentials, and
next-step guidance. Those concerns change per project and are better owned by
the project itself.
This distinction matters because Base and a project installer serve different audiences:
basectl setupis for developers and technically-adjacent users who are willing to run a terminal command and read setup output.- A project installer is for someone who wants to use a specific project and should not need to understand Base first.
For example, a future banyanlabs/install.sh can present Banyanlabs-specific
language, explain what will happen, bootstrap Base if needed, and then call Base
internally. Base should provide the reliable mechanics; Banyanlabs should provide
the product-shaped welcome mat.
Responsibilities
Base owns:
- macOS workstation bootstrap primitives and supported Ubuntu/Debian runtime setup
- Homebrew, Xcode Command Line Tools, apt-backed prerequisites, Base Python, and Base venv setup
- project discovery through
base_manifest.yaml - artifact reconciliation through
basectl setup - diagnostics through
basectl checkandbasectl doctor - shell integration through
basectl update-profile
The Ubuntu/Debian path is narrower than macOS: it covers the supported runtime and apt-backed prerequisites, not GUI IDE installation or every project runtime dependency. See Linux Support for the platform boundary.
A project installer owns:
- project-specific framing and instructions
- preflight messaging for that project's audience
- choosing where the project workspace should live
- cloning or updating the project repository
- installing or locating Base
- calling Base commands in the right order
- imperative setup steps that are not yet covered by a typed Base manifest contract
- explaining next steps after setup succeeds or fails
The installer should not reimplement Base's setup logic. It should call Base.
Recommended Flow
Start from Base's maintained template, then customize the project-owned copy:
basectl repo installer-template
basectl repo installer-template install.sh
basectl repo installer-template --print
basectl repo installer-template install.sh --issue 123 --category enhancement --pr --dry-run
With no path, the command writes an executable install.sh in the current
directory. With a path, it writes the template there. Use --print when you want
to inspect or pipe the maintained template. Existing files are left unchanged so
project customizations are not overwritten.
Use --pr --issue <number> when the generated installer should be reviewed
before landing. The target path must be inside a clean Git worktree, the GitHub
repository is inferred from origin unless --repo <owner/name> is provided,
and Base opens a draft pull request on the canonical issue-backed branch after
committing only the generated installer file. Real PR runs derive and verify the
issue's standard category label; offline --pr --dry-run previews require
--category <name>.
For a project such as Banyan Labs, the generated script should change the project-owned values at the top:
PROJECT_NAME="${PROJECT_NAME:-banyanlabs}"
PROJECT_REPO_URL="${PROJECT_REPO_URL:-https://github.com/basefoundry/banyanlabs.git}"
WORKSPACE_DIR="${WORKSPACE_DIR:-$HOME/work}"
BASE_DIR="${BASE_DIR:-$WORKSPACE_DIR/base}"
PROJECT_DIR="${PROJECT_DIR:-$WORKSPACE_DIR/$PROJECT_NAME}"
BASE_INSTALL_URL="${BASE_INSTALL_URL:-https://raw.githubusercontent.com/basefoundry/base/HEAD/install.sh}"
BASE_INSTALL_SHA256="${BASE_INSTALL_SHA256:-}"
RUN_UPDATE_PROFILE="${RUN_UPDATE_PROFILE:-true}"
BASE_INSTALL_SHA256 is empty by default because the maintained template points
at Base's mutable HEAD/install.sh. That path is convenient for starter
templates, but it is intentionally visible at runtime: when the value is empty,
the template warns that downloaded installer checksum verification was skipped.
For project-owned installers, prefer pinning BASE_INSTALL_URL to a tag, commit,
or project-owned copy and setting BASE_INSTALL_SHA256 to that installer's
SHA-256 digest. The template verifies the downloaded installer before executing
it and stops immediately on a mismatch. Update the digest whenever the pinned
installer content changes:
curl -fsSL "$BASE_INSTALL_URL" | shasum -a 256
It should also replace the generic success text with project-specific next steps, for example:
log "Banyan Labs setup is complete."
log "Next: open the project README and run the first Banyan Labs smoke test."
If a project installer uses Base's standalone installer, it should still avoid
reimplementing Base setup. Use BASE_INSTALL_DIR or install.sh --dir <path> to
choose the Base checkout location, and use --no-profile if the project
installer wants to defer shell startup integration until later.
User Experience Guidelines
Project installers should:
- say what they are about to do before making changes
- keep project-specific language in the project, not in Base
- call
basectl checkorbasectl doctorwhen setup fails - point users to the project documentation for next steps
- avoid hiding the underlying Base command that failed
Project installers should not:
- duplicate Homebrew, Python, venv, or artifact reconciliation logic
- write directly into Base-managed shell startup sections
- make Base itself responsible for product-specific onboarding
- assume every Base-managed project has the same audience
Relationship To basectl onboard
basectl onboard is still useful, but it should target a different layer. Its
command design is captured in basectl-onboard.md.
basectl onboard [project] should be a guided Base setup experience for
technically-adjacent users who want Base to reconcile its generic machine and
project primitives. A project installer should be a guided product setup
experience for users installing a particular project.
In short:
- use
basectl setupfor direct developer setup - use
basectl onboard [project]for guided Base setup and generic project reconciliation - use
<project>/install.shfor guided project setup
Keeping these layers separate lets Base stay small and reusable while each project can speak naturally to its own users.