2026-10-13 18:34:20 +00:00
2026-10-13 18:34:20 +00:00
2026-10-13 18:34:20 +00:00
2026-10-13 18:34:20 +00:00

@foss.global/gitmanager

Headless Git manager for the foss.global Git platform.

This package follows the push.rocks Rust-backed package pattern: TypeScript owns the public package API and lifecycle, while Rust owns the Git/storage/protocol engine. TypeScript controls the Rust binary through @push.rocks/smartrust, and builds use @git.zone/tsrust.

The current implementation provides lifecycle IPC, local bare repository and mirror management, bounded read APIs, durable repository bundle transfer, public and trusted-ingress private clone/fetch, trusted-ingress receive-pack, bounded raw Git services for SSH ingress, and revision-fenced protection for exact branch refs. User identity, permissions, SSH/TLS termination, durable protected-ref policy storage, and durable post-push repository storage remain the embedding service's responsibility.

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

pnpm add @foss.global/gitmanager

Usage

import { GitManager } from '@foss.global/gitmanager';

const gitManager = new GitManager({
  dataDir: '/var/lib/foss-global/gitmanager',
});

await gitManager.start();
const status = await gitManager.getStatus();
console.log(status.running, status.dataDir);

await gitManager.importMirror({
  org: 'foss.global',
  repo: 'interfaces',
  remoteUrl: 'ssh://git@code.foss.global:29419/foss.global/interfaces.git',
});

const branches = await gitManager.listRefs({
  org: 'foss.global',
  repo: 'interfaces',
  prefixes: ['refs/heads/'],
});
const tree = await gitManager.listTree({ org: 'foss.global', repo: 'interfaces', ref: 'main' });
const readme = await gitManager.readFile({
  org: 'foss.global',
  repo: 'interfaces',
  ref: 'main',
  path: 'readme.md',
});

await gitManager.stop();

Complete ref inventory

listRefs() returns one page of a repository's complete ref inventory as the @foss.global/interfaces TGitManagerRefInventoryResult. Without prefixes every ref under refs/ is listed: branches, tags, notes, pull and keep-around refs, and any other namespace. Each ref carries its object ID and type, the fully peeled object of annotated tags (including nested tags), and the target of symbolic refs. A symbolic ref whose target does not resolve is listed with a null objectId. HEAD is reported separately in head.

import type { IGitManagerRef } from '@foss.global/gitmanager';

const refs: IGitManagerRef[] = [];
let cursor: string | undefined;
while (true) {
  const result = await gitManager.listRefs({ org: 'foss.global', repo: 'interfaces', cursor });
  if (result.status === 'stale') {
    // The selected refs or HEAD changed between pages: start again.
    refs.length = 0;
    cursor = undefined;
    continue;
  }
  refs.push(...result.refs);
  if (result.pagination.status === 'exhausted') break;
  cursor = result.pagination.cursor;
}

// A complete ref name as the only prefix is an exact lookup.
const main = await gitManager.listRefs({
  org: 'foss.global',
  repo: 'interfaces',
  prefixes: ['refs/heads/main'],
});
  • Prefixes are refs/-rooted and match whole path components; overlapping prefixes select their union, and an empty prefixes array is invalid.
  • Pages are sorted by ref name in byte order. limit defaults to 1,000 refs and accepts 1–10,000; a page also ends early before its serialized refs exceed 8 MiB, far below the 50 MiB IPC payload limit.
  • Every page scans the whole selection and carries its inventoryDigest and refCount. The cursor is opaque and bound to the repository, the prefix set, and that digest, so no state is kept between pages and cursors survive an engine restart. A foreign or malformed cursor is rejected.
  • Git output is streamed record by record, so memory is bounded by one page regardless of the repository's ref count. Ref names that are not valid UTF-8 fail the request by name instead of being skipped. The reftable ref storage format is not supported.
  • Measured on 50,000 refs (25,000 annotated tags) with 10,000-ref pages: about 0.9 s for the complete inventory with packed refs and about 9.5 s with loose refs.

