@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.gzper 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.
License and Legal Information
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.