186 lines
8.8 KiB
Markdown
186 lines
8.8 KiB
Markdown
# Harmony Application CLI — Use Cases & Commands
|
|
|
|
> **Status: partially implemented.** The app lifecycle provides build,
|
|
> publish, ship, deploy, status, and logs. Other verbs below remain the design
|
|
> target. The *decisions and rationale*
|
|
> live in [ADR-026](../adr/026-application-lifecycle-cli.md) — read that
|
|
> for the "why"; this doc is the "what" and "how".
|
|
|
|
## Mental model
|
|
|
|
Four ideas carry the whole CLI:
|
|
|
|
- **`harmony <scope> <verb>`.** A small fixed set of scope nouns —
|
|
**`app`** (developer lifecycle), **`tenant`** (tenant-admin),
|
|
**`cluster`** (cluster-admin), **`context`** (everyone). What you *can*
|
|
run is decided server-side by your context's identity, not by the
|
|
command existing.
|
|
- **Implicit app, explicit context.** The app is *this project's app*
|
|
(inferred — you never name it). The **target is always explicit**:
|
|
`--context <name>` or `HARMONY_CONTEXT`. **There is no default context;
|
|
omitting it is a hard error.** Selection remains mandatory for local k3d and
|
|
binaries with one compiled target so shell history and CI identify the target.
|
|
- **Declarative vs operational.** Verbs that change desired state go
|
|
through the project's typed Scores and re-converge. Verbs that only
|
|
read or are ephemeral talk to the cluster directly and never mutate
|
|
desired state. You never imperatively edit live state — you edit a
|
|
Score and redeploy.
|
|
- **Config has three homes, none of them a config file.** Behavior →
|
|
typed Scores (in git). Targets → compiled contexts in the deploy binary.
|
|
Secrets → OpenBao.
|
|
|
|
### Contexts & profiles
|
|
|
|
A **context** is `{ cluster access, namespace, publication target, domain }`.
|
|
It carries no authorization role; authorization comes from the deploy
|
|
identity (Zitadel → OpenBao → RBAC). Contexts are typed Rust values compiled
|
|
into the deploy binary. Private targets stay in private deploy repositories.
|
|
|
|
```
|
|
harmony context list # what can I target?
|
|
harmony context show myapp-prod # cluster, tenant, profile, identity (+ role, derived from token)
|
|
```
|
|
|
|
The context variant determines the profile: local contexts use `Profile::Local`
|
|
and remote contexts use production behavior through `Profile::Prod`. A staging
|
|
cluster can have a staging context name, but it currently uses the same
|
|
replication, TLS, and credential behavior as production. Scores never infer
|
|
behavior from the context name. A separate staging profile should be added only
|
|
when it has distinct operational semantics. See ADR-026 §7.
|
|
|
|
Credentials ride on the context and degrade: **local** uses the ambient
|
|
k3d/kubeconfig; **remote** mints a short-lived, namespace-scoped token via
|
|
Zitadel→OpenBao. The CLI holds nothing standing (ADR-026 §10).
|
|
|
|
For remote contexts, the kubeconfig stored in OpenBao selects the Kubernetes
|
|
cluster through its `current-context`. Context resolution logs that context,
|
|
the API server, and the deployment namespace. Deploy reports repeat the same
|
|
target so CI output records where convergence occurred.
|
|
|
|
---
|
|
|
|
## `app` — the developer lifecycle
|
|
|
|
The verbs, by class:
|
|
|
|
| Verb | Class | Context? | What it does |
|
|
|---|---|---|---|
|
|
| `check` | declarative | no | Compile + type-check the Scores. The only pre-deploy validation today (no live diff yet). |
|
|
| `build` | declarative | **yes** | Build a digest-pinned OCI image using target-specific naming. Resolves context metadata, but not credentials or cluster access. |
|
|
| `publish` | declarative | **yes** | Push the image to the registry (remote) **or** `k3d image import` (local). Topology-specific. |
|
|
| `deploy` | declarative | **yes** | Converge the Scores against the context, pinned to `--image <digest>`. **Does not build.** Returns only after smoke-test. |
|
|
| `ship` | declarative | **yes** | `build` + `publish` + `deploy`, threading the digest. The everyday verb. |
|
|
| `run` | declarative | **yes** | Run a one-off Score-driven Job (migration, task). |
|
|
| `logs` | operational | **yes** | Stream/tail logs. Deep search lives in the observability tool. |
|
|
| `status` | operational | **yes** | Health, replicas, last deploy, recent events. |
|
|
| `exec` | operational | **yes** | Shell/exec into a running container. |
|
|
| `forward` | operational | **yes** | Port-forward a service to localhost. |
|
|
| `restart` | operational | **yes** | Rollout-restart a workload (pods return identical; no state change). |
|
|
| `describe` | operational | **yes** | Resource detail + events for diagnosis. |
|
|
| `history` | operational | **yes** | Deploy history — what shipped, when, which digest. |
|
|
|
|
### Develop (inner loop)
|
|
|
|
| I want to… | Command |
|
|
|---|---|
|
|
| Check my Scores compile/type-check | `harmony app check` |
|
|
| Build the image to test it builds | `harmony app build --context local` |
|
|
| Deploy to local k3d and test | `harmony app ship --context local` |
|
|
| See it running locally | `harmony app status --context local` · `harmony app logs --context local -f` |
|
|
|
|
### Ship to production
|
|
|
|
| I want to… | Command |
|
|
|---|---|
|
|
| Build + publish + deploy to prod | `harmony app ship --context myapp-prod` |
|
|
| Deploy an already-built image | `harmony app deploy --context myapp-prod --image <digest>` |
|
|
| Roll **forward** to a prior good build (recovery) | `harmony app deploy --context myapp-prod --image <prior-digest>` |
|
|
| Build / publish as separate CI stages | `harmony app build --context myapp-prod` → `harmony app publish --context myapp-prod` → `harmony app deploy …` |
|
|
|
|
There is no rollback command. Recovery deploys a known-good digest as a new
|
|
roll-forward operation.
|
|
|
|
### Operate (day-2)
|
|
|
|
| I want to… | Command |
|
|
|---|---|
|
|
| Tail logs | `harmony app logs --context myapp-prod -f` |
|
|
| Check health / what's deployed | `harmony app status --context myapp-prod` |
|
|
| Shell into the app | `harmony app exec --context myapp-prod -- /bin/sh` |
|
|
| Reach a service locally | `harmony app forward --context myapp-prod 8080:80` |
|
|
| Restart the app | `harmony app restart --context myapp-prod` |
|
|
| Run a DB migration | `harmony app run --context myapp-prod -- migrate` |
|
|
| Change replicas / env / resources | *edit the Score, then* `harmony app ship --context myapp-prod` |
|
|
|
|
> Changing replicas/env/config is **not** an imperative command — it's a
|
|
> Score edit + redeploy. An out-of-band `kubectl scale` survives only
|
|
> until the next deploy, then is reconciled away (ADR-026 §5, §8).
|
|
|
|
### Debug / diagnose
|
|
|
|
| I want to… | Command |
|
|
|---|---|
|
|
| Understand why a deploy failed | read the `deploy` output (convergence errors, rustc-style) |
|
|
| See why a workload is unhealthy | `harmony app describe --context myapp-prod` · `harmony app logs …` |
|
|
| See what shipped recently | `harmony app history --context myapp-prod` |
|
|
| Deep log search / traces / metrics | open the observability tool (HyperDX/SigNoz/Grafana) — the CLI deep-links |
|
|
|
|
---
|
|
|
|
## `tenant` — tenant administration (tenant-admin)
|
|
|
|
> Mostly **manual today** (ADR-026 Out of scope: step-0 provisioning).
|
|
> Listed as the target surface; authorization is by the context's role.
|
|
|
|
| I want to… | Command (planned) |
|
|
|---|---|
|
|
| List apps / environments in my tenant | `harmony tenant list --context my-tenant` |
|
|
| Create an environment (namespace — billed) | `harmony tenant env create <name> --context my-tenant` |
|
|
| Add / remove a developer | `harmony tenant member add <user> --context my-tenant` |
|
|
| See usage vs quota | `harmony tenant usage --context my-tenant` (or the dashboard) |
|
|
| Rotate an app's deploy key | `harmony tenant key rotate <app> --context my-tenant` |
|
|
|
|
---
|
|
|
|
## `cluster` — cluster administration (platform operator)
|
|
|
|
> **Future / manual today.** Same noun-by-scope, authorization by role
|
|
> (only a cluster-admin context may run these).
|
|
|
|
| I want to… | Command (planned) |
|
|
|---|---|
|
|
| Provision a tenant | `harmony tenant create <name> --context <cluster-admin-ctx>` |
|
|
| Cluster-wide status | `harmony cluster status --context <cluster-admin-ctx>` |
|
|
| Manage platform operators | `harmony cluster operator …` |
|
|
|
|
---
|
|
|
|
## Machine & agent usage
|
|
|
|
Every verb is built for CI and autonomous agents — the same surface,
|
|
strict mode (ADR-026 §9):
|
|
|
|
- `--json` → a frozen, versioned schema on **stdout**; logs/progress on
|
|
**stderr**.
|
|
- Non-interactive when stdin isn't a TTY — no hidden prompts.
|
|
- Idempotent verbs; `Outcome` (SUCCESS/NOOP/FAILURE/RUNNING/BLOCKED)
|
|
mapped to **exit codes** so CI/agents branch without scraping text.
|
|
- A future agent skill / MCP surface is *derived from* this reference,
|
|
not authored separately.
|
|
|
|
Example CI release (digest pinned end-to-end):
|
|
|
|
```sh
|
|
DIGEST=$(harmony app publish --context myapp-prod --json | jq -r .image)
|
|
harmony app deploy --context myapp-prod --image "$DIGEST" --json
|
|
```
|
|
|
|
---
|
|
|
|
## Status legend
|
|
|
|
Nothing here is implemented yet. As verbs land, tag them:
|
|
**`[impl]`** implemented · **`[wip]`** in progress · **`[planned]`** ·
|
|
**`[future]`** out of v1 scope (per ADR-026). Until the first verbs
|
|
ship, treat the whole document as `[planned]`.
|