Credentialed mirrors and repository size

importMirror() and refreshMirror() take the transfer options of IGitManagerRemoteTransferOptions:

const credential = { kind: 'basic' as const, username: 'mirror-reader', password: accessToken };
await gitManager.importMirror({
  org: 'mirror.org',
  repo: 'interfaces',
  remoteUrl: 'https://forge.example/foss.global/interfaces.git',
  credential,
  caCertificates: [privateCaPem],
  timeoutMs: 30 * 60 * 1000,
});
await gitManager.refreshMirror({ org: 'mirror.org', repo: 'interfaces', credential, caCertificates: [privateCaPem] });

const size = await gitManager.getRepositorySize({ org: 'mirror.org', repo: 'interfaces' });
// { org, repo, objectCount, objectBytes, storageBytes }
  • The credential reaches Git only through the child process environment (GIT_CONFIG_COUNT entries) as an Authorization: Basic header. It never appears in the remote URL, on a command line, in the repository's Git config or in an error: error text replaces the remote URL with <mirror-source> and every form of the credential with <redacted>.
  • A credential requires an https:// remote URL without user information. While it carries one, Git does not follow redirects and reads no global or system Git config, so insteadOf rewrites and credential helpers there cannot take effect.
  • caCertificates replaces Git's CA bundle for this transfer. The PEMs are written to a private file in transferDir that is removed when the transfer ends.
  • timeoutMs is the transfer's wall-clock limit: 120000 by default, at most 21600000 (six hours). The Git process group is killed when it runs out.
  • Both transfers run without credential helpers or hooks, refuse the ext:: transport and check incoming objects.
  • getRepositorySize() counts the distinct objects of the repository's own object database (objectCount) and the sum of their uncompressed sizes (objectBytes), which does not depend on packing. storageBytes is the size of every file below objects/. A repository with alternate object stores is refused. Measuring runs for at most 30 minutes.

Durable repository bundles

GitManager can export a repository as a full Git bundle and atomically restore it under a new managed repository identity. Bundle bytes never pass through JSON IPC: Rust writes or reads a temporary file inside the dedicated transferDir, and the caller streams that file to or from durable object storage.

const gitManager = new GitManager({
  dataDir: '/run/foss-global/gitmanager',
  transferDir: '/run/foss-global/git-transfers',
  maxBundleBytes: 8 * 1024 * 1024 * 1024,
});

const bundle = await gitManager.createRepositoryBundle({
  org: 'foss.global',
  repo: 'interfaces',
});
// Stream bundle.bundlePath to durable object storage, then remove the temporary file.

await gitManager.restoreRepositoryBundle({
  org: 'restored',
  repo: 'interfaces',
  bundlePath: '/run/foss-global/git-transfers/downloaded.bundle',
  defaultBranch: 'main',
});

transferDir defaults to <dataDir>/transfers; both paths must be absolute. maxBundleBytes defaults to 8 GiB and is enforced after export and before restore. Restore accepts only a direct, non-empty .bundle file inside transferDir, restores every bundled ref namespace (not only branches and tags), keeps no remote pointing at the bundle, requires the declared default branch, runs strict full git fsck, and moves the restored bare repository into place atomically. Git bundles store resolved refs, so a symbolic ref under refs/ is restored as a direct ref. Empty repositories have no Git refs and therefore cannot produce a bundle; persist their catalog metadata and recreate them with initBareRepository().

Exact-commit source archives default to a 128 MiB compressed limit, a 64 GiB extracted regular-file limit, and 1,000,000 filesystem entries. Configure these with maxArchiveBytes, maxArchiveExtractedBytes, and maxArchiveEntries; their hard ceilings are 16 GiB, 64 GiB, and 1,000,000 respectively. CI source consumers apply their negotiated compressed limit, which is at most 8 GiB in runner protocol v2. Before publication, GitManager validates the exact generated gzip/tar stream against the returned extractedSizeBytes and entryCount, including paths, entry types, links, PAX metadata, checksums, padding, and archive terminators.

