Files
Jyotisha/frontend/src/lib/personal-report-contract.server-core.ts
T

52 lines
1.9 KiB
TypeScript

import { createHash } from "node:crypto";
import {
canonicalEvidence,
safeParseReportDocument,
ReportDocumentValidationError,
type EvidenceAppendix,
type ReportDocumentParseResult,
type ReportDocumentV1,
} from "./personal-report-contract.ts";
/**
* Server hash core for ReportDocument v1.
*
* Pure Node implementation (node:crypto) without the server-only marker so
* tests can import it directly; the production entry
* personal-report-contract.server.ts adds `import "server-only"` and
* re-exports this module. Client bundles must never import this file: besides
* the marker on the production entry, node:crypto fails Next.js client builds.
*
* Flow per the architecture ruling: canonical isomorphic parse first, then
* verify provenance.evidenceHash against the recomputed hash. The hash is
* never trusted as a model self-report. The Python validator
* (scripts/personal_report_contract.py) performs the same recomputation.
*/
export function computeEvidenceHash(appendix: EvidenceAppendix): string {
const canonical = canonicalEvidence(appendix);
return createHash("sha256").update(JSON.stringify(canonical), "utf8").digest("hex");
}
export function safeParseServerReportDocument(input: unknown): ReportDocumentParseResult {
const parsed = safeParseReportDocument(input);
if (!parsed.ok) return parsed;
const recomputed = computeEvidenceHash(parsed.document.evidenceAppendix);
if (parsed.document.provenance.evidenceHash !== recomputed) {
return {
ok: false,
errors: [{
path: "provenance.evidenceHash",
message: `does not match recomputed evidence hash ${recomputed}`,
}],
};
}
return parsed;
}
export function parseServerReportDocument(input: unknown): ReportDocumentV1 {
const result = safeParseServerReportDocument(input);
if (!result.ok) throw new ReportDocumentValidationError(result.errors);
return result.document;
}