Skip to content

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 hch CLI (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 &ZeroWidthSpace;

OptionProsConsWhy not chosen
Node.js CLI in apps/cli/ (chosen)Monorepo, shares types, simple install, GitHub ReleasesRequires Node.js on admin machineChosen — aligns with existing stack
Azure Cloud Shell scriptNo install requiredRequires Azure access, harder to distributeNot chosen — dependency on Azure auth
Admin portal web UI onlyNo install, browser-nativeSlow for batch ops; no scriptingNot chosen — CLI complements portal; not a replacement
Dedicated Go binarySingle binary, no Node depNew language in stack, longer buildNot chosen — TypeScript reuse preferred

Consequences &ZeroWidthSpace;

Positive &ZeroWidthSpace;

  • Administrators can approve, suspend, and list members from a terminal without browser access, enabling scripted bulk operations.
  • Developer workflow improves: hch health and hch seed replace 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-tools page provides always-current install instructions and links to the latest release without separate documentation infrastructure.

Negative / trade-offs &ZeroWidthSpace;

  • Long-lived tokens are a credential management burden. Tokens must be stored securely by the administrator (e.g., in a secrets manager or .env file 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 &ZeroWidthSpace;

  • 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 &ZeroWidthSpace;

  • 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 health mirrors the /health endpoint)
  • docs/internal/design/platform-management-cli.md — CLI command structure, auth model, distribution

Heritage Community Hub — Internal. Access restricted via Cloudflare Access + Entra ID.