Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

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, returning conflict if 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.

Copyright © 2026 DataPrism.