Pass { signal } as the second argument to createRepositoryArchive() to cancel archive creation. GitManager keeps operation IDs private, terminates and reaps active Git or inspection subprocesses, removes partial staging files, and only then rejects with the caller's abort reason. If final archive publication wins the cancellation race, the method returns the completed archive. stop() is single-flight and uses the same cleanup barrier before confirmed bridge termination.

The transfer directory is disposable staging, not persistence. GitManager holds exclusive ownership of it while running so startup can safely remove interrupted archive temporary files. The caller owns upload/download completion and removal of finished transfer files. Durable repository bytes belong in managed object storage.

Git Smart HTTP

Smart HTTP is disabled unless a smartHttp options object is provided. The safe defaults bind to loopback on an operating-system-assigned port and expose no repositories:

const gitManager = new GitManager({
  dataDir: '/var/lib/foss-global/gitmanager',
  smartHttp: {
    allowedRepositories: [
      { org: 'foss.global', repo: 'interfaces' },
    ],
    privateRead: {
      trustedIngressToken: process.env.GITMANAGER_INGRESS_TOKEN!,
    },
    receivePack: {
      trustedIngressToken: process.env.GITMANAGER_INGRESS_TOKEN!,
    },
  },
});

await gitManager.start();
const status = await gitManager.getStatus();
console.log(status.smartHttp?.baseUrl);

The server implements git-upload-pack for clone and fetch with Git protocol versions 0, 1, and 2. Missing and inaccessible read repositories both return the same not-found response. Configuring privateRead lets an authenticated loopback ingress grant repository-scoped access by injecting the exact X-GitManager-Ingress-Token value after removing any client-supplied copy. Direct private requests still receive the same not-found response.

Configuring receivePack enables streamed push only for requests carrying the trusted ingress token. Private reads and receive-pack require a loopback bind even when allowUnsafeNonLoopback is set. The trusted ingress must authorize the user and repository; for pushes it must also serialize writes, persist the updated repository, and withhold success if persistence fails.

The transport does not terminate TLS or implement user identity. Keep it behind a bounded ingress. Read-only binding to a non-loopback IP is rejected unless allowUnsafeNonLoopback: true is set explicitly.

Smart HTTP option defaults and accepted ranges:

All numeric options must be integers.

  • enabled: enabled when the smartHttp object is present unless explicitly false.
  • bindAddress: 127.0.0.1; must be an IP address.
  • port: 0, allowing the operating system to select an available port; accepted range 0–65,535.
  • allowUnsafeNonLoopback: false.
  • allowAllRepositories: false.
  • allowedRepositories: [], with at most 100,000 entries.
  • maxConcurrentTransfers: 32; accepted range 1–1,024.
  • maxRequestBodyBytes: 16 MiB; accepted range 1 KiB–64 MiB.
  • maxAdvertisementBytes: 4 MiB; accepted range 1 KiB–64 MiB.
  • maxResponseBytes: 4 GiB; accepted range 1 MiB–16 TiB.
  • processTimeoutMs: 1,800,000; accepted range 1,000–86,400,000.
  • idleTimeoutSeconds: 300; accepted range 1–3,600.
  • gracefulShutdownMs: 10,000; accepted range 100–60,000.
  • privateRead.trustedIngressToken: required when trusted private reads are enabled; 32–512 non-whitespace characters.
  • receivePack.trustedIngressToken: required when receive-pack is enabled; 32–512 non-whitespace characters.
  • receivePack.maxRequestBodyBytes: 4 GiB; accepted range 1 KiB–16 TiB. Request bodies are streamed and counted. A declared Content-Length above the limit is answered with 413 before Git starts. A streamed body that exceeds it is answered with 413 as well: the response status is decided only after the request body has reached Git completely, the Git process tree is killed at once, and the private pre-receive hook admits ref updates only after the transfer supervisor confirmed the complete body within the limit. An over-limit push therefore never updates a ref, even when Git has already read a complete pack before the excess bytes arrive. The rest of a rejected body is read for up to 1 MiB or 2 seconds so the client receives the 413.

