Files
VerseVDI-Protocol/openapi/control-v1.yaml
T

620 lines
26 KiB
YAML

openapi: 3.1.0
info:
title: VerseVDI control contract
version: 1.0.0
description: Bounded control-plane wire contract. The current SessionRequest shape is control wire version 2; no provider or media endpoints.
servers:
- url: /
tags:
- name: authentication
- name: resources
- name: broker
- name: events
- name: session-quality
- name: gateway
paths:
/api/v1/auth/login:
post:
tags: [authentication]
operationId: loginBrowserSession
requestBody:
required: true
content:
application/json:
schema:
$ref: ../schemas/control-v1.schema.json#/$defs/LoginRequest
responses:
'200':
description: Browser session established through secure cookies; native identity is structurally forbidden.
content:
application/json:
schema:
$ref: ../schemas/control-v1.schema.json#/$defs/BrowserAuthenticatedSession
'400': {$ref: '#/components/responses/InvalidRequest'}
'401': {$ref: '#/components/responses/Unauthorized'}
'503': {$ref: '#/components/responses/Unavailable'}
/api/v1/auth/refresh:
post:
tags: [authentication]
operationId: rotateNativeCredential
requestBody:
required: true
content:
application/json:
schema:
$ref: ../schemas/control-v1.schema.json#/$defs/RefreshRequest
responses:
'200':
description: Rotated opaque native credential.
content:
application/json:
schema:
$ref: ../schemas/control-v1.schema.json#/$defs/NativeCredential
'400': {$ref: '#/components/responses/InvalidRequest'}
'401': {$ref: '#/components/responses/Unauthorized'}
/api/v1/auth/session:
get:
tags: [authentication]
operationId: getAuthenticatedSession
security:
- browserSession: []
- nativeBearer: []
responses:
'200':
description: Route-context-specific authenticated subject summary. Browser responses forbid native identity; native bearer responses require it.
content:
application/json:
schema:
oneOf:
- $ref: ../schemas/control-v1.schema.json#/$defs/BrowserAuthenticatedSession
- $ref: ../schemas/control-v1.schema.json#/$defs/NativeAuthenticatedSession
'401': {$ref: '#/components/responses/Unauthorized'}
/api/v1/auth/tunnel-credentials:
post:
tags: [authentication]
operationId: issueNativeTunnelCredential
security:
- nativeBearer: []
responses:
'200':
description: Short-lived native tunnel credential. The response must not be stored.
headers:
Cache-Control:
description: Prevent storage of the credential response.
required: true
schema: {type: string, const: no-store}
content:
application/json:
schema:
$ref: ../schemas/control-v1.schema.json#/$defs/NativeTunnelCredential
'401': {$ref: '#/components/responses/Unauthorized'}
/api/v1/auth/reauth:
post:
tags: [authentication]
operationId: issueReauthenticationGrant
security:
- browserSession: []
browserCsrfCookie: []
browserCsrfHeader: []
requestBody:
required: true
content:
application/json:
schema:
$ref: ../schemas/control-v1.schema.json#/$defs/ReauthRequest
responses:
'200':
description: Short-lived opaque step-up grant.
content:
application/json:
schema:
$ref: ../schemas/control-v1.schema.json#/$defs/ReauthGrant
'400': {$ref: '#/components/responses/InvalidRequest'}
'401': {$ref: '#/components/responses/Unauthorized'}
/api/v1/auth/logout:
post:
tags: [authentication]
operationId: logoutSession
security:
- browserSession: []
browserCsrfCookie: []
browserCsrfHeader: []
- nativeBearer: []
responses:
'204': {description: Session revoked and browser cookies cleared.}
'401': {$ref: '#/components/responses/Unauthorized'}
/api/v1/auth/devices:
post:
tags: [authentication]
operationId: registerDevice
security:
- browserSession: []
browserCsrfCookie: []
browserCsrfHeader: []
requestBody:
required: true
content:
application/json:
schema:
$ref: ../schemas/control-v1.schema.json#/$defs/DeviceRegistrationRequest
responses:
'201':
description: Single-use device challenge.
content:
application/json:
schema:
$ref: ../schemas/control-v1.schema.json#/$defs/DeviceChallenge
'400': {$ref: '#/components/responses/InvalidRequest'}
'401': {$ref: '#/components/responses/Unauthorized'}
/api/v1/auth/devices/{device_id}/prove:
post:
tags: [authentication]
operationId: proveDevice
security:
- browserSession: []
browserCsrfCookie: []
browserCsrfHeader: []
parameters:
- $ref: '#/components/parameters/DeviceID'
requestBody:
required: true
content:
application/json:
schema:
$ref: ../schemas/control-v1.schema.json#/$defs/DeviceProofRequest
responses:
'200':
description: Opaque native credential.
content:
application/json:
schema:
$ref: ../schemas/control-v1.schema.json#/$defs/NativeCredential
'400': {$ref: '#/components/responses/InvalidRequest'}
'401': {$ref: '#/components/responses/Unauthorized'}
/api/v1/auth/devices/{device_id}:
delete:
tags: [authentication]
operationId: revokeDevice
security:
- browserSession: []
browserCsrfCookie: []
browserCsrfHeader: []
parameters:
- $ref: '#/components/parameters/DeviceID'
responses:
'204': {description: Device and its native authority revoked.}
'401': {$ref: '#/components/responses/Unauthorized'}
'404': {$ref: '#/components/responses/NotFound'}
/api/v1/resources:
get:
tags: [resources]
operationId: listResources
security:
- browserSession: []
- nativeBearer: []
parameters:
- $ref: '#/components/parameters/Limit'
- $ref: '#/components/parameters/Cursor'
responses:
'200':
description: Subject-scoped bounded resources and entitlements.
content:
application/json:
schema:
$ref: ../schemas/control-v1.schema.json#/$defs/ResourceList
'400': {$ref: '#/components/responses/InvalidRequest'}
'401': {$ref: '#/components/responses/Unauthorized'}
/api/v1/broker/sessions:
post:
tags: [broker]
operationId: requestBrokerSession
description: Control wire version 2 endpoint. Legacy version-1 SessionRequest payloads containing client-supplied policy_snapshot are rejected.
security:
- browserSession: []
browserCsrfCookie: []
browserCsrfHeader: []
- nativeBearer: []
parameters:
- $ref: '#/components/parameters/IdempotencyKey'
requestBody:
required: true
content:
application/json:
schema:
$ref: ../schemas/control-v1.schema.json#/$defs/SessionRequest
responses:
'201':
description: Durable broker session.
content:
application/json:
schema:
$ref: ../schemas/control-v1.schema.json#/$defs/BrokerSession
'202':
description: Durable session waiting for an eligible desktop or capacity.
content:
application/json:
schema:
$ref: ../schemas/control-v1.schema.json#/$defs/BrokerSession
'400': {$ref: '#/components/responses/InvalidRequest'}
'401': {$ref: '#/components/responses/Unauthorized'}
'403': {$ref: '#/components/responses/Forbidden'}
'409': {$ref: '#/components/responses/Conflict'}
/api/v1/broker/sessions/{session_id}:
get:
tags: [broker]
operationId: getBrokerSession
security:
- browserSession: []
- nativeBearer: []
parameters:
- $ref: '#/components/parameters/SessionID'
responses:
'200':
description: Subject-scoped broker session projection.
content:
application/json:
schema:
$ref: ../schemas/control-v1.schema.json#/$defs/BrokerSession
'401': {$ref: '#/components/responses/Unauthorized'}
'404': {$ref: '#/components/responses/NotFound'}
/api/v1/broker/sessions/{session_id}/allocate:
post:
tags: [broker]
operationId: allocateBrokerSession
security:
- browserSession: []
browserCsrfCookie: []
browserCsrfHeader: []
- nativeBearer: []
parameters:
- $ref: '#/components/parameters/SessionID'
responses:
'200':
description: Gateway-only launch manifest with a single-use grant.
content:
application/json:
schema:
$ref: ../schemas/control-v1.schema.json#/$defs/ConnectionManifest
'202':
description: Session remains waiting for an eligible desktop or capacity.
content:
application/json:
schema:
$ref: ../schemas/control-v1.schema.json#/$defs/BrokerSession
'401': {$ref: '#/components/responses/Unauthorized'}
'404': {$ref: '#/components/responses/NotFound'}
'409': {$ref: '#/components/responses/Conflict'}
/api/v1/broker/sessions/{session_id}/reconnect:
post:
tags: [broker]
operationId: reconnectBrokerSession
security:
- browserSession: []
browserCsrfCookie: []
browserCsrfHeader: []
- nativeBearer: []
parameters:
- $ref: '#/components/parameters/SessionID'
requestBody:
required: true
content:
application/json:
schema:
$ref: ../schemas/control-v1.schema.json#/$defs/ReconnectRequest
responses:
'200':
description: Gateway-only reconnect manifest with a monotonic sequence.
content:
application/json:
schema:
$ref: ../schemas/control-v1.schema.json#/$defs/ConnectionManifest
'400': {$ref: '#/components/responses/InvalidRequest'}
'401': {$ref: '#/components/responses/Unauthorized'}
'404': {$ref: '#/components/responses/NotFound'}
'409': {$ref: '#/components/responses/Conflict'}
/api/v1/broker/sessions/{session_id}/cancel:
post:
tags: [broker]
operationId: cancelBrokerSession
security:
- browserSession: []
browserCsrfCookie: []
browserCsrfHeader: []
- nativeBearer: []
parameters:
- $ref: '#/components/parameters/SessionID'
responses:
'204': {description: Cancellation accepted idempotently.}
'401': {$ref: '#/components/responses/Unauthorized'}
'404': {$ref: '#/components/responses/NotFound'}
'409': {$ref: '#/components/responses/Conflict'}
/api/v1/session-quality-limits:
get:
tags: [session-quality]
operationId: getSessionQualityLimits
security: [{nativeBearer: []}]
responses:
'200': {description: 'Current global native session quality limits. Maximum JSON body: 16384 bytes.', content: {application/json: {schema: {$ref: ../schemas/control-v1.schema.json#/$defs/SessionQualityLimits}}}}
'401': {$ref: '#/components/responses/Unauthorized'}
/api/v1/session-quality-limits/assignments/{assignment_id}:
get:
tags: [session-quality]
operationId: getAssignmentSessionQualityLimits
security: [{nativeBearer: []}]
parameters: [{$ref: '#/components/parameters/AssignmentID'}]
responses:
'200': {description: 'Current assignment-specific native session quality limits. Maximum JSON body: 16384 bytes.', content: {application/json: {schema: {$ref: ../schemas/control-v1.schema.json#/$defs/SessionQualityLimits}}}}
'401': {$ref: '#/components/responses/Unauthorized'}
'404': {$ref: '#/components/responses/NotFound'}
/api/v1/session-quality-limits/pools/{pool_id}:
get:
tags: [session-quality]
operationId: getPoolSessionQualityLimits
security: [{nativeBearer: []}]
parameters: [{$ref: '#/components/parameters/PoolID'}]
responses:
'200': {description: 'Current pool-specific native session quality limits. Maximum JSON body: 16384 bytes.', content: {application/json: {schema: {$ref: ../schemas/control-v1.schema.json#/$defs/SessionQualityLimits}}}}
'401': {$ref: '#/components/responses/Unauthorized'}
'404': {$ref: '#/components/responses/NotFound'}
/api/v1/admin/entitlements/{entitlement_id}/display-limit-override:
patch:
tags: [session-quality]
operationId: updateEntitlementDisplayLimitOverride
security:
- browserSession: []
browserCsrfCookie: []
browserCsrfHeader: []
parameters: [{$ref: '#/components/parameters/EntitlementID'}]
requestBody: {required: true, description: 'Maximum JSON body: 16384 bytes.', content: {application/json: {schema: {$ref: ../schemas/control-v1.schema.json#/$defs/DisplayLimitOverrideRequest}}}}
responses:
'204': {description: Direct entitlement display override updated.}
'400': {$ref: '#/components/responses/InvalidRequest'}
'401': {$ref: '#/components/responses/Unauthorized'}
'403': {$ref: '#/components/responses/Forbidden'}
'404': {$ref: '#/components/responses/NotFound'}
'409': {$ref: '#/components/responses/Conflict'}
/api/v1/broker/sessions/{session_id}/quality-changes:
post:
tags: [session-quality]
operationId: createSessionQualityChange
description: The owning principal and active device/key MUST match the subject-scoped broker session. Maximum JSON body: 16384 bytes.
security: [{nativeBearer: []}]
parameters: [{$ref: '#/components/parameters/SessionID'}, {$ref: '#/components/parameters/IdempotencyKey'}]
requestBody: {required: true, description: 'Maximum JSON body: 16384 bytes.', content: {application/json: {schema: {$ref: ../schemas/control-v1.schema.json#/$defs/QualityChangeRequest}}}}
responses:
'201': {description: 'Durable quality change created. Maximum JSON body: 16384 bytes.', content: {application/json: {schema: {$ref: ../schemas/control-v1.schema.json#/$defs/QualityChangeOperation}}}}
'200': {description: 'Existing idempotent quality change returned. Maximum JSON body: 16384 bytes.', content: {application/json: {schema: {$ref: ../schemas/control-v1.schema.json#/$defs/QualityChangeOperation}}}}
'400': {$ref: '#/components/responses/InvalidRequest'}
'401': {$ref: '#/components/responses/Unauthorized'}
'404': {$ref: '#/components/responses/NotFound'}
'409': {$ref: '#/components/responses/Conflict'}
/api/v1/broker/sessions/{session_id}/quality-changes/{operation_id}:
get:
tags: [session-quality]
operationId: getSessionQualityChange
description: The owning principal and active device/key MUST match the subject-scoped broker session and operation. Maximum JSON body: 16384 bytes.
security: [{nativeBearer: []}]
parameters: [{$ref: '#/components/parameters/SessionID'}, {$ref: '#/components/parameters/OperationID'}]
responses:
'200': {description: 'Durable quality change state. Maximum JSON body: 16384 bytes.', content: {application/json: {schema: {$ref: ../schemas/control-v1.schema.json#/$defs/QualityChangeOperation}}}}
'401': {$ref: '#/components/responses/Unauthorized'}
'404': {$ref: '#/components/responses/NotFound'}
/api/v1/broker/sessions/{session_id}/stop-operations:
post:
tags: [session-quality]
operationId: createSessionStopOperation
description: The owning principal and active device/key MUST match the subject-scoped broker session. Maximum JSON body: 16384 bytes.
security: [{nativeBearer: []}]
parameters: [{$ref: '#/components/parameters/SessionID'}, {$ref: '#/components/parameters/IdempotencyKey'}]
responses:
'201': {description: 'Durable Stop operation created. Maximum JSON body: 16384 bytes.', content: {application/json: {schema: {$ref: ../schemas/control-v1.schema.json#/$defs/StopOperation}}}}
'200': {description: 'Existing idempotent Stop operation returned. Maximum JSON body: 16384 bytes.', content: {application/json: {schema: {$ref: ../schemas/control-v1.schema.json#/$defs/StopOperation}}}}
'401': {$ref: '#/components/responses/Unauthorized'}
'404': {$ref: '#/components/responses/NotFound'}
'409': {$ref: '#/components/responses/Conflict'}
/api/v1/broker/sessions/{session_id}/stop-operations/{operation_id}:
get:
tags: [session-quality]
operationId: getSessionStopOperation
description: The owning principal and active device/key MUST match the subject-scoped broker session and operation. Maximum JSON body: 16384 bytes.
security: [{nativeBearer: []}]
parameters: [{$ref: '#/components/parameters/SessionID'}, {$ref: '#/components/parameters/OperationID'}]
responses:
'200': {description: 'Durable Stop operation state. Maximum JSON body: 16384 bytes.', content: {application/json: {schema: {$ref: ../schemas/control-v1.schema.json#/$defs/StopOperation}}}}
'401': {$ref: '#/components/responses/Unauthorized'}
'404': {$ref: '#/components/responses/NotFound'}
/api/v1/gateway/quality-work:
post:
tags: [gateway]
operationId: acquireGatewayQualityWork
description: The authenticated gateway mTLS certificate identity MUST match `gateway_id`; Server authority MUST match the session, gateway, reconnect sequence, and supplied operation/revision/lease generation coordinates. A `poll` acquisition omits unknown operation coordinates and discovers pending session work after a lost prompt; `prompt` and `observation` coordinates MUST match exactly. Maximum JSON body: 16384 bytes.
security: [{gatewayMutualTLS: []}]
requestBody: {required: true, description: 'Maximum JSON body: 16384 bytes.', content: {application/json: {schema: {$ref: ../schemas/control-v1.schema.json#/$defs/GatewayQualityWorkRequest}}}}
responses:
'200': {description: 'Leased quality work or reconciliation observation. Maximum JSON body: 16384 bytes.', content: {application/json: {schema: {$ref: ../schemas/control-v1.schema.json#/$defs/GatewayQualityWork}}}}
'204': {description: No matching work is available.}
'400': {$ref: '#/components/responses/InvalidRequest'}
'401': {$ref: '#/components/responses/Unauthorized'}
'409': {$ref: '#/components/responses/Conflict'}
/api/v1/gateway/quality-ack:
post:
tags: [gateway]
operationId: acknowledgeGatewayQualityWork
description: The authenticated gateway mTLS certificate identity MUST match `gateway_id`; Server authority MUST exactly match the session, gateway, reconnect sequence, operation, revision, and monotonic lease generation tuple. Stale lease generations MUST be rejected. Maximum JSON body: 16384 bytes.
security: [{gatewayMutualTLS: []}]
requestBody: {required: true, description: 'Maximum JSON body: 16384 bytes.', content: {application/json: {schema: {$ref: ../schemas/control-v1.schema.json#/$defs/GatewayQualityAck}}}}
responses:
'204': {description: Quality acknowledgement committed idempotently.}
'400': {$ref: '#/components/responses/InvalidRequest'}
'401': {$ref: '#/components/responses/Unauthorized'}
'409': {$ref: '#/components/responses/Conflict'}
/api/v1/gateway/stop-work:
post:
tags: [gateway]
operationId: acquireGatewayStopWork
description: The authenticated gateway mTLS certificate identity MUST match `gateway_id`; Server authority MUST match the session, gateway, reconnect sequence, and any supplied operation coordinate. A `poll` acquisition omits the unknown operation ID and discovers pending session work after a lost prompt; a `prompt` coordinate MUST match exactly. Maximum JSON body: 16384 bytes.
security: [{gatewayMutualTLS: []}]
requestBody: {required: true, description: 'Maximum JSON body: 16384 bytes.', content: {application/json: {schema: {$ref: ../schemas/control-v1.schema.json#/$defs/GatewayStopWorkRequest}}}}
responses:
'200': {description: 'One-way non-reissued Stop work claim. Maximum JSON body: 16384 bytes.', content: {application/json: {schema: {$ref: ../schemas/control-v1.schema.json#/$defs/GatewayStopWork}}}}
'204': {description: No matching Stop work is available.}
'400': {$ref: '#/components/responses/InvalidRequest'}
'401': {$ref: '#/components/responses/Unauthorized'}
'409': {$ref: '#/components/responses/Conflict'}
/api/v1/gateway/stop-ack:
post:
tags: [gateway]
operationId: acknowledgeGatewayStopWork
description: The authenticated gateway mTLS certificate identity MUST match `gateway_id`; Server authority MUST exactly match the session, gateway, reconnect sequence, and operation tuple. Maximum JSON body: 16384 bytes.
security: [{gatewayMutualTLS: []}]
requestBody: {required: true, description: 'Maximum JSON body: 16384 bytes.', content: {application/json: {schema: {$ref: ../schemas/control-v1.schema.json#/$defs/GatewayStopAck}}}}
responses:
'204': {description: Stop acknowledgement committed idempotently.}
'400': {$ref: '#/components/responses/InvalidRequest'}
'401': {$ref: '#/components/responses/Unauthorized'}
'409': {$ref: '#/components/responses/Conflict'}
/api/v1/events:
get:
tags: [events]
operationId: resumeUserEvents
security:
- browserSession: []
- nativeBearer: []
parameters:
- name: version
in: query
required: false
schema: {type: string, enum: ['1']}
- name: cursor
in: query
required: false
schema: {type: string, maxLength: 512}
- name: Upgrade
in: header
required: false
schema: {type: string, enum: [websocket]}
- name: Sec-WebSocket-Version
in: header
required: false
schema: {type: string, enum: ['13']}
responses:
'200':
description: Bounded resumable NDJSON event snapshot; WebSocket upgrades emit the same envelopes as text frames.
headers:
X-VerseVDI-Event-Cursor:
schema: {type: string, maxLength: 512}
content:
application/x-ndjson:
schema:
$ref: ../schemas/control-v1.schema.json#/$defs/EventEnvelope
'400': {$ref: '#/components/responses/InvalidRequest'}
'401': {$ref: '#/components/responses/Unauthorized'}
'409': {$ref: '#/components/responses/Conflict'}
'426': {description: WebSocket version 13 is required.}
'503': {$ref: '#/components/responses/Unavailable'}
components:
securitySchemes:
browserSession:
type: apiKey
in: cookie
name: versevdi_session
browserCsrfCookie:
type: apiKey
in: cookie
name: versevdi_csrf
description: Must be identical to X-CSRF-Token and is checked against Server session state.
browserCsrfHeader:
type: apiKey
in: header
name: X-CSRF-Token
description: Must be identical to the versevdi_csrf cookie and is checked against Server session state.
nativeBearer:
type: http
scheme: bearer
bearerFormat: opaque
gatewayMutualTLS:
type: mutualTLS
parameters:
DeviceID:
name: device_id
in: path
required: true
schema: {type: string, minLength: 1, maxLength: 128}
SessionID:
name: session_id
in: path
required: true
schema: {type: string, minLength: 1, maxLength: 128}
AssignmentID:
name: assignment_id
in: path
required: true
schema: {type: string, minLength: 1, maxLength: 128}
PoolID:
name: pool_id
in: path
required: true
schema: {type: string, minLength: 1, maxLength: 128}
EntitlementID:
name: entitlement_id
in: path
required: true
schema: {type: string, minLength: 1, maxLength: 128}
OperationID:
name: operation_id
in: path
required: true
schema: {type: string, format: uuid, pattern: '^(?!00000000-0000-0000-0000-000000000000$)[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$'}
Limit:
name: limit
in: query
required: false
schema: {type: integer, minimum: 1, maximum: 100, default: 100}
Cursor:
name: cursor
in: query
required: false
schema: {type: string, maxLength: 512}
IdempotencyKey:
name: Idempotency-Key
in: header
required: true
schema: {type: string, minLength: 1, maxLength: 256}
responses:
InvalidRequest:
description: The request is invalid.
content:
application/json:
schema:
$ref: ../schemas/control-v1.schema.json#/$defs/ErrorEnvelope
Unauthorized:
description: Authentication or credential validation failed without revealing credential state.
content:
application/json:
schema:
$ref: ../schemas/control-v1.schema.json#/$defs/ErrorEnvelope
Forbidden:
description: The authenticated subject is not entitled to the requested action.
content:
application/json:
schema:
$ref: ../schemas/control-v1.schema.json#/$defs/ErrorEnvelope
NotFound:
description: The resource is unavailable to this subject.
content:
application/json:
schema:
$ref: ../schemas/control-v1.schema.json#/$defs/ErrorEnvelope
Conflict:
description: The expected version, protocol version, idempotency, or event retention boundary conflicts.
content:
application/json:
schema:
$ref: ../schemas/control-v1.schema.json#/$defs/ErrorEnvelope
Unavailable:
description: The authoritative service is temporarily unavailable.
content:
application/json:
schema:
$ref: ../schemas/control-v1.schema.json#/$defs/ErrorEnvelope