Elide/ Architecture

Architecture · native substrate

Native libraries, linked at build time.

Elide ships native transport, cryptography and codecs in one executable. Every guest language shares that foundation.

Walkthrough

Ship the native stack in one executable

Elide makes native dependencies part of the executable's build. On Linux and Apple Silicon, Netty's transports and TLS bindings link into Native Image as static archives1 2 9. The engine and Rust services share one deployment artifact24 36.

JVM libraries load native dependencies at runtime

Netty's conventional distribution packages platform libraries in classifier jars. Its loader extracts a shared object into a temporary directory and loads the copy4 5. tcnative probes library names for the host, then uses the same loader6. Brotli4j and zstd-jni also load native libraries at runtime38.

Link transport, TLS and codecs into Elide

Native Image links Netty's transport and tcnative archives into the engine1 9. Linux supplies epoll and io_uring; macOS supplies kqueue10. AWS-LC provides TLS cryptography11. Elide's Rust services also supply zstd, brotli and gzip codecs12.

Catch missing native bindings at build time

Elide registers the static JNI libraries and their Java-facing methods13 14. The image then references the corresponding native symbols15 16. The linker checks required definitions while assembling the executable, making native binding part of build validation17.

LTO optimizes across the stack

Elide configures ThinLTO for Native Image's native compiler and Cargo's final link3 23 24. The linker can inline and optimize across compilation units that supply compatible LLVM bitcode30.

Run transport and TLS on native libraries

The illustrated server design composes native transport and TLS with host-side HTTP framing, then hands the request to guest code 28. Elide links the transport and AWS-LC components into the engine 10 11. A retained fetch handler supplies the application callback 27.

Write application handlers in guest languages

The realm's event loop receives the request and invokes the retained handler29. Application code handles a Request and returns a Response27 29. Bun provides a precedent for this division: a native HTTP server with a JavaScript fetch handler31.

Embed the engine behind the CLI

Elide exposes a native entrypoint driven by a versioned Cap’n Proto record32 33. CLI and embedded hosts use the same interface36. A host can manage multiple Native Image isolates. Serial keeps memory overhead low for CLI tools and desktop apps; G1 targets sustained server throughput39.

kernel · sockets
Host OS
epoll · kqueue · io_uring
tcnative · AWS-LC
zstd · brotli · gzip
mimalloc
Rust entry
Rust + C · native
compatible ThinLTO inputs
Netty
JNI stubs
event loop
Engine · Native Image
JS/TS
Python
JVM
WASM
Languages
fetch handler
Application
  1. probetcnative tries classifier names from the host
  2. locateresource inside the classifier jar
  3. copyshared object into a temporary directory
  4. loadSystem.load on the copy
  5. maplibrary image mapped at run time
  6. callinvoke native methods
  1. fork*-static jar from Elide's Netty and tcnative forks
  2. extract.a unpacked by the build step
  3. link inputhanded to Native Image
  4. image linkregistered JNI symbols resolved
  5. archiveengine archive, linked by cargo
Netty forks epoll · Linux io_uring · Linux kqueue · macOS tcnative + AWS-LC
Rust crate archive zstd · brotli · gzip
image buildJNI registration archive + JNI classesnative method Java_<class>_<method>archive object resolved by the linkersymbol check required definitionsrun timenative call linked JNI calllinked code in the same binary
appfetch handler · cachedread once at bindenginerealm event looprealm task · builds Requestguest handoffhost servicesacceptepoll/kqueue/io_uringTLS decrypttcnative/AWS-LCHTTP framingheaders · bodyhostsocketreq

CLI

Quick startup · Serial GC

Cap’n Proto invocation
Elide engineNative ImageNative services

Embedded

Long-lived host · G1 GC

Cap’n Proto invocation
Elide engineIsolate 1 · Isolate 2 · …Native services
Host configurations. The collector is selected at build time; the host manages isolate lifetimes.

In practice

Static JNI and substitutions integrate native libraries into Native Image's build, while preserving their initialization and call contracts13 19 21. Transport, cryptography and codecs become shared runtime services10 11 12. This gives Elide a foundation for keeping protocol work below its Truffle application handlers28 29.

Example: decompressing a Maven download

A Maven download is one concrete example. When native compression is available, Elide's Netty decompressor sends zstd-compressed Maven responses to the Rust codec 12. This path reuses Elide's native compression service without adding zstd-jni or brotli4j to the Maven transport 12.

Thomas Wuerthinger

“You can have more startup time or more peak performance depending on what you care more about.”

Thomas Wuerthinger
Maximizing Performance with GraalVM · 2019

Codec throughput

Compare pure Java and native zstd decoding through FFM, using identical Maven payloads and preallocated buffers. The sliders select measured warm-up stages37.

Maven POM · zstd decoding

7,411 decoded bytes per bounce · median of seven forks.

Observed range: JVM ; native .

Maven source JAR · zstd decoding

580,485 decoded bytes per bounce · median of seven forks.

Observed range: JVM ; native .

Cold means the first codec call after construction. JVM startup, native library loading, file reads and output allocation are excluded. Aircompressor 3.7 · Apple M5 Max · GraalVM 25.0.4.1 · seven fresh JVM forks per implementation and payload. This measures codec throughput37.

Maven POM
Median MB/s · zero-based scale · seven forks
01,0002,000 0 10 100 1,000 10,000 Prior calls (measured stages)JVM: 1.2 MB/s after 0 callsJVM: 37.5 MB/s after 10 callsJVM: 50.0 MB/s after 100 callsJVM: 328.8 MB/s after 1,000 callsJVM: 1,617.1 MB/s after 10,000 callsNative: 9.8 MB/s after 0 callsNative: 1,004.9 MB/s after 10 callsNative: 1,118.6 MB/s after 100 callsNative: 1,646.9 MB/s after 1,000 callsNative: 1,646.9 MB/s after 10,000 callsJVMNative
Maven source JAR
Median MB/s · zero-based scale · seven forks
05,00010,000 0 10 100 1,000 10,000 Prior calls (measured stages)JVM: 60.5 MB/s after 0 callsJVM: 1,218.8 MB/s after 10 callsJVM: 2,750.6 MB/s after 100 callsJVM: 7,275.0 MB/s after 1,000 callsJVM: 7,248.5 MB/s after 10,000 callsNative: 696.3 MB/s after 0 callsNative: 8,034.4 MB/s after 10 callsNative: 8,180.6 MB/s after 100 callsNative: 8,428.1 MB/s after 1,000 callsNative: 8,505.3 MB/s after 10,000 callsJVMNative
See the data
Payload / prior callsJVM MB/sNative MB/s
Maven POM / 01.29.8
Maven POM / 1037.51,004.9
Maven POM / 10050.01,118.6
Maven POM / 1,000328.81,646.9
Maven POM / 10,0001,617.11,646.9
Maven source JAR / 060.5696.3
Maven source JAR / 101,218.88,034.4
Maven source JAR / 1002,750.68,180.6
Maven source JAR / 1,0007,275.08,428.1
Maven source JAR / 10,0007,248.58,505.3

Sources

  1. tools/jvm/netty-statics.mts:6-13,138-146 — build step extracts .a archives from the *-static.jar classifier jars into .dev/artifacts/unpacked-natives/ and returns -H:NativeLinkerOption=<abs-path> flags for the native-image command line.
  2. packages/base/main/dev/elide/features/AbstractStaticNativeLibraryFeature.kt:195-214 — preregisterUninitializedBuiltinLibrary, addBuiltinNativePrefix and addStaticJniLibrary register each archive as a builtin static-JNI library at beforeAnalysis.
  3. tools/settings.mts:60-63; tools/builder/cargo.mts:75-82 — enable native compiler ThinLTO and linker-plugin LTO at the Cargo link. These flags configure optimization; compatible bitcode inputs determine its scope. See30.
  4. Netty 4.2.17.Final io/netty/util/internal/NativeLibraryLoader.java:54,79-83,172-199,223 (sources jar in .dev/dependencies/m2/io/netty/netty-common/4.2.17.Final/; version pinned at tools/deps.yaml:29) — resource under META-INF/native/, createTempFile in io.netty.native.workdir (default java.io.tmpdir), copy, then System.load on the copy.
  5. https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/System.html#load(java.lang.String) § load — maps a named native library; also specifies initialization of statically linked JNI libraries. https://openjdk.org/jeps/178 § Description — the JVM supports statically linked JNI. Jar extraction is a library distribution convention.
  6. Netty 4.2.17.Final io/netty/handler/ssl/OpenSsl.java:746-771 (sources jar in .dev/dependencies/m2/io/netty/netty-handler/4.2.17.Final/) — loadTcNative builds netty_tcnative_<os>_<arch>[_<classifier>], _fedora, _<arch> and bare names and calls loadFirstAvailable; packages/base/main/dev/elide/features/OpenSslTcNativeSubstitution.java:19-25 describes the same probe.
  7. https://netty.io/wiki/native-transports.html § Native transports — platform-specific JNI transports expose operating-system features through Netty channel APIs. NIO is the portable API alternative; it also uses operating-system facilities.
  8. https://www.graalvm.org/latest/reference-manual/native-image/optimizations-and-performance/MemoryManagement/ § Memory Management — Native Image manages Java object allocations with garbage collection. Rust allocator selection is a separate allocation domain.
  9. tools/jvm/netty-statics.mts:52-64,78-125 — static archive classifiers for Linux x86-64, Linux AArch64 and macOS AArch64; this is the static integration scope described here. The target list selects Netty transports and tcnative for the image link.
  10. packages/base/main/dev/elide/features/NettyNativeTransportsFeature.kt:23,178-217 — Linux: epoll and io_uring; macOS: kqueue; Windows: no-op. The io_uring library name carries Netty 4.2's 42 suffix (tools/jvm/netty-statics.mts:100-105).
  11. tools/jvm/netty-statics.mts:116-123 — the Elide tcnative archive retains its upstream artifact name. crates/runtime/Cargo.toml:78-87; tools/jvm/aws-lc-static.mts:6-8,39-79 — stage and link AWS-LC libssl/libcrypto archives for the native engine. The TLS provider described here is AWS-LC.
  12. crates/nodeapi/src/compression.rs:1341-1437; packages/base/main/dev/elide/tooling/jvm/maven/transport/ElideNativeContentDecompressor.java:16-94 — Rust compression services expose zstd, brotli and gzip decoding, including use from the managed dependency transport.
  13. packages/base/main/dev/elide/features/NettyNativeTransportsFeature.kt:82-105,107-141,153-159 — each mounted archive is registered with registerJni = true; RuntimeJNIAccess covers classes, constructors, methods and fields.
  14. packages/base/main/dev/elide/features/NativeCryptoFeature.kt:73-118 — tcnative registered under its classifier link name with its JNI surface class list; NettyNativeTlsFeature.kt:18-28 — C-to-Java lookups (FindClass, GetMethodID) need separate RuntimeJNIAccess registration.
  15. packages/base/main/dev/elide/features/NettyNativeTransportsFeature.kt:89-94 — registerJni populates SVM's static-JNI prefix dispatch table, generating build-time references to every Java_<class>_<method> symbol of the layout classes.
  16. https://github.com/oracle/graal/blob/master/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/NativeLibraries.java addStaticJniLibrary (lines 470-482 at time of reading) and https://github.com/oracle/graal/blob/master/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/CCLinkerInvocation.java Options.NativeLinkerOption (line 82) appended to the linker command (lines 265-270).
  17. packages/base/main/dev/elide/features/NettyNativeTransportsFeature.kt:89-94; tools/jvm/netty-statics.mts:67-72 — registered JNI methods produce native symbol references checked at the image link. This establishes build-time validation of required definitions.
  18. https://github.com/oracle/graal/blob/master/sdk/src/com.oracle.svm.core.annotate/src/com/oracle/svm/core/annotate/TargetClass.java (javadoc, lines 55-75) and https://github.com/oracle/graal/blob/master/sdk/src/com.oracle.svm.core.annotate/src/com/oracle/svm/core/annotate/Substitute.java (javadoc) — @TargetClass names the original class by value, className or classNameProvider, optionally gated by onlyWith; @Substitute replaces a method of that class.
  19. packages/base/main/dev/elide/features/EpollNativeSubstitution.java:25-33, IoUringNativeSubstitution.java:19-27, KQueueNativeSubstitution.java:19-27 — Netty's Native.loadNativeLibrary() becomes a call into NativeTransportBootstrap, gated by onlyWith on elide.netty.staticNatives=true.
  20. packages/base/main/dev/elide/features/OpenSslTcNativeSubstitution.java:30-38 — OpenSsl.loadTcNative() becomes NativeCryptoBootstrap.bootstrap(), gated on elide.netty.staticNativesTls=true; packages/base/main/dev/elide/netty/NativeCryptoBootstrap.java:58-76.
  21. crates/runtime/src/lib.rs:373-398 (tcnative) and :416-464 (epoll, io_uring) and :478-499 (kqueue) — #[jni] shims declare JNI_OnLoad_<lib> with extern_weak linkage, take the JavaVM from the JNIEnv, and call the entry directly.
  22. packages/base/main/dev/elide/netty/NativeTransportBootstrap.java:65-80; crates/runtime/src/lib.rs:373-398 — bootstrap invokes JNI initialization and checks its version result; weak initialization references permit the shared bootstrap code to serve static and dynamic configurations.
  23. tools/jvm/native-image.mts:192,220,251-253 — passes --native-compiler-options=-flto=thin to clang on the non-Windows native build path. This enables ThinLTO for compiler inputs emitted as compatible bitcode.
  24. tools/builder/cargo.mts:75-82 — linker-plugin LTO and -flto=thin for Cargo; the final native executable links the engine archive. project/features/10-elide-engine.md:3-14 specifies the Engine as a Native Image component callable by a host.
  25. packages/entry/bin/main.rs:157-174 — #[global_allocator] is MiMallocExtern on linux-musl with the mimalloc-static feature, otherwise mimalloc::MiMalloc; tools/builder/cargo.mts:688-701 enables mimalloc-static only for musl targets.
  26. crates/core/src/allocator.rs:6-10,28-36; packages/entry/bin/main.rs:157-174 — musl configuration selects MiMallocExtern, which forwards Rust allocations to the mimalloc symbols supplied at link time. Other configured targets use the mimalloc crate. This policy concerns Rust allocations.
  27. packages/javascript/main/dev/elide/lang/javascript/whatwg/fetch/ServeOps.java:104-106,133-137,183-197 — options.fetch is read once, stored in ServerState, and bound through HttpServerTransport.shared().
  28. Netty ChannelPipeline, SslHandler, and HttpServerCodec — transport events pass through TLS and protocol handlers before application handling. This supports the illustrated composition of linked native transport/TLS, host-side HTTP framing, and a guest callback.
  29. ServeOps.java:203-218,296-309; packages/base/main/dev/elide/runtime/truffle/EventLoop.kt:185-196 — submitFromExternal posts one task to the realm's queue; invokeGuestHandler builds the guest Request and calls the stored handler.
  30. https://clang.llvm.org/docs/ThinLTO.html § Introduction and Usage — ThinLTO operates on LLVM bitcode with module summaries, permitting cross-module importing and optimization. Optimization spans the inputs that carry compatible bitcode.
  31. https://bun.sh/reference/bun/serve § Bun.serve — HTTP server API powered by a fork of uWebSockets. https://bun.sh/docs/runtime/http/server § Basic Setup and Configuration — JavaScript fetch callbacks accept Request objects and return responses. This is precedent for native protocol processing with a guest handler.
  32. project/features/10-elide-engine.md:3,25,40-47 and project/features/11-elide-cli.md:3,21,91-92 — the Engine is "a single dispatchable surface, driven by native calls"; the CLI dispatches via a Cap'n Proto message handed to elide_engine_v2_entry. Schema protocol/elide/v1/invocation.capnp:31-45 (struct EngineInvocation: binpath, args, env, meta), generated for Java as packages/generated/main/dev/elide/proto/v1/InvocationProtocol.java and for Rust by crates/rpc/src/lib.rs:23.
  33. packages/entry/src/invocation.rs:16-22,32-63 — serializes the invocation into an arena and supplies version, format, tenant metadata, pointer and length. packages/cli/main/dev/elide/Entry.kt:549-559 — matching native entrypoint signature. Tenant metadata belongs to the dispatch interface.
  34. packages/cli/main/dev/elide/Entry.kt:458-471; packages/base/main/dev/elide/runtime/rpc/CapnProto.kt:24-32,41-55 — protocol checks precede a direct ByteBuffer reader over the invocation address and length; generated readers access the serialized record. Field conversion may allocate managed values.
  35. https://capnproto.org/encoding.html § Objects and Structs — the data format is suitable for direct traversal in memory. In-place reading avoids reconstructing the whole record as an object graph; readers still interpret and validate the representation.
  36. packages/cli/main/dev/elide/Entry.kt:549-582 — the native entrypoint accepts version, format, tenant metadata, length and an invocation pointer. project/features/10-elide-engine.md § Use Cases and Requirements specifies long-lived engine hosts and setup/run phases. The entrypoint serves both CLI dispatch and embedded hosts.
  37. Local benchmark: tools/benchmarks/codec-warmup/ contains archived raw observations and measurement methodology. Aircompressor 3.7 supplies pure Java and native zstd implementations. Payloads: Maven Core 3.9.9 POM and source JAR, encoded with zstd 1.5.7 at level 3; both decoders read identical compressed bytes. Medians and observed ranges cover seven fresh JVM forks per implementation and payload; a warmed point is one timed call after the stated number of prior calls.
  38. Brotli4j documents automatic native-library loading. zstd-jni Native.java extracts a platform library and loads it, with system-library fallbacks.
  39. Native Image embedding documents isolate creation and teardown. Memory management documents build-time Serial/G1 selection and supported platforms. tools/settings.mts selects the build collector. The embedded host manages multiple isolates through the native interface.