Customer evidence journal
The SDK can build a canonical, hash-chained evidence journal without sending the entries to DataPrism. Your application supplies the persistence adapter; there is no default database, API call, or hosted copy.
import {
CustomerEvidenceJournal,
type EvidenceJournalStore,
} from "@dataprism/sdk/evidence";
const store: EvidenceJournalStore = customerDatabaseAdapter;
const journal = new CustomerEvidenceJournal({
tenantId: "tenant-42",
scopeId: "production-documents",
store,
});
await journal.append({
eventType: "file.write",
outcome: "succeeded",
source: {
kind: "sdk-observed",
coverage: "complete",
},
details: {
dataId: "0x...",
},
});
const records = await journal.readVerified();
const exported = await journal.exportPeriod();The journal assigns sequence 0 and a null previous hash to the first entry.
Later entries name the exact preceding hash. Entries are validated and reduced
to their canonical bytes before the adapter receives them.
Persistence contract
An EvidenceJournalStore must implement three operations:
readHead(scope)returns the current sequence and entry hash.compareAndAppend(scope, expectedHead, entry)atomically compares the head and appends, returningconflictif another writer won the race.readEntries(scope, through)returns the exact contiguous prefix ending at the requested head, even if a later append occurs concurrently.
A stale append raises EvidenceJournalConflictError; it does not retry with an
implicit order or create a second branch. Verified reads reject malformed or
non-canonical bytes, hash mismatches, missing or reordered entries, incomplete
reads, and tenant or scope mismatches.
Verified reads capture the head returned at the start and keep an independent
copy for verification. An adapter that reuses its head object or changes the
through argument cannot move that checkpoint. Later appends belong to a later
read. Cancellation is checked when iteration finishes too, including an empty
read; a cancelled read does not return verified records or a period export.
MemoryEvidenceJournalStore is available for deterministic tests and examples.
It is process-local and non-durable. Do not use it as a customer evidence
record.
Period exports
exportPeriod() verifies the journal snapshot before returning canonical
dataprism.evidence-period.v1 metadata, its exact bytes, and the local records
that produced the root:
const firstPeriod = await journal.exportPeriod();
await submitForSigning(firstPeriod.canonicalPeriod);
// firstPeriod.records remain in the customer environment.
const checkpoint = {
sequence: firstPeriod.period.lastSequence!,
hash: firstPeriod.records.at(-1)!.hash,
};
const nextPeriod = await journal.exportPeriod({ after: checkpoint });The canonical period metadata contains only its version, sequence boundaries, entry count, optional chain anchor, root profile, and root value. It does not contain event details or canonical entry bytes. An anchored export fails if the exact anchor sequence and hash are not present in the verified journal. An export after the current head is an explicit empty period with null boundaries and the RFC 6962 empty root.
The after anchor is copied and validated before storage is read. Changing the
caller's options or anchor while an export is running does not change its
requested boundary.
What the chain establishes
Given a trusted head checkpoint, verification detects edits, gaps, reordering, and truncation before that head. It does not prove that a collector observed every real-world event. A collector can be blind to calls that bypass it, and a store that silently rolls back both entries and its head cannot be detected without an independently retained checkpoint such as a signed period root.
The customer is responsible for adapter atomicity, access control, retention,
backup, restoration, and availability. Event details must not contain scope
keys, credentials, signed URLs, or private keys. collectedAt remains an
observation; established time is added separately to a period artefact.
The customer vector registry captures planned vector IDs and derives their perimeter from this verified log. It does not confirm provider writes or perform deletion.
Event-specific upload, deletion, grant, key-lifecycle and provider audit capture are separate integrations. Period export remains local and does not sign, timestamp, or submit the root by itself.

