Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
removing retired compatibility surfaces.
- Upgraded `@git-stunts/git-cas` to `^6.1.0` so Git-backed state caches can use
the library's crash-safe `RootSet` retention API.
- Moved shadow-trie leaf and branch storage from unretained raw Git blobs and
trees to bounded git-cas pages and composable bundle handles. Production
storage now shares one git-cas facade with the trie adapter, and the storage
ownership gate rejects direct raw Git object writers.

### Removed

Expand Down
28 changes: 8 additions & 20 deletions scripts/check-git-cas-invariants.sh
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
#!/bin/bash
# check-git-cas-invariants.sh — Detect direct git storage CLI usage bypassing @git-stunts/git-cas.
# check-git-cas-invariants.sh — Detect direct Git object writes bypassing @git-stunts/git-cas.
#
# git-warp mandates that ALL CAS operations MUST route through @git-stunts/git-cas.
# Direct invocation of raw git storage commands (git hash-object, git cat-file, etc.)
# is strictly banned to guarantee Buzhash Content-Defined Chunking (CDC) deduplication,
# constant-memory streaming, and CAS-First memoized materialization.
# git-warp mandates that CAS object creation route through @git-stunts/git-cas.
# Git history reads and ref operations remain owned by the history adapter.
#
# Runs as part of CI (strict-policy job) and can be run locally:
# bash scripts/check-git-cas-invariants.sh
Expand All @@ -18,33 +16,23 @@ SCAN_DIRS="src/"
# Pattern 1: Raw object hashing — git hash-object
# Bypasses Buzhash CDC chunking and streaming deduplication.
# ─────────────────────────────────────────────────────────────
if grep -rn --include='*.ts' -E '\bgit hash-object\b' "$SCAN_DIRS"; then
if grep -rn --include='*.ts' -E "git[[:space:]]+hash-object|['\"]hash-object['\"]" "$SCAN_DIRS"; then
echo ""
echo "::error::Detected raw git hash-object invocation. All CAS writes MUST use @git-stunts/git-cas cas.writeStream()."
echo "::error::Detected raw git hash-object invocation. Use @git-stunts/git-cas page or asset capabilities."
EXIT_CODE=1
fi

# ─────────────────────────────────────────────────────────────
# Pattern 2: Raw object inspection — git cat-file
# Bypasses streaming decompression and chunk reassembly.
# Pattern 2: Raw tree/pack writes — git mktree, git write-tree, git unpack-objects
# ─────────────────────────────────────────────────────────────
if grep -rn --include='*.ts' -E '\bgit cat-file\b' "$SCAN_DIRS"; then
echo ""
echo "::error::Detected raw git cat-file invocation. All CAS reads MUST use @git-stunts/git-cas cas.readStream() or cas.has()."
EXIT_CODE=1
fi

# ─────────────────────────────────────────────────────────────
# Pattern 3: Raw tree/pack manipulation — git mktree, git write-tree, git unpack-objects
# ─────────────────────────────────────────────────────────────
if grep -rn --include='*.ts' -E '\bgit mktree\b|\bgit write-tree\b|\bgit unpack-objects\b' "$SCAN_DIRS"; then
if grep -rn --include='*.ts' -E "git[[:space:]]+(mktree|write-tree|unpack-objects)|['\"](mktree|write-tree|unpack-objects)['\"]" "$SCAN_DIRS"; then
echo ""
echo "::error::Detected raw git tree/pack manipulation. All structural storage operations MUST route through @git-stunts/git-cas."
EXIT_CODE=1
fi

if [ $EXIT_CODE -eq 0 ]; then
echo "✓ No raw git storage CLI violations detected. All CAS operations cleanly encapsulated in @git-stunts/git-cas."
echo "✓ No raw Git object-write violations detected. CAS object creation is owned by @git-stunts/git-cas."
fi

