Skip to content

Node-agent internal: claim pending Steam library scans for this host. (Not operator-facing.)

GET
/v1/agent/library/scan-pending
curl --request GET \
--url http://localhost:8080/v1/agent/library/scan-pending \
--header 'Authorization: Bearer <token>'

Phase 4. Claims up to 50 pending scans for the calling host with FOR UPDATE SKIP LOCKED, so two agents polling simultaneously get DISJOINT sets rather than blocking on each other. An empty list is the steady state, and is also what this returns when discovery is switched off - the master switch is re-read HERE as well as in the scheduler, because the scheduler is what stops rows being created and this is what stops an already-queued row being handed out after an operator turned the feature off. relative_roots and the two bounds are control-plane-supplied rather than agent constants, so a bound can be tightened without an agent release. root_path is control-plane-supplied and the agent validates containment against its own configured homes root before walking it - a path outside that root is refused and reported as an error, never walked.

OK.

Media typeapplication/json
object
scans
required

Empty is the steady state, and is also the answer when discovery is switched off.

Array<object>

One scan job, exactly as it goes on the wire. NOTE WHAT IS ABSENT: no user id, no username, no user-derived field. root_path is an opaque home directory and which person it belongs to is resolved control-plane-side on receipt of the report and never leaves that process. This is agent-api.md’s P2-01 verdict - per-user concerns never reach the agent - honoured literally, and it is a GUARANTEE OF THE INTERFACE rather than a property of the current implementation.

object
scan_id
required

The only handle the agent ever holds.

string format: uuid
root_path
required

The absolute host path of the user’s managed home. Control-plane-supplied, so the agent VALIDATES CONTAINMENT against its own configured homes root before walking it: a path outside that root is refused and reported as an error, never walked. The agent does not trust the control plane with a filesystem path any more than it would trust a client.

string
relative_roots
required

In-home paths to glob for appmanifest_*.acf, today the flatpak/deb (.local/share/Steam/steamapps) and classic (.steam/steam/steamapps) layouts, which are usually symlinks to each other and are de-duplicated by resolved path. libraryfolders.vdf is deliberately NOT consulted: it records CONTAINER-side paths, meaningless to a host-side scanner, so translating it would be guesswork - libraries on other disks are an accepted limitation instead.

Array<string>
max_entries
required

Cap on manifests per scan (512 today).

integer
max_manifest_bytes
required

Per-file read cap (1 MiB today). Both bounds are CONTROL-PLANE-OWNED rather than agent constants, so they can be tightened without an agent release.

integer format: int64
Examplegenerated
{
"scans": [
{
"scan_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0",
"root_path": "example",
"relative_roots": [
"example"
],
"max_entries": 1,
"max_manifest_bytes": 1
}
]
}

Missing/invalid/expired/revoked token.

Media typeapplication/json
object
error
required
object
code
required

E.g. validation_failed, unauthorized, forbidden, not_found, conflict, session_quota_exceeded, home_in_use, home_not_provisioned, parent_app_disabled, profile_ineligible, profile_not_launchable_for_app, no_host_available, capacity_exhausted, restart_required, rate_limited, internal. Open string, not an enum: new codes are additive and an unknown one falls through to a client’s generic per-status branch.

string
message
required
string
live_sessions

Present on restart_required.

integer
session_id

Steam library discovery Phase 3, ADDITIVE: present on home_in_use when the guard could name the CONFLICTING live session - the one already holding the home. It is here so the client can offer “go to your running session” with a link instead of a dead-end toast. OMITTED rather than empty when the conflict is known but the session is not, so a client branches on presence and never renders a link to nowhere. Load-bearing once derived tiles exist: the lock is held by the PARENT’s home, so a user who clicks a game tile can be refused because a DIFFERENT app (the Steam launcher, or another game from the same install) is running, and without the session id the refusal reads as a bug. See control-api.md §Derived tiles.

string format: uuid
derived_tiles

Steam library discovery Phase 3, ADDITIVE: present on the 409 conflict from DELETE /v1/apps/{id} when the app has derived tiles and ?delete_derived=true was not sent. A LIST, not a count - the point of the confirmation is that the admin sees what they are about to destroy. Capped; an empty array means the tiles could not be listed, never that there are none.

Array<object>
object
id
required
string format: uuid
name
required
string
Examplegenerated
{
"error": {
"code": "example",
"message": "example",
"live_sessions": 1,
"session_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0",
"derived_tiles": [
{
"id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0",
"name": "example"
}
]
}
}

Could not list pending scans.

Media typeapplication/json
object
error
required
object
code
required

E.g. validation_failed, unauthorized, forbidden, not_found, conflict, session_quota_exceeded, home_in_use, home_not_provisioned, parent_app_disabled, profile_ineligible, profile_not_launchable_for_app, no_host_available, capacity_exhausted, restart_required, rate_limited, internal. Open string, not an enum: new codes are additive and an unknown one falls through to a client’s generic per-status branch.

string
message
required
string
live_sessions

Present on restart_required.

integer
session_id

Steam library discovery Phase 3, ADDITIVE: present on home_in_use when the guard could name the CONFLICTING live session - the one already holding the home. It is here so the client can offer “go to your running session” with a link instead of a dead-end toast. OMITTED rather than empty when the conflict is known but the session is not, so a client branches on presence and never renders a link to nowhere. Load-bearing once derived tiles exist: the lock is held by the PARENT’s home, so a user who clicks a game tile can be refused because a DIFFERENT app (the Steam launcher, or another game from the same install) is running, and without the session id the refusal reads as a bug. See control-api.md §Derived tiles.

string format: uuid
derived_tiles

Steam library discovery Phase 3, ADDITIVE: present on the 409 conflict from DELETE /v1/apps/{id} when the app has derived tiles and ?delete_derived=true was not sent. A LIST, not a count - the point of the confirmation is that the admin sees what they are about to destroy. Capped; an empty array means the tiles could not be listed, never that there are none.

Array<object>
object
id
required
string format: uuid
name
required
string
Examplegenerated
{
"error": {
"code": "example",
"message": "example",
"live_sessions": 1,
"session_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0",
"derived_tiles": [
{
"id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0",
"name": "example"
}
]
}
}