thisago's blog

Idea: Hauler For Git

Table of Contents

I was willing to ask opinion to a friend about this idea, so I had the idea to bring it to public as a way to think better, over a higher pressure.

Summary

Hauler is a CLI tool written in Go for packing Helm charts and Docker images to let you easily "transport" these dependencies into a airgapped environment.

The idea is a packager tool that "carries" the new commits and push to a remote with zero knowledge of remote objects and without direct connection.

Use cases

I have repositories that stores PII data across my OSes (both bare installations and VMs).

For easier understanding, take as example the repo browsers-user, defined in my dotfiles: It stores the states of web browsers such as browsing history, bookmarks and even Chawan cookies for some websites allowed to persist.1
The remote, in the offline intranet, each OS pushes to a different branch, never fetching the other HEADs available at remote.

But not all cases follows this pattern of complete divergence between the branches. For example, the dotfiles repo also needs to be available in the intranet.

Threat model overview

  • The data tracked with Git is considered safe.2
  • The data resides in a environment considered unsafe, and it was previously connected to the internet.
  • Once a OS get connected to the intranet, it should not be connected to the internet back again. Never. But I am currently violating this.

Summary of the goal

  • Transfer all new commits, refs and tags to origin.
  • Be able to resolve conflicts and rebase local repo to avoid force push.
  • Be able to force push if needed.

Git bundles is almost does the job, except because:

  • Its "remote" considered as read-only.
  • The full repository is required to be there

If I need to rebase, I'll need remote objects, so the option can be rebasing already in the intranet.

Initial Ideas

Fake Remote

A CLI HTTP server starts locally, and provides the HEADs state of the remote without any object in there. Previously the command was ran inside the intranet to dump the HEADs of the configured repos.

Idealized usage:

  1. Index the HEADs of the airgapped forge.
  2. Bring the indexed DB to the unsafe environment using a external disk and run the server
  3. Configure once the repo remote
  4. Create a Git bundle or patch based on the HEADs
  5. Push the bundles inside the airgapped forge.

Pros:

  • Lightweight transfers.
  • No leak of Git objects.
  • No pull, the objects aren't there.

Cons:

  • Need to mimic a Git remote.
  • No rebase because have no pull.
  • The mocked remote needs to be inside the unsafe machine.
  • A little complex.

Points of attention:

  • I needs to filter out the repos that is not intended to be synced.
  • Can the rebase be made in the second part.
  • Does Git succeeds in fetching remote HEADs in a case where remote cannot provide any object? Needs testing.

Ideas:

  • Hash the repo names to obfuscate its names.

Create bundles

A CLI with the indexed database of remote HEADs packs a bundle with the built commit range from remote head.

Idealized usage:

  • Index the HEADs on the remote.
  • Run something like this via qvm-run and grabbing the stdout, executing the command in format syntax at dom0:

    git bundle create - "$(theclitogethead owner/reponame)..HEAD"
    
  • Bring the bundles to the intranet and do the push.

Pros:

  • Can be ran via qvm-run.

Cons:

  • Focusing in QubesOS environment. How handle via Termux and bare subsystems?

Ideas:

  • Getting familiar with Xen secure shell it might widen my view for it.

Conclusion?

A dump of thoughts. Now I will let the mind defragmentation process work in background. Someday a insight will appear.

No conclusion yet, but I'd like to hear your opinion.

Footnotes:

1

Yeah I know, saving cookies in plain text is dumb, but at same time, keeping it in the unencrypted browser SQLite database can be even worse because its location is predictable, and a malware will know exactly what to do, but there's not much to do here…

2

Might be a weakness but for now we need to solve a larger issue

See the source code here.
Generated at 2025-12-03 Wed 10:01 +0000 by Emacs 29.4 (Org mode 9.6.15)