Reconciliation
CrossXCloud treats your canvas as the desired state. To plan a change, it compares that desired state against what actually exists and works out the operations to close the gap. This is the reconciliation loop, and it is the “why” behind the Plan and Apply guide.
flowchart LR
subgraph Edit
A[Canvas] --> B[Desired state]
end
subgraph Plan
C[Read live state] --> D[Diff]
B --> D
D --> E[Ordered ops]
end
subgraph Apply
E --> F[Execute sequentially]
F --> G[Commit]
end
Desired state versus live state
Section titled “Desired state versus live state”The desired state is what you have drawn on the canvas. It is a graph of resources and the edges between them, serialized from the editor’s node and edge stores.
The live state is what actually exists in your cloud account right now, read fresh each time you Plan. CrossXCloud only reads resources it manages, by filtering on the managed-by=crossxcloud label that every created resource gets. Each managed resource also carries a crossxcloud-node label that maps it back to the canvas node that produced it.
The diff between the two is the plan.
| Desired state | Live state | |
|---|---|---|
| Source | The canvas (nodes and edges) | Your cloud account, read via provider APIs |
| Keyed by | Editor node ID | crossxcloud-node label on the provider resource |
| Includes | Every node on the canvas | Only resources tagged managed-by=crossxcloud |
| When read | Continuously as you edit | Freshly on every Plan |
The diff
Section titled “The diff”The diff produces five kinds of operations:
| Operation | When it fires | Example |
|---|---|---|
| Create | A node exists on the canvas but not in your account. | You dragged a new compute node onto the canvas. |
| Update | A resource exists in both, but a mutable field changed. | You renamed a compute node or changed a private network’s IP range. |
| Delete | A managed resource exists in your account but not on the canvas. | You deleted a node that had already been applied. |
| Attach | An edge was added between two existing resources. | You wired a compute node into a private network. |
| Detach | An edge was removed between two existing resources. | You disconnected a floating IP from a compute node. |
Some fields are immutable on the provider. Changing one of those on an existing node does not produce an in-place update. It produces a labeled delete-plus-create, so you can see that you are replacing the resource, not silently mutating it.
Ordered operations
Section titled “Ordered operations”Operations run in dependency order so that every operation is valid against the state the previous one produced. The ordering is:
flowchart TD
A[Create networks] --> B[Create servers]
B --> C[Attach memberships]
C --> D[Update in place]
D --> E[Detach memberships]
E --> F[Delete servers]
F --> G[Delete networks]
Deletions run in reverse. A network is not deleted until every server in it has been detached and destroyed first.
In-place updates where possible
Section titled “In-place updates where possible”CrossXCloud updates mutable fields in place where the provider allows it. The fields that support in-place update today:
| Resource | Mutable fields |
|---|---|
| Compute node | Name |
| Private network | Name, IP range |
Everything else is a replace (delete-plus-create). This keeps apply small and quick when you only changed something cosmetic, and it makes destructive changes visible in the plan.
Auto-apply
Section titled “Auto-apply”When you Execute, every operation in the plan runs sequentially without further confirmation. A summary card shows live progress as each operation completes.
sequenceDiagram
participant User
participant Canvas
participant Reconcile as Reconcile Engine
participant Provider as Cloud Provider
participant History
User->>Canvas: Click Plan
Canvas->>Reconcile: Diff(desired, live)
Reconcile->>Provider: Read current state
Provider-->>Reconcile: Live resources
Reconcile-->>Canvas: Ordered ops
Canvas->>User: Show plan with diff badges
User->>Canvas: Click Execute
loop Each operation
Canvas->>Reconcile: ExecuteOp(op)
Reconcile->>Provider: Create / Update / Delete / Attach / Detach
Provider-->>Reconcile: Result
Reconcile-->>Canvas: Op status
end
Canvas->>History: Write commit + reconciliation record
Canvas->>User: Show completion
There is no resume-from-failed-op. After a failure, the canvas is unchanged, and you Plan again. The new plan is computed against the partially applied live state, so it picks up exactly where things left off, like a rebase replaying onto a new base.
Membership dispatch
Section titled “Membership dispatch”Attach and detach operations are routed to the correct provider API based on the target resource:
| Target kind | Attach action | Detach action |
|---|---|---|
| Private network | Attach server to network | Detach server from network |
| Floating IP | Assign floating IP to server | Unassign floating IP from server |
This routing happens at execute time. The resource IDs for membership operations are resolved at diff time, so the executor does not need to look them up again.
Mixed providers
Section titled “Mixed providers”A canvas can mix providers. When you Plan, the diff produces executable operations for live providers (Hetzner, Google Cloud) and notices for scaffolded providers (AWS, Azure). Notices appear in the plan so you can see what would happen if those providers were live, rather than having those nodes silently ignored.
Initial reconciliation
Section titled “Initial reconciliation”When you open a project canvas for the first time and the persisted graph is empty, CrossXCloud runs an initial reconciliation. It reads the live state from your connected provider and lays it out as nodes on the canvas. This is how existing infrastructure becomes visible.
After that, the canvas is the source of truth. Live state is only read again when you Plan.
When the apply is recorded
Section titled “When the apply is recorded”A commit is only written at Execute time, never at edit or Plan time, and never for edits that did not actually hit the provider. The history is a faithful record of what actually changed in your cloud. Every Execute writes a commit, whether the apply was fully successful, partially applied, or failed.
| Event | Commit written? |
|---|---|
| Edit the canvas | No |
| Run Plan | No |
| Execute (all ops succeed) | Yes, outcome: applied |
| Execute (some ops succeed) | Yes, outcome: partial |
| Execute (all ops fail) | Yes, outcome: failed |
See Version history for how commits are stored and browsed.
- Plan and Apply for the everyday walkthrough.
- Version history for what gets recorded.
- The canvas model for how the canvas becomes a desired state.