Python consumer
scrollcase_consumer mirrors the local Node consumer without depending on Node or its CLI:
python -m pip install scrollcase-consumerfrom scrollcase_consumer import (
attach_extracted_box,
run_box,
run_extracted_box,
verify_and_extract_box,
verify_extracted_payload,
)
prepared = verify_and_extract_box(
"release.json",
public_key_path="trusted-keys.json",
archive="box.zip",
destination="/srv/boxes/example-1.0.0",
)
result = run_extracted_box(
prepared,
args=("--port", "8080"),
env={"APPLICATION_MODE": "local"},
)The receipt fields use idiomatic snake case (box_id, target_id, required_assets, archive_sha256, environment_report). attach_extracted_box(release, public_key_path=…, root=…) and verify_extracted_payload(release, public_key_path=…, root=…) mirror their Node counterparts exactly, including the attached status and the refusal of a release that commits to no payload digest. run_box performs the same one-shot prepare/run/cleanup composition. Stream arguments accept Python file objects or subprocess constants; the default inherits the parent's streams. On the main Python thread, SIGINT, SIGTERM, and SIGHUP are forwarded and then the previous handlers are restored.
EnvironmentReport, EnvironmentVariableReport, and EnvironmentSourceValue are immutable public models. Their fields mirror the Node structure in snake case; BoxRunResult and every verification receipt include one.
The rest of the public models are the receipts and the box description they carry, all frozen dataclasses:
| Model | What it is |
|---|---|
PreparedBox | The receipt verify_and_extract_box and attach_extracted_box return. It is not a plain record: execution authority is bound to the exact instance, so a field-identical copy is refused |
PayloadVerification | What verify_extracted_payload returns — status, root, identity, and the number of entries checked |
BoxRunResult | The child application's terminal result: exit_code, signal, and a report |
RequiredAsset | One deferred asset the caller must materialize before execution, with the url, size_bytes and sha256 the release signed, and executable when the scroll declared the bit |
BoxTarget | platform, arch, accelerator, and cuda_version where it applies |
BoxRuntime | What runs inside the box: id, and the version and entry_point a runtime with an interpreter has |
BoxExecution | The union of the four entry-point shapes — PythonScriptExecution, PythonModuleExecution, NodeScriptExecution, NativeBinaryExecution. None on a library-only box |
All three runtimes — python, node and native — are implemented here. A box naming an id this release has no adapter for is refused by name, never misread as the runtime its paths resemble.
Every operation that verifies a signed release takes public_key_path or trusted_keys, exactly one, and parse_trusted_keys(source) reads both trust-file shapes from text or bytes — so an application holding its keys in a keyring, an environment variable or a secrets manager verifies against them directly instead of writing key material to a file first. Naming both sources or neither raises a ScrollcaseConsumerError; the Rust TrustAnchors enum makes those two invalid states unrepresentable instead.
The distribution is not a downloader: callers still supply local release, archive, trust-key, destination, and on-demand asset paths. It verifies Ed25519 signatures with cryptography and validates bundled, generated copies of the canonical schemas.