DPF install operations

Operational reference for running the DPF installer and interpreting what it tells you. The contributor-facing quick start lives in the project README; this page covers the post-install readiness contract and the diagnostic surfaces.

Runtime topology is capability-driven. See Capability-driven runtime profiles for the authority flow and transition protocol; this page covers the installer-facing operating contract.

Quick start

Install Claude Code or Codex CLI, then run install-dpf.ps1 (Windows) or bash install-dpf.sh (macOS / Linux). The installer wires the AI toolchain automatically.

That sentence is the whole contract for non-technical contributors. Everything below is for operators who need to diagnose a degraded state.

Capability-resolved runtime profiles

Every install, restart, autostart, setup, and governed promotion resolves its Compose profiles through scripts/lib/resolve-capability-compose-profiles.mjs. The persisted snapshot uses the canonical fields enabledRuntimeCapabilities, capabilityCatalogHash, and capabilityStateVersion; an unknown capability or a mismatched catalog/state hash fails closed before Compose changes the stack.

When upgrading a previous-release state that predates these fields, the adapter preserves the capabilities that were active in that release: core, build, browser automation, durable automation, local speech, deep observability, and external AI. External AI is retained even on hosts where it adds no local service because provider configuration remains live state. Previously disabled ADP and development capabilities are not enabled by migration. This is the compatibility set, rather than every optional service.

A capability the platform has retired is migrated off rather than refused. The catalog still carries the entry, marked retired, so a governed upgrade drops it from enabledRuntimeCapabilities, restamps the snapshot, and reports it as a dropped capability — an operator sees the withdrawal instead of an install that silently stops upgrading. A capability id the catalog does not carry at all is still an unknown capability and still fails closed, because nothing vouches for it. See Retiring a capability for the two-phase contract this depends on.

A newly initialized state is different from a previous-release state: its explicit empty capability selection migrates to the dependency-required core closure only. Optional runtime profiles remain inactive until enabled through the governed capability transition path.

promote, dev, integration-test, and linux-monitoring remain explicit lifecycle/host overlays. For one compatibility release, tts resolves to local speech and observability-ui resolves to deep observability; both aliases select the same portable service closure as their canonical runtime profile. Promotion copies the install snapshot into its recovery point and restores it on rollback.

State lives at %USERPROFILE%\.dpf\install-state.json on Windows. On POSIX hosts it lives at $XDG_STATE_HOME/dpf/install-state.json when XDG_STATE_HOME is set and at $HOME/.dpf/install-state.json otherwise. The host-aware resolver filters the catalog before it returns services: Linux may activate the runtime-external-ai Ollama service, while macOS and Windows keep configured external AI providers outside Compose. Do not copy a resolved profile string between hosts.

Lifecycle commands must pass through the installer/start helpers or scripts/dpf-compose.mjs. The wrapper binds the install state, project root, ordered Compose file chain, host, and resolved profiles before invoking Docker. It rejects caller attempts to enable a disabled capability profile. Explicit lifecycle overlays remain allowed; COMPOSE_PROFILES is not an authority for capability state. Allowlisted operator overlays such as linux-host-network are preserved and validated; runtime profile names in the environment are accepted only when the persisted capability projection already enables them.

Consumer release assets

Consumer installs materialize the canonical Compose topology and lifecycle adapter from the selected portal image. The installer verifies SHA256SUMS, rejects missing, duplicate, path-escaping, unlisted, or mismatched assets, and records the verified manifest plus release version. A resumed install revalidates those installed bytes and the version marker before it continues. Only then does it atomically bind DPF_IMAGE_TAG and GHCR_OWNER in .env; unrelated operator settings and comments are preserved. If verification or the atomic replacement fails, the previous image identity remains intact.

Use the governed self-upgrade surface for an installed portal. It owns quiescence, recovery-point creation, source/image replacement, capability projection, health evidence, and rollback. Do not use an ad hoc docker compose build or up to refresh the live portal.

After a consumer pull, both installers print the immutable repository digest and the image creation timestamp. For the moving latest tag they compare that timestamp with the current main commit time and warn when the image trails by more than 24 hours. The comparison is advisory because registry/GitHub access may be temporarily unavailable; the digest remains the authoritative identity of the bytes that were installed.

Fresh-install runtime and recovery

The portal always configures its local durable-execution endpoint. Redis and Inngest are therefore core services and start without a Compose profile; the runtime-durable-automation profile only adds optional supporting services. Startup reconciliation discovers an empty provider, then retries uncalibrated seed profiles on later boots until a durable evaluation is recorded. The exact bundled Qwen 3.8 27B model carries a low-confidence provisional routing floor; smaller generic Qwen models do not inherit it.

