---title: "Persistence And Migrations"description: "How executor stores source state today and how migration logic is intended to work."---Executor persistence is designed around durable runtime state, not just CRUD storage.That means the database stores:- installation and workspace identity- source bindings- recipes and revisions- raw documents- schema bundles- operations- credentials and auth sessions- executions and interactions## Why the storage is layeredThe storage model is layered because these concerns have different reuse and ownership semantics.Examples:- raw documents are provenance- revisions are compiled materializations- operations are the hot-path query surface- source bindings are workspace-local- credentials are private and actor-scopedTrying to collapse those into one table or one document shape causes churn quickly.## What is normalized vs serializedExecutor intentionally uses both relational columns and serialized JSON.Normalized:- ownership- ids and references- search and lookup fields- status and visibility- auth/session ownershipSerialized:- adapter-owned binding config- source config blobs- provider metadata- some session payloadsThis is deliberate.The rule is to normalize what the core system queries and enforces, and serialize what belongs to adapter-specific contracts.## Migrations philosophyThe intended migration model is:- SQL migrations move structural schema into the new shape- one-shot code migrations repair or rebuild data that SQL cannot derive sanely- steady-state runtime paths should not carry compatibility branches foreverThat last point matters.Compatibility logic belongs in upgrade code, not in normal reads and writes.## What is still acceptable in migration codeOne-shot rebuilds are still acceptable when:- the old data shape is structurally valid but not yet in the best compiled form- the rebuild can be derived from already-stored documents- the logic is ledgered and run onceWhat is not acceptable:- provider-specific legacy fallbacks in steady-state runtime logic- carrying old columns indefinitely because one path still reads them## Why this supports extensibilityThis persistence shape is meant to let new integrations land in one of two ways:- add a new adapter that compiles into the same source model- add a genuinely new top-level primitive if the thing is not really a sourceThat is how executor avoids turning every new integration into another schema redesign.
---
title: "Persistence And Migrations"
description: "How executor stores source state today and how migration logic is intended to work."
---
Executor persistence is designed around durable runtime state, not just CRUD storage.
That means the database stores:
- installation and workspace identity
- source bindings
- recipes and revisions
- raw documents
- schema bundles
- operations
- credentials and auth sessions
- executions and interactions
## Why the storage is layered
The storage model is layered because these concerns have different reuse and ownership semantics.
Examples:
- raw documents are provenance
- revisions are compiled materializations
- operations are the hot-path query surface
- source bindings are workspace-local
- credentials are private and actor-scoped
Trying to collapse those into one table or one document shape causes churn quickly.
## What is normalized vs serialized
Executor intentionally uses both relational columns and serialized JSON.
Normalized:
- ownership
- ids and references
- search and lookup fields
- status and visibility
- auth/session ownership
Serialized:
- adapter-owned binding config
- source config blobs
- provider metadata
- some session payloads
This is deliberate.
The rule is to normalize what the core system queries and enforces, and serialize what belongs to adapter-specific contracts.
## Migrations philosophy
The intended migration model is:
- SQL migrations move structural schema into the new shape
- one-shot code migrations repair or rebuild data that SQL cannot derive sanely
- steady-state runtime paths should not carry compatibility branches forever
That last point matters.
Compatibility logic belongs in upgrade code, not in normal reads and writes.
## What is still acceptable in migration code
One-shot rebuilds are still acceptable when:
- the old data shape is structurally valid but not yet in the best compiled form
- the rebuild can be derived from already-stored documents
- the logic is ledgered and run once
What is not acceptable:
- provider-specific legacy fallbacks in steady-state runtime logic
- carrying old columns indefinitely because one path still reads them
## Why this supports extensibility
This persistence shape is meant to let new integrations land in one of two ways:
- add a new adapter that compiles into the same source model
- add a genuinely new top-level primitive if the thing is not really a source
That is how executor avoids turning every new integration into another schema redesign.