npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@trebired/git-host

v0.1.0

Published

Embeddable Git host for Node.js and Bun apps, built on the real Git CLI with HTTP, SSH, and worktree APIs.

Downloads

111

Readme

@trebired/git-host

Embeddable Git host for Node.js and Bun apps.

@trebired/git-host gives your app real Git repository operations and real Git transports without making you adopt a full forge product. It runs the real Git CLI, helps you resolve repository paths safely, serializes mutations per repository, and exposes a reusable API for repository initialization, summary reads, content inspection, branch operations, working-tree changes, remote sync helpers, JSON API handlers, and smart HTTP and SSH hosting.

It is aimed at platforms and products that already own users, permissions, tokens, repository records, and UI, but want to stop hand-rolling the Git layer underneath all of that.

The package keeps auth, permission, and persistence decisions host-owned while giving you reusable Git behavior and transport adapters.

It also exposes an optional React companion at @trebired/git-host/react for typed API clients, providers, and headless data hooks on top of the JSON API.

In plain terms:

  • it is a Git hosting layer you embed into your app
  • it is not a full Git forge like GitLab, Gitea, or Forgejo
  • it is not a reimplementation of Git
  • it uses the real git binary for the hard parts

Install

Runtime support: Bun 1+ and Node.js 18+.

npm install @trebired/git-host
npm install @trebired/logger

Optional React companion:

npm install react
import { createGitHost, resolveRepositoryPath } from "@trebired/git-host";
import { createLog } from "@trebired/logger";

const log = createLog({
  console: true,
  quiet: true,
  save: false,
});

const repositoriesRoot = "/srv/git-workspaces";

const gitHost = createGitHost({
  logger: log,
  resolveRepository(repositoryId) {
    return {
      id: repositoryId,
      path: resolveRepositoryPath({
        rootDir: repositoriesRoot,
        repositoryPath: `${repositoryId}/workspace`,
      }),
    };
  },
});

await gitHost.ensureRepository("demo", {
  actor: {
    name: "Alice",
    email: "[email protected]",
  },
});

const summary = await gitHost.readSummary("demo");
console.log(summary.repository.current_branch);

const workingTree = await gitHost.readWorkingTree("demo");
console.log(workingTree.unstaged_entries);

await gitHost.fetch("demo", {
  remoteCredentials: {
    username: "git-user",
    password: process.env.GIT_TOKEN || "",
  },
});

Smart HTTP hosting:

import { createServer } from "node:http";
import { createGitHost, createGitHttpHandler } from "@trebired/git-host";
import { createLog } from "@trebired/logger";

const log = createLog({
  console: true,
  quiet: true,
  save: false,
});

const gitHost = createGitHost({
  resolveRepository(repositoryId) {
    return {
      id: repositoryId,
      path: `/srv/git-workspaces/${repositoryId}/workspace`,
    };
  },
});

const server = createServer(createGitHttpHandler({
  basePath: "/git",
  logger: log,
  resolveRepository(repositoryKey) {
    return {
      id: repositoryKey,
      path: `/srv/git-workspaces/${repositoryKey}/workspace`,
    };
  },
}));

server.listen(3000);

Then clients can use:

git clone http://127.0.0.1:3000/git/demo.git
git push

SSH hosting:

import { createGitSshServer } from "@trebired/git-host";
import { createLog } from "@trebired/logger";

const log = createLog({
  console: true,
  quiet: true,
  save: false,
});

const sshServer = createGitSshServer({
  hostKeys: [hostPrivateKeyPem],
  logger: log,
  authenticate({ publicKey, username }) {
    if (username !== "git") return null;
    const account = findAccountBySshPublicKey(publicKey);
    if (!account) return null;
    return {
      publicKey: account.publicKey,
      remoteUser: account.username,
      identity: account,
    };
  },
  resolveRepository(repositoryKey) {
    return {
      id: repositoryKey,
      path: `/srv/git-workspaces/${repositoryKey}/workspace`,
    };
  },
});

sshServer.listen(2222, "0.0.0.0");

Then clients can use:

git clone ssh://[email protected]:2222/demo.git
git push

JSON API hosting:

import { createServer } from "node:http";
import { createGitApiHandler, createGitHost } from "@trebired/git-host";
import { createLog } from "@trebired/logger";

const log = createLog({
  console: true,
  quiet: true,
  save: false,
});

const gitHost = createGitHost({
  resolveRepository(repositoryId) {
    return {
      id: repositoryId,
      path: `/srv/git-workspaces/${repositoryId}/workspace`,
    };
  },
});

const apiServer = createServer(createGitApiHandler({
  basePath: "/api/git",
  gitHost,
  logger: log,
  authorize({ action, repositoryId }) {
    return canReadRepository(repositoryId, action);
  },
}));

apiServer.listen(3100);

Then apps can use routes like:

GET /api/git/repositories/demo/summary
GET /api/git/repositories/demo/branches
GET /api/git/repositories/demo/commits?limit=20
GET /api/git/repositories/demo/commits/<commit-ref>
GET /api/git/repositories/demo/tree?ref=HEAD&path=src
GET /api/git/repositories/demo/blob?ref=HEAD&path=README.md
GET /api/git/repositories/demo/diff?baseRef=main&headRef=feature%2Fx

React companion:

import { createGitApiClient, GitApiClientProvider, useGitRepositorySummary } from "@trebired/git-host/react";

const gitClient = createGitApiClient({
  baseUrl: "/api/git",
});

function RepositorySummaryCard() {
  const summary = useGitRepositorySummary("demo");

  if (summary.loading) return "Loading...";
  if (summary.error) return summary.error.message;
  if (!summary.data) return "Missing repository";

  return `${summary.data.repository.current_branch} @ ${summary.data.repository.head_short}`;
}

function App() {
  return (
    <GitApiClientProvider client={gitClient}>
      <RepositorySummaryCard />
    </GitApiClientProvider>
  );
}

The React entry is intentionally headless. It helps apps fetch and mutate Git data consistently, but it does not ship a bundled styled UI.

Current API

The first public slice is intentionally small:

  • createGitHost()
  • resolveRepositoryPath()
  • runGit()
  • buildGitEnv()
  • RepositoryLockManager
  • createGitApiHandler()
  • createGitHttpHandler()
  • generateSshKeyPair()
  • normalizeSshPublicKey()
  • compareSshPublicKeys()
  • fingerprintSshPublicKey()
  • createGitSshServer()
  • @trebired/git-host/react

And the main host instance methods:

  • ensureRepository()
  • readSummary()
  • listBranches()
  • listCommits()
  • listTree()
  • readBlob()
  • readCommit()
  • diff()
  • readWorkingTree()
  • readStagedFile()
  • readUnstagedFile()
  • createBranch()
  • checkoutBranch()
  • checkoutRef()
  • deleteBranch()
  • stagePaths()
  • unstagePaths()
  • discardPaths()
  • commit()
  • continueOperation()
  • abortOperation()
  • fetch()
  • pull()
  • push()
  • withRepositoryLock()

The React entry currently exports:

  • createGitApiClient()
  • GitApiClientProvider
  • useGitRepositorySummary()
  • useGitBranches()
  • useGitCommits()
  • useGitCommit()
  • useGitTree()
  • useGitBlob()
  • useGitDiff()
  • useGitApiQuery()

Repository Model

This package does not own your app database.

Your app resolves a repository id to an absolute repository path. The package then runs Git operations against that path. This keeps repository metadata, permissions, tokens, SSH keys, and UI decisions inside the host app where they belong.

The current public API is worktree-first because that keeps the reusable boundary compact and predictable.

Private remotes are still host-owned. The package now helps with the transport plumbing by supporting:

  • remoteCredentials for clone, fetch, pull, and push
  • httpHeaders for per-command HTTP headers such as bearer auth
  • sshCommand for per-command SSH transport overrides

Why This Package

Most alternatives fall into one of three buckets:

  • full forge products such as GitLab, Gitea, or Forgejo
  • Git implementation libraries that reimplement Git behavior in another runtime
  • one-off app code that shells out to git without a reusable boundary

@trebired/git-host is aiming at the gap between those options.

