@contextgraph/provenance

Immutable provenance ledger with hash chain verification.

Installation

pnpm add @contextgraph/provenance

Overview

The provenance ledger tracks the origin of all data in ContextGraph:

  • Every mutation creates a provenance entry
  • Entries are hash-chained for integrity
  • The chain can be verified at any time

Creating the Ledger

import { ProvenanceLedger } from '@contextgraph/provenance';

const ledger = new ProvenanceLedger(storage);
await ledger.initialize();

Recording Provenance

Basic Record

const entry = await ledger.record({
  type: 'claim_created',
  subjectId: entityId,
  data: { predicate: 'status', value: 'active' },
  source: {
    type: 'agent',
    id: agentId,
    method: 'api_call',
  },
});

Entry Types

TypeDescription
claim_createdNew claim added
claim_revokedClaim revoked
entity_createdNew entity created
entity_updatedEntity properties updated
decision_recordedDecision recorded
decision_approvedDecision approved
decision_rejectedDecision rejected
execution_loggedAction executed
policy_createdPolicy created

Source Types

// Agent source
{ type: 'agent', id: 'agt_123', method: 'process_data' }

// User source
{ type: 'user', id: 'user_456', method: 'manual_entry' }

// System source
{ type: 'system', id: 'scheduler', method: 'batch_job' }

// External source
{ type: 'external', id: 'weather_api', method: 'sync' }

// Inference source
{ type: 'inference', id: 'reasoner', method: 'transitive_closure' }

Querying Provenance

Get by ID

const entry = await ledger.get(provenanceId);

Query by Subject

const entries = await ledger.query({
  subjectId: entityId,
  limit: 100,
});

Query by Type

const claimEntries = await ledger.query({
  type: 'claim_created',
  from: startTime,
  to: endTime,
});

Query by Agent

const agentActions = await ledger.query({
  agentId: agentId,
  limit: 50,
});

Chain Verification

Verify Entire Chain

const result = await ledger.verify();

console.log(`Valid: ${result.valid}`);
console.log(`Entries verified: ${result.entriesVerified}`);
console.log(`Broken links: ${result.brokenLinks}`);
console.log(`Invalid hashes: ${result.invalidHashes}`);

Verify Specific Range

const result = await ledger.verify({
  from: startId,
  to: endId,
});

Entry Structure

interface ProvenanceEntry {
  id: ProvenanceId;
  hash: string;              // SHA-256 hash
  previousHash: string | null; // Link to previous
  type: ProvenanceType;
  subjectId: string;
  data: unknown;
  source: ProvenanceSource;
  timestamp: Timestamp;
  agentId?: AgentId;
}

Hash Calculation

The hash includes:

const hashData = {
  previousHash,
  type,
  subjectId,
  data,
  source,
  timestamp,
};

const hash = sha256(JSON.stringify(hashData));

Statistics

const stats = await ledger.getStats();

console.log(`Total entries: ${stats.totalEntries}`);
console.log(`First entry: ${stats.firstEntryId}`);
console.log(`Last entry: ${stats.lastEntryId}`);

Integrity Monitoring

// Periodic verification
setInterval(async () => {
  const result = await ledger.verify();
  if (!result.valid) {
    alertOps('Provenance chain integrity failure');
  }
}, 60 * 60 * 1000); // Hourly