Write the transformation in Python. Synor detects what changed, runs the affected work, and reconciles every owned outcome.
Synor is a local-first Python framework with a Rust execution engine for building reliable data processing pipelines. It can power AI indexing, RAG ingestion, document extraction, knowledge graphs, conventional ETL, and stream-to-store workflows without introducing a separate pipeline DSL.
The closest category is an incremental dataflow and reconciliation framework. Synor can be used as the engine inside an AI ETL system, but it is not limited to AI and it is not a hosted data platform. Models are optional. The core job is to keep derived files, rows, vectors, and graph edges aligned with the inputs and code that produced them, and to publish keyed changes to streams.
[!IMPORTANT] Synor is currently an alpha release intended for local evaluation. The current version is
0.1.0a1.
For a slower conceptual pass through the diagram and execution model, read reading.md.
Most pipelines are straightforward on the first run. The difficult part is keeping outputs correct after a file changes, code is edited, a row disappears, a model is replaced, or a run is interrupted.
Synor handles that lifecycle through three ideas:
That model provides:
@syn.task(cache=True) reuses work whose inputs and
implementation have not changed.Synor is useful wherever derived data must stay synchronized with changing source data:
Read from local files, Amazon S3 and compatible stores, Azure Blob Storage, Google Drive, OCI Object Storage, Postgres, Kafka, and Apache Iggy. Source connectors expose stable keyed items or event streams. Keyed inputs let Synor process only the items that changed.
Use any Python function or library. Synor also includes operations for syntax-aware text and code splitting, local Sentence Transformers embeddings, hosted embeddings and audio transcription through LiteLLM, and entity resolution.
Declare outcomes in:
Several connectors support both source and target roles. A custom target connector can extend the same reconciliation model to another system.
This example turns Markdown notes into JSON records. Each note is an independent work unit, so changing one file refreshes one output. Removing a note removes the JSON file it previously owned.
import json
import pathlib
import synor as syn
from synor.connectors import localfs
from synor.resources.file import FileLike, PatternFilePathMatcher
@syn.task(cache=True)
async def catalog_note(file: FileLike, catalog_dir: pathlib.Path) -> None:
text = await file.read_text()
record = {"name": file.file_path.path.name, "word_count": len(text.split())}
localfs.ensure_file(
catalog_dir / f"{file.file_path.path.stem}.json",
json.dumps(record),
create_parent_dirs=True,
)
@syn.task
async def app_main(notes_dir: pathlib.Path, catalog_dir: pathlib.Path) -> None:
notes = localfs.walk_dir(
notes_dir,
recursive=True,
path_matcher=PatternFilePathMatcher(included_patterns=["**/*.md"]),
)
await syn.spawn_each(catalog_note, notes.items(), catalog_dir)
app = syn.App(
syn.AppConfig(name="NoteCatalog"),
app_main,
notes_dir=pathlib.Path("./notes"),
catalog_dir=pathlib.Path("./catalog"),
)
Run the app twice. The first run creates the catalog; the second reuses settled work. Edit one note and only its component runs again.
synor update main.py
The same ownership model scales from one JSON file per note to many chunks per document, rows in a warehouse, vectors in a search index, or relationships in a graph.
The normal App.update() API runs the native incremental engine directly.
SynorRuntime and the CLI add an opt-in control plane for teams that need to
inspect and govern a run:
synor doctor main.py --offline
synor plan main.py --offline
synor diff main.py --offline
synor update main.py --offline
synor explain main.py --offline
plan and diff use the engine preview path and do not apply target actions.
Preview still executes ordinary pipeline Python, so it is not a general
side-effect sandbox. Controlled runs can provide:
export SYNOR_STATE_KEY="$(synor state-key)"
synor replay .synor/runs/<run-id>/replay.json --offline
synor lock main.py
synor package main.py --output pipeline.synor
synor dashboard
The control plane runs beside the engine. The LMDB database remains authoritative for fingerprints, memoization, component ownership, and target reconciliation. See the documentation on controlled runs, trustworthy execution, and provable index revocation for the exact guarantees and limits.
The smallest useful demonstration is the local note catalog. It reads Markdown notes and maintains one JSON record per note without a database server, model API, or network request.
. "$HOME/.cargo/env"
uv sync --group build-test
uv run maturin develop
cd examples/local_note_catalog
../../.venv/bin/synor update main.py --offline
Run the final command twice. The first run creates the catalog. The second run
reuses both settled work units. Edit notes/deploy.md, run it again, and only
that note is refreshed.
| Example | What it demonstrates |
|---|---|
| Local note catalog | Service-free incremental processing and cleanup |
| Text embedding | Markdown to chunks, local embeddings, and pgvector |
| Postgres source | Incremental row enrichment from one table to another |
| Manual extraction | PDF parsing and typed LLM extraction |
| Docs to knowledge graph | LLM-extracted nodes and relationships in Neo4j |
| CSV to Kafka | Catch-up and live stream publishing |
| Provable index revocation | Governed suppression, cleanup, and evidence |
More examples cover image and code search, audio transcription, recommendation, entity resolution, cloud object stores, warehouses, graph databases, and multiple vector stores.
spawn_each are async.python/synor/ Python API, connectors, resources, and operations
rust/ Incremental engine and Python bindings
examples/ Runnable pipelines, starting with local_note_catalog
docs/ Documentation site and the Synor identity system
skills/synor/ Bundled coding-agent guidance
Build and validate the local package:
uv sync --group build-test
uv run maturin develop
cargo test --workspace
uv run mypy
uv run pytest python/
cd docs && npm run build
Start with the local note catalog, read what Synor does, then use the second-run model and connector overview as the map for the rest of the project.
4 commits
2 commits
Python
56.1%
Rust
43.5%
Write the transformation in Python. Synor detects what changed, runs the affected work, and reconciles every owned outcome.
Synor is a local-first Python framework with a Rust execution engine for building reliable data processing pipelines. It can power AI indexing, RAG ingestion, document extraction, knowledge graphs, conventional ETL, and stream-to-store workflows without introducing a separate pipeline DSL.
The closest category is an incremental dataflow and reconciliation framework. Synor can be used as the engine inside an AI ETL system, but it is not limited to AI and it is not a hosted data platform. Models are optional. The core job is to keep derived files, rows, vectors, and graph edges aligned with the inputs and code that produced them, and to publish keyed changes to streams.
[!IMPORTANT] Synor is currently an alpha release intended for local evaluation. The current version is
0.1.0a1.
For a slower conceptual pass through the diagram and execution model, read reading.md.
Most pipelines are straightforward on the first run. The difficult part is keeping outputs correct after a file changes, code is edited, a row disappears, a model is replaced, or a run is interrupted.
Synor handles that lifecycle through three ideas:
That model provides:
@syn.task(cache=True) reuses work whose inputs and
implementation have not changed.Synor is useful wherever derived data must stay synchronized with changing source data:
Read from local files, Amazon S3 and compatible stores, Azure Blob Storage, Google Drive, OCI Object Storage, Postgres, Kafka, and Apache Iggy. Source connectors expose stable keyed items or event streams. Keyed inputs let Synor process only the items that changed.
Use any Python function or library. Synor also includes operations for syntax-aware text and code splitting, local Sentence Transformers embeddings, hosted embeddings and audio transcription through LiteLLM, and entity resolution.
Declare outcomes in:
Several connectors support both source and target roles. A custom target connector can extend the same reconciliation model to another system.
This example turns Markdown notes into JSON records. Each note is an independent work unit, so changing one file refreshes one output. Removing a note removes the JSON file it previously owned.
import json
import pathlib
import synor as syn
from synor.connectors import localfs
from synor.resources.file import FileLike, PatternFilePathMatcher
@syn.task(cache=True)
async def catalog_note(file: FileLike, catalog_dir: pathlib.Path) -> None:
text = await file.read_text()
record = {"name": file.file_path.path.name, "word_count": len(text.split())}
localfs.ensure_file(
catalog_dir / f"{file.file_path.path.stem}.json",
json.dumps(record),
create_parent_dirs=True,
)
@syn.task
async def app_main(notes_dir: pathlib.Path, catalog_dir: pathlib.Path) -> None:
notes = localfs.walk_dir(
notes_dir,
recursive=True,
path_matcher=PatternFilePathMatcher(included_patterns=["**/*.md"]),
)
await syn.spawn_each(catalog_note, notes.items(), catalog_dir)
app = syn.App(
syn.AppConfig(name="NoteCatalog"),
app_main,
notes_dir=pathlib.Path("./notes"),
catalog_dir=pathlib.Path("./catalog"),
)
Run the app twice. The first run creates the catalog; the second reuses settled work. Edit one note and only its component runs again.
synor update main.py
The same ownership model scales from one JSON file per note to many chunks per document, rows in a warehouse, vectors in a search index, or relationships in a graph.
The normal App.update() API runs the native incremental engine directly.
SynorRuntime and the CLI add an opt-in control plane for teams that need to
inspect and govern a run:
synor doctor main.py --offline
synor plan main.py --offline
synor diff main.py --offline
synor update main.py --offline
synor explain main.py --offline
plan and diff use the engine preview path and do not apply target actions.
Preview still executes ordinary pipeline Python, so it is not a general
side-effect sandbox. Controlled runs can provide:
export SYNOR_STATE_KEY="$(synor state-key)"
synor replay .synor/runs/<run-id>/replay.json --offline
synor lock main.py
synor package main.py --output pipeline.synor
synor dashboard
The control plane runs beside the engine. The LMDB database remains authoritative for fingerprints, memoization, component ownership, and target reconciliation. See the documentation on controlled runs, trustworthy execution, and provable index revocation for the exact guarantees and limits.
The smallest useful demonstration is the local note catalog. It reads Markdown notes and maintains one JSON record per note without a database server, model API, or network request.
. "$HOME/.cargo/env"
uv sync --group build-test
uv run maturin develop
cd examples/local_note_catalog
../../.venv/bin/synor update main.py --offline
Run the final command twice. The first run creates the catalog. The second run
reuses both settled work units. Edit notes/deploy.md, run it again, and only
that note is refreshed.
| Example | What it demonstrates |
|---|---|
| Local note catalog | Service-free incremental processing and cleanup |
| Text embedding | Markdown to chunks, local embeddings, and pgvector |
| Postgres source | Incremental row enrichment from one table to another |
| Manual extraction | PDF parsing and typed LLM extraction |
| Docs to knowledge graph | LLM-extracted nodes and relationships in Neo4j |
| CSV to Kafka | Catch-up and live stream publishing |
| Provable index revocation | Governed suppression, cleanup, and evidence |
More examples cover image and code search, audio transcription, recommendation, entity resolution, cloud object stores, warehouses, graph databases, and multiple vector stores.
spawn_each are async.python/synor/ Python API, connectors, resources, and operations
rust/ Incremental engine and Python bindings
examples/ Runnable pipelines, starting with local_note_catalog
docs/ Documentation site and the Synor identity system
skills/synor/ Bundled coding-agent guidance
Build and validate the local package:
uv sync --group build-test
uv run maturin develop
cargo test --workspace
uv run mypy
uv run pytest python/
cd docs && npm run build
Start with the local note catalog, read what Synor does, then use the second-run model and connector overview as the map for the rest of the project.
4 commits
2 commits
Python
56.1%
Rust
43.5%