Skip to content

Latest commit

 

History

History
145 lines (106 loc) · 6.14 KB

File metadata and controls

145 lines (106 loc) · 6.14 KB

Session Management

Overview

Sessions are terminal instances backed by Docker containers (cloud mode) or direct PTY (local mode). Each session belongs to a project and runs in an isolated environment with resource limits.

State Machine

POST /projects/{id}/sessions
       │
       ▼
  ┌──────────┐   container ready    ┌─────────┐
  │ Starting │ ────────────────── ▶ │ Running │
  └──────────┘                      └─────────┘
       │                                │
       │ spawn failure                  │ POST /stop
       ▼                                ▼
  ┌──────────┐                     ┌─────────┐
  │  Failed  │                     │ Stopped │
  └──────────┘                     └─────────┘
       ▲                                │
       │ resume/retry failure           │ POST /resume
       │                                ▼
       └──────────────────────── ┌─────────┐
                                 │ Running │
                                 └─────────┘

States:

  • Starting — container is being created, image pulled if needed
  • Running — container running, exec attached, PTY available for I/O
  • Stopped — container stopped gracefully (user-initiated or idle timeout)
  • Failed — container failed to start or crashed; error_reason contains details

REST API

See REST API for full endpoint documentation. Summary:

Endpoint Method Description
/api/v1/projects/{project_id}/sessions POST Create session in project
/api/v1/projects/{project_id}/sessions GET List sessions for project
/api/v1/sessions GET List all sessions (global, paginated)
/api/v1/sessions/{id} GET Get session by ID
/api/v1/sessions/{id}/stop POST Stop session
/api/v1/sessions/{id}/resume POST Resume stopped session
/api/v1/sessions/{id}/retry POST Retry failed session
/api/v1/sessions/{id} DELETE Delete session record
/api/v1/images GET List available Docker images
/api/v1/profiles GET List session profiles

Docker Lifecycle

Managed by DockerOrchestrator (runner/src/docker.rs):

  1. Image pull — pulls the image if not locally available
  2. Container create — creates with resource limits, seccomp profile, and labels
  3. Container start — starts with idle entrypoint (container stays alive)
  4. Exec attachdocker exec with TTY for interactive shell
  5. Resize — resizes the exec's TTY window
  6. Stop/Remove — graceful stop, then removal

Labels

All Relay-managed Docker resources carry labels:

  • relay.managed=true — identifies Relay-managed resources
  • relay.session_id=<uuid> — links container to session

Resource Limits

Configured in [docker] section of config.toml:

Setting Default Description
memory_limit 2g Memory limit per container
memory_swap 2g Memory+swap (equal = no swap)
cpu_limit 2.0 CPU cores per container
exec_timeout_secs 30 Timeout for exec operations

Network Isolation

Each session gets a dedicated Docker network, preventing cross-session network access.

Allowed Images

Configure docker.allowed_images to restrict which images can be used. Empty list allows all images.

Session Profiles

Profiles are predefined terminal initialization templates:

[[profiles]]
name = "claude-default"
init_command = "claude"
env_vars = { "NODE_ENV" = "production" }

Available via GET /api/v1/profiles. When creating a session, specify a profile name to use its initialization command and environment configuration. The Docker image is specified separately at session creation time (see domain model §5 for image resolution chain).

Concurrency Control

CloudSessionManager enforces per-project session limits via tokio Semaphore:

  • Each project gets a semaphore with max_sessions permits
  • Session creation acquires a permit; stop/fail releases it
  • Exceeding the limit returns HTTP 429 / gRPC RESOURCE_EXHAUSTED

Source: runner/src/cloud_session_manager.rs

Reconciliation at Startup

On server start, before accepting connections, a reconciliation pass syncs SQLite state with Docker:

  1. Dead sessionsStarting/Running sessions whose container no longer exists or has exited are marked Stopped
  2. Orphan containers — containers labelled relay.managed=true without a DB session are force-removed
  3. Orphan networks — Relay-managed networks not attached to any managed container are removed

All three steps run concurrently via tokio::join!. Errors are logged but tolerated — partial reconciliation is better than refusing to start.

See ADR-009 for the design rationale.

Source: runner/src/reconciliation.rs

Persistence

Sessions are stored in SQLite via the Store layer:

Field Type Description
id UUID Primary key
project_id UUID FK → Project
container_id String? Docker container ID
state Enum Starting / Running / Stopped / Failed
profile String Profile name used
created_at DateTime Creation timestamp
updated_at DateTime Last state change
stopped_at DateTime? When stopped/failed
error_reason String? Error message when Failed

Optimistic concurrency: state updates include a WHERE clause matching the last-known updated_at, preventing lost updates from concurrent writers.

Event Bus Integration

State changes emit RelayEvent::SessionStateChanged events with session ID, new state, and optional error message. These propagate to SSE subscribers for real-time UI updates.