
Resumable tus protocol implementation offering a client, embeddable server library, standalone runnable server jar, shared protocol primitives, and end-to-end integration tests.
Tuskt is a Kotlin implementation of the tus resumable upload protocol.
The repository currently contains:
:tuskt-client: a Kotlin Multiplatform tus client:tuskt-server: a Ktor-based tus server library:tuskt-server-standalone: a runnable server fat jar:shared: shared tus protocol constants and primitives:integration-tests: end-to-end client/server interoperability testsPublished library modules:
dev.phillipslabs:tuskt-clientdev.phillipslabs:tuskt-serverdev.phillipslabs:tuskt-sharedChoose the module based on how you want to consume the project:
tuskt-client: add a multiplatform tus client to your applicationtuskt-server: embed tus endpoints in a Ktor servertuskt-shared: use protocol constants and shared primitives directlytuskt-server-standalone: build and run the fat jar instead of adding a library dependencyGradle Kotlin DSL examples:
dependencies {
implementation("dev.phillipslabs:tuskt-client:<version>")
implementation("dev.phillipslabs:tuskt-server:<version>")
implementation("dev.phillipslabs:tuskt-shared:<version>")
}For Kotlin Multiplatform, the client dependency typically belongs in commonMain:
kotlin {
sourceSets {
commonMain.dependencies {
implementation("dev.phillipslabs:tuskt-client:<version>")
}
}
}Tuskt is early-stage and currently implements the core resumable upload flow around:
OPTIONS for server capability discoveryHEAD for upload offset lookupPATCH for resuming/appending upload bytesThe current server does not yet implement tus extensions such as creation. You can see that reflected in the ignored server tests and TODOs in the codebase.
The multiplatform client currently supports:
OPTIONS
HEAD
PATCH
Targets currently configured in Gradle:
linuxX64, linuxArm64
mingwX64
The Ktor server library currently supports:
OPTIONS {basePath} returning Tus-Version
HEAD {basePath}/{id} returning Upload-Offset
PATCH {basePath}/{id} with application/offset+octet-stream
Tus-Resumable version enforcementX-HTTP-Method-Override support via Ktor's method override pluginThe shared module exposes protocol headers and constants used by both the client and server, including TusHeaders, TUS_VERSION, and TUS_RESUME_VERSION.
The project currently uses:
2.3.20
3.4.2
From the repository root:
./gradlew buildUseful module-level commands:
./gradlew :tuskt-client:build
./gradlew :tuskt-server:build
./gradlew :shared:build
./gradlew :integration-tests:buildRun the full verification suite:
./gradlew check testCommon targeted commands:
./gradlew :tuskt-server:test
./gradlew :tuskt-client:jvmTest
./gradlew :shared:jvmTest
./gradlew :integration-tests:test
./gradlew ktlintCheck detektTusktClient wraps a Ktor HttpClient and configures the required tus headers after initialization:
import dev.phillipslabs.tuskt.client.TusktClient
import io.ktor.client.*
import io.ktor.client.engine.cio.*
suspend fun example() {
val client =
TusktClient.initialize(
client =
HttpClient(CIO) {
expectSuccess = false
},
baseUrl = "http://localhost:8080/files",
)
client.use { tus ->
val offset = tus.getUploadOffset("upload-123")
val result = tus.uploadBytes("hello".encodeToByteArray(), id = "upload-123", offset = 0)
}
}Today, uploadBytes assumes the upload resource already exists on the server.
You can embed the server in a Ktor engine using embeddedTusktServer:
import dev.phillipslabs.tuskt.TusktServerConfiguration
import dev.phillipslabs.tuskt.embeddedTusktServer
import io.ktor.server.netty.*
import kotlin.io.path.Path
fun main() {
embeddedTusktServer(
factory = Netty,
configuration =
TusktServerConfiguration(
host = "127.0.0.1",
port = 8080,
basePath = "/files",
storagePath = Path("files"),
),
).start(wait = true)
}Defaults:
0.0.0.0
8080
/files
<repo-or-process-working-dir>/files
Build the fat jar:
./gradlew :tuskt-server-standalone:shadowJarRun it:
java -jar tuskt-server-standalone/build/libs/tuskt-server-standalone-0.1.0-SNAPSHOT.jarConfiguration is available through either system properties or environment variables:
tuskt.host or TUSKT_HOST
tuskt.port or TUSKT_PORT
tuskt.basePath or TUSKT_BASE_PATH
tuskt.storagePath or TUSKT_STORAGE_PATH
Example:
TUSKT_STORAGE_PATH=/tmp/tuskt-files TUSKT_PORT=9000 \
java -jar tuskt-server-standalone/build/libs/tuskt-server-standalone-0.1.0-SNAPSHOT.jardev.phillipslabs:tuskt-client: Kotlin Multiplatform tus clientdev.phillipslabs:tuskt-server: Ktor server librarydev.phillipslabs:tuskt-shared: shared protocol constants and primitivesSee CONTRIBUTING.md for local setup, coding conventions, and test guidance.
Tuskt is a Kotlin implementation of the tus resumable upload protocol.
The repository currently contains:
:tuskt-client: a Kotlin Multiplatform tus client:tuskt-server: a Ktor-based tus server library:tuskt-server-standalone: a runnable server fat jar:shared: shared tus protocol constants and primitives:integration-tests: end-to-end client/server interoperability testsPublished library modules:
dev.phillipslabs:tuskt-clientdev.phillipslabs:tuskt-serverdev.phillipslabs:tuskt-sharedChoose the module based on how you want to consume the project:
tuskt-client: add a multiplatform tus client to your applicationtuskt-server: embed tus endpoints in a Ktor servertuskt-shared: use protocol constants and shared primitives directlytuskt-server-standalone: build and run the fat jar instead of adding a library dependencyGradle Kotlin DSL examples:
dependencies {
implementation("dev.phillipslabs:tuskt-client:<version>")
implementation("dev.phillipslabs:tuskt-server:<version>")
implementation("dev.phillipslabs:tuskt-shared:<version>")
}For Kotlin Multiplatform, the client dependency typically belongs in commonMain:
kotlin {
sourceSets {
commonMain.dependencies {
implementation("dev.phillipslabs:tuskt-client:<version>")
}
}
}Tuskt is early-stage and currently implements the core resumable upload flow around:
OPTIONS for server capability discoveryHEAD for upload offset lookupPATCH for resuming/appending upload bytesThe current server does not yet implement tus extensions such as creation. You can see that reflected in the ignored server tests and TODOs in the codebase.
The multiplatform client currently supports:
OPTIONS
HEAD
PATCH
Targets currently configured in Gradle:
linuxX64, linuxArm64
mingwX64
The Ktor server library currently supports:
OPTIONS {basePath} returning Tus-Version
HEAD {basePath}/{id} returning Upload-Offset
PATCH {basePath}/{id} with application/offset+octet-stream
Tus-Resumable version enforcementX-HTTP-Method-Override support via Ktor's method override pluginThe shared module exposes protocol headers and constants used by both the client and server, including TusHeaders, TUS_VERSION, and TUS_RESUME_VERSION.
The project currently uses:
2.3.20
3.4.2
From the repository root:
./gradlew buildUseful module-level commands:
./gradlew :tuskt-client:build
./gradlew :tuskt-server:build
./gradlew :shared:build
./gradlew :integration-tests:buildRun the full verification suite:
./gradlew check testCommon targeted commands:
./gradlew :tuskt-server:test
./gradlew :tuskt-client:jvmTest
./gradlew :shared:jvmTest
./gradlew :integration-tests:test
./gradlew ktlintCheck detektTusktClient wraps a Ktor HttpClient and configures the required tus headers after initialization:
import dev.phillipslabs.tuskt.client.TusktClient
import io.ktor.client.*
import io.ktor.client.engine.cio.*
suspend fun example() {
val client =
TusktClient.initialize(
client =
HttpClient(CIO) {
expectSuccess = false
},
baseUrl = "http://localhost:8080/files",
)
client.use { tus ->
val offset = tus.getUploadOffset("upload-123")
val result = tus.uploadBytes("hello".encodeToByteArray(), id = "upload-123", offset = 0)
}
}Today, uploadBytes assumes the upload resource already exists on the server.
You can embed the server in a Ktor engine using embeddedTusktServer:
import dev.phillipslabs.tuskt.TusktServerConfiguration
import dev.phillipslabs.tuskt.embeddedTusktServer
import io.ktor.server.netty.*
import kotlin.io.path.Path
fun main() {
embeddedTusktServer(
factory = Netty,
configuration =
TusktServerConfiguration(
host = "127.0.0.1",
port = 8080,
basePath = "/files",
storagePath = Path("files"),
),
).start(wait = true)
}Defaults:
0.0.0.0
8080
/files
<repo-or-process-working-dir>/files
Build the fat jar:
./gradlew :tuskt-server-standalone:shadowJarRun it:
java -jar tuskt-server-standalone/build/libs/tuskt-server-standalone-0.1.0-SNAPSHOT.jarConfiguration is available through either system properties or environment variables:
tuskt.host or TUSKT_HOST
tuskt.port or TUSKT_PORT
tuskt.basePath or TUSKT_BASE_PATH
tuskt.storagePath or TUSKT_STORAGE_PATH
Example:
TUSKT_STORAGE_PATH=/tmp/tuskt-files TUSKT_PORT=9000 \
java -jar tuskt-server-standalone/build/libs/tuskt-server-standalone-0.1.0-SNAPSHOT.jardev.phillipslabs:tuskt-client: Kotlin Multiplatform tus clientdev.phillipslabs:tuskt-server: Ktor server librarydev.phillipslabs:tuskt-shared: shared protocol constants and primitivesSee CONTRIBUTING.md for local setup, coding conventions, and test guidance.