The first-time install flow. Brings a PMA host from "empty repo" to "all services running, SSO wired, initial backup taken". Phase-numbered, halt-on-failure, idempotent.
The operator entry point is just bootstrap-local <profile> [<target_env>].
Internally that runs scripts/bootstrap/<NN>-*.ts sequentially.
Each phase is its own script. Halt-on-failure between phases. If phase N
fails, you fix the cause + re-run the phase (or re-run from phase 0; phases
are idempotent).
| Phase | Script | What it does |
|---|---|---|
| 00 | 00-preflight.ts |
Check Docker, just, bun, port availability; abort if missing. |
| 01 | 01-clone.ts |
If target_env is set + dir doesn't exist, clone /opt/asd-pma/ to /opt/pma-<target_env>/ and re-exec there. |
| 02 | 02-discover-services.ts |
Read services.yaml + profile; produce list of active services. |
| 03 | 03-configure-env.ts |
Walk each active package's tpl.env; populate .env (expand {{random:N}}, {{service-url}} patterns). |
| 04 | 04-pull-images.ts |
docker pull every image declared in packages/*/manifest.yaml. |
| 05 | 05-net-apply.ts |
just mcp-asd net apply --tunnel --caddy — set up routes + tunnel + Caddy. |
| 06 | 06-start-services.ts |
For each service (in startup_priority order): pre_start_init → docker compose up → poll health → post_start_init. |
| 07 | 07-sso-config.ts |
For each sso.configured: false service: dispatch to scripts/sso/setup-<type>.py. |
| 07b | 07b-populate-api-keys.ts |
Some services need their API keys populated AFTER they're up + SSO is wired (Redmine, n8n). |
| 08 | 08-verify.ts |
Health-check every service. Run E2E tests. Take initial backup. |
A few phases delegate to sub-flows:
| Sub-flow | Owner | What |
|---|---|---|
bootstrap-env |
phase 03 | Pure env-var expansion |
bootstrap-sso |
phase 07 | Dispatch SSO setup per sso.type |
bootstrap-api-keys |
phase 07b | Pull API keys from running services |
bootstrap-verify |
phase 08 | Health + E2E + backup |
Each can be invoked standalone via just <flow> [profile] — useful for
debugging or re-running just one phase.
Bootstrap maintains a skill-state file at
.asd/workspace/skill-state/bootstrap.json:
{
"skill": "bootstrap",
"service": "_global",
"ticket": "—",
"phases": {
"00-preflight": "complete",
"01-clone": "skipped",
"02-discover-services": "complete",
"03-configure-env": "complete",
"04-pull-images": "complete",
"05-net-apply": "complete",
"06-start-services": "in_progress",
"07-sso-config": "pending"
}
}
This lets the framework + operators see "where did the bootstrap stop?" at
a glance.
Managed via bun scripts/helpers/skill-state.ts.
Every phase MUST be safe to re-run. Practical rules:
pre_start_init.ts checks "is the work already done?" before doing it..env.post_start_init.ts is idempotent by definition — apply config, even ifensure-config.ts is the hook designed for repeated invocation. Called onsetup-<sso-type>.py checks if the Authentik app already exists; if so,| Where it failed | First action |
|---|---|
| Phase 00 preflight | Install the missing dependency, re-run from phase 0 |
| Phase 04 pull-images | Check Docker Hub credentials / network; re-run from phase 4 |
| Phase 06 start-services | Look at just logs <svc> for the failing service; fix root cause; re-run phase 6 |
| Phase 07 sso-config | Check Authentik is healthy; just sso-fix <svc>; re-run phase 7 |
| Phase 08 verify | Address what verify flagged; re-run phase 8 (verify) |
Per Golden Rule 8, fix the bootstrap script first if the issue is
reproducible. Add a recovery playbook entry per Golden Rule 11.
Bootstrap is "open for extension, closed for modification" — adding a phase
is preferred over modifying an existing one.
# Create the new phase script
$EDITOR scripts/bootstrap/07c-my-new-step.ts
# Add it to the orchestrator's phase list
$EDITOR scripts/bootstrap/_orchestrator.ts
# Add to phases array in the right ordering
# Document it
$EDITOR docs/framework/BOOTSTRAP-PHASES.md
Then a recovery playbook entry for what to do if the phase fails (Rule 11).
/pma/learn/01-install-pma — bootstrap from the operator's seat./pma/internals/package-system — what bootstrap reads from each package./pma/internals/recovery-playbooks — for bootstrap failure modes./pma/internals/release-orchestrator — releases are "delta bootstrap" (only changed services).