Skip to content

feat(proxmox): support replay-safe fixed lease IDs - #1861

Open
devinkuhn wants to merge 2 commits into
openclaw:mainfrom
devinkuhn:feat/proxmox-fixed-lease-id
Open

feat(proxmox): support replay-safe fixed lease IDs#1861
devinkuhn wants to merge 2 commits into
openclaw:mainfrom
devinkuhn:feat/proxmox-fixed-lease-id

Conversation

@devinkuhn

Copy link
Copy Markdown

Summary

  • add replay-safe fixed --lease-id support to the direct Proxmox provider
  • persist create intent and selected VMID before clone submission
  • reconcile exact VMID, provider scope, labels, intent fingerprint, and vmgenid on replay
  • reject drift and ambiguous identity with lease_id_conflict
  • retain terminal tombstones after confirmed release/absence
  • preserve ordinary non-fixed Proxmox behavior

Closes #1847

Verification

  • go test ./internal/providers/proxmox -run "Fixed|RequestedLease|Proxmox" -count=1
  • go test ./internal/cli -run "Proxmox|FixedProxmox" -count=1
  • gofmt on changed Go files
  • git diff --check

Notes

No configuration or secret changes. The implementation uses the existing provider-neutral fixed-acquire framework and keeps Proxmox-specific reconciliation behind the provider adapter.

Implements replay-safe fixed Proxmox lease acquisition with durable VMID binding, exact identity validation, conflict detection, and terminal tombstones.\n\nCloses openclaw#1847
@clawsweeper

clawsweeper Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

🦞👀
ClawSweeper picked this up.

Pull request received. I will update this pull request when review starts.

ClawSweeper review complete

ClawSweeper finished reviewing this revision. The review result is being finalized.

View the workflow run.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 5, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-05T02:07:20.803500Z 25340a9 New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f7a1f42148

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

return &leaseBackend{DirectSSHBackend: shared.DirectSSHBackend{SpecValue: spec, Cfg: cfg, RT: rt, StoredLeaseKeys: true}}
}

func (b *leaseBackend) SupportsRequestedLeaseID() bool { return true }

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Add Proxmox fixed-ID support to the changelog

This advertises a new user-visible warmup --lease-id capability for Proxmox, but the commit leaves CHANGELOG.md's Unreleased section empty. Record the feature there so it is included in the next release notes, as required for agent-authored user-visible features.

AGENTS.md reference: AGENTS.md:L41-L41

Useful? React with 👍 / 👎.

Comment on lines +113 to +117
User: strings.TrimSpace(cfg.SSHUser), WorkRoot: strings.TrimSpace(cfg.WorkRoot),
FullClone: cfg.Proxmox.FullClone, ServerType: strings.TrimSpace(cfg.ServerType),
TargetOS: strings.TrimSpace(cfg.TargetOS), RequestedSlug: core.NormalizeLeaseSlug(req.RequestedSlug),
Keep: req.Keep, TTLNanoseconds: cfg.TTL.Nanoseconds(), IdleNanos: cfg.IdleTimeout.Nanoseconds(),
SSHPublicKey: strings.TrimSpace(publicKey),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Include SSH routing fields in the fixed intent fingerprint

The fingerprint includes the SSH user and key but omits cfg.SSHPort and cfg.SSHFallbackPorts, even though acquireFixed later builds its readiness/returned target from those current values via sshTargetFromConfig. Replaying one fixed lease ID with a changed SSH port therefore passes intent validation, attempts the existing VM over a different route, and can rewrite the claim endpoint instead of returning the promised lease_id_conflict; hash the effective SSH routing configuration as part of the immutable intent.

Useful? React with 👍 / 👎.

