Skip to content

Bind container deletion and exit callbacks to instances - #2201

Open
sturdy4days wants to merge 4 commits into
apple:mainfrom
sturdy4days:codex/force-removal-instance-token-20260827
Open

Bind container deletion and exit callbacks to instances#2201
sturdy4days wants to merge 4 commits into
apple:mainfrom
sturdy4days:codex/force-removal-instance-token-20260827

Conversation

@sturdy4days

@sturdy4days sturdy4days commented Aug 27, 2026

Copy link
Copy Markdown

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation update

Motivation and Context

Addresses #2116.

Container IDs can be explicitly reused. A delayed delete or exit callback that only carries an ID can therefore act on a replacement container after the original instance has gone away. Force deletion makes the ownership boundary especially important because stopping and cleanup span asynchronous work.

This change:

  • generates and persists a server-owned token for every container instance, overwriting any caller-supplied token;
  • returns the authoritative container ID and instance token from the same create operation through a result-capable API route;
  • adds container create --format json for orchestrators that need to retain that exact ownership token without a later inspect;
  • adds container delete --if-instance-token TOKEN ID and validates the token while holding the container lifecycle lock;
  • advertises result-capable create and conditional-delete support through the health check, with no unsafe fallback;
  • reserves an ID across forced stop/cleanup, revalidates the selected instance before final cleanup, and rejects bootstrap/start once force deletion has reserved the instance; and
  • binds exit-monitor tasks and callbacks to registration generations so cancelled work from an old instance cannot invoke a replacement callback.

Backward compatibility is preserved. The legacy create route and ordinary create output remain supported. The public Swift ContainerClient.create(...) API retains its exact legacy async throws -> Void signature and discards optional reply data internally; createWithResult(...) is the only result-returning create API. An older client can ignore the optional result returned by a newer server. Result-capable create checks the server capability and uses a distinct route, so a newer client fails before creation when connected to an older server instead of creating a container without receiving its identity. Legacy unconditional delete remains supported, while a conditional delete against an older tokenless container is rejected without deleting it.

Credit and overlap: @nassersala documented the reusable-ID race and an earlier local token-precondition proof of concept in this #2116 comment. This PR does not claim origin of that model. The comment describes an unpublished 9-file local patch; no public branch or PR is linked. This implementation covers the same ownership concern while also handling force-delete ID reservation/revalidation, mixed-version capability negotiation, atomic machine-readable create results, start/bootstrap reservation checks, and stale exit-callback generation ownership.

This is a source proposal only. It is not merged, released, or active in a shipped runtime.

Testing

  • Tested locally
  • Added/updated tests
  • Added/updated docs

Current local validation at 5a1500c39ae60f9231d809c752bb6d53dd9cb1c3:

  • make swift-fmt-check
  • focused client tests: 4 passed, including compile-time exact Void and result-returning function references
  • focused CLI command tests: 2 passed
  • focused container service tests: 10 passed
  • make test — 788 non-integration tests in 88 suites passed
  • selected integration run — 9 tests in 2 suites passed in 19.887 seconds:
    • TestCLICreateCommand: 6/6 passed, including JSON create identity matching the persisted server token
    • TestCLIRmRaceCondition: 3/3 passed, including deterministic same-ID replacement and stale-token rejection
  • git diff --check, signed-history verification, and git fsck --full --no-dangling

The final compatibility-only commit changes the legacy client method's static return type and discards the same optional reply internally; it does not alter the wire request, route, server, JSON-create path, or runtime behavior. The prior selected integration evidence therefore remains applicable and was not rerun.

The integration warmup completed and the selected suites passed. The Makefile invocation then interpreted an empty serial-suite filter as an unfiltered broader phase; that phase was detected and interrupted, and is not claimed as validation. The remaining isolated buildkit container and Apple Container services were stopped, the disposable app root/kernel/artifacts were removed, the host returned to unregistered, and the default-store kernel hash remained unchanged.

Return the server-generated ID and instance token from container creation, expose safe JSON CLI output with mixed-version fail-closed negotiation, and reject start/bootstrap after force-deletion reservation. Add deterministic ownership, compatibility, integration, and command-reference coverage.
Keep ContainerClient.create source-compatible as async throws -> Void while discarding the optional server reply internally. Expose create results only through createWithResult and compile-check both exact function types.
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.

1 participant