Skip to content

Add 0.2 archive format support with strict raw tar-header validation, versioned build-manifest dispatch, and classified entry verification - #437

Merged
eXamadeus merged 1 commit into
mainfrom
julian/07-20-add_strict_raw_tar-header_validation_0.2_archive_verification_and_structured_archiveverificationfailure_to_the_consumer_bridge
Jul 23, 2026
Merged

Add 0.2 archive format support with strict raw tar-header validation, versioned build-manifest dispatch, and classified entry verification#437
eXamadeus merged 1 commit into
mainfrom
julian/07-20-add_strict_raw_tar-header_validation_0.2_archive_verification_and_structured_archiveverificationfailure_to_the_consumer_bridge

Conversation

@eXamadeus

@eXamadeus eXamadeus commented Jul 20, 2026

Copy link
Copy Markdown
Member

Why

Archive verification needs to support both the legacy 0.1 format and the new 0.2 format, which introduces supplementary (non-asset) files, a files hash map instead of assets, and stricter membership rules derived from the shared archive plan. The consumer bridge lets the CLI and engine accept both formats while no producer yet emits 0.2.

Details

Strict raw tar-header validation (tar-headers.ts) is now applied to both the outer container and the inner archive before any path-keyed structure is built. This rejects duplicate paths, portable aliases (case/Unicode collisions), non-regular entry types (symlinks, directories, PAX/GNU headers), non-canonical names (traversal, absolute, backslash, ustar prefix), and non-zero bytes after the end-of-archive marker. After this validation passes, nanotar's extraction is guaranteed to agree with the validated entry list.

parseFacetArchive now performs raw-header validation before selecting either outer entry, enforces an exact two-entry set (build-manifest.json + archive.tar.gz), and delegates to parseBuildManifestDocument for versioned manifest parsing with duplicate-JSON-member rejection and structured unsupported-facet-version failures. A malformed 0.2 manifest is never retried under 0.1 rules.

validateFacetArchive now returns ValidateFacetArchiveResult (replacing Validated<VerifiedArchive>) with a tagged ArchiveVerificationFailure union covering every expected failure mode: container, invalid-json, duplicate-members, unsupported-facet-version, schema-violation, decompression, integrity, entry-integrity, and validation. The success branch returns a VerifiedFacetArchive tagged by exact archiveVersion (0.1 or 0.2).

Version-dispatched content verification splits into two isolated paths:

  • 0.1 (legacy, frozen): per-asset hash reconciliation against assets, legacy facet-manifest schema, conventional-path outer-exclusivity allowlist, legacy content rules.
  • 0.2 (current): embedded manifest validated under current rules; expected membership derived from planArchiveEntries (the shared archive plan, not the attacker-controlled build manifest); exact three-way set equality among expected paths, observed entries, and files hash-map keys; entries returned as tagged VerifiedEntry values with supplementary content kept as opaque bytes and only primary assets decoded as text.

listVerifiedFiles and verifiedFileHashes provide uniform extraction views over either format so callers (registry download, cache staging) don't branch per version.

cachePutVerified now accepts { integrity, fileHashes } instead of a BuildManifest, making it version-neutral. All call sites pass the version-selected hash map explicitly.

downloadAndExtractFacet now returns DownloadedArchiveInfo ({ integrity, fileHashes }) and surfaces unsupported-facet-version as a typed UNSUPPORTED_ARCHIVE registry error so the CLI can render actionable upgrade guidance instead of a generic network error.

CLI error rendering adds describeVerificationFailure for structured archive failures in publish, and FailureBlock and translateEngineRegistryError handle UNSUPPORTED_ARCHIVE with upgrade guidance.

Immutable fixtures (valid-0.1.facet, valid-0.2.facet) are checked in as compatibility anchors. They pin that future consumers keep accepting today's bytes — regenerating them signals a breaking format change.

Verification

The full test suite covers: immutable fixture round-trips for both versions, raw-header attacks (duplicates, aliases, all non-regular typeflags, traversal, absolute paths, ustar prefix, trailing data), tampered inner archives, missing/extra entries, hash mismatches for both primary and supplementary files, duplicate JSON members in both the build manifest and embedded facet.json, unsupported version dispatch, and the no-legacy-fallback invariant for malformed 0.2 archives. CI runs all protocol and engine consumer tests.


