> ## Documentation Index
> Fetch the complete documentation index at: https://sdk.docs.xano.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Drift

> Find out when your Xano workspace and your project have moved apart: the sync baseline in xano.lock, xanosdk workspace diff and its exit code, the 1.0.8 guard on deploy --to workspace, pulling editor changes back into git, and a scheduled drift check in CI.

**Drift** is a difference between what your workspace runs and what your project says. It
happens when someone edits an object in the Xano editor, or when a change in the project has not
been deployed yet. The Xano SDK records the last point where the two matched, so it can tell you
which side moved.

## The sync baseline

Every command that makes the workspace and the project match records a **baseline** in
`xano/xano.lock`, under `synced`: one content digest per object, kept per workspace and per Xano
branch.

| Command | Records a baseline for |
| - | - |
| `deploy --to workspace` | The Xano branch it landed on. |
| `promote`, when the landing verified | The new Xano branch. |
| `pull workspace` | The Xano branch it read. |
| `init --from workspace` | The live Xano branch. |

Commit the lock after each of these, so everyone (and CI) compares against the same baseline. A
fresh clone of a lock with no baseline still diffs, but cannot say which side changed.

## See what differs, and which side changed

```bash theme={null}
npx xanosdk workspace diff ./xano/index.ts                    # the live Xano branch
npx xanosdk workspace diff ./xano/index.ts --branch v1.4.0    # another Xano branch
npx xanosdk workspace diff ./xano/index.ts --json             # the classified document
```

`workspace diff` exits `0` when the workspace matches your compile and `2` when anything your
project declares differs or is missing, so a script can branch on it. Under `--json` the
differences are sorted by the side that moved since the baseline. An excerpt, for a project whose endpoint was edited in the Xano editor while a table changed in
git (the full document also carries `branch`, `live`, `missing`, `unexpected` and `warnings`):

```json theme={null}
{
  "matched": false,
  "differing": ["query:GET list (apiGroup orders)", "table:order"],
  "baseline": { "at": "2026-10-05T13:36:31.000Z", "by": "pull" },
  "changedThere": ["query:GET list (apiGroup orders)"],
  "changedHere": ["table:order"],
  "changedBoth": [],
  "unclassified": []
}
```

| List | Means | Do |
| - | - | - |
| `changedThere` | Edited in Xano since the baseline. | Pull it into the project before you deploy. |
| `changedHere` | Edited in the project since the baseline. | Deploy it. |
| `changedBoth` | Edited on both sides. | Reconcile by hand. |
| `unclassified` | The baseline cannot tell: the object has no baseline entry, both sides already differed when it was taken, or it is the workspace's settings. A changed URL slug shows only here. | Read the object on both sides. |

With no baseline for the Xano branch, `baseline` is `null` and the four lists are empty;
`differing` still lists what differs.

## The guard on `deploy --to workspace`

Since 1.0.8, `deploy --to workspace` reads the baseline before it writes. If an object it would
overwrite is in `changedThere` or `changedBoth`, it refuses with exit `2` and
`SDK_WORKSPACE_CHANGED`, and lists the objects under `details.objects`:

```bash theme={null}
npx xanosdk deploy ./xano/index.ts --to workspace               # refused: an object changed in Xano
npx xanosdk deploy ./xano/index.ts --to workspace --overwrite   # deploy over it anyway
```

`--overwrite` warns and names what it overwrote. A `--dry-run` refuses nothing: it lists the
objects under `changedInXano`.

The guard counts only what the deploy writes. A merge leaves an object that only the workspace
holds alone, so an object added in Xano is refused over only under `--prune` or `--replace`,
which would delete it. It refuses nothing when the Xano branch has no baseline, over an
`unclassified` difference, or on a `--branch` deploy to a Xano branch that does not exist yet.
It reads the workspace once, before the confirmation, so an edit made after that read is not
caught.

<Warning>
  **The guard covers `deploy --to workspace` only.** `promote`, `tenant deploy` and
  `deploy --to tenant:<name>` do not read a baseline. A promote lands the release on a new Xano
  branch, so an edit made on the old live Xano branch stops serving when you switch live. A tenant
  deploy replaces the tenant, so an edit made there is removed. Run `workspace diff` before you
  promote when anyone on the team edits in Xano.
</Warning>

## Bring an editor change back into git

When `changedThere` lists a change you want to keep, pull it into the project on a git branch
and review it like any other change:

```bash theme={null}
git switch -c pull-editor-changes
npx xanosdk pull workspace                 # rewrite xano/ from what the live Xano branch runs
git diff                                   # review what came back
npm run xano:check
git add -A && git commit -m "pull editor changes"
```

`pull` lists what it will change and asks first, and it refuses a dirty working tree. Add
`--branch <label>` to read a Xano branch that is not live. The pull also records a new baseline,
so the next `workspace diff` reports the workspace and the project in step.

To decide which side should own the backend at all, see
[Source of truth](/ship/source-of-truth).

## Check for drift on a schedule

A scheduled job tells you about an editor change the morning after, not at the next deploy. This
workflow runs `workspace diff` every weekday and fails when anything differs:

```yaml theme={null}
# .github/workflows/xano-drift.yml
name: xano-drift
on:
  schedule:
    - cron: "0 7 * * 1-5"
  workflow_dispatch:

jobs:
  drift:
    runs-on: ubuntu-latest
    env:
      XANO_INSTANCE_URL: ${{ secrets.XANO_INSTANCE_URL }}
      XANO_WORKSPACE_ID: ${{ secrets.XANO_WORKSPACE_ID }}
      XANO_META_TOKEN: ${{ secrets.XANO_META_TOKEN }}
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 20, cache: npm }
      - run: npm ci
      - name: Compare the live Xano branch with main
        run: |
          set +e
          npx xanosdk workspace diff ./xano/index.ts --json > drift.json
          code=$?
          set -e
          jq '{changedThere, changedHere, changedBoth, unclassified}' drift.json
          exit $code
```

Exit `2` fails the job and the log shows which side changed. `changedHere` on a schedule usually
means a merged change that has not been released yet. `changedThere` means someone edited in
Xano. [CI/CD](/govern/ci-cd) covers the credentials this job uses.

## Next

[Rollback and recovery](/govern/rollback-and-recovery): the way back when a release that landed
has to come out.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.