aq.dev / docs/execution-identity

Execution identity

Every process AQ starts in a work item knows which AQ work item, thread, run and organization it belongs to, and who asked for the work. Code that attributes deploys, leases or recoveries to a workspace and a person can read that from the environment, and prove it with a short-lived token AQ signs.

Environment variables

None of these are secrets. They are set in agent processes (every harness, both AQ models and your own subscriptions), terminals, previews and services.

VariableValueWhere
AQ_WORKSPACE_IDThe work item's id (the one in its URL)Everywhere
AQ_WORKSPACE_NAMEThe work item's title (it can change)Terminals, previews, services (for agents, read the identity file)
AQ_SESSION_IDThe AQ thread the agent runs inAgent processes
AQ_TENANT_ID, AQ_ORG_SLUG, AQ_ORG_NAMEThe organizationEverywhere (AQ_ORG_NAME for agents is in the identity file)
AQ_REPO_IDThe repository, when the item has oneEverywhere
AQ_RUN_AS_UID, AQ_RUN_AS_EMAILThe person whose account, sign-ins and secrets the process usesEverywhere
AQ_REQUESTED_BY_UID, AQ_REQUESTED_BY_EMAIL, AQ_REQUESTED_BY_KINDThe person who opened the terminalTerminals (for agents, read the identity file)
AQ_IDENTITY_FILE, AQ_IDENTITY_ENV_FILEThe identity file for the current turn (JSON, and the same values as export lines)Agent processes
AQ_IDENTITY_VERIFY_URL, AQ_IDENTITY_JWKS_URLWhere to check a tokenEverywhere

The identity file: the current turn

An agent process can live across many turns, so values that change per turn are not taken from its environment. Before every turn AQ rewrites the files named by $AQ_IDENTITY_FILE (JSON) and $AQ_IDENTITY_ENV_FILE (shell), readable only by the person the run acts as. They carry everything above plus:

  • AQ_RUN_ID (run_id): this turn.
  • AQ_REQUESTED_BY_UID, AQ_REQUESTED_BY_EMAIL, AQ_REQUESTED_BY_NAME, AQ_REQUESTED_BY_KIND (requested_by): who sent the newest message of the turn. kind is person (typed or spoken in AQ), api (an API key or MCP client), thread (another work item or chat), bot (a bot routine) or system; for anything but a person, on_behalf_of names the person it acts for, and the AQ_REQUESTED_BY_* lines name that person.
  • AQ_IDENTITY_TOKEN (token): the signed token for this turn, valid 4 hours.
set -a; . "$AQ_IDENTITY_ENV_FILE"; set +a
echo "$AQ_WORKSPACE_ID $AQ_RUN_ID $AQ_REQUESTED_BY_EMAIL"
import json, os
identity = json.load(open(os.environ["AQ_IDENTITY_FILE"]))
print(identity["workspace_id"], identity["run_id"], identity["requested_by"])

The token is a credential for its 4 hours: pass it to your own services, never print it or commit it.

Verify instead of trusting the environment

Anything can set an environment variable. The token cannot be forged: it is an ES256 JWT signed by AQ, with iss https://aq.dev and aud aq-execution-identity. Check it either way:

  • Offline with any JWT library against the public keys at https://aq.dev/v1/identity/jwks.json (cache them for a few minutes, refetch on an unknown kid). Check the signature, aud, iss and exp.
  • Online with GET https://aq.dev/v1/identity/verify and the token in the AQ-Identity-Token header (or Authorization: Bearer). No API key is needed. A valid token answers 200 with its claims; anything else answers 401 with the reason. Never put the token in a URL.
curl -s -H "AQ-Identity-Token: $(python3 -c 'import json,os;print(json.load(open(os.environ["AQ_IDENTITY_FILE"]))["token"])')" \
  "$AQ_IDENTITY_VERIFY_URL"

The claims are workspace_id, workspace_name, session_id, run_id, tenant_id, org_slug, repo_id, run_as (user_id, email) and requested_by, plus the standard iat, nbf, exp and jti. Your service should also check that tenant_id is your organization.

Who sent each message

read_item on AQ's MCP server and in agent threads gives every message a sender (the kinds above, the surface it came from, and on whose behalf) beside created_by, the same author the REST API's messages report.

Coming from Conductor

AQ also sets CONDUCTOR_WORKSPACE_PATH, CONDUCTOR_ROOT_PATH, CONDUCTOR_WORKSPACE_NAME and CONDUCTOR_PORT in agent processes, terminals, setup, previews and services, so scripts keep working (migration guide). It never sets CONDUCTOR_IS_LOCAL or anything that claims to be Conductor. For attribution, map:

ConductorAQ
CONDUCTOR_WORKSPACE_IDAQ_WORKSPACE_ID
CONDUCTOR_WORKSPACE_NAME (as a label)AQ_WORKSPACE_NAME (the title); CONDUCTOR_WORKSPACE_NAME stays the branch-derived name
CONDUCTOR_SESSION_IDAQ_SESSION_ID, and run_id for the single turn
The sender of a messagerequested_by in the identity file and token, sender in read_item