While the service is running, access can be replaced atomically:

await gitManager.setSmartHttpRepositoryAccess({
  allowAllRepositories: false,
  allowedRepositories: [
    { org: 'foss.global', repo: 'interfaces' },
    { org: 'foss.global', repo: 'gitmanager' },
  ],
});

setSmartHttpRepositoryAccess() replaces the complete allow-all/allowlist state and resets per-repository visibility revision watermarks. For ordered visibility events, keep allowAllRepositories disabled and update one repository at a time:

await gitManager.setSmartHttpRepositoryVisibility({
  org: 'foss.global',
  repo: 'gitmanager',
  public: true,
  revision: 42,
});

Use monotonic, non-negative safe integers for revision. Stale revisions are ignored, equal revisions are idempotent when their visibility matches, conflicting equal revisions fail, and newer revisions replace the previous visibility state.

Protected refs

Protected refs are configured per repository as one complete, revisioned rule set:

const result = await gitManager.setSmartHttpRepositoryProtectedRefs({
  org: 'foss.global',
  repo: 'gitmanager',
  revision: 1,
  rules: [
    {
      refName: 'refs/heads/main',
      allowCreates: false,
      allowDeletes: false,
      allowForcePushes: false,
    },
  ],
});

console.log(result.status, result.effectiveRevision, result.rulesDigest);

Rules accept canonical, exact refs/heads/... names only; patterns, tags, roles, and bypass identities are not supported. All three permission booleans are required. Unlisted refs are unaffected. GitManager accepts at most 64 unique rules and canonicalizes their order.

Protected creates, deletes, and updates require valid commit objects. When allowForcePushes is false, an existing protected branch can move only through a fast-forward update.

Every start begins with no admitted policy for any repository, so receive-pack fails closed until the embedding service explicitly initializes that repository. Revision 0 is reserved for the empty policy and must use rules: []; the first non-empty policy must use revision 1 or higher. Clearing protection requires a newer revision with rules: [].

The first explicit policy after a start and every higher revision return applied. An equal revision with identical canonical rules returns idempotent. A lower revision returns stale with the effective revision and digest, while conflicting rules at an equal revision fail. Every successful result includes the effective revision and SHA-256 digest of the canonical rules.

Protected-ref state is process-local by design. The embedding service must persist the desired revision and rules, replay them after every start(), verify that the returned effective revision and digest match the desired policy, and admit no pushes before that verification succeeds. Policy mutation and receive-pack admission for the same repository must be serialized. Each receive-pack invocation is bound to one immutable policy snapshot.

Receive-pack uses only GitManager's private, process-specific hook directory; repository-configured hooks are not executed. The managed pre-receive hook rejects the complete push when policy bootstrap, decoding, schema validation, repository selection, object-format validation, commit or ancestry checks, or timeout handling fails.

When enabled, getStatus().smartHttp reports { running, bindAddress, port, baseUrl }. The server also exposes GET /healthz for a local ingress health check.

Raw Git services for SSH ingress

openRepositoryService() starts a bounded local git upload-pack or protected git receive-pack process for an already authenticated ingress. Rust validates the repository selector, canonical path, Git protocol version, receive-pack availability, and immutable protected-ref policy snapshot before the TypeScript facade starts the process. Pack bytes use Node streams and never pass through JSON IPC.

const service = await gitManager.openRepositoryService({
  org: 'foss.global',
  repo: 'gitmanager',
  service: 'upload-pack',
  protocol: 'version=2',
});

sshChannel.pipe(service.stdin);
service.stdout.pipe(sshChannel);
service.stderr.pipe(sshChannel.stderr);
const result = await service.completed;

