hq-secrets
hq-secrets is a capability of the
hqCLI, not a standalone repo. It operates against the encrypted vault provided by hq-pro.
hq-secrets is how HQ handles credentials safely. Secrets live in AWS SSM Parameter Store behind the HQ vault, and the CLI injects them into your commands as environment variables — it never prints the values. Access is Cognito-authenticated and governed by per-secret ACLs.
When to use it
- Repos with a committed
.env.schema→ usehq run(the recommended path). - One-off or scripted invocations without a schema → use
hq secrets exec. - A credential the agent should never see (vendor keys, personal tokens) → use
hq secrets generate-linkto collect it from a human.
Schema-driven workflow: hq run
For repos that commit a .env.schema, hq run discovers the schema by walking up to the repo root, batch-fetches every hq()-declared secret in a single request, and spawns your command with them injected as env vars — no --only list to maintain, and values are never printed.
# .env.schema (committed)# @hqCompany("indigo")
NODE_ENV=development # non-sensitive, passed straight through# @requiredDATABASE_URL=hq() # var name == secret name# @requiredDB_URL=hq("INDIGO_NX/DB_URL") # explicit secret-name override# @optionalSENTRY_DSN=hq() # skipped if missing / no ACL accesshq run -- npm run devhq run --check # resolve + report without spawning a childhq run --company indigo -- node script.jsA gitignored .env.local overrides any schema value locally.
One-off injection: hq secrets exec
hq secrets exec --only DATABASE_URL,API_KEY -- npm run migratehq secrets exec --only AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY -- aws s3 lsValues are fetched server-side, injected into the child process, and never written to stdout/stderr. The --only flag is required — there is no “inject all” mode. Results cache locally (encrypted, 5-minute TTL).
Never-print-secrets discipline
The defining rule of this capability: the safe path is the easy path.
hq run/hq secrets execinject secrets into a child process; the CLI itself prints nothing.hq secrets get <PATH>redacts the value by default;--revealis a deliberate human-in-the-loop escape hatch, not for automation.hq secrets env(sourcing into a shell) has a TTY guard — it prints[REDACTED]when stdout is a terminal, emitting real values only into a pipe / process substitution.- Do not capture
execoutput, and do not run value-printing commands (env,printenv,echo $SECRET) inside it.
These are prompt-level guidelines reinforced by CLI design, not absolute technical enforcement — a child process deliberately built to print its environment can still expose values. The contract is: don’t.
Scoping and ACLs
| Scope | Flag | Sharing |
|---|---|---|
| Company (shared) | default / --company <slug> | per-secret ACLs + groups |
| Personal | --personal | owner-only, no sharing surface |
Each company secret has an ACL with read / write / admin permissions. The creator is implicitly admin (non-revocable). New secrets are restricted by default (creator-only); an open flag grants company-wide read. Grants can target an individual or a group (group membership changes adjust access without touching the ACL).
hq secrets acl PROD/DB_PASSWORD # who has accesshq secrets share PROD/DB_PASSWORD --with alice@example.com --permission readhq secrets unshare PROD/DB_PASSWORD --from alice@example.comhq groups create grp_backend --name "Backend team"hq secrets share PROD/DB_PASSWORD --with grp_backend --permission readYou need admin on a secret to share / unshare it. Widening to admin or setting open is a privilege escalation — confirm with a human first.
Collecting human-supplied credentials
When a secret should reach the vault without the agent ever seeing it, generate a single-use submission link:
hq secrets generate-link VENDOR_API_KEY --expires 1hThe human opens the URL and enters the value, which goes straight to SSM. The resulting URL is a single-use capability — surface it only as a Markdown inline link (label free of the token), never as bare text, and never echo it again in any later turn or persisted artifact. Use the redacted form https://hq.{co}.com/secrets-input/<TOKEN_REDACTED> everywhere after minting.