@git.zone/tspack

TsPack packages compiled executables and supporting assets into verified tar.gz bundles and named standalone files. It records each file's digest and executable role, seals the complete artifact set, and verifies archive contents without extracting filesystem paths. TsDeno and TsRust own compilation; GitZone and CI own publication.

Issue Reporting and Security

For reporting bugs, issues, or security vulnerabilities, please visit community.foss.global/. This is the central community hub for all issue reporting. Developers who sign and comply with our contribution agreement and go through identification can also get a code.foss.global/ account to submit Pull Requests directly.

Install and configure

Use Node.js 22 or newer in a Git project with a committed package.json:

pnpm install --save-dev @git.zone/tspack

Add the explicit bundle contents to .smartconfig.json. Input files must already exist. Add dist_pack/ to the project's .gitignore.

{
  "@git.zone/tspack": {
    "schemaVersion": 1,
    "name": "example-control",
    "bundles": [
      {
        "id": "linux-amd64",
        "files": [
          { "source": "dist_binary/control", "path": "control", "executable": true },
          { "source": "dist_binary/engine", "path": "engine", "executable": true },
          { "source": "license.md", "path": "license.md" },
          { "source": "notices/runtime.txt", "path": "third-party-notices.txt" }
        ]
      }
    ]
  }
}

name defaults to the package name with its scope separator replaced by a hyphen. outputDirectory defaults to dist_pack. Both are project-relative configuration; input and archive paths must be canonical relative paths. A file may specify sha256 to require an upstream digest. Files are mode 0644 unless executable is true, which records mode 0755. Inputs and their ancestors cannot be symlinks.

There is no implicit directory sweep. Select notices, provenance and source materials explicitly according to the component's distribution requirements. TsPack does not automatically publish application source, vendor dependencies, rebuild runtimes or reject accepted licenses such as LGPL.

Standalone release assets

Add assets when an installer needs a bare executable or script with a fixed download name. Each asset participates in the same sealed set as the archives:

{
  "schemaVersion": 1,
  "bundles": [],
  "assets": [
    {
      "id": "bootstrap-linux-amd64",
      "source": "dist_binary/bootstrap",
      "name": "app-linux-x64",
      "executable": true
    },
    { "id": "installer", "source": "install.sh", "name": "install.sh" }
  ]
}

You can combine bundles and assets, or leave bundles empty for a file-only release. Asset IDs are unique across both kinds. Names are single portable path components; metadata names, duplicate names differing only by case, and collisions with generated archive names are rejected. Standalone inputs accept the same optional sha256 pin and executable role as bundle files. Their original bytes are copied without an archive wrapper. Verification does not execute them.

Archive-only configurations retain their v1 manifest and configuration identity, including when assets is empty. A set with standalone files uses tspack.release.v2; each standalone artifact has kind: "file" and one exact entry in files. Consumers must support v2 before such a release is published. TsPack continues to verify and extract published v1 bundles. A downloader applies the recorded role when installing a standalone file; retention services may discard the downloaded file's filesystem executable bit without changing its identity.

Package and verify

# Development artifacts are explicitly marked unpublishable.
pnpm exec tspack pack

# Release builds require clean Git source at v<package.json version>.
pnpm exec tspack pack --release

# An alternate JSON file contains the tspack configuration block directly.
pnpm exec tspack pack --release --config dist_binary/pack-inputs.json

# Verify the complete retained artifact set against a trusted manifest digest.
pnpm exec tspack verify dist_pack/example-control-1.2.3-COMMITPREFIX \
  --release --sha256 MANIFEST_SHA256

Commands return one JSON result with the output directory, manifest digest, publishable state, reuse state, bundle count and standalone asset count. Invalid arguments or failed verification return a nonzero exit code.

Each artifact directory contains:

  • One NAME-VERSION-BUNDLE.tar.gz per configured bundle.
  • Each configured standalone asset under its exact name.
  • tspack-manifest.json: package, source, configuration, archive and file identities.
  • SHA256SUMS.txt: digests of the manifest and every archive or standalone file.

Each bundle includes its selected files plus tspack.json, which binds the same source/configuration identity and selected file records. tspack.json is reserved. Verification rejects extra or missing assets, duplicate or undeclared archive members, links, unexpected modes, changed metadata and digest mismatches. Archive verification never writes member paths to disk.

Files are copied into private staging while their bytes and source file identity are checked. Archives use sorted members, fixed timestamps and ownership, explicit modes and streaming compression. The output directory is published only after the whole set verifies. Identical inputs produce identical bytes with the same packaging/compression toolchain; this does not claim the compilers themselves are reproducible. Failed packaging removes only its own temporary staging directory.

