Add Kotlin CultMesh facade

This commit is contained in:
Meta Vens 2026-06-16 00:50:29 +01:00
commit 5201dd6ce6
3 changed files with 98 additions and 6 deletions

View file

@ -224,6 +224,11 @@ Current progress:
media, sensor, tensor, and byte streams: stream declaration, clock metadata,
preferred body transports, consumer negotiation, copy-budget classification,
max in-flight frame pressure, and latest-frame cursors.
- Kotlin now has a branded `CultMesh` facade matching the C#/TypeScript/Python
entrypoint pattern for local nodes, Verse catalogs, peer catalogs, authority
lease catalogs, stream catalogs, and RUDP client/server construction. The
facade delegates to the same owners as the top-level helpers instead of
creating a second authority path.
- The remaining parity work is to deepen Python's server-side use of its port
and move each runtime's existing TCP/LiteNetLib/WebSocket bodies behind those
ports, then broaden RUDP interop into reconnect policy and the wider

View file

@ -6,6 +6,16 @@ It provides typed MessagePack document codecs, a tiny WebSocket CultNet lane,
a single-peer CultNet RUDP socket transport, stream catalog negotiation, an
in-memory CultCache, and the first Eve dashboard/sensor document contracts.
The branded entrypoint mirrors C#/TypeScript/Python:
```kotlin
val node = CultMesh.startNode()
val verses = CultMesh.createVerseCatalog()
val peers = CultMesh.createPeerCatalog()
val leases = CultMesh.createAuthorityLeaseCatalog()
val streams = CultMesh.createStreamCatalog()
```
## CultCache Happy Path
Kotlin keeps the cache small but typed: document definitions wrap codecs,
@ -130,12 +140,12 @@ write client/server code without hand-binding sockets or repeating channel
strings:
```kotlin
cultNetRudpServer(
CultMesh.createRudpServer(
runtimeId = "kotlin-server",
connectionId = 0x10203040,
tuning = CultNetRudpSocketTuning(maxFragmentBytes = 1024),
).use { server ->
cultNetRudpClient(
CultMesh.createRudpClient(
runtimeId = "kotlin-client",
connectionId = 0x10203040,
remoteHost = "127.0.0.1",
@ -225,7 +235,7 @@ val peer = CultMeshPeerCard(
authorityLeaseId = "lease:kotlin-peer",
)
val leases = createAuthorityLeaseCatalog()
val leases = CultMesh.createAuthorityLeaseCatalog()
leases.upsert(
CultMeshAuthorityLease(
leaseId = "lease:kotlin-peer",
@ -250,7 +260,7 @@ stream identity, clock metadata, body transport negotiation, and latest-frame
cursors; it does not own the frame bytes themselves.
```kotlin
val streams = createStreamCatalog()
val streams = CultMesh.createStreamCatalog()
streams.declare(
CultMeshStreamDescriptor(
streamId = "mimir:camera",

View file

@ -268,6 +268,52 @@ class CultMeshNode(
fun applyShardLogResponse(response: CultNetMessage): List<Any?> = cache.applyShardLogResponse(response)
}
object CultMesh {
fun createNode(cache: CultCache = CultCache()): CultMeshNode = CultMeshNode(cache)
fun startNode(cache: CultCache = CultCache()): CultMeshNode = createNode(cache)
fun createVerseCatalog(): CultMeshVerseCatalog = CultMeshVerseCatalog()
fun createPeerCatalog(): CultMeshPeerCatalog = CultMeshPeerCatalog()
fun createAuthorityLeaseCatalog(): CultMeshAuthorityLeaseCatalog = CultMeshAuthorityLeaseCatalog()
fun createStreamCatalog(): CultMeshStreamCatalog = CultMeshStreamCatalog()
fun createRudpServer(
runtimeId: String,
connectionId: Long,
bindHost: String = "127.0.0.1",
bindPort: Int = 0,
tuning: CultNetRudpSocketTuning = CultNetRudpSocketTuning(),
): CultNetRudpSocketTransportConnection = cultNetRudpServer(
runtimeId = runtimeId,
connectionId = connectionId,
bindHost = bindHost,
bindPort = bindPort,
tuning = tuning,
)
fun createRudpClient(
runtimeId: String,
connectionId: Long,
remoteHost: String,
remotePort: Int,
bindHost: String = "127.0.0.1",
bindPort: Int = 0,
tuning: CultNetRudpSocketTuning = CultNetRudpSocketTuning(),
): CultNetRudpSocketTransportConnection = cultNetRudpClient(
runtimeId = runtimeId,
connectionId = connectionId,
remoteHost = remoteHost,
remotePort = remotePort,
bindHost = bindHost,
bindPort = bindPort,
tuning = tuning,
)
}
data class CultNetFrame(val opcode: Int, val payload: ByteArray)
data class CultNetTransportStats(
@ -2236,6 +2282,7 @@ private fun nowMs(): Long = Instant.now().toEpochMilli()
fun main(args: Array<String>) {
if (args.isEmpty()) {
cultCacheErgonomicsCoverTypedDocumentsAndGlobals()
cultMeshFacadeRoutesErgonomicEntrypoints()
cultCacheRawSnapshotsRoundTripThroughCultNetMessages()
cultNetSchemaMessagesUseMessagePackMaps()
cultNetSchemaCatalogsRoundTripDescriptors()
@ -2294,6 +2341,36 @@ private fun cultCacheErgonomicsCoverTypedDocumentsAndGlobals() {
check(node.forget(notes, "node-note"))
}
private fun cultMeshFacadeRoutesErgonomicEntrypoints() {
val notes = stringDocument("kotlin.facade_note", "kotlin.facade_note.v1")
val cache = CultCache()
cache.register(notes)
val node = CultMesh.startNode(cache)
node.remember(notes, "note:facade", "facade")
check(node.require(notes, "note:facade") == "facade")
val verses = CultMesh.createVerseCatalog()
verses.upsert(
CultMeshVerseDescriptor(
verseId = "facade",
displayName = "Facade Verse",
authorityModel = "federated",
compatibility = CultMeshVerseCompatibility("cultmesh.v0", "rules"),
),
)
check(verses.get("facade")?.displayName == "Facade Verse")
val peers = CultMesh.createPeerCatalog()
peers.upsert(CultMeshPeerCard("facade-peer", "facade", listOf("rudp://127.0.0.1:4100"), roles = listOf("schema")))
check(peers.find("facade", "schema").single().peerId == "facade-peer")
val leases = CultMesh.createAuthorityLeaseCatalog()
check(leases.leases.isEmpty())
val streams = CultMesh.createStreamCatalog()
check(streams.streams.isEmpty())
}
private fun cultCacheRawSnapshotsRoundTripThroughCultNetMessages() {
val notes = stringDocument("kotlin.note", "kotlin.note.v1")
val settings = stringDocument("kotlin.settings", "kotlin.settings.v1", global = true)
@ -3036,12 +3113,12 @@ private fun rudpSessionFragmentsAndReassemblesReliableOrderedPayloads() {
private fun rudpSocketTransportErgonomicFactoriesCarrySchemaFrames() {
val connectionId = 0x10203042L
cultNetRudpServer(
CultMesh.createRudpServer(
runtimeId = "kotlin-rudp-sugar-server",
connectionId = connectionId,
tuning = CultNetRudpSocketTuning(resendDelayMs = 25, maxFragmentBytes = 8, maxPendingReliablePackets = 16),
).use { server ->
cultNetRudpClient(
CultMesh.createRudpClient(
runtimeId = "kotlin-rudp-sugar-client",
connectionId = connectionId,
remoteHost = "127.0.0.1",