rfc64-evidence.ts is a protocol-independent evidence layer for RFC-64 Gate 0+
devnet harnesses. It does not discover peers, transfer data, retry operations,
or modify runtime sync behavior. A harness supplies the expected and observed
Knowledge Asset datasets after its own operation completes.
- UALs are parsed with
parseDeterministicKnowledgeAssetUal, converted to the canonical protocol spelling, rejected on duplicate canonical identity, and sorted lexically. - Received N-Quads are parsed row-by-row and projected to S/P/O before hashing; physical graph placement is never part of semantic equality. Any duplicate S/P/O row after projection is rejected instead of silently deduplicated.
- The placement-neutral dataset is canonicalized with RDFC-1.0, sorted lexically, joined with LF, and terminated by one LF when non-empty.
ualsSha256is SHA-256 over the sorted UAL list (UAL + LFper entry).- Each
semanticNQuadsSha256is SHA-256 over that KA's exact canonical N-Quads UTF-8 bytes. - The snapshot-level semantic digest is SHA-256 over the domain string
rfc64-semantic-nquads-manifest/v1\nfollowed by stable JSON containing the sorted(UAL, quadCount, per-KA digest)manifest. - Stable JSON recursively sorts object keys and rejects sparse/custom arrays,
accessors, symbols, hidden properties, custom prototypes, lossy values, and
non-finite numbers. An own
__proto__key remains ordinary JSON data.
Snapshots contain KA and quad counts plus exact digests, without embedding the potentially large N-Quads payload. Validation recomputes every redundant count and manifest digest before comparison. Duplicate UALs, malformed snapshots, missing KAs, unexpected KAs, count differences, and digest differences cannot produce a passing comparison.
Created and validated snapshots are defensively copied and deeply frozen. Run
evidence closes over its own frozen expected/observed copies, so later caller
mutation cannot change a previously derived passed result. Public constructors
capture caller-owned records and arrays exactly once through data descriptors;
proxies, accessors, sparse/custom arrays, and custom containers are rejected
before caller code can affect validation. String timestamps must carry Z or an
explicit UTC offset, contain a real Gregorian calendar instant, and use at most
millisecond fractional precision. Timestamp inputs are exactly primitive strings
or genuine non-proxy Date objects. Dates are read through the intrinsic
Date.prototype.getTime and normalized through a fresh Date; durations must be
non-negative safe integers. Emitted timestamps use canonical UTC form.
The run artifact adds the stable gate/observer label, selected source peer,
canonical ISO timing and duration, attempt/retry/failure details, expected and
observed snapshots, and the derived comparison. passed is derived from the
comparison and terminal failure; a caller cannot manually force it to true.
Artifact publication uses a same-directory exclusive temporary file, fsyncs
its contents, atomically renames it, and verifies the published bytes. POSIX
publication enforces mode 0600 and fsyncs the containing directory. Every
directory created for a nested artifact path is made durable through a
parent-directory fsync before it is used. The caller must provide a trusted,
static parent-directory topology for the full call. Node has no portable
directory-handle-relative openat/renameat surface, so path checks cannot
prevent a cooperating process from changing a parent between validation and
rename. Initial checks reject existing symlinks and post-operation checks provide
best-effort detection, but an error raised after rename can leave publication
side effects in the changed topology. On Windows, Node cannot fsync a directory
through its ordinary filesystem API and POSIX mode bits do not prove ACL
isolation, so the returned publication metadata explicitly reports
file-flush-rename-no-directory-flush and windows-inherited-acl instead of
claiming the stronger POSIX policies.
import {
createRfc64DevnetEvidence,
createRfc64SemanticSnapshot,
writeStableJsonArtifact,
} from '@origintrail-official/dkg-devnet-harness/rfc64-evidence';
const expected = await createRfc64SemanticSnapshot(expectedAssets);
const observed = await createRfc64SemanticSnapshot(observedAssets);
const evidence = createRfc64DevnetEvidence({
gate: 'gate-1-semantic-recovery',
observer: 'receiver-node-2',
sourcePeerId,
startedAt,
completedAt: new Date(),
attemptCount,
retryFailures,
terminalFailure: null,
expected,
observed,
});
writeStableJsonArtifact(artifactPath, evidence);
if (!evidence.passed) throw new Error('RFC-64 evidence gate failed');Run the focused no-devnet verification with:
pnpm test:devnet:rfc64-evidence
pnpm typecheck:devnet:rfc64-evidence