Curiosity

Upgrades and Migrations

How to take a running workspace through a version upgrade, a schema migration, or a dev-to-prod promotion without losing data or surprising users. This page is the runbook.

For backup-side coverage see Backup and restore. For per-deployment-target details see Installation.

Version compatibility

Component Compatible with Notes
Workspace server Workspace data (graph, indexes) NN+2 versions Forward-compatible across two minor versions; schema migrations run automatically.
Curiosity.Library (C# SDK) Workspace server NN+1 Pin to a known-good SDK version in your connector projects.
curiosity (Python SDK) Workspace server NN+1 Same.
Custom endpoints Workspace server N (same version) Re-compile when the server major version changes.
Custom front-end (Tesserae) Workspace server NN+1 Re-upload with UploadNewApplicationInterfaceAsync after major upgrades.

Major versions can break compatibility. Always read the release notes before upgrading across one.

Upgrade procedure

The full procedure, in order. Skip nothing.

1. Read the release notes

What's marked breaking, schema migration, or manual step required? If anything: schedule extra time and a maintenance window.

2. Snapshot

# Force a full backup before doing anything.
curl -X POST "$WORKSPACE_URL/api/admin/backup/run" \
  -H "Authorization: Bearer $ADMIN_TOKEN"

Verify the backup completed and is on durable storage (not just the local disk). See Backup and restore.

3. Stage the upgrade

Run the new version against a staging copy of the production data, ideally same hardware tier. Confirm:

  • The workspace starts cleanly.
  • Schema migrations finish without errors.
  • The admin UI loads.
  • A representative search query returns expected results.
  • The relevance golden set (see Relevance evaluation) doesn't regress.

If anything fails in staging, stop. Open the issue with Curiosity; don't proceed to prod.

4. Drain and stop

# Stop accepting new ingestion writes.
curl -X POST "$WORKSPACE_URL/api/admin/ingestion/pause"

# Wait for in-flight commits to drain. Watch the queue depth in the admin UI.

For zero-downtime upgrades (multi-node deployments), follow the rolling-upgrade procedure in the deployment-target page.

5. Apply

# Docker
docker pull curiosity/workspace:NEW_VERSION
docker compose up -d

# Kubernetes
kubectl set image deployment/workspace workspace=curiosity/workspace:NEW_VERSION

# Bare metal
systemctl stop curiosity-workspace
# install new package
systemctl start curiosity-workspace

6. Verify migrations

The workspace logs every schema migration on startup. Tail the logs:

docker logs -f workspace 2>&1 | grep -E '(migrate|MIGRATE|error|ERROR)'

Wait until the log shows "Workspace ready" before allowing traffic.

7. Smoke-test

  • Sign in as admin.
  • Run a small search query.
  • Run the relevance golden set against the upgraded instance.
  • Spot-check one node from each major type.

8. Resume ingestion

curl -X POST "$WORKSPACE_URL/api/admin/ingestion/resume"

9. Observe

For the next 24 hours, watch:

  • Error rate on endpoints (/api/endpoints/metrics/all) — should stay near baseline.
  • Latency P95 — may rise briefly while indexes warm.
  • Ingestion throughput — should return to baseline within minutes.

Rollback

If something goes wrong before traffic returns, rolling back is straightforward:

  1. Stop the new version.
  2. Restore the snapshot from step 2.
  3. Start the previous version (docker pull curiosity/workspace:OLD_VERSION).
  4. Resume ingestion.

If something goes wrong after traffic has resumed, you have a harder choice:

  • No data was lost (read-only failure): rollback is still safe, but ingestion may need replay from the last checkpoint.
  • Data was written: rolling back to the old snapshot loses that data. Decide whether the failure is worse than the lost data.

This is why staging is mandatory.

Schema migrations

Schema changes come in three flavours:

Additive (safe)

Adding a new node type, edge type, or property. The workspace handles these without downtime.

await graph.CreateNodeSchemaAsync<NewType>(overwrite: false);
await graph.CreateEdgeSchemaAsync("NewEdgeType");

Existing data is unaffected. New fields are null on old nodes until back-filled.

Compatible (safe with re-index)

Adding an index, changing an analyzer, switching an embedding model. The workspace stays up; the index rebuilds in the background.

# Watch the rebuild
curl "$WORKSPACE_URL/api/admin/indexing/status"

See Reindexing and re-embedding.

Breaking (requires migration)

Removing a property, renaming an edge, changing a key. These are real migrations:

  1. Add the new shape alongside the old. Don't drop yet.
  2. Migrate data in a connector or scheduled task: read old, write new.
  3. Update consumers. Endpoints, front-end, AI tools must use the new shape.
  4. Verify in staging with the full golden set.
  5. Drop the old shape. A separate release.

Breaking changes split across at least two releases — usually three to keep rollback safe.

Promoting from dev to prod

Use definitions to ship schema, indexes, endpoints, UI, and NLP config; use connectors to fill data.

// In dev
await using (var stream = await devGraph.ExportWorkspaceDefinitionAsync())
    await SaveAsync(stream, "workspace-2026-01-15.def");

// In prod (or staging)
var importResult = await prodGraph
    .ImportWorkspaceDefinitionsAsync(File.OpenRead("workspace-2026-01-15.def"));

The .def file is version-controllable. Commit it; promote like any other artifact. Data is not exported — only configuration. Run your connectors against prod to populate data.

Data migration between workspaces

If you need to move actual nodes/edges (not just configuration) between workspaces:

  1. Export source data through a connector that reads from the source workspace's API and writes to the target. There is no built-in "graph dump" — use a connector for traceability.
  2. Match schemas first. Import definitions before data; the target must accept the data shape.
  3. Migrate users last. SSO mappings differ between environments; map users at the destination, not at the source.
  4. Cut traffic. During the final cutover, freeze writes on source, copy the delta, point clients at target.

CI/CD for workspaces

The endgame: every change to the workspace's configuration is a PR, every promotion is a CI pipeline run.

# .github/workflows/promote.yml
on:
  push:
    branches: [main]
    paths: ["workspace-definitions/**"]

jobs:
  promote-to-staging:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Import definition to staging
        env:
          CURIOSITY_URL:   ${{ secrets.STAGING_URL }}
          CURIOSITY_TOKEN: ${{ secrets.STAGING_TOKEN }}
        run: dotnet run --project tools/import-definition -- workspace-definitions/workspace.def
      - name: Run golden set
        run: python tools/run-golden-set.py --workspace staging

Staging green → manual approval → prod.

Common pitfalls

  • Upgrading prod before staging. Always staging first. No exceptions.
  • No backup before upgrade. "It's a minor version" until it isn't.
  • Skipping the golden-set rerun. Schema migrations can quietly shift relevance.
  • Forgetting front-end re-upload. A new server version with an old UI bundle works sometimes and breaks subtly otherwise.
  • One-step breaking migrations. Always two phases — add new, migrate, drop old. Don't try to do it atomically.
  • Definitions out of source control. A workspace whose configuration drifts in-place is unauditable.

Where to go next

© 2026 Curiosity. All rights reserved.
Powered by Neko