mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-10-26 05:30:18 +00:00
5b41c0512f
* docs: reorganize GitOps documentation into one section Add a GitOps overview that defines the model once (Direct sources versus Blueprints, the chain from commit to container, the three authority steps, the policies, source states, executable identity). Split the oversized Git Sources and Blueprints pages into focused pages for Git authentication, provider and CI webhooks, repository secrets, and Git-managed Blueprints, and trim the remaining pages to their own slice. Group the GitOps pages in the navigation and split the old "Deploy and automate" group into "Deploy and recover" and "Automate". Existing page slugs are unchanged. Correct the provider hook labels to match the app, and repoint What's New links to the new Git-managed Blueprints page. * docs: add current GitOps screenshots and correct UI labels Retake the Git Sources, Blueprints, and GitOps portfolio screenshots on the integration image, and add screenshots to the new GitOps pages (answer, path, and evidence block; SSH deploy key form; provider hooks; Secrets tab; Convert to Git, placement review, and rollout controls). Correct labels to match the app: Deploy key (SSH), the provider hook event scopes, and the encrypted source policy buttons. The change plan dialog shows the stack name, not the commit. * docs: map the GitOps policy names once in the overview Evidence, the policy controls, and the Git source form name the same settings differently. State the mapping in one place.
160 lines
16 KiB
Plaintext
160 lines
16 KiB
Plaintext
---
|
|
title: "GitOps Drift"
|
|
description: "The drift classes Sencho derives for every Git-managed stack and Blueprint, what each one proves, and which policy decides what happens next."
|
|
---
|
|
|
|
Sencho reports drift in two layers. The **spatial** layer compares the effective Compose model against the containers running right now and is covered in [Drift Detection](/features/stack-drift). The **GitOps** layer compares what you intended against what Sencho has proven, accepted, and observed over time, for anything managed by a Git source or a [Blueprint](/features/blueprint-model).
|
|
|
|
This page is the reference for that second layer: the classes it reports, the evidence each one rests on, and the policy that decides what happens after a class is reported. The vocabulary (generation, stage, authority step) is defined in the [GitOps overview](/features/gitops-overview).
|
|
|
|
## The seven classes
|
|
|
|
Every GitOps drift item carries a class. The class says which part of the chain diverged, and that decides who owns the fix.
|
|
|
|
| Class | The comparison it is made from | A mismatch is reported when |
|
|
|---|---|---|
|
|
| **Source** | The accepted generation's commit against the commit the configured ref names. | The two differ, or the last fetch or validation failed. |
|
|
| **Managed project** | The stack's recorded managed-project manifest against the accepted generation. | They differ, or the manifest is missing or unreadable. |
|
|
| **Placement** | The approved blast against the nodes that hold applied authority. | A recorded approval no longer binds the current intent, or a node still holds authority outside the required set after the rollout settled. |
|
|
| **Health** | The latest stack-scoped health run for a node against the generation that node runs. | The run failed for exactly that generation. |
|
|
| **Runtime** | What is running against what was approved: the artifact identity, and the generation each node was asked to run. | A node reports another artifact identity, or runs a generation other than the one it was asked for. |
|
|
| **Rollout** | A required rollout target's observed artifact identity against the authorized rollout set. | A required target reports another identity. |
|
|
| **Invocation** | The compose invocation a node recorded when the stack was applied, against the invocation that generation was applied with. | The node's compose file order, project name, project directory, or env file order is not the one it was given. |
|
|
|
|
This is the model's vocabulary: the boundaries a drift item can fall into, and what each one would be measured against. A class appears only where Sencho holds the evidence for it and the rows prove a mismatch, so a class whose evidence is not held is absent rather than approximated. The table does not promise that every class is reported today; it says what each class means when it is.
|
|
|
|
The [Drift tab](/features/stack-drift) and the application sheet show the classes they have evidence for. The portfolio's drift summary counts them by class, and its attention queue names the ones that are waiting on you.
|
|
|
|
## Reading an item
|
|
|
|
Each item is one line of comparison plus the facts you need to act on it. The drift row shows the class, the owner, the reason, and the expected identity beside the observed one.
|
|
|
|
- **Class** names the part of the chain that diverged.
|
|
- **Expected** is the identity Sencho was meant to run: a commit, a generation, an artifact set, an intent, or a health run.
|
|
- **Observed** is what it found: the same kind of identity, or `unknown` when Sencho found nothing to compare.
|
|
- **Reason** says in one line what differs.
|
|
- **Owner** names the surface that owns the fix, so you know whether to go to the Git Source sheet, the Blueprint, or the node.
|
|
- **Affected targets** names the nodes involved. A direct Git source has one; a Blueprint can have many.
|
|
- **Freshness** is when the evidence was recorded, when the evidence carries a time. An old observation is weaker proof than a new one, so the timestamp travels with the item; an item derived from two pointers rather than a recorded observation has no timestamp to show.
|
|
- **Action** is the one step that would resolve the item for the affected target, and only when that step is legal right now. A paused or failed node gets no action rather than one that cannot run.
|
|
|
|
## When Sencho will not repair it
|
|
|
|
A Blueprint whose drift mode is **Enforce** can restore runtime drift on its own, but only the state the target was already authorized to run: the generation that target acknowledged and the image identity approved for it. A newer accepted generation never changes what an older target restores, because the repair answers "put this node back" rather than "deploy the newest thing".
|
|
|
|
Holding is an **Enforce** decision, made fresh each tick. Observe and Suggest still detect and report the same drift, because "auto-fix was declined" is not an answer for someone who never asked for one. And a hold never suppresses detection: Sencho still inspects the node and still records what is running, it only declines to overwrite it.
|
|
|
|
Two things a held row does not tell you. Its reason is the hold, not the drift, so read the Drift panel for what is actually running. And a row held under Enforce keeps reading **Repair held** if you switch to Observe or Suggest while the drift cannot be classified, because the next check has nothing to record until the cause clears. The deployment row reads **Repair held** with the reason, the attention queue names it, and the workload is left untouched. A held target is still drifted: it is never counted as up to date.
|
|
|
|
The reasons a repair is held are the states this model already treats as uncertainty rather than divergence: a stateful or unclassifiable Blueprint, a rollout that has moved on, on-node evidence that does not name a generation, an unreachable target or stale connectivity, an interrupted change, a recovery or rollback that owns the target, and a disagreement between the rollout's frozen pair and the target's own expectation.
|
|
|
|
Most holds clear themselves. Sencho keeps re-checking the target, so once the cause is resolved, by a node returning or an interrupted change finishing, the next reconciliation repairs the drift normally.
|
|
|
|
One does not: a hold because the rollout moved on stays until a later rollout includes that target. Superseding is a one-way fact, so nothing reopens that rollout on its own, and Git-managed Blueprints have no manual redeploy to fall back on, so the rollout is the only way out.
|
|
|
|
## Where drift is shown
|
|
|
|
| Surface | What it shows |
|
|
|---|---|
|
|
| **Drift** tab on a stack | The spatial findings plus the GitOps state for that stack, one card per node, with the per-service digest comparison under each node. |
|
|
| **Application sheet** in the portfolio | The same drift items for one application, alongside its source, artifact, placement, and rollout state. |
|
|
| **Portfolio application table** | A count and the classes involved, so you can narrow the list to one class. |
|
|
|
|
The same projection feeds all three, so a class never reads differently depending on where you look.
|
|
|
|
## Per-service digests
|
|
|
|
When the approved artifact set and the node's observation both carry per-service image evidence, each target's card shows the approved digest beside the running one, per service, and marks each one matched, drifted, or unverified. The comparison uses the child digest for the platform that node runs, because a multi-platform image has one child per platform and a node only ever proves its own.
|
|
|
|
Where evidence is missing, the card says so. It never renders an empty comparison, because an empty table reads as agreement.
|
|
|
|
When a Blueprint is first applied, Sencho freezes the exact image identity it was approved at by resolving each image against its registry. If that resolution cannot reach the registry, the target has no approved identity to compare against and its drift reads as unverified rather than as agreement.
|
|
|
|
For a Blueprint applied from stored content, Sencho retries that resolution on its own, so the target recovers on its own schedule instead of waiting for you to redeploy it. A caveat under the state names the condition while it lasts, and clears once the identity is provable. The retry interval is yours to set under **Settings → GitOps**, and it has no effect while every target's identity is already resolved.
|
|
|
|
The retry resolves the images the Blueprint was approved at, not whatever compose happens to be in the stack directory at the time. If someone edits a file on the node directly, that change is drift to be reported, never an approved identity to be adopted.
|
|
|
|
Some Compose shapes are not recovered this way, and Sencho says so rather than guessing. The approved images are read from what you wrote, while a deploy resolves them the way Compose does, and the two can disagree for `profiles`, `extends`, `include`, and YAML merge keys. For those, the retry stands down and the target stays unverified until you redeploy the Blueprint.
|
|
|
|
The tick that resolves a target waits before judging it. It reports unverified that pass, and compares on the following one. That is one reconciler interval, not a review window: if the registry resolves a tag that moved while the identity was unproven, the next tick reports it as drift, and Enforce mode repairs against it.
|
|
|
|
<Note>
|
|
A digest is compared, never pulled. When Sencho repairs a digest divergence it redeploys against the approved identity, so authored tags on disk stay as you wrote them.
|
|
</Note>
|
|
|
|
## What is not drift
|
|
|
|
Work in progress is not drift, and the classes above stay silent while it happens:
|
|
|
|
- a commit that has been fetched but not yet accepted, including one waiting on review
|
|
- a placement or authorization decision that is still open
|
|
- a rollout that is queued, canarying, batching, or waiting on its health verdict
|
|
- a manifest being written as part of an apply that has not landed on the node yet
|
|
- a node that is deploying, interrupted, or recovering
|
|
- a drift that Enforce is repairing on its current pass, which is why the Blueprint deployment row reads as correcting rather than drifted
|
|
- a target that has not been deployed yet, or has never had one recorded
|
|
|
|
Those states have their own surfaces, and each one names the next step. A node running a different generation than the one it was asked for is a separate class of its own, and is reported whether or not something else about that node is in flight.
|
|
|
|
One more case sits here by decision rather than by accident. Source drift compares the accepted commit against the commit the ref names. When those still agree but the materialization settings changed, for example a compose path, an env path, or a context directory, there is no expected-versus-observed pair of commits to report, so no source drift item is created. Instead the source shows as **reconcile required** in the attention queue, and for a Direct application that is where you fetch again: the fetch rebuilds the candidate under the settings now in force, and accepting that candidate produces the new generation. This is a choice about what the word drift means, not a claim that the materialized content was proven unchanged. Changing where Compose is read from can change the content, which is why Sencho asks rather than assumes.
|
|
|
|
## Invocation
|
|
|
|
Invocation drift answers one question: is this stack being brought up the way this generation said it would be?
|
|
|
|
When a generation is applied, Sencho reads back how Compose was actually invoked on the node: the compose files in order, the project name, the project directory, and the env files in order. That record is compared against the invocation the accepted generation was applied with. It is the only evidence in the model that can contradict the authored invocation at all, because reading the command back would only confirm what Sencho already believed.
|
|
|
|
What it catches in practice is a project that came up differently from the way the generation named it. The common case is a stack directory holding a compose file the generation did not name: Compose resolves it, the node records more files than were asked for, and the difference is reported. A node that answers with a different project name, project directory, or file order is reported the same way.
|
|
|
|
Two limits worth knowing, both about when the evidence is taken:
|
|
|
|
- **The record is read after the apply finishes.** A stack brought up by hand *before* an apply is overwritten by that apply, so the apply reads back its own work. A hand-made invocation that the apply replaced is not what you see here.
|
|
- **It is a snapshot, not a watch.** Nothing re-reads it on a timer. An invocation changed on the node after the apply is not reported until the next apply records a new one. Every item carries the time its evidence was taken, so you can see how old it is.
|
|
|
|
A stack whose invocation has not been recorded yet reports nothing and says why, rather than showing you an empty comparison that would read as agreement. The first apply records it and the caveat clears.
|
|
|
|
## What Sencho cannot prove
|
|
|
|
Drift is derived from what Sencho has recorded, so missing evidence is never rounded up to agreement. A few cases worth knowing:
|
|
|
|
- A target on a node Sencho does not host has no health verdict here, so health drift stays silent for it rather than claiming a verdict nobody wrote.
|
|
- A Git-managed Blueprint deployment that opens no generation-bound health gate produces no health item.
|
|
- A required-target set Sencho cannot read records a caveat naming the unreadable record, instead of quietly reporting no placement drift.
|
|
- A stack whose invocation has not been recorded yet records a caveat saying so, rather than reporting no invocation drift.
|
|
- A stack whose generation names no compose file records a caveat, rather than reporting a difference against a file list that was never written.
|
|
- A node that was withdrawn while a recovery on it had failed records a caveat, because the withdrawal ended what Sencho tracks there and the failed recovery is no longer reported as a state.
|
|
- A Blueprint deployment is not compared at all. Each of its targets is brought up from the Blueprint rather than from a recorded compose invocation, so there is no expected invocation to compare against, and the class stays silent for them rather than reporting every one as different.
|
|
- A Blueprint target whose approved image identity could not be resolved records a caveat and is not compared against what is running, rather than reporting no drift. For a Blueprint applied from stored content, Sencho retries the resolution on its own, so this clears without a redeploy.
|
|
- A service built on the node has no published digest to compare, so the per-service view labels it **not compared** rather than matched or drifted.
|
|
- A service running something the approved set does not name is outside the comparison, so it reads **not compared** rather than drift.
|
|
|
|
Each of those is listed as a caveat under the state it applies to, on the Drift tab and in the application sheet, saying what could not be proven. The per-service digest view says the same in its own words: a target with no per-service evidence on either side says nothing rather than showing an empty comparison that would read as agreement.
|
|
|
|
## Which policy acts on a class
|
|
|
|
A drift class reports; a policy decides. Each policy is independent, and none authorizes another. They are defined together in the [GitOps overview](/features/gitops-overview#who-decides-each-step). The two that act after something has drifted:
|
|
|
|
| Policy | What it decides |
|
|
|---|---|
|
|
| **Drift mode** (Observe, Suggest, Enforce) | What happens when a Blueprint's own drift is detected: surface it, notify you, or redeploy it |
|
|
| **Health rollout policy** | Whether a deploy must prove healthy before it finishes, and what a failure does next. See [Health rollout policy](/features/health-rollout-policy) |
|
|
|
|
Two rules keep them honest. A digest repair redeploys against the approved identity rather than pulling whatever the tag now points at, and a stateful change always re-enters operator confirmation, whatever the drift mode says.
|
|
|
|
## Related
|
|
|
|
<CardGroup cols={2}>
|
|
<Card title="Drift Detection" icon="magnifying-glass" href="/features/stack-drift">
|
|
The spatial layer: effective Compose model against the containers running now.
|
|
</Card>
|
|
<Card title="GitOps overview" icon="route" href="/features/gitops-overview">
|
|
The chain from commit to container, and who decides each step.
|
|
</Card>
|
|
<Card title="Blueprints" icon="layers" href="/features/blueprint-model">
|
|
Drift modes and digest drift on a fleet template.
|
|
</Card>
|
|
<Card title="GitOps portfolio" icon="chart-pie" href="/features/gitops-portfolio">
|
|
Every application in one table, with drift by class and the attention queue.
|
|
</Card>
|
|
</CardGroup>
|