# Auto Installer > Module 03 · Plane: Control · Roadmap phase: 2 > Part of [MCP Nexus architecture](../ARCHITECTURE.md). ## Purpose The Auto Installer is the actor that turns a matched triple — `(DiscoveredResource + Package + Recipe)` — into a running, registered `MCPInstance` (Architecture §7) with **no manual work**. It is the `act` step of the reconciliation loop (Architecture §6) for new installs: given a plan from the reconciler, it pulls the pinned image, creates a **sandboxed** container through the [Runtime](16-infrastructure-discovery.md) abstraction, renders config, injects secrets at runtime, starts, health-gates, and registers the instance so the [Router/Gateway](04-gateway.md) begins exposing its tools. Per tenet #2 it is *reconcile, not script*: the Installer is invoked by the reconciler with a desired spec and must be **idempotent** — re-running with the same inputs yields no duplicate containers and no duplicate instances. ## Responsibilities - Accept an install `Plan` (bound resource + resolved, digest-pinned package version + recipe) and drive it to a healthy, registered `MCPInstance`. - **Registry lookup / resolution:** obtain the concrete `PackageVersion` (digest) from the [Registry](02-package-registry.md) (the reconciler usually pre-resolves; the Installer re-validates the pin). - **Pull** the image by digest through the target Runtime. - **Create** a sandboxed container: least privilege, read-only rootfs where possible, no host network unless the recipe demands it (Architecture §9). - **Render** the recipe's config template against the package `config_schema`, injecting [Secrets](07-secrets-manager.md) at runtime (env/file mount) — never baking secret values into the image, the stored spec, or logs. - **Start**, then **health-gate**: block registration until the instance passes an initial readiness check (handed off to [Health](09-health-monitoring.md)). - **Register** the `MCPInstance` in the **Inventory** store and emit an event so the Router refreshes its upstream set. - Be **idempotent** and support **rollback** on any failed step. - Target multiple **Runtimes** (Docker first; K8s/containerd later) discovered by [Infrastructure Discovery](16-infrastructure-discovery.md). ## Non-goals - Deciding *what* to install or *whether* to (matching + policy) — that is the reconciler + [Recipes](15-smart-recipes.md). - Choosing the version or verifying signatures — that is the [Registry](02-package-registry.md) (the Installer trusts the digest-pinned, verified version handed to it, and re-checks the pin). - Ongoing liveness/restart — owned by [Health Monitoring](09-health-monitoring.md). - Applying version upgrades — a re-install driven by the [Update Manager](10-update-manager.md) reuses this module. ## Interfaces ```go type Plan struct { Resource DiscoveredResource // bound_resource_uuid target Version PackageVersion // digest-pinned (Registry) Recipe Recipe // config template + runtime constraints } type Installer interface { // Idempotent: same Plan -> same MCPInstance, no duplicate container. Install(ctx context.Context, p Plan) (MCPInstance, error) Rollback(ctx context.Context, instanceID string) error } // Runtime abstraction (tenet #3). Docker first; K8s/containerd adapters later, // selected per-target from Infrastructure Discovery (Module 16). type Runtime interface { Name() string // "docker", "k8s", ... Pull(ctx context.Context, ref, digest string) error Create(ctx context.Context, spec ContainerSpec) (ContainerRef, error) Start(ctx context.Context, ref ContainerRef) error Stop(ctx context.Context, ref ContainerRef) error Remove(ctx context.Context, ref ContainerRef) error Inspect(ctx context.Context, ref ContainerRef) (ContainerStatus, error) } // Sandbox defaults come from Security (Module 19); recipes may widen with reason. type ContainerSpec struct { Ref string Digest string Labels map[string]string // includes nexus.instance-id for idempotency Env []EnvVar // secret refs resolved at start Mounts []Mount // incl. secret file mounts (tmpfs) ReadOnlyRoot bool NoNewPrivs bool Network NetworkMode } ``` Idempotency key: the Installer derives a stable `instance-id` from `(package, version-digest, bound_resource_uuid, recipe-hash)` and stamps it as a container **label** and the `MCPInstance.id`. Before creating anything it queries the Runtime for a container carrying that label and adopts it instead of re-creating. No public HTTP surface of its own; it is invoked in-process by the reconciler. Install progress is observable via Inventory state transitions and [Metrics](18-metrics.md). ## Data - **Writes:** `MCPInstance` `{id, package, version, config, container_ref, state, health, bound_resource_uuid}` into the **Inventory** store (Architecture §8), transitioning `state` through `Pulling → Creating → Starting → HealthGating → Running` (or `Failed`). - **Reads:** `Package`/`PackageVersion` and config template from the [Registry](02-package-registry.md); `Recipe` for constraints; `Secret` references resolved via [Secrets](07-secrets-manager.md) at start time. - **Module-local persistence:** an install-attempt journal (step, timestamp, outcome) used for rollback and audit; the previous known-good spec is retained to support Update Manager rollback. - The stored `MCPInstance.config` is the **rendered template with secret values redacted to references** — secret material is never persisted here. ## Dependencies - [MCP Package Registry](02-package-registry.md) — resolved, pinned version + template. - [Smart Recipes](15-smart-recipes.md) — config template + runtime constraints. - [Secrets Manager](07-secrets-manager.md) — runtime secret injection. - [Infrastructure Discovery](16-infrastructure-discovery.md) — discovers Runtimes/targets. - [Health Monitoring](09-health-monitoring.md) — the health gate + post-install liveness. - [Gateway](04-gateway.md) / [Dynamic Tool Registry](05-dynamic-tool-registry.md) — consume the registered instance's tools. - [Security](19-security.md) — sandbox defaults. - [Update Manager](10-update-manager.md) — re-invokes Install for version upgrades. ## Failure modes & handling - **Pull fails** (network, missing digest): mark `Failed`, no container created, reconciler retries with backoff; nothing to roll back. - **Create/start fails:** run `Rollback` — stop+remove any partial container, release reserved secret mounts, revert Inventory to pre-install state. - **Health gate times out:** treat as failed install; roll back the new container so the Gateway never sees a broken instance. On an *update* re-install, restore the previous known-good spec (see [Update Manager](10-update-manager.md)). - **Duplicate on re-run (idempotency):** the label lookup adopts the existing container; no second container, no second `MCPInstance`. - **Crash mid-install:** the install-attempt journal + label lookup let the next reconcile cycle resume or clean up orphaned containers (garbage-collect containers labelled with an `instance-id` absent from Inventory). - **Runtime unavailable** (e.g. Docker socket down): install is deferred, not errored permanently; data plane is unaffected (Architecture §5). ## Security notes - Containers are **sandboxed by default** (Architecture §9): least privilege, `NoNewPrivs`, read-only rootfs where possible, no host network unless the recipe explicitly requires it with a recorded reason. - **Secrets injected at runtime only** — env vars or tmpfs file mounts populated at `Start`; never in the image, the stored `MCPInstance.config`, or logs. - Only **digest-pinned, signature-verified** versions are installed; the pin is re-validated against the Registry before pull. - Every install/rollback is an audited mutating control-plane action (Architecture §9) with principal (reconciler/user), target, and outcome. ## Open questions - How much Docker installer semantics map cleanly to a K8s operator vs. a separate controller (Architecture §12 "K8s runtime parity")? - Concurrency/locking model when two reconcile cycles race on the same `instance-id` — advisory lock in Inventory vs. Runtime-level create idempotency. - Orphan GC cadence and safety margin before removing an unrecognized Nexus-labelled container. - Whether the health gate criteria live in the Recipe or default from the Package `config_schema`. ## Milestone Phase 2 — *Discover → install*. Installer v1 + Docker Runtime satisfies the phase exit criteria: a Postgres container appears on the LAN and, within one reconcile cycle, `postgres.query` is live at the [Gateway](04-gateway.md) with no human action — idempotently, with rollback on failure.