Skip to content

Re-fetch the quasar-images manifest at the pinned ref and refresh the cached catalog.

POST
/v1/admin/images/sync
curl --request POST \
--url http://localhost:8080/v1/admin/images/sync \
--header 'Authorization: Bearer <token>'

Fetches the manifest at instance_settings.image_catalog_ref, validates manifest_version, and upserts the cached catalog. A fetch failure is reported here and surfaced in the UI; it never affects launches (the cached catalog continues to serve). Returns the refreshed catalog.

Catalog refreshed (or served from cache with sync_error set).

Media typeapplication/json
object
manifest_version

The manifest schema version the cached catalog was parsed from.

integer
catalog_ref

The pinned quasar-images ref the catalog was fetched at (instance_settings.image_catalog_ref).

string
fetched_at

When the cached catalog was last successfully refreshed. P3: backed by instance_settings.image_synced_at, so this survives a control-plane restart.

string | null format: date-time
sync_error

Non-null when the last sync failed; the cached catalog is still served. P3: backed by instance_settings.image_sync_error, so this survives a control-plane restart.

string | null
manifest_provenance
One of:

Provenance of the manifest the served catalog was parsed from (#548). Recorded only on a successful sync, in the same transaction as the catalog rows, so it can never describe a manifest other than the one whose rows are stored. Backed by instance_settings.image_manifest_* (schema.md, migration 0070).

object
sha256
required

SHA-256 of the fetched manifest bytes the served catalog was parsed from. Never empty - the envelope carries null instead when nothing has been recorded.

string
previous_sha256
required

The digest recorded before sha256; null until a change has ever been observed.

string | null
commit_sha
required

The resolved upstream commit the manifest was fetched at; null when ref resolution failed and the fetch fell back to the mutable ref.

string | null
ref
required

The configured quasar-images ref the catalog is pinned to (instance_settings.image_catalog_ref).

string
url
required

The URL the manifest bytes were retrieved from.

string
changed
required

The digest moved at the LAST sync. Self-clears on the next unchanged sync; false on a first-ever sync, which has nothing to have changed from.

boolean
changed_at
required

When the digest last moved. Durable - unlike changed, it does not self-clear.

string | null format: date-time
images
required
Array<object>
object
id
required

Stable manifest id; never reused for a different image.

string
display_name
required
string
description
string
kind
required

Prebuilt = pull a registry ref; template = build locally (later phase).

string
Allowed values: prebuilt template
version
required

Upstream’s version for this entry; an update is available when this exceeds installed_version.

string
registry_ref

Pinned image reference for a prebuilt entry (never a floating tag).

string | null
registry_digest

P3 (#440). Content-digest form (name@sha256:<64hex>) resolved from registry_ref’s tag at sync; empty/null when the last sync could not resolve it — installing this image is then refused (409 digest_unresolved) until a later sync resolves it. Install/update adopt this form, never the mutable registry_ref tag.

string | null
artwork
object
key
additional properties
any
library_provider

Set when this image is a library provider’s canonical image (e.g. steam).

string | null
installed
required
boolean
installed_version
string | null
pinned

P3: real. A pinned image is never auto-updated under any policy (auto sync or explicit …/update, both 409 while pinned).

boolean
lazy

P3. True when installed without dispatching ensure-everywhere; hosts pull this image on first launch placement instead.

boolean
runtime_preset_id

P5. The managed runtime_presets row materialized from this image’s manifest runtime block at install; null when not installed or the image carries no runtime block. Makes the installed image launchable.

string | null format: uuid
update_available
boolean
hosts

Per-host presence (present from the phase that ships host_images; may be empty in the read-only P1 surface).

Array<object>
object
host_id
required
string format: uuid
node_name
string
version
string | null
state
required
string
Allowed values: absent pulling building ready failed
error
string | null
bytes
integer | null
Example
{
"images": [
{
"kind": "prebuilt",
"hosts": [
{
"state": "absent"
}
]
}
]
}

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"
}
]
}
}

Authenticated but insufficient role / not the owner (precedes resource lookup).

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"
}
]
}
}