Python-first implementation of Vyral's portable local runtime.
Current version: 0.1.1
Current maturity: prototype
Supported Vyral contract: 0.3.0
This package is implementing the sequence in
design/python-runtime.md. The current
prototype includes:
- the canonical OpenAPI, JSON Schema, and public operation catalog;
- shared Python/.NET conformance fixtures for primitives, snapshots, deterministic embeddings, record CRUD, compound filters, ordering, continuation, RAG plan/manifest hashes, graph record mapping, and durable execution failure/cancellation semantics;
- SQLite collections and records, filtering, lexical and exact-vector search, snapshots, filesystem objects, and persisted traces;
- deterministic embedding providers, retrieval profiles and fusion, reranking seams, ingestion, context and prompt assembly, citations, and evaluation;
- graph import/export, traversal, inspection, doctor, and bounded GraphRAG context expansion;
- the strong transactional SQLite CanonicalStore profile with fences, revisions, idempotency, outbox leasing, migrations, preflight, and hash-verified archives;
- native durable execution with retries, cancellation, events, timers, waits, leases, checkpoints, artifacts, restart recovery, maintenance, policy, and thirteen built-in domain job adapters, including staged cross-store artifact ingestion and receipt-bound collection lifecycle;
- an OpenAPI-derived ASGI host covering all 133 REST operation IDs;
- parity with the shared
vyral.admission.v1contract: aggregate mutations return durable, idempotent jobs withLocation, while non-mutating RAG dry-runs remain synchronous; - a stateless MCP
2026-07-28endpoint with self-describing routing headers, authorization, bounded resources/tools, and durable Tasks; - synchronous APIs plus bounded-executor asynchronous facades; and
- Python-native handler descriptors, an async run context, a replayable local handler harness, and a dependency-free token-safe HTTP external-worker transport covering leases, heartbeats, progress, events, artifacts, checkpoints, waits, completion, and cancellation.
Every required portable-local profile is implemented and reports
available: true. All remain prototype, and fullLocalReady deliberately
remains false pending independent security review and an explicit promotion
decision. The manual Python 3.10–3.12 Linux/macOS/Windows matrix now passes:
every cell builds and installs clean wheel and source artifacts, completes the
cited restart/replay quickstart, verifies the server extra, and contributes to
one consistent aggregate receipt. The combined 0.1.0 → 0.1.1
upgrade/restart rehearsal and the executable adversarial security gate also
pass locally; automated evidence does not substitute for independent review.
Unsupported optional providers are disclosed explicitly; the runtime does not
silently delegate embedded behavior to .NET or to the existing HTTP client.
From the repository root, the shortest path runs directly from source:
./scripts/vyralIt performs the connected retrieval-and-execution proof described below. No
installation or third-party service is required. Use python scripts/vyral on
Windows.
To install the command into an isolated environment instead:
python3 -m venv .venv
. .venv/bin/activate
python -m pip install --editable runtimes/pythonThe installed command is vyral, and running it without arguments performs the
same proof:
vyralThe quickstart creates a three-document, source-backed record corpus without vectors, returns cited lexical context, admits a decorated handler with a stable idempotency key, closes the runtime before dispatch, reopens the same SQLite/filesystem state, and completes the preserved run identity. It reports the queued receipt before the close/reopen boundary so acceptance is not confused with completion.
The quickstart does not invoke an embedding provider. The runtime's available
default local-token-hash provider remains CPU-only, model-free, and requires
no network or downloaded assets; when explicitly selected for vector mechanics,
its lexical-overlap ranking is not a semantic-model quality claim.
Inspect the state and its material limitations independently:
vyral inspectThe quickstart records an ownership marker and will reset only a dedicated directory bearing that marker:
vyral quickstart --resetGenerate one editable application when you are ready to own the code:
vyral init
python ./vyral_app.pyThe generated file uses @vyral(...), admits work with a stable idempotency
key, prints the durable receipt, closes the first runtime before dispatch,
reopens its own .vyral/vyral_app directory, and completes the preserved run.
Running it again reports replayed=true and dispatches no duplicate work. The
generator refuses to overwrite an existing path; the result is ordinary Python
source intended to be edited or absorbed into an application. Leave the visible
RUN_VERSION unchanged to prove replay, then increment it after changing the
work to admit a new run. Use --path or --root when you need non-default code
or state locations.
Use --json with init, quickstart, or inspect for machine-readable
output. vyral-runtime remains a compatibility command alias. Install the
published runtime with python -m pip install vyral without changing the
local commands.
The published REST/MCP host has a separate hash-verified profile for the current release. From a source checkout, create an isolated environment and install the pinned runtime and server dependencies together:
python3 -m venv .venv
. .venv/bin/activate
python -m pip install --require-hashes -r runtimes/python/requirements-server.lockThis profile is intentionally release-specific. Regenerate it when changing
the published Python version or its server extra; development extras and
experimental integrations remain separate, opt-in dependency choices.
From a source checkout, run the generated application through the same launcher that created it so no editable installation is required:
./scripts/vyral init
./scripts/vyral run ./vyral_app.pyAfter installation, the equivalent commands are vyral init and
vyral run ./vyral_app.py.
The quickstart JSON includes measured firstCitationMs, durableReceiptMs,
restartRecoveryMs, and completedMs milestones. Artifact qualification runs
the installed wheel and sdist through the generated editable application, its
idempotent rerun, the connected quickstart, a second-process quickstart replay,
independent inspection, and marker-bounded reset. Each supported platform cell
rejects either installed path if package installation through its first useful
result takes more than five minutes.
from vyral_runtime import VyralRuntime
runtime = VyralRuntime()
status = runtime.readiness().to_dict()
print(status["runtimeVersion"])
print(status["contract"]["operationCount"])
print(status["fullLocalReady"])from vyral_runtime import VyralRuntime
with VyralRuntime.open_local("./.vyral") as runtime:
runtime.records.create_collection({"name": "notes"})
runtime.records.upsert_record(
"notes",
{
"id": "hello",
"partitionKey": "local",
"content": {"text": "Hello from the Python runtime"},
},
)Python handlers use the same authoring surface in the local harness and over the remote external-worker protocol. The decorator is deliberately thin: it builds the existing portable descriptor and handler objects without adding a workflow graph, scheduler, or alternate retry model.
from vyral_runtime import (
ExecutionRunContext,
ExecutionRunResult,
execution_plugin,
vyral,
)
@vyral(
"example.echo",
plugin="example.plugin",
max_attempts=3,
)
async def echo(context: ExecutionRunContext) -> ExecutionRunResult:
await context.record_event("log", message="handler started")
return ExecutionRunResult.succeeded_result({"received": context.run.payload})
plugin = execution_plugin(
"example.plugin",
name="Example",
version="1.0.0",
handlers=(echo,),
)The decorated handler remains directly awaitable for focused tests. Explicit
execution_handler(...), DelegateExecutionHandler, and
StaticExecutionPlugin construction remains available when descriptor-oriented
naming or dynamic registration is clearer. Handler and plugin IDs remain
explicit because they are durable contract identities; renaming or moving a
Python function must not silently create a different operation.
vyral_runtime.integrations.ripgrep provides a bounded, read-only experiment
for code, Markdown, and other safely accessible text sources. It uses a static
root and glob allowlist, fixed-string queries over standard input, source
revision citations, and strict resource limits. It is not part of the stable
wire contract and is not automatically exposed through REST or MCP. Retained
comparison evidence supports it for exact identifiers and phrases in current
local sources when maintaining a duplicate index is not worthwhile. Use Vyral
lexical retrieval for reordered terms, prefixes, record filters, tenant
boundaries, snapshots, and lower post-index query latency. See the
source-native retrieval guide
and comparison receipt.
Install the optional extropic extra to place a registered Python workload
behind the same Vyral execution lifecycle:
python -m pip install --editable "runtimes/python[extropic]"from vyral_runtime import ExecutionRunContext, ExecutionRunResult, vyral
from vyral_runtime.integrations.extropic import (
ExtropicAdapterOptions,
ExtropicExecutionAdapter,
)
def simulate(payload):
return {"seed": payload["seed"], "samples": payload["samples"]}
extropic = ExtropicExecutionAdapter(
"example.simulation.v1",
simulate,
options=ExtropicAdapterOptions(tier="l4", require_seed=True),
)
@vyral("example.simulate", plugin="example.extropic", max_attempts=3)
async def run_simulation(
context: ExecutionRunContext,
) -> ExecutionRunResult:
return await extropic.execute(context)Vyral retains the provider job id, safe status, bounded logs, and replay state;
it never persists Extropic credentials or upload grants. Because Extropic 0.5
does not accept an idempotency key during job creation, a lost create response
fails closed and is not resubmitted automatically. Known provider jobs are
reconnected and retried by id. The integration is a prototype, remains outside
the adapter qualification matrix, and makes no Z1 support claim. Registered
workloads should be self-contained plain Python functions; Vyral serializes
those functions by value, while third-party imports must exist in Extropic's
sandbox. A pinned extropic-torx extra and
examples/python/extropic_torx_workload.py
provide a real, credit-free Torx packaging proof on Python 3.11 or newer. See the
Extropic execution guide for the
complete lifecycle and current boundaries.
| Goal | Install |
|---|---|
| Talk to a running .NET or Python Vyral host | vyral-client |
| Run Vyral in-process without a server or .NET | vyral |
| Run a Python-hosted REST and MCP endpoint | vyral[server] |
| Rehearse the current Extropic/Torx proof locally | vyral[extropic-torx] (Python 3.11+) |
The existing vyral-client distribution remains the supported lightweight
client for a running Vyral server. vyral is a separate runtime distribution
and does not depend on or route its embedded behavior through that client.
The embedded runtime accepts schema-shaped mappings where convenient, then
returns rich Python models such as VyralRecord, readiness receipts, plans,
and execution state. Use their to_dict() methods when serializing to JSON or
crossing an HTTP, MCP, queue, or persistence boundary. The REST host and
vyral-client expose the canonical JSON wire representation instead; callers
must not depend on Python class names or private object layout as portable
contract.
OpenAPI operation IDs remain the semantic naming authority. Embedded methods
use idiomatic snake_case names and mirror those operation IDs where practical;
a convenience name may improve Python composition but cannot add alternate
wire semantics, defaults, validation, or failure behavior. When exact
cross-runtime correspondence matters, start from the operation ID and its
shared schema or conformance fixture.
The Python runtime's SQLite tables, migration ledger, filesystem layout, and
the corresponding .NET layouts are implementation-private. They are not a
binary compatibility surface, even when the filenames look alike. Do not copy
vyral.sqlite, a CanonicalStore database, or an execution database from one
runtime into the other.
Cross-runtime movement uses documented public envelopes: collection and graph exports, CanonicalStore tenant archives, objects, and execution-owned outputs where supported. Those envelopes and their shared conformance fixtures—not raw SQLite bytes—are the portability contract.
Every runtime construction validates the bundled OpenAPI, JSON Schema, and SDK
catalog. Embedded VyralRuntime() construction does not execute the complete
golden corpus by default, keeping notebook and test composition proportional as
the fixture set grows. Use VyralRuntime(verify_assets=True) when construction
itself must fail closed on both the contract bundle and goldens.
Readiness always executes the bundled goldens. The optional REST/MCP host also
constructs its owned runtime with verify_assets=True, and release
qualification runs the canonical fixture suite separately. This keeps
construction fast without weakening host startup or qualification evidence.
Install the server extra and choose an explicit durable directory:
python -m pip install "vyral[server]"
export VYRAL_API_KEY="replace-with-a-secret"
vyral serve --root ./.vyral --host 127.0.0.1 --port 5220REST is available at the public OpenAPI paths and stateless MCP at /mcp.
API-key hosts accept X-Vyral-Api-Key or a bearer token. Localhost Host/Origin
validation is enabled by default. A non-loopback CLI bind is rejected unless
VYRAL_API_KEY is set; wildcard binds also require one or more explicit
--allowed-host values. Browser deployments opt into exact origins with
--allowed-origin; add --require-explicit-origin to disable MCP's
same-host browser fallback. Add --require-api-key when a loopback host must
also fail closed without credentials. Request access logs are disabled by
default. The SQLite/filesystem composition is a single-node topology even
though each MCP request can be parsed and authorized without session affinity.
See the Python host security guide
before exposing a host beyond a single trusted user or process boundary.
The baseline qualification corpus is 2,000 records with 384-dimensional exact
vectors, a roughly 200-chunk RAG document, and 20 durable jobs. This is a
bounded evidence run for notebook and small local-service use, not an SLA or a
maximum supported corpus. Hosted wall-clock time is recorded rather than used
as a release gate; controlled runners can opt into a limit with
--max-seconds.
From the repository root:
scripts/verify-python-runtime.sh
python3 scripts/verify-python-external-worker-integration.py
scripts/verify-python-runtime-external-worker.sh path/to/vyral.whl
scripts/verify-python-runtime-mcp-conformance.sh path/to/vyral.whl
python3 scripts/verify-python-runtime-security.py path/to/vyral.whl
python3 scripts/verify-python-runtime-upgrade.py \
path/to/vyral-0.1.0.whl path/to/vyral-0.1.1.whl
python3 scripts/benchmark-python-runtime.py
dotnet test tests/Vyral.Tests.Conformance/Vyral.Tests.Conformance.csproj \
--filter 'PortableRuntimeGoldenFixtureTests|PortableExternalWorkerLifecycleFixtureTests'The main verification command runs the full unit suite with branch instrumentation using pinned Coverage.py 7.15.4 and enforces a 77.5% combined line/branch regression floor. The floor is a regression guard, not a claim that every dispatch or error branch is exhaustively tested.
The cross-platform qualification workflow is manual-only:
.github/workflows/python-runtime-qualification.yml. This preserves the
repository's current GitHub-run gate while keeping the promotion matrix
reproducible. Every cell builds both artifacts, installs each into a clean
environment, completes the real cited/restart quickstart, replays it from a
second process, inspects its limitations, and safely resets its owned state.
The HTTPS URL and SHA-256 of a previously qualified 0.1.x wheel are optional
paired inputs: when supplied, the workflow also runs the installed upgrade
gate, which cannot silently self-compare the candidate. The aggregate job then runs
scripts/verify-python-runtime-platform-matrix.py; all nine cells must be
clean, refer to one commit, agree on runtime and contract identity, and carry
passing measured local-experience evidence before the matrix is valid. A run
without a baseline proves the platform matrix but is not upgrade evidence and
does not by itself authorize maturity promotion.