Windows and POSIX lifecycle commands share the same preservation contract. A normal stop or uninstall names every overlay that may have created resources but does not remove volumes. Permanent deletion is an explicit purge operation. On Windows, uninstall-dpf.ps1 -Headless -Purge is refused unless -Yes is also present.

Before decommissioning a contributor machine, inspect only the repository roots you intend to evaluate:

node scripts/salvage-sweep.mjs --operator-owner OpenDigitalProductFactory --json <repo>...

The report separates LOCAL-ONLY, OPERATOR-REMOTE, and third-party UPSTREAM-CACHE clones. Branch risk is the count from `git rev-list –count

--not --remotes`, supplemented by dirty paths and stashes. An at-risk result exits 2; the command never scans a drive or deletes anything. ### Optional services, backup, and health Disabling a capability does not delete its volumes or data. New governed work is blocked first; queued or running work returns `drain_required` until it is drained or cancelled under its operation policy. A failed service reconcile or required health check restores the prior snapshot and service closure. PostgreSQL remains the scheduled core backup and trial-restore target. Enabled capability services marked `included` are covered by their canonical core data owner; `separate-required` targets need a dedicated runner and report Optional degraded when none is available. Disabled targets are Optional inactive, not a failed schedule. External providers are never local backup targets. Retired Neo4j and Qdrant backup schedules remain disabled. The health pages distinguish Required, Optional inactive, Optional degraded, and External provider-managed states. Only missing required services and enabled-but-unavailable optional services degrade aggregate health. Core-only installs therefore remain healthy when deep observability is disabled. Provider availability comes from bounded reconciliation, not from a fabricated local container check. ## Agent toolchain readiness After the install completes, `install-dpf` prints a single readiness banner. There are eight possible states. The wording shown to the contributor matches this table exactly — drift between the table and the installer copy is a CI lint enforced by `readiness-state.test.ts` in the `@dpf/bootstrap` package. | State | Banner message | Primary action | |---|---|---| | `ready` | Claude Code and Codex are ready for DPF work. | Open readiness | | `partial` | One contributor client is ready; the other needs setup. | Repair toolchain | | `missing_cli` | Install the selected agent client to enable contributor sessions. | Open setup guide | | `missing_token` | DPF MCP needs a development token before agents can use governed tools. | Issue development token | | `needs_refresh` | A token exists, but the running client has not picked it up yet. | Refresh client binding | | `portal-unavailable` | The portal is rebooting; I can still repair local agent tooling and will sync evidence when it comes back. | Continue local repair | | `mcp-unavailable` | DPF coordination is unavailable; I can still repair local agent tooling and will sync evidence when it returns. | Continue local repair | | `failed_smoke` | The agent is installed but did not apply a DPF kernel principle. | View evidence | ### Why each state appears - **`ready`** — both Claude Code and Codex CLI are installed, the DPF MCP server returned a non-empty `tools/list`, and a destructive-action prompt was refused by the agent. The contributor can start work immediately. - **`partial`** — exactly one of Claude Code or Codex CLI was wired; the other was not detected on PATH. The contributor can still work in the available client; the missing one is a follow-up. - **`missing_cli`** — neither Claude Code nor Codex CLI was detected. The contributor needs to install one before any agent work is possible. The installer does NOT print a command for the contributor to type — install the CLI from its official documentation, then re-run the installer. - **`missing_token`** — the DPF MCP server requires a bearer token before governed tools (backlog, build studio, deliberations) become available. Issue one from **Admin > Platform Development > MCP** in the portal. - **`needs_refresh`** — a token exists in the contributor's environment, but the running client hasn't picked it up yet. Restart Claude Code / Codex in this worktree. If the issue persists after restart, the endpoint is unreachable or returning an unexpected shape — collect a `dpf-doctor` bundle. - **`portal-unavailable`** — a token exists, but the portal endpoint cannot be reached or is clearly rebooting/quiescing. The bootstrap still performs local-only repairs such as plugin convergence, MCP client config writes, and memory seeding, then records a local state that can be reconciled after the portal returns. - **`mcp-unavailable`** — the portal is reachable enough to answer, but the MCP route is missing, unavailable, or returning a server-side failure that is not a portal reboot signal. The bootstrap still performs the same local repairs and keeps the contributor unblocked for source-local work. - **`failed_smoke`** — the installed CLI responded to the kernel smoke prompt but did not include any of the expected refusal signatures. This is usually a CLI version that hasn't loaded the kernel memory yet. See the smoke-test transcript under `~/.dpf/install-state.json` → `agentToolchain.smokeTest.transcript`. ### Idempotence guarantee Re-running `install-dpf` is a true no-op when nothing has drifted. The installer should report *'Claude Code plugin already converged'*, *'Codex plugin already converged'*, *'Kernel-tier memory already converged'*, and produce **zero file writes**. If you see writes on a re-run without any version bump, that's a defect — capture a `dpf-doctor` bundle and file a backlog item. ### Agent-toolchain-only update The DPF platform skills and MCP client wiring can be updated without installing or running the full DPF project. Use this path when a contributor only needs the Codex / Claude agent substrate, or when a source-only checkout lacks the Node dependencies needed by the full bootstrap planner. The standalone updater is shipped inside the skill pack and requires only Python 3 plus the skill-pack files: - Windows: `packages/dpf-skill-pack/scripts/update-agent-toolchain.ps1` - macOS / Linux: `packages/dpf-skill-pack/scripts/update-agent-toolchain.sh` It copies the current skill pack to the managed personal plugin location, updates Codex's personal marketplace and `~/.codex/config.toml`, writes a Claude local marketplace, and installs the Claude plugin when the Claude CLI is available. It does not require Docker, pnpm, Node dependencies, database access, or a running portal. It does not mint `DPF_MCP_BEARER_TOKEN`. Codex-only update: ```powershell .\packages\dpf-skill-pack\scripts\update-agent-toolchain.ps1 -CodexOnly ``` ```bash bash packages/dpf-skill-pack/scripts/update-agent-toolchain.sh --codex-only ``` Claude-only update: ```powershell .\packages\dpf-skill-pack\scripts\update-agent-toolchain.ps1 -ClaudeOnly ``` ```bash bash packages/dpf-skill-pack/scripts/update-agent-toolchain.sh --claude-only ``` Use `DPF_MCP_URL` to point at a non-local MCP endpoint. Restart Codex or Claude Code after the updater finishes because both clients load plugins and skills at session start. ### Where state lives `~/.dpf/install-state.json` carries the `agentToolchain` block after every install run. Schema reference: `scripts/installer/install-state.schema.json`. The block is: ```jsonc "agentToolchain": { "appliedAt": "", "dpfPlatformVersion": "0.1.0", "superpowersVersion": null, // pin advisory only; null if not pinned by contributor "claudeCodeWired": true, "codexWired": true, "memorySeededAt": "", "mcpReadiness": { "ok": true, "toolCount": 160, "observedAt": "" }, "smokeTest": { "result": "passed", "kernelPrincipleObserved": "destructive-actions-require-explicit-go", "transcript": "" }, "readinessState": "ready" } ``` Bearer tokens never appear in this file. The `transcript` field is routed through `redactTranscriptForPersistence` before write; the `mcpReadiness` and `smokeTest` shapes are bearer-free by contract. If you find a bearer-shaped substring in this file on any install, it is a security regression — see `BI-4B17051B` for the contract. ## Installation operating intent and environment class Per EP-1FABA22D (BI-A9F60372), `install-state.json` (v2 schema) captures the canonical local host environment class and pre-DB bootstrap intent envelope: - **`environmentClass`** — `"production"`, `"development"`, `"test"`, or `null`. Installer state is the canonical source of truth for the local host's environment fact; `FederationLink.environmentClass` is canonical for peer link facts. - **`bootstrapIntent`** — Pre-DB envelope recorded by the installer before runtime database availability. On portal runtime boot, `absorbBootstrapIntent` idempotently ingests this envelope into `PlatformConfig` under key `installation.operating-intent.v1` with `status: "suggested"` and marks `absorbedAt`. - **Purpose Confirmation Invariant** — Expressing operating purpose (`operate-organization`, `evolve-dpf`, `deliver-managed-services`, `grow-channel`, `participate-community`) configures platform productivity and compiles work; it **never grants identity, trust, authority, qualification, or permission**. ### Naming the installation, and overriding what it is Two facts describe an installation: the **environment class** (production / development / test) and the **estate name** — the company or team that OPERATES it, which is not the business it runs for. An IT services company running installations for twenty customers is one estate and twenty organizations. Both resolve through the same precedence chain, highest first: ``` process override -> installer state -> portal declaration -> default / unset ``` The process overrides are `DPF_ENVIRONMENT_CLASS` and `DPF_ESTATE_NAME`. Set either in the install's `.env`; both are declared on the portal service with an empty default, so an unset variable leaves the tier silent and the next authority answers (BI-10BF6206 — before that they were absent from the portal's environment allow-list, which meant the documented top tier could never be set on a consumer install). The estate name is a label, not an authorization input: it changes what the header badge and connected AI coworkers CALL this installation, never what they may do on it. A non-production installation shows a badge beside the logo (`ACME DEV`); production shows none, so a badge always means "this is not production". ## Diagnostics ### `--show-substrate` flag `scripts/dpf-bootstrap-agent-toolchain.{ps1,sh}` accepts a `--show-substrate` (`-ShowSubstrate` on Windows) flag that prints plugin paths, memory directory, and state file location under the banner. This is for operator debugging only; the normal install banner is substrate-free by design. ### `dpf-doctor` `bash install-dpf.sh doctor` (POSIX) or `install-dpf.ps1 doctor` (Windows; if added in a future phase) emits a tar bundle at `~/.dpf/doctor-.tar.gz` containing install-state, recent compose output, and the `agentToolchain` block. Attach this bundle when filing install-failure reports. ### `--reconcile-installed-plugins` flag `scripts/dpf-bootstrap-agent-toolchain.{ps1,sh}` warns by default when stale entries are detected in `~/.claude/plugins/installed_plugins.json` (e.g. entries for deleted worktrees). To actually clean them, re-run with `--reconcile-installed-plugins`. This is opt-in because a contributor may have other worktrees the bootstrap doesn't know about. ## What `install-dpf` does NOT do For the avoidance of doubt, the installer: - Does **not** edit your `~/.codex/config.toml` outside the `[plugins."dpf-platform@personal"]` block. The updater migrates the retired bare `[plugins."dpf-platform"]` key because current Codex requires `@`. Other blocks (user MCP servers, marketplaces, feature flags, project trust levels) are preserved byte-for-byte. User intent (`enabled = false` set manually) is preserved on re-runs. - Does **not** write your DPF MCP bearer token into any tracked file. The token is read from `DPF_MCP_BEARER_TOKEN` and never persisted to the agent toolchain state. - Does **not** auto-upgrade upstream-owned plugins (`superpowers@openai-curated`). If your installed version differs from the DPF pin, the banner shows an advisory line — no action is taken. - Does **not** prompt the contributor to run scripts or copy commands. Missing CLIs / tokens / drifted state become explicit readiness states with one primary action; never a command-copy. ## See also - Spec: [`docs/superpowers/specs/2026-08-08-purpose-aware-installation-ecosystem-productivity-design.md`](../superpowers/specs/2026-08-08-purpose-aware-installation-ecosystem-productivity-design.md) - Plan: [`docs/superpowers/plans/2026-08-08-purpose-aware-installation-ecosystem-productivity.md`](../superpowers/plans/2026-08-08-purpose-aware-installation-ecosystem-productivity.md) - Spec: [`docs/superpowers/specs/2026-05-26-agent-toolchain-bootstrap-design.md`](../superpowers/specs/2026-05-26-agent-toolchain-bootstrap-design.md) - Plan: [`docs/superpowers/plans/2026-05-26-agent-toolchain-bootstrap.md`](../superpowers/plans/2026-05-26-agent-toolchain-bootstrap.md) - Skill pack: [`packages/dpf-skill-pack/README.md`](../../packages/dpf-skill-pack/README.md) - Planning library: [`packages/dpf-bootstrap/README.md`](../../packages/dpf-bootstrap/README.md) - State schema: [`scripts/installer/install-state.schema.json`](../../scripts/installer/install-state.schema.json) - Backlog: BI-A9F60372 (EP-1FABA22D), BI-4B17051B (EP-INSTALL-HARDENING-2026-05-23) ## Mapping a network from another machine Installing DPF does not start an edge node — a small host-resident agent that maps a network and reports back. Mapping a network is a deliberate choice, made at setup or later under **Platform → Edge Nodes**. You want one when the network you care about is not the one your portal sits on: a branch office, a customer site, a shop floor. Businesses with several locations run one per location, and IT service companies run one per customer per site, so each estate stays separate. Start with the [edge node deployment topology guide](/edge-node/deployment-topology/), which covers where a node can run and how it connects. For many nodes at once, see [fleet operations](/edge-node/fleet-operations/).