Python SDK
Python SDK reference
The `verentis` Python SDK gives engine code typed access to the run context, the VFS file API, platform HTTP, and output — with the same surface in Docker and the browser.
Python engines import the SDK with a single line:
from verentis import engine
The package is verentis-engine-sdk (pip install verentis-engine-sdk, Python 3.10+). On import it auto-loads the execution context the platform
injects at /verentis/context.json, so engine is ready to use with no boilerplate.
One SDK, two runtimes
The exact same engine.context / engine.files / engine.http / engine.output / engine.token
surface works whether your engine runs server-side (Docker) or client-side in the browser (Pyodide/WASM).
A script you write once behaves identically in both. In the browser the file/HTTP clients need a scoped
token, which the platform mints for directory programs — see Runtimes and
the execution model.
engine.context
The read-only run context (a property, not a call). Mirrors the C# ExecutionContext.
executionobjectid, mode (request-response / long-running), trigger (user / schedule / event / …),
timeout (seconds), tool.
workspaceobjectid, slug, name.
fileobject | NoneThe target file: path, name, mime_type, size, version, branch. None for runs with no
single target file.
inputobjectarguments (list), environment (dict), parameters (dict or None).
platformobjectapi_url and token (access_token, expires_at, scopes) — the scoped token your file/HTTP calls
use. You rarely read this directly; use engine.files / engine.http.
print(engine.context.execution.id) # exec-uuid
print(engine.context.file.path) # /scripts/transform.py
print(engine.context.input.arguments) # ["--verbose"]
params = engine.context.input.parameters or {}
engine.is_initialized is True when a context is present; reading engine.context without one raises
RuntimeError.
engine.files
Read and write workspace files through the gateway file API, authenticated with the run's scoped token.
Paths are VFS paths (leading / optional); the branch is taken from engine.context.file.branch
(defaults to main).
read(path) → bytesRaw bytes. read_text(path, encoding='utf-8') → str and read_json(path) → Any are convenience wrappers.
write(path, content, *, content_type='text/plain', encoding='utf-8') → dictCreate or overwrite a text file. Returns the created file's metadata.
write_bytes(path, content, *, content_type='application/octet-stream') → dictCreate or overwrite with raw bytes.
write_json(path, data) → dictSerialise data to JSON and write it.
list(path='/', *, page_size=50, page_no=1) → dictList entries under a directory path.
move(from_path, to_path)Move/rename a file.
delete(path)Delete a file.
data = engine.files.read_json("/data/input.json")
result = {"rows": len(data)}
engine.files.write_json("/data/output.json", result)
engine.files.move("/data/tmp.json", "/archive/tmp.json")
Writing needs authoring scopes
Reads need node.file.read; writing/deleting a file exercises the full VFS authoring cascade
(node.node.create/update, node.content.create/update, node.journal.*, node.branch.update,
node.file.delete). Declare what you need in your manifest's spec.permissions. The scoped token is
confined to the run's launch-directory subtree and intersected with the caller's own grants, so a
read-only caller can't write even if the engine asks to.
engine.http
An authenticated HTTP client scoped to the platform gateway — for platform calls beyond files (within your granted scopes). Every request carries the scoped token.
resp = engine.http.get("/v1/some/endpoint", params={"q": "x"})
if resp.is_success:
payload = resp.json()
engine.http.post("/v1/things", json={"name": "example"})
get / post / put / delete / request return a response with .status_code, .content,
.text, .json(), .is_success and .headers. A non-2xx status raises VerentisApiError
(status_code, detail) with RFC 7807 ProblemDetails parsed when present.
engine.http is for platform APIs. To call third-party services, use requests/urllib directly —
in the browser they're transparently backed by the browser's fetch (via pyodide-http), subject to CORS.
engine.output
Report progress, logs, artifacts and the structured result. Server-side these are written to
/verentis/result.json and /verentis/progress.json; in the browser they're captured in-memory and
read back by the host — same API either way.
log(message)Emit a log line (captured as execution logs / streamed for long-running runs).
progress(percent, message=None, phase=None)Report progress 0–100 (polled by the platform for long-running engines).
artifact(path)Declare a VFS path your run produced; included in the final result.
result(data=None, *, status='success', error=None)Write the final structured result.
fail(error, data=None)Convenience for a failed result.
engine.output.log("Processing started…")
engine.output.progress(50, "Half done", phase="transform")
engine.output.artifact("/data/output.json")
engine.output.result({"rows": 42})
engine.token
The scoped token for the run. engine.token.get_token() → str returns the bearer value and
engine.token.scopes → list[str] the granted scopes. You normally don't need this — engine.files and
engine.http use it for you.
For server-side long-running executions, node-bounded user tokens and service-principal tokens renew
automatically shortly before their five-minute expiry. Renewal preserves the original principal, scopes,
workspace, and node boundary; a request that would widen any of them is rejected. Transient proactive
renewal failures retry with bounded backoff while the bearer remains valid, and engine.http retries one
request after a 401 by forcing renewal.
Local development
With no /verentis mount, build an engine manually to test against a real (or local) gateway:
from verentis import VerentisEngine
engine = VerentisEngine.initialize(
api_url="https://api.localtest.me:6500",
access_token="<dev-token>",
workspace_id="<workspace-guid>",
file_path="/scripts/transform.py",
branch="main",
)
See Testing your engine for a mock-context harness that needs no running platform.
Next
Expose typed operations the platform and agents can invoke.