Skip to main content

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.
ParameterTypeDescription
instanceIdString6-character unique identifier for the instance.
workingDirPathAbsolute path to ./running/<instanceId>/.
portIntAllocated local port for this instance.
commandStringFull command string from the Configuration.
ramMBIntMaximum RAM in megabytes. 0 means unlimited.
cpuIntMaximum 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

MethodDescription
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

KeyDescription
screenStarts the process inside a GNU Screen session. Supports stdin piping via screen -S <id> -X stuff.
tmuxStarts the process inside a tmux session. Supports stdin piping via tmux send-keys.
processStarts the instance as a direct child process without a terminal multiplexer.
dockerAvailable via the extensions/runtime-docker extension. Runs the instance as a Docker container.

Implementing a custom runtime

1

Create the extension module

Add a new module under extensions/runtime-<name>/. Depend on :api and :extensions:extension-api. Do not depend on :app.
2

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
    }
}
3

Register in your Extension's onLoad

class MyExtension : Extension {
    @Inject lateinit var registry: RuntimeRegistry

    override fun onLoad() {
        registry.register("my-runtime", MyRuntimeProvider())
    }
}
4

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.

Build docs developers (and LLMs) love