Documentation Index
Fetch the complete documentation index at: https://mintlify.com/ohemilyy/universe/llms.txt
Use this file to discover all available pages before exploring further.
RuntimeProvider is the abstraction that Universe uses to start, stop, and communicate with instance processes. The core Wrapper daemon never calls Docker, tmux, or screen directly — it only calls RuntimeProvider methods through the RuntimeRegistry. Extension authors implement this interface to add new execution backends.
Both interfaces are defined in the api module (gg.scala.universe.runtime) and are available to all extensions.
RuntimeProvider interface
interface RuntimeProvider {
fun start(instanceId: String, workingDir: Path, port: Int, command: String, ramMB: Int, cpu: Int): ProcessHandle
fun stop(instanceId: String)
fun executeCommand(instanceId: String, command: String)
fun isRunning(instanceId: String): Boolean
}
Methods
start
Launches the instance process. Called by the Wrapper node after templates have been installed.
| Parameter | Type | Description |
|---|
instanceId | String | 6-character unique identifier for the instance. |
workingDir | Path | Absolute path to ./running/<instanceId>/. |
port | Int | Allocated local port for this instance. |
command | String | Full command string from the Configuration. |
ramMB | Int | Maximum RAM in megabytes. 0 means unlimited. |
cpu | Int | Maximum CPU units. 0 means unlimited. |
Returns a ProcessHandle for the started process.
stop
Terminates the process associated with instanceId. Implementations are expected to send a graceful signal before force-killing.
executeCommand
Pipes command into the stdin of the running process. Used to send in-game commands or control messages to the instance without stopping it.
isRunning
Returns true if the process for instanceId is still alive. The Wrapper uses this to detect unexpected crashes.
RuntimeRegistry interface
interface RuntimeRegistry {
fun register(key: String, provider: RuntimeProvider)
fun unregister(key: String)
fun get(key: String): RuntimeProvider?
fun getAll(): Map<String, RuntimeProvider>
}
Methods
| Method | Description |
|---|
register(key, provider) | Binds provider to the given technology key (e.g., "docker"). |
unregister(key) | Removes the provider registered under key. |
get(key) | Returns the provider for key, or null if not registered. |
getAll() | Returns a snapshot of all registered key → provider mappings. |
The key must match the runtime field in a Configuration. For example, a configuration with "runtime": "screen" will use the provider registered under "screen".
Built-in runtimes
| Key | Description |
|---|
screen | Starts the process inside a GNU Screen session. Supports stdin piping via screen -S <id> -X stuff. |
tmux | Starts the process inside a tmux session. Supports stdin piping via tmux send-keys. |
process | Starts the instance as a direct child process without a terminal multiplexer. |
docker | Available via the extensions/runtime-docker extension. Runs the instance as a Docker container. |
Implementing a custom runtime
Create the extension module
Add a new module under extensions/runtime-<name>/. Depend on :api and :extensions:extension-api. Do not depend on :app.
Implement RuntimeProvider
class MyRuntimeProvider : RuntimeProvider {
override fun start(instanceId: String, workingDir: Path, port: Int, command: String, ramMB: Int, cpu: Int): ProcessHandle {
// launch the process and return its ProcessHandle
}
override fun stop(instanceId: String) {
// terminate the process
}
override fun executeCommand(instanceId: String, command: String) {
// pipe command into stdin
}
override fun isRunning(instanceId: String): Boolean {
// return true if the process is alive
}
}
Register in your Extension's onLoad
class MyExtension : Extension {
@Inject lateinit var registry: RuntimeRegistry
override fun onLoad() {
registry.register("my-runtime", MyRuntimeProvider())
}
}
Reference the key in a configuration
{
"name": "my-config",
"runtime": "my-runtime"
}
Extensions must never depend on the :app module. Only :api and :extensions:extension-api are permitted as extension dependencies.