scrollcase turns a declarative recipe into a box: a portable, locked, self-contained Python environment for one operating system and accelerator, packed so it runs somewhere other than where it was built, signed so a consumer can prove what they received, and accompanied by a dependency licence inventory.
It is built on exactly one substrate: pixi solves a committed pixi.lock
against conda-forge, conda-pack
relocates the resulting prefix, and the tree ships inside the box as venv/.
- Locked. The environment is a pure function of a committed
pixi.lock;buildinstalls, it never resolves. - Deterministic. Rebuilding the same commit produces a byte-identical archive: timestamps are normalised, the build time comes from the commit rather than the clock, and the rollout cohort salt is derived rather than random.
- Relocatable. The packed tree is repaired so nothing depends on the build machine's paths: prefix-carrying service files are removed, symlinks dereferenced, and console scripts rewritten to find Python next to themselves.
- Signed. Every release is a signed document — with a local ed25519 key out of the box, or through any external signer you already trust.
- Verified.
verifymirrors what an installing client does: signature, archive size and hash, safe entry names, manifest agreement, and — with--self-test— a real extraction whose own interpreter imports the modules the recipe declares. - Audited.
auditderives a licence inventory per package straight from the lock, and a dependency without a declared licence fails the parse. - Honest about provenance. A box records the commit it was built from. Building outside a git
checkout fails rather than inventing a revision; a dirty tree needs
--allow-dirtyand is recorded as such in the box itself.
- Node.js ≥ 20 for the CLI.
- For real builds:
pixi(at the version the recipe pins) andconda-pack. Point scrollcase at them with--pixi/--conda-packorSCROLLCASE_PIXI/SCROLLCASE_CONDA_PACKif they are not onPATH.scrollcase doctorreports exactly what is missing and how to install it. - Locking, auditing, signing and verifying an existing archive need no toolchain at all.
npm install -g scrollcaseUntil the first release lands on npm, run it from a checkout instead:
git clone https://github.com/Suffro/scrollcase.git
cd scrollcase && npm install && npm linkscrollcase init # scaffold scrollcase.config.json, an example recipe, ignore rules
scrollcase doctor # can this machine build?
scrollcase keygen # create a local ed25519 signing key
scrollcase lock my-recipe # resolve the recipe's pixi manifest into pixi.lock
scrollcase audit my-recipe # licence inventory, derived from the lock
scrollcase build my-recipe # install, self-test, archive, sign
scrollcase verify .scrollcase/dist/<box>.release.json --self-testThe repository ships a working example, examples/hello-box-macos-arm64-metal: a stdlib-only
Python 3.11 environment that exercises the whole pipeline in about a minute and produces a ~48 MB
archive. Its final verify --self-test extracts the box and imports json and sqlite3 with the
interpreter inside it — the check that proves the environment runs somewhere other than where it
was built. See examples/README.md.
| Command | What it does |
|---|---|
init |
Scaffold a config, an example recipe, and ignore rules |
doctor |
Report whether this machine can build a box |
keygen |
Create a local ed25519 signing key |
lock <recipe> |
Resolve the recipe's pixi manifest into pixi.lock |
audit <recipe> |
Dependency licence inventory, derived from the lock |
build <recipe> |
Build, self-test, archive, and sign a box |
verify <release.json> |
Verify signature, archive hash, and layout |
scrollcase help documents every option.
Paths come from the project, not from the tool. A scrollcase.config.json at the project root —
discovered by walking up from the working directory — declares where recipes live and where
builds, artefacts and keys go. Defaults are recipes/ and .scrollcase/{build,dist,keys}, and
every path can be overridden per invocation.
The tool signs with a local ed25519 key so anyone gets verifiable boxes without infrastructure.
An operator with real key custody — a KMS, an HSM, a signing service — plugs it in through
--signer-command: the command receives the payload on stdin and returns the signed document on
stdout. The returned document must echo back the exact payload it was given and its signature is
verified locally, so a signer that substitutes a payload fails the build.
Document kinds are namespaced (<namespace>.release, .channel, .revocations), defaulting to
scrollcase.box. A project with boxes already installed in the field sets --namespace to keep
emitting the kinds its clients recognise.
--weights embed (the default) packs assets into the archive: the box installs with no network
and works air-gapped. --weights on-demand leaves them out and carries their URL, path, size and
SHA-256 in the signed release, so a consumer fetches and verifies them at install time — the
declared hash commits to exactly which bytes the box expects, whatever host serves them.
A recipe may declare a parity block: a check script inside the box, the accelerators to run it
under, and tolerances (absolute, relative, minimumCosine). The tool runs the check once per
accelerator, compares every run against the first, and fails the build on a breach. It catches the
failures a packaging tool is responsible for — the wrong wheels solved in, a CPU-only build
shipped as CUDA, a broken BLAS — while the project owns the check script and what closeness means
for its model.
scrollcase stops at a signed, verified box on disk. Uploading to object storage, serving a registry, promoting or revoking releases, allocating CI runners, and model-specific scientific validation all belong to whoever consumes the tool. The reasoning behind this boundary — and every other decision that looks arbitrary — lives in docs/concepts/design-decisions.md.
npm install
npm test # vitest; no network, no toolchain requiredThe pipeline tests stub the environment solve, so the suite runs anywhere; everything after the solve is the real implementation.
Apache-2.0. The licence covers scrollcase's own source code — the contents of the boxes it builds (interpreters, conda-forge and PyPI dependencies, model source and weights) carry their own licences, which is what the licence audit shipped inside every box exists to record. See NOTICE.