No description
  • C# 42.6%
  • TypeScript 13.9%
  • Rust 13.1%
  • HTML 10.7%
  • Python 10.6%
  • Other 9.1%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Meta Vens 00f4c02dd7 CultNet selection cut1, commit 4: the parity vectors, and the ruling this branch landed under
Two things landed on this branch while commit 3 was in flight, both addressed
here rather than left for the vectors to paper over:

**Self's ruling, 2026-09-22 (commit 2 fix batch, 568e8e3/021e3f1): empty
lists, blank entries, and v0's own cleaning.** Rust already refused an empty
schemas/keys list (inherited from before the ruling); it now also matches
the reference's three further decisions exactly, not just in spirit:

- The door's refusal field is the plain list name ("schemas"/"keys"), not an
  indexed "schemas[i]" - selection::validate's SelectionInvalid.field now
  matches CultNetSelectionInvalidException("schemas", schema, ...) literally,
  because the parity vectors compare this string across runtimes.
- snapshot_query.rs's lower_v0_list now deduplicates a mixed list the same
  way CultNetV0SelectionLowering.Lower does (Distinct(StringComparer.Ordinal)
  - order-preserving, first occurrence wins), not just blank-filters it.
  A new test (serve_read_only_raw_snapshot_lowers_an_empty_or_blank_v0_schema_list_to_no_filter,
  extended) pins the dedup case directly.
- Two mutation entries (RS-EmptySchemas-Revert, RS-EmptyKeys-Revert) added
  for the door's original empty-list refusal, which had tests but no
  mutation coverage before this commit.

**S12: the two-way parity vectors**, contracts/cultnet/interop/selection-vectors.{cs,rs}-written.json,
eight vectors each: the six evaluated selections from commit 3 (any_of+ge
conjunction, descending+limit, gt boundary, cited exists true/false, three
rows sharing one index value) plus two door-refusal vectors added for this
ruling - `refuses_empty_schemas_list` (schemas: []) and `refuses_blank_key_entry`
(keys: ["a-eq", "   "]) - which decode their selection bytes and assert
`validate`/`Validate` refuses at exactly the named field, never evaluating.
The blank-key vector could not be generated before this commit: the door
rule it exercises did not exist in the reference until 568e8e3 landed.
`cites`-based hop vectors are deliberately absent (documented in both test
files): MatchesCitation compares Citation.Target.SchemaId against the
reference's real SHA-256 schema id exactly, with no SchemaName fallback,
and this fixture pair has no way to agree on that hash across runtimes;
`cited` (role/existence, matched by record key alone) is the hop direction
both sides can cross-check, so incoming_exists_true/incoming_negation carry
that coverage instead. Both reader tests pass:
SelectionVectorsWrittenByRustDecodeAndEvaluateIdenticallyInTheReference (C#)
and selection_vectors_written_by_the_reference_decode_and_evaluate_identically_in_rust
(Rust), each judging the other runtime's file.

**The remaining mutation matrix.** Of the coordinator's named set (S1, S2,
S6, S9-S15, S17, S20, S23): S1 was already covered (RS-EmptySchemas/Keys,
RS-BlankSchema/Key); S2 (any_of matches membership, RS-S2-Revert) and S6
(cites.role filters the hop, RS-S6-Revert) are newly covered - S6 needed a
new fixture (cites_role_excludes_a_second_reference_at_the_same_target)
because base_rows()'s citer-1 only ever carries one reference, so a mutant
dropping the role filter had nothing else to wrongly match and survived
against the original hops_one_edge_by_declared_role test; that finding is
recorded in-line as a comment, not silently swapped for an easier mutant.
S9-S15 (subscription reconcile, v0-lowering-of-subscribe) do not apply:
D7 - no Rust caller subscribes in this cut, so there is no subscription
client to mutate. S17 (inherited alias reaches every leaf) does not apply
either: Rust's toy Row/RowSet has no inheritance to begin with - both
leaves in the fixture declare "mass" independently, which is the Rust
spelling of the same guarantee, already exercised throughout the suite
without a dedicated mutant (there is no shared base-type discovery path to
revert). S20 (edges only for a hopping selection) was investigated and
found structurally vacuous in Rust as built: `edges` is only ever populated
inside the cites/cited branches, so a mutant on the has_hop() gate cannot
produce a different observable result for a non-hopping selection - no
entry added; a real gap would need the evaluator's structure to change
first. S23 (matches every row sharing an index value, never a cache's
unique-index winner) has no revertable bug surface: Rust's Row/RowSet
never had a unique-index map to begin with (D7), so this is pinned by
construction, not by a mutation-testable rule; matches_every_row_sharing_an_index_value
still exists as a positive test naming it.

