Docs: TypeScript CultMesh content plane target (campaign cultmesh-ts-content)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
a88c629e82
commit
8990bd466d
1 changed files with 99 additions and 0 deletions
99
docs/cultmesh-ts-content-target.md
Normal file
99
docs/cultmesh-ts-content-target.md
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
# TypeScript CultMesh Content Plane Target
|
||||
|
||||
Campaign `cultmesh-ts-content`. Self wrote this on 2026-10-19 for the
|
||||
StreamPixels M1 live character preview. That preview is a three.js page that
|
||||
loads glTF bodies and accessories by content hash from Yggdrasil.
|
||||
|
||||
## Why now
|
||||
|
||||
CultMesh moves large immutable artifacts as content-addressed chunks described
|
||||
by a `gamecult.mesh.cdn_artifact_manifest`. C# packs manifests, serves chunks
|
||||
over TCP (`CultMeshContentServer`), fetches them over HTTPS
|
||||
(`CultMeshHttpsContentTransportConnector`), and verifies each chunk and the
|
||||
whole body (`CultMeshContentTransferService`). Rust speaks the chunk messages
|
||||
through `cultnet-rs` `fetch_content` and `answer_content_chunk_request`.
|
||||
TypeScript has none of this: no manifest type, no packer, no provider and no
|
||||
reader. And no runtime has an HTTPS content *server*; the C# HTTPS connector
|
||||
has never had a peer.
|
||||
|
||||
Standing ruling (StreamPixels `state/map.yaml`, "Content plane", operator
|
||||
2026-09-15): HTTPS content transport serves both browser and overlay.
|
||||
Yggdrasil plus SHA-256 verification is the trust boundary for now.
|
||||
Distributed zero-trust distribution is future work. The catalog never stores
|
||||
URLs; clients resolve content through CultMesh.
|
||||
|
||||
## Target state
|
||||
|
||||
- **One TypeScript owner of the CDN document shapes.** It holds the manifest
|
||||
and chunk-ref types, their MessagePack wire form, `normalizeHash`, the
|
||||
record-key grammar (`mesh:cdn:chunk:<hex>` and
|
||||
`mesh:cdn:artifact:<token>:<version>:<hex>`), and the packer. A manifest
|
||||
that TypeScript packs is byte-identical to the one C# packs from the same
|
||||
bytes and options. The owner can be imported in a browser, because the
|
||||
reader needs it there.
|
||||
- **An HTTPS content provider in `cultmesh-ts`.** It answers
|
||||
`GET <endpoint>?chunkHash=<hex>&recordKey=<key>` with exactly the chunk's
|
||||
bytes and a `Content-Length` equal to the chunk's size: the request shape
|
||||
the C# connector sends (`CultMeshHttpsContentTransport.cs` `BuildChunkUri`).
|
||||
Its core is framework-neutral: a request maps to an answer, and a host can
|
||||
mount it on `node:https` or on any HTTP framework. It reads from a content
|
||||
store port. The store shipped with it serves content-addressed files from a
|
||||
directory outside git. The provider never serves a byte it has not
|
||||
verified against its hash, and it never serves anything but a chunk:
|
||||
there is no generic static-file route.
|
||||
- **A verifying reader in `cultmesh-browser`.** Given a resolved endpoint and
|
||||
a manifest, it fetches the manifest's chunks over HTTPS with the C# query
|
||||
shape. It checks each response's length, each chunk's SHA-256 and the whole
|
||||
body's SHA-256 with WebCrypto, and returns the bytes or throws a typed
|
||||
error. No byte that failed its hash reaches the caller, in whole or in part.
|
||||
It runs in browsers and in Node 20+.
|
||||
- **Endpoints come from CultMesh, not from content.** The reader takes the
|
||||
endpoint as an argument that the caller resolved through CultMesh. Manifests
|
||||
carry no URL.
|
||||
|
||||
## Invariants
|
||||
|
||||
- `wire-parity`: manifests, chunk refs and record keys are byte-identical
|
||||
across C#, Rust and TypeScript for the same input. The C# runtime is the
|
||||
reference. TypeScript writes and reads the shared vectors in
|
||||
`contracts/cultmesh/`.
|
||||
- `https-shape-parity`: the C# `CultMeshHttpsContentTransportConnector`
|
||||
fetches content from the TypeScript provider unmodified, in a cross-process
|
||||
test. The TypeScript reader fetches the same chunks from the same provider.
|
||||
- `verify-before-use`: the reader releases no byte that failed its chunk hash
|
||||
or the whole-body hash, and refuses a response whose length differs from the
|
||||
advertised size before reading it all.
|
||||
- `verify-before-serve`: the provider serves only bytes whose SHA-256 equals
|
||||
the requested chunk hash.
|
||||
- `chunk-only-route`: the provider answers only chunk requests. A path, a
|
||||
filename or a hash outside the store can never select a file.
|
||||
- `no-urls-in-content`: no manifest, chunk ref or record carries an endpoint.
|
||||
- `one-owner-per-shape`: one TypeScript module owns the CDN shapes, and both
|
||||
packages consume it. Neither package keeps a private copy.
|
||||
- `general-purpose`: the plane is judged by what a reasonable consumer
|
||||
expects, not only by StreamPixels' slice (operator, 2026-09-30). Multi-chunk
|
||||
artifacts, empty artifacts, cancellation, bounded concurrency and browser
|
||||
cross-origin use all work.
|
||||
- `released`: the npm packages StreamPixels vendors are published at versions
|
||||
that contain this plane. The release is its own cut with a Soul pass before
|
||||
the tag.
|
||||
|
||||
## Not in this campaign
|
||||
|
||||
- Odin route verification for content. Single-host trust is ruled for now.
|
||||
- Resumable or durable transfer state in TypeScript. The browser holds
|
||||
artifacts in memory.
|
||||
- Distributed or zero-trust distribution, and peer serving.
|
||||
- A TypeScript TCP or RUDP content server, and an HTTPS content server in Rust
|
||||
or C#.
|
||||
- Garbage collection of stored blobs.
|
||||
- StreamPixels' own export, publish and service wiring (its B2 and B3 cuts).
|
||||
StreamPixels is the consumer, not part of this change.
|
||||
|
||||
## Consumer
|
||||
|
||||
StreamPixels `map/web-customize` (dfbe2a6),
|
||||
`docs/web-character-customization-map.md` section B1. Each option's artifact is
|
||||
10-125 KB, and a full avatar is 0.5-0.8 MB. The browser fetches them. The
|
||||
service serves them from `/srv/streampixels/content/` on Yggdrasil, outside
|
||||
git, and hashes every referenced blob at boot.
|
||||
Loading…
Reference in a new issue