./bai CLI Usage Guide
Guide for testing and verifying API endpoints and managing resources with the Backend.AI v2 CLI (./bai).
IMPORTANT:
./bai is the v2 REST API CLI. It is separate from the legacy v1 CLI (backend.ai / ./backend.ai).
- Do NOT use v1 CLI commands. Do all testing and verification with
./bai.
- Before running a
./bai command, confirm the command exists in the Entity-Command Reference below. No guessing or fabrication, no searching the CLI source.
- Verify the command tree with
--help (works without a server): ./bai {entity} --help, ./bai admin {entity} --help, ./bai my {entity} --help.
v1 → v2 terminology
| v1 (legacy, do NOT use) |
v2 (./bai) |
group |
project |
backend.ai vfolder list |
./bai vfolder my-search |
backend.ai admin vfolders |
./bai vfolder admin-search |
backend.ai ps |
./bai my session search |
Entity-Command Reference
Syntax: ./bai [admin|my] {entity} [{sub-entity}] {command} [options]
Each entity is marked by access level — user (user-facing) / admin (superadmin only) / my (own resources).
"(empty group)" is a placeholder group with no commands (example: ./bai agent is empty and the actual commands are under ./bai admin agent).
Check options with --help.
Core
- domain: user(get) · admin(search, create, update, delete, purge)
- user: user(get, create, update, delete, search) · admin(create, delete, search)
- project: user(get, assign-users, unassign-users · sub role: search) · admin(search, create, update, delete, purge)
- agent: user(empty group) · admin(search, total-resources, update-resource-group)
- image: user(empty group) · admin(search, forget, purge, update · sub alias: create, remove, search)
- entity-label: user(upsert, purge, search)
- session: user(compute-schedule, enqueue, exclude-idle-checks, include-idle-checks, get, logs, project-search, start-service, shutdown-service, terminate, update) · admin(search · sub kernel: search) · my(search)
Compute & Serving
- deployment: user(create, get, update, delete, project-search, chat) · admin(search) · my(search)
- user sub: access-token(create, get, search, delete, bulk-delete), auto-scaling-rule(create, get, search, update, delete, bulk-delete), replica(search), revision(add, get, current, activate, search), revision-preset(get, search), options(get, replace), chat-cache(show, clear), chat-config(set, show, clear), chat-history(show, clear), policy(empty group)
- admin sub: policy(search), replica(search), revision(search, refresh), revision-preset(create, get, search, update, delete)
- model-card: user(project-search, get, deploy, available-presets) · admin(search, get, create, update, delete, bulk-delete, scan)
- service-catalog: user(empty group) · admin(search)
- runtime-variant: user(get, search) · admin(get, search, create, update, delete, bulk-delete)
- runtime-variant-preset: user(get, search) · admin(get, search, create, update, delete)
- scheduling-history: sub session / kernel / deployment / route / replica-group — each (search, search-scoped)
- scheduling-handler: admin(list)
Storage
- vfolder: user(my-search, project-search, admin-search, create, project-create, get, upload, download, delete, purge, restore, ls, mkdir, mv, rm, clone, deploy, bulk-delete, bulk-purge)
- vfs-storage: user(create, get, search, list-all, update, delete)
- storage-namespace: user(register, unregister, search, get-by-storage)
- object-storage: user(create, get, search, update, delete)
- storage-host: my(permissions)
Registries & Artifacts
- container-registry: user(empty group) · admin(search, create, update, delete)
- artifact: user(get, update, delete, restore · sub revision: get, approve, reject, cancel-import, cleanup) · admin(search)
- artifact-registry: user(get)
- huggingface-registry: user(create, get, search, update, delete) — no admin variant
- reservoir-registry: user(create, get, search, update, delete)
Access Control & Auth
- entity-type: user(list)
- rbac: sub assignment(assign, revoke, search), entity(search), permission(search),
invitation(create, accept, reject, cancel, my-search, my-sent-search, role-search),
role(create, get, search, update, delete, project-search, add-permission, remove-permission, replace-permission)
- role: my(search)
- role-preset: admin(create, get, search, update, delete, purge, restore, permission-add, permission-remove, permission-search)
- invitation: admin(search)
- keypair: admin(create, get, search, update, delete · sub ssh: register, get, delete) · my(issue, revoke, search, update, switch-main)
- login-history: admin(search) · my(search)
- login-session: admin(search, revoke) · my(search, revoke)
- login-client-type: user(get) · admin(search, create, update, delete)
Resource Management
- resource-group: user(empty group) · admin(search, get, create, update, delete, resource-info, default-options, default-session-options, allow-domains, allowed-domains, allow-projects, allowed-projects, allow-for-domain, allowed-for-domain, allow-for-project, allowed-for-project)
- resource-allocation: user(project-usage, resource-group-usage) · admin(domain-usage, effective) · my(effective, keypair-usage)
- resource-preset: admin(search, get, create, update, delete, check-availability)
- resource-policy: admin(sub keypair / project / user — each create, get, search, update, delete) · my(keypair, user)
- resource-slot: sub slot-type(search, create, update, delete), agent-resource(search), allocation(search)
- resource-usage: sub domain(search), project(search), user(search)
Monitoring & Audit
- audit-log: user(search)
- fair-share: sub domain / project / user — each (get, search)
- idle-checker-assignment: user(scoped-search, update, purge) · admin(create, search)
- notification: sub channel(get, search, delete), rule(get, search, delete)
- prometheus-query-definition: user(get, search, execute) · admin(create, update, delete, preview)
- prometheus-query-definition-category: user(get, search) · admin(create, delete)
- app-config: my(get) · public(get) — the merged view;
public is an app-config-only group for the pre-login read, needing no credentials
- app-config-definition: admin(create, get, search, purge)
- app-config-allow-list: admin(create, get, search, update, purge)
- app-config-fragment: user(get, update, purge, bulk-purge) · my(get, update) · admin(search)
- idle-checker: admin(create, search, update, purge)
- export: admin(list-reports, get-report, audit-logs, keypairs, projects, sessions, sessions-by-project, users, users-by-domain) · my(keypairs, sessions)
Utilities (not entities)
login, logout, config, gql — single commands at the root.
When adding a new CLI command, update this Reference as well (see "Adding a new entity" in client/cli/v2/AGENTS.md).
Setup (Webserver session — recommended)
./bai config set endpoint http://127.0.0.1:8090
./bai config set endpoint-type session
./bai login
# User ID: admin@lablup.com
# Password: (admin password)
./bai config show
Non-interactive environments (CI, Claude Code):
BACKEND_USER=admin@lablup.com BACKEND_PASSWORD=wJalrXUt ./bai login
The session cookie is stored in ~/.backend.ai/session/cookie.dat.
Direct API (alternative)
Access the manager directly without the webserver (HMAC signature auth):
./bai config set endpoint http://127.0.0.1:8091
./bai config set endpoint-type api
./bai config set access-key AKIAIOSFODNN7EXAMPLE
./bai config set secret-key wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
./bai config show
The configuration is stored in ~/.backend.ai/config.toml and credentials.toml.
Command pattern
./bai [admin|my] {entity} [{sub-entity}] {command} [options]
admin — superadmin-only operations
my — self-service (the current user's own resources)
- Entity names are singular (domain, user, agent)
- A sub-entity is a Click sub-group (revision, channel, role, etc.)
The standard 6 operations: create, get, search, update, delete, purge (only some, depending on the entity).
Special operations: enqueue/terminate (session), revision add/revision activate (deployment), login/logout.
CLI input style
- Default: an individual
--option flag per field.
- Secondary: a JSON string or an
@file path for complex nested structures (example: --initial-revision, --config).
- Do NOT use raw JSON as a positional argument for create/update (with some admin command exceptions:
admin domain create).
- For get/delete/purge, use the entity identifier (UUID, name) as a positional argument.
Search patterns
# admin search — whole system (superadmin only)
./bai admin {entity} search --limit 5
# project-scoped search — the scope ID is a positional argument (not an option)
./bai {entity} project-search {project_id} --limit 5
# self-service search — own resources
./bai my {entity} search --limit 5
Options and filters differ per entity, so check with ./bai {entity} {command} --help.
--order-by syntax
Multi-sort with field:direction:
./bai admin user search --order-by created_at:desc --order-by username:asc
Naming conventions
- The CLI
--order-by maps to the DTO order field (common across all entities).
- The CLI
--kebab-case option maps to the DTO snake_case field (Click standard).
- Scoped search: the scope ID is a positional argument, not a
--scope-* option.
Raw GraphQL
./bai gql (not ./bai admin gql) sends a raw GraphQL query. Useful for testing GQL schema changes or when there is no REST CLI:
./bai gql '{ domain(name: "default") { name } }'
./bai gql --v2 '{ myKeypairs(first: 5) { count edges { node { accessKey } } } }'
./bai gql -f query.graphql
./bai gql --var limit=5 '{ keypair_list(limit: $limit) { items { access_key } } }'
--v2: targets the Strawberry v2 schema (only needed in direct API mode; session mode provides both)
--var key=value: query variables (repeatable)
-f file: read the query from a file
- stdin:
echo '{ ... }' | ./bai gql
Common commands
# admin search + filter (superadmin only)
./bai admin domain search --limit 5 --name-contains default
./bai admin user search --status active --order-by created_at:desc
./bai admin agent search --limit 10
./bai admin image search --name-contains python
./bai admin session search --limit 5
# single entity lookup
./bai domain get default
./bai user get <uuid>
# sub-entity operations
./bai admin deployment revision search
./bai rbac role search
./bai notification channel search
./bai scheduling-history session search --limit 10
Testing workflow
Prerequisites
./bai config show # check endpoint-type
./bai login # log in if the session expired
After modifying server code
For local development, restart the service first — see the /local-dev skill.
# 1. confirm basic connectivity
./bai admin domain search --limit 1
./bai domain get default
# 2. test the modified entity (with the command matching its level)
# if user-facing ./bai {entity} ..., if admin-only ./bai admin {entity} ...
./bai admin {entity} search --limit 1
./bai {entity} get {id}
# 3. test the permission boundary
./bai admin {entity} search # admin succeeds
# after switching to a regular user, the same command → should fail with 403
After commands, verify the runtime behavior with the Grafana MCP — see /observability.
To catch errors not surfaced in the CLI response, check Loki ({service_name="manager"} |= "error");
to check the request count, look at Prometheus (backendai_api_request_count).
Testing as a regular user
The default accounts are in fixtures/manager/example-users.json.
# log in as a regular user session
BACKEND_USER=user@lablup.com BACKEND_PASSWORD=C8qnIo29 ./bai login
# should succeed (user-facing)
./bai domain get default
# should fail with 403 (admin only)
./bai admin domain search --limit 1
After testing, revert to the admin credentials.
Smoke test script
Run everything with admin credentials. OK if each command returns JSON.
for cmd in \
"admin domain search --limit 1" \
"domain get default" \
"admin user search --limit 1" \
"admin project search --limit 1" \
"admin agent search --limit 1" \
"admin image search --limit 1" \
"admin session search --limit 1" \
"admin resource-group search --limit 1" \
"audit-log search --limit 1" \
"rbac role search --limit 1"; do
echo -n "$cmd: "
./bai $cmd 2>&1 | python3 -c "import sys,json;json.load(sys.stdin);print('OK')" 2>&1 || echo "FAIL"
done
When a CLI command is unavailable
If the CLI command for an entity/operation is not in the Reference above:
- That command is not implemented.
- Report to the user: "{entity} {operation} is not provided via the CLI. It needs to be implemented."
- As a temporary workaround, try
./bai gql for operations available via GraphQL.
- Do NOT guess CLI options or fabricate commands — it wastes time and causes errors.
Related skills
/local-dev — restart local services before CLI testing
/observability — check logs/metrics with the Grafana MCP after CLI testing
/cli-sdk-guide — implement new CLI commands
1---2name: bai-cli3description: Bai Cli4---56# ./bai CLI Usage Guide78Guide for testing and verifying API endpoints and managing resources with the Backend.AI **v2 CLI** (`./bai`).910**IMPORTANT:**11- `./bai` is the v2 REST API CLI. It is separate from the legacy v1 CLI (`backend.ai` / `./backend.ai`).12- Do NOT use v1 CLI commands. Do all testing and verification with `./bai`.13- **Before running a `./bai` command, confirm the command exists in the Entity-Command Reference below.** No guessing or fabrication, no searching the CLI source.14- Verify the command tree with `--help` (works without a server): `./bai {entity} --help`, `./bai admin {entity} --help`, `./bai my {entity} --help`.1516### v1 → v2 terminology1718| v1 (legacy, do NOT use) | v2 (`./bai`) |19|--------------------------|-------------|20| `group` | `project` |21| `backend.ai vfolder list` | `./bai vfolder my-search` |22| `backend.ai admin vfolders` | `./bai vfolder admin-search` |23| `backend.ai ps` | `./bai my session search` |2425---2627## Entity-Command Reference2829Syntax: `./bai [admin|my] {entity} [{sub-entity}] {command} [options]`3031Each entity is marked by access level — **user** (user-facing) / **admin** (superadmin only) / **my** (own resources).32"(empty group)" is a placeholder group with no commands (example: `./bai agent` is empty and the actual commands are under `./bai admin agent`).33Check options with `--help`.3435### Core3637- **domain**: user(get) · admin(search, create, update, delete, purge)38- **user**: user(get, create, update, delete, search) · admin(create, delete, search)39- **project**: user(get, assign-users, unassign-users · sub role: search) · admin(search, create, update, delete, purge)40- **agent**: user(empty group) · admin(search, total-resources, update-resource-group)41- **image**: user(empty group) · admin(search, forget, purge, update · sub alias: create, remove, search)42- **entity-label**: user(upsert, purge, search)43- **session**: user(compute-schedule, enqueue, exclude-idle-checks, include-idle-checks, get, logs, project-search, start-service, shutdown-service, terminate, update) · admin(search · sub kernel: search) · my(search)4445### Compute & Serving4647- **deployment**: user(create, get, update, delete, project-search, chat) · admin(search) · my(search)48 - user sub: access-token(create, get, search, delete, bulk-delete), auto-scaling-rule(create, get, search, update, delete, bulk-delete), replica(search), revision(add, get, current, activate, search), revision-preset(get, search), options(get, replace), chat-cache(show, clear), chat-config(set, show, clear), chat-history(show, clear), policy(empty group)49 - admin sub: policy(search), replica(search), revision(search, refresh), revision-preset(create, get, search, update, delete)50- **model-card**: user(project-search, get, deploy, available-presets) · admin(search, get, create, update, delete, bulk-delete, scan)51- **service-catalog**: user(empty group) · admin(search)52- **runtime-variant**: user(get, search) · admin(get, search, create, update, delete, bulk-delete)53- **runtime-variant-preset**: user(get, search) · admin(get, search, create, update, delete)54- **scheduling-history**: sub session / kernel / deployment / route / replica-group — each (search, search-scoped)55- **scheduling-handler**: admin(list)5657### Storage5859- **vfolder**: user(my-search, project-search, admin-search, create, project-create, get, upload, download, delete, purge, restore, ls, mkdir, mv, rm, clone, deploy, bulk-delete, bulk-purge)60- **vfs-storage**: user(create, get, search, list-all, update, delete)61- **storage-namespace**: user(register, unregister, search, get-by-storage)62- **object-storage**: user(create, get, search, update, delete)63- **storage-host**: my(permissions)6465### Registries & Artifacts6667- **container-registry**: user(empty group) · admin(search, create, update, delete)68- **artifact**: user(get, update, delete, restore · sub revision: get, approve, reject, cancel-import, cleanup) · admin(search)69- **artifact-registry**: user(get)70- **huggingface-registry**: user(create, get, search, update, delete) — no admin variant71- **reservoir-registry**: user(create, get, search, update, delete)7273### Access Control & Auth7475- **entity-type**: user(list)76- **rbac**: sub assignment(assign, revoke, search), entity(search), permission(search),77 invitation(create, accept, reject, cancel, my-search, my-sent-search, role-search),78 role(create, get, search, update, delete, project-search, add-permission, remove-permission, replace-permission)79- **role**: my(search)80- **role-preset**: admin(create, get, search, update, delete, purge, restore, permission-add, permission-remove, permission-search)81- **invitation**: admin(search)82- **keypair**: admin(create, get, search, update, delete · sub ssh: register, get, delete) · my(issue, revoke, search, update, switch-main)83- **login-history**: admin(search) · my(search)84- **login-session**: admin(search, revoke) · my(search, revoke)85- **login-client-type**: user(get) · admin(search, create, update, delete)8687### Resource Management8889- **resource-group**: user(empty group) · admin(search, get, create, update, delete, resource-info, default-options, default-session-options, allow-domains, allowed-domains, allow-projects, allowed-projects, allow-for-domain, allowed-for-domain, allow-for-project, allowed-for-project)90- **resource-allocation**: user(project-usage, resource-group-usage) · admin(domain-usage, effective) · my(effective, keypair-usage)91- **resource-preset**: admin(search, get, create, update, delete, check-availability)92- **resource-policy**: admin(sub keypair / project / user — each create, get, search, update, delete) · my(keypair, user)93- **resource-slot**: sub slot-type(search, create, update, delete), agent-resource(search), allocation(search)94- **resource-usage**: sub domain(search), project(search), user(search)9596### Monitoring & Audit9798- **audit-log**: user(search)99- **fair-share**: sub domain / project / user — each (get, search)100- **idle-checker-assignment**: user(scoped-search, update, purge) · admin(create, search)101- **notification**: sub channel(get, search, delete), rule(get, search, delete)102- **prometheus-query-definition**: user(get, search, execute) · admin(create, update, delete, preview)103- **prometheus-query-definition-category**: user(get, search) · admin(create, delete)104- **app-config**: my(get) · public(get) — the merged view; `public` is an app-config-only group for the pre-login read, needing no credentials105- **app-config-definition**: admin(create, get, search, purge)106- **app-config-allow-list**: admin(create, get, search, update, purge)107- **app-config-fragment**: user(get, update, purge, bulk-purge) · my(get, update) · admin(search)108- **idle-checker**: admin(create, search, update, purge)109- **export**: admin(list-reports, get-report, audit-logs, keypairs, projects, sessions, sessions-by-project, users, users-by-domain) · my(keypairs, sessions)110111### Utilities (not entities)112113`login`, `logout`, `config`, `gql` — single commands at the root.114115> When adding a new CLI command, update this Reference as well (see "Adding a new entity" in `client/cli/v2/AGENTS.md`).116117---118119## Setup (Webserver session — recommended)120121```bash122./bai config set endpoint http://127.0.0.1:8090123./bai config set endpoint-type session124./bai login125# User ID: admin@lablup.com126# Password: (admin password)127./bai config show128```129130Non-interactive environments (CI, Claude Code):131132```bash133BACKEND_USER=admin@lablup.com BACKEND_PASSWORD=wJalrXUt ./bai login134```135136The session cookie is stored in `~/.backend.ai/session/cookie.dat`.137138### Direct API (alternative)139140Access the manager directly without the webserver (HMAC signature auth):141142```bash143./bai config set endpoint http://127.0.0.1:8091144./bai config set endpoint-type api145./bai config set access-key AKIAIOSFODNN7EXAMPLE146./bai config set secret-key wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY147./bai config show148```149150The configuration is stored in `~/.backend.ai/config.toml` and `credentials.toml`.151152## Command pattern153154```155./bai [admin|my] {entity} [{sub-entity}] {command} [options]156```157158- `admin` — superadmin-only operations159- `my` — self-service (the current user's own resources)160- Entity names are **singular** (domain, user, agent)161- A sub-entity is a Click sub-group (revision, channel, role, etc.)162163The standard 6 operations: `create`, `get`, `search`, `update`, `delete`, `purge` (only some, depending on the entity).164Special operations: `enqueue`/`terminate` (session), `revision add`/`revision activate` (deployment), `login`/`logout`.165166## CLI input style167168- **Default:** an individual `--option` flag per field.169- **Secondary:** a JSON string or an `@file` path for complex nested structures (example: `--initial-revision`, `--config`).170- Do NOT use raw JSON as a positional argument for create/update (with some admin command exceptions: `admin domain create`).171- For get/delete/purge, use the entity identifier (UUID, name) as a positional argument.172173## Search patterns174175```bash176# admin search — whole system (superadmin only)177./bai admin {entity} search --limit 5178179# project-scoped search — the scope ID is a positional argument (not an option)180./bai {entity} project-search {project_id} --limit 5181182# self-service search — own resources183./bai my {entity} search --limit 5184```185186Options and filters differ per entity, so check with `./bai {entity} {command} --help`.187188### --order-by syntax189190Multi-sort with `field:direction`:191192```bash193./bai admin user search --order-by created_at:desc --order-by username:asc194```195196## Naming conventions197198- The CLI `--order-by` maps to the DTO `order` field (common across all entities).199- The CLI `--kebab-case` option maps to the DTO `snake_case` field (Click standard).200- Scoped search: the scope ID is a **positional argument**, not a `--scope-*` option.201202## Raw GraphQL203204`./bai gql` (not `./bai admin gql`) sends a raw GraphQL query. Useful for testing GQL schema changes or when there is no REST CLI:205206```bash207./bai gql '{ domain(name: "default") { name } }'208./bai gql --v2 '{ myKeypairs(first: 5) { count edges { node { accessKey } } } }'209./bai gql -f query.graphql210./bai gql --var limit=5 '{ keypair_list(limit: $limit) { items { access_key } } }'211```212213- `--v2`: targets the Strawberry v2 schema (only needed in direct API mode; session mode provides both)214- `--var key=value`: query variables (repeatable)215- `-f file`: read the query from a file216- stdin: `echo '{ ... }' | ./bai gql`217218## Common commands219220```bash221# admin search + filter (superadmin only)222./bai admin domain search --limit 5 --name-contains default223./bai admin user search --status active --order-by created_at:desc224./bai admin agent search --limit 10225./bai admin image search --name-contains python226./bai admin session search --limit 5227228# single entity lookup229./bai domain get default230./bai user get <uuid>231232# sub-entity operations233./bai admin deployment revision search234./bai rbac role search235./bai notification channel search236./bai scheduling-history session search --limit 10237```238239## Testing workflow240241### Prerequisites242243```bash244./bai config show # check endpoint-type245./bai login # log in if the session expired246```247248### After modifying server code249250For local development, restart the service first — see the `/local-dev` skill.251252```bash253# 1. confirm basic connectivity254./bai admin domain search --limit 1255./bai domain get default256257# 2. test the modified entity (with the command matching its level)258# if user-facing ./bai {entity} ..., if admin-only ./bai admin {entity} ...259./bai admin {entity} search --limit 1260./bai {entity} get {id}261262# 3. test the permission boundary263./bai admin {entity} search # admin succeeds264# after switching to a regular user, the same command → should fail with 403265```266267After commands, verify the runtime behavior with the Grafana MCP — see `/observability`.268To catch errors not surfaced in the CLI response, check Loki (`{service_name="manager"} |= "error"`);269to check the request count, look at Prometheus (`backendai_api_request_count`).270271### Testing as a regular user272273The default accounts are in `fixtures/manager/example-users.json`.274275```bash276# log in as a regular user session277BACKEND_USER=user@lablup.com BACKEND_PASSWORD=C8qnIo29 ./bai login278279# should succeed (user-facing)280./bai domain get default281282# should fail with 403 (admin only)283./bai admin domain search --limit 1284```285286After testing, revert to the admin credentials.287288## Smoke test script289290Run everything with admin credentials. OK if each command returns JSON.291292```bash293for cmd in \294 "admin domain search --limit 1" \295 "domain get default" \296 "admin user search --limit 1" \297 "admin project search --limit 1" \298 "admin agent search --limit 1" \299 "admin image search --limit 1" \300 "admin session search --limit 1" \301 "admin resource-group search --limit 1" \302 "audit-log search --limit 1" \303 "rbac role search --limit 1"; do304 echo -n "$cmd: "305 ./bai $cmd 2>&1 | python3 -c "import sys,json;json.load(sys.stdin);print('OK')" 2>&1 || echo "FAIL"306done307```308309## When a CLI command is unavailable310311If the CLI command for an entity/operation is not in the Reference above:3123131. That command is **not implemented**.3142. **Report to the user**: "{entity} {operation} is not provided via the CLI. It needs to be implemented."3153. As a temporary workaround, try `./bai gql` for operations available via GraphQL.3164. Do NOT guess CLI options or fabricate commands — it wastes time and causes errors.317318## Related skills319320- `/local-dev` — restart local services before CLI testing321- `/observability` — check logs/metrics with the Grafana MCP after CLI testing322- `/cli-sdk-guide` — implement new CLI commands