The goodqa command line: your QA Commander, your vault and your data from a terminal

Status: not released yet. The CLI is built and tested but not yet published as a download or shipped in the QA Commander instance image. Its secrets commands also need the organization switch vaultEnv.enabled, which is off on every plan until an administrator turns it on; chat, tickets and custom data use routes that are already live.

goodqa is one small program (Linux, macOS and Windows, amd64 and arm64; no runtime to install). Run with no arguments it opens a chat with your organization's QA Commander in the terminal, in the style of a coding agent: replies as they arrive, visible tool calls, scrollback, multi-line input, history, and slash commands. The same binary reads your organization's vault secrets into one command's environment and works with your custom data.

Who it speaks as

Where it runsIdentityHow
Inside a QA Commander instancethe instance's own identity (the QA Commander persona key)found automatically from the instance environment; nothing to configure
Anywhere elsean organization API key you approve in the browsergoodqa login shows a code; you confirm it signed in to GoodQA and pick the organization
Scriptsa key in a fileGOODQA_API_KEY_FILE=<path to the key file> (or GOODQA_API_KEY)

A key is never accepted as a command-line argument (every process on a machine can read another's arguments) and is never printed. A stored login is a file only you can read (mode 0600). Inside an instance, a person's own /login is kept in memory for that session only: the instance's disk is shared with its agent.

Inside an instance the box speaks as QA Commander, so to chat with QA Commander you type /login and sign in as yourself. The secrets, data, tickets and desktop commands work with the box identity straight away.

The chat

goodqa                     open the chat (QA Commander first; your other personas one /persona away)
goodqa -c                  open it on your last thread
goodqa -p "what failed in last night's run?"     ask once, print the reply, exit
git diff | goodqa -p "review this"               piped input is appended to the prompt

Each persona has its own thread, which is an ordinary direct message in GoodQA: what you write in the terminal is in the app too, and the other way round.

CommandWhat it does
/persona [number or handle]list your organization's personas, or switch (each keeps its own thread and history)
/secretsthe vault secrets this identity may run commands with: names only, never values
/data [database] [query]custom data: status, describe a database, or run a read query
/tickets [words], /tickets open <id>, /tickets comment <id> <text>search, read and comment on tickets
/desktopthe remote desktop's open handoffs, and where the live-view link was sent
/follow-upsthe follow-ups your agents are tracking
/resume, /clear, /login, /whoami, /help, /exitas named

A line that starts with / never reaches the persona: an unknown command (a typo such as /secrest) is answered with help, locally. Start a line with // to send a literal /.

Keys: Enter sends; Alt+Enter or Ctrl+J adds a line; Up and Down walk your history; Page Up and Page Down scroll. Ctrl+C stops whatever is running (a command, a request, waiting for a reply) before it ever exits; pressed twice on an empty line it exits.

Secrets in the environment

goodqa secrets list                                  the names this identity may use
goodqa run --secrets STRIPE_KEY,DB_URL=prod-db -- make deploy
goodqa run --secret-set deploy --mask -- ./release.sh
goodqa env --secrets STRIPE_KEY                      export lines with the values hidden
goodqa secrets get STRIPE_KEY --reveal               prints the value, on a terminal only
printf %s "$NEW_VALUE" | goodqa secrets set STRIPE_KEY
goodqa secrets rm OLD_KEY --yes

goodqa run reads the named secrets and starts the command with them in its environment only: not in its arguments, not in a file, not in your shell or its history. NAME becomes the variable NAME (upper-cased, other characters as _); ENV=NAME picks the variable. Variables that change how a program starts (PATH, LD_*, NODE_OPTIONS and the like) cannot be set from a secret. --mask replaces the values (and their common encodings) in the command's output with ********; it is off by default so the command keeps your terminal and full speed.

Values are only ever printed by a person asking for them on a terminal: secrets get --reveal and env --reveal refuse when their output is a pipe or a file. Without --reveal, nothing is read.

Who may read what is the organization's decision. A vault manager lists, per persona or per API key, the secret names (or name prefixes such as DEPLOY_*) it may read this way, and named sets such as deploy. A machine reads nothing until it is listed. Every read is recorded in the vault's access log and the audit log, with the names, who, when and why (run, get, env), never the value. A request is all or nothing: one name it may not read and no value is returned. Reads are rate-limited per identity. A user token never reads a value; personal secrets are never reachable this way.

Secrets in Google Cloud. A name of the form gcp:projects/<project>/secrets/<id> (optionally /versions/<n>) is read from Google Cloud with this machine's own Google credentials, after GoodQA checks it against the organization's allowed prefixes and records it. GoodQA never holds or sees that value.

When QA Commander needs to run something with a secret

QA Commander can ask you to run a command; it can never read a value. Its request appears as a tool call:

[tool] run request: make deploy
  with secrets in its environment: STRIPE_KEY (values never shown to the persona)
  why: deploy the fix to staging
  Allow? type y and Enter to run it, n and Enter to decline

Only a request in the selected persona's own message is offered; the same text in a ticket, a page or a command's output is shown as text and never run. A request may not start goodqa itself. Nothing runs without your y. The command's output goes back to QA Commander masked and marked as untrusted data, and the CLI refuses to send any later message that contains a value it handled. goodqa -p never runs a request: it declines it and tells you.

Custom data

goodqa data status
goodqa data describe crm
goodqa data query crm "select name from leads where stage = $1" --param '"won"'
goodqa data rows crm leads --limit 20
echo '[{"name": "Ada"}]' | goodqa data insert crm leads

The server applies the identity's data role: a persona needs a data role bound to it.

Exit codes

0 ok, 1 the API refused, 2 usage (including a refused --reveal into a pipe), 3 not signed in, revoked, or not allowed, 4 not found or not on for this organization, 5 not configured on this deployment, 6 rate limited or unavailable, 7 network. goodqa run exits with the command's own status.

API

Method and pathWhoWhat
GET /v1/orgs/{orgId}/vault-envkeys and peoplethe names this identity may read, the sets, the allowed external prefixes
POST /v1/orgs/{orgId}/vault-env/revealkeys and people (never a user token){ names, purpose, commandDigest? } to { values }; all or nothing; audited
POST /v1/orgs/{orgId}/vault-env/external/authorizekeys and peoplechecks and records gcp: references before the client reads them itself
GET / PUT /v1/orgs/{orgId}/vault-env/configvault managers; PUT signed in onlythe allow-list: rules, sets, providers

All four answer the not-found of an unknown route while vaultEnv.enabled is off for the organization.

Reference

  • API reference: the vault-env operations above are in the all-features document; the served /v1/openapi.json lists them only for an org with vaultEnv.enabled.
  • MCP tools: the client calls REST routes only and adds no MCP tool; what an agent's key gets is in MCP tools.
  • Runbook: building and checksumming the client (nothing is published); the switch row in feature switches.

We use essential cookies to keep you signed in. See our Privacy Policy for details.