osp-provider-runtime
Thin, boring runtime harness for OSP providers.
This package handles RabbitMQ message plumbing so provider implementations can focus on business logic.
What it does
- Parses a versioned request envelope.
- Builds provider
RequestContext/ProviderRequestand callsexecute(...). - Dispatches optional read-only provider assessment RPCs without task execution.
- Emits canonical asynchronous task updates and synchronous control-RPC replies.
- Applies explicit ack/requeue/dead-letter decisions.
- Emits structured logs for delivery decisions.
- Supports explicit runtime knobs for prefetch/concurrency/retries/transport timeouts/DLQ.
- Accepts wire-version-7 request envelopes with task identity and cleared Gate fingerprints separated from provider action payload.
- Provides
ProviderIdentityso providers derive the same RabbitMQ queue/binding, routing prefix, and lane name fromPROVIDER,PROVIDER_INSTANCE, and optionalPROVIDER_INSTANCE_ID.
Provider identity
Use a provider family for permissions and API requests, then add an instance only when you need a separate runtime lane:
from osp_provider_runtime import ProviderIdentity
identity = ProviderIdentity(
provider="nrec",
instance="pr",
instance_id=37,
)
identity.routing_prefix # "nrec.pr.37"
identity.request_queue # "provider.nrec.pr.37"
identity.request_binding # "nrec.pr.37"
The common case stays small: ProviderIdentity("vmware", "dev") gives the
vmware.dev routing prefix. Instance IDs refine routing within a lane.
Result payload conventions
Provider results should keep ProviderResult.data focused on the resolved
values for the task. The runtime builds the full update payload envelope:
success: provider outcome flag;falsestill represents a completed no-op or degraded outcomeresolved: yourProviderResult.data(after runtime normalization)provenance: optional; extracted fromProviderResult.data["provenance"]produced_resources: typed provider-native references produced by the operation
Raise a ProviderError when the task should enter a failed lifecycle state.
ProviderResult(success=False) does not turn a completed outcome into a task
failure.
Special keys in ProviderResult.data:
progress_events(list): lifted into the update payload and removed fromresolvedto avoid duplicate TaskEvent rows.provenance(dict): lifted into the top-levelprovenancefield.
Providers may opt into live updates by accepting a keyword-only progress
argument on execute. Use ProgressReporter so the same action also works
without live delivery:
from osp_provider_contracts import ProducedResource, ProviderResult
from osp_provider_runtime import ProgressReporter
def execute(self, request, context, *, progress=None):
reporter = ProgressReporter(progress)
reporter.report("Creating virtual machine", {"stage": "openstack_create"})
vm = create_virtual_machine(request)
return ProviderResult(
success=True,
message="Virtual machine created",
produced_resources=(ProducedResource(kind="vm", target_ref=vm.id),),
data={"progress_events": reporter.events},
)
With a callback, the event is published live and reporter.events stays
empty. Without one, the event is returned through progress_events and the
runtime expands it from the terminal update. This keeps each event on one
delivery path. The callback is best-effort and transport-owned. request.action
is the sole action source.
Cancellation
Cancellation RPCs (.cancellation.rpc) use their own
<request_queue>.cancellation queue, channel and one-worker executor, so a
slow resource listing or console read never delays a cancel.
- Not started: a task delivered to this runtime but still waiting for a
task worker is revoked by the runtime itself; no provider hook is needed.
The RPC answers
supported=truewithcancellation={"cancellable": true, "requested": true}. When a worker picks the delivery up it does not callexecute(): it publishes the normal terminal cancelled update (TaskReporter.STATUS_CANCELLED) and ACKs the delivery. - Running: stopping live work needs provider support. The runtime forwards
to the optional
task_cancellation(task_ref)/cancel_task(task_ref)hooks, which recheck backend state and stop the work (cooperative checkpoints or a native backend cancel). Providers without the hooks report unsupported cancellation. - Unknown to this runtime (still in the broker, or held by another replica): treated like a running task.
requested=true only records the stop request. The task's terminal update is
the confirmation: a revoked task's update follows once a task worker frees up,
and a running task must finish with TaskCancelled(ProviderResult(...)) after
confirmed termination, retaining any surviving resource references.
Revocations live in memory. A revoked result enters the idempotency cache, so a redelivery of the same dispatch replays the cancellation. After a restart, unacknowledged deliveries return and run normally; the orchestrator still waits for a terminal update and a repeated cancel is safe.
Task logging
The runtime binds the task context once around execute() and emits one
task.summary event when the delivery finishes. Provider log sinks must use
JSON serialization so these fields are retained:
task_ref: stable provider execution UUIDdispatch_id: one delivery attemptproviderandtask_typestep,outcome, andduration_mserror.type,error.message, anderror.codewhen applicable
Use task_step("external.operation") around meaningful external operations.
It records the step in the summary trail and logs a full traceback before
re-raising failures. Task code must not bind correlation fields itself.
from osp_provider_runtime import task_step
with task_step("openstack.create"):
server = openstack.create_server(...)
Provider data is preserved inside result.resolved; its keys cannot override
the shared result fields. Runtime extracts the existing progress_events and
provenance metadata into their dedicated result fields.
The orchestrator already owns the original task request. Provider updates do not echo it; HTTP and CLI read models compose request and result when needed.
What it does not do
- No provider framework.
- No plugin system.
- No workflow orchestration.
Install
pip install osp-provider-runtime
Development
env -u VIRTUAL_ENV uv sync --extra dev
hatch shell
hatch run dev:check
hatch run dev:build
hatch run dev:verify
Runtime Knobs
Set these via RuntimeConfig in your provider runtime_app.py:
prefetch_count(default1)concurrency(default1)max_attempts(default5)idempotency_cache_max_entries(default1024)snapshot_cache_ttl_seconds(default3600)dead_letter_exchange(optional)dead_letter_routing_key(optional)heartbeat_seconds(default60)blocked_connection_timeout_seconds(default30)
Update Emission
Use TaskReporter for provider task lifecycle updates. The runtime keeps
transport details compatible with orchestrator consumers.
Docs:
docs/runtime-contract.mddocs/provider-updates.mddocs/migration-task-reporter.mddocs/runtime-upgrade-checklist.mddocs/release-notes-task-reporter.md
Tag and push:
git tag v0.2.0
git push origin v0.2.0
Execution build evidence
The runtime adds payload.execution to live and terminal task updates. Each
actual invocation gets a fresh attempt_id; cached duplicate deliveries replay
the original update and identity. dispatch_id links the invocation to its
orchestrator dispatch. Approval resumes and broker retries can run another build.
Deployment supplies OSP_IMAGE_DIGEST (registry manifest digest),
OSP_GIT_COMMIT (the image's OCI revision label), and optional
OSP_RELEASE_VERSION (a production release tag). Missing values are omitted,
not inferred from moving tags or a package version. The progress callback captures
the attempt evidence before it crosses worker-thread boundaries.
Providers with external data wrap execution in data_snapshot(path) and use
pinned_data_dir() in their data path resolver. The deployment publisher writes
.osp-revision into a retained immutable directory and switches its current
symlink atomically. The runtime records that revision only when such a snapshot
is used. Plain local working copies remain unversioned. This identifies code and
provider data, not secrets, live infrastructure, inventory, or promotion state.
RuntimeConfig.control_concurrency sizes the independent control-RPC worker
pool and its broker prefetch (default 1). Providers expose this as
CONTROL_CONCURRENCY; 4 allows overlapping reads without increasing task
execution concurrency. Delivery logs include worker wait, handler and
finalization timings, correlated with the orchestrator by rpc_id.
Unexpected execution failures
unexpected_provider_error(exc, produced_resources=...) is the public conversion
for adapter boundaries that must attach surviving resources before re-raising.
It uses the runtime's task-step trail for failed_step, marks state uncertain,
and forbids automatic retries. The transport catch-all uses the same function.
Metadata
Release files for osp-provider-runtime 0.10.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| osp_provider_runtime-0.10.1.tar.gz | 75.6 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| osp_provider_runtime-0.10.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 128.7 kB
Release files / osp_provider_runtime-0.10.1.tar.gz
| Download URL | osp_provider_runtime-0.10.1.tar.gz |
|---|---|
| Size | 75.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
a97093e7c5986e75722fb3c59b1693b8d1772252058761ac69e1c919039d3087
|
|
BLAKE2b-256 checksum How to use checksums |
bfd08f03c8ec1875636686df59287077e74b2b5ec143918c166cb858dbd4bc28
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.3
|
Release files / osp_provider_runtime-0.10.1-py3-none-any.whl
| Download URL | osp_provider_runtime-0.10.1-py3-none-any.whl |
|---|---|
| Size | 53.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
264c3bdb9d50066d7f864f83768d1879aa93999ffd2ba25dc21877c0c252868a
|
|
BLAKE2b-256 checksum How to use checksums |
f2334c561acb7262dfebd15db022212f8428e3490da6f26384a2aa257451cd5d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.3
|
Security for osp-provider-runtime 0.10.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.