The returned service counts request, response, and stderr bytes, runs with an isolated Git environment, has its own process group and deadline, and is terminated when GitManager.stop() runs; close() and stop() release its streams, so they return even when the caller never read its output. It owns the errors of its streams, so piping into stdin never throws in the caller: when a byte limit or the process timeout ends the process, completed resolves with failure set to 'requestLimitExceeded', 'responseLimitExceeded', 'stderrLimitExceeded', 'streamFailed', or 'processTimeout'; otherwise failure is null. The process is killed as soon as a limit is crossed. Unlike Smart HTTP, whose receive gate guarantees that an over-limit request never updates a ref, the interactive SSH protocol has no end of input before the pre-receive hook runs: Git updates refs as soon as it has read a complete pack, and excess bytes that arrive afterwards cannot undo those updates. failure therefore means the transfer was cut, not that the refs are unchanged; callers must reconcile refs from the repository after every receive-pack, whatever failure says. Receive-pack uses the same private managed pre-receive hook and exact policy snapshot as Smart HTTP. The caller must authenticate the SSH key, authorize the repository operation, cap sessions, serialize writes with HTTP pushes and policy changes, inspect refs, persist the repository, record downstream events, and withhold the final receive-pack status until all durable work succeeds.

Bounded readiness

checkReadiness() performs one bounded, read-only probe. Lifecycle states return failure results instead of throwing; only an invalid timeoutMs throws a TypeError. The default timeout is 2000 ms, the maximum is 30000 ms, and an AbortSignal cancels the local wait.

const readiness = await gitManager.checkReadiness({ timeoutMs: 2000 });
if (!readiness.ready) {
  console.error(`GitManager unavailable: ${readiness.reason}`);
}

When Git Smart HTTP is running on a loopback bind, the probe targets the engine's own GET /healthz route out-of-band, so readiness stays available while the serial management channel is busy with a long-running command such as a mirror import. Repository archive creation also keeps management status and cancellation responsive while preserving serial admission for every other stateful command. Without a loopback Smart HTTP route, including a service bound to a non-loopback address with allowUnsafeNonLoopback, one shared bounded getStatus round trip per instance is used; that fallback queues behind other in-flight management commands and a Smart HTTP probe failure never falls through to it. Failure reasons are not_running, starting, stopping, aborted, timeout, and unavailable; the result matches the readiness contracts of @push.rocks/smartdata and @push.rocks/smartbucket.

Commit ancestry

isAncestor() answers git merge-base --is-ancestor exactly: whether the ancestor commit is reachable from the descendant commit, where a commit is its own ancestor. Both object IDs must be full lowercase SHA-1 or SHA-256 IDs of commits in the repository; revision expressions, abbreviations, and tag objects are refused, so peel a tag before asking.

// Throws GitManagerError (error.code) unless the answer is exact.
const reachableFromMain = await gitManager.isAncestor({
  org: 'foss.global',
  repo: 'gitmanager',
  ancestorObjectId: taggedCommitId,
  descendantObjectId: mainTipCommitId,
  timeoutMs: 10_000,
});

timeoutMs is the work budget for the object lookup and the history walk together: 1 to 60000 ms, 10000 by default. When it runs out, the Git process is killed and the call throws. It never returns false for anything but an exact answer; the typed codes are invalidObjectId, objectNotFound, objectNotCommit, and ancestryBudgetExceeded. The check runs on the serial management channel like the other read APIs.

Archive inspection

inspectArchive() validates a tar.gz on the receiving side of a transfer, for example a CI artifact or cache upload. It streams the archive, given as a Uint8Array or any async iterable of Uint8Array chunks such as a Node stream or a fetch body, into a dedicated engine process. That process checks every entry against the rules GitManager applies to its own source archives and the @foss.global/ci-spec archive conformance vectors: safe relative paths, no duplicates or late ancestors, only regular files, directories and symlinks whose targets stay inside the root (including through chains), USTAR and supported PAX metadata only, zeroed padding, no trailing data or concatenated gzip members, and the fixed 134,217,728-byte path-metadata ceiling.