Limits are 2 GiB per selected file, 8 GiB total uncompressed content, 10,000 selected files, 128 artifacts, 64 KiB for the checksum list, and 16 MiB for JSON metadata. Paths are portable ASCII relative names up to 240 characters. The source checkout, selected build tools and local filesystem are trusted; these checks do not sandbox malicious project code or a concurrent privileged writer. A checksum establishes content identity. Obtain the expected manifest digest through a trusted release channel to authenticate it.

Retain release bytes for retries

Store the entire sealed artifact directory using the CI provider's artifact storage before publishing any release asset. A retry must restore that exact directory and verify it before deciding whether compilation is necessary:

pnpm exec tspack pack --release --reuse

If a retained set exists, this command verifies its complete contents and requires the same clean tagged source, package/version and configuration. It returns the original artifacts even if ignored compiler outputs have since changed. If no set exists, it packages the current inputs. A corrupt or mismatched retained set fails; it is never silently replaced. Ordinary packaging also refuses an occupied output directory. --reuse is available only in release mode.

TsPack does not provide remote artifact storage. A workflow must retain and restore the directory; rebuilding after losing it cannot reproduce a nondeterministic compiler's original bytes. Published assets remain immutable.

TypeScript API

import { TsPack, type ITsPackConfig } from '@git.zone/tspack';

const packer = new TsPack('/absolute/project');
const config: ITsPackConfig = {
  schemaVersion: 1,
  bundles: [{
    id: 'linux-amd64',
    files: [{ source: 'dist_binary/app', path: 'app', executable: true }]
  }]
};

const result = await packer.pack(config, { release: true, reuse: true });
await packer.verify(result.directory, {
  expectedManifestSha256: result.manifestSha256,
  expectedSourceCommit: result.manifest.sourceCommit,
  requireRelease: true
});

For a platform-specific download, keep the complete tspack-manifest.json and SHA256SUMS.txt alongside exactly the selected archive. verifyBundle(directory, bundleId, options) checks this selected-only set, including the complete manifest and checksum list. Ordinary verify requires every archive and standalone file.

verifyArtifact(directory, artifactId, options) accepts a selected-only archive or standalone file with the same required trusted manifest pin, optional source pin, release requirement and AbortSignal. Standalone verification holds the actual file descriptor while checking its size, digest and identity. It returns only after closing the descriptor, including on cancellation. verifyBundle and extractBundle reject standalone selections; they never treat bare bytes as tar.

extractBundle verifies the selected archive and returns a fresh private directory under an existing canonical staging parent:

async function unpackPlatform(
  downloadDirectory: string,
  stagingParent: string,
  manifestSha256: string,
  sourceCommit: string,
  signal?: AbortSignal,
) {
  return new TsPack().extractBundle(downloadDirectory, 'linux-amd64', stagingParent, {
    expectedManifestSha256: manifestSha256,
    expectedSourceCommit: sourceCommit,
    requireRelease: true,
    signal,
  });
}

Both bundle methods require expectedManifestSha256 from a trusted release channel. Reading a digest from the downloaded checksum list alone does not authenticate the release. They preserve the complete manifest in the result and identify the selected archive as artifact. Extraction returns the new path as directory and the original download path as sourceDirectory.

Extraction authenticates compressed bytes before parsing, then reads the archive again through the same open file descriptor while verifying members. It rejects links, traversal, duplicate or missing members, file/directory collisions and changed bytes, and applies the recorded executable modes. Errors and cancellation drain pending writes before removing the owned temporary output. The source and staging parent must be on a trusted local filesystem; concurrent privileged writers are outside this boundary. The caller owns the returned directory and handles installation, persistence, activation, service control and later cleanup.

For pack, omit the configuration argument to load .smartconfig.json. TsPackError.code identifies configuration, source, input, archive, integrity or occupied-output failures. Unexpected filesystem or Git errors remain ordinary errors at the API boundary; the CLI reports them without dumping file contents.

Development

pnpm install
pnpm build
pnpm test
pnpm exec tsbuild check 'test/**/*'

Tests use disposable Git repositories and real archives. The independent archive interoperability check requires the system tar executable. Tests neither publish releases nor operate on production installations.

This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the repository license file.

Please note: The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.

Trademarks

This project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH or third parties, and are not included within the scope of the MIT license granted herein.

Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines or the guidelines of the respective third-party owners, and any usage must be approved in writing. Third-party trademarks used herein are the property of their respective owners and used only in a descriptive manner, e.g. for an implementation of an API or similar.

Company Information

Task Venture Capital GmbH
Registered at District Court Bremen HRB 35230 HB, Germany

For any legal inquiries or further information, please contact us via email at hello@task.vc.

By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.

S
Description
No description provided
Readme
331 KiB
Languages
TypeScript 99.9%
JavaScript 0.1%