Appearance
0035 — Platform Management CLI ​
Status: Accepted
Date: 2026-06-23
ADO work item: AB#4328 (Platform Management CLI Epic); see also AB#4329–AB#4332
Deciders: Kristopher Turner (platform owner)
Cross-references: ADR 0006 (RBAC), ADR 0021 (Admin Portal), ADR 0034 (Platform Status Dashboard)
Context ​
Administrative tasks — approving pending members, suspending accounts, running health checks, and seeding development data — currently require either direct database access or manual API calls via curl. Neither is accessible to non-developer administrators.
A dedicated CLI tool distributed as a Node.js script gives administrators a reproducible, auditable way to perform these operations from a terminal. It also provides a developer workflow shortcut for seeding and health-checking local environments.
The CLI must authenticate against the Heritage Virginia API without requiring Clerk browser flows, so a long-lived API token model (admin-only, generated inside the admin portal) is needed. This is distinct from Clerk session tokens, which are short-lived and browser-bound.
Decision ​
We will build a standalone Node.js CLI in apps/cli/ (tracked as AB#4328–AB#4332), distribute it via GitHub Releases and as an npm package, and expose a CLI documentation page at /app/admin/cli-tools inside the admin portal. The CLI authenticates with a long-lived API token generated in the admin portal; the token is an ADMIN-only credential.
We will ship a
hchCLI (Node.js,apps/cli/) that authenticates via a long-lived admin API token, distributed through GitHub Releases; admin portal exposes token management and CLI docs at/app/admin/cli-tools.
Amendment 2026-06-23 (AB#4387) ​
The in-app CLI page (/app/admin/cli-tools) will be extended from a documentation page to an in-browser terminal UI. The terminal executes commands against a server-side command allowlist — no arbitrary shell execution. Allowed commands mirror the CLI: users list, users approve <id>, users suspend <id>, users show <id>, health, seed, config. Each execution is authenticated via admin session token and writes an AuditLog row (CLI_COMMAND_EXECUTED action). The allowlist is enforced server-side regardless of what the browser sends. This is compatible with the minors-data security posture because no arbitrary code runs. Tracked: AB#4387.
Alternatives considered ​
| Option | Pros | Cons | Why not chosen |
|---|---|---|---|
Node.js CLI in apps/cli/ (chosen) | Monorepo, shares types, simple install, GitHub Releases | Requires Node.js on admin machine | Chosen — aligns with existing stack |
| Azure Cloud Shell script | No install required | Requires Azure access, harder to distribute | Not chosen — dependency on Azure auth |
| Admin portal web UI only | No install, browser-native | Slow for batch ops; no scripting | Not chosen — CLI complements portal; not a replacement |
| Dedicated Go binary | Single binary, no Node dep | New language in stack, longer build | Not chosen — TypeScript reuse preferred |
Consequences ​
Positive ​
- Administrators can approve, suspend, and list members from a terminal without browser access, enabling scripted bulk operations.
- Developer workflow improves:
hch healthandhch seedreplace ad-hoc curl commands. - Long-lived token model is scoped to ADMIN role; tokens are revocable from the admin portal.
- Distribution via GitHub Releases keeps the CLI installable without a private registry.
- The
/app/admin/cli-toolspage provides always-current install instructions and links to the latest release without separate documentation infrastructure.
Negative / trade-offs ​
- Long-lived tokens are a credential management burden. Tokens must be stored securely by the administrator (e.g., in a secrets manager or
.envfile with restricted permissions). - The CLI is a thin HTTP client over the API; any API-breaking change requires a CLI release.
- GitHub Releases distribution means the CLI is publicly downloadable (though API token is still required to authenticate).
Risks ​
- Token leakage would give an attacker ADMIN-level API access. Mitigate: token rotation support from day one, short default expiry with refresh, audit log entry on every token use.
- CLI falls behind API — mitigate by versioning the API and including a minimum-CLI-version header check in the API's token-auth middleware.
References ​
- AB#4328 — Platform Management CLI Epic
- AB#4329–AB#4332 — CLI implementation stories and tasks
- ADR 0006 — two-plane RBAC (admin token scope)
- ADR 0021 — admin portal (CLI docs page at
/app/admin/cli-tools) - ADR 0034 — platform status dashboard (CLI's
hch healthmirrors the/healthendpoint) docs/internal/design/platform-management-cli.md— CLI command structure, auth model, distribution