Hold on. He’s turning Git into a blob store. Let him cook.
Most potent clone available on GitHub (legally).
Git isn’t source control.
Git is a content-addressed object database.
We use the object database.
git-cas chunks files into Git blobs (dedupe for free), optionally encrypts them, and emits a manifest + a real Git tree so you can commit/tag/ref it like any other artifact.
- Dedupe for free Git already hashes objects. We just lean into it.
- Chunked storage big files become stable, reusable blobs.
- Optional AES-256-GCM encryption store secrets without leaking plaintext into the ODB.
- Compression gzip before encryption — smaller blobs, same round-trip.
- Passphrase encryption derive keys from passphrases via PBKDF2 or scrypt — no raw key management.
- Merkle manifests large files auto-split into sub-manifests for scalability.
- Manifests a tiny explicit index of chunks + metadata (JSON/CBOR).
- Tree output generates standard Git trees so assets snap into commits cleanly.
- Full round-trip store, tree, and restore — get your bytes back, verified.
- Lifecycle management
readManifest,deleteAsset,findOrphanedChunks— inspect trees, plan deletions, audit storage. - Vault GC-safe ref-based storage. One ref (
refs/cas/vault) indexes all assets by slug. No more silent data loss fromgit gc.
Use it for: binary assets, build artifacts, model weights, data packs, secret bundles, weird experiments, etc.
Compression — compression: { algorithm: 'gzip' } on store(). Compression runs before encryption. Decompression on restore() is automatic.
Passphrase-based encryption — Pass passphrase instead of encryptionKey. Keys are derived via PBKDF2 (default) or scrypt. KDF parameters are stored in the manifest for deterministic re-derivation. Use deriveKey() directly for manual control.
Merkle tree manifests — When chunk count exceeds merkleThreshold (default: 1000), manifests are automatically split into sub-manifests stored as separate blobs. readManifest() transparently reconstitutes them. Full backward compatibility with v1 manifests.
See CHANGELOG.md for the full list of changes.
Vault — GC-safe ref-based storage. A single Git ref (refs/cas/vault) indexes all stored assets by slug, so git gc can no longer silently discard your data. Initialize with vault init, store with --tree, restore by --slug.
CLI breaking change — git cas restore no longer takes a positional <tree-oid> argument. Use --oid <tree-oid> or --slug <slug> instead.
See CHANGELOG.md for the full list of changes.
npm install @git-stunts/git-cas
npx jsr add @git-stunts/git-cas
import GitPlumbing from '@git-stunts/plumbing'; import ContentAddressableStore from '@git-stunts/cas'; const git = new GitPlumbing({ cwd: './assets-repo' }); const cas = new ContentAddressableStore({ plumbing: git }); // Store a file -> returns a manifest (chunk list + metadata) const manifest = await cas.storeFile({ filePath: './image.png', slug: 'my-image', encryptionKey: myKeyBuffer, // optional (32 bytes) }); // Turn the manifest into a Git tree OID const treeOid = await cas.createTree({ manifest }); // Restore later — get your bytes back, integrity-verified await cas.restoreFile({ manifest, outputPath: './restored.png' }); // Read the manifest back from a tree OID const m = await cas.readManifest({ treeOid }); // Lifecycle: inspect deletion impact, find orphaned chunks const { slug, chunksOrphaned } = await cas.deleteAsset({ treeOid }); const { referenced, total } = await cas.findOrphanedChunks({ treeOids: [treeOid] }); // v2.0.0: Compressed + passphrase-encrypted store const manifest2 = await cas.storeFile({ filePath: './image.png', slug: 'my-image', passphrase: 'my secret passphrase', compression: { algorithm: 'gzip' }, });
git-cas installs as a Git subcommand:
# Store a file — prints manifest JSON git cas store ./image.png --slug my-image # Store and vault the tree OID (GC-safe) git cas store ./image.png --slug my-image --tree # Restore from a vault slug git cas restore --slug my-image --out ./restored.png # Restore from a direct tree OID git cas restore --oid <tree-oid> --out ./restored.png # Vault management git cas vault init git cas vault list git cas vault info my-image git cas vault remove my-image git cas vault history # Encrypted vault round-trip (passphrase via env var or --vault-passphrase flag) export GIT_CAS_PASSPHRASE="secret" git cas vault init git cas store ./secret.bin --slug vault-entry --tree git cas restore --slug vault-entry --out ./decrypted.bin
Because sometimes you want the Git object database to be the store:
- deterministic
- content-addressed
- locally replicable
- commit-addressable
Also because LFS is, well... LFS.
dhtuxTHIS HASH’LL KNOCK YOUR SHAs OFF! FIRST COMMIT’S FREE, MAN.
Apache-2.0 Copyright © 2026 James Ross
Built by FLYING ROBOTS