Embedded in a host application¶
Embedded mode skips the HTTP surface entirely. You import zeroth.runtime into
your own FastAPI app, CLI, worker, or notebook and drive the orchestrator
directly. Use it when you already have a deployable process and just want
zeroth-core as an in-process library.
Use case¶
- You already run a FastAPI service and want to expose graph runs on your own routes
- A CLI tool that invokes graphs on demand
- A background worker that runs a graph per job
- Notebook or scripting usage where an extra HTTP hop is overhead
Prerequisites¶
- Python 3.12+
zeroth-coreinstalled in the host project's virtualenv- An async context to call the orchestrator from
Install¶
pip install zeroth-core
# Or, with optional backends matching your host app
pip install "zeroth-core[memory-pg]"
Minimal pattern¶
import asyncio
from zeroth.platform.config.settings import get_settings
from zeroth.service.bootstrap.factory import bootstrap_service
from zeroth.platform.storage.factory import create_database
async def main() -> None:
# Settings are read from ZEROTH_* env vars (and any .env file present).
settings = get_settings()
database = await create_database(settings)
bootstrap = await bootstrap_service(database, deployment_ref="default")
orchestrator = bootstrap.orchestrator
# `bootstrap` exposes every wired subsystem — graphs, runs, audit, etc.
# Submit runs via the orchestrator or via the runs service.
runs = bootstrap.runs
run = await runs.start_run(
graph_ref="my-graph@v1",
inputs={"query": "hello"},
)
print(run.run_id)
if __name__ == "__main__":
asyncio.run(main())
bootstrap_service is the same wiring function used by the HTTP entrypoint,
so every subsystem (orchestrator, runs, audit, memory, guardrails, secrets)
is fully initialized. See the
Python API Reference — service for
the full Bootstrap dataclass surface.
FastAPI host app¶
If your host app wants to expose the zeroth-core HTTP routes alongside its
own, mount the service sub-app:
from fastapi import FastAPI
from zeroth.service.app import create_app
from zeroth.service.bootstrap.factory import bootstrap_service
from zeroth.platform.storage.factory import create_database
from zeroth.platform.config.settings import get_settings
async def lifespan(app: FastAPI):
settings = get_settings()
db = await create_database(settings)
bootstrap = await bootstrap_service(db, deployment_ref="default")
app.state.zeroth = create_app(bootstrap)
yield
host = FastAPI(lifespan=lifespan)
You can then mount app.state.zeroth under a prefix (host.mount("/zeroth",
app.state.zeroth)) or import individual routers from
zeroth.service.api.run_api, approval_api, etc.
Storage and migrations¶
- SQLite: no migration step required; the schema is created lazily on first boot.
- Postgres: call
zeroth.service.bootstrap.migrations.run_migrationswith the database URL before the first boot; it locates the bundled Alembic scripts.
Common gotchas¶
- Event loops:
bootstrap_serviceis async. Call it from inside an existing loop — do not wrap it inasyncio.run()repeatedly from synchronous code, or you will churn the database pool. - Double bootstrap: build the
Bootstraponce per process and share it. Each bootstrap opens its own DB connections. - Logging:
zeroth-coreuses the stdlibloggingmodule. Configure the root logger in your host app; do not fight it with a second handler set. - Settings precedence: env vars win over
.env. If the host app already loads.envviapython-dotenv, make sureZEROTH_*vars are loaded beforeget_settings()is called.