Before restoring, I want to see what is coming in and what would change. I wrote a small independent module that validates fixed synthetic records and produces a replacement candidate.

It neither accesses files nor restores a client ledger. The package shares JSON validation, integrity checking, a change preview, tests and bilingual demos. A matching hash does not establish an author or recovery authority.

Keep the accepted format deliberately narrow

The bundle declares synthetic-restore-v1 and a synthetic marker. Its entry path is fixed to records.json. The module does not extract an archive or write a filesystem path; other names and traversal strings are rejected. Rows have exactly id and quantity. Identifiers are S followed by two digits, quantities are integers from one to ninety-nine, and identifiers must be unique.

Bundle text is limited to 80,000 characters and payload text to 60,000. The array bound is 1,000 rows, although the identifier grammar permits only one hundred distinct rows. This is a compact teaching contract, not a large-backup processor. Supporting operational formats and older versions requires a separate migration policy and tests for the accepted conversions.

Check bytes and digest before accepting the records

makeSyntheticBundle serializes rows and records their UTF-8 byte length and SHA-256. inspectSyntheticBundle recomputes both before accepting the payload. Character count is not assumed to equal byte length; TextEncoder supplies the byte representation. A matching digest does not skip record validation either. An invalid quantity remains invalid even if its digest has been recomputed correctly.

The implementation uses the environment’s Web Crypto SHA-256. If it is unavailable, validation fails instead of substituting a weaker checksum. Node tests compare the result independently with node:crypto. A target browser must provide the required functionality and should be checked in its actual environment. This round evaluates code and event adapters, not a rendered browser’s feature support.

Return a candidate and a preview before applying anything

rehearseSyntheticRestore compares verified new rows with current synthetic rows. It returns candidate, preview, integrityVerified and applied:false. The preview counts additions, removals and quantity changes without mutating current input. Equal record counts can conceal a removed row and a different replacement, so a count alone is not used as the explanation of a restore.

The demo’s separate apply action verifies the reviewed string again before replacing only its page-memory list. A review generation check prevents a late asynchronous result from overwriting a newly selected condition. No database or file is replaced. Operational recovery still needs write control, authority, a copy of current records and a rollback route.

Explore the implementation

Complete independent synthetic implementation

// Fixed-path synthetic bundle; checks integrity, not author identity. Never writes files or production data.
const exact=(v,k)=>v&&typeof v==='object'&&!Array.isArray(v)&&Object.keys(v).sort().join(',')===k;
function validRecords(rows){
 return Array.isArray(rows)&&rows.length<=1000&&rows.every(r=>exact(r,'id,quantity')&&typeof r.id==='string'&&/^S\d{2}$/.test(r.id)&&Number.isSafeInteger(r.quantity)&&r.quantity>=1&&r.quantity<=99)&&new Set(rows.map(r=>r.id)).size===rows.length;
}
async function digest(text){
 if(!globalThis.crypto?.subtle)throw new Error('SHA-256 unavailable');
 const data=await globalThis.crypto.subtle.digest('SHA-256',new TextEncoder().encode(text));
 return Array.from(new Uint8Array(data),n=>n.toString(16).padStart(2,'0')).join('');
}
export async function makeSyntheticBundle(rows){
 if(!validRecords(rows))throw new TypeError('Invalid synthetic records');
 const payload=JSON.stringify(rows),bytes=new TextEncoder().encode(payload).length;
 return JSON.stringify({format:'synthetic-restore-v1',synthetic:true,entry:{path:'records.json',bytes,sha256:await digest(payload)},payload});
}
export async function inspectSyntheticBundle(text){
 if(typeof text!=='string'||text.length>80000)throw new TypeError('Bundle too large or invalid');
 const b=JSON.parse(text);
 if(!exact(b,'entry,format,payload,synthetic')||b.format!=='synthetic-restore-v1'||b.synthetic!==true||!exact(b.entry,'bytes,path,sha256')||b.entry.path!=='records.json'||!Number.isSafeInteger(b.entry.bytes)||b.entry.bytes<0||typeof b.entry.sha256!=='string'||! /^[a-f0-9]{64}$/.test(b.entry.sha256)||typeof b.payload!=='string'||b.payload.length>60000)throw new TypeError('Unsupported synthetic bundle');
 if(new TextEncoder().encode(b.payload).length!==b.entry.bytes||await digest(b.payload)!==b.entry.sha256)throw new Error('Integrity mismatch');
 const rows=JSON.parse(b.payload);if(!validRecords(rows))throw new TypeError('Unsupported synthetic records');
 return {format:b.format,rows:rows.map(r=>({...r})),recordCount:rows.length,sha256:b.entry.sha256};
}
export async function rehearseSyntheticRestore(text,currentRows){
 if(!validRecords(currentRows))throw new TypeError('Invalid target example');
 const checked=await inspectSyntheticBundle(text),oldIds=new Set(currentRows.map(r=>r.id)),newIds=new Set(checked.rows.map(r=>r.id));
 return {candidate:checked.rows,preview:{before:currentRows.length,after:checked.rows.length,added:checked.rows.filter(r=>!oldIds.has(r.id)).length,removed:currentRows.filter(r=>!newIds.has(r.id)).length,changed:checked.rows.filter(r=>currentRows.some(old=>old.id===r.id&&old.quantity!==r.quantity)).length},integrityVerified:true,applied:false};
}

Expose the limit of replacing both data and its hash

A person can construct valid replacement data and compute its hash. This consistency check may accept that pair. It establishes that bytes agree with the supplied manifest, not who created the manifest. Protecting trusted references, signatures and separate origin verification have different responsibilities. I therefore do not describe digest equality as a security approval.

The demo compares a normal bundle, payload-only tampering, an unexpected path and a coherent new payload with its new hash. The last condition deliberately passes. That comparison makes integrity and origin trust visibly different questions. All records are fixed synthetic fixtures; the page neither receives nor sends company files, contacts or credentials.

Independent synthetic example; no client records or operational server

Download the implementation together with its evidence

The ZIP includes the model, independent tests, standalone Korean and English HTML demos, boundaries and reuse permission. Extract it and run node test.mjs in a Node environment with global Web Crypto support. Both demos inline identical model code. Modification, redistribution and commercial reuse are permitted without warranty. No external library installation or network connection is required.

Twelve extracted-package test groups cover an independent SHA-256 comparison, byte mismatches, unsupported paths and formats, invalid rehashed rows, duplicates, extra fields and input preservation. Demo event checks include unavailable SHA-256. Encryption, signatures, persistent storage, actual database recovery, multi-file consistency and a recovery-time guarantee are not implemented. The module remains a small, inspectable foundation with a stated responsibility.

Download code, tests and bilingual demos
Includes reuse permission and boundaries