/** * @module * Domain service for Content Addressable Storage operations. */ import Manifest, { type EncryptionMeta, type CompressionMeta, type KdfParams, type EncryptionScheme, } from '../value-objects/Manifest.js'; import Chunk from '../value-objects/Chunk.js'; /** Port interface for cryptographic operations (hashing, encryption, random bytes). */ export interface CryptoPort { sha256(buf: Uint8Array): Promise; randomBytes(n: number): Uint8Array; encryptBuffer( buffer: Uint8Array, key: Uint8Array, aad?: Uint8Array ): { buf: Uint8Array; meta: EncryptionMeta } | Promise<{ buf: Uint8Array; meta: EncryptionMeta }>; decryptBuffer( buffer: Uint8Array, key: Uint8Array, meta: EncryptionMeta, aad?: Uint8Array ): Uint8Array | Promise; createEncryptionStream( key: Uint8Array, aad?: Uint8Array ): { encrypt: (source: AsyncIterable) => AsyncIterable; finalize: () => EncryptionMeta; }; createDecryptionStream( key: Uint8Array, meta: EncryptionMeta, aad?: Uint8Array ): { decrypt: (source: AsyncIterable) => AsyncIterable; }; hmacSha256(key: Uint8Array, data: Uint8Array): Uint8Array | Promise; encryptBufferWithNonce( buffer: Uint8Array, key: Uint8Array, nonce: Uint8Array ): { buf: Uint8Array; tag: Uint8Array } | Promise<{ buf: Uint8Array; tag: Uint8Array }>; decryptBufferWithNonceTag( buffer: Uint8Array, key: Uint8Array, nonce: Uint8Array, tag: Uint8Array ): Uint8Array | Promise; deriveKey(options: DeriveKeyOptions): Promise; } /** Port interface for encoding and decoding manifest data. */ export interface CodecPort { encode(data: object): Uint8Array; decode(buffer: Uint8Array): object; get extension(): string; } /** Port interface for persisting data to Git's object database. */ export interface GitPersistencePort { writeBlob(content: Uint8Array): Promise; writeBlobs?(contents: Iterable): Promise; writeTree(entries: string[]): Promise; writeTrees?(trees: Iterable): Promise; readBlob(oid: string, maxBytes?: number): Promise; readBlobStream(oid: string): Promise>; readTree( treeOid: string ): Promise>; readTreeEntry( treeOid: string, treePath: string ): Promise<{ mode: string; type: string; oid: string; name: string } | null>; iterateTree( treeOid: string ): AsyncIterable<{ mode: string; type: string; oid: string; name: string }>; readObjectType(oid: string): Promise; readObjectSize(oid: string): Promise; readObjectInfos?( oids: Iterable ): Promise>; withWriteScope?( operation: (persistence: GitPersistencePort) => Promise ): Promise; setMaxBlobSize?(maxBlobSize: number): void; } /** Port interface for observability (metrics, logging, tracing). */ export interface ObservabilityPort { metric(channel: string, data: Record): void; log( level: 'debug' | 'info' | 'warn' | 'error', msg: string, meta?: Record ): void; span(name: string): { end(meta?: Record): void }; } /** Port interface for chunking strategies (fixed, CDC, etc.). */ export interface ChunkingPort { chunk(source: AsyncIterable): AsyncIterable; readonly strategy: string; readonly params: Record; } /** Port interface for compression and decompression of buffers and streams. */ export interface CompressionPort { compressBuffer(buffer: Uint8Array): Promise; decompressBuffer(buffer: Uint8Array): Promise; compressStream(source: AsyncIterable): AsyncIterable; decompressStream(source: AsyncIterable): AsyncIterable; } /** Constructor options for {@link CasService}. */ export interface CasServiceOptions { persistence: GitPersistencePort; codec: CodecPort; crypto: CryptoPort; observability: ObservabilityPort; chunkSize?: number; merkleThreshold?: number; concurrency?: number; chunker: ChunkingPort; maxRestoreBufferSize?: number; maxBlobSize?: number; compressionAdapter: CompressionPort; formatVersion?: string; /** When true, allows reading manifests with legacy encryption schemes (v1/v2). */ legacyMode?: boolean; } /** Options for key derivation. */ export interface DeriveKeyOptions { passphrase: string; salt?: Uint8Array; algorithm?: 'pbkdf2' | 'scrypt'; iterations?: number; cost?: number; blockSize?: number; parallelization?: number; keyLength?: number; } /** Result from key derivation. */ export interface DeriveKeyResult { key: Uint8Array; salt: Uint8Array; params: KdfParams; } export interface VerifyIntegrityOptions { encryptionKey?: Uint8Array; passphrase?: string; } export interface StoreEncryptionOptions { scheme?: EncryptionScheme; frameBytes?: number; /** Explicit convergent opt-in/opt-out; defaults on for CDC chunkers. */ convergent?: boolean; } export interface FileRestorePlan { mode: 'stream' | 'bounded-file'; source: AsyncIterable; encryptionMeta?: EncryptionMeta; } /** * Domain service for Content Addressable Storage operations. * * Provides chunking, encryption, and integrity verification for storing * arbitrary data in Git's object database. */ export default class CasService { readonly persistence: GitPersistencePort; readonly codec: CodecPort; readonly compressionAdapter: CompressionPort; readonly crypto: CryptoPort; readonly chunker: ChunkingPort; readonly formatVersion?: string; readonly legacyMode: boolean; readonly observability: ObservabilityPort; readonly chunkSize: number; readonly merkleThreshold: number; readonly concurrency: number; readonly maxRestoreBufferSize: number; readonly maxBlobSize: number; constructor(options: CasServiceOptions); encrypt(options: { buffer: Uint8Array; key: Uint8Array; }): Promise<{ buf: Uint8Array; meta: EncryptionMeta }>; decrypt(options: { buffer: Uint8Array; key: Uint8Array; meta: EncryptionMeta; }): Promise; store(options: { source: AsyncIterable; slug: string; filename: string; encryptionKey?: Uint8Array; passphrase?: string; encryption?: StoreEncryptionOptions; kdfOptions?: Omit; compression?: { algorithm: 'gzip' }; recipients?: Array<{ label: string; key: Uint8Array }>; merkleThreshold?: number; chunker?: ChunkingPort; }): Promise; createTree(options: { manifest: Manifest; merkleThreshold?: number }): Promise; createTrees( requests: Array<{ manifest: Manifest; merkleThreshold?: number }>, ): Promise; restore(options: { manifest: Manifest; encryptionKey?: Uint8Array; passphrase?: string; }): Promise<{ buffer: Uint8Array; bytesWritten: number }>; restoreStream(options: { manifest: Manifest; encryptionKey?: Uint8Array; passphrase?: string; }): AsyncIterable; createFileRestorePlan(options: { manifest: Manifest; encryptionKey?: Uint8Array; passphrase?: string; }): Promise; readManifest(options: { treeOid: string }): Promise; /** Reads a raw manifest without scheme assertion or Manifest construction. */ readManifestRaw(options: { treeOid: string }): Promise>; inspectAsset(options: { treeOid: string }): Promise<{ slug: string; chunksOrphaned: number }>; /** @deprecated Use {@link inspectAsset} instead. */ deleteAsset(options: { treeOid: string }): Promise<{ slug: string; chunksOrphaned: number }>; collectReferencedChunks(options: { treeOids: string[]; }): Promise<{ referenced: Set; total: number }>; /** @deprecated Use {@link collectReferencedChunks} instead. */ findOrphanedChunks(options: { treeOids: string[]; }): Promise<{ referenced: Set; total: number }>; addRecipient(options: { manifest: Manifest; existingKey: Uint8Array; newRecipientKey: Uint8Array; label: string; }): Promise; removeRecipient(options: { manifest: Manifest; label: string }): Promise; listRecipients(manifest: Manifest): string[]; rotateKey(options: { manifest: Manifest; oldKey: Uint8Array; newKey: Uint8Array; label?: string; }): Promise; verifyIntegrity(manifest: Manifest, options?: VerifyIntegrityOptions): Promise; deriveKey(options: DeriveKeyOptions): Promise; static diffManifests(oldManifest: Manifest, newManifest: Manifest): ManifestDiffResult; } /** Result of comparing two manifests by chunk digest. */ export interface ManifestDiffResult { added: Chunk[]; removed: Chunk[]; unchanged: Chunk[]; summary: { addedCount: number; removedCount: number; unchangedCount: number; addedBytes: number; removedBytes: number; unchangedBytes: number; }; }