
Multiplatform RPC toolkit with Ktor integration, enabling service interface definition and cross-boundary calls. Offers client/server stub generation, pluggable transports, and optional obfuscation.
KWire is a Kotlin Multiplatform RPC toolkit with Ktor integration, kotlinx.serialization, and optional obfuscation. It provides a small set of core message types, a pluggable client transport, a native Ktor server plugin for service registration, optional code generation support, and runnable samples.
This repository contains the core library, Ktor integration (client and server plugin), an obfuscation-support module, and runnable samples for a UserService over WebSockets.
KWire helps you define RPC service interfaces in Kotlin and call them across process/network boundaries. You can:
@RpcService and @RpcMethod
Flow
UserService)core: message types (RpcRequest/RpcResponse/Stream*), errors, and RpcTransport plus service annotationsktor-integration-client: Ktor WebSocket client transportktor-integration-server: Native Ktor plugin to expose services over WebSocketsktor-integration-common: Shared Ktor integration utilitiesobfuscation-support: Maps obfuscated ids to service/method namessamples: minimal UserService API and runnable client/server examplescore/ — Core abstractions and messages
net.tactware.kwire.core.RpcService, RpcMethod annotationsnet.tactware.kwire.core.messages.* (RpcRequest, RpcResponse, StreamData, StreamEnd, etc.)net.tactware.kwire.core.RpcTransport interfacektor-integration-client/ — Ktor WebSocket client transport
net.tactware.kwire.ktor.KtorWebSocketClientTransportktorWebSocketClientTransport { ... }
ktor-integration-server/ — Ktor server plugin to expose RPC services
KWireRpc and register services with pathswithGeneratedServer { transport, impl -> ... }
ktor-integration-common/ — Shared Ktor integration utilitiesobfuscation-support/ — Obfuscation mapping and validation utilitiessample-api/ — Sample shared API (UserService, DTOs)sample-server/ — Runnable server example using the Ktor pluginsample-client/ — Runnable client example using the Ktor client transport@RpcService("UserService")
interface UserService {
@RpcMethod("createUser")
suspend fun createUser(request: CreateUserRequest): User
@RpcMethod("getAllUsers")
suspend fun getAllUsers(): List<User>
@RpcMethod("streamUsers")
fun streamUsers(): Flow<User>
}install(KWireRpc) {
service<UserService>("/users") {
implementation { UserServiceImpl() }
withGeneratedServer { transport, impl ->
UserServiceServerImpl(transport, impl)
}
}
}val transport = ktorWebSocketClientTransport(scope) {
serverUrl("ws://localhost:8082/users")
}
val client = UserServiceClientImpl(transport)The samples show a production-style WebSocket setup using the native Ktor plugin on the server and the Ktor client transport.
Server entry point:
sample-server/src/main/kotlin/net/tactware/kwire/sample/server/ServerExample.kt (object PluginServerExample)ws://0.0.0.0:8082/users)Client entry point:
sample-client/src/main/kotlin/net/tactware/kwire/sample/client/ClientExample.ktws://localhost:8082/users and demonstrates request/response and streamingWays to run:
PluginServerExample), then ClientExample
./gradlew :sample-server:run./gradlew :sample-client:runClient: net.tactware.kwire.ktor.KtorWebSocketClientTransport
ktorWebSocketClientTransport(scope) { serverUrl("ws://localhost:8082/users"); pingInterval(15); requestTimeout(30000) }
Server: native Ktor plugin KWireRpc
withGeneratedServer { transport, impl -> ... }
ktor-integration-server/README.md and ktor-integration-server/PLUGIN_README.md
Each transport implements net.tactware.kwire.core.RpcTransport with:
suspend fun connect()/disconnect()val isConnected: Booleansuspend fun send(message: RpcMessage)fun receive(): Flow<RpcMessage>The obfuscation-support module includes:
ObfuscationManager and related classes to map obfuscated identifiers to original namesWhen enabled, requests carry an obfuscated methodId, which is resolved at runtime by the server.
./gradlew build
./gradlew test
Kotlin/Gradle versions are controlled from gradle/libs.versions.toml and gradle.properties.
ws://localhost:8082/users in the samples).PluginServerExample and ClientExample mains directly from your IDE if mainClass isn’t aligned.@Serializable and the same version of kotlinx.serialization is used across modules.Module-specific guides:
ktor-integration-server/README.md — Ktor server integration overviewktor-integration-server/PLUGIN_README.md — Ktor plugin usage and configurationUnless otherwise specified in the repository, this project is provided under the license declared in the repository root or headers (add your license text or file reference here).
To keep secrets out of gradle.properties, the build reads publishing/signing credentials from a local.properties file at the project root (if present), then falls back to gradle.properties, then environment variables.
Add a local.properties with the following keys (do not commit this file):
ossrhUsername=your-sonatype-username
ossrhPassword=your-sonatype-password
signing.keyId=XXXXXXXX
signing.key=-----BEGIN PGP PRIVATE KEY BLOCK-----\n...\n-----END PGP PRIVATE KEY BLOCK-----
signing.password=your-key-passphrase
Notes:
local.properties should be excluded from version control (it is commonly ignored by default; ensure your VCS ignores it).core, ktor-integration-client, ktor-integration-server.OSSRH_USERNAME, OSSRH_PASSWORD, SIGNING_KEY, SIGNING_PASSWORD.KWire is a Kotlin Multiplatform RPC toolkit with Ktor integration, kotlinx.serialization, and optional obfuscation. It provides a small set of core message types, a pluggable client transport, a native Ktor server plugin for service registration, optional code generation support, and runnable samples.
This repository contains the core library, Ktor integration (client and server plugin), an obfuscation-support module, and runnable samples for a UserService over WebSockets.
KWire helps you define RPC service interfaces in Kotlin and call them across process/network boundaries. You can:
@RpcService and @RpcMethod
Flow
UserService)core: message types (RpcRequest/RpcResponse/Stream*), errors, and RpcTransport plus service annotationsktor-integration-client: Ktor WebSocket client transportktor-integration-server: Native Ktor plugin to expose services over WebSocketsktor-integration-common: Shared Ktor integration utilitiesobfuscation-support: Maps obfuscated ids to service/method namessamples: minimal UserService API and runnable client/server examplescore/ — Core abstractions and messages
net.tactware.kwire.core.RpcService, RpcMethod annotationsnet.tactware.kwire.core.messages.* (RpcRequest, RpcResponse, StreamData, StreamEnd, etc.)net.tactware.kwire.core.RpcTransport interfacektor-integration-client/ — Ktor WebSocket client transport
net.tactware.kwire.ktor.KtorWebSocketClientTransportktorWebSocketClientTransport { ... }
ktor-integration-server/ — Ktor server plugin to expose RPC services
KWireRpc and register services with pathswithGeneratedServer { transport, impl -> ... }
ktor-integration-common/ — Shared Ktor integration utilitiesobfuscation-support/ — Obfuscation mapping and validation utilitiessample-api/ — Sample shared API (UserService, DTOs)sample-server/ — Runnable server example using the Ktor pluginsample-client/ — Runnable client example using the Ktor client transport@RpcService("UserService")
interface UserService {
@RpcMethod("createUser")
suspend fun createUser(request: CreateUserRequest): User
@RpcMethod("getAllUsers")
suspend fun getAllUsers(): List<User>
@RpcMethod("streamUsers")
fun streamUsers(): Flow<User>
}install(KWireRpc) {
service<UserService>("/users") {
implementation { UserServiceImpl() }
withGeneratedServer { transport, impl ->
UserServiceServerImpl(transport, impl)
}
}
}val transport = ktorWebSocketClientTransport(scope) {
serverUrl("ws://localhost:8082/users")
}
val client = UserServiceClientImpl(transport)The samples show a production-style WebSocket setup using the native Ktor plugin on the server and the Ktor client transport.
Server entry point:
sample-server/src/main/kotlin/net/tactware/kwire/sample/server/ServerExample.kt (object PluginServerExample)ws://0.0.0.0:8082/users)Client entry point:
sample-client/src/main/kotlin/net/tactware/kwire/sample/client/ClientExample.ktws://localhost:8082/users and demonstrates request/response and streamingWays to run:
PluginServerExample), then ClientExample
./gradlew :sample-server:run./gradlew :sample-client:runClient: net.tactware.kwire.ktor.KtorWebSocketClientTransport
ktorWebSocketClientTransport(scope) { serverUrl("ws://localhost:8082/users"); pingInterval(15); requestTimeout(30000) }
Server: native Ktor plugin KWireRpc
withGeneratedServer { transport, impl -> ... }
ktor-integration-server/README.md and ktor-integration-server/PLUGIN_README.md
Each transport implements net.tactware.kwire.core.RpcTransport with:
suspend fun connect()/disconnect()val isConnected: Booleansuspend fun send(message: RpcMessage)fun receive(): Flow<RpcMessage>The obfuscation-support module includes:
ObfuscationManager and related classes to map obfuscated identifiers to original namesWhen enabled, requests carry an obfuscated methodId, which is resolved at runtime by the server.
./gradlew build
./gradlew test
Kotlin/Gradle versions are controlled from gradle/libs.versions.toml and gradle.properties.
ws://localhost:8082/users in the samples).PluginServerExample and ClientExample mains directly from your IDE if mainClass isn’t aligned.@Serializable and the same version of kotlinx.serialization is used across modules.Module-specific guides:
ktor-integration-server/README.md — Ktor server integration overviewktor-integration-server/PLUGIN_README.md — Ktor plugin usage and configurationUnless otherwise specified in the repository, this project is provided under the license declared in the repository root or headers (add your license text or file reference here).
To keep secrets out of gradle.properties, the build reads publishing/signing credentials from a local.properties file at the project root (if present), then falls back to gradle.properties, then environment variables.
Add a local.properties with the following keys (do not commit this file):
ossrhUsername=your-sonatype-username
ossrhPassword=your-sonatype-password
signing.keyId=XXXXXXXX
signing.key=-----BEGIN PGP PRIVATE KEY BLOCK-----\n...\n-----END PGP PRIVATE KEY BLOCK-----
signing.password=your-key-passphrase
Notes:
local.properties should be excluded from version control (it is commonly ignored by default; ensure your VCS ignores it).core, ktor-integration-client, ktor-integration-server.OSSRH_USERNAME, OSSRH_PASSWORD, SIGNING_KEY, SIGNING_PASSWORD.