Skip to documentation
Documentation navigation

Documentation navigation

Documentation / guides

Session lifecycle endpoints

Create, restore, inspect, and close Sessions over HTTP.

developer

The live lifecycle routes are a create path for a new session and a restore path for an existing durable session. Both attach the returned live session to the handler’s in-process registry before the client can use input, interrupt, gates, or events.

Create

POST /v1/sessions accepts no body, an empty body, {}, or an empty blocks array for an idle session. With blocks, the body is decoded by content.UnmarshalBlocks after the JSON envelope has been read.

RequestStatusResponse
no body, {}, or {"blocks":[]}201{"session_id":"..."}
{"blocks":[...]}201{"session_id":"...","command_id":"..."}
malformed JSON, malformed tagged block, or body over cap400invalid_body
Rig.NewSession failure500internal
Submit failure after attach500internal

The handler validates the optional body before calling Rig.NewSession, so bad input cannot leave an unreachable session. It calls NewSession, registers the session, then submits non-empty blocks. If that submit fails, the session stays registered and the client can use the returned session ID from its own observability path to decide whether to retry input or interrupt; the handler does not detach it as a side effect of a submit error.

type createResult struct {
		SessionID uuid.UUID  `json:"session_id"`
		CommandID *uuid.UUID `json:"command_id,omitempty"`
}

func create(ctx context.Context, baseURL string, blocks []content.Block) (createResult, error) {
	var out createResult
	// The wire body is the same tagged-block envelope accepted by Harness.
	body := struct {
		Blocks []content.Block `json:"blocks,omitempty"`
	}{Blocks: blocks}
	raw, err := json.Marshal(body)
	if err != nil {
		return out, err
	}
	req, err := http.NewRequestWithContext(ctx, http.MethodPost, baseURL+"/v1/sessions", bytes.NewReader(raw))
	if err != nil {
		return out, err
	}
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		return out, err
	}
	defer res.Body.Close()
	if res.StatusCode != http.StatusCreated {
		return serve.createResponse{}, fmt.Errorf("create: HTTP %s", res.Status)
	}
	err = json.NewDecoder(res.Body).Decode(&out)
	return out, err
}

The example uses a local response struct because createResponse is an unexported wire implementation type. Consumer code should decode the documented JSON fields, not depend on unexported serve types.

Restore

POST /v1/sessions/{sid}/restore parses a canonical UUID before calling Rig.RestoreSession.

ConditionStatusResponse code
valid restore200{"session_id":"..."}
malformed {sid}400invalid_parameter
Rig.RestoreSession returns serve.SessionNotFoundError404session_not_found
any other restore failure500internal

On success the returned session is registered under the requested ID. A restore failure never attaches a partial session.

%%{init: {"theme":"dark"}}%%
sequenceDiagram
    participant C as client
    participant H as lifecycle handler
    participant R as Rig
    participant G as live registry
    participant S as LiveSession
    C->>H: POST /v1/sessions
    H->>H: read and validate optional body
    H->>R: NewSession(ctx)
    R-->>H: live session and ID
    H->>G: put(session ID, session)
    opt blocks present
        H->>S: Submit(ctx, blocks)
        S-->>H: command ID
    end
    H-->>C: 201 session_id and optional command_id
    C->>H: POST /v1/sessions/{sid}/restore
    H->>H: parse canonical UUID
    H->>R: RestoreSession(ctx, sid)
    R-->>H: restored live session
    H->>G: put(sid, session)
    H-->>C: 200 session_id

Ordering and failure

The create path’s order matters for recovery. A malformed request is rejected before session creation; a new-session failure does not attach anything; a submit failure leaves the newly-created live session attached. This preserves the session’s ownership and makes the failure observable through later status, events, or a control request rather than silently abandoning resources.

Restore errors are generic at this HTTP boundary because serve deliberately does not import pkg/session. A composition root that wants a 404 must return serve.SessionNotFoundError; otherwise the failure is a 500 and the internal cause is logged only.

Source and runnable proof

go test ./pkg/serve -run 'TestServerHandle(Create|Restore)'

← back to documentation