Skip to content

fix(migration): fail-closed legacy runtime project identity migration for imported stacks - #146

Merged
mstrhakr merged 5 commits into
devfrom
mstrhakr/issue140
Sep 1, 2026
Merged

fix(migration): fail-closed legacy runtime project identity migration for imported stacks#146
mstrhakr merged 5 commits into
devfrom
mstrhakr/issue140

Conversation

@mstrhakr

@mstrhakr mstrhakr commented Sep 1, 2026

Copy link
Copy Markdown
Owner

Summary

Imported stacks from original Compose Manager can have runtime project identity different from folder-derived identity.
This change makes migration verified and fail-closed, not best-effort.

Problem

Plus uses folder-derived identity for compose -p.
Legacy plugin used name-file identity for compose -p.
Mismatch risk:

  • existing containers not matched in UI
  • actions scoped to wrong project
  • parallel project identities
  • unexpected resource naming and storage behavior

What Changed

  1. Added centralized ProjectIdentity resolver.
  2. Resolver computes two candidates:
  • folder candidate
  • legacy name-file candidate
  1. Resolver probes Docker ownership via com.docker.compose.project on:
  • containers
  • volumes
  • networks
  1. Resolver decision:
  • exactly one candidate owns resources: preserve that candidate
  • neither owns resources: use canonical folder candidate
  • both own resources: unresolved, fail closed
  • probe unavailable: unresolved, fail closed
  1. Persisted effective identity in project_name metadata.
  2. StackInfo now uses persisted effective identity for compose -p.
  3. Added explicit owner-choice flow to resolve ambiguous stacks.
  4. Added read-only identity preview endpoint for migration visibility.

Fail-Closed Enforcement

Mutating paths now block when identity unresolved:

  • single-stack actions
  • multi-stack actions
  • compose args builder path used by event flows
  • manual auto-update
  • scheduled auto-update

Read-only logs path remains allowed.

UI

  • stack list warning indicator for unresolved identity
  • action attempt opens identity chooser
  • owner can pin identity explicitly, then actions resume

Tests

Added regression coverage for:

  • legacy name different from folder
  • running ownership
  • stopped ownership
  • volume-only ownership
  • both candidates present
  • neither candidate present
  • Docker probe failure
  • pinned identity reuse
  • explicit owner choice
  • mutation blocked while logs allowed
  • new stack behavior remains canonical

Bootstrap now sets deterministic probe default so tests never depend on host Docker state.

Outcome

Upgrade path safer for legacy users:

  • preserve proven runtime identity
  • no silent project rename
  • no blind compose mutation when ownership unclear
  • explicit owner decision required on ambiguity

Add ProjectIdentity resolver that compares folder-derived and legacy name-derived candidates against live compose project label ownership across containers, volumes, and networks.

Persist resolved runtime identity to project_name metadata and wire StackInfo to always use that centralized pinned identity for docker compose -p.

Fail closed on ambiguous or unprobeable identity and add explicit owner-choice support via StackInfo.
Enforce resolved project identity across compose action builders, single and multi-stack command launchers, manual and scheduled auto-update paths, and event/script consumers via compose_args.

Add read-only identity preview endpoint plus explicit identity selection endpoint for owner-driven conflict resolution.

Surface blocked stacks in list/UI with warning indicator and chooser modal when actions hit ambiguous identity.
…osed behavior

Add ProjectIdentity tests covering legacy name mismatch, running/stopped ownership, volume-only ownership, ambiguous dual ownership, no ownership, Docker probe failure, pinned reuse, owner choice, and logs-vs-mutation guard behavior.

Set deterministic default probe in test bootstrap so unit tests never depend on host Docker state.
Copilot AI lite review requested due to automatic review settings September 1, 2026 18:00

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The current fail-closed enforcement blocks composeLogs (contradicting the “logs allowed” contract) and the identity error response shape is inconsistent across single vs multi-stack callers, causing confusing/incorrect frontend behavior.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR implements a fail-closed migration for stacks imported from the legacy Compose plugin where the runtime docker compose -p identity may differ from the folder-derived identity. It introduces a resolver that uses Docker resource ownership evidence to pick (and persist) the effective project identity, blocks mutating actions when identity is ambiguous/unverifiable, and adds UI flows and tests to support explicit owner selection.

Changes:

  • Added ProjectIdentity resolver with Docker-label probing, pinned project_name metadata, and explicit owner-choice support.
  • Updated backend action paths (single stack, multi stack, auto-update) to fail closed when identity is unresolved.
  • Added UI warning/chooser flow and regression tests covering ownership, ambiguity, probe failure, and pinning behavior.
File summaries
File Description
tests/unit/ProjectIdentityTest.php Adds regression coverage for identity resolution, pinning, ambiguity, and fail-closed behavior.
tests/bootstrap.php Sets a deterministic default probe for tests to avoid host Docker dependencies.
source/compose.manager/javascript/composeManagerMain.js Adds identity chooser UI and handles identity-block error responses from backend actions.
source/compose.manager/include/Util.php Integrates identity resolution into StackInfo construction and pins identity for new stacks.
source/compose.manager/include/ProjectIdentity.php Introduces centralized identity resolution, Docker probing, pinning, and messaging.
source/compose.manager/include/Helpers.php Enforces fail-closed behavior for compose command generation (single + multi-stack).
source/compose.manager/include/Exec.php Adds identity preview and identity pinning endpoints.
source/compose.manager/include/ComposeList.php Adds identity-block warning indicator and data attributes for UI chooser.
source/compose.manager/include/ComposeCommandBuilder.php Blocks mutating compose arg generation when identity is unresolved (logs allowed).
source/compose.manager/include/AutoUpdateRunner.php Skips scheduled auto-updates when identity is unresolved.
source/compose.manager/include/AutoUpdate.php Blocks manual auto-updates when identity is unresolved (returns 409 + message).
Review details

Suppressed comments (1)

source/compose.manager/include/Helpers.php:388

  • echoComposeCommandMultiple() returns {error: 'identity', stacks: [...]} when all stacks are blocked. Several multi-stack callers (e.g. Start All / Stop All) only treat responses as either {background:true} or a ttyd URL string, so this JSON payload will be shown as raw text (and action-in-progress spinners may never be cleared). Consider either (a) returning a ttyd script/URL that prints the blocked-stack message, or (b) using a distinct error shape/code and updating the corresponding JS callers to handle it and clear UI state.
        if (!empty($blockedStacks)) {
            echo json_encode([
                'error' => 'identity',
                'stacks' => $blockedStacks,
                'message' => 'Compose project identity is unresolved for: ' . implode(', ', $blockedStacks),
  • Files reviewed: 11/11 changed files
  • Comments generated: 2
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread source/compose.manager/include/Helpers.php Outdated
Comment thread source/compose.manager/javascript/composeManagerMain.js
Scope the fail-closed identity guard in echoComposeCommand() to mutating actions only. composeLogs stays read-only and continues opening ttyd even when identity is unresolved.
Handle identity error payloads that do not include per-stack candidates (e.g. multi-stack operations) with a simple warning dialog instead of opening the chooser with no options.
@mstrhakr
mstrhakr merged commit d0bc116 into dev Sep 1, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants