OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-tps commits / 5112cb5

#120 L4: an asset's oid is the content overlay's hash, not SHA-256
Placements name blobs by oid and peers fetch with ContentOverlay.request(oid), whose names are hyper-overlay v2's chunk-tree hash (cdc::StreamingHasher). A SHA-256 oid would name blobs nobody can fetch. hash_of hashes through the overlay (scratch file), "" without it. gate_asset_doc 24/24, gate_asset_fold 21/21. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
5112cb50bd0bdf0b090ff5b0b2805a54f8903bf3
by plankatron <fleeky@gmail.com> · 2026-10-19T21:06:33.000Z

Changed files (2) +21 -6

docs/node-tree-and-stacks.md +3 -1
……@@ -1133,7 +1133,9 @@ What exists (survey, 2026-10-19, HEAD 2b1a31a):
11331133
11341134### 15.1 The asset document
11351135
1136An asset is ONE immutable blob, `.hasset`, named by its content hash (the oid):
1136An asset is ONE immutable blob, `.hasset`, named by its content hash (the oid) — the
1137CONTENT OVERLAY's hash (hyper-overlay v2's chunk-tree `cdc::StreamingHasher`, not a plain
1138SHA-256), because a peer fetches it with `ContentOverlay.request(oid)`:
11371139
11381140```
11391141{ "v": 1, "name": "Pillar", "pivot": "base-centre",
scenes/maps/asset_doc.gd +18 -5
……@@ -313,12 +313,25 @@ static func _canon(v: Variant) -> Variant:
313313 return v
314314
315315
316## The blob's name: SHA-256, hex.
316## The blob's name = the CONTENT OVERLAY's hash of its bytes (hyper-overlay v2's streaming
317## chunk-tree hash, cdc::StreamingHasher — NOT a plain SHA-256): a placement names its blob
318## by this, and a peer fetches it with ContentOverlay.request(hash), so it must be the
319## overlay's own name. The overlay hashes files, so the bytes go through a scratch file.
320## "" when the overlay is not loaded: no name rather than one nobody can fetch.
317321static func hash_of(bytes: PackedByteArray) -> String:
318 var h := HashingContext.new()
319 h.start(HashingContext.HASH_SHA256)
320 h.update(bytes)
321 return h.finish().hex_encode()
322 var co: Node = (Engine.get_main_loop() as SceneTree).root.get_node_or_null("ContentOverlay")
323 if co == null:
324 return ""
325 var tmp := "user://content/.asset_hash.tmp"
326 DirAccess.make_dir_recursive_absolute(ProjectSettings.globalize_path("user://content"))
327 var f := FileAccess.open(tmp, FileAccess.WRITE)
328 if f == null:
329 return ""
330 f.store_buffer(bytes)
331 f.close()
332 var h := String(co.content_hash_of_file(tmp))
333 DirAccess.remove_absolute(ProjectSettings.globalize_path(tmp))
334 return h
322335
323336
324337## Read a blob — FROM ANOTHER PEER, so nothing is trusted: no Objects (bytes_to_var's