Zipline Documentation
repository·trunk·Indexed 25 days ago
https://github.com/cashapp/ziplineA library for integrating Kotlin/JS libraries into Kotlin/JVM and Kotlin/Native applications using the QuickJS engine. Zipline enables continuous deployment of business logic, plugins, and content without app store updates through interface bridging, secure code signing with EdDSA/ECDSA, and a dedicated Gradle plugin for compiling JavaScript into .zipline binary files.
What's inside Zipline
- Zipline Cryptography provides cryptography APIs specifically designed for use by guest code within the Zipline environment. It allows guest code to perform cryptographic operations by bridging to the host.
Access Wycheproof test data
trunkThis directory provides a subset of Google's Apache-licensed Wycheproof test data for cryptographic testing. It includes test vectors for ECDSA (secp256r1 with SHA-256) and EdDSA. These vectors can be used to verify the correctness and security of cryptographic implementations.
https://github.com/google/wycheproof/blob/master/testvectors/ecdsa_secp256r1_sha256_test.json https://github.com/google/wycheproof/blob/master/testvectors/eddsa_test.jsonImportant limitations of Zipline Bytecode
trunkWhen using the
zipline-bytecodemodule, be aware of the following constraints:- Undocumented Format: The QuickJS serialized object format is undocumented and may change.
- Build Flag Dependency: The structure of serialized data depends on specific QuickJS build flags, such as
CONFIG_ATOMICandCONFIG_BIGNUM. - Version Coupling: This module only supports the exact version of QuickJS that is built alongside this project.
How interface bridging works in Zipline
trunkZipline allows you to share interfaces between a host platform (Kotlin/JVM or Kotlin/Native) and a guest runtime (Kotlin/JS).
Key Rules for Bridging:
- Interface Requirement: All bridged interfaces must extend
ZiplineService. This provides aclose()method used to release resources and allow garbage collection. - Data Passing: By default, arguments and return values are passed by value using
kotlinx.serializationfor encoding/decoding. - Reference Passing: Interfaces extending
ZiplineServiceare passed by reference, meaning the receiver can call methods on a live instance of the implementation. - Asynchrony: Interface functions can be
suspendfunctions. Zipline uses an internalsetTimeout()implementation to support Kotlin/JS coroutines. - Reactive Streams: Zipline supports
Flow<T>as both parameters and return types.
Important: You must call
close()on a bridged interface once it is no longer needed to prevent memory leaks.interface TriviaService : ZiplineService { fun games(): List<TriviaGame> fun answer(questionId: String, answer: String): AnswerResult }- Interface Requirement: All bridged interfaces must extend
Read and write QuickJS objects with Zipline Bytecode
trunkThezipline-bytecodemodule provides functionality to read and write QuickJS objects using theJS_WriteObject()encoding. This is the same encoding format utilized byQuickJs.compile().Understand the Zipline Loader data sources
trunkThe Zipline Loader is responsible for structured, cached loading of JavaScript modules into a Zipline QuickJS instance. It retrieves code from three distinct sources in order of priority:
- Embedded: Built-in resources packaged directly within the application (e.g., inside a
.jarfile). - Cache: Files stored on the local disk.
- Network: Remote resources fetched via URLs.
When an application successfully loads, its manifest and modules are pinned. Pinned files are protected from deletion during cache pruning and are only unpinned when a newer version of the application is successfully loaded.
- Embedded: Built-in resources packaged directly within the application (e.g., inside a
Understand the Zipline multiplatform structure
trunkZipline is organized into a multiplatform hierarchy that allows code to be shared across different environments. The structure is centered around a
commonmodule, which branches into ajsmodule and ahostmodule. Thehostmodule further branches intojniandnativeimplementations, withjnisupporting bothjvmandandroidtargets. This architecture enables running JavaScript logic within various host environments (like Android or JVM) via a bridge.┌────────┐ │ common │ └───┬────┘ ┌─────────┴───────┐ ┌──┴───┐ ┌─┴──┐ │ host │ │ js │ └──┬───┘ └────┘ ┌───────┴─────────┐ ┌──┴───┐ ┌───┴────┐ │ jni │ │ native │ └──┬──┘ └────────┘ ┌──────┴────────┐ ┌──┴──┐ ┌────┴────┐ │ jvm │ │ android │ └─────┘ └─────────┘Install the zipline-cli
trunkThe
zipline-cliis a command-line tool that allows non-Gradle projects (such as iOS-Swift apps) to access Zipline functionality, specifically for downloading Zipline code.To install it, download the latest zip archive from the official releases page. Once extracted, you can execute the tool using
bin/zipline-cli(macOS/Linux) orbin/zipline-cli.bat(Windows).Set up zipline-cli for local development
trunkIf you are contributing to thezipline-clirepository, you can create a local distribution to test changes without manually unzipping archives. Run theinstallDistGradle task to generate an exploded version of the tool in thebuild/install/directory.Build JNI Libraries Locally using Zig
trunkYou can cross-compile Zipline's JVM native libraries to various platforms and architectures using Zig.
Requirements:
- Zig version: 0.14.0
- Supported Platforms: macOS (tested) and Linux (may work). Windows is not supported.
Steps:
- Install Zig 0.14.0 to your system.
- Navigate to the
ziplinedirectory. - Run the build command targeting the JNI resources directory.
$ cd zipline $ zig build -p src/jvmMain/resources/jni/Serve .zipline files via Webpack for local development
trunkFor local development, the plugin can serve compiled.ziplinefiles on the Webpack server. This allows you to run the Webpack compiler continuously and have the resulting.ziplinefiles served directly to aZiplineLoader.Configure Zipline code signing and verification
trunkZipline uses EdDSA Ed25519 or ECDSA P-256 signatures to authenticate downloaded libraries.
1. Generate Keys
Use the Zipline Gradle plugin to generate an EdDSA key pair:
$ ./gradlew :generateZiplineManifestKeyPairEd255192. Sign Builds (Build Server)
Configure your build to use the private key in your
build.gradle.kts:zipline { signingKeys { create("key1") { privateKeyHex.set(...) algorithmId.set(app.cash.zipline.loader.SignatureAlgorithmId.Ed25519) } } }3. Verify Signatures (Host Application)
In your host application, use the public key to create a
ManifestVerifierand pass it to theZiplineLoader:val manifestVerifier = ManifestVerifier.Builder() .addEd25519("key1", ...) .build() val loader = ZiplineLoader( manifestVerifier = manifestVerifier, ... )