Note

High Risk
Touches archive integrity and verification at a supply-chain boundary with a breaking public API and extensive failure-mode behavior; regressions could accept bad archives or reject valid ones.

Overview
Ships a protocol-only, consumer-first release: verifiers accept legacy 0.1 and current 0.2 .facet archives with exact facetVersion dispatch and no cross-version fallback.

Breaking validateFacetArchive API. Failures are a tagged ArchiveVerificationFailure (failure.code) instead of a flat errors[]. Success returns VerifiedFacetArchive discriminated on archiveVersion (0.1 flat assets vs 0.2 classified entries). New helpers listVerifiedFiles / verifiedFileHashes avoid version branching at call sites.

Stricter parsing before trust. validateRawTarEntries runs on outer and inner tars before path-keyed maps (duplicates, aliases, non-regular entries, bad paths). parseFacetArchive enforces exactly two outer members and versioned parseBuildManifestDocument.

Engine/CLI wiring. Registry download returns DownloadedArchiveInfo and surfaces UNSUPPORTED_ARCHIVE; cache cachePutVerified takes { integrity, fileHashes }; computeDirIntegrity hashes raw bytes for binary supplementary files. Publish/install render structured verification failures and basic upgrade hints.

Release/docs. Adds the protocol changeset and refreshes OpenSpec migration/tasks for staged protocol → registry → adapter → held CLI rollout. Immutable valid-0.1 / valid-0.2 fixtures anchor compatibility tests.

Reviewed by Cursor Bugbot for commit c4e7771. Bugbot is set up for automated code reviews on this repo. Configure here.

@changeset-bot

changeset-bot Bot commented Jul 20, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: c4e7771

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@agent-facets/protocol Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

eXamadeus commented Jul 20, 2026

Copy link
Copy Markdown
Member Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@eXamadeus, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 57 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 5ce27f4a-0915-4087-a507-76b7bfae1a2f

📥 Commits

Reviewing files that changed from the base of the PR and between 98d6750 and c4e7771.

📒 Files selected for processing (35)
  • .changeset/protocol-0-2-consumer-support.md
  • openspec/changes/support-non-asset-files/design.md
  • openspec/changes/support-non-asset-files/proposal.md
  • openspec/changes/support-non-asset-files/tasks.md
  • packages/cli/src/commands/publish/__tests__/publish.test.ts
  • packages/cli/src/commands/publish/index.ts
  • packages/cli/src/tui/views/install/failure-block.tsx
  • packages/cli/src/util/registry-errors.ts
  • packages/engine/src/__tests__/cache.test.ts
  • packages/engine/src/__tests__/run-install.test.ts
  • packages/engine/src/cache/operations.ts
  • packages/engine/src/install/__tests__/materialize-version.test.ts
  • packages/engine/src/install/__tests__/run-add.test.ts
  • packages/engine/src/install/__tests__/run-install.chain.test.ts
  • packages/engine/src/install/__tests__/run-install.receipt.test.ts
  • packages/engine/src/install/__tests__/run-install.test.ts
  • packages/engine/src/install/__tests__/run-remove.test.ts
  • packages/engine/src/install/commit/resolve-git.ts
  • packages/engine/src/install/materialize-version/download-miss.ts
  • packages/engine/src/registry/download.ts
  • packages/engine/src/registry/types.ts
  • packages/protocol/src/__tests__/archive-helpers.ts
  • packages/protocol/src/__tests__/content-hash.test.ts
  • packages/protocol/src/__tests__/fixtures/generate.ts
  • packages/protocol/src/__tests__/fixtures/valid-0.1.facet
  • packages/protocol/src/__tests__/fixtures/valid-0.2.facet
  • packages/protocol/src/__tests__/tar-headers.test.ts
  • packages/protocol/src/__tests__/validate-archive.test.ts
  • packages/protocol/src/build/archive-plan.ts
  • packages/protocol/src/build/content-hash.ts
  • packages/protocol/src/build/tar-headers.ts
  • packages/protocol/src/index.ts
  • packages/protocol/src/integrity/index.ts
  • packages/protocol/src/integrity/validate-archive.ts
  • scripts/smoke/protocol-node.mjs
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch julian/07-20-add_strict_raw_tar-header_validation_0.2_archive_verification_and_structured_archiveverificationfailure_to_the_consumer_bridge

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 64ce981e5e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

