Pellet Data Model: Framed Format, PID, Signatures, Provenance, Lockfile
nestr intermediate 6 min read
ELI5
A Pellet is a sealed box with a label. The box’s name is a fingerprint of the box and its label, so you cannot swap contents without the name changing. Signatures and the builder’s receipt are stapled to the outside in an envelope; you can add more staples without changing the box’s name. The lockfile is your shopping list of exact box names.
Technical Deep Dive
Source: nestr-tools/engine internal/pellets/format.go:1-74, internal/pellets/footer.go:53-132, internal/pellets/pellets.go:1-16,122-142, internal/pellets/cas.go:1-25, internal/pellets/lockfile/lockfile.go:1-40 @e9180e4, read 2026-10-25. The previous revision described a {id}.pellet + .meta.json pair with a timestamp ID and a sha256 over compressed bytes; all three were replaced at M2.1 (pellets.go:3-7).
Wire layout (PEL-ART-001)
| Section | Encoding |
|---|---|
| MAGIC | NSTR\0PEL\1 (9 bytes; the spec’s “8 bytes” is a counting error noted in code) |
| HEADER LENGTH | u32 little-endian |
| HEADER | CBOR PelletHeader |
| PAYLOAD | zstd(tar(contents)) |
| FOOTER | CBOR Footer |
PID = pid:blake3:<hex> over MAGIC, HEADER LENGTH, HEADER and PAYLOAD. The footer never contributes (PEL-ART-004), so signatures can be added without changing identity. All hashes are BLAKE3-256; crypto/sha256 is gone from the package.
classDiagram class PelletHeader { +uint32 spec_version +string name +string version +string arch +string platform +bytes payload_blake3 +uint64 payload_size +string created_at +bytes manifest_hash } class Footer { +SignatureBlock[] signature_blocks +ProvenanceAttestation provenance_attestation } class SignatureBlock { +string pid +string algorithm +bytes public_key +IdentityClaim identity +bytes signature +time signed_at +LogEntry log } class ProvenanceAttestation { +InTotoStatement statement +SignatureBlock builder_sig } class LogEntry { +string log_id +uint64 log_index +bytes leaf_hash +bytes[] inclusion_proof +uint64 tree_size } Footer --> SignatureBlock Footer --> ProvenanceAttestation SignatureBlock --> LogEntry ProvenanceAttestation --> SignatureBlockOn disk: content-addressed store (PEL-ART-030..032)
flowchart LR P["pid:blake3:abc123..."] --> D["store/blake3/ab/c1/abc123.../"] D --> F1["pellet: framed file"] D --> F2["extracted/: files 0444, dirs 0555"] D --> F3["meta.json: operator metadata"] F2 --> W["workspace: hardlinks, symlink fallback, never copies"]Extraction and materialisation are pure data operations with no exec syscall anywhere in the package.
nest.lock (PEL-LOCK-*)
Canonical, byte-stable YAML; one per workspace. Each entry is name, pid, provenance_hash, sig_blk_hash, source, version (alphabetical field order); name and version are informational, the PID is the reference. A BLAKE3 self-hash under the frozen domain nestr.pellets.lock.v1 makes tampering detectable.
Key Terms
- PID → content address over everything but the footer.
- SignatureBlock → PEL-SIG-010 record: algorithm, key, identity claim, signature, time, optional log entry.
- ProvenanceAttestation → in-toto v1.0 statement with a SLSA v1.0 predicate, signed by the builder.
- CAS →
<store>/blake3/<aa>/<bb>/<hex>/.
Q&A
Q: What bytes does the PID cover, and what does it deliberately exclude? A: MAGIC, the header length, the CBOR header and the zstd payload. It excludes the footer, so signatures and provenance can be attached without changing the Pellet’s identity.
Q: What does the footer carry, and why can it change without changing the PID? A: Signature blocks (each optionally with a transparency-log entry and inclusion proof) and a SLSA provenance attestation signed by the builder. It is outside the PID by spec (PEL-ART-004), so adding a co-signature does not create a new Pellet.
Q: What does each nest.lock entry commit to? A: The PID plus hashes of the signature block and the provenance, with name, version and source as information. The file carries its own BLAKE3 self-hash.
Examples
nestr install reads nest.lock, fetches pid:blake3:..., recomputes the PID and payload_blake3 before anything else, and refuses with E_PID_MISMATCH if a mirror served different bytes under the same name and version.
neighbors on the map
- CRDT Operation Message adding a new operation type
- FNP CRDT Conflict-Free Merge Semantics understanding CRDT properties and guarantees
- Blake3 Canonical Serialisation Pipeline implementing a non-JS GRACE verifier (Rust, Go, Python)
- Sprite Data Model & Schema creating or validating a new sprite