Files

Kotlin compatibility fixture and size baseline

This fixture uses a Gradle composite build to consume Kiloparsec source from the repository root (../..), with explicit dependency substitution. The JVM fixture uses setupWebsocketServer and KiloInterface unchanged.

Build and start the reference server

From the TypeScript module directory (requires JDK 21 and the parent build's Android SDK configuration):

npm run fixture:build
npm run baseline:build
JAVA_HOME=/path/to/jdk21 evaluation/server/build/install/server/bin/server

Default endpoint: ws://127.0.0.1:18732/kp; optional first argument overrides port. The server binds localhost only. Its deterministic Ed25519 TEST-ONLY seed is bytes 0 through 31. Public key:

03a107bff3ce10be1d70dd18e74bc09967e4d6309ba50d5f1ddc8664125531b8

Commands use ordinary Kotlin serialization/BiPack:

Command Arguments Result / behavior
echo String same String
sum List Int sum
identity Unit authenticated remote public key raw lowercase hex, or anonymous
callback String server calls clientEcho(String): String, returns its result
push String server calls notice(String): Unit, waits for acknowledgment
fail String throws registered IllegalArgumentException with that message

The fixture supports anonymous clients to allow explicit coverage of identity presence/absence; a production application's authorization policy must decide which identities may invoke its methods.

Optimized Kotlin/JS baseline

baseline exports a usable BaselineClient accepting binary block callbacks. It retains full DH, signed identity, expected server identity checking and RPC, without retaining the Ktor WebSocket client intentionally. This is the smaller bridge option rather than a deliberately inflated full library bundle.

npm ci --prefix evaluation --ignore-scripts
node evaluation/baseline-smoke.cjs
node_modules/.bin/esbuild evaluation/baseline/build/compileSync/js/main/productionExecutable/kotlin/kiloparsec-evaluation-baseline.js --bundle --minify --platform=browser --external:fs --external:path --external:crypto --outfile=evaluation/baseline-browser.min.js

fs, path, crypto are libsodium's conditional Node imports, unused in browser execution. Bundle includes libsodium-sumo WASM payload. Source maps are excluded. Kotlin production compiler performs dead-code elimination, followed by esbuild minification. npm explicitly pins sodium wrapper 0.7.15 for comparison with TS.

For the common comparison, use the same Node CJS platform for both libraries:

npm run build
node scripts/measure.mjs
node evaluation/baseline-smoke.cjs --bundled

Results are saved in evaluation/build/measurement/results.json. A recorded run is preserved in measurements.json. The script performs five fresh subprocess measurements, from immediately before module loading through WASM initialization and generation of one Ed25519 identity. It does not include process startup. RSS includes Node; RSS delta is resident growth during workload.

Historical measurements of the pre-hardening published 0.7.2 baseline, 2026-10-18 on Node 24.19.0, Linux x64 (not regenerated for current source):

Measure TS full exports Kotlin minimal bridge
Minified self-contained Node bundle, bytes 1,124,612 1,982,755
Gzip level 9, bytes 330,101 559,003
Cold init + identity median, ms 88.59 166.50
Resident memory median, bytes 62,898,176 70,574,080
Resident growth median, bytes 19,300,352 26,976,256

The Kotlin baseline exposes only echo and the bridge plus the measurement identity helper; TS includes all exported API. This is not a claim of full feature parity. Both include sodium's WASM payload. Both actual standalone bundles initialize successfully; the measured Kotlin bundle additionally passed authenticated, pinned-server echo to the JVM fixture. Measurements vary by host and revision.

The Kotlin sodium bindings eagerly capture imported functions: await require('libsodium-wrappers-sumo').ready before loading the compiled bridge, as shown in the smoke script. Loading the bridge first produces _sodium_init is not a function with these published dependencies. That observation concerns the historical published baseline. The browser bundle's size was measured; browser execution is not claimed by the Node smoke check. The bridge is an evaluation fixture, not a polished production wrapper (bounded buffering, lifecycle error propagation, and key ownership would need finishing if this alternative were selected).

Protocol 2 validation and measurements

Current-source protocol 2 results are in measurements-v2.json. TS: 1,126,081 bytes / gzip330,527; Kotlin baseline: 1,993,070 / gzip561,965. Both measured bundles completed pinned-server echo against the local protocol2 fixture. Historical measurements above remain unchanged for traceability. The full root verification command passed 43 JVM tests, 28 Kotlin/JS Node tests, 35 TS unit/security tests, and the real TS/JVM interoperability scenario.

Optimized Kotlin/Wasm comparison

wasm-baseline retains the same full handshake, server key pin, fresh client identity and echo RPC as the JS baseline. It exports a small string/callback bridge, avoiding non-exportable Kotlin objects at the JS/Wasm boundary.

From the TypeScript module directory:

npm run wasm:build
npm ci --prefix evaluation --ignore-scripts
npm run measure:wasm
npm run fixture:build
npm run test:wasm-baseline

The measurement uses the optimized production executable .wasm emitted by Binaryen, together with an esbuild-minified ESM loader containing sodium and joda. The raw .klib, unoptimized compiler output, maps and test runtime are excluded. Both the raw and wrapper sodium imports share the same CommonJS module instance, so the crypto payload is counted once. The wrapper is pinned to 0.7.15; its locked transitive raw sodium dependency is 0.7.16, as in the JS/TS comparison.

The Kotlin loader must receive the sodium modules after sodium.ready. The small wasm-entry.mjs adapter does this, supplies the expected default/namespace shapes, and includes Node CommonJS helpers for the bundled sodium dependency. It does not change the protocol or cryptography. The optimized asset stays beside client.mjs.

Recorded Node/Linux results are in wasm-measurements.json. The runnable distribution consists of two assets:

Asset Bytes Gzip level 9 bytes
Minified loader, sodium and joda 1,319,098 368,938
Optimized Kotlin executable 764,584 247,073
Total 2,083,682 616,011

Gzip is applied separately to each transferred asset and the sizes are summed. The distribution passed initialization, Ed25519 identity generation, full DH, server pinning and authenticated echo against the local protocol-2 JVM fixture. This qualifies the comparison on Node; no browser runtime result is claimed.