The .blackbox package format — version 1¶
A .blackbox file is a standard ZIP archive with fixed, deterministic
member metadata and a documented internal layout. You can open it with unzip,
7-Zip, or any ZIP tool; BLACKBOX layers are zstd-compressed TARs and the
human-facing files are YAML/JSON. The format is open: nothing here is
proprietary, and any conforming implementation may read or write it.
Rules that make a package deterministic¶
The same source + manifest + lockfile MUST produce byte-identical bytes:
- ZIP members are stored in a fixed order (below), with a fixed timestamp (1980-01-01 00:00:00), fixed external attrs (mode 0644), no extra fields and no archive comment.
- Layer TARs sort members, zero uid/gid/uname/gname, fix mtime, normalise file modes to 0644 and directory/exec modes to 0755.
- JSON is canonical: UTF-8, object keys sorted, no insignificant whitespace, no floats/doubles (only strings, ints, bools, null, arrays, objects).
- YAML lockfiles are emitted with sorted keys.
Top-level members (fixed order)¶
| # | member | present | contents |
|---|---|---|---|
| 1 | manifest.json |
always | canonical JSON, normalised manifest (§Manifest) |
| 2 | blackbox.lock |
always | YAML lockfile (§Lockfile) |
| 3 | application.tar.zst |
always | deterministic layer: source + assets (§Layers) |
| 4 | dependencies.tar.zst |
if any deps | deterministic layer: installed site-packages / node_modules |
| 5 | layers.json |
always | canonical JSON layer index (§Layers index) |
| 6 | checksums.json |
always | canonical JSON: sha256 of every member except itself and signature.json |
| 7 | signature.json |
if signed | canonical JSON Ed25519 signature (§Signing) |
checksums.json is the integrity root for content; signature.json is the
trust root over that content.
Manifest (manifest.json)¶
Normalised view of the authoring file blackbox.yaml. Keys:
{
"format_version": "1",
"name": "datasift", // ^[a-z0-9][a-z0-9._-]{0,63}$
"version": "0.1.0", // semver-ish
"description": "…",
"publisher": "…",
"runtime": {
"type": "python", // python | node | native
"version": "3.12", // interpreter family; native → "any"
"target": "x86_64-unknown-linux-gnu" // build/run target triple
},
"entrypoint": { "command": "python", "args": ["src/main.py"] },
"permissions": {
"filesystem": { "read": ["./input"], "write": ["./output"] }, // ./-relative only
"network": { "enabled": false },
"process": { "spawn": false }
},
"environment": { "variables": { "KEY": "value" } }, // values are always strings
"interface": { "type": "cli", "port": null }, // cli | web
"requirements": "requirements.txt" // node → "package.json"
}
Entrypoint command by runtime type:
python→ must bepython/python3;args[0]is a script path inside the application layer (resolved there at run time). The bundled interpreter is used, never the host's.node→ must benode;args[0]is a.jspath inside the app layer.native→ must start with./and point at a file in the app layer (the compiled executable).argspass straight through.
Constraint rules (rejected at pack time, not run time)¶
format_versionmust be1.- Filesystem paths must start with
./and contain no..traversal — a package can never request an absolute host path. - Runtime
type/versionmust be among those the build supports. entrypoint.commandmust match the runtime type.
Lockfile (blackbox.lock)¶
Records the exact interpreter identity and every dependency, by hash.
Python:
lock_version: 1
runtime: {type: python, version: "3.12", target: x86_64-unknown-linux-gnu}
packages:
- name: jinja2
version: 3.1.4
sha256: bc5dd2ab… # sha256 of the exact wheel
url: https://files.pythonhosted.org/…/jinja2-3.1.4-py3-none-any.whl
- name: markupsafe
version: 3.0.3
sha256: 26a5784d…
url: https://files.pythonhosted.org/…/markupsafe-…-cp312-…-linux_x86_64.whl
Node (interpreter pinned into the lock too, because the version is major-relative):
runtime:
type: node
version: "22"
target: x86_64-unknown-linux-gnu
version_exact: 22.14.0 # resolved at pack time
asset: node-v22.14.0-linux-x64.tar.gz
url: https://nodejs.org/dist/v22.14.0/node-v22.14.0-linux-x64.tar.gz
sha256: <from upstream SHASUMS256.txt>
packages:
- {name: minimist, version: 1.2.8, integrity: sha512-…} # from package-lock
Native: packages: [] (the executable carries its dependencies).
The lockfile is the reproducibility contract: fetching verifies each
downloaded artifact against its recorded sha256, so a package rebuilds the
same environment even on a machine that never had these versions.
Layer index (layers.json)¶
{
"layers": [
{"kind": "application", "digest": "sha256:…", "file": "application.tar.zst",
"members": 4, "bytes": 784, "exec": ["bin/app"]},
{"kind": "dependencies", "digest": "sha256:…", "file": "dependencies.tar.zst",
"members": null, "bytes": 140902, "exec": []}
]
}
digestissha256:<hex>over the raw layer bytes → the content-addressed key under which BLACKBOX caches and deduplicates the layer.execlists app-layer paths that must bechmod +xon extraction (native binaries, POSIX launch scripts).kindmaps to an install destination:application→ the app tree;dependencies→ the site-packages / node_modules tree.
A layer's digest MUST equal the sha256 of its ZIP member; run verifies this
before expansion, so a tampered layer inside an otherwise-valid archive is
rejected.
Checksums (checksums.json)¶
{"sha256": {
"manifest.json": "<hex>", "blackbox.lock": "<hex>",
"application.tar.zst": "<hex>", "dependencies.tar.zst": "<hex>",
"layers.json": "<hex>"
}}
Excludes checksums.json and signature.json themselves. blackbox verify
fails if any listed member's hash differs.
Signing (signature.json)¶
Ed25519 over the content digest:
content_digest = sha256( canonical_json( { member -> sha256hex } ) ) # checksums, minus signature
{
"alg": "ed25519",
"publisher": "Research Lab X",
"public_key": "-----BEGIN PUBLIC KEY-----…", # PEM, SubjectPublicKeyInfo
"signature": "<hex of Ed25519 signature over content_digest>",
"content_digest": "sha256:…"
}
Verification checks content_digest matches the recomputed one AND the
signature validates under public_key. A local trust store maps a publisher
name → expected key fingerprint, so verify can upgrade VALID to
VALID - trusted publisher. Re-signing a package rewrites the ZIP (members
re-emitted in the fixed order) with signature.json added — still
deterministic given the same inputs.
Safety invariants any reader should enforce¶
- Reject ZIP/TAR members whose resolved path escapes the destination (traversal) or is absolute.
- Never execute content whose hash does not match its layer
digestor a member'schecksums.jsonentry. - Treat the manifest as untrusted input for display and consent; the runtime decides enforcement, not the packaged code.
- Fail closed: a missing required member, a bad checksum, or an invalid signature must stop execution, not warn and continue.