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.