@totemsdk/proofgraph 1.0.1 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -21
- package/NOTICE +60 -0
- package/README.md +31 -10
- package/dist/canonical.js +11 -19
- package/dist/cjs/canonical.d.ts +22 -0
- package/dist/cjs/canonical.js +51 -0
- package/dist/cjs/durable-store.d.ts +50 -0
- package/dist/cjs/durable-store.js +178 -0
- package/dist/cjs/evidence.d.ts +89 -0
- package/dist/cjs/evidence.js +130 -0
- package/dist/cjs/graph.d.ts +103 -0
- package/dist/cjs/graph.js +298 -0
- package/dist/cjs/index.d.ts +26 -0
- package/dist/cjs/index.js +84 -0
- package/dist/cjs/io.d.ts +26 -0
- package/dist/cjs/io.js +173 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/query.d.ts +85 -0
- package/dist/cjs/query.js +189 -0
- package/dist/cjs/replicated-evidence.d.ts +42 -0
- package/dist/cjs/replicated-evidence.js +98 -0
- package/dist/cjs/types.d.ts +62 -0
- package/dist/cjs/types.js +7 -0
- package/dist/cjs/verify.d.ts +23 -0
- package/dist/cjs/verify.js +77 -0
- package/dist/durable-store.js +9 -12
- package/dist/evidence.js +14 -19
- package/dist/graph.d.ts +11 -0
- package/dist/graph.js +47 -34
- package/dist/index.d.ts +3 -2
- package/dist/index.js +8 -52
- package/dist/io.js +4 -8
- package/dist/query.d.ts +4 -0
- package/dist/query.js +30 -38
- package/dist/replicated-evidence.d.ts +42 -0
- package/dist/replicated-evidence.js +95 -0
- package/dist/types.d.ts +1 -1
- package/dist/types.js +1 -2
- package/dist/verify.js +3 -40
- package/package.json +25 -15
- package/rust/pkg/package.json +2 -2
- package/rust/pkg/proofgraph_wasm_bg.wasm +0 -0
- package/rust/pkg-node/package.json +2 -2
- package/rust/pkg-node/proofgraph_wasm_bg.wasm +0 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Query helpers for ProofGraph — pure read-only utilities.
|
|
3
|
+
*
|
|
4
|
+
* All functions receive a ProofGraph and return data without mutating the graph.
|
|
5
|
+
* Function names match the @totemsdk/proofgraph public API spec exactly.
|
|
6
|
+
*/
|
|
7
|
+
import type { ProofGraph, ProofGraphNode, ProofGraphEdge, ProofGraphEdgeType } from './types.js';
|
|
8
|
+
/**
|
|
9
|
+
* Find a node by its composed node ID (type:refId) or by refId alone.
|
|
10
|
+
*/
|
|
11
|
+
export declare function findNode(graph: ProofGraph, id: string): ProofGraphNode | undefined;
|
|
12
|
+
/**
|
|
13
|
+
* Return all outgoing edges from a node (identified by node ID or refId).
|
|
14
|
+
*/
|
|
15
|
+
export declare function getEdgesFrom(graph: ProofGraph, nodeIdOrRefId: string): ProofGraphEdge[];
|
|
16
|
+
/**
|
|
17
|
+
* Return all incoming edges to a node (identified by node ID or refId).
|
|
18
|
+
*/
|
|
19
|
+
export declare function getEdgesTo(graph: ProofGraph, nodeIdOrRefId: string): ProofGraphEdge[];
|
|
20
|
+
/**
|
|
21
|
+
* Return all edges of a given type.
|
|
22
|
+
*/
|
|
23
|
+
export declare function getEdgesByType(graph: ProofGraph, type: ProofGraphEdgeType): ProofGraphEdge[];
|
|
24
|
+
/**
|
|
25
|
+
* Return all proof nodes in the graph.
|
|
26
|
+
*/
|
|
27
|
+
export declare function getProofNodes(graph: ProofGraph): ProofGraphNode[];
|
|
28
|
+
/**
|
|
29
|
+
* Find all 'proof' nodes connected to a subject via 'about' OR 'proves' edges.
|
|
30
|
+
*/
|
|
31
|
+
export declare function findProofsBySubject(graph: ProofGraph, subjectId: string): ProofGraphNode[];
|
|
32
|
+
/**
|
|
33
|
+
* Find all 'proof' nodes issued by a given address (via 'issued_by' edges).
|
|
34
|
+
*/
|
|
35
|
+
export declare function findProofsByIssuer(graph: ProofGraph, issuerId: string): ProofGraphNode[];
|
|
36
|
+
/**
|
|
37
|
+
* Find all 'anchor' nodes linked to a proof via 'anchored_to' edges.
|
|
38
|
+
*/
|
|
39
|
+
export declare function findAnchorsForProof(graph: ProofGraph, proofId: string): ProofGraphNode[];
|
|
40
|
+
/**
|
|
41
|
+
* Find all 'revokes' edges whose target is the given proof node.
|
|
42
|
+
*/
|
|
43
|
+
export declare function findRevocations(graph: ProofGraph, proofId: string): ProofGraphEdge[];
|
|
44
|
+
/**
|
|
45
|
+
* Find all 'supersedes' edges whose target is the given proof node.
|
|
46
|
+
*/
|
|
47
|
+
export declare function findSupersessions(graph: ProofGraph, proofId: string): ProofGraphEdge[];
|
|
48
|
+
/**
|
|
49
|
+
* Find all 'conflicts_with' edges involving the given proof node (in either direction).
|
|
50
|
+
*/
|
|
51
|
+
export declare function findConflicts(graph: ProofGraph, proofId: string): ProofGraphEdge[];
|
|
52
|
+
/**
|
|
53
|
+
* Traverse 'references' edges from a proof to its evidence nodes.
|
|
54
|
+
* Returns nodes in insertion order (the order edges were added, which matches
|
|
55
|
+
* the original evidence array order from addProof).
|
|
56
|
+
*/
|
|
57
|
+
export declare function getEvidenceTrail(graph: ProofGraph, proofId: string): ProofGraphNode[];
|
|
58
|
+
/**
|
|
59
|
+
* Recursively traverse 'derived_from' edges from a proof node.
|
|
60
|
+
* Returns the ordered chain of ancestor proof nodes (closest ancestor first).
|
|
61
|
+
* Terminates on cycle detection.
|
|
62
|
+
*/
|
|
63
|
+
export declare function getProofLineage(graph: ProofGraph, proofId: string, _visited?: Set<string>): ProofGraphNode[];
|
|
64
|
+
/**
|
|
65
|
+
* Return all proof nodes that are NOT the target of any 'revokes' or 'supersedes' edge.
|
|
66
|
+
* These are the current / active proofs in the graph.
|
|
67
|
+
*/
|
|
68
|
+
export declare function resolveCurrentProofSet(graph: ProofGraph): ProofGraphNode[];
|
|
69
|
+
/**
|
|
70
|
+
* Return all manifest nodes linked to a given address via 'manifests_as' edges.
|
|
71
|
+
*/
|
|
72
|
+
export declare function getManifestsForAddress(graph: ProofGraph, address: string): ProofGraphNode[];
|
|
73
|
+
/**
|
|
74
|
+
* Return all content-addressed revisions of a logical manifest, ordered by envelope ID.
|
|
75
|
+
*/
|
|
76
|
+
export declare function getManifestRevisions(graph: ProofGraph, manifestId: string): ProofGraphNode[];
|
|
77
|
+
/**
|
|
78
|
+
* Return all edges between two nodes (in either direction).
|
|
79
|
+
*/
|
|
80
|
+
export declare function getEdgesBetween(graph: ProofGraph, refIdA: string, refIdB: string): ProofGraphEdge[];
|
|
81
|
+
/**
|
|
82
|
+
* Return the set of all node refIds reachable from a given node via directed edges.
|
|
83
|
+
* Includes the start node itself.
|
|
84
|
+
*/
|
|
85
|
+
export declare function reachableFrom(graph: ProofGraph, startRefId: string, _visited?: Set<string>): Set<string>;
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Query helpers for ProofGraph — pure read-only utilities.
|
|
4
|
+
*
|
|
5
|
+
* All functions receive a ProofGraph and return data without mutating the graph.
|
|
6
|
+
* Function names match the @totemsdk/proofgraph public API spec exactly.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.findNode = findNode;
|
|
10
|
+
exports.getEdgesFrom = getEdgesFrom;
|
|
11
|
+
exports.getEdgesTo = getEdgesTo;
|
|
12
|
+
exports.getEdgesByType = getEdgesByType;
|
|
13
|
+
exports.getProofNodes = getProofNodes;
|
|
14
|
+
exports.findProofsBySubject = findProofsBySubject;
|
|
15
|
+
exports.findProofsByIssuer = findProofsByIssuer;
|
|
16
|
+
exports.findAnchorsForProof = findAnchorsForProof;
|
|
17
|
+
exports.findRevocations = findRevocations;
|
|
18
|
+
exports.findSupersessions = findSupersessions;
|
|
19
|
+
exports.findConflicts = findConflicts;
|
|
20
|
+
exports.getEvidenceTrail = getEvidenceTrail;
|
|
21
|
+
exports.getProofLineage = getProofLineage;
|
|
22
|
+
exports.resolveCurrentProofSet = resolveCurrentProofSet;
|
|
23
|
+
exports.getManifestsForAddress = getManifestsForAddress;
|
|
24
|
+
exports.getManifestRevisions = getManifestRevisions;
|
|
25
|
+
exports.getEdgesBetween = getEdgesBetween;
|
|
26
|
+
exports.reachableFrom = reachableFrom;
|
|
27
|
+
/**
|
|
28
|
+
* Find a node by its composed node ID (type:refId) or by refId alone.
|
|
29
|
+
*/
|
|
30
|
+
function findNode(graph, id) {
|
|
31
|
+
return graph.nodes.find((n) => n.id === id || n.refId === id);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Return all outgoing edges from a node (identified by node ID or refId).
|
|
35
|
+
*/
|
|
36
|
+
function getEdgesFrom(graph, nodeIdOrRefId) {
|
|
37
|
+
const node = findNode(graph, nodeIdOrRefId);
|
|
38
|
+
if (!node)
|
|
39
|
+
return [];
|
|
40
|
+
return graph.edges.filter((e) => e.from === node.refId || e.from === node.id);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Return all incoming edges to a node (identified by node ID or refId).
|
|
44
|
+
*/
|
|
45
|
+
function getEdgesTo(graph, nodeIdOrRefId) {
|
|
46
|
+
const node = findNode(graph, nodeIdOrRefId);
|
|
47
|
+
if (!node)
|
|
48
|
+
return [];
|
|
49
|
+
return graph.edges.filter((e) => e.to === node.refId || e.to === node.id);
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Return all edges of a given type.
|
|
53
|
+
*/
|
|
54
|
+
function getEdgesByType(graph, type) {
|
|
55
|
+
return graph.edges.filter((e) => e.type === type);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Return all proof nodes in the graph.
|
|
59
|
+
*/
|
|
60
|
+
function getProofNodes(graph) {
|
|
61
|
+
return graph.nodes.filter((n) => n.type === 'proof');
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Find all 'proof' nodes connected to a subject via 'about' OR 'proves' edges.
|
|
65
|
+
*/
|
|
66
|
+
function findProofsBySubject(graph, subjectId) {
|
|
67
|
+
const relevantEdges = graph.edges.filter((e) => (e.type === 'about' || e.type === 'proves') && e.to === subjectId);
|
|
68
|
+
const proofRefIds = new Set(relevantEdges.map((e) => e.from));
|
|
69
|
+
return graph.nodes.filter((n) => proofRefIds.has(n.refId) && n.type === 'proof');
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Find all 'proof' nodes issued by a given address (via 'issued_by' edges).
|
|
73
|
+
*/
|
|
74
|
+
function findProofsByIssuer(graph, issuerId) {
|
|
75
|
+
const issuedEdges = graph.edges.filter((e) => e.type === 'issued_by' && e.to === issuerId);
|
|
76
|
+
const proofRefIds = new Set(issuedEdges.map((e) => e.from));
|
|
77
|
+
return graph.nodes.filter((n) => proofRefIds.has(n.refId) && n.type === 'proof');
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Find all 'anchor' nodes linked to a proof via 'anchored_to' edges.
|
|
81
|
+
*/
|
|
82
|
+
function findAnchorsForProof(graph, proofId) {
|
|
83
|
+
const anchoredEdges = graph.edges.filter((e) => e.type === 'anchored_to' && e.from === proofId);
|
|
84
|
+
const anchorRefIds = new Set(anchoredEdges.map((e) => e.to));
|
|
85
|
+
return graph.nodes.filter((n) => anchorRefIds.has(n.refId) && n.type === 'anchor');
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Find all 'revokes' edges whose target is the given proof node.
|
|
89
|
+
*/
|
|
90
|
+
function findRevocations(graph, proofId) {
|
|
91
|
+
return graph.edges.filter((e) => e.type === 'revokes' && e.to === proofId);
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Find all 'supersedes' edges whose target is the given proof node.
|
|
95
|
+
*/
|
|
96
|
+
function findSupersessions(graph, proofId) {
|
|
97
|
+
return graph.edges.filter((e) => e.type === 'supersedes' && e.to === proofId);
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Find all 'conflicts_with' edges involving the given proof node (in either direction).
|
|
101
|
+
*/
|
|
102
|
+
function findConflicts(graph, proofId) {
|
|
103
|
+
return graph.edges.filter((e) => e.type === 'conflicts_with' &&
|
|
104
|
+
(e.from === proofId || e.to === proofId));
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Traverse 'references' edges from a proof to its evidence nodes.
|
|
108
|
+
* Returns nodes in insertion order (the order edges were added, which matches
|
|
109
|
+
* the original evidence array order from addProof).
|
|
110
|
+
*/
|
|
111
|
+
function getEvidenceTrail(graph, proofId) {
|
|
112
|
+
const refEdges = graph.edges.filter((e) => e.type === 'references' && e.from === proofId);
|
|
113
|
+
return refEdges
|
|
114
|
+
.map((e) => graph.nodes.find((n) => n.refId === e.to && n.type === 'evidence'))
|
|
115
|
+
.filter((n) => n !== undefined);
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Recursively traverse 'derived_from' edges from a proof node.
|
|
119
|
+
* Returns the ordered chain of ancestor proof nodes (closest ancestor first).
|
|
120
|
+
* Terminates on cycle detection.
|
|
121
|
+
*/
|
|
122
|
+
function getProofLineage(graph, proofId, _visited = new Set()) {
|
|
123
|
+
if (_visited.has(proofId))
|
|
124
|
+
return [];
|
|
125
|
+
_visited.add(proofId);
|
|
126
|
+
const derivedEdges = graph.edges.filter((e) => e.type === 'derived_from' && e.from === proofId);
|
|
127
|
+
const result = [];
|
|
128
|
+
for (const edge of derivedEdges) {
|
|
129
|
+
const node = graph.nodes.find((n) => n.refId === edge.to);
|
|
130
|
+
if (node) {
|
|
131
|
+
result.push(node);
|
|
132
|
+
result.push(...getProofLineage(graph, edge.to, _visited));
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return result;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Return all proof nodes that are NOT the target of any 'revokes' or 'supersedes' edge.
|
|
139
|
+
* These are the current / active proofs in the graph.
|
|
140
|
+
*/
|
|
141
|
+
function resolveCurrentProofSet(graph) {
|
|
142
|
+
const supersededOrRevoked = new Set();
|
|
143
|
+
for (const edge of graph.edges) {
|
|
144
|
+
if (edge.type === 'revokes' || edge.type === 'supersedes') {
|
|
145
|
+
supersededOrRevoked.add(edge.to);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return graph.nodes.filter((n) => n.type === 'proof' && !supersededOrRevoked.has(n.refId));
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Return all manifest nodes linked to a given address via 'manifests_as' edges.
|
|
152
|
+
*/
|
|
153
|
+
function getManifestsForAddress(graph, address) {
|
|
154
|
+
const manifestEdges = graph.edges.filter((e) => e.type === 'manifests_as' && e.to === address);
|
|
155
|
+
const manifestRefIds = new Set(manifestEdges.map((e) => e.from));
|
|
156
|
+
return graph.nodes.filter((n) => manifestRefIds.has(n.refId) && n.type === 'manifest');
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Return all content-addressed revisions of a logical manifest, ordered by envelope ID.
|
|
160
|
+
*/
|
|
161
|
+
function getManifestRevisions(graph, manifestId) {
|
|
162
|
+
const revisionRefs = new Set(graph.edges
|
|
163
|
+
.filter((edge) => edge.type === 'revision_of' && edge.to === manifestId)
|
|
164
|
+
.map((edge) => edge.from));
|
|
165
|
+
return graph.nodes
|
|
166
|
+
.filter((node) => node.type === 'manifest' && revisionRefs.has(node.refId))
|
|
167
|
+
.sort((a, b) => a.refId.localeCompare(b.refId));
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Return all edges between two nodes (in either direction).
|
|
171
|
+
*/
|
|
172
|
+
function getEdgesBetween(graph, refIdA, refIdB) {
|
|
173
|
+
return graph.edges.filter((e) => (e.from === refIdA && e.to === refIdB) ||
|
|
174
|
+
(e.from === refIdB && e.to === refIdA));
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Return the set of all node refIds reachable from a given node via directed edges.
|
|
178
|
+
* Includes the start node itself.
|
|
179
|
+
*/
|
|
180
|
+
function reachableFrom(graph, startRefId, _visited = new Set()) {
|
|
181
|
+
if (_visited.has(startRefId))
|
|
182
|
+
return _visited;
|
|
183
|
+
_visited.add(startRefId);
|
|
184
|
+
const outgoing = graph.edges.filter((e) => e.from === startRefId);
|
|
185
|
+
for (const edge of outgoing) {
|
|
186
|
+
reachableFrom(graph, edge.to, _visited);
|
|
187
|
+
}
|
|
188
|
+
return _visited;
|
|
189
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RFC-057 Phase 4 — replicated proofgraph evidence adapter.
|
|
3
|
+
*
|
|
4
|
+
* Keeps the authoritative proofgraph evidence store local while allowing a
|
|
5
|
+
* replicated index to locate the same digest-verified artifacts on other
|
|
6
|
+
* peers. The replicated index is never trusted for bytes: every read
|
|
7
|
+
* re-verifies the artifact digest.
|
|
8
|
+
*/
|
|
9
|
+
import type { ArtifactRef, ArtifactStore } from '@totemsdk/storage/artifacts';
|
|
10
|
+
import type { ProofGraph } from './types.js';
|
|
11
|
+
export interface ReplicatedEvidenceIndexEntry {
|
|
12
|
+
readonly nodeId: string;
|
|
13
|
+
readonly ref: ArtifactRef;
|
|
14
|
+
readonly contentHash: string;
|
|
15
|
+
readonly storedAt: number;
|
|
16
|
+
}
|
|
17
|
+
export interface ReplicatedEvidenceIndexPort {
|
|
18
|
+
get(key: string): Promise<unknown | null>;
|
|
19
|
+
put(key: string, value: unknown): Promise<void>;
|
|
20
|
+
list?(prefix?: string): AsyncIterable<{
|
|
21
|
+
key: string;
|
|
22
|
+
value: unknown;
|
|
23
|
+
}>;
|
|
24
|
+
}
|
|
25
|
+
export interface ReplicatedEvidenceRead {
|
|
26
|
+
readonly nodeId: string;
|
|
27
|
+
readonly status: 'ok' | 'not-found' | 'corrupt' | 'unavailable';
|
|
28
|
+
readonly ref?: ArtifactRef;
|
|
29
|
+
readonly bytes?: Uint8Array;
|
|
30
|
+
readonly message?: string;
|
|
31
|
+
}
|
|
32
|
+
export interface ReplicatedProofGraphEvidenceStore {
|
|
33
|
+
putEvidence(graph: ProofGraph, bytesFor: (nodeId: string) => Uint8Array | undefined): Promise<ArtifactRef[]>;
|
|
34
|
+
getEvidence(nodeId: string): Promise<ReplicatedEvidenceRead>;
|
|
35
|
+
verifyEvidence(nodeId: string): Promise<boolean>;
|
|
36
|
+
listEvidence(): AsyncIterable<ReplicatedEvidenceIndexEntry>;
|
|
37
|
+
}
|
|
38
|
+
export declare function createReplicatedProofGraphEvidenceStore(options: {
|
|
39
|
+
artifacts: ArtifactStore;
|
|
40
|
+
index: ReplicatedEvidenceIndexPort;
|
|
41
|
+
namespace?: string;
|
|
42
|
+
}): ReplicatedProofGraphEvidenceStore;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* RFC-057 Phase 4 — replicated proofgraph evidence adapter.
|
|
4
|
+
*
|
|
5
|
+
* Keeps the authoritative proofgraph evidence store local while allowing a
|
|
6
|
+
* replicated index to locate the same digest-verified artifacts on other
|
|
7
|
+
* peers. The replicated index is never trusted for bytes: every read
|
|
8
|
+
* re-verifies the artifact digest.
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.createReplicatedProofGraphEvidenceStore = createReplicatedProofGraphEvidenceStore;
|
|
12
|
+
const core_1 = require("@totemsdk/core");
|
|
13
|
+
const errors_1 = require("@totemsdk/storage/errors");
|
|
14
|
+
const KEY_PREFIX = 'proofgraph:evidence:';
|
|
15
|
+
const DEFAULT_NAMESPACE = 'totem-proofgraph-evidence';
|
|
16
|
+
function normalizeHash(value) {
|
|
17
|
+
return value.replace(/^0x/i, '').toLowerCase();
|
|
18
|
+
}
|
|
19
|
+
function evidenceNodes(graph) {
|
|
20
|
+
return graph.nodes.filter((node) => node.type === 'evidence');
|
|
21
|
+
}
|
|
22
|
+
function contentHashOf(node) {
|
|
23
|
+
const value = node.data?.contentHash;
|
|
24
|
+
return typeof value === 'string' && value.length > 0 ? value : undefined;
|
|
25
|
+
}
|
|
26
|
+
function createReplicatedProofGraphEvidenceStore(options) {
|
|
27
|
+
if (!options.artifacts.capabilities.writable) {
|
|
28
|
+
throw new Error('replicated evidence store requires a writable artifact store');
|
|
29
|
+
}
|
|
30
|
+
const namespace = options.namespace ?? DEFAULT_NAMESPACE;
|
|
31
|
+
const key = (nodeId) => `${KEY_PREFIX}${nodeId}`;
|
|
32
|
+
return {
|
|
33
|
+
async putEvidence(graph, bytesFor) {
|
|
34
|
+
const refs = [];
|
|
35
|
+
for (const node of evidenceNodes(graph)) {
|
|
36
|
+
const bytes = bytesFor(node.id);
|
|
37
|
+
if (!bytes)
|
|
38
|
+
continue;
|
|
39
|
+
const digest = (0, core_1.toHex)((0, core_1.sha3_256)(bytes));
|
|
40
|
+
const contentHash = contentHashOf(node);
|
|
41
|
+
if (contentHash && normalizeHash(contentHash) !== digest) {
|
|
42
|
+
throw new errors_1.StorageError(`evidence commitment mismatch for node "${node.id}"`, 'corrupt', {
|
|
43
|
+
key: key(node.id),
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
const receipt = await options.artifacts.put(namespace, bytes);
|
|
47
|
+
if (contentHash && normalizeHash(contentHash) !== receipt.ref.digest) {
|
|
48
|
+
throw new errors_1.StorageError(`artifact digest mismatch for node "${node.id}"`, 'corrupt', {
|
|
49
|
+
key: key(node.id),
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
await options.index.put(key(node.id), {
|
|
53
|
+
nodeId: node.id,
|
|
54
|
+
ref: receipt.ref,
|
|
55
|
+
contentHash: contentHash ?? receipt.ref.digest,
|
|
56
|
+
storedAt: Date.now(),
|
|
57
|
+
});
|
|
58
|
+
refs.push(receipt.ref);
|
|
59
|
+
}
|
|
60
|
+
return refs;
|
|
61
|
+
},
|
|
62
|
+
async getEvidence(nodeId) {
|
|
63
|
+
const raw = await options.index.get(key(nodeId));
|
|
64
|
+
if (!raw)
|
|
65
|
+
return { nodeId, status: 'not-found' };
|
|
66
|
+
const entry = raw;
|
|
67
|
+
const read = await options.artifacts.get(entry.ref);
|
|
68
|
+
if (read.status !== 'ok' || !read.bytes) {
|
|
69
|
+
return {
|
|
70
|
+
nodeId,
|
|
71
|
+
status: read.status,
|
|
72
|
+
ref: entry.ref,
|
|
73
|
+
...(read.message ? { message: read.message } : {}),
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
const digest = (0, core_1.toHex)((0, core_1.sha3_256)(read.bytes));
|
|
77
|
+
if (normalizeHash(entry.contentHash) !== digest) {
|
|
78
|
+
return {
|
|
79
|
+
nodeId,
|
|
80
|
+
status: 'corrupt',
|
|
81
|
+
ref: entry.ref,
|
|
82
|
+
message: `evidence digest mismatch: ${digest} !== ${entry.contentHash}`,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
return { nodeId, status: 'ok', ref: entry.ref, bytes: read.bytes };
|
|
86
|
+
},
|
|
87
|
+
async verifyEvidence(nodeId) {
|
|
88
|
+
return (await this.getEvidence(nodeId)).status === 'ok';
|
|
89
|
+
},
|
|
90
|
+
async *listEvidence() {
|
|
91
|
+
if (!options.index.list)
|
|
92
|
+
return;
|
|
93
|
+
for await (const entry of options.index.list(KEY_PREFIX)) {
|
|
94
|
+
yield entry.value;
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @totemsdk/proofgraph — Type definitions
|
|
3
|
+
*
|
|
4
|
+
* Pure schema — no network, no DHT, no blockchain submission, no crypto.
|
|
5
|
+
*/
|
|
6
|
+
export type ProofGraphNodeType = 'proof' | 'identity' | 'identity-claim' | 'manifest' | 'address' | 'subject' | 'evidence' | 'anchor' | 'receipt' | 'payment' | 'device' | 'service' | 'policy' | 'custom';
|
|
7
|
+
export type ProofGraphEdgeType = 'proves' | 'issued_by' | 'signed_by' | 'about' | 'references' | 'derived_from' | 'supports' | 'contradicts' | 'supersedes' | 'revokes' | 'anchored_to' | 'delegates_to' | 'controls' | 'depends_on' | 'manifests_as' | 'revision_of' | 'conflicts_with';
|
|
8
|
+
export interface ProofGraphNode {
|
|
9
|
+
id: string;
|
|
10
|
+
type: ProofGraphNodeType;
|
|
11
|
+
refId: string;
|
|
12
|
+
data?: Record<string, unknown>;
|
|
13
|
+
createdAt: number;
|
|
14
|
+
}
|
|
15
|
+
export interface ProofGraphEdge {
|
|
16
|
+
id: string;
|
|
17
|
+
type: ProofGraphEdgeType;
|
|
18
|
+
from: string;
|
|
19
|
+
to: string;
|
|
20
|
+
proofId?: string;
|
|
21
|
+
data?: Record<string, unknown>;
|
|
22
|
+
}
|
|
23
|
+
export interface ProofGraph {
|
|
24
|
+
graphId: string;
|
|
25
|
+
nodes: ProofGraphNode[];
|
|
26
|
+
edges: ProofGraphEdge[];
|
|
27
|
+
createdAt: number;
|
|
28
|
+
metadata?: Record<string, unknown>;
|
|
29
|
+
}
|
|
30
|
+
export interface ProofGraphVerifyResult {
|
|
31
|
+
valid: boolean;
|
|
32
|
+
invalidProofs: string[];
|
|
33
|
+
reason?: string;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Input object for addReceiptLike.
|
|
37
|
+
*/
|
|
38
|
+
export interface ReceiptLikeInput {
|
|
39
|
+
id: string;
|
|
40
|
+
data?: Record<string, unknown>;
|
|
41
|
+
proofId?: string;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Input object for addAnchor.
|
|
45
|
+
*/
|
|
46
|
+
export interface AnchorInput {
|
|
47
|
+
hash: string;
|
|
48
|
+
provider?: string;
|
|
49
|
+
txId?: string;
|
|
50
|
+
confirmedAt?: number;
|
|
51
|
+
metadata?: Record<string, unknown>;
|
|
52
|
+
proofId?: string;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Storage port — interface only, no concrete implementation in this package.
|
|
56
|
+
* Adapters (SQLite, LevelDB, in-memory) live in consumer packages.
|
|
57
|
+
*/
|
|
58
|
+
export interface ProofGraphStoragePort {
|
|
59
|
+
save(graph: ProofGraph): Promise<void>;
|
|
60
|
+
load(graphId: string): Promise<ProofGraph | null>;
|
|
61
|
+
findByNodeId(id: string): Promise<ProofGraph | null>;
|
|
62
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Graph-level verification.
|
|
3
|
+
*
|
|
4
|
+
* verifyProofGraph: iterates proof nodes, recovers SignedProof from node.data,
|
|
5
|
+
* and delegates to @totemsdk/proof's verifyProof for each one.
|
|
6
|
+
* Never reimplements cryptography.
|
|
7
|
+
*
|
|
8
|
+
* verifyGraphProofs: same logic, returns the list of invalid proof IDs (string[]).
|
|
9
|
+
*/
|
|
10
|
+
import type { ProofGraph, ProofGraphVerifyResult } from './types.js';
|
|
11
|
+
/**
|
|
12
|
+
* Verify every proof node stored in the graph.
|
|
13
|
+
*
|
|
14
|
+
* Iterates nodes of type 'proof', casts node.data back to SignedProof,
|
|
15
|
+
* and calls proofModule.verifyProof() from @totemsdk/proof.
|
|
16
|
+
* Returns { valid: true } if ALL proofs pass; otherwise lists the failing proofIds.
|
|
17
|
+
*/
|
|
18
|
+
export declare function verifyProofGraph(graph: ProofGraph, _options?: Record<string, unknown>): ProofGraphVerifyResult;
|
|
19
|
+
/**
|
|
20
|
+
* Returns the list of invalid proof IDs in the graph.
|
|
21
|
+
* Equivalent to verifyProofGraph(graph).invalidProofs.
|
|
22
|
+
*/
|
|
23
|
+
export declare function verifyGraphProofs(graph: ProofGraph): string[];
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Graph-level verification.
|
|
4
|
+
*
|
|
5
|
+
* verifyProofGraph: iterates proof nodes, recovers SignedProof from node.data,
|
|
6
|
+
* and delegates to @totemsdk/proof's verifyProof for each one.
|
|
7
|
+
* Never reimplements cryptography.
|
|
8
|
+
*
|
|
9
|
+
* verifyGraphProofs: same logic, returns the list of invalid proof IDs (string[]).
|
|
10
|
+
*/
|
|
11
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
12
|
+
if (k2 === undefined) k2 = k;
|
|
13
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
14
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
15
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
16
|
+
}
|
|
17
|
+
Object.defineProperty(o, k2, desc);
|
|
18
|
+
}) : (function(o, m, k, k2) {
|
|
19
|
+
if (k2 === undefined) k2 = k;
|
|
20
|
+
o[k2] = m[k];
|
|
21
|
+
}));
|
|
22
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
23
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
24
|
+
}) : function(o, v) {
|
|
25
|
+
o["default"] = v;
|
|
26
|
+
});
|
|
27
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
28
|
+
var ownKeys = function(o) {
|
|
29
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
30
|
+
var ar = [];
|
|
31
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
32
|
+
return ar;
|
|
33
|
+
};
|
|
34
|
+
return ownKeys(o);
|
|
35
|
+
};
|
|
36
|
+
return function (mod) {
|
|
37
|
+
if (mod && mod.__esModule) return mod;
|
|
38
|
+
var result = {};
|
|
39
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
40
|
+
__setModuleDefault(result, mod);
|
|
41
|
+
return result;
|
|
42
|
+
};
|
|
43
|
+
})();
|
|
44
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
|
+
exports.verifyProofGraph = verifyProofGraph;
|
|
46
|
+
exports.verifyGraphProofs = verifyGraphProofs;
|
|
47
|
+
const proofModule = __importStar(require("@totemsdk/proof"));
|
|
48
|
+
/**
|
|
49
|
+
* Verify every proof node stored in the graph.
|
|
50
|
+
*
|
|
51
|
+
* Iterates nodes of type 'proof', casts node.data back to SignedProof,
|
|
52
|
+
* and calls proofModule.verifyProof() from @totemsdk/proof.
|
|
53
|
+
* Returns { valid: true } if ALL proofs pass; otherwise lists the failing proofIds.
|
|
54
|
+
*/
|
|
55
|
+
function verifyProofGraph(graph, _options) {
|
|
56
|
+
const invalidProofs = [];
|
|
57
|
+
for (const node of graph.nodes) {
|
|
58
|
+
if (node.type !== 'proof' || !node.data)
|
|
59
|
+
continue;
|
|
60
|
+
const signedProof = node.data;
|
|
61
|
+
const result = proofModule.verifyProof(signedProof);
|
|
62
|
+
if (!result.valid) {
|
|
63
|
+
invalidProofs.push(node.refId);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return {
|
|
67
|
+
valid: invalidProofs.length === 0,
|
|
68
|
+
invalidProofs,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Returns the list of invalid proof IDs in the graph.
|
|
73
|
+
* Equivalent to verifyProofGraph(graph).invalidProofs.
|
|
74
|
+
*/
|
|
75
|
+
function verifyGraphProofs(graph) {
|
|
76
|
+
return verifyProofGraph(graph).invalidProofs;
|
|
77
|
+
}
|
package/dist/durable-store.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
/**
|
|
3
2
|
* Durable `ProofGraphStoragePort` adapter for @totemsdk/proofgraph.
|
|
4
3
|
*
|
|
@@ -16,11 +15,9 @@
|
|
|
16
15
|
* recomputes the graphId — a tampered or truncated record surfaces as
|
|
17
16
|
* `StorageError` `corrupt`, never as `not-found` (RFC-007 §4.2 strict).
|
|
18
17
|
*/
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const errors_1 = require("@totemsdk/storage/errors");
|
|
23
|
-
const io_js_1 = require("./io.js");
|
|
18
|
+
import { assertCapabilities } from '@totemsdk/storage/types';
|
|
19
|
+
import { StorageError } from '@totemsdk/storage/errors';
|
|
20
|
+
import { exportProofGraph, importProofGraph } from './io.js';
|
|
24
21
|
const DEFAULT_NAMESPACE = 'totem_proofgraph:v1:';
|
|
25
22
|
const GRAPH_PREFIX = 'graph:';
|
|
26
23
|
const NODE_PREFIX = 'node:';
|
|
@@ -45,10 +42,10 @@ function isCasStore(adapter) {
|
|
|
45
42
|
* so concurrent saves never clobber a newer graph. A non-CAS or low-durability
|
|
46
43
|
* adapter is rejected at construction (RFC-007 §4.2 no-silent-downgrade).
|
|
47
44
|
*/
|
|
48
|
-
function createDurableProofGraphStore(adapter, options = {}) {
|
|
45
|
+
export function createDurableProofGraphStore(adapter, options = {}) {
|
|
49
46
|
const namespace = options.namespace ?? DEFAULT_NAMESPACE;
|
|
50
47
|
if (typeof adapter.capabilities === 'object') {
|
|
51
|
-
|
|
48
|
+
assertCapabilities(adapter, {
|
|
52
49
|
acknowledge: options.requireAckMode ?? 'durably-acknowledged',
|
|
53
50
|
atomic: true,
|
|
54
51
|
conditional: true,
|
|
@@ -70,14 +67,14 @@ function createDurableProofGraphStore(adapter, options = {}) {
|
|
|
70
67
|
};
|
|
71
68
|
function parseGraph(graphId, raw) {
|
|
72
69
|
if (typeof raw !== 'string' && (typeof raw !== 'object' || raw === null || typeof raw.json !== 'string')) {
|
|
73
|
-
throw new
|
|
70
|
+
throw new StorageError(`proofgraph record "${graphId}" is malformed`, 'corrupt', { key: graphKey(graphId) });
|
|
74
71
|
}
|
|
75
72
|
const json = typeof raw === 'string' ? raw : raw.json;
|
|
76
73
|
try {
|
|
77
|
-
return
|
|
74
|
+
return importProofGraph(json);
|
|
78
75
|
}
|
|
79
76
|
catch (err) {
|
|
80
|
-
throw new
|
|
77
|
+
throw new StorageError(`proofgraph record "${graphId}" failed validation: ${err.message}`, 'corrupt', { key: graphKey(graphId), cause: err });
|
|
81
78
|
}
|
|
82
79
|
}
|
|
83
80
|
async function loadUnchecked(graphId) {
|
|
@@ -104,7 +101,7 @@ function createDurableProofGraphStore(adapter, options = {}) {
|
|
|
104
101
|
const savedAt = nextSavedAt();
|
|
105
102
|
return withStorageLock(adapter, async () => {
|
|
106
103
|
// Immutable content-addressed record first (idempotent, safe on retry).
|
|
107
|
-
const record = { json:
|
|
104
|
+
const record = { json: exportProofGraph(graph), savedAt };
|
|
108
105
|
await adapter.set(graphKey(graph.graphId), record);
|
|
109
106
|
// Reverse index: most-recent snapshot wins (CAS, never clobbers newer).
|
|
110
107
|
for (const node of graph.nodes) {
|