// gives Check C teeth: tampered content whose manifest still carries
// the original integrity now fails here.
const recomputed = computeDirIntegrity(stagingDir, Object.keys(buildManifest.assets))
const recomputed = computeDirIntegrity(stagingDir, Object.keys(archive.fileHashes))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Recompute cached archive hashes from raw bytes

For a valid 0.2 archive containing a binary supplementary file, this call reaches computeDirIntegrity, which reads every declared file as UTF-8 text before hashing and rebuilding the tar. Invalid UTF-8 bytes are replaced during decoding, so the recomputed file hashes and archive integrity no longer match the verified files map; handleMiss then reports an integrity failure and refuses to cache/install the archive. This affects the checked-in 0.2 fixture's assets/logo.bin; recomputation must preserve Uint8Array bytes.

Useful? React with 👍 / 👎.

Comment on lines 243 to +245
return {
ok: true,
data: { buildManifest: validated, innerArchiveBytes: new Uint8Array(innerEntry.data) },
data: { manifest: manifestResult.data, innerArchiveBytes: innerBytes },

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep the documented Node smoke test compatible

This public result shape no longer matches the documented Node smoke test: scripts/smoke/protocol-node.mjs still reads result.data.buildManifest for successful parses and result.errors for failures. After building the package, running the advertised node scripts/smoke/protocol-node.mjs therefore fails both parseFacetArchive checks instead of validating the published Node bundle. Update that script to use the tagged data.manifest.manifest and failure result shapes.

Useful? React with 👍 / 👎.

Comment thread packages/engine/src/cache/operations.ts
@greptile-apps

greptile-apps Bot commented Jul 20, 2026

Copy link
Copy Markdown

Greptile Summary

This PR ships consumer-first support for the 0.2 archive format alongside the frozen legacy 0.1 format. It introduces strict raw tar-header validation, versioned build-manifest dispatch with no cross-version fallback, and a new ArchiveVerificationFailure tagged-union failure type for validateFacetArchive (replacing the old errors[] flat array). Engine and CLI are updated to accept DownloadedArchiveInfo from the download path, route UNSUPPORTED_ARCHIVE as a typed registry error, and present upgrade guidance in the install failure block.

  • tar-headers.ts: New raw header walker rejects duplicates, portable aliases, non-regular entries, ustar-prefix paths, non-canonical names, and trailing data before any path-keyed structure is built.
  • validate-archive.ts: validateFacetArchive is now breaking — success returns VerifiedFacetArchive (discriminated by archiveVersion), failures return { ok: false; failure: ArchiveVerificationFailure }. 0.2 verification derives expected membership from planArchiveEntries (not the attacker-controlled build manifest) and enforces exact three-way set equality.
  • Engine & CLI: computeDirIntegrity and cachePutVerified now read raw bytes (fixing the previously-flagged UTF-8 decode issue for binary supplementary files), and translateEngineRegistryError handles UNSUPPORTED_ARCHIVE with upgrade guidance.

Confidence Score: 4/5

Safe to merge for consumers of the protocol package in isolation; the registry upload-verification gateway needs a coordinated update before the new protocol version is deployed to production.

The protocol verification pipeline, the engine download/cache paths, and the CLI error surface all look correct — the binary-file integrity regression flagged in the previous review round has been fixed. The remaining risk is that the registry verify.ts still imports the now-removed VerifiedArchive type and reads result.errors instead of result.failure, already identified in a prior review thread. When the protocol package version bumps, the registry upload-verification gateway will fail to compile.

packages/v0/api/src/routes/facets/publish/verify.ts in the facet-registry repo — still references VerifiedArchive (removed), accesses result.errors (now result.failure), and passes the old flat result shape to toVerifiedArtifact.

Reviews (8): Last reviewed commit: "Add strict raw tar-header validation, `0..." | Re-trigger Greptile

Comment thread packages/protocol/src/build/tar-headers.ts
Comment on lines +1 to 20
import { type AssetType, type ValidationError, validateAssetName } from '@agent-facets/common'
import { planArchiveEntries } from '../build/archive-plan.ts'
import { computeContentHash, INNER_ARCHIVE_NAME, parseFacetArchive, parseInnerArchive } from '../build/content-hash.ts'
import { detectNamingCollisions } from '../build/detect-collisions.ts'
import { validateRawTarEntries } from '../build/tar-headers.ts'
import { validateContentFiles } from '../build/validate-content.ts'
import { validateCompactFacets } from '../build/validate-facets.ts'
import { FACET_MANIFEST_FILE, resolvePromptsFromMap, validateLegacyFacetManifest } from '../loaders/facet.ts'
import type { BuildManifest } from '../schemas/build-manifest.ts'
import {
FACET_MANIFEST_FILE,
resolvePromptsFromMap,
validateFacetManifest,
validateLegacyFacetManifest,
} from '../loaders/facet.ts'
import type { CurrentBuildManifest, LegacyBuildManifest } from '../schemas/build-manifest.ts'
import { FACET_ARCHIVE_VERSION, LEGACY_FACET_ARCHIVE_VERSION } from '../schemas/build-manifest.ts'
import type { FacetManifest } from '../schemas/facet-manifest.ts'
import type { LegacyFacetManifest } from '../schemas/facet-manifest-legacy.ts'
import type { AssetIntegrityFailure, FacetIntegrityFailure } from './types.ts'
import { verifyHash } from './verify.ts'

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Registry consumer breaks on protocol API update

This PR removes VerifiedArchive from @agent-facets/protocol's public exports (replaced by VerifiedFacetArchive) and changes the validateFacetArchive failure branch from { ok: false, errors: ValidationError[] } to { ok: false, failure: ArchiveVerificationFailure }. The closely coupled registry at packages/v0/api/src/routes/facets/publish/verify.ts imports VerifiedArchive on line 12 (now gone), calls classifyError(result.errors) on line 65 where errors is now failure, and accesses v.assets in toVerifiedArtifact (line 103–115) which exists only on the 0.1 discriminant of the new VerifiedFacetArchive union. When the protocol package version bumps, the registry's upload-verification gateway will fail to compile.

@eXamadeus
eXamadeus force-pushed the julian/07-20-add_strict_raw_tar-header_validation_0.2_archive_verification_and_structured_archiveverificationfailure_to_the_consumer_bridge branch from 64ce981 to 7e815ef Compare July 21, 2026 01:52

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7e815ef96d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +255 to +258
} else {
byKey.set(key, name)
entries.push({ path: name })
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject non-canonical inner tar entry ordering

For a 0.2 archive whose entries and files hashes are correct but whose inner tar entries are not lexicographically ordered, this validator succeeds because it records paths without checking their sequence. The registry download path then extracts it and computeDirIntegrity sorts those paths before rebuilding the tar, so the reconstructed hash differs from the manifest's hash and installation fails after verification succeeded. Enforce the canonical ordering here (or preserve the verified tar bytes for the cache check) so accepted archives are installable.

Useful? React with 👍 / 👎.

Comment thread packages/cli/src/commands/publish/index.ts
@eXamadeus
eXamadeus force-pushed the julian/07-20-add_versioned_build-manifest_lockfile_and_archive-plan_schemas_with_exact_facetversion___lockfileversion_dispatch_and_supplementary_files_support branch from 2dbd6c3 to fef9bb7 Compare July 22, 2026 21:29
@eXamadeus
eXamadeus force-pushed the julian/07-20-add_strict_raw_tar-header_validation_0.2_archive_verification_and_structured_archiveverificationfailure_to_the_consumer_bridge branch from 7e815ef to 2b854bf Compare July 22, 2026 21:29

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2b854bf033

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +183 to +187
const typeflagByte = tarBytes[offset + TYPEFLAG_OFFSET] ?? 0
const typeflag = typeflagByte === 0 ? '0' : String.fromCharCode(typeflagByte)
const name = readField(tarBytes, offset + NAME_OFFSET, NAME_LENGTH)
const prefix = readField(tarBytes, offset + PREFIX_OFFSET, PREFIX_LENGTH)
const size = readOctalSize(tarBytes, offset + SIZE_OFFSET)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Validate tar header checksums before accepting entries

A tar whose header checksum has been corrupted while its name, size, and payload remain intact is accepted by this raw validator (for example, mutating byte 148 of valid-0.2.facet still returns ok: true). The checksum is the tar format's structural integrity check, so accepting it means parseFacetArchive can bless malformed archives that conforming tar consumers may reject. Parse and verify the checksum field for each header, returning tar-malformed on a mismatch.

Useful? React with 👍 / 👎.

Comment thread packages/engine/src/install/materialize-version/download-miss.ts
@eXamadeus
eXamadeus force-pushed the julian/07-20-add_versioned_build-manifest_lockfile_and_archive-plan_schemas_with_exact_facetversion___lockfileversion_dispatch_and_supplementary_files_support branch from fef9bb7 to 6a969ca Compare July 22, 2026 22:01
@eXamadeus
eXamadeus force-pushed the julian/07-20-add_strict_raw_tar-header_validation_0.2_archive_verification_and_structured_archiveverificationfailure_to_the_consumer_bridge branch from 2b854bf to 3526a29 Compare July 22, 2026 22:01
@eXamadeus
eXamadeus force-pushed the julian/07-20-add_versioned_build-manifest_lockfile_and_archive-plan_schemas_with_exact_facetversion___lockfileversion_dispatch_and_supplementary_files_support branch from 6a969ca to a3ab253 Compare July 23, 2026 00:04
@eXamadeus
eXamadeus force-pushed the julian/07-20-add_strict_raw_tar-header_validation_0.2_archive_verification_and_structured_archiveverificationfailure_to_the_consumer_bridge branch from 3526a29 to 93a9c9b Compare July 23, 2026 00:04
@eXamadeus
eXamadeus force-pushed the julian/07-20-add_versioned_build-manifest_lockfile_and_archive-plan_schemas_with_exact_facetversion___lockfileversion_dispatch_and_supplementary_files_support branch from a3ab253 to 1ba7c8d Compare July 23, 2026 00:04
@eXamadeus
eXamadeus force-pushed the julian/07-20-add_strict_raw_tar-header_validation_0.2_archive_verification_and_structured_archiveverificationfailure_to_the_consumer_bridge branch 2 times, most recently from 009140b to e2b15b8 Compare July 23, 2026 00:48
@eXamadeus
eXamadeus force-pushed the julian/07-20-add_versioned_build-manifest_lockfile_and_archive-plan_schemas_with_exact_facetversion___lockfileversion_dispatch_and_supplementary_files_support branch from 1ba7c8d to b6ee73c Compare July 23, 2026 00:48

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e2b15b8166

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +90 to +95
function readField(bytes: Uint8Array, offset: number, length: number): string {
let end = offset
const max = offset + length
while (end < max && bytes[end] !== 0) end++
return new TextDecoder().decode(bytes.subarray(offset, end))
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject malformed UTF-8 tar path bytes

When an inner entry name contains invalid UTF-8 (for example files/\xff.bin), the default TextDecoder silently replaces the bad byte with . A crafted 0.2 archive can declare that replacement-character path in facet.json and the files map, so verification succeeds even though the raw header path is not the path later extracted. The cache recomputation then rebuilds the tar using UTF-8 bytes for rather than the original invalid byte, producing a different integrity hash and making installation fail after successful validation. Decode header fields fatally (and return tar-malformed) so accepted paths round-trip losslessly.

Useful? React with 👍 / 👎.

@eXamadeus
eXamadeus force-pushed the julian/07-20-add_strict_raw_tar-header_validation_0.2_archive_verification_and_structured_archiveverificationfailure_to_the_consumer_bridge branch from e2b15b8 to 54323fe Compare July 23, 2026 01:13

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 54323fe4ae

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +272 to +276
const typeflagByte = tarBytes[offset + TYPEFLAG_OFFSET] ?? 0
const typeflag = typeflagByte === 0 ? '0' : String.fromCharCode(typeflagByte)
const name = readFieldFatal(tarBytes, offset + NAME_OFFSET, NAME_LENGTH)
const prefix = readFieldFatal(tarBytes, offset + PREFIX_OFFSET, PREFIX_LENGTH)
const size = readOctalSize(tarBytes, offset + SIZE_OFFSET)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject non-deterministic tar metadata

An archive whose entries and file hashes are valid but whose inner tar uses a nonzero mtime (or different mode/uid/gid) can pass this validator after its manifest integrity is computed from those raw bytes, because only type, name, prefix, and size are inspected. The registry miss path then extracts the files and reconstructs them with DETERMINISTIC_ATTRS, so its computed integrity differs from the accepted archive's manifest and installation fails at Check C. Validate the remaining deterministic header fields (and zero-filled name-field tails), or preserve the verified inner-tar bytes for cache integrity computation.

AGENTS.md reference: packages/protocol/AGENTS.md:L33-L37

Useful? React with 👍 / 👎.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 54323fe. Configure here.

Comment thread packages/protocol/src/build/tar-headers.ts
throw new Error(`unreachable archive failure: ${JSON.stringify(unreachable)}`)
}
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicated archive failure formatters

Low Severity

describeArchiveFailure and describeVerificationFailure both switch on the full ArchiveVerificationFailure union with nearly the same case logic. Keeping two copies raises the odds that a new failure code or message change is updated in only one path.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 54323fe. Configure here.

@eXamadeus
eXamadeus force-pushed the julian/07-20-add_strict_raw_tar-header_validation_0.2_archive_verification_and_structured_archiveverificationfailure_to_the_consumer_bridge branch 2 times, most recently from e4de25e to 8bdf957 Compare July 23, 2026 02:19

eXamadeus commented Jul 23, 2026

Copy link
Copy Markdown
Member Author

Merge activity

  • Jul 23, 2:25 AM UTC: A user started a stack merge that includes this pull request via Graphite.
  • Jul 23, 2:29 AM UTC: Graphite rebased this pull request as part of a merge.
  • Jul 23, 2:29 AM UTC: @eXamadeus merged this pull request with Graphite.

@eXamadeus
eXamadeus changed the base branch from julian/07-20-add_versioned_build-manifest_lockfile_and_archive-plan_schemas_with_exact_facetversion___lockfileversion_dispatch_and_supplementary_files_support to graphite-base/437 July 23, 2026 02:26
@eXamadeus
eXamadeus changed the base branch from graphite-base/437 to main July 23, 2026 02:27
… structured `ArchiveVerificationFailure` to the consumer bridge
@eXamadeus
eXamadeus force-pushed the julian/07-20-add_strict_raw_tar-header_validation_0.2_archive_verification_and_structured_archiveverificationfailure_to_the_consumer_bridge branch from 8bdf957 to c4e7771 Compare July 23, 2026 02:28
@eXamadeus
eXamadeus merged commit 5a837c5 into main Jul 23, 2026
7 checks passed
@eXamadeus
eXamadeus deleted the julian/07-20-add_strict_raw_tar-header_validation_0.2_archive_verification_and_structured_archiveverificationfailure_to_the_consumer_bridge branch July 23, 2026 02:29

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c4e7771ce1

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +216 to +220
if (offset === tarBytes.length) {
// Tar without an end-of-archive marker. nanotar tolerates this; a
// canonical builder always emits the marker, but the bytes are
// unambiguous, so accept.
break

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject inner tars without an end marker

When a 0.2 inner tar ends exactly after its last payload block, this accepts it even though assembleTar always emits terminal zero blocks. The archive can then pass validateFacetArchive, but registry installation extracts it and computeDirIntegrity rebuilds a terminated canonical tar, yielding a different integrity hash and rejecting the previously verified archive at Check C. Return tar-malformed here (at least for canonical inner archives) so accepted archives remain reproducible. Tar bytes are part of the hash contract.

AGENTS.md reference: packages/protocol/AGENTS.md:L33-L37

Useful? React with 👍 / 👎.

eXamadeus pushed a commit that referenced this pull request Jul 23, 2026
This PR was auto-generated by the release workflow. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.

# Releases
## @agent-facets/protocol@0.29.0

### Minor Changes

- [#437](#437) [`5a837c5`](5a837c5) Thanks [@eXamadeus](https://github.com/eXamadeus)! - **Consumer support for archive format `0.2` (pre-1.0 breaking minor).** The protocol now verifies both legacy `0.1` and current `0.2` `.facet` archives with strict, exact `facetVersion` dispatch and no fallback between versions. This is the consumer-first release: verification support ships before any producer emits `0.2`.
    **Breaking API — `validateFacetArchive`.** The result shape is now `{ ok: true; data: VerifiedFacetArchive } | { ok: false; failure: ArchiveVerificationFailure }`. The previous `{ ok: false; errors: ValidationError[] }` arm is replaced by a single tagged `failure`. The success payload type `VerifiedArchive` is renamed to `VerifiedFacetArchive` and is now a discriminated union on `archiveVersion`: the legacy `0.1` arm keeps the flat `assets: VerifiedAsset[]` list, while the current `0.2` arm exposes `entries: VerifiedEntry[]` (each tagged `manifest` \| `primary-asset` \| `skill-companion` \| `archive-only`). Consumers that read `.assets` unconditionally should migrate to the version-agnostic helpers `listVerifiedFiles(archive)` and `verifiedFileHashes(archive)`.
    **Structured failures.** `ArchiveVerificationFailure` is a tagged union (`container`, `invalid-json`, `duplicate-members`, `unsupported-facet-version`, `schema-violation`, `decompression`, `integrity`, `entry-integrity`, `validation`); classify on `failure.code` rather than parsing messages. No expected failure mode throws.
    **New public API.** `VerifiedFacetArchive`, `VerifiedEntry`, `ArchiveVerificationFailure`, `ValidateFacetArchiveResult`, `listVerifiedFiles`, `verifiedFileHashes`; versioned build-manifest and lockfile schemas plus their exact-dispatch parsers `parseBuildManifestDocument` and `parseLockfileDocument`; the shared archive plan (`planArchiveEntries`, `validateSupplementaryPath`, `portableCollisionKey`); strict raw tar-header validation (`validateRawTarEntries`, `RawTarValidationOptions`); and the archive-format constants `FACET_ARCHIVE_VERSION` (`0.2`), `LEGACY_FACET_ARCHIVE_VERSION` (`0.1`), and `SUPPORTED_FACET_VERSIONS`. `parseFacetArchive` now returns a version-tagged parsed build manifest and a structured `failure`.
    **Transitional exports retained.** `BuildManifestSchema`/`BuildManifest`, `LockfileSchema`/`Lockfile`, and `LOCKFILE_VERSION` (which equals the legacy value `1`, not the current `0.2`) remain exported and `@deprecated` for the compatibility window; they are removed once the engine lockfile-migration and producer work lands. Prefer the versioned parsers and `CURRENT_LOCKFILE_VERSION` in new code.
    This release intentionally carries **no** `@agent-facets/adapter` or `agent-facets` (CLI) version bump: the adapter API `0.0`→`0.1` cutover and the CLI `0.2` producer ship in later, separately gated releases. Other implementations of the spec (e.g. the registry) adopt this published package to gain dual-format verification.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> No application code changes—only version, lockfile, and changelog updates from the release workflow.
> 
> **Overview**
> Automated **Changesets** release PR that bumps **`@agent-facets/protocol`** from **0.25.0** to **0.29.0**, updates **`bun.lock`**, and appends the **0.29.0** section to **`packages/protocol/CHANGELOG.md`**.
> 
> It **removes** the consumed changeset **`.changeset/protocol-0-2-consumer-support.md`** so the release notes are not applied twice. There are **no runtime or source changes** in this diff—only version and changelog metadata ahead of npm publish.
> 
> The published **0.29.0** minor (from [#437](#437)) documents consumer support for facet archive **`0.2`**, breaking **`validateFacetArchive`** / **`VerifiedFacetArchive`** APIs, structured **`ArchiveVerificationFailure`** codes, and new verification helpers and versioned manifest/lockfile parsers; adapter and CLI bumps are explicitly deferred to later releases.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit f30ba6d. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant