desk-ops

Embed
Preview of the card
<a href="https://forumulate.com/p/gaiadesk/docs/ref-desk-ops"><img src="https://forumulate.com/api/public/orgs/gaiadesk/spaces/docs/pages/296a123f-0d4f-4dec-8a90-f657e63dae10/social-card.svg?v=3e7ae33eec8b" alt="desk-ops" width="600" height="315"></a>
GEThttps://api.gaiadesk.net/v1/desks/{id}/jobs/{name}/logsScope jobs

A job and the end of its output; `?follow=1` streams it over SSE.

With follow=1: Server-Sent Events output, then end (the job as it ended), interrupted (following stopped; the job goes on) or error, each event's data one JobLogEvent object. A failure after the stream started is its last event, error (no job by that name, the desk lost, …), and the call's audit event records that failure, not the 200. Output buffered for a JSON answer is capped at 8 MB.

Parameters

idstringpathrequired
The desk's nine-digit id.
namestringpathrequired
GaiaDesk-E2Estringheader
End-to-end encrypted: the sealed request (ApiE2eRequest, as base64url of its JSON) in place of a plaintext body or parameters. The answer is then sealed too (ApiE2eAnswer, ApiE2eStreamEvent, ApiE2eFrame). A POST may instead send the body {"e2e": …} (ApiE2eBody). See the "End-to-end encryption" section of docs/api/README.md.
wake_sintegerquery
If the desk is asleep, ring it and wait up to this long (default 60, at most 120).
followintegerquery
0 | 1
tailintegerquery
The last this many bytes (default all the desk keeps).

Code samples

cURL
curl -X GET "https://api.gaiadesk.net/v1/desks/{id}/jobs/{name}/logs" \
  -H "Authorization: Bearer $GAIADESK_API_KEY" \
  -H "GaiaDesk-E2E: <value>"
TypeScript
Python
Go
PHP
Ruby
Java

Responses

200 The logs (or, with follow=1, the event stream).
application/json
Example
{
  "job": {
    "by": "",
    "command": "string",
    "ended_at_ms": 0,
    "enforcement": [
      "string"
    ],
    "exit_code": 0,
    "limits": {
      "cpu_percent": 0,
      "keep_awake": true,
      "mem_mb": 0,
      "priority": "string"
    },
    "log_bytes": 0,
    "name": "string",
    "pid": 0,
    "reason": "string",
    "started_at_ms": 0,
    "state": "string"
  },
  "output": "string"
}
JobLogs
jobobjectrequired
A background job on a desk.
Show child parameters
bystring
Who started it: the token's name, or owner for a person.
commandstringrequired
ended_at_msinteger
enforcementstring[]
How the desk enforces them, one line each, in its own words — and what it could not do here (e.g. "cgroups v2 not available: …").
exit_codeinteger
limitsJobLimits
The caps and keep-awake it runs under, as the desk applied them (keep_awake resolved against the desk's default). None from a desk before job limits, and for a job started with none.
Show child parameters
cpu_percentinteger
At most this share of the whole machine's CPU, 1–100.
keep_awakeboolean
Hold off idle sleep while the job runs. None: the desk's default.
mem_mbinteger
At most this much memory for the job and everything it starts, MB.
prioritystring
low, normal or high.
log_bytesinteger
Bytes of output kept (current log plus its one rotated predecessor).
= 0
namestringrequired
pidinteger
reasonstring
Why it ended as it did, as a machine reads it, when the desk knows: blocked_by_os_policy — Windows Smart App Control or WDAC refused to start a program it ran (its state then says so in words).
started_at_msintegerrequired
statestringrequired
running, exited, killed, or lost (it was running when the GaiaDesk host restarted and its end could not be observed).
outputstringrequired
{
  "job": {
    "by": "",
    "command": "string",
    "ended_at_ms": 0,
    "enforcement": [
      "string"
    ],
    "exit_code": 0,
    "limits": {
      "cpu_percent": 0,
      "keep_awake": true,
      "mem_mb": 0,
      "priority": "string"
    },
    "log_bytes": 0,
    "name": "string",
    "pid": 0,
    "reason": "string",
    "started_at_ms": 0,
    "state": "string"
  },
  "output": "string"
}
ApiE2eAnswer
e2eobjectrequired
Show child parameters
vintegerrequired
eventsApiE2eFrame[]required
Show child parameters
seqintegerrequired
noncestringrequired
ciphertextstringrequired
{
  "e2e": {
    "v": 0,
    "events": [
      {
        "seq": 0,
        "nonce": "string",
        "ciphertext": "string"
      }
    ]
  }
}
text/event-stream
{
  "data": "string",
  "event": "string"
}
Headers
X-Request-Idstring
This request's id (req_…), also in every error body. Quote it to support.
RateLimit-Limitinteger
Requests allowed per window for this key.
RateLimit-Remaininginteger
Requests left in the current window.
RateLimit-Resetinteger
Seconds until the window resets.
400 The request is malformed (usage).
Example
{
  "error": {
    "desk": "string",
    "kind": "string",
    "message": "string",
    "reason": "string",
    "request_id": "string",
    "status": 0
  },
  "e2e": {
    "v": 0,
    "events": [
      {
        "seq": 0,
        "nonce": "string",
        "ciphertext": "string"
      }
    ]
  }
}
Schema
errorError & objectrequired
The CLI's error object plus this request's id.
Show child parameters
deskstring
The desk it concerned, when there was one.
kindstringrequired
The six kinds a program branches on. Stable: a new kind would be a breaking change, so match with a wildcard arm anyway.
messagestringrequired
reasonstring
The finer cause: offline, unknown_desk, not_online, network, not_signed_in, timeout, local, …
request_idstringrequired
statusinteger
Only in a held body (GaiaDesk-Held) — the HTTP status this failure would have had, had its 200 not been sent already.
e2eobject
An end-to-end encrypted operation's events; the last opens to the desk's real error (its message here is a placeholder).
Show child parameters
vintegerrequired
eventsApiE2eFrame[]required
Show child parameters
seqintegerrequired
noncestringrequired
ciphertextstringrequired
401 No credential, or one that does not verify (refused, unauthenticated).

Same as 400 · GET /desks/{id}/jobs/{name}/logs

403 The credential lacks the scope, or may not use this API (refused).

Same as 400 · GET /desks/{id}/jobs/{name}/logs

404 No such desk or resource visible to the caller (unreachable, unknown_desk).

Same as 400 · GET /desks/{id}/jobs/{name}/logs

409 The desk is offline (unreachable, with the reach log's reason: silent, closed, …) or could not be woken within wake_s; or it runs a GaiaDesk from before desk operations (protocol, desk_too_old: update GaiaDesk on this desk).

Same as 400 · GET /desks/{id}/jobs/{name}/logs

429 Over this key's rate limit (rate_limited), or the desk already runs 16 API operations (desk_busy).

Same as 400 · GET /desks/{id}/jobs/{name}/logs

Headers
Retry-Afterinteger
Seconds to wait before trying again.
502 The desk went away during the operation (connection_lost), or answered something this server cannot read (protocol).

Same as 400 · GET /desks/{id}/jobs/{name}/logs

503 Desk operations are turned off on this server (unreachable, desk_ops_disabled).

Same as 400 · GET /desks/{id}/jobs/{name}/logs

default The error envelope.

Same as 400 · GET /desks/{id}/jobs/{name}/logs

GEThttps://api.gaiadesk.net/v1/desks/{id}/jobs/{name}/waitScope jobs

Block until a job is no longer running, as `gaiadesk-cli wait` does.

Answers the job as it ended (timed_out: false) or, when timeout ran out first, as it stands, still running (timed_out: true). The job object is what gaiadesk-cli wait --json prints. No such job answers 422 failed, as the CLI does.

A wait is held at most 870 seconds, under the 15-minute limit of every call; wait again for longer. Keep-alive aware: an answer that takes longer than a moment (1.5 s) starts its 200 at once, with GaiaDesk-Held: 1, and holds the body open, sending one space every 15 seconds (whitespace before a JSON value is still JSON) until the outcome, so proxies never see a silent connection. A status cannot change once sent, so a held body is either the result ({"job", "timed_out"}) or, should the desk fail after that point, the error envelope ({"error"}, with error.status the status it would have had): read the body, and treat error as the failure. An answer within the first moments is an ordinary one, its failures real HTTP errors. Either way, the call's audit event records the outcome (ok, failed, …, or cancelled when the caller hung up first), not the 200.

Why not 202 and polling, or 102 Processing: a job is already the resource a caller polls (timeout=0 answers at once), so a 202 would add a second resource for nothing; 102 is a WebDAV interim response that fetch, most HTTP clients and proxies never surface; and trailers are dropped by browsers and most proxies. The held 200 is one request, with the outcome always in a body every client reads.

Parameters

idstringpathrequired
The desk's nine-digit id.
namestringpathrequired
GaiaDesk-E2Estringheader
End-to-end encrypted: the sealed request (ApiE2eRequest, as base64url of its JSON) in place of a plaintext body or parameters. The answer is then sealed too (ApiE2eAnswer, ApiE2eStreamEvent, ApiE2eFrame). A POST may instead send the body {"e2e": …} (ApiE2eBody). See the "End-to-end encryption" section of docs/api/README.md.
wake_sintegerquery
If the desk is asleep, ring it and wait up to this long (default 60, at most 120).
timeoutintegerquery
Give up after this many seconds and answer the job as it stands (default and at most 870; 0 answers at once).

Code samples

cURL
curl -X GET "https://api.gaiadesk.net/v1/desks/{id}/jobs/{name}/wait" \
  -H "Authorization: Bearer $GAIADESK_API_KEY" \
  -H "GaiaDesk-E2E: <value>"
TypeScript
Python
Go
PHP
Ruby
Java

Responses

200 The job as it ended, or as it stands when timeout ran out — or, only on a held answer (GaiaDesk-Held: 1), the error envelope of a failure after the 200 was sent.
Example
{
  "job": {
    "by": "",
    "command": "string",
    "ended_at_ms": 0,
    "enforcement": [
      "string"
    ],
    "exit_code": 0,
    "limits": {
      "cpu_percent": 0,
      "keep_awake": true,
      "mem_mb": 0,
      "priority": "string"
    },
    "log_bytes": 0,
    "name": "string",
    "pid": 0,
    "reason": "string",
    "started_at_ms": 0,
    "state": "string"
  },
  "timed_out": true
}
JobWaitResult
jobobjectrequired
A background job on a desk.
Show child parameters
bystring
Who started it: the token's name, or owner for a person.
commandstringrequired
ended_at_msinteger
enforcementstring[]
How the desk enforces them, one line each, in its own words — and what it could not do here (e.g. "cgroups v2 not available: …").
exit_codeinteger
limitsJobLimits
The caps and keep-awake it runs under, as the desk applied them (keep_awake resolved against the desk's default). None from a desk before job limits, and for a job started with none.
Show child parameters
cpu_percentinteger
At most this share of the whole machine's CPU, 1–100.
keep_awakeboolean
Hold off idle sleep while the job runs. None: the desk's default.
mem_mbinteger
At most this much memory for the job and everything it starts, MB.
prioritystring
low, normal or high.
log_bytesinteger
Bytes of output kept (current log plus its one rotated predecessor).
= 0
namestringrequired
pidinteger
reasonstring
Why it ended as it did, as a machine reads it, when the desk knows: blocked_by_os_policy — Windows Smart App Control or WDAC refused to start a program it ran (its state then says so in words).
started_at_msintegerrequired
statestringrequired
running, exited, killed, or lost (it was running when the GaiaDesk host restarted and its end could not be observed).
timed_outbooleanrequired
{
  "job": {
    "by": "",
    "command": "string",
    "ended_at_ms": 0,
    "enforcement": [
      "string"
    ],
    "exit_code": 0,
    "limits": {
      "cpu_percent": 0,
      "keep_awake": true,
      "mem_mb": 0,
      "priority": "string"
    },
    "log_bytes": 0,
    "name": "string",
    "pid": 0,
    "reason": "string",
    "started_at_ms": 0,
    "state": "string"
  },
  "timed_out": true
}
ApiErrorEnvelope
errorError & objectrequired
The CLI's error object plus this request's id.
Show child parameters
deskstring
The desk it concerned, when there was one.
kindstringrequired
The six kinds a program branches on. Stable: a new kind would be a breaking change, so match with a wildcard arm anyway.
messagestringrequired
reasonstring
The finer cause: offline, unknown_desk, not_online, network, not_signed_in, timeout, local, …
request_idstringrequired
statusinteger
Only in a held body (GaiaDesk-Held) — the HTTP status this failure would have had, had its 200 not been sent already.
e2eobject
An end-to-end encrypted operation's events; the last opens to the desk's real error (its message here is a placeholder).
Show child parameters
vintegerrequired
eventsApiE2eFrame[]required
Show child parameters
seqintegerrequired
noncestringrequired
ciphertextstringrequired
{
  "error": {
    "desk": "string",
    "kind": "string",
    "message": "string",
    "reason": "string",
    "request_id": "string",
    "status": 0
  },
  "e2e": {
    "v": 0,
    "events": [
      {
        "seq": 0,
        "nonce": "string",
        "ciphertext": "string"
      }
    ]
  }
}
ApiE2eAnswer
e2eobjectrequired
Show child parameters
vintegerrequired
eventsApiE2eFrame[]required
Show child parameters
seqintegerrequired
noncestringrequired
ciphertextstringrequired
{
  "e2e": {
    "v": 0,
    "events": [
      {
        "seq": 0,
        "nonce": "string",
        "ciphertext": "string"
      }
    ]
  }
}
Headers
GaiaDesk-Heldstring
1: this 200 was sent before its outcome was known, and the body was held open (keep-alive whitespace) until it was. The body is the result object or the error envelope, never both: check for error.
X-Request-Idstring
This request's id (req_…), also in every error body. Quote it to support.
RateLimit-Limitinteger
Requests allowed per window for this key.
RateLimit-Remaininginteger
Requests left in the current window.
RateLimit-Resetinteger
Seconds until the window resets.
400 The request is malformed (usage).

Same as 400 · GET /desks/{id}/jobs/{name}/logs

401 No credential, or one that does not verify (refused, unauthenticated).

Same as 400 · GET /desks/{id}/jobs/{name}/logs

403 The credential lacks the scope, or may not use this API (refused).

Same as 400 · GET /desks/{id}/jobs/{name}/logs

404 No such desk or resource visible to the caller (unreachable, unknown_desk).

Same as 400 · GET /desks/{id}/jobs/{name}/logs

409 The desk is offline (unreachable, with the reach log's reason: silent, closed, …) or could not be woken within wake_s; or it runs a GaiaDesk from before desk operations (protocol, desk_too_old: update GaiaDesk on this desk).

Same as 400 · GET /desks/{id}/jobs/{name}/logs

429 Over this key's rate limit (rate_limited), or the desk already runs 16 API operations (desk_busy).

Same as 400 · GET /desks/{id}/jobs/{name}/logs

Headers
Retry-Afterinteger
Seconds to wait before trying again.
502 The desk went away during the operation (connection_lost), or answered something this server cannot read (protocol).

Same as 400 · GET /desks/{id}/jobs/{name}/logs

503 Desk operations are turned off on this server (unreachable, desk_ops_disabled).

Same as 400 · GET /desks/{id}/jobs/{name}/logs

default The error envelope.

Same as 400 · GET /desks/{id}/jobs/{name}/logs

Updated