# Execution identity

> How code running in AQ learns which work item, thread, run and organization it belongs to and who asked for the work, and how it verifies that with a signed token.

Published November 10, 2026 · by the AQ team · Canonical: https://aq.dev/docs/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.

| Variable | Value | Where |
| --- | --- | --- |
| `AQ_WORKSPACE_ID` | The work item's id (the one in its URL) | Everywhere |
| `AQ_WORKSPACE_NAME` | The work item's title (it can change) | Terminals, previews, services (for agents, read the identity file) |
| `AQ_SESSION_ID` | The AQ thread the agent runs in | Agent processes |
| `AQ_TENANT_ID`, `AQ_ORG_SLUG`, `AQ_ORG_NAME` | The organization | Everywhere (`AQ_ORG_NAME` for agents is in the identity file) |
| `AQ_REPO_ID` | The repository, when the item has one | Everywhere |
| `AQ_RUN_AS_UID`, `AQ_RUN_AS_EMAIL` | The person whose account, sign-ins and secrets the process uses | Everywhere |
| `AQ_REQUESTED_BY_UID`, `AQ_REQUESTED_BY_EMAIL`, `AQ_REQUESTED_BY_KIND` | The person who opened the terminal | Terminals (for agents, read the identity file) |
| `AQ_IDENTITY_FILE`, `AQ_IDENTITY_ENV_FILE` | The identity file for the current turn (JSON, and the same values as `export` lines) | Agent processes |
| `AQ_IDENTITY_VERIFY_URL`, `AQ_IDENTITY_JWKS_URL` | Where to check a token | Everywhere |

## 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](https://aq.dev/docs/mcp/) 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](https://aq.dev/guides/migrate-from-conductor-to-aq/)). It never sets `CONDUCTOR_IS_LOCAL` or anything that claims to be Conductor. For attribution, map:

| Conductor | AQ |
| --- | --- |
| `CONDUCTOR_WORKSPACE_ID` | `AQ_WORKSPACE_ID` |
| `CONDUCTOR_WORKSPACE_NAME` (as a label) | `AQ_WORKSPACE_NAME` (the title); `CONDUCTOR_WORKSPACE_NAME` stays the branch-derived name |
| `CONDUCTOR_SESSION_ID` | `AQ_SESSION_ID`, and `run_id` for the single turn |
| The sender of a message | `requested_by` in the identity file and token, `sender` in `read_item` |

---

About AQ: AQ is the multiplayer coding harness where teams build and run bots with every AI coding agent, like Claude Code and Codex: reviewed pull requests, live app previews, Genie and standing jobs, on private cloud machines of their own. Start at [aq.dev](https://aq.dev/).
