SKEEP-007: Cartridge blueprints
Status: Draft
Audience: cartridge-spec maintainers; authors of cartridges, adapters and cartridge tooling; anyone who wants to publish a cartridge without redistributing its weights
Created: 2026-09-20
Tracking issue: SKaiNET-cartridge#6
Decision record: ADR-015
Numbering: SKEEP numbers come from the SKaiNET series; this proposal lives here because it changes this specification.
Summary
A cartridge bundles its weights and compiled model artifacts, pinned by digest in a signed manifest. That is what a host should load and what a public source repository should not contain. This proposal adds the missing half: a blueprint โ identity, a descriptor template, sources pinned by revision and digest with their license and redistribution class, a pinned toolchain, targets, declared inputs, ordered steps and an output mapping โ and materialization, which executes a blueprint against a materialization profile and yields an ordinary cartridge whose manifest records the blueprint as provenance. Normative text: Blueprints. Spec v0.4; additive.
Motivation
-
The first cartridges cannot be published as they are. The Moonshine v2 streaming ASR and FunctionGemma NLU cartridges exist and work, but their repositories either contain hundreds of megabytes of compiled modules and parameter archives, or reference them out of band. Neither shape is publishable: the weights have a publisher and terms (MIT for Moonshine’s streaming checkpoints, a non-commercial community license for its legacy non-English models, Gemma’s Terms of Use for FunctionGemma), and re-hosting them drops those terms on the floor.
-
The build loop is unrecorded. "Developing a cartridge" is eight lines of prose. Which upstream revision, which files, how they were converted and quantized, which exporter and compiler versions โ known to whoever built it, written down nowhere a reviewer or a machine can check.
provenance.inputsrecords digests after the fact;provenance.produceris a free string. -
OQ11 has no subject. "Can builds be made reproducible" cannot be tested while "the build" is not a defined object.
-
The open/closed seam already exists one level up and works. Cartridge vs. adapter separates public, app-agnostic code from product policy. The same seam is needed inside the build: a public recipe, and a private place for the target fleet, license acceptances, mirrors, product-specific inputs and signing keys. Without it the only way to build privately is to fork.
Goals
-
A cartridge’s code, interface, descriptor template and complete build recipe can be open source while no weight and no compiled artifact is redistributed.
-
License terms are part of the recipe and are evaluated before anything is downloaded; acceptance is an explicit, recorded act of the party that builds.
-
A materialized cartridge is an ordinary cartridge. Hosts, adapters, the ABI, the threat model and verification are untouched.
-
The descriptor honesty rule survives: a recipe cannot supply
performanceorquality. -
A private build of a public blueprint needs no fork โ only a profile.
-
The lineage source bytes โ recipe โ artifact bytes is auditable from the manifest.
Non-Goals
-
Reproducible builds themselves (OQ11). Blueprints make them testable, not true.
-
A hub, registry or build service. Blueprints are source; materialized cartridges are distributed by the existing modes A and C.
-
Describing how a model was trained. A blueprint starts at a published checkpoint.
-
A general-purpose build system. The step vocabulary is closed and small on purpose.
-
Changing weight-only updates. A
weights-updatestill pins a base cartridge by digest.
Proposed Design
The normative text is Cartridge blueprints. In outline:
The blueprint (blueprint.json)
spec_version, kind: blueprint, id (<task>-<model>-<runtime>, no target), version (of the
recipe), license (of the blueprint’s own content), descriptor_template, sources[],
toolchain{}, targets[], flavors[], inputs[], steps[], outputs[], verification[],
reference_measurements[]. Schema: examples/cartridge-blueprint.schema.json.
Design decisions worth recording:
-
Two pins per source. An immutable publisher revision and a SHA-256 per file. The revision says where to look; the digest is what is trusted. A mirror may replace the first, never the second.
-
Redistribution class on every source โ
allowed,restricted,acceptance-required. It is the one license fact a materializer needs before it acts, and it cannot be derived from an SPDX id alone (a community license with a revenue cap has no SPDX id and is not "proprietary"). -
A closed step vocabulary โ
fetch,convert,quantize,export,compile,build-runtime,measure,pack,sign. A reviewer can see that onlyfetchtouches the network and that numerics change only inquantize(and declared dtype casts inconvert) without executing anything. -
Declared inputs. Anything host- or organization-specific โ a function-calling model’s tool catalog is the motivating case โ is an input with a schema, supplied by the profile. This is the cartridge contract’s app-agnosticism rule applied to the build: product content never enters the blueprint, not even as a sample.
-
The template excludes what cannot be known.
id,version,target,license,performance,qualityare rejected by the schema insidedescriptor_template.reference_measurementsexists so authors can still say what they saw โ as an expectation.
Materialization and the profile
materialization-profile.schema.json: blueprint{id, version, digest, source}, target,
flavors[], cartridge{id?, version}, license_acceptance[], mirrors{}, inputs{},
measurements{}, descriptor_overrides{}, signing{keyid, signer, key_ref}.
The procedure is ten ordered steps (validate โ license check โ fetch + verify โ validate inputs
โ run steps โ verification gate โ resolve descriptor โ compute effective license โ pack โ
sign). Two rules carry most of the weight: a materializer MUST stop at an
acceptance-required source without a matching acceptance record and MUST NOT offer to accept;
and a digest mismatch is fatal.
Manifest
Two optional members under provenance โ blueprint{id, version, digest, source} and
materialization{profile_digest, target, flavors, toolchain, materializer}. profile_digest
pins a profile without publishing it, so a private profile stays private and the build stays
auditable to whoever holds it.
Reference implementation
A Gradle plugin, sk.ainet.cartridge.blueprint, in the blueprints repository, with tasks that
mirror the step kinds and an umbrella materializeCartridge -Pprofile=โฆ. Model-side steps are
Kotlin on the SKaiNET stack (weight loading, conversion, quantization, StableHLO export);
compilation is delegated to IREE tools (StableHLO โ IREE). The specification does not depend on
the plugin.
Requirements
Functional
-
The worked blueprint and profile validate against their schemas; the descriptor resolved from them validates against
cartridge-descriptor.schema.json. -
A blueprint with a
performanceblock in its template, a source file without a digest, or an unknown redistribution class is rejected by the schema. -
Existing descriptors and manifests validate unchanged against the v0.4 schemas.
-
A manifest carrying
provenance.blueprintverifies with the existingverify_manifest.py.
Compatibility & Migration
Additive. Descriptor shape is unchanged; its spec_version accepts 0.3 and 0.4. Manifest
schema v2 gains two optional provenance members. No existing cartridge needs to change, and a
cartridge need not have a blueprint.
Migrating an existing cartridge repository to a blueprint: remove model artifacts and prebuilt
libraries from the tree (and from history, by starting the public repository fresh); describe
the sources and steps in blueprint.json; move device-, fleet- and product-specific content
into a profile and declared inputs; replace measured descriptor fields with
reference_measurements.
Rollout Plan
-
Spec โ this proposal, ADR-015,
blueprints.adoc, the two schemas with a worked pair, manifest provenance, concept-page cross-references, first guides. (this change) -
Materializer โ the Gradle plugin, exercised by a synthetic blueprint in its functional tests.
-
First blueprints โ
nlu-functiongemma-270m-iree, thenasr-moonshine-v2-streaming-iree(the latter first needs its weight conversion ported to Kotlin). -
Proof โ existing private cartridges re-materialized from the public blueprints and shown to pass the same golden checks as the hand-built ones.
Acceptance Criteria
-
All functional requirements above hold in CI.
-
The documentation site builds with the new pages and navigation.
-
Both first blueprints materialize end to end for at least one target each, from a clean machine, by following the published tutorial.
-
At least one private, downstream materialization of a public blueprint exists that required no change to the blueprint.
Risks
-
The recipe drifts from reality if cartridges keep being built by hand. Mitigation: the first blueprints replace, not accompany, the existing build paths.
-
Publisher endpoints change or disappear. Digests keep a mirrored copy trustworthy; they do not make a vanished file reappear. Mirrors are the mitigation and are first-class for that reason.
-
License classification is a judgment.
restrictedvs.allowedis stated by blueprint authors and can be wrong. The materializing party remains responsible; the field makes the question visible, it does not answer it for them. -
A closed step vocabulary may be too small for a backend not yet met (OQ-B1).
-
Compiler availability. A recipe is only as open as its toolchain; a step whose tool is not publicly obtainable must say so, and must document an equivalent public invocation.
Open Questions
OQ-B1 step-vocabulary extension ยท OQ-B2 expected digests per target ยท OQ-B3 signed blueprint releases and their trust role ยท OQ-B4 referencing third-party measurements โ see Cartridge blueprints.
References
-
Blueprints โ normative text
-
Developing a cartridge, Consuming a cartridge (Modes B and C), OQ11