Why justrust should compile remotely, what remote builds can and cannot speed up, and how the product and licensing should be structured. The machine itself is covered in remote.md. The phased plan is in toolchain.md.
Measured on justrust itself (47 dependencies, 16-core laptop) with
JUSTRUST_PASSES=always JUSTRUST_DEPCACHE=0. Codegen means rustc generating
LLVM IR plus LLVM's own work.
| Scenario | Codegen share | LLVM alone | Link |
|---|---|---|---|
| Clean debug, local crate | 60% (3.7 of 6.2s) | ~15% (IR generation was 2.8s) | 8% |
| Clean debug, all crates | 29% (10.8 of 36.9s) | ~9% | 3% |
| Clean release, local crate | 80% (9.6 of 12.0s) | ~70% (passes 3.4s, ThinLTO 5.0s) | 2% |
| Clean release, all crates | 50% (47 of 94s) | ~45% | 1% |
Debug rebuild after touch |
17% | small | 51% |
Conclusions:
- The LLVM optimizer only dominates release builds.
- The debug edit loop is mostly the frontend (single-threaded, re-checks more than the edit touched), rustc's IR generation, and linking.
- Clean builds are dominated by dependencies: 17.6s of 36.9s of rustc time.
- Today justrust's codegen bucket merges IR generation with LLVM. Splitting them is a follow-up.
Round trip: about 60 ms in our own measurement, and 29 to 42 ms to us-west-2 per remote.md. Planning uses 60 ms as the worst case. With a persistent connection an edit costs 2 to 3 round trips (120 to 180 ms). Latency is not the limit.
| Workload | Remote gain | Why |
|---|---|---|
| Single-crate edit loop | Small, 1.2 to 2x | Mostly a single-threaded frontend. More cores barely help, but a faster, unthrottled CPU does |
check, clippy, remote test |
Good when local runs take more than ~0.2s | Only diagnostics and results come back, a few KB |
build that runs locally (Desktop hot reload) |
Limited by bandwidth | The artifact must come back. Use binary diffs and split debug info |
| Clean builds, new checkouts, agent slots, CI | Large, near instant | Dependencies come from the shared cache |
| Many agents building at once | Large | Builds scale on the server instead of fighting over 16 laptop cores |
| Release builds and tests | Medium to large | LLVM, ThinLTO, and tests parallelize well |
The pitch is not "compile my edit faster." It is: never compile dependencies, scale out parallel agents, and offload heavy builds and storage.
Most of the value comes from caching across users and projects.
Status (implemented): src/depcache_remote.rs here (client) and
src/cache.js in the private ~/justrust-cloud (server, R2). After a local
depcache miss, eligible registry and git units look up
build.jcode.sh/v1/build/cache and download a bundle on a match. Successful
compiles queue an upload that a detached justrust cache --sync sends after
the run. Two tiers: shared, written only by the prebuild fleet
(JUSTRUST_CACHE_PUBLISH_TOKEN), read by every signed-in account, and
private, per account (its other machines, CI, hosted hosts). Keys are
portable across machines (scheme 2: the cargo home is a placeholder, and
proc macros and bins key the glibc version). Verified end to end with two
isolated machines (different cargo homes): a cold build of serde+itoa on the
second machine was 12/12 remote hits, byte-identical rlibs, a runnable binary,
1.0s instead of 6.1s; another account could not see private entries or
publish to the shared tier. Remaining: the prebuild fleet that fills the
shared tier from the top crates, a remap of the cargo home in debug info
(entries still name the publishing machine's paths in panic locations and
debug line tables, like the OUT_DIR case in depcache.rs), and deploying
(R2 bucket, CACHE_ENABLED).
| Artifact | Shared across | Value |
|---|---|---|
| Popular crates.io dependencies | All users | Large |
| Proc macros and build script outputs | All users | High (syn-heavy crates are slow) |
.rmeta only, for check/clippy |
All users | Very high, small to transfer |
| Whole dependency set keyed by lockfile hash | Users of one project | One download replaces the dependency build |
| Own crates at a commit | One team, its agents, CI | Big for swarms |
| Own crate after an edit | Nobody | Stays in the edit loop |
Why it improves with scale:
- Usage follows a power law. A few thousand crates cover most builds, so hit rates rise quickly with users.
- The marginal cost of a hit is storage and egress only.
- justrust sets the flags, so it can standardize toolchains, profiles, and remapped paths. Fragmentation is the main enemy of hit rate.
- A self-hosted copy starts with an empty cache. The full global cache is the moat.
Pre-building:
- On demand: the first miss builds once, everyone after gets it free.
- Proactive: on each stable rustc release, build the top N crates using the feature combinations users actually request, learned from misses.
Hard parts:
- Hermeticity. Some build scripts probe system libraries, read the environment, or embed absolute paths. The cache key must capture every input that can change the output, and units that cannot be made hermetic stay out of the shared tier.
- Poisoning. The shared tier holds only artifacts our servers built from crates.io sources. Never artifacts uploaded by users. Private code is isolated per organization.
Rust artifacts are huge. Measured on the dev laptop with du:
| Directory | Size |
|---|---|
jcode-desktop/target |
940 GB |
jcode/target |
920 GB |
~/.justrust (shared dependency cache) |
22 GB |
justrust/target |
4.5 GB |
The partition (btrfs) reports only 842 GB used in total, so these directories
share blocks and du counts the shared blocks once per directory. Even so,
build output is likely the largest single use of the disk.
Causes: debug info, incremental caches that keep growing, monomorphized generics copied into each crate, and one copy per target dir, profile, and agent slot. Swarms multiply all of it.
Remote moves storage to the server:
- Dependencies,
.rmetafiles, incremental caches, release builds, and test binaries live on the server. check,clippy, and remotetestleave almost nothing on the local disk.- Only artifacts the user runs locally come back, as binary diffs where possible.
- Users never need
cargo cleanorcargo sweep, and swarms stop filling disks.
What we take on: server storage costs, LRU eviction, expiry of old incremental state, deduplication, and a local fallback for offline work.
Follow-up: break down jcode-desktop/target into incremental caches, debug
info, and duplicate slots to measure what a zero-target-dir mode saves.
Decision: open core, licensed by layer. The exact rules and file layout are in LICENSING.md.
| Layer | License | Why |
|---|---|---|
| Client and local tool (today's crate) | MIT | Adoption. Companies avoid restrictive licenses on build tools, and it is already public under MIT |
| Wire protocol and cache key format | MIT | Third-party clients and integrations (CI, sccache or Bazel bridges) make justrust the standard |
Clean-room compiler (main branch, whole repository) |
FSL-1.1-ALv2, inbound Apache 2.0 | All new work and the core asset. Usable for anything except a competing service. Each release becomes Apache 2.0 after two years |
Self-hostable single-tenant server (server/, future) |
FSL-1.1-ALv2 | Readable, self-hostable, modifiable. Nobody may sell it as a competing service. Each release becomes Apache 2.0 after two years |
| Multi-tenant cloud: global shared cache, prebuild fleet, isolation, poisoning defenses, scheduling, billing | Closed, separate private repo | Unproven security surface, and it is the revenue layer. Revisit FSL once it is hardened |
Reasoning:
- The value is in operations and in the network effect of the cache, not in the code. A self-hosted copy starts empty.
- FSL on the server blocks the one real threat: someone hosting our server as a competing paid service.
- Opening the poisoning and isolation layer before it is proven publishes the attack surface. Closing it is for security, not secrecy.
- Comparable products follow this split: Databricks (Apache 2.0 Spark, Delta Lake, and MLflow, closed Photon and managed platform), Turborepo with Vercel Remote Cache, Nx with Nx Cloud, Bazel with BuildBuddy or EngFlow, and Sentry (FSL).
- Contributing has no friction: no CLA, bot, or sign-off. Contributions to
server/come in under Apache 2.0 (stated in CONTRIBUTING.md and the pull request template), so the maintainer can relicense them and use them in the hosted service. Switch to a click-through CLA if legal review requires it.
Share versus total: open source captures a smaller share of the value created but likely a larger total. Local build speedups were never chargeable. Hosted cache, compute, and team features are.
Before launching the paid service, have a lawyer review the FSL text and its interaction with the MIT parts.
- Split justrust's codegen bucket into IR generation and LLVM.
- Time a real one-line edit in
jcode-desktop-uiwith full pass timings. - Measure the composition of
jcode-desktop/target. - Use recorded runs to measure how much dependency build time a global cache would have served.
- Design cache keys and hermeticity rules for a server-built shared tier.