exit $EXIT_CODE
1 change: 0 additions & 1 deletion scripts/source-size-gate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ export const SOURCE_SIZE_RELAXATIONS = Object.freeze([
'test/unit/domain/services/controllers/SyncController.test.ts',
'test/unit/domain/services/strand/ConflictAnalyzerService.test.ts',
'test/unit/domain/services/strand/StrandService.test.ts',
'test/unit/infrastructure/adapters/GitTrieStoreAdapter.test.ts',
'test/unit/scripts/visible-state-upgrade.test.ts',
'test/unit/specs/audit-receipt-vectors.test.ts',
]);
Expand Down
8 changes: 4 additions & 4 deletions src/domain/errors/TrieStoreError.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ import WarpError, { type WarpErrorOptions } from "./WarpError.ts";
*
* | Code | Meaning |
* |-------------------------|-------------------------------------------------------|
* | `E_TRIE_STORE_READ` | A read call failed against the backing Git store. |
* | `E_TRIE_STORE_WRITE` | A write call failed against the backing Git store. |
* | `E_TRIE_STORE_MISSING` | The requested OID does not exist. |
* | `E_TRIE_STORE_CORRUPT` | The OID resolved but its bytes failed trie decoding. |
* | `E_TRIE_STORE_READ` | A read call failed against the backing store. |
* | `E_TRIE_STORE_WRITE` | A write call failed against the backing store. |
* | `E_TRIE_STORE_MISSING` | The requested root does not exist. |
* | `E_TRIE_STORE_CORRUPT` | The root resolved but failed trie decoding. |
*
* The default code is `E_TRIE_STORE_READ` because the most common
* trie-store failure in practice is a read miss or a transport
Expand Down
20 changes: 10 additions & 10 deletions src/domain/orset/trie/TrieStorePort.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,34 +55,34 @@ import type { TrieBranchEntries } from "./TrieBranchEntries.ts";
*/
export default interface TrieStorePort {
/**
* Read a leaf blob's raw bytes by OID.
* Read a leaf page's raw bytes by opaque root handle.
*
* Throws `TrieStoreError` with code `E_TRIE_STORE_MISSING` if the
* OID does not exist, or `E_TRIE_STORE_READ` if the backing store
* root does not exist, or `E_TRIE_STORE_READ` if the backing store
* fails for any other reason.
*/
readLeaf(oid: string): Promise<Uint8Array>;
readLeaf(root: string): Promise<Uint8Array>;

/**
* Read a branch tree's nibble-indexed child map by OID.
* Read a branch bundle's nibble-indexed child map by opaque root handle.
*
* Throws `TrieStoreError` with code `E_TRIE_STORE_MISSING` if the
* OID does not exist, `E_TRIE_STORE_CORRUPT` if the stored bytes
* fail branch decoding, or `E_TRIE_STORE_READ` otherwise.
* root does not exist, `E_TRIE_STORE_CORRUPT` if the stored bundle
* fails branch decoding, or `E_TRIE_STORE_READ` otherwise.
*/
readBranch(oid: string): Promise<TrieBranchEntries>;
readBranch(root: string): Promise<TrieBranchEntries>;

/**
* Write a leaf blob and return its content-addressed OID.
* Write a leaf page and return its content-addressed root handle.
*
* Throws `TrieStoreError` with code `E_TRIE_STORE_WRITE` if the
* backing store rejects the write.
*/
writeLeaf(data: Uint8Array): Promise<string>;

/**
* Write a branch tree from its nibble-indexed child map and return
* its content-addressed OID.
* Write a branch bundle from its nibble-indexed child map and return
* its content-addressed root handle.
*
* Throws `TrieStoreError` with code `E_TRIE_STORE_WRITE` if the
* backing store rejects the write.
Expand Down
9 changes: 6 additions & 3 deletions src/infrastructure/adapters/GitCasRepositoryAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import { CborIndexStoreAdapter } from './CborIndexStoreAdapter.ts';
import { CborPatchJournalAdapter } from './CborPatchJournalAdapter.ts';
import { GitCasWarpStateCacheAdapter } from './GitCasWarpStateCacheAdapter.ts';
import type { GitCasRootSetClient } from './GitCasStateCacheRootSetCoordinator.ts';
import GitTrieStoreAdapter from './GitTrieStoreAdapter.ts';
import GitCasTrieStoreAdapter from './GitCasTrieStoreAdapter.ts';
import GitTrustChainAdapter from './GitTrustChainAdapter.ts';
import type { GitPlumbing } from './gitErrorClassification.ts';
import LoggerObservabilityBridge from './LoggerObservabilityBridge.ts';
Expand All @@ -44,7 +44,10 @@ export type GitCasFacade = Pick<
| 'store'
> & {
readonly assets: Pick<AssetCapability, 'put' | 'adopt' | 'open'>;
readonly bundles: Pick<BundleCapability, 'putOrdered' | 'iterateMembers'>;
readonly bundles: Pick<
BundleCapability,
'getMember' | 'putOrdered' | 'iterateMembers'
>;
readonly caches: GitCasMaterializationFacade['caches'];
readonly pages: GitCasMaterializationFacade['pages'];
readonly publications: Pick<PublicationCapability, 'commit'>;
Expand Down Expand Up @@ -101,7 +104,7 @@ export default class GitCasRepositoryAdapter implements RuntimeStorageProviderPo
indexes: this._createIndexStore(request, content),
materializations: this._createMaterializationStore(request),
stateSnapshots: this._createStateSnapshots(request),
trie: new GitTrieStoreAdapter({ plumbing: this._plumbing }),
trie: new GitCasTrieStoreAdapter({ cas: this._cas }),
})
);
}
Expand Down
Loading
Loading