Files
HiveGrid-Wireframes/03 Quellen und Referenzen/hivegrid-cloud/docs/contributing/cli-ops.md
T
2026-07-08 16:15:37 +02:00

204 lines
7.1 KiB
Markdown

# CLI ops (`hivegrid ops`)
The operator CLI under `packages/cli/` ships three ops sub-commands
for day-to-day operational work:
- `hivegrid ops doctor` — local environment health check
- `hivegrid ops migrate-tenant` — resumable tenant migration between envs
- `hivegrid ops impersonate` — audit-logged debug magic-link for a tenant user
All three commands accept the `--env` flag to target a named env file
under `~/.hivegrid/envs/`. The default is the current working-dir's
`.env` (i.e. whatever is already loaded into `process.env`).
## Env-file layout
Named envs live under:
```
~/.hivegrid/envs/<name>.env
```
Example layout:
```
~/.hivegrid/envs/
├── staging.env # chmod 600
├── staging-backup.env
└── dev.env
```
- File format: standard `KEY=VALUE` `.env` syntax with `#` comments.
No shell interpolation, no multi-line values.
- File permissions must be `0600` (owner-only). The CLI emits a warning
when world-readable bits are set; rotate the file with
`chmod 600 ~/.hivegrid/envs/<name>.env`.
- Production env files **must not** live in CI. The `impersonate`
command refuses any env named `prod`, `production`, or `live`
regardless of file content.
## `hivegrid ops doctor`
Runs eight probes against the resolved env and emits an ok/warn/fail
table:
| Probe | Description | Fail hint |
|---|---|---|
| `node.version` | Process Node version >=20 | `nvm use` |
| `pnpm.version` | `pnpm --version` >=9 | `corepack enable` |
| `docker.reachable` | `docker info` succeeds | `docker compose up -d` |
| `postgres.reachable` | DATABASE_URL ping (injected) | Check `docker compose ps postgres` |
| `redis.reachable` | REDIS_URL ping (injected) | `docker compose up -d redis` |
| `sentry.dsn` | HEAD against SENTRY_DSN origin | Check Coolify Sentry container |
| `env.required` | Required env keys present | Update your `.env` |
| `prisma.migrations` | `prisma migrate status` (injected) | `pnpm db:migrate dev` |
Examples:
```bash
# Default — read local .env
node packages/cli/bin/hivegrid-cli.mjs ops doctor
# Read ~/.hivegrid/envs/staging.env
node packages/cli/bin/hivegrid-cli.mjs ops doctor --env=staging
# JSON output for CI
node packages/cli/bin/hivegrid-cli.mjs ops doctor --json
```
**Exit codes:** `0` when every probe is `ok` or `warn`; `1` when any
probe reports `fail`.
### Troubleshooting
| Symptom | Likely cause |
|---|---|
| `postgres.reachable: fail (DATABASE_URL not set)` | Forgot to export `.env` before running CLI |
| `docker.reachable: warn` | Docker Desktop not running |
| `sentry.dsn: warn` | SENTRY_DSN intentionally unset — error reporting disabled |
| `env.required: fail Missing required env vars: SESSION_SECRET` | Bootstrap your `.env` from `.env.example` |
| `prisma.migrations: warn N pending` | Run `pnpm db:migrate dev` |
## `hivegrid ops migrate-tenant`
End-to-end tenant migration between named envs. Reuses the
`packages/cli/src/commands/tenant/snapshot.ts` infrastructure from
phase 03 (#44). Resumable via a state file under
`~/.hivegrid/migrations/<tenantId>-<timestamp>.state.json`.
Steps (each is an atomic resume-point):
1. `preflight` — both envs resolve, names validated
2. `snapshot` — `tenant snapshot` exports source tenant to a local archive
3. `restore` — archive is restored into the target env
4. `verify` — row-counts compared source vs target
5. `cleanup` — snapshot kept on disk for audit
Examples:
```bash
# Plan a migration (no writes)
node packages/cli/bin/hivegrid-cli.mjs ops migrate-tenant tenant-abc \
--from=staging --to=staging-backup --dry-run
# Real migration
node packages/cli/bin/hivegrid-cli.mjs ops migrate-tenant tenant-abc \
--from=staging --to=staging-backup
# Resume after a crash
node packages/cli/bin/hivegrid-cli.mjs ops migrate-tenant tenant-abc \
--from=staging --to=staging-backup \
--resume ~/.hivegrid/migrations/tenant-abc-2026-05-17T120000000Z.state.json
```
The CLI emits a per-step status line:
```
state-file=/home/op/.hivegrid/migrations/tenant-abc-2026-05-17T120000000Z.state.json
preflight: ok — from=staging to=staging-backup
snapshot: ok — /home/op/.hivegrid/migrations/tenant-abc-….tar.gz
restore: ok — restored 12345 rows
verify: ok — row-counts match
cleanup: ok — snapshot kept on disk for audit
```
The Prisma + S3 backends are injected at the `bin/hivegrid-cli.mjs`
entrypoint. The standalone subcommand prints a warning if invoked
without that injection (no destructive operations happen).
## `hivegrid ops impersonate`
Generates a 15-minute HMAC-signed magic-link the engineer can paste
into a browser to debug a specific tenant user. Every issued link
writes an `AuditEvent` so the action is reviewable.
```bash
node packages/cli/bin/hivegrid-cli.mjs ops impersonate \
tenant-abc user-123 \
--env=staging \
--reason="support-ticket-456"
```
**Safety guarantees:**
- Production env names (`prod`, `production`, `live`) are hard-blocked.
No `--force` bypass exists. Use the admin self-service impersonate
UI for production.
- TTL is fixed at 15 minutes. Tokens cannot be longer-lived.
- HMAC signing key must be at least 16 characters (the CLI throws
otherwise).
- The audit-event captures:
- `actorType = admin`
- `action = ops.impersonate.issued`
- `targetType = user`, `targetId = <userId>`
- `regulatoryScope = [soc2]`
- `dataSensitivity = pii`
- `outcome = success`
- `metadata = { envName, tokenId, expiresAt, ttlMinutes, reason, userEmail }`
The audit-event is written before the link is returned, so even a
crashing operator workflow leaves a trail.
### Backend route
The corresponding loader at
`apps/web/app/routes/api.auth.impersonate.tsx` (to be wired in a
follow-up phase) is responsible for:
1. Verifying the HMAC signature against the same secret
2. Looking up the audit-event by `tokenId` and refusing if absent
3. Refusing if the audit-event's `outcome ≠ success`
4. Setting a session cookie scoped to the impersonated user with a
short lifetime
## Where things live
```
packages/cli/src/commands/ops/
├── _env.ts # env-file resolver (local vs named)
├── _env.test.ts
├── doctor.ts # doctor command + 8 probes
├── doctor.test.ts
├── migrate-tenant.ts # 5-step resumable migration
├── migrate-tenant.test.ts
├── impersonate.ts # HMAC magic-link + audit-event
├── impersonate.test.ts
└── index.ts # citty subcommand group
```
The bin entrypoint in `packages/cli/bin/hivegrid-cli.mjs` is responsible
for injecting the live Prisma client + S3 client + URL base into the
commands. The standalone files keep zero dependencies on `@prisma/client`
or AWS SDK so the package builds without optional peers.
## Security considerations
- **Never commit `~/.hivegrid/envs/*.env`.** The path is outside the
repo on purpose.
- The `migrate-tenant` state files contain tenant IDs but no secrets.
They are safe to retain for audit.
- Snapshot archives may contain PII. Apply the standard PII-retention
policy (chmod 600, delete after migration audit window).
- The `impersonate` audit-event is admin-visible and forms part of
the soc2 audit trail. Do not redact it after the fact.