**A pre-existing, unrelated flaky test, found and worked around, not
fixed.** tests/cultnet.rs's reactive_document_coalesces_direct_same_schema_alias_member_writes
fails consistently when the mutation harness runs the full tests/cultnet.rs
binary (both default-parallel and RUST_TEST_THREADS=1), but passes 5/5 in
isolation and passes in a plain `cargo test -p cultnet-rs` run - state
pollution from another test in that binary under the harness's specific
invocation path, not a race this campaign's code participates in (nothing
here touches reactive_document machinery). RS-V0Lowering-Revert's Command
is narrowed to `cargo test --test cultnet serve_read_only_raw_snapshot`
(filtering both M0 and the entry's own run to this campaign's test) so the
suite does not depend on that unrelated test's health; the flake itself is
reported here as a finding, not patched.

Tests: cultnet-rs 174 passed, 0 failed (default parallel run).
GameCult.Networking.Tests 196 passed, 1 skipped (WriteSelectionVectors,
env-gated), 0 failed.

Mutation harness: 24 entries (was 22 in commit 3; +RS-S2-Revert, +RS-S6-Revert;
RS-BlankSchema/BlankKey and RS-V0Lowering-Revert's anchors and field-name
assertions updated to match the landed fix batch's exact spelling), all
KILLED, M0 green.

Negative grep: `rg -n "untagged" packages/cultnet-rs/src packages/cultcache-rs/src` empty.

Structural delta, this commit only: 6 files changed, +108/-23 (selection.rs
+15/-9 field-name fix; snapshot_query.rs +25/-14 dedup; tests/cultnet.rs +21
dedup test; tests/selection.rs +35/-2 S6 fixture and field-name fixes;
scripts/mutate-cultnet-selection-rust.entries.ps1 +33/-4 two new entries
plus three corrected anchors/commands; SelectionParityVectorTests.cs +2/-1
field name), plus the two committed vector JSON files (~90 lines each, new).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-22 06:55:52 +02:00
.github/workflows Write and enforce the semver policy, per operator ruling on 1.0.60 2026-09-17 22:03:22 +02:00
.voidbot REC-102: cut stale generated mirrors and a dead maintenance receipt 2026-09-13 14:10:10 +02:00
.vscode Initial commit 2026-04-18 06:04:39 +01:00
contracts CultNet selection cut1, commit 4: the parity vectors, and the ruling this branch landed under 2026-09-22 06:55:52 +02:00
docs Record Soul's sixth pass: the wait's predicate is pinned by nothing 2026-09-22 00:50:29 +02:00
native/GameCult.Mesh.Quic.Native Tell an existing Windows clone how to get its build script back to LF 2026-09-22 01:03:51 +02:00
notes Specify canonical CultCache persistence format 2026-05-08 00:16:55 +01:00
packages CultNet selection cut1, commit 4: the parity vectors, and the ruling this branch landed under 2026-09-22 06:55:52 +02:00
research/cultnet-distributed-database Add CultNet simulation observation server 2026-05-19 18:48:53 +01:00
samples Give the counter sample integer MessagePack keys 2026-09-14 00:27:19 +02:00
scripts CultNet selection cut1, commit 4: the parity vectors, and the ruling this branch landed under 2026-09-22 06:55:52 +02:00
src CultNet selection cut1, commit 2 fix batch: door, shard-via-evaluator, Mesh v0 refusal, dead Reconcile code, mutation harness crash handling 2026-09-22 06:13:15 +02:00
tests CultNet selection cut1, commit 4: the parity vectors, and the ruling this branch landed under 2026-09-22 06:55:52 +02:00
tools Authenticate CultMesh authority routes and replay operations 2026-08-18 19:02:46 +02:00
unity/org.gamecult.cultlib Say the OpenSSL bridge ships with this release, and pack the changelog 2026-09-17 20:40:12 +02:00
.gitattributes Tell an existing Windows clone how to get its build script back to LF 2026-09-22 01:03:51 +02:00
.gitignore Ignore Python package build output 2026-09-10 13:55:46 +02:00
AGENTS.md JSON backing store, I/O fixes, Networking Security 2026-04-18 13:01:24 +01:00
CLAUDE.md Wire AGENTS.md into Claude Code memory 2026-09-04 00:33:01 +02:00
CultLib.sln Delete the CultCache MessagePack source generator 2026-09-13 23:38:08 +02:00
Directory.Build.props Ship authenticated remote transports in Unity package 2026-08-18 20:23:27 +02:00
LICENSE Initial commit 2026-04-18 06:04:39 +01:00
package-lock.json Give every cultcache-ts document type one home store 2026-09-14 00:37:31 +02:00
package.json Add real browser CultMesh transport proof 2026-08-18 00:05:57 +02:00
README.md Delete the CultCache MessagePack source generator 2026-09-13 23:38:08 +02:00

CultLib

CultLib is GameCult's shared state, transport, and runtime substrate. It started as reusable C# game infrastructure, but the useful thing now is larger: typed .cc persistence, schema-stable wire formats, ergonomic reliable-UDP networking, distributed database sync, simulation witness consensus, streaming surfaces, and UI/runtime affordances that can be used from C#, TypeScript, Rust, Python, and Kotlin without each runtime inventing its own local folklore.

The stack is less a single product than a set of capabilities:

  • logging primitives and implementations
  • CultCache typed documents, .cc persistence, and SoA memory access
  • CultNet schema-v0 messages over TCP and LiteNetLib adapters, a bounded ASP.NET Core WebSocket host adapter, and CultLib's native cross-runtime RUDP pipe
  • CultMesh distributed realtime database, Verse discovery, authority leases, shard replication, and witness-authoritative simulation consensus
  • typed geometry domain/chunk documents for distributed CSG and LOD streaming
  • low-latency stream catalogs and frame-handle negotiation for media, tensors, and runtime-native buffers
  • Eve/CultUI daemon-published surfaces that lower into GUI, TUI, web, native, overlay, or agent-facing clients
  • declarative Unity UI composition and reflective runtime inspector tooling

The practical pitch: a runtime can persist typed state, exchange it with peers, discover compatible schemas, join a Verse, publish observations, reconcile canonical state, and expose an operator surface without translating through a bespoke JSON bridge at every boundary. A frontend is just another CultMesh client: the daemon publishes a typed Eve/CultUI surface, and any compatible runtime can lower that surface where it needs to live.

Which Package Do I Want?

If the job is shared game state, start here before inventing a fourth drawer and pretending the label makes it furniture.

Job Start With Owns Use When Do Not Use It For
Local typed state GameCult.Caching / CultCache Document identity, schema compatibility, record keys, indexes, globals, and local persistence You need a typed cache, file-compatible save data, local reactive reads, or a stable domain document model Peer discovery, transport security, shard routing, or mesh consensus
Portable math GameCult.Math / CultMath Shader-shaped numeric primitives, deterministic random, noise, and CPU/GPU parity helpers Shared simulation and geometry code must not depend on an engine's math or RNG types Domain-specific geometry, rendering, or world authority
Procedural geometry GameCult.Geometry / CultGeometry Isosurface extraction plus CultCache-native domains, LOD requests, selected cuts, and mesh artifacts C#, Rust, Godot, or remote workers need shared geometry algorithms and typed results Transport policy, peer discovery, or gameplay authority
Network transport and database plumbing GameCult.Networking / CultNet Native RUDP sessions, LiteNetLib/TCP adapters, an authenticated WebSocket host adapter, schema-v0 wire contracts, shard authority, raw document replication, snapshots, and subscriptions You need a client/server pipe, login/session flow, schema discovery, reliable UDP, or a low-level distributed CultCache lane Gameplay-facing mesh ergonomics, Verse policy, mod branches, or simulation consensus composition
Distributed realtime gameplay state GameCult.Mesh / CultMesh Public mesh entrypoints, Verse discovery, peer exchange, shard replication defaults, authority leases, client prediction, and witness consensus You want the game to treat clients and servers as one reactive database for persistent state, input state, and simulation facts A tiny local-only tool, a bare transport client, or a storage format contract
Realtime media/frame streams GameCult.Mesh / CultMesh streaming mode Stream identity, authority, clock metadata, body transport negotiation, frame cursors, and backpressure state Audio/video/tensor frames need to move between runtimes through shared memory, GPU handles, platform buffers, or CultCache page refs Durable document mutation, mesh consensus facts, or pretending inline bytes are zero-copy
Daemon-published UI Eve / CultUI Typed operator and user-facing surfaces over CultMesh state A daemon should expose controls, inspection, or workflow UI that can lower into GUI, TUI, web, native, overlay, or agent clients One-off vendor dashboards, untyped debug panels, or runtime-local UI that cannot travel with the daemon

Quick rule:

  • Choose CultCache when the problem is "how do I model and persist typed state?"
  • Choose GameCult.Math when the problem is "which portable numeric and random semantics do these runtimes share?"
  • Choose GameCult.Geometry when the problem is "how do geometry workers build and share engine-neutral geometry?"
  • Choose CultNet when the problem is "how do peers exchange authenticated, schema-aware database messages?"
  • Choose CultMesh when the problem is "how does a game join a Verse and share realtime state across a mesh?"
  • Choose CultMesh streaming mode when the problem is "how do these runtimes move audio/video frames while the mesh owns identity, clocks, cursors, and pressure?"
  • Choose Eve/CultUI when the problem is "how does this daemon publish its own operable surface instead of waiting for a hosted admin console?"

CultMesh sits on CultNet, and CultNet distributes CultCache documents. That is the stack. If a design needs another peer-to-peer category, first check whether it is really Verse policy, peer exchange, or shard authority wearing a fake mustache.

If you want to build an application rather than tour the component drawer, start with the maintained CultMesh And Eve Getting Started series. Its first executable package-consumer checkpoint is samples/eve-two-runtime. The real-process browser continuation is samples/eve-browser-network. One documented command runs both layers:

pwsh -File ./scripts/verify-eve-getting-started.ps1

The implemented browser client boundary and remaining deployed-infrastructure gates are mapped explicitly in browser-verse-transport.md.

If you want only the smallest durable "open node -> put typed doc -> get typed doc" path, use src/GameCult.Mesh/docs/durable-node-quickstart.md. If you want the lower-level "typed local document -> raw wire document -> mesh/runtime watch surface" handoff, follow it with src/GameCult.Mesh/docs/typed-document-path.md.

Repository Scope

The solution includes:

  • GameCult.Logging: common logging abstraction plus console and file implementations
  • GameCult.Caching: DatabaseEntry-based cache, indexes, global entries, and backing-store abstractions
  • GameCult.Caching.MessagePack: MessagePack-backed persistence for the cache
  • GameCult.Caching.NewtonsoftJson: Newtonsoft.Json-backed persistence for the cache
  • GameCult.Math / CultMath: portable numeric primitives, deterministic random, noise, and shader-parity helpers
  • GameCult.Geometry / CultGeometry: engine-neutral geometry algorithms plus CultCache-native domain, selected-cut, and mesh artifact documents
  • GameCult.Networking: encrypted login/register/verify flows, schema-v0 contracts, transport adapters, and native RUDP sessions
  • GameCult.Networking.WebSockets: bounded ASP.NET Core binary WebSocket host adapter for browser CultMesh clients
  • GameCult.Mesh: CultMesh package home for distributed realtime database, shard replication, client prediction, Verse discovery, and mesh witness consensus
  • GameCult.Caching.Tests: NUnit tests for cache and backing-store behavior
  • GameCult.Networking.Tests: NUnit tests for networking behavior
  • GameCult.Unity: CultUI, a Unity runtime UI composition framework with reflective inspector generation, prefab-backed field resolvers, reusable controls, and a demo project packaged for UPM-style consumption
  • packages/cultcache-ts: TypeScript CultCache with MessagePack persistence and inspector tooling
  • packages/cultnet-ts: TypeScript CultNet schema-v0 contracts, framing, discovery, raw document replication, and interop tests
  • packages/cultmesh-ts: TypeScript CultMesh local node and mesh catalog surface for local runtimes such as VoidBot
  • packages/cultmesh-browser: browser-safe, read-only CultMesh document leases and typed operations over an Odin-resolved WebSocket route
  • packages/cultcache-py: Python CultCache with CultCache v1 wire parity
  • packages/cultnet-py: Python CultNet contracts, framing, RUDP transport, replication, and interop peer
  • packages/cultmesh-py: Python CultMesh local node, discovery, sessions, local server, and daemon
  • packages/cultcache-rs: Rust CultCache and derive macro
  • packages/cultnet-rs: Rust CultNet contracts, framing, discovery, and interop peer
  • packages/cultmesh-kotlin: Kotlin/JVM CultMesh and CultNet surface for Android-adjacent runtimes

Repository Layout

package.json
src/
  GameCult.Logging/
  GameCult.Caching/
  GameCult.Caching.MessagePack/
  GameCult.Caching.NewtonsoftJson/
  GameCult.Geometry/
  GameCult.Networking/
  GameCult.Networking.WebSockets/
  GameCult.Mesh/
  GameCult.Unity/
tests/
  GameCult.Caching.Tests/
  GameCult.Geometry.Tests/
  GameCult.Networking.Tests/
packages/
  cultmath/
  cultcache-ts/
  cultnet-ts/
  cultmesh-ts/
  cultmesh-browser/
  cultmesh-kotlin/
  cultcache-py/
  cultnet-py/
  cultmesh-py/
  cultcache-rs/
  cultnet-rs/

Build

dotnet build CultLib.sln

Test

dotnet test CultLib.sln

TypeScript package tests:

npm test --workspace packages/cultcache-ts
npm test --workspace packages/cultnet-ts
npm run test:interop --workspace packages/cultnet-ts
npm test --workspace packages/cultmesh-ts
npm test --workspace packages/cultmesh-browser

Measured reactive, retained-session, and shared-memory hot-body gates:

pwsh -File ./scripts/measure-reactive-document-performance.ps1 -Quick
pwsh -File ./scripts/verify-eve-getting-started.ps1

The first command measures the same 16 KiB document workload in C#, TypeScript, and Python and includes a mapped hot-body probe with explicit copy telemetry on Windows named maps and Unix file-backed maps. The second includes 10,000 real WebSocket operations on one retained identity-owned C# session after provider route replacement.

Rust package tests:

cargo test --manifest-path packages/cultcache-rs/Cargo.toml
cargo test --manifest-path packages/cultnet-rs/Cargo.toml

Kotlin package build:

.\packages\cultmesh-kotlin\build.ps1

Common Concepts

DatabaseEntry

The cache-centric libraries revolve around DatabaseEntry. Every entry has a stable Guid identifier and can optionally:

  • expose a human-readable name through INamedEntry
  • participate in generic indexes registered at runtime
  • be treated as a global singleton entry through GlobalSettingsAttribute

Typical entry shape:

using GameCult.Caching;

public class ItemData : DatabaseEntry, INamedEntry
{
    public string Name = string.Empty;
    public int Value;

    public string EntryName
    {
        get => Name;
        set => Name = value;
    }
}

CultCache and Backing Stores

CultCache is an in-memory index over DatabaseEntry objects. It can operate entirely in memory, or it can be attached to one or more backing stores for persistence and synchronization.

  • the cache is the query surface
  • backing stores are persistence adapters
  • indexes and name maps are maintained inside the cache, not inside the store

Multiple Backing Stores

Every document type has exactly one home store. There are no mirrors.

cache.AddBackingStore(catalogStore, typeof(ItemData), typeof(Faction));
cache.AddBackingStore(runStore, typeof(SavedGame));
  • the home is chosen by the document's runtime type: the most derived routed type wins, otherwise the untyped store
  • at most one untyped store; a type claimed by two stores is an error
  • a write with no home is an error once any store is attached
  • attaching a store reads it; attach routed stores first and the untyped store (if any) last, because a record's home cannot move after it is loaded
  • loading never writes: opening and flushing without a mutation leaves every file byte-identical
  • a read-only store refuses writes and is skipped by flush

The contract is src/GameCult.Caching/Contracts/cultcache-store-composition.md. Until the C# cache implements it, AddBackingStore takes no home types and every attached store receives every record; attach one store per cache.

Example: Cache + Networking

using GameCult.Caching;
using GameCult.Logging;
using GameCult.Networking;

var cache = new CultCache
{
    Logger = new ConsoleLogger()
};

var server = new Server(cache)
{
    Logger = new ConsoleLogger()
};

server.Start();

Secrets and Runtime Configuration

GameCult.Networking uses two pieces of deployment configuration:

  • GAMECULT_CONNECTION_KEY
  • GAMECULT_SESSION_SIGNING_SECRET

Production guidance:

  • set both values before starting the server
  • treat GAMECULT_CONNECTION_KEY as shared client/server protocol configuration
  • treat GAMECULT_SESSION_SIGNING_SECRET as a server-only secret
  • generate high-entropy random values and store the server-side value in your platform's secret store
  • do not rely on local-development defaults in production
  • partial configuration is rejected at startup

Recommended value format:

  • at least 32 random bytes per value
  • encode as Base64 or Base64Url if you want a portable text representation

Example secret generation in PowerShell:

[Convert]::ToBase64String((1..32 | ForEach-Object { Get-Random -Maximum 256 }))

Typical production setup patterns:

  • inject GAMECULT_SESSION_SIGNING_SECRET and the server copy of GAMECULT_CONNECTION_KEY as environment variables from Docker secrets, Kubernetes secrets, systemd environment files, or your cloud secret manager
  • ship the matching GAMECULT_CONNECTION_KEY to the client through a build-generated config asset, code-generated constants, or another explicit client configuration mechanism
  • scope them per environment so development, staging, and production do not share values
  • rotate GAMECULT_SESSION_SIGNING_SECRET operationally
  • treat GAMECULT_CONNECTION_KEY as versioned protocol config; changing it requires a coordinated client/server rollout

Example server startup with strict environment validation:

using GameCult.Caching;
using GameCult.Networking;

var cache = new CultCache();
var security = ServerSecurityOptions.FromEnvironment();
var server = new Server(cache, security);

Example local-development server startup:

using GameCult.Caching;
using GameCult.Networking;

var cache = new CultCache();
var security = ServerSecurityOptions.Development();
var server = new Server(cache, security);

Example shipped-client startup with explicit client configuration:

using GameCult.Networking;

var security = new ClientSecurityOptions("<matching-connection-key>");
var client = new Client(security);

Project Docs

Each subproject has a local README with package-specific detail: