B^

elide.sh (2025)

Elide as a single native binary that unifies runtime, compiler, toolchain, and cross-language interoperability — and what that consolidation means for developers, organizations, and their customers.

Abstract

The contemporary software stack is defined less by any single tool than by the seams between many. A typical polyglot project depends on a language runtime, a separate compiler or transpiler, one or more package managers, a formatter, a build orchestrator, and the configuration files that mediate among them. Each component is individually reasonable, and the aggregate is a system of considerable operational cost. This article examines Elide, a runtime built on GraalVM that consolidates these functions into one native binary. It argues that Elide's principal contribution is not a novel language or a faster interpreter in isolation, but the removal of the seams: the runtime, the toolchain, the compilers, and the interoperability layer are delivered as a single artifact that shares one process and one heap. The article characterizes the architecture that makes this possible, situates it against the accretion of tooling that defines modern development, and evaluates the consequences from three vantage points: the individual developer, the engineering organization, and the end customer.

I. Introduction: The Accretion Problem

Software toolchains grow by accretion. A new capability arrives as a new tool, the tool arrives with its own configuration format, and the configuration accumulates faster than any single engineer can reason about it. A JavaScript project that began with a runtime now carries a transpiler, a bundler, a linter, a formatter, a test runner, and a package manager, each with a distinct version, a distinct configuration surface, and distinct failure modes. A JVM project layers a build system over a compiler over a dependency resolver, and the build system frequently becomes the most complex program in the repository. When a project spans several languages, which most non-trivial projects eventually do, the accretion compounds across ecosystems that do not share conventions, package formats, or process boundaries.

The cost of this arrangement is usually described in terms of configuration, and configuration is real, but the deeper cost is architectural. Because each language ecosystem runs in its own process, communication between languages requires a boundary, and a boundary requires serialization, a protocol, and an operational surface to keep it alive. The canonical example is the microservice whose only purpose is to expose a library written in one language to an application written in another. Such a service exists not because the problem demands a network hop, but because the runtimes cannot share a heap. The accretion problem, then, is not merely that there are many tools. It is that the tools are separated by process boundaries, and those boundaries harden into architecture.

Elide addresses the problem at its root. Rather than orchestrate a set of separate tools more cleverly, it delivers the runtime, the compilers, the formatters, the dependency resolver, and the interoperability layer as one binary, executing in one process. The remainder of this article describes how that consolidation is achieved and what follows from it.

II. What Elide Is

Elide is a polyglot runtime and toolchain, distributed as a single native executable and built on GraalVM. In the terminology GraalVM inherits, Elide distinguishes between host code, meaning Elide's own implementation, and guest code, meaning the programs a developer runs on it. It executes JavaScript and Python as guest languages on Truffle, by way of GraalJs and GraalPy respectively, and it both runs and compiles Java and Kotlin. TypeScript is handled by type erasure: types are stripped and the source transpiled at native speed using OXC, the Rust-based Oxidation Compiler, with no tsconfig.json and no separate build step. A file handed to elide is dispatched by extension, so a .ts, .js, .py, or JVM source runs through one entrypoint.

Three properties are inherited directly from the GraalVM foundation. The runtime is memory-safe, resting on JVM semantics and extending into native code through Rust, whose guarantees hold where the JVM's do not apply. It is high-performance, because GraalVM's just-in-time compilation extends to guest applications rather than stopping at the host. And it exhibits strong memory behavior, drawing on the mature garbage collectors of the GraalVM ecosystem. These are not aspirations layered on top of Elide; they are characteristics of the platform it is built upon.

The scope of language and tool support is best stated concretely. The following table summarizes what the single binary provides today.

CapabilityProvided by ElideNotes
JavaScript executionGraalJs on TruffleNode.js compatibility sufficient for most server programs
TypeScript executionType erasure via OXCNo tsconfig.json, no build step
Python executionGraalPy on TruffleTargets Python 3.12; bundled standard library; currently alpha
Java compile & runEmbedded toolchain and JVMSource up to JDK 25; runs on HotSpot or as guest
Kotlin compile & runEmbedded toolchainUp to Kotlin 2.3.x
FormattingGoogle Java Format, ktfmtUnified under elide fmt
DependenciesIn-process resolversnpm, PyPI, and Maven from one manifest
PackagingJAR assembly, OCI imagesContainer images via Google Jib
ServingBuilt-in HTTP and dev serverelide serve, elide dev
Agent integrationMCP serverelide mcp
One binary in place of node, python, a JDK, tsc, mvn, and a bundler.

The claim implicit in the table is that a developer can compile Java, transpile TypeScript, resolve a Maven artifact, format a Kotlin file, and serve the result without installing node, python, a JDK, tsc, mvn, or a bundler. That claim is the thesis of the product, and its plausibility rests on the architecture described next.

III. The Architecture of Consolidation

A single binary that behaves like a runtime, a compiler, and a package manager at once is not the natural output of a conventional build. Elide's construction is unusual and worth describing precisely, because the architecture is what makes the consolidation more than a bundling exercise.

The shipped executable is a Rust program that wraps a GraalVM Native Image. The Rust layer is the command-line front end: it parses arguments, and then packs them into a Cap'n Proto message that it dispatches across a foreign-function boundary into the engine. The engine, produced by GraalVM Native Image, is the component that actually runs guest code and drives the toolchain. It presents a single dispatchable surface, entered through a small family of native methods, and it is designed to be driven either as a binary, when a developer types elide, or as an embedded library, when a host application links it directly.

The build that produces this artifact is a three-phase sequence in which Native Image is sandwiched between two Rust compilations. In the first phase, the Rust workspace is compiled into a set of native libraries. In the second, Native Image consumes those libraries as link-time inputs and emits both a standalone executable and a static archive of its object files. In the third, the Rust entrypoint binary is compiled and statically linked against that archive, and it is this final binary that ships. The arrangement is unusual, but it is the mechanism by which a garbage-collected, JIT-capable engine and a native command-line front end become one statically linked program rather than two processes communicating over a socket.

The diagram below sketches the runtime layering, from the command a developer types down to the languages and tools that execute.

One statically linked binary: from the typed command down to the languages and tools that run inside a single heap.

Two aspects of this design carry disproportionate weight. First, because the toolchain is compiled ahead of time into the native image, invoking a compiler does not boot a JVM. The elide kotlinc and elide javac commands begin executing immediately rather than paying the startup and warmup cost that stock javac and kotlinc incur on every invocation. Second, because every guest language executes on the same Truffle framework within the same engine, values do not need to be serialized to cross a language boundary. The shared object model is the technical fact underneath the interoperability described in the next section, and it is what distinguishes Elide's polyglot claim from the mere co-installation of several runtimes.

IV. One Runtime, Many Languages

The phrase “polyglot runtime” is often used loosely to mean that several languages happen to be available. Elide's usage is stricter. Because JavaScript, TypeScript, and Python execute on Truffle within one engine, a value produced in one language is available to another as a native value of that language, on the same heap, without a serialization step or an intervening protocol. A Python function exported into the shared bindings arrives in JavaScript as an ordinary callable. A JavaScript expression evaluated from Python returns a native Python value.

The interoperability is expressed through small, language-idiomatic surfaces rather than a foreign-function ceremony. From Python, a built-in elide module provides the entry points: js(source) evaluates a JavaScript string and returns its result as a Python value, while decorators publish Python callables under names that other languages can resolve.

interop.py
# Python -> JS and back, on one shared heap.
from elide import bind, js, poly

# Python -> JS: evaluate JavaScript and use the result back in Python.
total = js("[1, 2, 3, 4].reduce((a, b) => a + b, 0)")
print(f"js sum = {total}")

# Python -> host: export functions for JavaScript or the host to call.
@bind
def add(a, b):
    return a + b

@poly(name="multiply")
def mul(a, b):
    return a * b

The architectural consequence is significant. A common pattern in production systems is the service whose sole responsibility is to expose a library written in one language to an application written in another, most often a Python library reached from a Node application over HTTP or a message queue. Under a shared-heap model, that service has no reason to exist. The library call becomes a function call. What was an operational surface, a deployment, a network hop, and a serialization format, collapses into an ordinary invocation within one process.

The bridge service exists only because two runtimes cannot share memory. On one heap, it collapses into a call.

The elimination of such intermediary services is not a performance optimization at the margin; it is the removal of an architectural component that existed only because two runtimes could not share memory.

The same principle extends to capabilities normally delivered as external dependencies. SQLite, for example, is a built-in module rather than a package to install and compile. A program imports elide:sqlite and proceeds, with no npm install, no native build, and no lockfile entry, because the driver ships inside the runtime. The pattern generalizes: functionality that would ordinarily arrive through the package manager and its attendant build steps is instead present in the binary, which shortens both the dependency graph and the surface on which builds fail.

V. Modernizing the JVM Toolchain

The JVM ecosystem is the clearest illustration of the accretion problem, and therefore the clearest illustration of what consolidation buys. A conventional JVM build layers a build system over a compiler over a dependency resolver, and the build system, whether Gradle, Maven, or Bazel, frequently becomes the most complex and least understood program in the repository. Elide's approach is to make the runtime itself a complete JDK toolchain, so that it can stand in for the pieces individually or serve as the foundation an existing build sits upon.

Because Elide ships a complete JDK toolchain, it can act as a JAVA_HOME for any existing build tool. This is deliberately unglamorous and deliberately important: a team need not migrate away from Gradle or Maven to benefit. Pointing an existing build at Elide's toolchain substitutes native-compiled, fast-starting compilers into a pipeline that otherwise remains unchanged. The startup cost that stock javac and kotlinc pay on every invocation, which compounds into minutes across a build that invokes the compiler dozens of times, is removed without a rewrite of the build definition.

The more forward-looking contribution is a Kotlin ABI fast path, internally named Karbine, that addresses the structure of the build graph rather than the speed of a single compile. A dependent module does not require the full compiled output of the module it depends on; it requires only that module's public application binary interface. Karbine emits a Kotlin header jar carrying exactly that surface, the public and protected signatures, the metadata, the ABI-relevant inline bodies, the constant values, and the default-argument markers, and nothing a dependent does not need. The emitted jar is digest-stable, meaning that an unchanged public ABI produces a byte-identical jar regardless of changes to method bodies or incidental compiler state.

A dependent begins against the header jar as soon as the producer's frontend resolves — the critical path shortens from full-compile depth to frontend depth.

Two consequences follow, and they are the reason the technique matters at scale. First, a dependent target can begin compiling against the header jar as soon as the producer's frontend resolves, rather than waiting for the producer's full code generation. The critical path of the build shortens from full-compile depth to frontend depth. Second, an edit confined to a method body yields a byte-identical header jar, so every dependent of that module is a cache hit with no compiler work at all. The honest scope of the saving is stated in the design itself: the technique trims lowering and code generation of non-inline bodies while leaving frontend and intermediate-representation costs in place, so the improvement is real rather than order-of-magnitude, and it is best measured on large module graphs where critical-path depth and dependent fan-out dominate. The distinction between a within-target speedup and a reduction in critical-path depth is precisely the distinction that matters for large organizations, and it is addressed directly rather than conflated.

VI. Three Vantage Points

The value of consolidation is best understood by examining who benefits from it and how. Three vantage points are useful: the individual developer, the engineering organization, and the end customer whose experience is shaped by decisions made far upstream.

The Individual Developer

For the individual developer, the immediate benefit is the collapse of setup and the disappearance of configuration files that exist only to mediate between tools. A TypeScript file with genuine type annotations runs directly, with no package.json, no node_modules, and no transpiler configuration. A Python script runs against a bundled standard library with nothing to provision. A Java or Kotlin file compiles through a native compiler that begins work immediately. The reduction in ceremony is not merely aesthetic. Each configuration file is a surface on which a project can break, a version that can drift, and a piece of knowledge a new contributor must acquire before becoming productive. Removing the file removes all three.

A second benefit is the disappearance of the interpreter-management problem. A single binary executes several languages, so the daily work of selecting and switching between language runtimes, and reconciling their versions, is subsumed into one tool. The formatter is illustrative: elide fmt is a unified formatter for both Java and Kotlin, classifying each source by extension and routing it to Google Java Format or ktfmt as appropriate, so that a mixed project is formatted, or checked in continuous integration, by one command rather than two independently configured ones.

The Engineering Organization

For the organization, the relevant unit is not the individual invocation but the aggregate, across many engineers, many builds, and a continuous-integration system that runs constantly. Here the benefits of consolidation compound. Native-compiled compilers remove startup cost from every build, and the saving accumulates across the organization's entire build volume. A single dependency manifest that resolves npm, PyPI, and Maven artifacts together, through in-process resolvers invoked by one elide install, removes the need to install and maintain three separate package managers in every build environment, while still writing conventional layouts so that existing tools continue to function. The Karbine header-jar path, wired into a build graph, shortens the critical path of large Kotlin builds and prunes dependent rebuilds that would otherwise occur.

A less obvious organizational benefit is architectural simplification. Because languages share a heap, the intermediary services that exist only to bridge language boundaries can be removed from the system's topology. Every such removal eliminates a deployment, a monitoring surface, an on-call responsibility, and a class of failures. The reduction in operational surface is a direct consequence of the runtime's design rather than a discipline the organization must impose on itself.

Finally, the runtime speaks the Model Context Protocol natively, through elide mcp, so that automated agents can build, run, and introspect a project through first-class tooling rather than by shelling out to a heterogeneous collection of external programs. As automated agents become a larger part of how organizations produce and maintain software, a runtime that presents a single, coherent, machine-drivable surface is materially easier to integrate than a stack assembled from many independently configured tools.

The End Customer

The end customer never invokes elide and never reads a manifest, yet the customer's experience is shaped by the same properties. The most direct is startup latency. A runtime that begins executing in milliseconds rather than after JVM warmup changes what is feasible at the edges of a system: short-lived processes, functions invoked per request, and command-line tools that must feel instantaneous become practical on a JVM-family foundation that would otherwise be disqualified by cold-start cost. The customer experiences this as responsiveness, without any awareness of its cause.

The second is reliability. Elide is memory-safe by construction, resting on JVM semantics and Rust's guarantees where it extends into native code, and it inherits mature garbage collection and just-in-time compilation from GraalVM. A system built on a memory-safe, well-collected foundation exhibits fewer of the failure modes that manifest to customers as crashes, corruption, or the security incidents that follow from memory-unsafe code. The third is the compounding effect of the organizational benefits above: an engineering organization that spends less effort maintaining its toolchain, and that operates a simpler system topology, has more capacity to direct at the product the customer actually uses. The benefit reaches the customer indirectly, but it reaches them.

VII. On the Economics of Consolidation

It is worth stating plainly why consolidation, rather than better orchestration, is the more consequential approach. Orchestration accepts the boundaries between tools and invests in coordinating across them, and the investment is never finished, because each new tool adds a new boundary to coordinate. Consolidation removes the boundaries. The distinction is the difference between reducing the friction of a seam and eliminating the seam.

Every recurring tool, and the boundary around it, folds into one native binary.

The following comparison makes the contrast concrete for a representative polyglot project, contrasting the conventional arrangement with the consolidated one.

ConcernConventional stackElide
JS / TS executionNode or Bun, plus tsc and configOne binary, type erasure via OXC
Python executionA provisioned CPython installBundled, targeting 3.12
Java and KotlinA JDK, plus kotlinc, plus JVM warmup per callNative compilers, immediate start
Cross-language callsA service, a protocol, serializationA function call on a shared heap
Dependenciesnpm, pip, and mvn separatelyOne manifest, one elide install
FormattingA Java formatter and a Kotlin formatterelide fmt, unified
ServingAn added web framework and serverelide serve and elide dev, built in
DistributionSeveral runtimes and tools to installOne native binary
Each row on the left is a recurring cost; the right replaces it with one artifact.

The economic argument is that each row of the left column carries a recurring cost, in installation, in configuration, in version drift, and in the surfaces on which builds and deployments fail, and that the right column replaces those recurring costs with a single artifact. The saving is not a one-time convenience at project setup. It is the removal of an ongoing maintenance burden that the conventional stack imposes for as long as the project lives.

VIII. Limitations and Honesty

An account of this kind is only useful if it is candid about maturity. Python support is currently alpha and may change between releases; native module isolation is unavailable on macOS, some C extension modules may not work owing to a GraalPy limitation, and importing Python modules from JavaScript or TypeScript is not yet available in that direction. The Node.js compatibility surface is described as sufficient for most server programs rather than complete, and an application that depends on a specific core module should be verified against the current build before it is ported. The Karbine fast path, as noted in its own design, saves lowering and code generation rather than the entire compile, and it does not yet compose with plugin-provided intermediate-representation generation such as kotlinx.serialization's, so modules relying on such plugins fall back to a full compile. Additional web tooling, including npm dependency management, JavaScript bundling, CSS compilation, and Markdown rendering, is being ported and will arrive in subsequent releases rather than being present today.

These limitations do not undermine the thesis; they locate it in time. The architecture that makes consolidation possible is in place, and the surface it exposes is widening release by release. The honest position is that Elide already delivers the consolidation it claims for a substantial and growing set of workflows, and that the boundary of what it covers is moving outward rather than fixed.

IX. Conclusion

The defining difficulty of the modern development stack is not the absence of good tools but the abundance of them, separated by process boundaries that harden into architecture and by configuration that accumulates faster than it can be understood. Elide's response is to treat the seams themselves as the problem. By delivering the runtime, the compilers, the formatters, the dependency resolver, and the interoperability layer as one native binary executing in one process on one heap, it removes the boundaries rather than coordinating across them. For the individual developer, this appears as the disappearance of setup and configuration. For the organization, it appears as compounding savings in build time and a simpler system topology. For the customer, it appears as responsiveness and reliability whose causes lie far upstream.

There is a reasonable case that the appropriate description of such a system is not “a runtime” among others but “the runtime,” a single foundation on which a project's languages, tools, and dependencies converge. Whether or not the phrase endures, the underlying claim is defensible on the evidence of the architecture: a system designed so that the boundaries between tools need not exist at all is a different kind of thing from a system that manages those boundaries well. Elide is an argument, expressed in a single binary, that the boundaries were never necessary in the first place.

Notes. Architectural and capability claims in this article are drawn from Elide's feature specifications and engineering documentation, including the runtime, toolchain, engine, and CLI feature definitions, the Kotlin ABI fast path (“Karbine”) design, and the startup and build-flow documentation. Performance characterizations of native-compiled compilers and cross-language execution reflect previously measured and reproducible benchmarks reported in the hands-on tour of Elide; this article does not introduce new measurements.