Skip to content

Mint a throwaway, auto-reaped ephemeral identity + token for automated UI validation (dev-gated;

POST
/v1/dev/agent-session
curl --request POST \
--url http://localhost:8080/v1/dev/agent-session \
--header 'Content-Type: application/json' \
--header 'X-Quasar-Dev-Key: example' \
--data '{ "role": "user", "ttl_seconds": 1800 }'

Registered ONLY when QUASAR_DEV_AGENT_AUTH=1; absent the flag the route does not exist (404), and the control plane refuses to boot with the flag set while QUASAR_ENV=production. Auth is the per-boot dev key (written to the container log and /run/quasar/dev-agent-key), not a bearer token. Response is shape-compatible with POST /v1/auth/login so every existing client path works unchanged. The minted identity is a REAL user row with a real token — server-side role enforcement is untouched — and is deleted (with its sessions and device bindings) when its TTL lapses.

X-Quasar-Dev-Key
required
string

The per-boot dev secret. Wrong or missing → 401 with no message/timing distinction.

Media typeapplication/json
object
role

Role=admin mints a REAL admin (same enforcement path); the server logs it at WARN with the source address.

string
default: user
Allowed values: user admin
ttl_seconds

Identity lifetime. Default 30 min, hard cap 8 h. The token TTL is clamped to this — a token cannot outlive its user.

integer
default: 1800 >= 60 <= 28800

Ephemeral identity minted (login-shape + storage_keys).

Media typeapplication/json
object
access_token
required

The only credential ever returned — the identity’s password is random and never exposed.

string
token_type
required
string
Allowed values: Bearer
expires_at
required

Token AND identity expiry; the reaper deletes the user at/after this instant.

string format: date-time
user
required
object
id
required
string format: uuid
email
required
string format: email
username
required
string
role
required
string
Allowed values: user admin
created_at

Present on /v1/me + register; omitted from the login user.

string format: date-time
storage_keys
required

Convenience: the three web-SPA localStorage entries (quasar.auth.token / quasar.auth.expires_at / quasar.auth.user) ready to inject.

object
key
additional properties
string
Example
{
"token_type": "Bearer",
"user": {
"role": "user"
}
}

Malformed or invalid request.

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

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