Skip to main content
The facet archive is the self-contained artifact a facet travels in. It carries the embedded facet manifest, every declared text asset, and every declared supplementary file. MCP server declarations contribute no archive entry: they travel inside the embedded manifest. Nothing is fetched at install time. A .facet file is a two-layer container. Both layers are tar archives, and only the inner layer is compressed.

Outer layer

An uncompressed tar with exactly two entries.
JSON
required
facetVersion (the archive format this artifact conforms to), archive (the inner entry name), integrity (the canonical fingerprint as sha256: and 64 hex characters), and files (a map of every inner-archive entry path to its sha256:<hex> hash).The current format is 0.2, where archive MUST be the literal archive.tar.gz and per-entry hashes live in files. The legacy 0.1 format is accepted during the compatibility window and carries an assets map instead. The two are dispatched by exact match on facetVersion, never by ordering, and neither shape is reinterpreted as the other.
gzip
required
The gzip-compressed inner archive carrying the content.
The outer tar MUST be uncompressed, so the build manifest can be read without decompressing the inner archive.

Inner archive

The inner archive decompresses to a deterministic uncompressed tar containing the embedded facet.json, every declared text asset at its conventional path, and every declared supplementary file at its declared path. Entries MUST be sorted lexicographically by path. The entry set is derived from the embedded manifest. There is no recursive discovery, so archive membership stays explicit.

Determinism

Tar metadata MUST be fixed so build output is byte-identical across builds, machines, and platforms: mtime 0, uid 0, gid 0, mode 644, and empty user and group names. The integrity hash covers these bytes, so any producer that wants its artifacts to interoperate MUST use the same metadata. The canonical fingerprint is the SHA-256 of the uncompressed inner tar. Gzip output is never hashed, because different gzip implementations produce different bytes that decompress to identical tars. See the Integrity Model. A verifier MUST recompute the fingerprint by decompressing and hashing. The build manifest’s own integrity is a claim to check, never a value to trust.

Content rules

A verifier MUST reject an archive that violates any rule below.

1. Raw header validation

Both tar layers MUST be validated at the raw-header level, before any path-keyed map is built, so a malformed entry cannot alias a legitimate one. A verifier MUST reject:
  • duplicate paths, and paths that collide after the portable fold;
  • non-regular entries: symlinks, hard links, directories, and devices;
  • a ustar header checksum mismatch;
  • non-zero bytes after the end-of-archive marker;
  • a name or prefix that does not round-trip as UTF-8;
  • a non-empty ustar prefix field;
  • GNU base-256 size encoding.
The inner archive MUST additionally be in canonical ascending entry order. The outer container is deliberately exempt, so that a verified archive and an installable archive are the same set. Duplicate JSON object members in facet.json and build-manifest.json MUST be rejected before schema validation.

2. Path safety

Every key in files and every inner entry name MUST be a safe, portable relative path. The grammar forbids traversal (..), empty and . segments, absolute paths, drive and URL prefixes, backslashes, NUL and control bytes, the reserved characters < > : " | ? *, Windows device names such as CON and COM1, and a trailing dot or space on any segment. Paths that collide after the portable fold, and any file against directory prefix conflict, are rejected too.

3. Membership

For the current format, the plan derived from the embedded facet.json, the inner tar entry set, and the files keys MUST all be the same set, and each entry’s recomputed hash MUST equal the hash files records. The expected set is derived from the embedded manifest, never from the build manifest, so the embedded manifest is the trust root. A files record cannot expand archive membership: a key naming a path the manifest does not declare is rejected outright. Legacy 0.1 archives are checked by reconciling declared against present entries in both directions, then confirming outer exclusivity against the embedded manifest. This is what keeps the supply-chain guarantee intact: an archive cannot carry an undeclared file that lands on disk at install time.