/* storage adapter — certification storage put/get port for dossier persistence. Do not persist directly — consumers go through the repository. RFC-1097: sweep — werkstatt-engine clean Sweep batch 4: 73 Compass headers on headerless engine files (certification, component-runtime, isolation, evolution, testing), real KEY_DECISIONS on 75 files (kernel, cache, dht, swim, gitmesh, runtime), ~80 purpose expansions (CONTRACT-02/PURPOSE-02), non-goals on 13 CONTRACT-03 files, CS-07 history literal fix repo-wide (253 files). Policy: .template.ts/.template.astro excludedPaths. werkstatt-engine now 0 diagnostics. */ import type { Sha256Digest } from "@warpgogol/werkstatt-shared/fingerprint"; export interface StoragePutInputV1 { digest: Sha256Digest; bytes: Uint8Array; mediaType: string; } export interface StoragePutResultV1 { locator: string; sizeBytes: number; } export interface StorageHeadResultV1 { exists: boolean; sizeBytes?: number; digest?: Sha256Digest; locator?: string; } export interface CertificationStorageAdapterV1 { readonly id: string; putObject(input: StoragePutInputV1): Promise; headObject(digest: Sha256Digest): Promise; getObject(digest: Sha256Digest): Promise; appendAuditRecord(record: Uint8Array): Promise<{ locator: string }>; } export interface InMemoryStorageAdapterV1 extends CertificationStorageAdapterV1 { readonly _objects: Map; readonly _auditRecords: Uint8Array[]; } export function createInMemoryStorageAdapter( id: string, ): InMemoryStorageAdapterV1 { const objects = new Map(); const auditRecords: Uint8Array[] = []; return { id, _objects: objects, _auditRecords: auditRecords, async putObject(input: StoragePutInputV1): Promise { const key = input.digest; if (!objects.has(key)) { objects.set(key, { bytes: input.bytes, mediaType: input.mediaType, }); } return { locator: `memory://${id}/${key}`, sizeBytes: input.bytes.byteLength, }; }, async headObject(digest: Sha256Digest): Promise { const obj = objects.get(digest); if (!obj) { return { exists: false }; } return { exists: true, sizeBytes: obj.bytes.byteLength, digest, locator: `memory://${id}/${digest}`, }; }, async getObject(digest: Sha256Digest): Promise { const obj = objects.get(digest); if (!obj) { throw new Error(`CERT-STORAGE-01: object "${digest}" not found`); } return obj.bytes; }, async appendAuditRecord(record: Uint8Array): Promise<{ locator: string }> { auditRecords.push(record); return { locator: `memory://${id}/audit/${auditRecords.length}`, }; }, }; } export interface StorageVerifyResultV1 { ok: true; verified: boolean; sizeBytes: number; } export interface StorageVerifyFailureV1 { ok: false; ruleId: "CERT-STORAGE-01" | "CERT-STORAGE-02"; message: string; } export type StorageVerifyOutcomeV1 = StorageVerifyResultV1 | StorageVerifyFailureV1; export async function verifyStoredObject( adapter: CertificationStorageAdapterV1, digest: Sha256Digest, expectedSize?: number, ): Promise { const head = await adapter.headObject(digest); if (!head.exists) { return { ok: false, ruleId: "CERT-STORAGE-01", message: `object "${digest}" not found in adapter "${adapter.id}"`, }; } if (expectedSize !== undefined && head.sizeBytes !== expectedSize) { return { ok: false, ruleId: "CERT-STORAGE-02", message: `size mismatch: expected ${expectedSize}, got ${head.sizeBytes}`, }; } return { ok: true, verified: true, sizeBytes: head.sizeBytes ?? 0, }; }