Comment on lines +229 to +232
if claim.CloudImmutableID == "" {
claim.CloudNumericID = server.ID
claim.CloudImmutableID = server.ImmutableID
claim.Labels = maps.Clone(server.Labels)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Refuse to learn vmgenid from an ambiguous replay

When a clone attempt has been persisted but CreateServerWithVMID never returned successfully, CloudImmutableID remains empty and this branch binds the vmgenid of whichever matching-labeled VM is first observed. If the original VM was deleted or restored/replaced at the same VMID with its description labels preserved, replay accepts the replacement generation instead of reporting the documented identity conflict. Only bind the generation from a create proven to have completed in the current invocation, or durably attest it before permitting ambiguous replay adoption.

Useful? React with 👍 / 👎.

const fixedProxmoxCreateIntentVersion = 1

var fixedProxmoxLeaseKind = core.FixedLeaseKind{
ClaimProvider: core.FixedProxmoxClaimProvider,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Canonicalize fixed claims during numeric VMID lookup

Using proxmox-fixed-v1 as the claim provider makes fixed claims invisible to the existing resolveNumericClaim, which filters with claim.Provider != "proxmox" rather than canonicalizing the marker. Consequently, when a fixed attempt's VM is already absent, crabbox stop --provider proxmox --id <vmid> reports not found and cannot turn the prepared claim into its terminal tombstone, even though numeric VMIDs are otherwise accepted by this backend; include the fixed marker in that lookup.

Useful? React with 👍 / 👎.

if len(claim.Labels) == 0 {
claim.Labels = fixedProxmoxIdentityLabels(b.Cfg, claim.LeaseID, claim.Slug, claim.FixedCreateIntent.Fingerprint, node)
}
*claim = fixedProxmoxLeaseKind.TerminalClaim(*claim, time.Now().UTC())

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Keep unresolved clone attempts nonterminal while tasks may materialize

A failed clone submission can mean Proxmox accepted the asynchronous request but the client lost the response before receiving its UPID, so the VM may not yet appear in inventory. releaseFixed treats a single absent VMExistsInCluster observation as final and writes a released tombstone; the still-running clone task can then create the VM afterward, leaving an unlabeled leak that future replay and release refuse to reconcile. A prepared, unbound attempt needs task-completion evidence or another durable cancellation/reconciliation barrier before absence can be terminalized.

Useful? React with 👍 / 👎.

Comment on lines +866 to +868
if req.DryRun {
fmt.Fprintf(b.RT.Stderr, "would delete server id=%s name=%s\n", server.DisplayID(), server.Name)
continue

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Validate fixed ownership during cleanup dry-runs

For a fixed claim, dry-run returns here before releaseFixed performs provider-scope, VMID, label, and vmgenid validation. A VM that merely copies the fixed lease label—or a replaced VM with a mismatched generation—will therefore be reported as would delete, while the real cleanup refuses it; this makes the dry-run output unreliable as a safety preview. Run the same non-mutating fixed-identity checks before printing the deletion.

Useful? React with 👍 / 👎.

@devinkuhn

Copy link
Copy Markdown
Author

Downstream live qualification

A dist-patched Linux amd64 binary built from commit f7a1f421483a37eb0afac663ccf0849993100b90 was qualified against a three-node Proxmox VE 9.2 cluster using a dedicated token, pool, Ceph RBD storage, and Ubuntu 24.04 template.

Results:

  • fixed create cbx_892b544f29f3 allocated VMID 100;
  • identical fixed-ID replay returned the same VMID in 2.3 seconds and created no second VM;
  • bounded command returned CRABBOX_RUN_OKx86_64;
  • heartbeat refreshed the lease successfully;
  • stop deleted VMID 100;
  • replay after release returned lease_id_conflict: fixed lease ... is terminal and cannot be replayed;
  • cluster inventory confirmed no remaining disposable VM.

The test used only generic Proxmox/Crabbox behavior. No private configuration or credentials are included in this PR.

@clawsweeper clawsweeper Bot added P2 Normal priority bug or improvement with limited blast radius. merge-risk: 🚨 compatibility 🚨 Merging this PR could break existing users, config, migrations, defaults, or upgrades. merge-risk: 🚨 security-boundary 🚨 Merging this PR could weaken sandboxing, authorization, credentials, or sensitive data. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Sep 5, 2026
@clawsweeper

clawsweeper Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs real behavior proof before merge. Reviewed September 4, 2026, 8:50 PM ET / September 5, 2026, 00:50 UTC.

ClawSweeper review

What this changes

Adds caller-supplied Proxmox lease IDs with persisted VM allocation intent, replay reconciliation, terminal release records, tests, and documentation.

Merge readiness

Blocked before merge - 15 items remain

This remains useful work: neither the inspected main revision nor v0.49.1 supports fixed Proxmox lease IDs. The reported live qualification demonstrates normal replay, but source inspection confirms blocking generation-binding and release-reconciliation defects.

Priority: P2
Reviewed head: f7a1f421483a37eb0afac663ccf0849993100b90

Review scores

Measure Result What it means
Overall readiness 🦪 silver shellfish (2/6) The implementation and real-setup report provide useful signal, but unsafe failure-state transitions prevent merge readiness.
Proof confidence 🦐 gold shrimp (3/6) Needs stronger real behavior proof before merge: Authority-chain proof required: the reported three-node Proxmox run meaningfully covers normal create, replay, execution, and release, but does not show the production adapter rejecting a same-VMID replacement with copied labels before SSH, label writes, stop, or purge, nor safe handling of a clone that materializes after apparent absence. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.
Patch quality 🦪 silver shellfish (2/6) Security review found an item that needs attention.

Verification

Check Result Evidence
Real behavior Needs proof Needs stronger real behavior proof before merge: Authority-chain proof required: the reported three-node Proxmox run meaningfully covers normal create, replay, execution, and release, but does not show the production adapter rejecting a same-VMID replacement with copied labels before SSH, label writes, stop, or purge, nor safe handling of a clone that materializes after apparent absence. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.
Evidence reviewed 12 items Pinned introduction and checkout identity: The checkout is the original head f7a1f42, whose recorded parent is 775ebb1. The introduced change contains nine files; unrelated base-branch changes were excluded. The inspected test merge records the supplied base followed by the exact PR head.
Still absent from inspected main and release: The fetched main backend still calls ordinary acquisition without implementing requested-ID support. Its Proxmox implementation is unchanged from the commit tagged v0.49.1. No merged replacement is established by the supplied related-item context.
Applicable repository policy and prior review disposition: The complete root AGENTS.md was read; no applicable nested policy or maintainer-note directory was found. Provider reconciliation belongs in the adapter, and contributor authors leave changelog edits to maintainers, so the earlier changelog request is not retained as a finding.
Findings 6 actionable findings [P1] Reject unbound generations before adoption or deletion
[P1] Preserve unresolved clone attempts until completion is established
[P2] Include effective SSH routing in the immutable intent
Security Needs attention Inventory observation becomes deletion authority: For a prepared claim without a recorded generation, release accepts a same-VMID replacement with copied labels, stores its current generation, and authorizes checked stop/purge against that newly learned value. The final checks therefore validate the replacement, not the original allocation.

How this fits together

Crabbox’s direct Proxmox backend turns CLI lease requests into template clones and SSH execution targets. Local claim records bind those requests to provider resources and govern subsequent replay, release, and cleanup.

flowchart TD
  A[CLI lease request] --> B[Proxmox provider adapter]
  B --> C[Durable local claim]
  C --> D{New allocation or replay}
  D --> E[Clone selected VMID]
  D --> F[Check VM identity]
  E --> F
  F --> G[SSH lease]
  G --> H[Checked release and terminal claim]
Loading

Before merge

  • Add real behavior proof - Needs stronger real behavior proof before merge: Authority-chain proof required: the reported three-node Proxmox run meaningfully covers normal create, replay, execution, and release, but does not show the production adapter rejecting a same-VMID replacement with copied labels before SSH, label writes, stop, or purge, nor safe handling of a clone that materializes after apparent absence. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.
  • Reject unbound generations before adoption or deletion (P1) - After an interrupted create, CloudImmutableID can remain empty. Validation then accepts any nonempty generation on the matching VMID, and this block persists it as authority. A replacement retaining the description labels is consequently accepted; releaseFixed repeats this pattern at lines 731–749 and can stop/purge it without SSH verification. Bind the generation from an attested create result, or retain the ambiguous claim without adopting or deleting.
  • Preserve unresolved clone attempts until completion is established (P1) - If submission loses its response while the clone can still materialize, an empty inventory is not final absence. This path nevertheless terminalizes the prepared claim and clears its attempt; later replay rejects it and later release returns immediately for the terminal record. A late VM is then no longer recoverable through this operation. Require completion/cancellation evidence before terminalization, or leave the attempt unresolved.
  • Include effective SSH routing in the immutable intent (P2) - The fingerprint omits SSHPort and SSHFallbackPorts, although acquisition uses both through sshTargetFromConfig. Replaying an existing fixed ID with different ports passes intent validation, contacts the VM through the changed route, and can persist a different endpoint instead of returning lease_id_conflict. Include normalized effective routing values and cover drift in each field.
  • Recognize fixed claims during numeric VMID recovery (P2) - The newly introduced proxmox-fixed-v1 records are returned unchanged by ListLeaseClaims, but this existing filter accepts only proxmox. Once a fixed VM is absent, stop --provider proxmox --id &lt;vmid> cannot resolve its claim and cannot complete release reconciliation. Canonicalize the provider here while retaining the existing scope and ambiguity checks.
  • Validate fixed ownership before reporting dry-run deletion (P2) - This branch recognizes a fixed candidate by its lease label and prints would delete before checking provider scope, VMID, fingerprint, or generation. A copied-label or replacement VM can therefore appear deletable even though real cleanup rejects it in releaseFixed. Apply the same non-mutating identity validation before producing the preview, as the ordinary cleanup branch already does.
  • Allow retry after a provably unsubmitted allocation failure (P2) - Late discovery on the unchanged reviewed head: the framework persists a prepared claim before NextVMID runs. A transient allocator error leaves no durable attempt, but every later invocation reaches this rejection because the claim now exists; release also rejects its missing VMID. Since clone submission requires a successfully persisted attempt, this state is provably pre-submit. Resume allocation under the claim lock instead of permanently stranding the ID.
  • Resolve security concern: Inventory observation becomes deletion authority - For a prepared claim without a recorded generation, release accepts a same-VMID replacement with copied labels, stores its current generation, and authorizes checked stop/purge against that newly learned value. The final checks therefore validate the replacement, not the original allocation.
  • Resolve merge risk (P1) - An unbound fixed attempt can promote a replacement VM into replay and deletion authority; the reported live qualification does not exercise this forbidden case.
  • Resolve merge risk (P1) - A delayed clone can outlive the new terminalization path, leaving a resource that subsequent fixed replay and release will not reconcile.
  • Resolve merge risk (P1) - The new persistent claim marker lacks demonstrated lifecycle compatibility across numeric-ID recovery and old/new clients sharing local state.
  • Complete next step (P2) - Repair the findings and provide production-path proof of replacement-generation rejection and delayed-clone recovery before merge. Redacted terminal output or logs are appropriate; remove credentials, private addresses, and endpoints. Updating the PR body should trigger another review; otherwise ask a maintainer to comment @clawsweeper re-review.
  • Improve patch quality - Establish generation authority without learning it from ambiguous inventory, and preserve unresolved submissions until finality is proven.
  • Improve patch quality - Repair pre-submit retry, SSH intent drift, numeric recovery, and dry-run parity with focused regression coverage.
  • Improve patch quality - Provide redacted production-path evidence for allowed and replacement-generation cases, delayed completion, and ordinary/fixed claim compatibility across upgrades.

Findings

  • [P1] Reject unbound generations before adoption or deletion — internal/providers/proxmox/backend.go:229-234
  • [P1] Preserve unresolved clone attempts until completion is established — internal/providers/proxmox/backend.go:762-773
  • [P2] Include effective SSH routing in the immutable intent — internal/providers/proxmox/backend.go:113-117
  • [high] Inventory observation becomes deletion authority — internal/providers/proxmox/backend.go:731
Agent review details

Security

Needs attention: An unbound attempt can acquire destructive authority from a replacement VM rather than proving the original generation.

Review metrics

Metric Value Why it matters
Production versus test growth production +448 net lines; tests +365 net lines The stated purpose justifies provider-specific lifecycle code, but its failure-state coverage must match the expanded persistent-state behavior.

Root-cause cluster

Relationship: fixed_by_candidate
Canonical: #1847
Summary: This PR is the explicit implementation candidate for the open same-author feature request; neither item should close before a safe implementation lands.

Members:

Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything.

Merge-risk options

Maintainer options:

  1. Repair ownership and lifecycle checks (recommended)
    Preserve unresolved operations, reject unproven generations before side effects, and validate stored-claim compatibility before landing.
  2. Pause the capability
    Keep fixed Proxmox IDs unavailable until the provider can establish safe replay and terminalization evidence.

Technical review

Best possible solution:

Keep Proxmox-specific reconciliation in the existing fixed-lease framework, with attested generation binding, recoverable submission states, consistent ownership checks, and verified claim-format compatibility.

Do we have a high-confidence way to reproduce the issue?

Yes, the proposed-head defects have clear source-level triggers: replay or release an unbound prepared claim against a replacement generation, or retry after allocator failure. These paths were inspected, not executed; current main does not yet expose the capability.

Is this the best way to solve the issue?

No, not as submitted: reusing the provider-neutral framework is appropriate, but inventory must not create missing ownership authority and momentary absence must not erase unresolved submission state.

Full review comments:

  • [P1] Reject unbound generations before adoption or deletion — internal/providers/proxmox/backend.go:229-234
    After an interrupted create, CloudImmutableID can remain empty. Validation then accepts any nonempty generation on the matching VMID, and this block persists it as authority. A replacement retaining the description labels is consequently accepted; releaseFixed repeats this pattern at lines 731–749 and can stop/purge it without SSH verification. Bind the generation from an attested create result, or retain the ambiguous claim without adopting or deleting.
    Confidence: 0.99
  • [P1] Preserve unresolved clone attempts until completion is established — internal/providers/proxmox/backend.go:762-773
    If submission loses its response while the clone can still materialize, an empty inventory is not final absence. This path nevertheless terminalizes the prepared claim and clears its attempt; later replay rejects it and later release returns immediately for the terminal record. A late VM is then no longer recoverable through this operation. Require completion/cancellation evidence before terminalization, or leave the attempt unresolved.
    Confidence: 0.95
  • [P2] Include effective SSH routing in the immutable intent — internal/providers/proxmox/backend.go:113-117
    The fingerprint omits SSHPort and SSHFallbackPorts, although acquisition uses both through sshTargetFromConfig. Replaying an existing fixed ID with different ports passes intent validation, contacts the VM through the changed route, and can persist a different endpoint instead of returning lease_id_conflict. Include normalized effective routing values and cover drift in each field.
    Confidence: 0.99
  • [P2] Recognize fixed claims during numeric VMID recovery — internal/providers/proxmox/backend.go:571-573
    The newly introduced proxmox-fixed-v1 records are returned unchanged by ListLeaseClaims, but this existing filter accepts only proxmox. Once a fixed VM is absent, stop --provider proxmox --id &lt;vmid> cannot resolve its claim and cannot complete release reconciliation. Canonicalize the provider here while retaining the existing scope and ambiguity checks.
    Confidence: 0.99
  • [P2] Validate fixed ownership before reporting dry-run deletion — internal/providers/proxmox/backend.go:866-868
    This branch recognizes a fixed candidate by its lease label and prints would delete before checking provider scope, VMID, fingerprint, or generation. A copied-label or replacement VM can therefore appear deletable even though real cleanup rejects it in releaseFixed. Apply the same non-mutating identity validation before producing the preview, as the ordinary cleanup branch already does.
    Confidence: 0.99
  • [P2] Allow retry after a provably unsubmitted allocation failure — internal/providers/proxmox/backend.go:191-196
    Late discovery on the unchanged reviewed head: the framework persists a prepared claim before NextVMID runs. A transient allocator error leaves no durable attempt, but every later invocation reaches this rejection because the claim now exists; release also rejects its missing VMID. Since clone submission requires a successfully persisted attempt, this state is provably pre-submit. Resume allocation under the claim lock instead of permanently stranding the ID.
    Confidence: 0.98
    Late finding: first raised on code an earlier review cycle already covered.

Overall correctness: patch is incorrect
Overall confidence: 0.98

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 530d9b586aa3.

Labels

Label changes:

  • add P2: This is a bounded new provider capability, not an established urgent regression affecting current released users.
  • add merge-risk: 🚨 compatibility: The new persisted provider marker is excluded by an existing numeric-ID lookup, and cross-version claim lifecycle behavior remains unproven.
  • add merge-risk: 🚨 security-boundary: The introduced release path can learn a replacement VM's generation and then authorize stop and purge against that learned identity.
  • add rating: 🦪 silver shellfish: Overall readiness is 🦪 silver shellfish; proof is 🦐 gold shrimp and patch quality is 🦪 silver shellfish.
  • add status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs stronger real behavior proof before merge: Authority-chain proof required: the reported three-node Proxmox run meaningfully covers normal create, replay, execution, and release, but does not show the production adapter rejecting a same-VMID replacement with copied labels before SSH, label writes, stop, or purge, nor safe handling of a clone that materializes after apparent absence. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.

Label justifications:

  • P2: This is a bounded new provider capability, not an established urgent regression affecting current released users.
  • merge-risk: 🚨 security-boundary: The introduced release path can learn a replacement VM's generation and then authorize stop and purge against that learned identity.
  • merge-risk: 🚨 compatibility: The new persisted provider marker is excluded by an existing numeric-ID lookup, and cross-version claim lifecycle behavior remains unproven.
  • rating: 🦪 silver shellfish: Overall readiness is 🦪 silver shellfish; proof is 🦐 gold shrimp and patch quality is 🦪 silver shellfish.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs stronger real behavior proof before merge: Authority-chain proof required: the reported three-node Proxmox run meaningfully covers normal create, replay, execution, and release, but does not show the production adapter rejecting a same-VMID replacement with copied labels before SSH, label writes, stop, or purge, nor safe handling of a clone that materializes after apparent absence. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.

Evidence

Security concerns:

  • [high] Inventory observation becomes deletion authority — internal/providers/proxmox/backend.go:731
    For a prepared claim without a recorded generation, release accepts a same-VMID replacement with copied labels, stores its current generation, and authorizes checked stop/purge against that newly learned value. The final checks therefore validate the replacement, not the original allocation.
    Confidence: 0.99

What I checked:

  • Pinned introduction and checkout identity: The checkout is the original head f7a1f42, whose recorded parent is 775ebb1. The introduced change contains nine files; unrelated base-branch changes were excluded. The inspected test merge records the supplied base followed by the exact PR head. (f7a1f421483a)
  • Still absent from inspected main and release: The fetched main backend still calls ordinary acquisition without implementing requested-ID support. Its Proxmox implementation is unchanged from the commit tagged v0.49.1. No merged replacement is established by the supplied related-item context. (internal/providers/proxmox/backend.go:61, 530d9b586aa3)
  • Applicable repository policy and prior review disposition: The complete root AGENTS.md was read; no applicable nested policy or maintainer-note directory was found. Provider reconciliation belongs in the adapter, and contributor authors leave changelog edits to maintainers, so the earlier changelog request is not retained as a finding. (AGENTS.md:41, f7a1f421483a)
  • Unbound generation becomes destructive authority: Generation comparison is conditional on an already populated CloudImmutableID. Acquisition learns an empty binding from inventory, and release independently does the same before calling checked stop/purge. A replacement with the same VMID and copied labels therefore supplies the generation against which it is subsequently authorized. (internal/providers/proxmox/backend.go:731, f7a1f421483a)
  • Release loses unresolved submission state: releaseFixed terminalizes prepared attempts after inventory absence without checking submission completion. The real clone client receives a task identifier and waits separately, while TerminalClaim clears the attempt. The added absent-attempt test explicitly exercises terminalization after a simulated lost clone response but never models later materialization. (internal/providers/proxmox/backend.go:762, f7a1f421483a)
  • Remaining intent and lookup defects: The new fingerprint omits SSHPort and SSHFallbackPorts despite using them to construct the returned SSH target. The new persisted provider marker also reaches resolveNumericClaim, whose raw provider comparison excludes it; ListLeaseClaims does not canonicalize stored providers. (internal/providers/proxmox/backend.go:108, f7a1f421483a)

Likely related people:

  • Peter Steinberger: Suggested for follow-up; no historical authorship or introduction is verified. (role: unverified routing candidate; confidence: low)
  • coygeek: Suggested for follow-up; no historical authorship or introduction is verified. (role: unverified routing candidate; confidence: low)

Rating scale

Score Internal tier Crab rank Meaning
6/6 S 🦀 challenger crab Exceptional readiness
5/6 A 🦞 diamond lobster Very strong readiness
4/6 B 🐚 platinum hermit Good normal PR; ordinary maintainer review
3/6 C 🦐 gold shrimp Useful, but confidence is limited
2/6 D 🦪 silver shellfish Proof or implementation needs work
1/6 F 🧂 unranked krab Not merge-ready
N/A NA 🌊 off-meta tidepool Rating does not apply

Overall follows the weaker of proof and patch quality.
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

Workflow

  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

Release-only resolution of an exact fixed Proxmox lease ID failed with exit 4
"lease/server not found" whenever Crabbox held no claim, leaving callers stuck
retrying destruction of a VM that no longer exists. A completed cluster-wide
inventory that shows no VM carrying the lease identity now converges as
confirmed absence, so release is idempotent.

Unreadable inventory, reused or conflicting VM identity, an unresolved cluster
scope, and non-exact identifiers still fail closed, and no tombstone is
fabricated for an ID that has no durable create intent.

Refs openclaw#1847

Co-authored-by: Cursor <cursoragent@cursor.com>
@devinkuhn

Copy link
Copy Markdown
Author

Follow-up commit 25340a91781fe7e0c726dd07236823ae0568238b fixes a live cleanup deadlock discovered during downstream Control UI dispatch: an exact fixed Proxmox lease with no local claim and no matching cluster VM now converges as confirmed absence instead of returning lease/server not found. Cluster inventory/read errors, reused provider identity, noncanonical IDs, and mismatches remain fail-closed. Focused Proxmox tests and package race tests pass.

@clawsweeper

clawsweeper Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

ClawSweeper status: review started.

I am starting a fresh review of this pull request: feat(proxmox): support replay-safe fixed lease IDs This is item 1/1 in the current shard. Shard 0/1.

This placeholder means the worker is alive and reading the current context. I will edit this same comment with the actual review when the claws are done clicking.

Crustacean status: shell secured, claws on keyboard, evidence pebbles being sorted.

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 25340a9178

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +253 to +255
if req.OnAcquired != nil {
if err := req.OnAcquired(acquired); err != nil {
return LeaseTarget{}, fmt.Errorf("acknowledge fixed Proxmox acquisition: %w", err)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Acknowledge the VM identity before readiness work

When controller identity acknowledgment is configured, this callback runs only after AcquireFixedLease has completed CreateServerWithVMID, SSH bootstrap/readiness, label updates, and acquisition-claim persistence. If the process is interrupted during those potentially long readiness steps, or if the callback itself fails, the controller never learns the created VM identity and this path neither rolls it back nor returns it to the caller, leaving the controller unable to reconcile the resource. Invoke OnAcquired once the raw VM identity is validated and before readiness side effects, as required by the AcquireRequest.OnAcquired contract, or explicitly roll back on acknowledgment failure.

Useful? React with 👍 / 👎.

return &leaseBackend{DirectSSHBackend: shared.DirectSSHBackend{SpecValue: spec, Cfg: cfg, RT: rt, StoredLeaseKeys: true}}
}

func (b *leaseBackend) SupportsRequestedLeaseID() bool { return true }

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Validate fixed claims on every resolve

Enabling fixed IDs also makes their claims available to normal lifecycle commands, but Proxmox Resolve still returns inventory matches without checking the fixed claim's fingerprint or CloudImmutableID. If a VM is restored or replaced at the same VMID while retaining the lease labels, status --wait and heartbeat accept it because the generic claim check compares only provider scope and CloudID, and then Touch writes lifecycle labels to the wrong generation instead of returning lease_id_conflict. Route fixed-claim resolution through fixedProxmoxAttempt and validateFixedProxmoxServer, as the acquire and release paths already do.

Useful? React with 👍 / 👎.

}
fmt.Fprintf(b.RT.Stderr, "provisioning provider=proxmox lease=%s slug=%s node=%s template=%d vmid=%d keep=%v fixed=true\n",
leaseID, intent.Slug, cfg.Proxmox.Node, cfg.Proxmox.TemplateID, attemptVMID, req.Keep)
server, err = client.CreateServerWithVMID(ctx, cfg, publicKey, leaseID, intent.Slug, req.Keep, attemptVMID, labels)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Preserve fixed VMs when acquisition is canceled

This fixed-ID path reuses CreateServerWithVMID, whose post-clone error paths call cleanupClone() from a background context, including when waitTask, IP discovery, or SSH bootstrap returns because the caller canceled the command. The durable fixed claim still retains its submitted VMID, so after that deletion an identical replay finds no VM and permanently returns the unresolved-attempt conflict instead of recovering the lease; this also contradicts the documented fixed-ID guarantee that caller cancellation never releases the resource. Give fixed creation a no-cleanup mode and leave the VM for identity-checked replay or explicit release.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

merge-risk: 🚨 compatibility 🚨 Merging this PR could break existing users, config, migrations, defaults, or upgrades. merge-risk: 🚨 security-boundary 🚨 Merging this PR could weaken sandboxing, authorization, credentials, or sensitive data. P2 Normal priority bug or improvement with limited blast radius. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add replay-safe fixed lease IDs to the Proxmox provider

1 participant