Commit graph CultLib/packages/cultnet-ts
Author SHA1 Message Date
Meta Vens
8dd5a45378 Give every cultcache-ts document type one home store
Cut 5 (TypeScript). The cache no longer mirrors: StoreRoute and its mirrors
list are gone, #resolveRoute returns the single home store (the store that
claims the type, else the generic store), and put, putEnvelope and delete write
exactly one store.

addBackingStore now refuses a second generic store and a type already claimed
by another store, attaching nothing when it refuses, matching C#. No bytes
change; nothing in CultLib attached more than one store.

Tests: rejects a second generic backing store, rejects a type registered to
two stores, routes each type to its home store. The writer-by-reader interop
test now runs the C# write-routed mode and checks that catalog.cc and run.cc
are each a complete v1 snapshot holding only their own record, and that every
reader (TS, Rust, C#, Python) reads the routed catalog store.

Version 0.13.5 -> 0.14.0; cultnet-ts and cultmesh-ts depend on ^0.14.0 so the
workspace link still satisfies their range.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 00:37:31 +02:00
Meta Vens
f94f5581b0 Let the verse catalog schema say what the reference emits
The C# reference publishes authorityRoutes with certificate: null for
loopback routes, and its own verifier treats null as "no certificate" in
local development. The TypeScript JSON schema only allowed the object,
so the browser client rejected every catalog the getting-started
provider served: "certificate: must be object", Odin could not resolve
the Verse, and the real browser/C# checkpoint has failed since f622ee1
(2026-08-18), behind the sample failure that hid it. TypeScript
consumers already guard route.certificate; the schema now accepts null.

The package-closure smoke also still spelled the browser identity with
providerId; it is authorityRuntimeId since c194bd4.

  npm test --workspace packages/cultnet-ts -> 54 pass
  npm run test:pack:ts -> passed

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-10 13:55:46 +02:00
Meta Vens
830a5d718f Make the Eve onboarding checkpoint run against the contract that exists
The portability workflow has been red since 2026-08-22. Three things,
each hidden behind the one before it:

- Eve started refusing to build a command intent for a surface that has
  not advertised a command boundary and receipt schema (Eve 4338a42,
  2026-08-22). The two-runtime sample never advertised one. It now passes
  a provider advertisement with worldInteraction for its surface, which is
  the declaration the lowering was right to demand.
- The sample and the package-closure script still imported "cultcache-ts";
  the package became @gamecult/cultcache-ts on 2026-09-04 (8cb3b72) and
  CI never got far enough to say so. Two READMEs carried the same import.
- The job pinned Eve to codex/eve-browser-host-isolation, a branch that was
  never merged and stopped moving on 2026-08-25. The sample is written
  against Eve main and passes there; the pin now says so.

The interop workflow's Python step ran one test file by path; it now
discovers the three split suites.

  scripts/verify-eve-two-runtime-sample.ps1 -EveRoot F:\Projects\Eve
  (Eve main ccc3ffe) -> browserCount 1, headlessCount 1, restartCount 1

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-10 13:55:46 +02:00
Meta Vens
95493862bd Split the Python runtime into cultcache-py, cultnet-py, cultmesh-py
One distribution named cultcache-py carried three libraries. Rust and
TypeScript layer them as cultcache < cultnet < cultmesh; Python now does
the same, one package per layer, module names unchanged so every
`from cultmesh_py import ...` keeps working.

Two imports ran against that layering and are resolved by moving code to
where the dependency direction says it lives, not by adding a shim:

- cultnet_py.interop_peer imported the Verse/peer catalog, authority
  lease, stream, and simulation-fact contracts from cultmesh_py. They are
  wire contracts a bare peer has to speak without owning a node, which is
  why cultnet-rs and cultnet-ts keep them in the net layer. They now live
  in cultnet_py.cultmesh_contracts; cultmesh_py re-exports them.
- cultcache_py.benchmark imported cultnet_py. It measures framed CultNet
  parse beside cache paths, so it and compare_csharp move to cultnet_py.
  verify checks all three packages and moves to cultmesh_py.

The single 6,693-line test module is split by what each test touches: a
test using any cultmesh_py name lands in cultmesh-py, else cultnet_py in
cultnet-py, else cultcache-py. Each suite runs with only its layer and the
layers below on the path, so a test that reaches up fails to import
rather than quietly binding the packages back together. 11 / 53 / 78 =
the same 142.

Workflows install all three; the publish job is one matrix over the
three packages keyed by tag prefix. The TS test harnesses put all three
src directories on PYTHONPATH. cultcache-py bumps to 0.2.0 because it no
longer contains cultnet_py or cultmesh_py; the new packages start at
0.1.0. Nothing was ever published, so no PyPI consumer moves.

  cultcache-py  11 tests, path: cultcache-py/src
  cultnet-py    53 tests, path: + cultnet-py/src
  cultmesh-py   78 tests, path: + cultmesh-py/src
  python -m cultmesh_py.verify -> ok, all five checks
  cultnet_py.interop_peer imports with cultmesh_py absent

Not run here: the cultnet-ts interop ring and cultcache-ts store matrix
need dotnet/kotlin peers; their PYTHONPATH change is four lines.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-10 13:10:47 +02:00
Meta Vens
2041a0e0ed Bound the TypeScript fragment reassembly map
cultnet-ts removed a fragment buffer only on successful reassembly, with
no cap, no eviction and no timeout. A fragment set that loses one
fragment was stranded for the life of the session, so the map grew
without bound under any loss on a fragmenting channel. cultnet-rs at
least fails closed at 64 pending sets; TypeScript just consumes memory.

This is live, not theoretical. Ghostlight sends Heimdall sealed envelopes
over CultMesh RUDP with max_fragment_bytes = 2048
(Ghostlight/crates/ghostlight-dungeon/src/heimdall.rs:697), and Heimdall's
private command plane reassembles them through this path. Every lost
fragment on that link left a buffer nothing would ever collect, on a
long-running auth service.

Caps pending sets at 64 to match cultnet-rs's max_pending_fragment_sets,
and evicts the oldest stranded set rather than refusing the payload:
a receiver that stops accepting fragmented traffic after a bounded number
of losses is a denial of service delivered by the network. Map iteration
is insertion-ordered, so the first key is the oldest. fragmentSetsEvicted
and pendingFragmentSetCount make it observable.

Found by the Muninn media load test, which hit the Rust half of the same
defect; cultcache-py has the same unbounded map and is not touched here.

  npm run test --workspace=packages/cultnet-ts -> pass 54, fail 0

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-10 11:42:13 +02:00
Meta Vens
d1a9edad3a Port gamecult.runtime_presence_health.v2 to cultnet-ts
The dual-proved runtime presence contract existed only in cultnet-rs, so no
TypeScript service could make a statement Odin admits as presence. Odin files
anything else away as an ordinary document and never produces a topology
correlation for it, which is why Heimdall could warm forever without being
promoted.

The port is what it should be: the same fields at the same key slots. The Rust
derive serialises a positional tuple indexed by those slots, so the TypeScript
side names the slots and fills the same 24. Both proofs cover one payload --
the complete record with both signature fields empty -- and the two signing
messages use the domain separation their respective identities already define.

Conformance is against vectors emitted by cultnet-rs, not a fixture written
for both sides at once, which would agree with itself and prove nothing:
`cargo run --example runtime_presence_vector -p cultnet-rs` regenerates them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-07 21:42:54 +02:00
Meta Vens
5407be713f Add generic signed daemon-health publication to cultnet-ts
Odin carried a 324-line module named idunn-rudp.cjs. Thirteen of those lines
mentioned Idunn, and most were error strings. The rest was CultNet client work:
socket binding, RUDP handshake, Ed25519 signing, packet receipt, msgpack
framing. It sat in a consumer because that is where it was written, not because
that is where it belonged, and it was about to be duplicated when Hermodr moves
to its own repository.

The transport now lives here as signed-daemon-health, beside the RUDP session it
builds on. The receiving service supplies a contract naming its connection id,
signed schema, and unsigned diagnostic schema; nothing service-specific is
hardcoded.

The signature and identity domains are deliberately not parameterised. They are
the shared gamecult.provider-health domains, and a caller able to vary them
could silently produce statements no verifier accepts. The signing purpose is
the caller's signed schema id, so a statement signed for one service cannot
verify as another's.

The module also loses the sibling-path assumption it was carrying: it no longer
resolves CultLib by walking up three directories from wherever it happens to
live, because it now lives in CultLib.

Type-checks clean and builds. Verified faithful by Odin's existing signed-health
test, which asserts exact canonical statement fields, the observation timestamp,
and Ed25519 signature verification against the ported implementation.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-06 19:29:33 +02:00
Meta Vens
77313ed48c Stop the interop harness leaking peers and hanging after it passes
The suite finished its work in about 60 seconds and then never exited. Four runs
were left alive for hours, holding 11 node and 2 dotnet peers, and a stranded
peer holding cultnet_interop_peer.exe is what made two unrelated cargo builds
fail with LNK1104. In CI this is a job that burns its whole timeout on green
tests.

Two real defects, found by asking the stuck process what it was waiting on
rather than by guessing. process.getActiveResourcesInfo reported four UDPWraps
and two PipeWraps still held after the last test.

The four UDP sockets: eight tests bind a server socket with bindUdpSocket and
close only the transport wrapper in their finally, never the socket itself. Four
leaked per run, matching the four Python lanes that fail on a machine without
msgpack -- the wrapper is built inside the try, so when the peer's readiness
rejects it never exists to be closed, and the raw socket is orphaned. Now closed
through a guarded helper that tolerates a transport having adopted it.

The teardown registration: t.after was registered after every `await ready`, so
a peer whose readiness rejected skipped the registration entirely and stranded
every peer already spawned. Moved ahead of the spawns at all four sites.

With those fixed the suite still does not drain on its own, so test:interop now
passes --test-force-exit. That is safe only because cleanup demonstrably
completes first: a forced run now leaves zero peers behind, where before it left
nine. Forcing the exit without the fixes above would have guaranteed orphans
rather than prevented them.

Verified: 65 seconds, exit 1, the expected 15 pass and 12 fail, no surviving
processes. The 12 failures are the Python lanes, which cannot run on this
machine and are unrelated.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 16:32:10 +02:00
Meta Vens
8cb3b7282b Publish the non-Rust packages and make the licensing uniform
Rust consumers pin a CultLib revision through cargo git dependencies, which is
how Ghostlight, Epiphany, CodexConnector and Odin already consume the core
stack. npm and pip cannot take a package from a subdirectory of a git repo at
all, which is why the TypeScript and Python consumers vendored forks instead:
there was nothing else to depend on. This gives them a versioned artifact.

Tagging cultcache-ts-v* or cultcache-py-v* builds, tests and publishes. The tag
version must match the manifest version or the workflow refuses rather than
shipping a mislabelled artifact. PyPI uses trusted publishing, so no long-lived
token is stored; npm needs an NPM_TOKEN secret.

cultcache-ts is now @gamecult/cultcache-ts. The scope is not cosmetic: it makes
the package unambiguously org-owned on a public registry, and it is what lets
CultLib hold the name rather than racing for an unscoped one. CultLib's own
cultmesh-ts and cultnet-ts and their imports move with it; all three packages
typecheck and cultcache-ts passes 12 of 13 tests, the exception being the
cross-runtime store test that shells out to Python, which cannot run on this
machine because its Python has no msgpack.

Licensing was inconsistent with the repo. cultcache-py declared itself "Private
GameCult infrastructure" and cultcache-rs and cultmesh-rs declared nothing,
inside a repo whose LICENSE is MIT and whose every other package says MIT. All
three now say MIT. That was load-bearing here: a package that calls itself
private is not one to push to a public registry.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 04:41:44 +02:00
Meta Vens
0736ae4824 Separate RUDP reliable and lossy sequence domains 2026-09-04 00:33:16 +02:00
Meta Vens
334e60f192 Recover authority-bound CultMesh and atomic generations 2026-08-26 19:01:24 +02:00
Meta Vens
75c180782a Acknowledge retransmits across RUDP horizons 2026-08-23 19:15:22 +02:00
Meta Vens
fda51fad50 Acknowledge reliable RUDP windows cumulatively 2026-08-23 19:00:33 +02:00
Meta Vens
a2e3d3fa35 Bind reliable windows to the RUDP ACK horizon 2026-08-23 18:54:18 +02:00
Meta Vens
f4dabfefb0 Bound RUDP reliable transmission windows 2026-08-23 18:35:49 +02:00
Meta Vens
f622ee1b24 Authenticate CultMesh authority routes and replay operations 2026-08-18 19:02:46 +02:00
Meta Vens
6c848bd6c0 Bind CultMesh authority identity to physical routes 2026-08-18 17:21:58 +02:00
Meta Vens
c5d9435deb Carry portable schema identity on raw records 2026-08-18 02:08:49 +02:00
Meta Vens
d563a1ac64 Discover browser providers through Odin 2026-08-18 00:34:44 +02:00
Meta Vens
8eb89e38ec Add real browser CultMesh transport proof 2026-08-18 00:05:57 +02:00
Meta Vens
4af7398372 Normalize CultNet schema files 2026-08-17 23:27:07 +02:00
Meta Vens
3fe2e9be1b Publish browser subscription wire contracts 2026-08-17 23:26:51 +02:00
Meta Vens
693df0901d Release CultLib 1.0.19 2026-07-19 00:54:26 +01:00
Meta Vens
5c075bc212 Fence provider RUDP connection generations 2026-07-19 00:50:20 +01:00
Meta Vens
8965f3c0e0 Authenticate provider session ingress 2026-07-15 01:54:06 +01:00
Meta Vens
6528f8ea2f Make TypeScript packages release-packable 2026-07-13 23:47:38 +01:00
Meta Vens
ddb5418e04 Add CultNet RUDP operation service host 2026-07-10 11:20:51 +01:00
Meta Vens
2272aeff08 Add CultNet operation service envelopes 2026-07-10 11:11:28 +01:00
Meta Vens
340df225d4 Align TypeScript RUDP acknowledgement sequencing 2026-07-10 06:56:49 +01:00
Meta Vens
94a8c5b104 Resolve TS shard discovery filters by schema alias 2026-06-27 21:57:54 +01:00
Meta Vens
aeb7a96bd3 Resolve TS typed document messages by schema alias 2026-06-27 21:56:02 +01:00
Meta Vens
2738f4c2db Resolve TS snapshot filters by schema alias 2026-06-27 21:46:05 +01:00
Meta Vens
af2bd4a82d Resolve TS snapshot aliases by payload shape 2026-06-27 21:17:07 +01:00
Meta Vens
b2f2e1a0ff Add Kotlin interop RUDP serve dial path 2026-06-17 12:45:30 +01:00
Meta Vens
cd3f3c28f6 Add C# interop RUDP serve dial path 2026-06-17 11:22:17 +01:00
Meta Vens
c80703172a Add Rust interop RUDP serve dial path 2026-06-17 10:07:37 +01:00
Meta Vens
91d9ecda93 Add Python interop RUDP serve dial path 2026-06-17 08:05:41 +01:00
Meta Vens
25847c0845 Add TypeScript interop RUDP serve dial path 2026-06-17 07:28:47 +01:00
Meta Vens
e9ee0b59a1 Harden interop lane diagnostics 2026-06-16 18:52:02 +01:00
Meta Vens
f0ae8aed7f Harden interop discovery lane timeouts 2026-06-16 18:05:23 +01:00
Meta Vens
fa2f20977e Fix interop workflow TypeScript build prerequisites 2026-06-16 17:13:32 +01:00
Meta Vens
e06e3f5b3a Add cross-runtime interop workflow 2026-06-16 17:09:33 +01:00
Meta Vens
7711edf930 Advertise TS shard catalog schemas 2026-06-16 15:12:12 +01:00
Meta Vens
8bc505f0c1 Add TypeScript shard catalog sync helpers 2026-06-16 13:53:12 +01:00
Meta Vens
30934fc776 Add TypeScript schema catalog sync helpers 2026-06-16 13:36:34 +01:00
Meta Vens
4335ab10bd Fix RUDP ordered control sequence parity 2026-06-16 13:18:39 +01:00
Meta Vens
805539384e Add TypeScript TCP peer helper 2026-06-16 12:15:23 +01:00
Meta Vens
fad79906be Route TS interop through transport port 2026-06-16 09:51:00 +01:00
Meta Vens
3805352dbb Add TypeScript RUDP reconnect loop 2026-06-16 05:58:28 +01:00
Meta Vens
4a54966b10 Add portable reconnect controllers 2026-06-16 05:29:45 +01:00