Architecture
Transit is a polyglot interop system built around three core principles:
- No glue layers — functions call each other directly across language boundaries
- Zero source changes for basics — the scanner discovers existing public functions automatically
- Optimal transport per pair — each language pair uses the fastest available mechanism
System Overview
Section titled “System Overview”┌──────────────────────────────────────────────────────────────┐│ Your JavaScript Code ││ ││ const rs = transit.rust("./rust") ││ const jv = transit.java("./java") ││ const py = transit.python("./python") ││ await rs.processFile(job) ││ await jv.processSpecialized(result) ││ await py.processData(transformed) │└─────────────┬────────────────────────────────┬───────────────┘ │ │ ▼ ▼┌─────────────────────┐ ┌─────────────────────────────┐│ transit-scanner │ │ transit-js ││ (Rust native) │ │ (Public API + Proxy) ││ │ │ ││ • tree-sitter parse │◄─────────│ scanDirectorySync() ││ • manifest output │ │ createLanguageHandle() ││ • tier detection │ │ FunctionProxy (Proxy) │└─────────────────────┘ └──────┬──────────────┬───────┘ │ │ ┌─────────────┘ └─────────────┐ ▼ ▼┌──────────────────────────────────┐ ┌─────────────────────────────────┐│ RustDevBridge │ │ JavaDevBridge ││ │ │ ││ Loads .node native addon │ │ Manages JVM process lifecycle ││ Calls #[napi] functions │ │ TCP binary protocol client ││ In-process, zero-copy │ │ Health checks + auto-restart │└──────────────────┬───────────────┘ └──────────────┬──────────────────┘ │ │ ▼ ▼┌──────────────────────────────────┐ ┌─────────────────────────────────┐│ Your Rust Codebase │ │ TransitServer (Java) ││ (compiled .node addon) │ │ (resident JVM process) │└──────────────────────────────────┘ └─────────────────────────────────┘Components
Section titled “Components”transit-scanner (Rust)
Section titled “transit-scanner (Rust)”The scanner is written in Rust for performance. It’s compiled to a native addon (.node) and loaded by transit-js at module init.
Capabilities:
- Fast directory walk with
.gitignoreawareness (via theignorecrate, same as ripgrep) - Tree-sitter parsing for Rust, JavaScript, Python, and Java grammars
- Text-based fallback for Rust (
pub fn) and Java (publicmethods) - Comment marker detection:
transit:file(Tier 2) andtransit:function(Tier 3) - Outputs a JSON manifest:
{ language, sourceFile, functionName, signature, exportTier }[]
Why Rust: Directory walking + tree-sitter parsing is CPU-bound work where Rust’s speed matters. The scanner runs on every dev startup and will eventually run on file-save in watch mode.
transit-js (TypeScript)
Section titled “transit-js (TypeScript)”The public API surface. Exports the transit singleton and the FunctionProxy type.
Key classes:
Transit— factory that creates and caches language handlesRustDevBridge— loads the native addon, dispatches callsJavaDevBridge— manages the JVM process, dispatches calls via TCPPythonDevBridge— manages the Python process, dispatches calls via TCPcreateLanguageHandle()— builds aProxythat resolves names against the manifest
transit-java-runtime (Java)
Section titled “transit-java-runtime (Java)”A minimal TCP server (TransitServer.java) and example service (TransitService.java). No external dependencies.
- Binds to
127.0.0.1on an ephemeral port - Prints
PORT=<port>to stdout for the parent process to read - Handles
CALL_REQUESTandHEALTH_PINGmessages - Functions are registered via
server.registerFunction(name, fn)
transit-java-runtime-js (TypeScript)
Section titled “transit-java-runtime-js (TypeScript)”The Node.js client for the Java TCP bridge.
JavaProcessManager— manages JVM lifecycle (spawn, port detection, connect, health checks, auto-restart)- Binary protocol encoder/decoder
- Pending call tracking with timeouts
transit-py-runtime (Python)
Section titled “transit-py-runtime (Python)”A minimal TCP server (transit_server.py) for Python function hosting. No external dependencies — uses only stdlib (socket, struct, json).
- Binds to
127.0.0.1on an ephemeral port - Prints
PORT=<port>to stdout on startup - Handles
CALL_REQUEST,HEALTH_PINGmessages - Functions registered via
register_function(name, fn) - Same binary protocol as Java
transit-python-runtime-js (TypeScript)
Section titled “transit-python-runtime-js (TypeScript)”The Node.js client for the Python TCP bridge.
PythonProcessManager— manages Python process lifecycle (same pattern asJavaProcessManager)- Binary protocol encoder/decoder (identical to Java client)
- Pending call tracking with timeouts
transit-schema (TypeScript)
Section titled “transit-schema (TypeScript)”Shared type definitions for the Transit IDL, manifest, and configuration.
transit-codegen (TypeScript)
Section titled “transit-codegen (TypeScript)”Code generation from scanner manifests. Produces typed stubs and glue code.
Generators:
generateTypeScript(manifest)— producestransit.gen.tswith typed function interfaces and acreateTransit()factorygenerateJavaGlue(manifest)— producesTransitService.gen.javawith function registration stubsgeneratePythonGlue(manifest)— producestransit_service.gen.pywith function stubs and camelCase registrations
transit-cli (TypeScript)
Section titled “transit-cli (TypeScript)”Command-line interface for Transit development workflow.
Commands:
transit init— Scans source files fortransit.rust(),transit.java(),transit.python()calls, detects languages, and writestransit.config.jsontransit dev— Live development mode with file watching, automatic re-scanning, and function change loggingtransit build— Build pipeline that orchestrates scan → codegen → compile. Generates typed stubs and compiles Rust/Java codetransit start— Production mode with built artifacts, resident processes, and signal forwarding
Transport Strategies
Section titled “Transport Strategies”| Pair | Mechanism | Serialization | Latency |
|---|---|---|---|
| JS ↔ Rust | In-process native addon | N/A (direct memory) | ~ns |
| JS ↔ Java | TCP socket, binary protocol | Custom binary (10-byte header + payload) | ~ms |
| JS ↔ Python | TCP socket, binary protocol | Custom binary (10-byte header + payload) | ~ms |
| Rust ↔ Java | Socket (planned) | Binary | ~ms |
Why different transports?
Section titled “Why different transports?”V8 (JavaScript) and native Rust code can share a process via N-API — this is how Node.js native addons work. A Rust function compiled with napi-rs is loaded directly into the Node.js process, giving true zero-copy interop.
The JVM cannot share a process with V8. Java runs as a separate process, connected via TCP. To avoid the latency of per-request process startup, Transit keeps the JVM alive as a resident process.
Binary Protocol (JS ↔ Java)
Section titled “Binary Protocol (JS ↔ Java)”See Binary Protocol for the wire format specification.
Key design decisions:
- Little-endian, fixed 10-byte header (version, type, request ID, payload length)
- Function names sent as length-prefixed UTF-8 strings
- Arguments and results are JSON-encoded strings (not binary-encoded — simplicity over raw performance for v0.1)
- Health checks keep the connection alive and detect crashed processes
- TCP on loopback only (never exposed to the network)
Call Flow
Section titled “Call Flow”JS → Rust (dev mode)
Section titled “JS → Rust (dev mode)”1. User calls: rs.processGeneral(job)2. Proxy intercepts property access "processGeneral"3. flatMap lookup finds the entry in the manifest4. Proxy returns a function that calls target._bridge.call("processGeneral", [job])5. RustDevBridge.call() looks up "processGeneral" (or "process_general") in the loaded addon6. The #[napi] function executes in-process7. Result is returned directly (no serialization for Rust→JS via N-API)JS → Java (dev mode)
Section titled “JS → Java (dev mode)”1. User calls: jv.processSpecialized(data)2. Proxy intercepts, flatMap lookup succeeds3. JavaDevBridge.call() triggers doStart() if not started4. JVM is spawned, PORT=<port> is read from stdout5. TCP connection is established to 127.0.0.1:<port>6. Binary protocol: CALL_REQUEST message sent7. TransitServer dispatches to the registered function8. Function returns a JSON string9. CALL_RESPONSE message received by JavaProcessManager10. Result string is returned to the callerJS → Python (dev mode)
Section titled “JS → Python (dev mode)”1. User calls: py.processData(data)2. Proxy intercepts, flatMap lookup succeeds3. PythonDevBridge.call() triggers doStart() if not started4. Python process is spawned, PORT=<port> is read from stdout5. TCP connection is established to 127.0.0.1:<port>6. Binary protocol: CALL_REQUEST message sent7. transit_server.py dispatches to the registered function8. Function returns a JSON string9. CALL_RESPONSE message received by PythonProcessManager10. Result string is returned to the callerDev Mode vs Build Mode
Section titled “Dev Mode vs Build Mode”Dev mode (current)
Section titled “Dev mode (current)”transit devstarts a live development server- Scanner runs on startup, builds a manifest
FunctionProxyresolves names dynamically viaProxy- File watcher re-scans directories on changes
- Rust: loads the
.nodeaddon directly - Java: spawns a resident JVM process on first call
- Python: spawns a resident Python process on first call
- Optimized for iteration speed, not raw throughput
Build mode
Section titled “Build mode”transit buildruns the scan → codegen → compile pipeline- Codegen replaces dynamic
Proxywith generated typed stubs (transit.gen.ts) - Rust:
cargo build --releaseproduces an optimized.nodeaddon - Java:
javaccompiles classes dist/transit-manifest.jsonrecords all discovered functions and compilation statustransit startloads the manifest and runs in production mode- No runtime scanning overhead in production
Directory Walking
Section titled “Directory Walking”The scanner skips these directories by default:
node_modules,target,__pycache__,.git,dist,build,.next,vendor
Hidden files are included (.gitignore-style hidden files are read).
Error Model
Section titled “Error Model”Rust errors: N-API propagates Rust Result::Err as JavaScript exceptions. The error message from Rust is included in the exception.
Java errors: If a Java function throws an exception, the server catches it and returns a CALL_RESPONSE with STATUS_ERROR and a JSON error message: {"error": "..."}.
Python errors: If a Python function raises an exception, the server catches it and returns a CALL_RESPONSE with STATUS_ERROR and a JSON error message: {"error": "..."}.
Scanner errors: If a file fails to parse, it’s silently skipped. The scanner continues with the rest of the directory.
Bridge errors: If the Java process crashes, it auto-restarts up to 3 times with exponential backoff. In-flight calls are rejected with an error.