Repository navigation
rutis v0.9.0
rutis 0.9.0
0.9.0 adds Bun and Go as plugin runtimes. The host refuses a synchronous call that would close a wait cycle across runtimes, rutis-host check finds services nothing provides, and stopping and local channels are more reliable. The minimum Python goes down to 3.10 and the minimum Node to 22.
Packages
All of these are at 0.9.0:
| Registry | Packages |
|---|---|
| crates.io | rutis (the core), rutis-bridge, rutis-loader, rutis-host; the dylib toolchain rutis-sdk, rutis-dylib, rutis-dylib-meta, rutis-dylib-launcher |
| npm | @arcships/rutis, @arcships/rutis-runtime, @arcships/rutis-bun (first release), @arcships/rutis-host, and the five platform host packages |
| PyPI | rutis, rutis-host |
| Go | github.com/arcships/rutis/go/rutis (first release, tag go/rutis/v0.9.0) |
| GitHub | rutis-host binaries for Linux / macOS (x64 / arm64) and Windows (x64), with SHA-256 checksums |
rutis-cli keeps its own version.
What's new
- Bun runtime (
@arcships/rutis-bun, Bun 1.4+): JavaScript and TypeScript plugins can run in Bun; plugin code still uses@arcships/rutis. The host takesruntimes.bun,rutis-host new --lang buncreates a project, andrutis-host devrecognizes Bun projects and reloads them. In Rust:Launcher::bun/LocalRuntime::bun(featurebun) and the loader'sbun:<module>rows. Synchronous calls are re-entrant. A plugin with a syntax error is reported withfile:line:column. Remote listening and Windows come in a later release. - Go runtime (
go/rutis, Go 1.24+): a Go plugin is compiled into a binary, and each binary is one runtime with only the standard library as a dependency. Write plugins withDefine/Plugin[C]/Serve; the config struct gives the JSON Schema,--rutis-manifestprints the manifest, andrutistesttests plugins. The host takesruntimes.go(dir/binaries/start/idle) and starts a runtime when a row needs it and stops it when idle;rutis-host new --lang go,rutis-host go add <module>@<version>;devbuilds Go projects and restarts only the Go runtime. In Rust:Launcher::go/LocalRuntime::goand the loader'sGoResolver/GoRuntimes(featurego). - Wait cycles across runtimes: before forwarding a synchronous call, the host checks who waits for whom; a call that would close a cycle (for example Python → Node → Python, each waiting synchronously) fails with
SyncWaitCycleinstead of deadlocking. The Node, Python and Bun runtimes declaresync-wait. A nested call's origin carries a session label, so nested calls such as Python → Node → Python now complete. rutis-host check: without running plugins, it checks that every service a row injects is provided by some row, and exits with 1 when one is not (when the provider is on another node, it only notes it). Disabled rows are no longer resolved. In Rust:Loader::check. It also prints each runtime's implementation and engine, and the Go binaries.- Stopping and signals:
run/devhandle SIGINT / SIGTERM / SIGHUP (Ctrl-C / Ctrl-Break / closing the console on Windows), unload the rows and exit.--shutdown-timeout <seconds>(orRUTIS_SHUTDOWN_TIMEOUT, 10 by default) bounds the cleanup, and the exit code is 2 when it did not finish. Runtime processes run in their own process group, so Ctrl-C in the terminal reaches only the host. - A runtime process's standard streams:
stdin/stdout/stderronLauncher/LocalRuntimecan each beStdio::NullorStdio::Inherit, for example to give stdin to a plugin that reads the terminal. - A plugin that disposes itself: when a plugin in a Node row unloads itself, the host gets
SelfDisposedand the row becomes disabled (a copy in an instance becomesStopped) instead of staying shown as running. New: therows.endedmessage andProcess::on_row_ended. - Local channels: one message is at most 16 MiB (the same in Rust and every language runtime); a larger one closes the channel, and a stream that ends in the middle of a message is a failure everywhere.
- Configuration: with
rutis.jsongiven as a relative path, rows such as./plugin.mjsnow resolve against the configuration file; unknown fields inrowsare warned about; a listener with onlycertor onlykeyis an error instead of silently running without TLS; runtime names must be unique. - Python (3.10 minimum): on 3.10 / 3.11 rutis starts plugin methods eagerly itself, the same way as the native implementation on 3.12+. A cancel that arrives during a plugin method's first step is no longer ignored; a WebSocket listener whose
accept()fails ends as a failure;python -m rutisprints an error and exits with 1;Listener.close()works with websockets older than 17. The optionalnetworkdependency needs websockets 15+. - Other fixes:
- Child processes inherit only fds 0–3, which fixes occasional Broken pipe errors and hangs when runtimes start concurrently on macOS.
- A process that ends has its exit status reported, including one that closed its channel but has not exited after 10 s, and how it ended when a send fails.
- A protocol version mismatch reliably gives
Handshake::Incompatibleon both sides. - Calling the root's
dispose()andCtx::shutdown()at the same time no longer makesdispose()returnClosednow and then. - A crashed Go runtime is
Downinstead of staying "Starting".
Compatibility
- Upgrade the host and the language runtimes to 0.9 together: protocol versions are unchanged (local sessions use protocol 2, sessions with endpoint identity protocol 3) and PLUGIN_API is still 1, but a 0.9 host forwards call origins with a session label that 0.8 runtimes do not accept; they end the session.
- Rust API:
LauncherandSpawninrutis-bridgeare#[non_exhaustive], andRowSchemahas a new fieldversion;rutisandrutis-loaderhave no incompatible changes. Dylib plugins must be rebuilt with the 0.9.0 SDK. rutis-host:checkfails on a service nothing provides; exit code 2 is new.- Minimum versions: Rust 1.88 (was 1.85), Node 22 (was 24), Python 3.10 (was 3.12), Bun 1.4, Go 1.24.
See the migration guide.
Getting started
npx @arcships/rutis-host@0.9.0 new greeter --lang node
npx @arcships/rutis-host@0.9.0 new greeter-bun --lang bun
npx @arcships/rutis-host@0.9.0 new greeter-go --lang go
uvx --from rutis-host==0.9.0 rutis-host new greeter-py --lang pythonThen read TypeScript plugins, Bun plugins, Python plugins, Go plugins, embedding in Rust or connecting nodes.