Use it when you want:

  • your app to keep owning users, permissions, tokens, SSH keys, repository records, and UI
  • real Git behavior from the system git binary
  • clone, fetch, pull, and push over smart HTTP and SSH
  • a reusable Git runtime instead of spreading Git shell calls all over your platform code
  • optional headless React helpers over the JSON API without coupling the core package to a UI framework

Do not use it when you want:

  • a ready-made Git product with issues, pull requests, teams, admin screens, and built-in account management
  • a pure JavaScript Git implementation with no git binary dependency

That makes it useful for internal developer platforms, product-specific source management, deployment systems, controlled automation environments, and apps that need Git as a capability rather than Git hosting as a separate product.

Path Safety

Repository paths should never come straight from request input.

The intended flow is:

request repo id -> host app record lookup -> absolute repository path -> git-host

resolveRepositoryPath() is provided as a safe join helper when your host app stores repository-relative paths under one known root.

Hosted Transport Hooks

Hosted transports keep identity and permission policy in your app.

  • createGitHttpHandler() supports host-owned repository resolution, optional identity resolution, permission checks, and request audit events.
  • createGitSshServer() supports host-owned public key authentication, permission checks, and command audit events.
  • createGitApiHandler() supports host-owned repository id mapping and per-route authorization.
  • generateSshKeyPair(), normalizeSshPublicKey(), compareSshPublicKeys(), and fingerprintSshPublicKey() help host apps manage SSH transport setup without owning the parsing details themselves.

Platform Fit

@trebired/git-host is a good fit when a larger platform already owns users, permissions, repository records, tokens, SSH keys, and UI, but wants to stop hand-rolling the reusable Git layer.

The package is meant to replace or simplify:

  • Git CLI execution and environment shaping
  • repository locking and mutation coordination
  • repository summary, tree, blob, commit, diff, and working-tree reads
  • branch, checkout, commit, fetch, pull, and push operations
  • smart HTTP and SSH Git transport handling
  • thin JSON API route internals around those Git operations

The host platform should still own:

  • repository and source metadata persistence
  • permission checks and route authorization policy
  • access token issuance, revocation, and storage
  • SSH key ownership, private key storage, and known-host persistence
  • merge requests, reviews, UI flows, and other product-specific features

That boundary is where the package simplifies a platform the most without turning into a forge product of its own.

Logger Support

@trebired/git-host works best with @trebired/logger, and that is the recommended logger.

Why we recommend it:

  • it is simple
  • it already matches git-host's expected method shape
  • it keeps application logs and git-host diagnostics in one consistent format

The logger style:

log.info("git-host", "initializing repository", { repositoryId: "demo" });

comes from @trebired/logger.

You can pass that same log object into createGitHost(), createGitHttpHandler(), createGitSshServer(), and createGitApiHandler() through their logger option.

If you do not pass a logger and @trebired/logger is installed in the host app, git-host will create a quiet console-only logger automatically before falling back to raw console.

If you also set verbose: true, git-host will emit successful lifecycle and transport diagnostics through that logger. Without verbose, it stays much quieter and mainly reports rejected or failed operations.

Custom loggers can also use one of these shapes:

type Logger = {
  info(group: string, message: string, metadata?: unknown): void;
  warn(group: string, message: string, metadata?: unknown): void;
  error(group: string, message: string, metadata?: unknown): void;
  fail(group: string, message: string, metadata?: unknown): void;
};

type Event = {
  level: "info" | "warn" | "error" | "fail";
  group: string;
  message: string;
  metadata?: unknown;
};

type EventLogger = (event: Event) => void;

type SinkLogger = {
  log?(event: Event): void;
  write?(event: Event): void;
  fatal?(message: string, metadata?: unknown): void;
};

Common logger objects such as console, pino-style level methods, or Winston-style sinks are also adapted as sensibly as possible.

If no logger is provided and @trebired/logger is not installed, git-host falls back to plain console output for its own diagnostics.

Roadmap

The remaining package work is mostly convenience and hardening:

  • thin Express wrappers when they stay truly thin
  • broader examples for host-app integration patterns

Contributing

See CONTRIBUTING.md for development commands and package guidelines.