rk server managed lifecycle (phase 0 contract)
The managed lifecycle mode adds VS Code window-aware lifecycle management to rk server without breaking existing behavior. This document defines the Phase 0 API contract for that mode.
You start the mode with rk server start --lifecycle managed. The server then tracks active VS Code windows as client leases and stops when no active clients remain.
Goals
The managed lifecycle has the following goals:
- Preserve current behavior for manually started servers.
- Add optional managed lifecycle mode for VS Code/Pilot-started background servers.
- Track active VS Code windows (one extension host = one client lease).
- Stop managed servers when active clients reach zero.
- Keep existing inactivity timeout behavior (
--timeout) as a global fallback.
Non-goals
The managed lifecycle does not aim to do the following:
- Replace or remove inactivity timeout logic.
- Require client leases for manual CLI usage.
- Persist client lease state across server restarts.
Lifecycle modes
Server startup supports two modes:
-
unmanaged(default):- The mode is intended for manual server startup (for example, a user runs
npx rk serverin a terminal). - The server does not require client registration.
- The existing inactivity timeout and an explicit stop control the server lifetime.
- The mode is intended for manual server startup (for example, a user runs
-
managed:- The mode is intended for Pilot background mode.
- The server tracks window clients through the register, heartbeat, and release lease API.
- The server can stop early when active clients become zero.
- The existing inactivity timeout still applies.
CLI contract changes
The rk server command has an optional lifecycle flag:
npx rk server start --lifecycle unmanaged
npx rk server start --lifecycle managedThe flag follows these rules:
- The default is
unmanagedfor backward compatibility. - An invalid lifecycle value should return a non-zero exit code with a validation message.
- The
rk server stopcommand remains the explicit stop command. - The deprecated
--stopcommand continues to work during the migration window.
Lease model
A lease works as follows:
- A lease represents one active VS Code window client.
- The lease key is
clientId, an opaque string that the client generates. - The server stores the following fields in memory:
clientIdworkspaceRoot(optional but recommended for diagnostics)createdAtlastHeartbeatAtmetadata(optional: client type/version)
- The server defines the lease time to live (TTL), for example 90 seconds.
- Clients send a heartbeat periodically (less than half the TTL is recommended).
HTTP API contract
All lease endpoints are under /api/clients.
POST /api/clients/register
This endpoint registers or refreshes a client lease.
The following example shows the request body:
{
"clientId": "extension-window-uuid",
"workspaceRoot": "/abs/path/to/workspace",
"clientType": "extension",
"clientVersion": "0.3.0"
}The following example shows the response (HTTP 200):
{
"success": true,
"mode": "managed",
"clientId": "extension-window-uuid",
"leaseTtlMs": 90000,
"heartbeatIntervalMs": 30000,
"activeClients": 2,
"serverStartTime": 1710870000000
}The endpoint returns the following errors:
- If the mode is
unmanaged, the endpoint returns409with the codeLIFECYCLE_UNMANAGED. - If a required field is missing or not valid, the endpoint returns
400.
POST /api/clients/heartbeat
This endpoint extends an existing lease.
The following example shows the request body:
{
"clientId": "extension-window-uuid"
}The following example shows the response (HTTP 200):
{
"success": true,
"clientId": "extension-window-uuid",
"activeClients": 2,
"leaseExpiresInMs": 90000
}The endpoint returns the following errors:
- An unknown or expired client should receive
404with the codeCLIENT_NOT_FOUND. - If the mode is
unmanaged, the endpoint returns409with the codeLIFECYCLE_UNMANAGED.
POST /api/clients/release
This endpoint releases a client lease explicitly.
The following example shows the request body:
{
"clientId": "extension-window-uuid"
}The following example shows the response (HTTP 200):
{
"success": true,
"clientId": "extension-window-uuid",
"activeClients": 1
}The endpoint returns the following errors:
- An unknown client may receive an idempotent
200success or404. - Recommended: return an idempotent
200success to simplify deactivate flows.
GET /api/clients/status (optional but recommended)
This endpoint is a debug and diagnostic endpoint.
The following example shows the response (HTTP 200):
{
"mode": "managed",
"activeClients": 2,
"leaseTtlMs": 90000,
"clients": [
{
"clientId": "extension-window-uuid",
"workspaceRoot": "/abs/path",
"lastHeartbeatAt": 1710871234567
}
]
}Shutdown rules
In managed mode, the server follows these rules:
- If active clients become
0, the server starts a short grace timer (for example, 5 to 10 seconds). - When the timer fires, the server re-checks the active clients.
- If the count is still
0, the server stops. - If a new client appeared, the server cancels the shutdown.
- If the count is still
In both modes, the following rules apply:
- The existing inactivity timeout (
--timeout) remains active. - The timeout can stop the server even if active clients exist (explicit requirement).
- Every stop (zero clients, timeout,
/api/stop, or a signal) first stops the Roku Cloud Emulator devices that the server started through MCPdevice_start, then closes its sessions, and then closes the listener (forced after 10 seconds). See Server and MCP for more information.
Health/diagnostics metadata
Extend the health response with lifecycle fields:
{
"status": "ok",
"mode": "managed",
"activeClients": 2,
"leaseTtlMs": 90000
}Optionally, you can include the same metadata in .roku/rk-server.json for richer client diagnostics.
Concurrency and race handling
The server must follow these concurrency rules:
- Serialize register, heartbeat, release, lease sweep, and zero-client shutdown checks, or otherwise make them race-safe.
- Release and heartbeat for the same
clientIdshould be idempotent and safe under retries. - Before the server stops, it must do a final active-client check.
Backward compatibility
The lifecycle flag preserves existing behavior:
- Existing clients that do not use the lease API continue working in
unmanagedmode. - The existing
--timeoutbehavior is unchanged. - The existing
/api/stopbehavior is unchanged.
Pilot integration contract (for phase 1)
In managed background mode, Pilot should do the following:
- Call
registeronce per VS Code window on activation or start. - Start periodic heartbeats.
- Call
releaseon deactivation. - When the server is unavailable, keep the heartbeat loop active but do not start the server implicitly.
- Re-register after an explicit server start path succeeds.
Manual terminal usage remains unmanaged and does not require leases.
Clarification: no implicit start from lease API
Lease endpoints are liveness and coordination endpoints only. They should not serve as an implicit server startup path:
- The
register,heartbeat, andreleaseendpoints are expected to return an unavailable or not-applicable response when the server is down. - Server start remains an explicit operation (
rk server, Pilot Start rk server, or explicitensureRunningentry points).
Acceptance mapping
The acceptance criteria map to two modes:
- Mode A (manual terminal start): The server behaves as it does without managed mode, and the timeout drives its lifetime.
- Mode B (VS Code managed start): The server counts clients across windows and stops when no clients remain.
- B extends A: The timeout remains active in both modes.
Understanding current managed runtime behavior
In current Pilot-managed flows, the following behaviors apply:
- Lease APIs (
register,heartbeat,release) do not start the server automatically when it is down. - The server treats a heartbeat as a liveness update only. If the server is unavailable, clients log a skipped heartbeat and keep waiting.
- Server restart should happen only from explicit start flows (for example, user-triggered Start rk server or other explicit
ensureRunningpaths). - In multi-window use, one window stopping the server can cause all windows to report skipped heartbeat until a new explicit start occurs.
Related documentation
- rk server: Describes server mode in full.
- Server and MCP: Describes how the server stops Roku Cloud Emulator devices that it started.
Updated about 6 hours ago