Installer Architecture¶
The installer is layered: a thin bootstrap (legacy install.sh for curl | bash) hands off to a CLI dispatcher (scripts/bin/install), which loads focused library modules from scripts/lib/install/, which read declarative YAML profiles from templates/profiles/, which can attach pluggable deploy modules from scripts/lib/install/deploy/.
flowchart TD
subgraph Entry["Entry points"]
A1["curl | bash<br/>install.sh"]
A2["./scripts/bin/install"]
end
subgraph CLI["Dispatcher (scripts/bin/install)"]
C1["init"]
C2["wizard"]
C3["agents"]
C4["deploy"]
C5["doctor"]
C6["diagnose"]
C7["upgrade"]
end
subgraph Libs["Libraries (scripts/lib/install/)"]
L1["core / fs / template / config"]
L2["platform / pages"]
L3["profile.sh ← YAML reader"]
L4["wizard_interactive.sh"]
L5["doctor.sh"]
L6["upgrade.sh"]
L7["agents.sh"]
L8["ai/{wizard,diagnose,suggest}.sh"]
end
subgraph Data["Declarative data"]
D1["templates/profiles/*.yml"]
D2["templates/deploy/<target>/"]
D3["templates/agents/"]
D4["templates/ai/prompts/*"]
D5["templates/config/install.conf"]
end
subgraph Deploy["Deploy modules (scripts/lib/install/deploy/)"]
DP1["github-pages.sh"]
DP2["azure-swa.sh"]
DP3["docker-prod.sh"]
DP4["registry.sh"]
end
A1 --> A2
A2 --> CLI
CLI --> Libs
Libs --> Data
C4 --> DP4 --> DP1 & DP2 & DP3
DP1 & DP2 & DP3 --> D2
style Entry fill:#e3f2fd
style CLI fill:#fff3e0
style Libs fill:#f3e5f5
style Data fill:#e8f5e9
style Deploy fill:#fce4ec
Layer responsibilities¶
| Layer | Responsibility | Hard rules |
|---|---|---|
Bootstrap (install.sh) |
Detect platform, ensure prereqs, hand off to CLI. Self-contained for curl \| bash. |
No business logic. No template knowledge. |
CLI (scripts/bin/install) |
Parse subcommand, source the right modules, validate args, dispatch. | Never hardcode template content. |
Libraries (scripts/lib/install/*.sh) |
One concern per file. Each file ≤ 300 lines. Bash 3.2 compatible. | Don't set -euo pipefail (caller does). Don't exit. Return non-zero on recoverable failure. |
Profiles (templates/profiles/*.yml) |
Declarative. Describe what to install, not how. | No code. No conditionals. |
Deploy modules (scripts/lib/install/deploy/*.sh) |
Uniform contract: *_check_prereqs / *_install / *_verify / *_doc_url. |
Each renders only from templates/deploy/<target>/. |
AI modules (scripts/lib/install/ai/*.sh) |
Opt-in only. Sanitize before send. Diff before write. | Gated behind --ai/--ai-suggest. Honor ZER0_NO_AI=1. |
Compatibility & safety contracts¶
- Bash 3.2 — the macOS default. No
declare -A, no=~capture groups, nomapfile/readarray. - Idempotency — every file write goes through
fs.sh::copy_file_with_backup(timestamped backup) ortemplate.sh::create_from_template(skip-if-present unless--force). - No exit from libraries — modules return non-zero; the caller decides whether to abort.
- Logging — modules use
log_info / log_success / log_warning / log_errorfromscripts/lib/common.sh(or the inlined fallback ininstall.sh). - Templates are the single source of truth — when you need to change generated content, edit the template under
templates/, never inline a heredoc.
Where things live¶
scripts/
├── bin/install # CLI dispatcher
├── lib/
│ ├── common.sh # logging, dry_run_exec, confirm
│ └── install/
│ ├── core.sh fs.sh template.sh config.sh platform.sh pages.sh
│ ├── profile.sh # pure-bash YAML reader
│ ├── wizard_interactive.sh
│ ├── doctor.sh upgrade.sh agents.sh
│ ├── ai/{wizard,diagnose,suggest,openai}.sh
│ └── deploy/
│ ├── registry.sh
│ └── {github-pages,azure-swa,docker-prod}.sh
└── platform/setup-{macos,linux,wsl}.sh # platform check primitives
templates/
├── profiles/{full,minimal,fork,remote,github,blog,docs}.yml
├── deploy/{github-pages,azure-swa,docker-prod}/
├── agents/{CLAUDE.md,aider.conf.yml}.template
├── ai/prompts/{wizard,diagnose,suggest}-system.md
├── config/install.conf
└── pages/{INSTALLATION.md,admin/*}.template
Lifecycle for one install init --profile full --deploy github-pages /path/to/site¶
scripts/bin/installsourcescore.sh+profile.sh+deploy/registry.sh.profile.sh::profile_path fullresolves totemplates/profiles/full.yml.- Doctor preflight runs (skip with
--skip-doctor). - CLI translates
--profile fullto the profile'slegacy_flag(--full) and invokesinstall.sh. install.shrenders starter pages viapages.sh::render_starter_pages(driven by the profile'sincludes:list).deploy/registry.sh::deploy_run_target github-pagescallsdeploy_github-pages_installwhich renderstemplates/deploy/github-pages/jekyll-gh-pages.yml.template.agents.sh::agents_installcopies the agent files declared in the profile'sai_features.agent_files:.- CLI writes
.zer0-installed(consumed later byupgrade).
Last updated: 2026-04-20 — Phase 7.