const inspection = await gitManager.inspectArchive({
  archive: uploadStream,
  maxCompressedBytes: 16 * 1024 ** 3,
  maxExtractedBytes: 128 * 1024 ** 3,
  maxEntries: 1_000_000,
}, { signal });
// { sizeBytes, sha256, extractedSizeBytes, entryCount, pathMetadataBytes }

The metadata is recomputed from the bytes, so compare it with what the sender declared rather than trusting the declaration. Every limit is inclusive and must be a non-negative safe integer; maxPathMetadataBytes is 1 to 134217728 and defaults to the ceiling. A refused archive throws a GitManagerError with code archiveLimitExceeded (a requested limit) or archiveRejected (a format, path, or link rule), and the inspector stops reading at the first violation and closes the source. Aborting the signal or calling stop() kills the inspector; the call rejects only after the process exited. The inspection does not use the management channel, so several can run at once.

Public API

  • GitManager: TypeScript facade for the Rust engine lifecycle.
  • GitManager.start(): spawns the Rust binary through smartrust and starts the engine.
  • GitManager.stop(): shares one in-flight stop, cancels and cleans active archives, then confirms bridge process termination.
  • GitManager.getStatus(): returns { running, dataDir, uptimeMs, smartHttp? }.
  • GitManager.checkReadiness(): performs a bounded, cancellable read-only readiness probe and returns { ready, status, durationMs } plus a failure reason when unavailable.
  • GitManager.setSmartHttpRepositoryAccess(): atomically replaces the running Smart HTTP allow-all/allowlist state.
  • GitManager.setSmartHttpRepositoryVisibility(): applies one revision-ordered repository visibility update.
  • GitManager.setSmartHttpRepositoryProtectedRefs(): applies a complete revision-fenced set of exact protected-head rules and returns its effective revision and digest.
  • GitManager.initBareRepository(): creates an empty local bare repository.
  • GitManager.importMirror(): imports a remote repository with git clone --mirror. Supported remote forms are https://, http://, ssh://, git://, file://, and SCP-like git@host:org/repo.git URLs.
  • GitManager.refreshMirror(): atomically fetches and verifies the configured mirror source.
  • GitManager.importMirror() and GitManager.refreshMirror() accept a credential, caCertificates and timeoutMs; see Credentialed mirrors and repository size.
  • GitManager.getRepositorySize(): returns the exact object count, uncompressed object bytes and on-disk object storage bytes of a repository.
  • GitManager.verifyRepository(): runs a strict full integrity check.
  • GitManager.createRepositoryBundle(): writes a bounded full Git bundle into transferDir and returns its temporary path and size.
  • GitManager.createRepositoryArchive(request, { signal? }): writes a bounded deterministic exact-commit tar.gz archive into transferDir and returns its digest, compressed size, extracted regular-file size, entry count, and temporary path; cancellation settles after Rust process and staging cleanup.
  • GitManager.restoreRepositoryBundle(): validates and atomically restores a bounded bundle from transferDir.
  • GitManager.openRepositoryService(): opens a bounded upload-pack or protected receive-pack stream for an authenticated SSH-style ingress.
  • GitManager.listRepositories(): lists local repositories managed below the configured data directory.
  • GitManager.listRefs(): lists one coherent page of the complete ref inventory of every namespace, with peeled tags, symbolic refs, HEAD, and a digest-bound cursor.
  • GitManager.listTree(): lists tree entries at a ref and optional path.
  • GitManager.readFile(): reads a blob as base64 after checking the blob size limit.
  • GitManager.listCommits(): lists commit summaries for a ref.
  • GitManager.getCommit(): reads commit detail metadata, parents, author/committer data, and diff stats for a ref.
  • GitManager.getDiff(): returns a structured unified diff between a head ref and its first parent or an explicit base ref.
  • GitManager.listTags(): lists lightweight and annotated tags with target object metadata.
  • GitManager.getTag(): reads one tag by exact name.
  • GitManager.isAncestor(): answers exact commit ancestry within a work budget, with typed errors for anything else.
  • GitManager.inspectArchive(request, { signal? }): streams a receiver-side tar.gz through the engine inspector and returns its recomputed metadata or a typed refusal.
  • GitManager.running: local facade running state.
  • RustGitManagerBridge: lower-level typed bridge around @push.rocks/smartrust.
  • GitManagerError and TGitManagerErrorCode: typed failures with a stable code; gitManagerArchivePathMetadataCeilingBytes is the fixed archive path-metadata ceiling.
  • IGitManagerAncestryRequest, IGitManagerArchiveInspectionRequest, IGitManagerArchiveInspectionOptions, IGitManagerArchiveInspection, IGitManagerOptions, IGitManagerStatus, IGitManagerReadinessOptions, TGitManagerReadinessResult, TGitManagerReadinessFailureReason, IRustGitManagerConfig, IGitManagerProtectedRefRule, IGitManagerSmartHttpRepositoryProtectedRefs, TGitManagerProtectedRefsApplyStatus, and IGitManagerProtectedRefsApplyResult: exported option, status, readiness, and protected-ref contracts.

