@totemsdk/raster-proof 0.1.0
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 +21 -0
- package/README.md +383 -0
- package/dist/canonical.d.ts +40 -0
- package/dist/canonical.js +76 -0
- package/dist/hash.d.ts +19 -0
- package/dist/hash.js +32 -0
- package/dist/index.d.ts +36 -0
- package/dist/index.js +72 -0
- package/dist/manifest.d.ts +24 -0
- package/dist/manifest.js +146 -0
- package/dist/merkle.d.ts +49 -0
- package/dist/merkle.js +171 -0
- package/dist/proof.d.ts +54 -0
- package/dist/proof.js +247 -0
- package/dist/proofgraph.d.ts +51 -0
- package/dist/proofgraph.js +120 -0
- package/dist/provenance.d.ts +29 -0
- package/dist/provenance.js +95 -0
- package/dist/spatial.d.ts +23 -0
- package/dist/spatial.js +77 -0
- package/dist/types.d.ts +189 -0
- package/dist/types.js +14 -0
- package/dist/window.d.ts +19 -0
- package/dist/window.js +59 -0
- package/package.json +69 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Totem SDK Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
# @totemsdk/raster-proof
|
|
2
|
+
|
|
3
|
+
Edge-capable raster and visual evidence proof primitives for Totem Edge — asset hashes, tile Merkle roots, raster manifests, derived-layer provenance, and proof envelope integration.
|
|
4
|
+
|
|
5
|
+
No network. No storage. No GDAL. No GeoTIFF parsing. No ML. Pure hashing, manifest, provenance, and proof primitives that run safely on a drone, ship, robot, vehicle, site camera, field laptop, or edge gateway.
|
|
6
|
+
|
|
7
|
+
## Mission
|
|
8
|
+
|
|
9
|
+
`@totemsdk/raster-proof` lets drones, robots, ships, vehicles, site cameras, satellites, field laptops, and edge gateways create verifiable proof records for imagery and raster-like data:
|
|
10
|
+
|
|
11
|
+
- drone photos
|
|
12
|
+
- video segments
|
|
13
|
+
- satellite scene windows
|
|
14
|
+
- GeoTIFF / COG references
|
|
15
|
+
- map tiles
|
|
16
|
+
- orthomosaic outputs
|
|
17
|
+
- thermal images
|
|
18
|
+
- lidar-derived rasters
|
|
19
|
+
- robot camera frames
|
|
20
|
+
- ship radar snapshots
|
|
21
|
+
- flood masks
|
|
22
|
+
- vegetation masks
|
|
23
|
+
- change-detection masks
|
|
24
|
+
- construction progress images
|
|
25
|
+
- derived analysis layers
|
|
26
|
+
|
|
27
|
+
It is **not ZEDD-specific**. ZEDD Satellite is only one application.
|
|
28
|
+
|
|
29
|
+
### What this package does
|
|
30
|
+
|
|
31
|
+
```text
|
|
32
|
+
asset metadata
|
|
33
|
+
+ byte/content hash
|
|
34
|
+
+ optional chunk Merkle root
|
|
35
|
+
+ optional tile/window proof
|
|
36
|
+
+ spatial metadata
|
|
37
|
+
+ provenance
|
|
38
|
+
+ signed proof envelope
|
|
39
|
+
+ proofgraph linkage
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### What this package does NOT do
|
|
43
|
+
|
|
44
|
+
```text
|
|
45
|
+
✗ GDAL
|
|
46
|
+
✗ full GeoTIFF parsing
|
|
47
|
+
✗ satellite provider APIs
|
|
48
|
+
✗ STAC search
|
|
49
|
+
✗ cloud masking
|
|
50
|
+
✗ NDVI calculation
|
|
51
|
+
✗ ML segmentation
|
|
52
|
+
✗ orthomosaic generation
|
|
53
|
+
✗ image tile server
|
|
54
|
+
✗ map renderer
|
|
55
|
+
✗ file-system storage
|
|
56
|
+
✗ network storage
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Heavy processing belongs elsewhere. This package only hashes bytes and records the metadata that makes them verifiable.
|
|
60
|
+
|
|
61
|
+
## Important wording
|
|
62
|
+
|
|
63
|
+
> This package proves bytes, manifests, provenance, windows and declared relationships. It does **not** prove that a visual interpretation is correct unless a reviewer or downstream model proof says so.
|
|
64
|
+
|
|
65
|
+
A `RasterManifest` and its proof say: "these bytes, captured by this device/operator/mission, at this time, with this content hash / Merkle root, in this spatial context". They do not claim "this water mask is correct". Interpretation correctness is a separate, explicit claim.
|
|
66
|
+
|
|
67
|
+
## Edge-safe design
|
|
68
|
+
|
|
69
|
+
- **On a small device:** hash bytes, create manifests, sign capture records, verify Merkle proofs.
|
|
70
|
+
- **On a stronger edge node:** verify derived-layer provenance, evaluate spatial relations, build proof graphs.
|
|
71
|
+
- SHA3-256 only (from `@totemsdk/core`). No WASM gate, no Node crypto dependency, no heavy GIS imports. Deterministic everywhere — identical input always produces identical IDs, hashes, and proof IDs.
|
|
72
|
+
|
|
73
|
+
## Installation
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
npm install @totemsdk/raster-proof
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Package scope
|
|
80
|
+
|
|
81
|
+
- Byte / string hashing (`hashBytes`, `hashString`)
|
|
82
|
+
- Edge-safe Merkle chunking (default 64 KiB), root, inclusion proofs (domain-separated `totem-raster-leaf` / `totem-raster-node`)
|
|
83
|
+
- Deterministic raster manifests (`totem:raster:<sha3-256-hex>`) with asset, spatial, and provenance metadata
|
|
84
|
+
- Manifest validation with structured errors/warnings
|
|
85
|
+
- Window / tile proofs (`totem:raster-window:<sha3-256-hex>`)
|
|
86
|
+
- Derived-raster provenance (`createDerivedRasterManifest`, `verifyRasterDerivation`)
|
|
87
|
+
- `@totemsdk/proof` evidence refs, unsigned proof creation, WOTS signing, end-to-end verification
|
|
88
|
+
- `@totemsdk/spatial-proof` integration (raster footprint → spatial object, spatial relations)
|
|
89
|
+
- `@totemsdk/proofgraph` node/edge helpers
|
|
90
|
+
|
|
91
|
+
### Dependencies
|
|
92
|
+
|
|
93
|
+
Only what is imported: `@totemsdk/core` (SHA3-256), `@totemsdk/proof` (envelopes), `@totemsdk/proofgraph` (graph helpers), `@totemsdk/spatial-proof` (relations).
|
|
94
|
+
|
|
95
|
+
## Merkle chunking
|
|
96
|
+
|
|
97
|
+
- Default chunk size **64 KiB**; empty bytes are rejected; chunk size must be positive.
|
|
98
|
+
- Chunk content hash = SHA3-256 of the raw bytes. Merkle leaves and internal nodes are domain-separated, so a chunk hash can never be confused with a node hash.
|
|
99
|
+
- **Odd-layer rule (deterministic):** when a Merkle level has an odd number of hashes, the last hash is **promoted** unchanged to the next level (it is NOT duplicated or hashed with itself).
|
|
100
|
+
- All hashes are lowercase hex without `0x`.
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
import { chunkBytes, computeMerkleRoot, createRasterMerkleSummary } from '@totemsdk/raster-proof';
|
|
104
|
+
|
|
105
|
+
const bytes = new TextEncoder().encode('drone photo bytes…');
|
|
106
|
+
|
|
107
|
+
const summary = createRasterMerkleSummary(bytes, { chunkSizeBytes: 64 * 1024 });
|
|
108
|
+
// { contentHash, merkleRoot, chunkSizeBytes, chunkCount, byteSize }
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## IDs and hashing
|
|
112
|
+
|
|
113
|
+
| ID / hash | Shape | Stable fields exclude |
|
|
114
|
+
|-----------|-------|----------------------|
|
|
115
|
+
| `rasterId` | `totem:raster:<sha3-256-hex>` | `rasterId`, `metadata` |
|
|
116
|
+
| `windowProofId` | `totem:raster-window:<sha3-256-hex>` | `windowProofId`, `metadata` |
|
|
117
|
+
|
|
118
|
+
## API table
|
|
119
|
+
|
|
120
|
+
### Hashing
|
|
121
|
+
|
|
122
|
+
| Export | Description |
|
|
123
|
+
|--------|-------------|
|
|
124
|
+
| `hashBytes(bytes)` | SHA3-256 of raw bytes → lowercase hex |
|
|
125
|
+
| `hashString(value)` | SHA3-256 of a UTF-8 string → lowercase hex |
|
|
126
|
+
| `hashSubarray(bytes, offset, length)` | Hash a byte window in place |
|
|
127
|
+
|
|
128
|
+
### Merkle
|
|
129
|
+
|
|
130
|
+
| Export | Description |
|
|
131
|
+
|--------|-------------|
|
|
132
|
+
| `DEFAULT_CHUNK_SIZE_BYTES` | 64 KiB |
|
|
133
|
+
| `chunkBytes(bytes, chunkSizeBytes?)` | Split into content-hashed chunks |
|
|
134
|
+
| `merkleLeafHash(chunk)` | Domain-separated leaf hash of a chunk |
|
|
135
|
+
| `computeMerkleRoot(chunks)` | Deterministic root (promote odd last hash) |
|
|
136
|
+
| `createMerkleProof(chunks, leafIndex)` | Inclusion proof with siblings |
|
|
137
|
+
| `verifyMerkleProof(proof)` | Recompute root from leaf + siblings |
|
|
138
|
+
| `createRasterMerkleSummary(bytes, options?)` | Hash + chunk + root in one pass |
|
|
139
|
+
|
|
140
|
+
### Manifests
|
|
141
|
+
|
|
142
|
+
| Export | Description |
|
|
143
|
+
|--------|-------------|
|
|
144
|
+
| `createRasterManifest(params)` | Deterministic manifest with computed `rasterId` |
|
|
145
|
+
| `validateRasterManifest(manifest)` | Structured validation (errors + warnings) |
|
|
146
|
+
| `rasterManifestToEvidenceRef(manifest)` | Manifest → `EvidenceRef` |
|
|
147
|
+
|
|
148
|
+
### Derived provenance
|
|
149
|
+
|
|
150
|
+
| Export | Description |
|
|
151
|
+
|--------|-------------|
|
|
152
|
+
| `createDerivedRasterManifest(params)` | Derived manifest from source rasters |
|
|
153
|
+
| `verifyRasterDerivation(manifest, sources)` | Check declared provenance structure |
|
|
154
|
+
|
|
155
|
+
### Window proofs
|
|
156
|
+
|
|
157
|
+
| Export | Description |
|
|
158
|
+
|--------|-------------|
|
|
159
|
+
| `createRasterWindowProof(params)` | Deterministic window proof |
|
|
160
|
+
| `rasterWindowProofToEvidenceRef(proof)` | Window proof → `EvidenceRef` |
|
|
161
|
+
|
|
162
|
+
### Spatial-proof integration
|
|
163
|
+
|
|
164
|
+
| Export | Description |
|
|
165
|
+
|--------|-------------|
|
|
166
|
+
| `rasterFootprintToSpatialObject(manifest)` | Bounds → `SpatialObject`, or `null` with no bounds |
|
|
167
|
+
| `createRasterSpatialRelation(params)` | Raster footprint vs spatial object relation claim |
|
|
168
|
+
|
|
169
|
+
### Proof integration
|
|
170
|
+
|
|
171
|
+
| Export | Description |
|
|
172
|
+
|--------|-------------|
|
|
173
|
+
| `rasterEvidenceRefs(manifest, windowProof?, spatialObjectId?)` | Full evidence list for a raster proof |
|
|
174
|
+
| `createUnsignedRasterProof(params)` | Build an unsigned `attestation` proof |
|
|
175
|
+
| `signRasterProof(unsigned, seed, keyIndex)` | WOTS-sign the proof |
|
|
176
|
+
| `verifyRasterProof(signed)` | End-to-end verification with structured reasons |
|
|
177
|
+
|
|
178
|
+
### Proofgraph integration
|
|
179
|
+
|
|
180
|
+
| Export | Description |
|
|
181
|
+
|--------|-------------|
|
|
182
|
+
| `rasterManifestToProofGraphNode(manifest)` | Manifest → `custom` node |
|
|
183
|
+
| `rasterWindowProofToProofGraphNode(proof)` | Window proof → `custom` node |
|
|
184
|
+
| `rasterManifestToGraphEdges(manifest)` | `derived_from` / `references` / `about` edges |
|
|
185
|
+
| `rasterWindowProofToGraphEdges(proof)` | Window proof `derived_from` raster edge |
|
|
186
|
+
| `addRasterManifestToGraph(graph, manifest)` | Immutably add a manifest node |
|
|
187
|
+
|
|
188
|
+
## Drone example
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
import {
|
|
192
|
+
createRasterMerkleSummary,
|
|
193
|
+
createRasterManifest,
|
|
194
|
+
createUnsignedRasterProof,
|
|
195
|
+
signRasterProof,
|
|
196
|
+
verifyRasterProof,
|
|
197
|
+
} from '@totemsdk/raster-proof';
|
|
198
|
+
|
|
199
|
+
const photoBytes = new TextEncoder().encode('JPEG bytes…');
|
|
200
|
+
const summary = createRasterMerkleSummary(photoBytes);
|
|
201
|
+
|
|
202
|
+
const manifest = createRasterManifest({
|
|
203
|
+
sourceType: 'drone',
|
|
204
|
+
layerType: 'rgb',
|
|
205
|
+
capturedAt: 1_712_000_000_000,
|
|
206
|
+
deviceId: 'drone-007',
|
|
207
|
+
missionId: 'mission-42',
|
|
208
|
+
asset: {
|
|
209
|
+
uri: 'https://cdn.example/flights/drone-007/ortho-001.tif',
|
|
210
|
+
mediaType: 'image/tiff',
|
|
211
|
+
format: 'geotiff',
|
|
212
|
+
byteSize: photoBytes.length,
|
|
213
|
+
contentHash: summary.contentHash,
|
|
214
|
+
hashAlgorithm: 'sha3-256',
|
|
215
|
+
merkleRoot: summary.merkleRoot,
|
|
216
|
+
chunkSizeBytes: summary.chunkSizeBytes,
|
|
217
|
+
},
|
|
218
|
+
spatial: {
|
|
219
|
+
crs: 'EPSG:4326',
|
|
220
|
+
bounds: [36.78, -1.29, 36.82, -1.25], // minLon, minLat, maxLon, maxLat
|
|
221
|
+
widthPx: 8192,
|
|
222
|
+
heightPx: 6144,
|
|
223
|
+
resolutionM: 0.05,
|
|
224
|
+
},
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
const unsigned = createUnsignedRasterProof({ manifest, issuer: 'drone-007', issuedAt: 1_712_000_000_000 });
|
|
228
|
+
const signed = signRasterProof(unsigned, seedBytes, 9); // reserve the WOTS key index first!
|
|
229
|
+
|
|
230
|
+
const result = verifyRasterProof(signed);
|
|
231
|
+
console.log(result.valid, result.rasterId);
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
## Satellite scene window example
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
import { chunkBytes, createMerkleProof, createRasterWindowProof } from '@totemsdk/raster-proof';
|
|
238
|
+
|
|
239
|
+
// A strong node chunks the full scene; a small device only needs its window.
|
|
240
|
+
const chunks = chunkBytes(sceneBytes, 64 * 1024);
|
|
241
|
+
const windowProof = createRasterWindowProof({
|
|
242
|
+
rasterId: manifest.rasterId,
|
|
243
|
+
merkleRoot: manifest.asset.merkleRoot!,
|
|
244
|
+
chunkIndices: [12, 13, 14],
|
|
245
|
+
chunkHashes: [chunks[12].hash, chunks[13].hash, chunks[14].hash],
|
|
246
|
+
spatial: { bounds: [36.79, -1.28, 36.80, -1.27] },
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
// Optional: carry the full leaf proofs so a verifier can recompute the root
|
|
250
|
+
// from the window's own bytes.
|
|
251
|
+
createUnsignedRasterProof({
|
|
252
|
+
manifest,
|
|
253
|
+
windowProof,
|
|
254
|
+
merkleProofs: [13].map((i) => createMerkleProof(chunks, i)),
|
|
255
|
+
issuedAt,
|
|
256
|
+
});
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
## Derived water-mask / change-mask example
|
|
260
|
+
|
|
261
|
+
```typescript
|
|
262
|
+
import { createDerivedRasterManifest, verifyRasterDerivation } from '@totemsdk/raster-proof';
|
|
263
|
+
|
|
264
|
+
const waterMask = createDerivedRasterManifest({
|
|
265
|
+
sourceManifests: [sceneManifest], // satellite scene → water mask
|
|
266
|
+
layerType: 'water-mask',
|
|
267
|
+
asset: maskAsset,
|
|
268
|
+
pipelineId: 'flood-watermask-v2',
|
|
269
|
+
parametersHash: 'sha3-256:ab12…', // required when pipelineId is set
|
|
270
|
+
uncertainty: ['Model output; manual review recommended near edges.'],
|
|
271
|
+
spatial: sceneManifest.spatial,
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
const result = verifyRasterDerivation(waterMask, [sceneManifest]);
|
|
275
|
+
if (!result.valid) {
|
|
276
|
+
console.log(result.reasons); // e.g. missing source rasters
|
|
277
|
+
}
|
|
278
|
+
console.log(result.uncertainty); // uncertainty is preserved
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
`verifyRasterDerivation` only verifies the **declared** provenance structure — that every `derivedFrom` ID is supplied, parameters are present when a pipeline is named, and the manifest is honestly marked `sourceType: 'derived'`. It does not verify that image processing was correct.
|
|
282
|
+
|
|
283
|
+
## Manifest validation
|
|
284
|
+
|
|
285
|
+
`validateRasterManifest` checks:
|
|
286
|
+
|
|
287
|
+
- `sourceType`, `layerType`, `asset.format`, `asset.contentHash` required
|
|
288
|
+
- `asset.hashAlgorithm` must be `sha3-256`
|
|
289
|
+
- `createdAt` positive; `byteSize` non-negative
|
|
290
|
+
- `capturedAt` far after `createdAt` is rejected unless `metadata.allowFutureCapture === true`
|
|
291
|
+
- `spatial.bounds` in `[minLon, minLat, maxLon, maxLat]` GeoJSON order with valid ranges/ordering
|
|
292
|
+
- `widthPx` / `heightPx` positive integers, `resolutionM` positive
|
|
293
|
+
- derived rasters without `provenance.derivedFrom` produce a warning
|
|
294
|
+
|
|
295
|
+
## Proof verification
|
|
296
|
+
|
|
297
|
+
`verifyRasterProof` checks, in order:
|
|
298
|
+
|
|
299
|
+
1. underlying `@totemsdk/proof` verification (signature, `proofId`, expiry)
|
|
300
|
+
2. payload contains a structurally valid `RasterManifest`
|
|
301
|
+
3. `rasterId` recomputes from stable fields
|
|
302
|
+
4. manifest evidence hash matches the payload manifest
|
|
303
|
+
5. content hash and Merkle root evidence refs present when declared
|
|
304
|
+
6. window proof (when supplied): `windowProofId` recomputes, root matches the manifest's root, and any supplied Merkle proofs verify and reference referenced leaves
|
|
305
|
+
7. derivation structure valid for `sourceType: 'derived'`
|
|
306
|
+
|
|
307
|
+
Anchoring is not required. Source rasters are not embedded in the proof, so derivation is checked structurally inside the envelope; full cross-source verification is `verifyRasterDerivation`.
|
|
308
|
+
|
|
309
|
+
## Spatial-proof integration example
|
|
310
|
+
|
|
311
|
+
```typescript
|
|
312
|
+
import { computeSpatialObjectId } from '@totemsdk/spatial-proof';
|
|
313
|
+
import { rasterFootprintToSpatialObject, createRasterSpatialRelation } from '@totemsdk/raster-proof';
|
|
314
|
+
|
|
315
|
+
const site = {
|
|
316
|
+
spatialId: computeSpatialObjectId({
|
|
317
|
+
kind: 'site-boundary',
|
|
318
|
+
name: 'ZEDD flood site A',
|
|
319
|
+
geometry: { type: 'Polygon', coordinates: [[[36.78, -1.29], [36.82, -1.29], [36.82, -1.25], [36.78, -1.25], [36.78, -1.29]]] },
|
|
320
|
+
}),
|
|
321
|
+
kind: 'site-boundary' as const,
|
|
322
|
+
name: 'ZEDD flood site A',
|
|
323
|
+
geometry: { type: 'Polygon', coordinates: [[[36.78, -1.29], [36.82, -1.29], [36.82, -1.25], [36.78, -1.25], [36.78, -1.29]]] },
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
const footprint = rasterFootprintToSpatialObject(manifest); // null when manifest has no bounds
|
|
327
|
+
const claim = createRasterSpatialRelation({
|
|
328
|
+
manifest,
|
|
329
|
+
spatialObject: site,
|
|
330
|
+
relation: 'covers', // raster scene footprint covers the site boundary
|
|
331
|
+
});
|
|
332
|
+
console.log(claim.result.matched); // true (bbox cover)
|
|
333
|
+
console.log(claim.inputs.rasterManifestId); // totem:raster:…
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
Geometry math, bbox relations, and uncertainty notes are delegated to `@totemsdk/spatial-proof` — nothing here is reimplemented. `covers`/`covered_by`/`intersects`/`overlaps` are bounding-box approximations there and always carry an explicit `uncertainty` note.
|
|
337
|
+
|
|
338
|
+
## Proofgraph example
|
|
339
|
+
|
|
340
|
+
```typescript
|
|
341
|
+
import { createProofGraph, addEdge } from '@totemsdk/proofgraph';
|
|
342
|
+
import { rasterManifestToProofGraphNode, rasterManifestToGraphEdges } from '@totemsdk/raster-proof';
|
|
343
|
+
|
|
344
|
+
let graph = createProofGraph();
|
|
345
|
+
graph = addEdge(graph, rasterManifestToGraphEdges(manifest)[0]);
|
|
346
|
+
// custom:<rasterId> node via rasterManifestToProofGraphNode + addNode
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
Edges represent:
|
|
350
|
+
|
|
351
|
+
- raster `derived_from` source rasters (when derived)
|
|
352
|
+
- raster `references` its spatial object (when present)
|
|
353
|
+
- raster `about` its device / operator / mission (when present)
|
|
354
|
+
|
|
355
|
+
The "raster supports proof" edge is created by `@totemsdk/proofgraph`'s `addProof` when the signed proof is added to the graph.
|
|
356
|
+
|
|
357
|
+
## Limitations
|
|
358
|
+
|
|
359
|
+
- **Not a raster engine.** No rendering, decoding, projection, or format parsing. Sizes and formats are declared, not verified.
|
|
360
|
+
- **Interpretation is not proven.** A proof binds bytes and metadata; it does not validate a water mask, NDVI value, or defect label. That requires an explicit reviewer/model claim.
|
|
361
|
+
- **Spatial relations are spatial-proof relations.** `covers`/`intersects`/`overlaps`/`covered_by` are bounding-box approximations there, always flagged with `uncertainty`.
|
|
362
|
+
- **Merkle odd-layers promote the last hash** — documented, deterministic, but different from duplication-based trees.
|
|
363
|
+
- **Window proofs carry chunk hashes, not sibling paths.** Full leaf-level re-derivation requires the optional Merkle proofs.
|
|
364
|
+
- **Source rasters are not embedded in proofs** — cross-source derivation verification needs the source manifests supplied separately.
|
|
365
|
+
|
|
366
|
+
## Security notes
|
|
367
|
+
|
|
368
|
+
- **WOTS one-time keys.** Each WOTS key index can be used exactly once. Never sign two different proofs with the same key index. Reserve indices through [`@totemsdk/wots-lease`](https://www.npmjs.com/package/@totemsdk/wots-lease) before signing.
|
|
369
|
+
- **Content hash binding.** `rasterId` and the evidence hash are derived from stable fields including the content hash — mutating `asset.contentHash`, `layerType`, `sourceType`, or `spatial.bounds` invalidates the ID and the proof.
|
|
370
|
+
- **Provenance is declared, not trusted.** `verifyRasterDerivation` checks structure, not the truthfulness of `derivedFrom` claims. Verify source manifests themselves before trusting a chain.
|
|
371
|
+
- **CRS / bounds are metadata.** Wrong CRS or wrong bounds are detected only by comparing against trusted ground truth.
|
|
372
|
+
|
|
373
|
+
## Related packages
|
|
374
|
+
|
|
375
|
+
- [`@totemsdk/core`](https://www.npmjs.com/package/@totemsdk/core) — SHA3-256, WOTS signing, script derivation
|
|
376
|
+
- [`@totemsdk/proof`](https://www.npmjs.com/package/@totemsdk/proof) — proof envelopes, signing, verification
|
|
377
|
+
- [`@totemsdk/proofgraph`](https://www.npmjs.com/package/@totemsdk/proofgraph) — content-addressed proof relationship graph
|
|
378
|
+
- [`@totemsdk/location-proof`](https://www.npmjs.com/package/@totemsdk/location-proof) — device-neutral location claims and confidence scoring
|
|
379
|
+
- [`@totemsdk/spatial-proof`](https://www.npmjs.com/package/@totemsdk/spatial-proof) — geospatial relation proofs (geofences, routes, coverage)
|
|
380
|
+
- [`@totemsdk/identity`](https://www.npmjs.com/package/@totemsdk/identity) — device/agent identities
|
|
381
|
+
- [`@totemsdk/manifest`](https://www.npmjs.com/package/@totemsdk/manifest) — signed entity declarations
|
|
382
|
+
- [`@totemsdk/wots-lease`](https://www.npmjs.com/package/@totemsdk/wots-lease) — one-time key safety
|
|
383
|
+
- [`@totemsdk/edge`](https://www.npmjs.com/package/@totemsdk/edge) — unified edge runtime
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical JSON, hashing, and stable ID rules for @totemsdk/raster-proof.
|
|
3
|
+
*
|
|
4
|
+
* canonicalJson and toHex are re-exported from @totemsdk/proof, which is the
|
|
5
|
+
* canonical implementation across the Totem SDK (deterministic canonical JSON
|
|
6
|
+
* with recursively sorted keys, lowercase hex without 0x prefix).
|
|
7
|
+
*
|
|
8
|
+
* ID rules:
|
|
9
|
+
* Raster manifest: "totem:raster:" + sha3_256("totem-raster" + canonicalJson(stableManifest))
|
|
10
|
+
* Raster window: "totem:raster-window:" + sha3_256("totem-raster-window" + canonicalJson(stableWindow))
|
|
11
|
+
*
|
|
12
|
+
* Stable IDs deliberately exclude mutable / non-content fields so equivalent
|
|
13
|
+
* logical content always produces the same identifier:
|
|
14
|
+
* raster manifest: rasterId, metadata
|
|
15
|
+
* raster window: windowProofId, metadata
|
|
16
|
+
*/
|
|
17
|
+
import { canonicalJson, toHex } from '@totemsdk/proof';
|
|
18
|
+
import type { RasterManifest, RasterWindowProof } from './types.js';
|
|
19
|
+
export { canonicalJson, toHex };
|
|
20
|
+
/**
|
|
21
|
+
* Compute the stable raster manifest ID: "totem:raster:<sha3-256-hex>".
|
|
22
|
+
* Deterministic over stable fields — the same logical manifest always hashes
|
|
23
|
+
* to the same identifier.
|
|
24
|
+
*/
|
|
25
|
+
export declare function computeRasterManifestId(input: Omit<RasterManifest, 'rasterId'>): string;
|
|
26
|
+
/**
|
|
27
|
+
* Hash a complete RasterManifest (excluding rasterId and metadata) to
|
|
28
|
+
* lowercase SHA3-256 hex without a 0x prefix — the value used in
|
|
29
|
+
* EvidenceRef.hash.
|
|
30
|
+
*/
|
|
31
|
+
export declare function hashRasterManifest(manifest: RasterManifest): string;
|
|
32
|
+
/**
|
|
33
|
+
* Compute the stable raster window proof ID: "totem:raster-window:<sha3-256-hex>".
|
|
34
|
+
*/
|
|
35
|
+
export declare function computeRasterWindowProofId(input: Omit<RasterWindowProof, 'windowProofId'>): string;
|
|
36
|
+
/**
|
|
37
|
+
* Hash a complete RasterWindowProof (excluding windowProofId and metadata)
|
|
38
|
+
* to lowercase SHA3-256 hex without a 0x prefix.
|
|
39
|
+
*/
|
|
40
|
+
export declare function hashRasterWindowProof(proof: RasterWindowProof): string;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Canonical JSON, hashing, and stable ID rules for @totemsdk/raster-proof.
|
|
4
|
+
*
|
|
5
|
+
* canonicalJson and toHex are re-exported from @totemsdk/proof, which is the
|
|
6
|
+
* canonical implementation across the Totem SDK (deterministic canonical JSON
|
|
7
|
+
* with recursively sorted keys, lowercase hex without 0x prefix).
|
|
8
|
+
*
|
|
9
|
+
* ID rules:
|
|
10
|
+
* Raster manifest: "totem:raster:" + sha3_256("totem-raster" + canonicalJson(stableManifest))
|
|
11
|
+
* Raster window: "totem:raster-window:" + sha3_256("totem-raster-window" + canonicalJson(stableWindow))
|
|
12
|
+
*
|
|
13
|
+
* Stable IDs deliberately exclude mutable / non-content fields so equivalent
|
|
14
|
+
* logical content always produces the same identifier:
|
|
15
|
+
* raster manifest: rasterId, metadata
|
|
16
|
+
* raster window: windowProofId, metadata
|
|
17
|
+
*/
|
|
18
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
+
exports.toHex = exports.canonicalJson = void 0;
|
|
20
|
+
exports.computeRasterManifestId = computeRasterManifestId;
|
|
21
|
+
exports.hashRasterManifest = hashRasterManifest;
|
|
22
|
+
exports.computeRasterWindowProofId = computeRasterWindowProofId;
|
|
23
|
+
exports.hashRasterWindowProof = hashRasterWindowProof;
|
|
24
|
+
const core_1 = require("@totemsdk/core");
|
|
25
|
+
const proof_1 = require("@totemsdk/proof");
|
|
26
|
+
Object.defineProperty(exports, "canonicalJson", { enumerable: true, get: function () { return proof_1.canonicalJson; } });
|
|
27
|
+
Object.defineProperty(exports, "toHex", { enumerable: true, get: function () { return proof_1.toHex; } });
|
|
28
|
+
const RASTER_PREFIX = 'totem-raster';
|
|
29
|
+
const RASTER_WINDOW_PREFIX = 'totem-raster-window';
|
|
30
|
+
function stableManifestInput(input) {
|
|
31
|
+
const { rasterId: _rasterId, metadata: _metadata, ...stable } = input;
|
|
32
|
+
return stable;
|
|
33
|
+
}
|
|
34
|
+
function stableWindowInput(input) {
|
|
35
|
+
const { windowProofId: _windowProofId, metadata: _metadata, ...stable } = input;
|
|
36
|
+
return stable;
|
|
37
|
+
}
|
|
38
|
+
function hashStableManifest(input) {
|
|
39
|
+
const digest = (0, core_1.sha3_256)(new TextEncoder().encode(RASTER_PREFIX + (0, proof_1.canonicalJson)(stableManifestInput(input))));
|
|
40
|
+
return (0, proof_1.toHex)(digest);
|
|
41
|
+
}
|
|
42
|
+
function hashStableWindow(input) {
|
|
43
|
+
const digest = (0, core_1.sha3_256)(new TextEncoder().encode(RASTER_WINDOW_PREFIX + (0, proof_1.canonicalJson)(stableWindowInput(input))));
|
|
44
|
+
return (0, proof_1.toHex)(digest);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Compute the stable raster manifest ID: "totem:raster:<sha3-256-hex>".
|
|
48
|
+
* Deterministic over stable fields — the same logical manifest always hashes
|
|
49
|
+
* to the same identifier.
|
|
50
|
+
*/
|
|
51
|
+
function computeRasterManifestId(input) {
|
|
52
|
+
return 'totem:raster:' + hashStableManifest(input);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Hash a complete RasterManifest (excluding rasterId and metadata) to
|
|
56
|
+
* lowercase SHA3-256 hex without a 0x prefix — the value used in
|
|
57
|
+
* EvidenceRef.hash.
|
|
58
|
+
*/
|
|
59
|
+
function hashRasterManifest(manifest) {
|
|
60
|
+
const { rasterId: _rasterId, ...rest } = manifest;
|
|
61
|
+
return hashStableManifest(rest);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Compute the stable raster window proof ID: "totem:raster-window:<sha3-256-hex>".
|
|
65
|
+
*/
|
|
66
|
+
function computeRasterWindowProofId(input) {
|
|
67
|
+
return 'totem:raster-window:' + hashStableWindow(input);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Hash a complete RasterWindowProof (excluding windowProofId and metadata)
|
|
71
|
+
* to lowercase SHA3-256 hex without a 0x prefix.
|
|
72
|
+
*/
|
|
73
|
+
function hashRasterWindowProof(proof) {
|
|
74
|
+
const { windowProofId: _windowProofId, ...rest } = proof;
|
|
75
|
+
return hashStableWindow(rest);
|
|
76
|
+
}
|
package/dist/hash.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Byte / string hashing for @totemsdk/raster-proof.
|
|
3
|
+
*
|
|
4
|
+
* SHA3-256 comes from @totemsdk/core. All hashes are lowercase hex without a
|
|
5
|
+
* 0x prefix. No Node crypto, no WASM gating — safe on edge devices.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* SHA3-256 hash of raw bytes → lowercase hex (no 0x prefix).
|
|
9
|
+
*/
|
|
10
|
+
export declare function hashBytes(bytes: Uint8Array): string;
|
|
11
|
+
/**
|
|
12
|
+
* SHA3-256 hash of a UTF-8 string → lowercase hex (no 0x prefix).
|
|
13
|
+
*/
|
|
14
|
+
export declare function hashString(value: string): string;
|
|
15
|
+
/**
|
|
16
|
+
* Hash a chunk's bytes in-place using hashBytes. Kept as a named helper so
|
|
17
|
+
* callers can hash arbitrary sub-byte-ranges without allocating.
|
|
18
|
+
*/
|
|
19
|
+
export declare function hashSubarray(bytes: Uint8Array, offset: number, length: number): string;
|
package/dist/hash.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Byte / string hashing for @totemsdk/raster-proof.
|
|
4
|
+
*
|
|
5
|
+
* SHA3-256 comes from @totemsdk/core. All hashes are lowercase hex without a
|
|
6
|
+
* 0x prefix. No Node crypto, no WASM gating — safe on edge devices.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.hashBytes = hashBytes;
|
|
10
|
+
exports.hashString = hashString;
|
|
11
|
+
exports.hashSubarray = hashSubarray;
|
|
12
|
+
const core_1 = require("@totemsdk/core");
|
|
13
|
+
const canonical_js_1 = require("./canonical.js");
|
|
14
|
+
/**
|
|
15
|
+
* SHA3-256 hash of raw bytes → lowercase hex (no 0x prefix).
|
|
16
|
+
*/
|
|
17
|
+
function hashBytes(bytes) {
|
|
18
|
+
return (0, canonical_js_1.toHex)((0, core_1.sha3_256)(bytes));
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* SHA3-256 hash of a UTF-8 string → lowercase hex (no 0x prefix).
|
|
22
|
+
*/
|
|
23
|
+
function hashString(value) {
|
|
24
|
+
return (0, canonical_js_1.toHex)((0, core_1.sha3_256)(new TextEncoder().encode(value)));
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Hash a chunk's bytes in-place using hashBytes. Kept as a named helper so
|
|
28
|
+
* callers can hash arbitrary sub-byte-ranges without allocating.
|
|
29
|
+
*/
|
|
30
|
+
function hashSubarray(bytes, offset, length) {
|
|
31
|
+
return hashBytes(bytes.subarray(offset, offset + length));
|
|
32
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module @totemsdk/raster-proof
|
|
3
|
+
*
|
|
4
|
+
* Edge-capable raster and visual evidence proof primitives for Totem Edge.
|
|
5
|
+
*
|
|
6
|
+
* This package proves bytes, manifests, provenance, windows and declared
|
|
7
|
+
* relationships. It does NOT prove that a visual interpretation is correct
|
|
8
|
+
* unless a reviewer or downstream model proof says so.
|
|
9
|
+
*
|
|
10
|
+
* Pipeline:
|
|
11
|
+
*
|
|
12
|
+
* bytes + asset metadata + spatial context + provenance
|
|
13
|
+
* → chunk hashes / Merkle root
|
|
14
|
+
* → deterministic raster manifest
|
|
15
|
+
* → window proof (optional)
|
|
16
|
+
* → Totem proof envelope
|
|
17
|
+
* → proofgraph linkage
|
|
18
|
+
*
|
|
19
|
+
* This is NOT a raster-processing engine: no GDAL, no GeoTIFF parsing, no
|
|
20
|
+
* satellite provider APIs, no STAC, no cloud masking, no NDVI, no ML
|
|
21
|
+
* segmentation, no orthomosaic generation, no tile server, no storage.
|
|
22
|
+
*
|
|
23
|
+
* No network, no storage, no map rendering, no GIS engine dependency.
|
|
24
|
+
* SHA3-256 everywhere, safe to run on edge devices.
|
|
25
|
+
*/
|
|
26
|
+
export type { RasterSourceType, RasterAssetFormat, RasterLayerType, RasterSpatialMetadata, RasterAssetRef, RasterProvenance, RasterManifest, RasterChunk, RasterMerkleProof, RasterWindowProof, RasterValidationResult, RasterMerkleSummary, RasterMerkleOptions, CreateRasterManifestParams, CreateDerivedRasterManifestParams, CreateRasterWindowProofParams, RasterDerivationVerifyResult, CreateRasterSpatialRelationParams, CreateRasterProofParams, RasterProofVerifyResult, } from './types.js';
|
|
27
|
+
export { canonicalJson, toHex } from './canonical.js';
|
|
28
|
+
export { computeRasterManifestId, hashRasterManifest, computeRasterWindowProofId, hashRasterWindowProof, } from './canonical.js';
|
|
29
|
+
export { hashBytes, hashString, hashSubarray, } from './hash.js';
|
|
30
|
+
export { DEFAULT_CHUNK_SIZE_BYTES, chunkBytes, computeMerkleRoot, merkleLeafHash, createMerkleProof, verifyMerkleProof, createRasterMerkleSummary, } from './merkle.js';
|
|
31
|
+
export { createRasterManifest, validateRasterManifest, rasterManifestToEvidenceRef, } from './manifest.js';
|
|
32
|
+
export { createRasterWindowProof, rasterWindowProofToEvidenceRef, } from './window.js';
|
|
33
|
+
export { createDerivedRasterManifest, verifyRasterDerivation, } from './provenance.js';
|
|
34
|
+
export { rasterFootprintToSpatialObject, createRasterSpatialRelation, } from './spatial.js';
|
|
35
|
+
export { rasterEvidenceRefs, createUnsignedRasterProof, signRasterProof, verifyRasterProof, } from './proof.js';
|
|
36
|
+
export { rasterManifestToProofGraphNode, rasterWindowProofToProofGraphNode, rasterManifestToGraphEdges, rasterWindowProofToGraphEdges, addRasterManifestToGraph, } from './proofgraph.js';
|