# ADR-006 — Bootstrap Composition Model: Shared Provider + Per-Component Factories **Date**: 2026-06-30 **Status**: Accepted ## Context The vendored `@olsitec/pulumi-docker` `DockerDeployments` class (T02) is **monolithic**: its constructor takes the full provider map + a flat list of all containers and creates them together, with no mechanism for ordering between containers beyond the network. The foundation bootstrap, however, needs **phase gates** (PLAN-002 §2/§5): Vault must be `operator init`'d and unsealed *between* the data-plane and Forgejo consuming its secrets, and Forgejo must be healthy *before* runner registration/handover. Expressing those gates through the monolithic wrapper is awkward (it would create the provider/network multiple times and cannot interleave an imperative init step). ## Decision The bootstrap creates the **shared `docker.Provider` and `foundation-net` once** (`bootstrap/lib/context.ts`, `bootstrap/components/network.ts`), and each service is a **pure factory** in `bootstrap/components/.ts` with signature `(ctx: DeployCtx) => ` that creates its own `docker.Container`(s) against the shared provider/network. `bootstrap/index.ts` is the single composition point; **phase gates are Pulumi `dependsOn` edges**, not imperative sequencing, so `pulumi up` derives the order. The vendored `DockerDeployments` is **retained** as the published `@olsitec/pulumi-docker` API for simple *downstream* (Layer-1) use — deploying a flat list of containers — but the egg does **not** route its phased bootstrap through it. Validation override: `FOUNDATION_DOCKER_HOST=ssh://user@host` points the provider at a dev Docker host without editing committed config; `FOUNDATION_ALLOW_UNPINNED=1` lets `VERSIONS` PIN_DIGEST placeholders fall back to tags for local validation only. ## Consequences **Easier**: - Phase gates (Vault init, Forgejo-before-handover) are natural dependency edges. - One file per service, each independently ownable by a Wave-2+ agent (clean parallelism). - Validated end-to-end: `pulumi up` over Docker-over-SSH created `foundation-net` on crunchy01 (172.30.0.0/24, attachable) and `destroy` removed it cleanly (2026-06-30). **Harder**: - More explicit plumbing than the one-call wrapper (a shared `ctx` threaded through factories). - Two composition styles coexist (egg = direct; downstream = `DockerDeployments`) — documented here to avoid confusion. ## Alternatives Considered - **Route the whole bootstrap through `DockerDeployments`**: rejected — cannot express phase gates or the imperative Vault-init step without recreating the provider/network per call. - **Modify the vendored module to support phases**: rejected — it must stay a faithful Stage-1 vendor (ADR-005) and remain useful as the simple downstream API; forking its behaviour now splits it. ## Confidence **High** — the model is implemented and the Docker-over-SSH path is proven against a real x86_64 host (crunchy01). Companion: CONTRACT_003, PLAN-002 §5.