Repository/read DTO contracts are re-exported from the published @foss.global/interfaces package.

Read/list APIs and raw-service preparation go through JSON IPC. Git packfile streaming uses controlled git upload-pack and git receive-pack subprocesses behind bounded HTTP or Node streams; packfiles do not pass through JSON IPC.

Read APIs and Smart HTTP use the system git CLI with explicit arguments, non-interactive environment variables, command timeouts, ref-to-object-id resolution, path validation, bounded stdout/stderr capture, ref/tree result caps, and a 1 MiB file read cap. Receive-pack streams bounded requests, replaces repository-configured hooks with GitManager's verified managed pre-receive hook, enables incoming object fsck, and rejects the complete push when policy evaluation cannot complete safely.

Rust Build

The package builds a Rust binary named rustgitmanager from rust/ using @git.zone/tsrust.

Configured targets:

  • linux_amd64
  • linux_arm64

The ARM64 target uses rust/.cargo/config.toml with aarch64-linux-gnu-gcc as linker.

Testing

pnpm test
pnpm run test:rust
pnpm run build

pnpm test runs the pretest hook first, which builds the Rust binaries with tsrust, and then runs lifecycle, bounded readiness, local mirror/read API, raw upload-pack/receive-pack preparation, clone/fetch denial, trusted receive-pack denial, explicit policy initialization, protected create/delete/non-fast-forward enforcement, inherited self-test hardening, restart replay, and real push tests through smartrust. test/test.refinventory.node.ts covers every ref namespace, peeled and nested tags, symbolic and dangling symbolic refs, unborn HEAD, prefix selection and exact lookup, ref names that are not valid UTF-8, pagination of 1,001 refs, stale and foreign cursors, 50,000 loose and packed refs, and a bundle round trip across namespaces. test/test.pushlimits.node.ts covers fixed-length and chunked Smart HTTP pushes over the limit, a complete pack followed by late excess bytes, an over-limit SSH-style receive-pack, an SSH-style receive-pack cut after Git already moved a ref (the update stays), and a bounded stop() while a service's output is never read, the rejected cases proving the refs unchanged, no Git or hook process left, no uncaught stream error, the engine serving, and a later push succeeding. test/test.ancestry.node.ts covers ancestors, the reverse direction, siblings, merges, unrelated histories, the same commit, invalid, missing and non-commit objects, a budget exceeded on a 20,000-commit history next to the exact answer, and a stalled walk whose Git process is killed. test/test.archiveinspection.node.ts runs every ci-spec archive conformance vector through inspectArchive() as bytes and as a chunked stream, the compressed limit at its boundary, an 8 MiB streamed archive, an early refusal that closes the source, and abort, source failure and stop() during an inspection.

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

Bundled Rust binaries include third-party Rust crates under their respective licenses. See thirdparty.md for the packaged notice list.

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
1.9 MiB
Languages
HTML 41.9%
Rust 38.1%
TypeScript 20%