9.6 KiB
Runa — Rune Actors
Runa (short for Rune Actors) is an experimental project to build a persistent, live programming world around isolated actors written in Rune. The long-term vision combines an inspectable graphical environment with a programmatic API and a VS Code extension, all connected to the same running image.
Status
Runa is at an early prototype stage. The executable runs a Phase 0 feasibility experiment using Rune 0.14.2: script execution, native calls, async suspension, instruction budgets, stepping, and compiled-code sharing. It also runs a minimal single-worker actor demonstration with bounded mailboxes and explicit message effects. Source and compiled implementation identities are BLAKE3 content addresses with an in-memory compile cache. Persistence, canonicalization, remote network access, and the graphical world are not implemented yet. A minimal VS Code extension can connect to the local image, list actors, and inspect state, queued/reserved messages, and execution telemetry in read-only documents. The code library now supports an in-memory versioned behavior registry, and the CLI exposes publishing, history, rollback, safe-boundary installation, pause/resume, and cancellation through a trusted in-process image API. A versioned transport-neutral protocol contract is now defined, but no authentication is implemented. The dispatcher is available through local JSON-lines stdio and an unauthenticated TCP listener restricted to localhost; both use a bounded request size. The interactive CLI connects to the localhost server over TCP, so multiple clients operate on the same server-owned image.
The prototype uses worker-local invocation stacks and cooperative instruction slices, and actor snapshots report basic active-execution progress. Native callbacks are not preemptible; source-level debugging and persistent storage are not implemented.
Design goals
- Keep actor identity, mailbox, private state, behavior, and incarnation separate.
- Use message passing rather than shared mutable state for actor interaction.
- Keep behavior implementations immutable and support safe, versioned updates.
- Expose the same runtime capabilities to in-world tools and external clients.
- Be explicit about execution, delivery, and persistence guarantees.
See the architecture spec for the full vision, architecture, roadmap, and open questions.
Getting started
You need a Rust toolchain that supports the workspace's Rust 2024 edition.
Start the live image from the repository root:
mise run serve-network
In another terminal, connect the REPL:
mise run start
The REPL defaults to 127.0.0.1:3737. To select another loopback endpoint,
use cargo run -p runa -- --connect 127.0.0.1:4000 and start the server with
the corresponding --listen address. Quitting the REPL disconnects only that
client; actors and published behaviors survive until the server exits.
Reconnect and use actors to recover their references. REPL bindings and
command history are session-local and are not restored on reconnect.
For a local child-process protocol endpoint, run mise run serve and exchange
one JSON request per line on stdin; responses are written to stdout. Request
lines are limited to 1 MiB.
For a TCP endpoint, run mise run serve-network. It listens on
127.0.0.1:3737 and accepts the same JSON-lines protocol, with up to 16
simultaneous clients sharing one live image. The listener rejects non-loopback
addresses and has no authentication; any local process that can connect has
full access to the development API. Use it only on a trusted local machine.
The runa-client crate can launch the stdio process or connect over loopback
TCP and exchange typed protocol requests with request/response correlation.
TCP connection establishment is limited to 5 seconds and reads/writes to 30
seconds. Transport failures disconnect the client and are never automatically
retried: an operation may have committed even if its response was lost.
The VS Code extension connects over the network to this same live image; actors remain in
the image regardless of how clients connect. Remote network access requires
authentication and authorization, which are not implemented.
The TCP server publishes the bundled counter behavior once at startup;
connecting a client does not publish or reset behavior revisions:
runa> let sender = spawn counter 0
sender = @<actor-reference>
runa> let receiver = spawn counter 10
receiver = @<actor-reference>
runa> send sender {"op": "send", "value": 5, "target": {"$actor": "receiver"}}
message queued
runa> tick 100
runa> inspect sender
runa> inspect receiver
runa> events
Actor references are generated NanoIDs rather than sequential numbers. The
let form binds each spawned actor to a local name; references can also be
entered directly with an @ prefix. The sender commits state 1 and sends a
message to the receiver, which commits state 15. The REPL provides Tab
completion and Up/Down command history. It also supports publishing Rune source
files as named, versioned behaviors, viewing history, rolling back a behavior
reference, installing the current version on an idle actor, and cooperatively
pausing and resuming or cancelling actor invocations between scheduler ticks.
Runtime users can also supply an explicit state migration closure during
installation; the CLI's install command preserves state as-is. Use help in
the session for the complete command list.
State and messages support null, booleans, signed 64-bit integers, strings,
lists, records, and distinct actor-reference values. spawn <behavior> <json>
sets initial state; send <actor> <json> sends any supported value. JSON
objects of the form {"$actor": "name-or-@reference"} embed an actor reference,
including inside a record or list. See the runtime guide
for Rune conversions and value limits. This is protocol version 3; old
four-argument Rune handlers and kind/value/target message envelopes must be
updated to the two-argument handler contract.
See the feasibility report for verified Rune capabilities and limitations, and the runtime guide for the runtime contract.
VS Code client
With Node.js 22+ and npm installed, run npm --prefix vscode ci. In VS Code,
open this repository and launch Run Runa Extension from Run and Debug
(F5). Start mise run serve-network, then use Runa: Connect to Local Image
in the Extension Development Host. The Runa Actors view in Explorer lists
actors created through the REPL or another client. Click an actor to inspect
it, and use Runa: Refresh Actors to fetch current snapshots.
The extension does not start, tick, or stop the image. Disconnecting leaves actors running in the server. Editing, spawn/send controls, and event subscriptions are not implemented yet. See the extension guide for connection restrictions, snapshot semantics, and tests.
Development
Install mise and run mise install to install Tombi.
Rust, rustfmt, Clippy, Node.js 22+, and npm must also be available on your PATH.
Install the extension's locked dependencies with npm --prefix vscode ci.
mise run format
mise run lint
mise run test
mise run pre-commit
The pre-commit task runs formatting, linting, and tests in that order, stopping on the first failure. Run it successfully before committing and review any formatter changes. This is a task, not an automatically installed Git hook.
Tombi formats TOML files offline; rustfmt formats all workspace Rust code;
Prettier formats the extension. Clippy checks all workspace targets with
warnings treated as errors. Extension checks include strict TypeScript and
Prettier. The test task runs the Rust workspace tests, extension unit tests,
and a TypeScript-to-Rust TCP interoperability test.
mise run test-extension-host additionally downloads VS Code 1.96.4 into an
ignored test cache and tests the actual inspector commands in an isolated
Extension Development Host. It requires a graphical display; on headless Linux,
use xvfb-run -a mise run test-extension-host.
Project structure
.
├── Cargo.toml
├── crates/
│ ├── runa/ # Main binary
│ ├── runa-core/ # Shared stable identities and portable actor values
│ ├── runa-actor/ # Actor value contracts
│ ├── runa-code/ # Source identities and compile cache
│ ├── runa-rune/ # Phase 0 Rune experiments
│ ├── runa-runtime/ # Single-worker actor prototype
│ ├── runa-image-api/ # Trusted in-process image operations
│ ├── runa-protocol/ # Versioned transport-neutral wire DTOs
│ ├── runa-server/ # Protocol dispatcher and local JSON-lines transport
│ └── runa-client/ # Stdio child-process and loopback TCP clients
├── docs/
│ ├── architecture.md
│ ├── protocol.md
│ └── decisions/
├── runa-std/
│ ├── core/
│ ├── actors/
│ ├── ui/
│ └── tools/
├── scripts/
├── vscode/
├── examples/
└── tests/
runa-std/ is reserved for the default Rune scripts loaded into an image.
scripts/ is for general-purpose development automation. Root-level tests/
is reserved for system and end-to-end tests; it is not automatically discovered
by Cargo. Add an explicit runner when those tests are introduced, keeping Rust
crate integration tests within each crate.
Only the Phase 0 and minimal Phase 1 crates are present. Remaining crates will be added as their functionality is implemented.