# Source Bundle Contract Audience: producer developers, integrators, and maintainers who create or validate source bundles consumed by `distributor`. A source bundle is a directory containing `manifest.json` and every regular file listed by that manifest. This is the producer-to-`distributor` file-format contract. ## Manifest Schema Current schema version: `1`. ```json { "schema_version": 1, "id": "reports.example.2026-06-04", "digest": "sha256:...", "created": "2026-06-04T12:00:00Z", "files": [ { "path": "report.md", "sha256": "sha256:...", "size": 1234 } ] } ``` Required manifest fields: - `schema_version`: must be `1`. - `id`: non-empty bundle identifier. - `digest`: lowercase `sha256:<64 hex>` digest of the ordered `files` list. - `created`: RFC3339 timestamp. - `files`: non-empty ordered list of file records. Required file fields: - `path`: bundle-relative slash-separated file path. - `sha256`: lowercase `sha256:<64 hex>` digest of the file bytes. - `size`: file size in bytes, zero or greater. ## Path Rules Manifest file paths must be clean relative slash-separated paths. They must not be empty, absolute, contain backslashes, contain `.` or `..` segments, include empty path segments, or normalize to a different path. Any basename of `manifest.json` or `.distributor.json` is reserved, including nested occurrences such as `nested/manifest.json`. Listed files must be regular files. Symlinks and other special file types are rejected during local bundle validation and manifest building. ## Digest Rules File digests use SHA-256 over each file's raw bytes. The bundle digest is SHA-256 over the canonical JSON-like payload for the ordered file records. The payload is constructed as: ```text [{"path":"","sha256":"","size":},...] ``` File order is significant. Explicit file lists preserve caller order. Scan mode sorts paths in ascending slash-path order. ## Producer APIs Go producers can use `gitea.maximumdirect.net/eric/distributor/pkg/bundle` to build and validate this contract. See [`pkg/bundle`](../consumers/pkg-bundle.md) for producer workflow guidance. - `BuildManifest`: builds a manifest from explicit file paths or scan mode. - `WriteManifest`: writes `manifest.json`, optionally replacing an existing manifest. - `WriteBundle`: copies source files into a complete bundle, validates it, and promotes it into place. - `LoadManifest`, `ParseManifest`, `ValidateManifest`, and `ValidateBundle`: parse and validate local bundles. - `FileDigest`, `BundleDigest`, and `ValidateDigest`: digest helpers. Go producers that submit bundles to `distributor serve` can use `gitea.maximumdirect.net/eric/distributor/pkg/upload`. See [Upstream Producer Integration](../consumers/api.md) and [HTTP Upload API Contract](http-upload.md). CLI producers can use: ```sh go run ./cmd/distributor manifest create --id go run ./cmd/distributor validate ``` ## Scan Mode Manifest scan mode walks the local bundle root recursively, includes regular files, includes dotfiles, skips files whose basename is `manifest.json` or `.distributor.json`, rejects symlinks, and sorts paths before building the manifest. ## Boundaries The source bundle manifest does not configure routing, destination selection, public URLs, credentials, transforms, notification behavior, or storage backends. Those concerns belong in `distributor` configuration and destination state. ## Tests Before changing this contract, inspect and run: ```sh go test ./pkg/bundle ./pkg/upload ./internal/bundle ```